Skip to content

Commit 1f31d57

Browse files
authored
Merge pull request #8788 from dotty-staging/fix-8781
Fix #8781: handle union of primitive types in type test
2 parents d6a2d59 + 16e8869 commit 1f31d57

File tree

3 files changed

+40
-16
lines changed

3 files changed

+40
-16
lines changed

compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala

+25-16
Original file line numberDiff line numberDiff line change
@@ -199,29 +199,38 @@ object TypeTestsCasts {
199199
// if `test` is primitive but `found` is not, we might have a case like
200200
// found = java.lang.Integer, test = Int, which could be true
201201
// (not sure why that is so, but scalac behaves the same way)
202+
!(!testCls.isPrimitiveValueClass && foundCls.isPrimitiveValueClass) &&
203+
// foundCls can be `Boolean`, while testCls is `Integer`
204+
// it can happen in `(3: Boolean | Int).isInstanceOf[Int]`
202205
!isDerivedValueClass(foundCls) && !isDerivedValueClass(testCls)
203206
// we don't have the logic to handle derived value classes
204207

205208
/** Check whether a runtime test that a value of `foundCls` can be a `testCls`
206209
* can be true in some cases. Issues a warning or an error otherwise.
207210
*/
208-
def checkSensical(foundCls: Symbol)(using Context): Boolean =
209-
if (!isCheckable(foundCls)) true
210-
else if (foundCls.isPrimitiveValueClass && !testCls.isPrimitiveValueClass) {
211+
def checkSensical(foundClasses: List[Symbol])(using Context): Boolean =
212+
def check(foundCls: Symbol): Boolean =
213+
if (!isCheckable(foundCls)) true
214+
else if (!foundCls.derivesFrom(testCls)) {
215+
val unrelated = !testCls.derivesFrom(foundCls) && (
216+
testCls.is(Final) || !testCls.is(Trait) && !foundCls.is(Trait)
217+
)
218+
if (foundCls.is(Final))
219+
unreachable(i"type ${expr.tpe.widen} is not a subclass of $testCls")
220+
else if (unrelated)
221+
unreachable(i"type ${expr.tpe.widen} and $testCls are unrelated")
222+
else true
223+
}
224+
else true
225+
end check
226+
227+
val foundEffectiveClass = effectiveClass(expr.tpe.widen)
228+
229+
if foundEffectiveClass.isPrimitiveValueClass && !testCls.isPrimitiveValueClass then
211230
ctx.error("cannot test if value types are references", tree.sourcePos)
212231
false
213-
}
214-
else if (!foundCls.derivesFrom(testCls)) {
215-
val unrelated = !testCls.derivesFrom(foundCls) && (
216-
testCls.is(Final) || !testCls.is(Trait) && !foundCls.is(Trait)
217-
)
218-
if (foundCls.is(Final))
219-
unreachable(i"type ${expr.tpe.widen} is not a subclass of $testCls")
220-
else if (unrelated)
221-
unreachable(i"type ${expr.tpe.widen} and $testCls are unrelated")
222-
else true
223-
}
224-
else true
232+
else foundClasses.exists(check)
233+
end checkSensical
225234

226235
if (expr.tpe <:< testType)
227236
if (expr.tpe.isNotNull) {
@@ -232,7 +241,7 @@ object TypeTestsCasts {
232241
else {
233242
val nestedCtx = ctx.fresh.setNewTyperState()
234243
val foundClsSyms = foundClasses(expr.tpe.widen, Nil)
235-
val sensical = foundClsSyms.exists(sym => checkSensical(sym)(using nestedCtx))
244+
val sensical = checkSensical(foundClsSyms)(using nestedCtx)
236245
if (!sensical) {
237246
nestedCtx.typerState.commit()
238247
constant(expr, Literal(Constant(false)))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
object Test:
2+
3+
println((3: Boolean | Int).isInstanceOf[Boolean])
4+
5+
println(3.isInstanceOf[Boolean]) // error
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
@main
2+
def Test =
3+
4+
val x: Int | String = 1
5+
6+
println(x.isInstanceOf[Int])
7+
8+
x match
9+
case _: Int => println("Int")
10+
case _: String => println("String")

0 commit comments

Comments
 (0)