From c522caa1755b0c9b3d3a4a5f31761f259754c3c2 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Fri, 24 Apr 2020 11:00:17 +0200 Subject: [PATCH 1/4] Fix #8781: handle union of primitive types in type test --- .../tools/dotc/transform/TypeTestsCasts.scala | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index 63ad12abbe5e..3ba14408c350 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -199,29 +199,38 @@ object TypeTestsCasts { // if `test` is primitive but `found` is not, we might have a case like // found = java.lang.Integer, test = Int, which could be true // (not sure why that is so, but scalac behaves the same way) + !(!testCls.isPrimitiveValueClass && foundCls.isPrimitiveValueClass) && + // foundCls can be `Boolean`, while testCls is `Integer` + // it can happen in `(3: Boolean | Int).isInstanceOf[Boolean]` !isDerivedValueClass(foundCls) && !isDerivedValueClass(testCls) // we don't have the logic to handle derived value classes /** Check whether a runtime test that a value of `foundCls` can be a `testCls` * can be true in some cases. Issues a warning or an error otherwise. */ - def checkSensical(foundCls: Symbol)(using Context): Boolean = - if (!isCheckable(foundCls)) true - else if (foundCls.isPrimitiveValueClass && !testCls.isPrimitiveValueClass) { + def checkSensical(foundClasses: List[Symbol])(using Context): Boolean = + def check(foundCls: Symbol): Boolean = + if (!isCheckable(foundCls)) true + else if (!foundCls.derivesFrom(testCls)) { + val unrelated = !testCls.derivesFrom(foundCls) && ( + testCls.is(Final) || !testCls.is(Trait) && !foundCls.is(Trait) + ) + if (foundCls.is(Final)) + unreachable(i"type ${expr.tpe.widen} is not a subclass of $testCls") + else if (unrelated) + unreachable(i"type ${expr.tpe.widen} and $testCls are unrelated") + else true + } + else true + end check + + val foundEffectiveClass = effectiveClass(expr.tpe.widen) + + if foundEffectiveClass.isPrimitiveValueClass && !testCls.isPrimitiveValueClass then ctx.error("cannot test if value types are references", tree.sourcePos) false - } - else if (!foundCls.derivesFrom(testCls)) { - val unrelated = !testCls.derivesFrom(foundCls) && ( - testCls.is(Final) || !testCls.is(Trait) && !foundCls.is(Trait) - ) - if (foundCls.is(Final)) - unreachable(i"type ${expr.tpe.widen} is not a subclass of $testCls") - else if (unrelated) - unreachable(i"type ${expr.tpe.widen} and $testCls are unrelated") - else true - } - else true + else foundClasses.exists(check) + end checkSensical if (expr.tpe <:< testType) if (expr.tpe.isNotNull) { @@ -232,7 +241,7 @@ object TypeTestsCasts { else { val nestedCtx = ctx.fresh.setNewTyperState() val foundClsSyms = foundClasses(expr.tpe.widen, Nil) - val sensical = foundClsSyms.exists(sym => checkSensical(sym)(using nestedCtx)) + val sensical = checkSensical(foundClsSyms)(using nestedCtx) if (!sensical) { nestedCtx.typerState.commit() constant(expr, Literal(Constant(false))) From deaf44d7a38302978ffd5ca905d17f32a1c9b69d Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Fri, 24 Apr 2020 11:00:38 +0200 Subject: [PATCH 2/4] Add test --- tests/pos-special/fatal-warnings/i8781.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/pos-special/fatal-warnings/i8781.scala diff --git a/tests/pos-special/fatal-warnings/i8781.scala b/tests/pos-special/fatal-warnings/i8781.scala new file mode 100644 index 000000000000..fe0d5d1ce61c --- /dev/null +++ b/tests/pos-special/fatal-warnings/i8781.scala @@ -0,0 +1,10 @@ +@main +def Test = + + val x: Int | String = 1 + + println(x.isInstanceOf[Int]) + + x match + case _: Int => println("Int") + case _: String => println("String") From 06119487f7b8d8c2671b60b60fdcca63c52159ac Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Fri, 24 Apr 2020 11:37:31 +0200 Subject: [PATCH 3/4] Add more test --- tests/neg-custom-args/fatal-warnings/i8781b.scala | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 tests/neg-custom-args/fatal-warnings/i8781b.scala diff --git a/tests/neg-custom-args/fatal-warnings/i8781b.scala b/tests/neg-custom-args/fatal-warnings/i8781b.scala new file mode 100644 index 000000000000..a30b0e5249c9 --- /dev/null +++ b/tests/neg-custom-args/fatal-warnings/i8781b.scala @@ -0,0 +1,5 @@ +object Test: + + println((3: Boolean | Int).isInstanceOf[Boolean]) + + println(3.isInstanceOf[Boolean]) // error From 16e886913d2a8bc9bf3f1e7fefb5b1833b1b7147 Mon Sep 17 00:00:00 2001 From: Fengyun Liu Date: Fri, 24 Apr 2020 13:23:42 +0200 Subject: [PATCH 4/4] Update compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala --- compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index 3ba14408c350..722867215adc 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -201,7 +201,7 @@ object TypeTestsCasts { // (not sure why that is so, but scalac behaves the same way) !(!testCls.isPrimitiveValueClass && foundCls.isPrimitiveValueClass) && // foundCls can be `Boolean`, while testCls is `Integer` - // it can happen in `(3: Boolean | Int).isInstanceOf[Boolean]` + // it can happen in `(3: Boolean | Int).isInstanceOf[Int]` !isDerivedValueClass(foundCls) && !isDerivedValueClass(testCls) // we don't have the logic to handle derived value classes