From b96b5ccb53d4e0415f14bff18a39282e57fa9da7 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Thu, 22 Feb 2018 13:36:31 +0100 Subject: [PATCH 01/30] Fix #3324: introduce IsInstanceOfChecker add check for runtime realizability of type test --- compiler/src/dotty/tools/dotc/Compiler.scala | 1 + .../dotc/transform/IsInstanceOfChecker.scala | 84 +++++++++++++++++++ .../dotty/tools/dotc/CompilationTests.scala | 1 + .../neg-custom-args/isInstanceOf/3324b.scala | 9 ++ .../neg-custom-args/isInstanceOf/3324c.scala | 10 +++ .../neg-custom-args/isInstanceOf/i3324.scala | 4 + 6 files changed, 109 insertions(+) create mode 100644 compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala create mode 100644 tests/neg-custom-args/isInstanceOf/3324b.scala create mode 100644 tests/neg-custom-args/isInstanceOf/3324c.scala create mode 100644 tests/neg-custom-args/isInstanceOf/i3324.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 16fcaf694b98..8ea64cf62f40 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -81,6 +81,7 @@ class Compiler { new CrossCastAnd, // Normalize selections involving intersection types. new Splitter) :: // Expand selections involving union types into conditionals List(new ErasedDecls, // Removes all erased defs and vals decls (except for parameters) + new IsInstanceOfChecker, // check runtime realisability for `isInstanceOf` new VCInlineMethods, // Inlines calls to value class methods new SeqLiterals, // Express vararg arguments as arrays new InterceptedMethods, // Special handling of `==`, `|=`, `getClass` methods diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala new file mode 100644 index 000000000000..0a5bd5c11ddb --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -0,0 +1,84 @@ +package dotty.tools.dotc +package transform + +import util.Positions._ +import MegaPhase.MiniPhase +import core._ +import Contexts.Context, Types._, Decorators._, Symbols._, typer._ +import TypeUtils._, Flags._ +import config.Printers.{ transforms => debug } + +/** check runtime realizability of type test + */ +class IsInstanceOfChecker extends MiniPhase { + + import ast.tpd._ + + val phaseName = "isInstanceOfChecker" + + override def transformTypeApply(tree: TypeApply)(implicit ctx: Context): Tree = { + def ensureCheckable(qual: Tree, pt: Tree): Tree = { + if (!Checkable.checkable(qual.tpe, pt.tpe)) + ctx.warning( + s"the type test for ${pt.show} cannot be checked at runtime", + tree.pos + ) + + tree + } + + tree.fun match { + case fn: Select if fn.symbol == defn.Any_typeTest => + ensureCheckable(fn.qualifier, tree.args.head) + case fn: Select if fn.symbol == defn.Any_isInstanceOf => + ensureCheckable(fn.qualifier, tree.args.head) + case _ => tree + } + } +} + +object Checkable { + import Inferencing._ + import ProtoTypes._ + + /** Whether `(x:X).isInstanceOf[P]` can be checked at runtime? + * + * The following cases are not checkable at runtime: + * + * 1. if `P` refers to an abstract type member + * 2. if `P` is `pre.F[Ts]` and `pre.F` refers to a class: + * (a) replace `Ts` with fresh type variables `Xs` + * (b) instantiate `Xs` with the constraint `pre.F[Xs] <:< X` + * (c) `pre.F[Xs] <:< P` doesn't hold + * 3. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2). + */ + def checkable(X: Type, P: Type)(implicit ctx: Context): Boolean = { + def Psym = P.dealias.typeSymbol + + def isAbstract = !Psym.isClass + + def isClassDetermined(tpe: AppliedType) = { + val AppliedType(tycon, args) = tpe + val tvars = tycon.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) } + val P2 = tycon.appliedTo(tvars) + + debug.println("P2 : " + P2) + debug.println("X : " + X) + + !(P2 <:< X.widen) || { + val syms = maximizeType(P2, Psym.pos, fromScala2x = false) + val res = P2 <:< P + debug.println("P2: " + P2.show) + debug.println("P2 <:< P = " + res) + res + } + } + + P match { + case tpe: AppliedType => !isAbstract && isClassDetermined(tpe) + case AndType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2) + case OrType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2) + case _ => !isAbstract + } + } +} \ No newline at end of file diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 2a7b78b6c64c..4e6a16f8b498 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -106,6 +106,7 @@ class CompilationTests extends ParallelTesting { compileFilesInDir("tests/pos-deep-subtype", allowDeepSubtypes) + compileFilesInDir("tests/pos-kind-polymorphism", defaultOptions and "-Ykind-polymorphism") + compileDir("tests/pos/i1137-1", defaultOptions and "-Yemit-tasty") + + compileDir("tests/neg-custom-args/isInstanceOf", defaultOptions and "-Xfatal-warnings") + compileFile( // succeeds despite -Xfatal-warnings because of -nowarn "tests/neg-custom-args/fatal-warnings/xfatalWarnings.scala", diff --git a/tests/neg-custom-args/isInstanceOf/3324b.scala b/tests/neg-custom-args/isInstanceOf/3324b.scala new file mode 100644 index 000000000000..8b60bff4d9da --- /dev/null +++ b/tests/neg-custom-args/isInstanceOf/3324b.scala @@ -0,0 +1,9 @@ +class C[T] { + val x: Any = ??? + if (x.isInstanceOf[List[String]]) // error: unchecked + if (x.isInstanceOf[T]) // error: unchecked + x match { + case x: List[String] => // error: unchecked + case x: T => // error: unchecked + } +} diff --git a/tests/neg-custom-args/isInstanceOf/3324c.scala b/tests/neg-custom-args/isInstanceOf/3324c.scala new file mode 100644 index 000000000000..129d3f8d89f2 --- /dev/null +++ b/tests/neg-custom-args/isInstanceOf/3324c.scala @@ -0,0 +1,10 @@ +sealed trait A[T] +class B[T] extends A[T] + +class Test { + def f(x: B[Int]) = x match { case _: A[Int] if true => } + + def g(x: A[Int]) = x match { case _: B[Int] => } + + def foo(x: Any) = x.isInstanceOf[List[String]] // error +} diff --git a/tests/neg-custom-args/isInstanceOf/i3324.scala b/tests/neg-custom-args/isInstanceOf/i3324.scala new file mode 100644 index 000000000000..9fac958e8f3c --- /dev/null +++ b/tests/neg-custom-args/isInstanceOf/i3324.scala @@ -0,0 +1,4 @@ +class Foo { + def foo(x: Any): Boolean = + x.isInstanceOf[List[String]] // error +} From 94d83131b5607740096fd25783c55dcf296ac8c8 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Tue, 27 Feb 2018 14:21:03 +0100 Subject: [PATCH 02/30] handle Array in isInstanceOf check --- .../dotty/tools/dotc/config/Printers.scala | 2 +- .../dotc/transform/IsInstanceOfChecker.scala | 62 ++++++++++++------- .../neg-custom-args/isInstanceOf/t2755.scala | 58 +++++++++++++++++ 3 files changed, 99 insertions(+), 23 deletions(-) create mode 100644 tests/neg-custom-args/isInstanceOf/t2755.scala diff --git a/compiler/src/dotty/tools/dotc/config/Printers.scala b/compiler/src/dotty/tools/dotc/config/Printers.scala index bd8cb9844c0c..56e1f69fe107 100644 --- a/compiler/src/dotty/tools/dotc/config/Printers.scala +++ b/compiler/src/dotty/tools/dotc/config/Printers.scala @@ -31,7 +31,7 @@ object Printers { val plugins: Printer = noPrinter val simplify: Printer = noPrinter val subtyping: Printer = noPrinter - val transforms: Printer = noPrinter + val transforms: Printer = new Printer val typr: Printer = noPrinter val unapp: Printer = noPrinter val variances: Printer = noPrinter diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index 0a5bd5c11ddb..1b47f1d17983 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -8,7 +8,7 @@ import Contexts.Context, Types._, Decorators._, Symbols._, typer._ import TypeUtils._, Flags._ import config.Printers.{ transforms => debug } -/** check runtime realizability of type test +/** Check runtime realizability of type test, see the documentation for `Checkable`. */ class IsInstanceOfChecker extends MiniPhase { @@ -20,7 +20,7 @@ class IsInstanceOfChecker extends MiniPhase { def ensureCheckable(qual: Tree, pt: Tree): Tree = { if (!Checkable.checkable(qual.tpe, pt.tpe)) ctx.warning( - s"the type test for ${pt.show} cannot be checked at runtime", + s"the type test for ${pt} cannot be checked at runtime", tree.pos ) @@ -43,42 +43,60 @@ object Checkable { /** Whether `(x:X).isInstanceOf[P]` can be checked at runtime? * - * The following cases are not checkable at runtime: - * - * 1. if `P` refers to an abstract type member - * 2. if `P` is `pre.F[Ts]` and `pre.F` refers to a class: + * 0. if `P` is a singleton type, TRUE + * 1. if `P` refers to an abstract type member, FALSE + * 2. if `P = Array[T]`, checkable(E, T) where `E` is the element type of `X`, defaults to `Any`. + * 3. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`: * (a) replace `Ts` with fresh type variables `Xs` * (b) instantiate `Xs` with the constraint `pre.F[Xs] <:< X` - * (c) `pre.F[Xs] <:< P` doesn't hold - * 3. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2). + * (c) `pre.F[Xs] <:< P2`, where `P2` is `P` with pattern binder types (e.g., `_$1`) + * replaced with `WildcardType`. + * 4. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2). + * 5. otherwise, TRUE */ def checkable(X: Type, P: Type)(implicit ctx: Context): Boolean = { def Psym = P.dealias.typeSymbol def isAbstract = !Psym.isClass - def isClassDetermined(tpe: AppliedType) = { + def replaceBinderMap(implicit ctx: Context) = new TypeMap { + def apply(tp: Type) = tp match { + case tref: TypeRef if !tref.typeSymbol.isClass && tref.symbol.is(Case) => WildcardType + case _ => mapOver(tp) + } + } + + def isClassDetermined(tpe: AppliedType)(implicit ctx: Context) = { val AppliedType(tycon, args) = tpe val tvars = tycon.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) } - val P2 = tycon.appliedTo(tvars) + val P1 = tycon.appliedTo(tvars) - debug.println("P2 : " + P2) - debug.println("X : " + X) + debug.println("P1 : " + P1) + debug.println("X : " + X.widen) - !(P2 <:< X.widen) || { - val syms = maximizeType(P2, Psym.pos, fromScala2x = false) - val res = P2 <:< P - debug.println("P2: " + P2.show) - debug.println("P2 <:< P = " + res) + !(P1 <:< X.widen) || { + // val syms = maximizeType(P1, Psym.pos, fromScala2x = false) + isFullyDefined(P1, ForceDegree.noBottom) + val P2 = replaceBinderMap.apply(P) + val res = P1 <:< P2 + debug.println("P1: " + P1) + debug.println("P2: " + P2) + debug.println("P1 <:< P2 = " + res) res } } P match { - case tpe: AppliedType => !isAbstract && isClassDetermined(tpe) - case AndType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2) - case OrType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2) - case _ => !isAbstract + case _: SingletonType => true + case defn.ArrayOf(tpT) => + X match { + case defn.ArrayOf(tpE) => checkable(tpE, tpT) + case _ => checkable(defn.AnyType, tpT) + } + case tpe: AppliedType => !isAbstract && isClassDetermined(tpe)(ctx.fresh.setFreshGADTBounds) + case AndType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2) + case OrType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2) + case _ => !isAbstract } } -} \ No newline at end of file +} diff --git a/tests/neg-custom-args/isInstanceOf/t2755.scala b/tests/neg-custom-args/isInstanceOf/t2755.scala new file mode 100644 index 000000000000..8d10b567346b --- /dev/null +++ b/tests/neg-custom-args/isInstanceOf/t2755.scala @@ -0,0 +1,58 @@ +// Test cases: the only place we can cut and paste without crying +// ourself to sleep. +object Test { + def f1(a: Any) = a match { + case x: Array[Int] => x(0) + case x: Array[Double] => 2 + case x: Array[Float] => x.sum.toInt + case x: Array[String] => x.size + case x: Array[AnyRef] => 5 + case x: Array[_] => 6 + case _ => 7 + } + def f2(a: Array[_]) = a match { + case x: Array[Int] => x(0) + case x: Array[Double] => 2 + case x: Array[Float] => x.sum.toInt + case x: Array[String] => x.size + case x: Array[AnyRef] => 5 + case x: Array[_] => 6 + case _ => 7 + } + def f3[T](a: Array[T]) = a match { + case x: Array[Int] => x(0) + case x: Array[Double] => 2 + case x: Array[Float] => x.sum.toInt + case x: Array[String] => x.size + case x: Array[AnyRef] => 5 + case x: Array[_] => 6 + case _ => 7 + } + + + def main(args: Array[String]): Unit = { + println(f1(Array(1, 2, 3))) + println(f1(Array(1.0, -2.0, 3.0, 1.0))) + println(f1(Array(1.0f, 2.0f, 3.0f, -3.0f))) + println(f1((1 to 4).toArray map (_.toString))) + println(f1(new Array[Any](10))) // should match as Array[AnyRef] + println(f1(Array(1L))) + println(f1(null)) + + println(f2(Array(1, 2, 3))) + println(f2(Array(1.0, -2.0, 3.0, 1.0))) + println(f2(Array(1.0f, 2.0f, 3.0f, -3.0f))) + println(f2((1 to 4).toArray map (_.toString))) + println(f2(new Array[Any](10))) // should match as Array[AnyRef] + println(f2(Array(1L))) + println(f2(null)) + + println(f3(Array(1, 2, 3))) + println(f3(Array(1.0, -2.0, 3.0, 1.0))) + println(f3(Array(1.0f, 2.0f, 3.0f, -3.0f))) + println(f3((1 to 4).toArray map (_.toString))) + println(f3(new Array[Any](10))) // should match as Array[AnyRef] + println(f3(Array(1L))) + println(f3(null)) + } +} From 0b9f8a80a63b3551b8bcd0a71af3bab8bd0516aa Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Tue, 27 Feb 2018 14:48:04 +0100 Subject: [PATCH 03/30] fix check for Array --- .../dotty/tools/dotc/config/Printers.scala | 2 +- .../dotc/transform/IsInstanceOfChecker.scala | 26 ++++++++++++------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/Printers.scala b/compiler/src/dotty/tools/dotc/config/Printers.scala index 56e1f69fe107..bd8cb9844c0c 100644 --- a/compiler/src/dotty/tools/dotc/config/Printers.scala +++ b/compiler/src/dotty/tools/dotc/config/Printers.scala @@ -31,7 +31,7 @@ object Printers { val plugins: Printer = noPrinter val simplify: Printer = noPrinter val subtyping: Printer = noPrinter - val transforms: Printer = new Printer + val transforms: Printer = noPrinter val typr: Printer = noPrinter val unapp: Printer = noPrinter val variances: Printer = noPrinter diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index 1b47f1d17983..ac00a2c392d2 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -20,7 +20,7 @@ class IsInstanceOfChecker extends MiniPhase { def ensureCheckable(qual: Tree, pt: Tree): Tree = { if (!Checkable.checkable(qual.tpe, pt.tpe)) ctx.warning( - s"the type test for ${pt} cannot be checked at runtime", + s"the type test for ${pt.show} cannot be checked at runtime", tree.pos ) @@ -43,16 +43,18 @@ object Checkable { /** Whether `(x:X).isInstanceOf[P]` can be checked at runtime? * - * 0. if `P` is a singleton type, TRUE - * 1. if `P` refers to an abstract type member, FALSE - * 2. if `P = Array[T]`, checkable(E, T) where `E` is the element type of `X`, defaults to `Any`. - * 3. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`: + * 1. if `P` is a singleton type, TRUE + * 2. if `P` is WildcardType, TRUE + * 3. if `P` refers to an abstract type member, FALSE + * 4. if `P = Array[T]`, checkable(E, T) where `E` is the element type of `X`, defaults to `Any`. + * 5. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`: * (a) replace `Ts` with fresh type variables `Xs` * (b) instantiate `Xs` with the constraint `pre.F[Xs] <:< X` * (c) `pre.F[Xs] <:< P2`, where `P2` is `P` with pattern binder types (e.g., `_$1`) * replaced with `WildcardType`. - * 4. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2). - * 5. otherwise, TRUE + * 6. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2). + * 7. if `P` is a refinement type, FALSE + * 8. otherwise, TRUE */ def checkable(X: Type, P: Type)(implicit ctx: Context): Boolean = { def Psym = P.dealias.typeSymbol @@ -86,8 +88,9 @@ object Checkable { } } - P match { + val res = P match { case _: SingletonType => true + case WildcardType => true case defn.ArrayOf(tpT) => X match { case defn.ArrayOf(tpE) => checkable(tpE, tpT) @@ -96,7 +99,12 @@ object Checkable { case tpe: AppliedType => !isAbstract && isClassDetermined(tpe)(ctx.fresh.setFreshGADTBounds) case AndType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2) case OrType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2) - case _ => !isAbstract + case AnnotatedType(tp, _) => checkable(X, tp) + case _ => replaceBinderMap.apply(P) == WildcardType || !isAbstract } + + debug.println(i"checking ${X.show} isInstanceOf ${P} = $res") + + res } } From 4c2436b7be41611d4cf5c47504215eb6064aa044 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Tue, 27 Feb 2018 15:24:59 +0100 Subject: [PATCH 04/30] fix runtime check warning for canEqual & equals of case class --- .../dotc/transform/IsInstanceOfChecker.scala | 9 +++++---- .../dotc/transform/SyntheticMethods.scala | 9 +++++---- .../neg-custom-args/isInstanceOf/Result.scala | 20 +++++++++++++++++++ 3 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 tests/neg-custom-args/isInstanceOf/Result.scala diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index ac00a2c392d2..5b55e6f5cb63 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -45,6 +45,7 @@ object Checkable { * * 1. if `P` is a singleton type, TRUE * 2. if `P` is WildcardType, TRUE + * 3. if `P = T @unchecked`, TRUE * 3. if `P` refers to an abstract type member, FALSE * 4. if `P = Array[T]`, checkable(E, T) where `E` is the element type of `X`, defaults to `Any`. * 5. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`: @@ -73,7 +74,7 @@ object Checkable { val tvars = tycon.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) } val P1 = tycon.appliedTo(tvars) - debug.println("P1 : " + P1) + debug.println("P1 : " + P1.show) debug.println("X : " + X.widen) !(P1 <:< X.widen) || { @@ -81,8 +82,7 @@ object Checkable { isFullyDefined(P1, ForceDegree.noBottom) val P2 = replaceBinderMap.apply(P) val res = P1 <:< P2 - debug.println("P1: " + P1) - debug.println("P2: " + P2) + debug.println("P2: " + P2.show) debug.println("P1 <:< P2 = " + res) res } @@ -99,7 +99,8 @@ object Checkable { case tpe: AppliedType => !isAbstract && isClassDetermined(tpe)(ctx.fresh.setFreshGADTBounds) case AndType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2) case OrType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2) - case AnnotatedType(tp, _) => checkable(X, tp) + case AnnotatedType(t, an) => an.symbol == defn.UncheckedAnnot || checkable(X, t) + case _: RefinedType => false case _ => replaceBinderMap.apply(P) == WildcardType || !isAbstract } diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala index 105763856049..47e97e519a68 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala @@ -11,6 +11,7 @@ import ast.Trees._ import ast.untpd import Decorators._ import NameOps._ +import Annotations.Annotation import ValueClasses.isDerivedValueClass import scala.collection.mutable.ListBuffer import scala.language.postfixOps @@ -152,7 +153,7 @@ class SyntheticMethods(thisPhase: DenotTransformer) { * def equals(that: Any): Boolean = * (this eq that) || { * that match { - * case x$0 @ (_: C) => this.x == this$0.x && this.y == x$0.y + * case x$0 @ (_: C @unchecked) => this.x == this$0.x && this.y == x$0.y * case _ => false * } * ``` @@ -162,7 +163,7 @@ class SyntheticMethods(thisPhase: DenotTransformer) { def equalsBody(that: Tree)(implicit ctx: Context): Tree = { val thatAsClazz = ctx.newSymbol(ctx.owner, nme.x_0, Synthetic, clazzType, coord = ctx.owner.pos) // x$0 def wildcardAscription(tp: Type) = Typed(Underscore(tp), TypeTree(tp)) - val pattern = Bind(thatAsClazz, wildcardAscription(clazzType)) // x$0 @ (_: C) + val pattern = Bind(thatAsClazz, wildcardAscription(AnnotatedType(clazzType, Annotation(defn.UncheckedAnnot)))) // x$0 @ (_: C @unchecked) val comparisons = accessors map { accessor => This(clazz).select(accessor).equal(ref(thatAsClazz).select(accessor)) } val rhs = // this.x == this$0.x && this.y == x$0.y @@ -250,10 +251,10 @@ class SyntheticMethods(thisPhase: DenotTransformer) { * gets the `canEqual` method * * ``` - * def canEqual(that: Any) = that.isInstanceOf[C] + * def canEqual(that: Any) = that.isInstanceOf[C @unchecked] * ``` */ - def canEqualBody(that: Tree): Tree = that.isInstance(clazzType) + def canEqualBody(that: Tree): Tree = that.isInstance(AnnotatedType(clazzType, Annotation(defn.UncheckedAnnot))) symbolsToSynthesize flatMap syntheticDefIfMissing } diff --git a/tests/neg-custom-args/isInstanceOf/Result.scala b/tests/neg-custom-args/isInstanceOf/Result.scala new file mode 100644 index 000000000000..dd91888f7475 --- /dev/null +++ b/tests/neg-custom-args/isInstanceOf/Result.scala @@ -0,0 +1,20 @@ +import scala.util.control.NonFatal +object p { + + enum Result[+T, +E] { + case OK [T](x: T) extends Result[T, Nothing] + case Err[E](e: E) extends Result[Nothing, E] + } + + type Try[T] = Result[T, Throwable] + object Try { + def apply[T](x: => T): Try[T] = + try Result.OK(x) + catch { + case NonFatal(ex) => Result.Err(ex) + } + } + + def foo(x: Any): Boolean = + x.isInstanceOf[List[String]] // error +} From ce2248594ace2d29f0c701c480f383c75ffc65cf Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Tue, 27 Feb 2018 15:32:00 +0100 Subject: [PATCH 05/30] fix test group --- compiler/test/dotty/tools/dotc/CompilationTests.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 4e6a16f8b498..9740dda3694c 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -106,7 +106,6 @@ class CompilationTests extends ParallelTesting { compileFilesInDir("tests/pos-deep-subtype", allowDeepSubtypes) + compileFilesInDir("tests/pos-kind-polymorphism", defaultOptions and "-Ykind-polymorphism") + compileDir("tests/pos/i1137-1", defaultOptions and "-Yemit-tasty") + - compileDir("tests/neg-custom-args/isInstanceOf", defaultOptions and "-Xfatal-warnings") + compileFile( // succeeds despite -Xfatal-warnings because of -nowarn "tests/neg-custom-args/fatal-warnings/xfatalWarnings.scala", @@ -191,6 +190,7 @@ class CompilationTests extends ParallelTesting { compileFile("tests/neg-custom-args/noimports2.scala", defaultOptions.and("-Yno-imports")) + compileFile("tests/neg-custom-args/i3882.scala", allowDeepSubtypes) + compileFile("tests/neg-custom-args/i1754.scala", allowDeepSubtypes) + + compileDir("tests/neg-custom-args/isInstanceOf", defaultOptions and "-Xfatal-warnings") + compileFile("tests/neg-custom-args/i3627.scala", allowDeepSubtypes) }.checkExpectedErrors() From 825dbdf6bd28d968ce55ae668609cff573a97e01 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Tue, 27 Feb 2018 15:35:10 +0100 Subject: [PATCH 06/30] fix typo in numbering of rules --- .../tools/dotc/transform/IsInstanceOfChecker.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index 5b55e6f5cb63..8d8c3ee99ddc 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -46,16 +46,16 @@ object Checkable { * 1. if `P` is a singleton type, TRUE * 2. if `P` is WildcardType, TRUE * 3. if `P = T @unchecked`, TRUE - * 3. if `P` refers to an abstract type member, FALSE - * 4. if `P = Array[T]`, checkable(E, T) where `E` is the element type of `X`, defaults to `Any`. - * 5. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`: + * 4. if `P` refers to an abstract type member, FALSE + * 5. if `P = Array[T]`, checkable(E, T) where `E` is the element type of `X`, defaults to `Any`. + * 6. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`: * (a) replace `Ts` with fresh type variables `Xs` * (b) instantiate `Xs` with the constraint `pre.F[Xs] <:< X` * (c) `pre.F[Xs] <:< P2`, where `P2` is `P` with pattern binder types (e.g., `_$1`) * replaced with `WildcardType`. - * 6. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2). - * 7. if `P` is a refinement type, FALSE - * 8. otherwise, TRUE + * 7. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2). + * 8. if `P` is a refinement type, FALSE + * 9. otherwise, TRUE */ def checkable(X: Type, P: Type)(implicit ctx: Context): Boolean = { def Psym = P.dealias.typeSymbol From 183be33496f31d3bc78563253487353b2482cec0 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Tue, 27 Feb 2018 15:58:58 +0100 Subject: [PATCH 07/30] add more tests --- .../dotc/transform/IsInstanceOfChecker.scala | 6 +-- .../dotty/tools/dotc/CompilationTests.scala | 2 +- tests/neg-custom-args/isInstanceOf/gadt.scala | 40 +++++++++++++++++++ 3 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 tests/neg-custom-args/isInstanceOf/gadt.scala diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index 8d8c3ee99ddc..bb08376b2431 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -78,10 +78,8 @@ object Checkable { debug.println("X : " + X.widen) !(P1 <:< X.widen) || { - // val syms = maximizeType(P1, Psym.pos, fromScala2x = false) - isFullyDefined(P1, ForceDegree.noBottom) val P2 = replaceBinderMap.apply(P) - val res = P1 <:< P2 + val res = isFullyDefined(P1, ForceDegree.noBottom) && P1 <:< P2 debug.println("P2: " + P2.show) debug.println("P1 <:< P2 = " + res) res @@ -96,7 +94,7 @@ object Checkable { case defn.ArrayOf(tpE) => checkable(tpE, tpT) case _ => checkable(defn.AnyType, tpT) } - case tpe: AppliedType => !isAbstract && isClassDetermined(tpe)(ctx.fresh.setFreshGADTBounds) + case tpe: AppliedType => !isAbstract && isClassDetermined(tpe) case AndType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2) case OrType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2) case AnnotatedType(t, an) => an.symbol == defn.UncheckedAnnot || checkable(X, t) diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 9740dda3694c..f87f5359954b 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -190,7 +190,7 @@ class CompilationTests extends ParallelTesting { compileFile("tests/neg-custom-args/noimports2.scala", defaultOptions.and("-Yno-imports")) + compileFile("tests/neg-custom-args/i3882.scala", allowDeepSubtypes) + compileFile("tests/neg-custom-args/i1754.scala", allowDeepSubtypes) + - compileDir("tests/neg-custom-args/isInstanceOf", defaultOptions and "-Xfatal-warnings") + + compileFilesInDir("tests/neg-custom-args/isInstanceOf", defaultOptions and "-Xfatal-warnings") + compileFile("tests/neg-custom-args/i3627.scala", allowDeepSubtypes) }.checkExpectedErrors() diff --git a/tests/neg-custom-args/isInstanceOf/gadt.scala b/tests/neg-custom-args/isInstanceOf/gadt.scala new file mode 100644 index 000000000000..7a1ccc3320e4 --- /dev/null +++ b/tests/neg-custom-args/isInstanceOf/gadt.scala @@ -0,0 +1,40 @@ +sealed trait Exp[T] +case class Num(n: Int) extends Exp[Int] +case class Plus(e1: Exp[Int], e2: Exp[Int]) extends Exp[Int] +case class Var[T](name: String) extends Exp[T] +case class Lambda[T, U](x: Var[T], e: Exp[U]) extends Exp[T => U] +case class App[T, U](f: Exp[T => U], e: Exp[T]) extends Exp[U] + +abstract class Env { outer => + def apply[T](x: Var[T]): T + + def + [T](xe: (Var[T], T)) = new Env { + def apply[T](x: Var[T]): T = + if (x == xe._1) xe._2.asInstanceOf[T] + else outer(x) + } +} + +object Env { + val empty = new Env { + def apply[T](x: Var[T]): T = ??? + } +} + +object Test { + + val exp = App(Lambda(Var[Int]("x"), Plus(Var[Int]("x"), Num(1))), Var[Int]("2")) + + def eval[T](e: Exp[T])(env: Env): T = e match { + case Num(n) => n + case Plus(e1, e2) => eval(e1)(env) + eval(e2)(env) + case v: Var[T] => env(v) + case Lambda(x: Var[s], e) => ((y: s) => eval(e)(env + (x -> y))) + case App(f, e) => eval(f)(env)(eval(e)(env)) + } + + eval(exp)(Env.empty) + + def foo(x: Any): Boolean = + x.isInstanceOf[List[String]] // error +} From 974a7c19317ddb6ff7f7e3539753a5f2117d64cf Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Tue, 27 Feb 2018 16:06:17 +0100 Subject: [PATCH 08/30] fix nested @unchecked --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 2 +- .../src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala | 5 ++++- tests/neg-custom-args/isInstanceOf/t2755.scala | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 29096e44bc38..33e3a3642653 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -805,7 +805,7 @@ object Trees { def unforced: AnyRef protected def force(x: AnyRef): Unit def forceIfLazy(implicit ctx: Context): T = unforced match { - case lzy: Lazy[T] => + case lzy: Lazy[T @unchecked] => val x = lzy.complete force(x) x diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index bb08376b2431..0dddf7ef8325 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -64,7 +64,10 @@ object Checkable { def replaceBinderMap(implicit ctx: Context) = new TypeMap { def apply(tp: Type) = tp match { - case tref: TypeRef if !tref.typeSymbol.isClass && tref.symbol.is(Case) => WildcardType + case tref: TypeRef + if !tref.typeSymbol.isClass && tref.symbol.is(Case) => WildcardType + case AnnotatedType(_, annot) + if annot.symbol == defn.UncheckedAnnot => WildcardType case _ => mapOver(tp) } } diff --git a/tests/neg-custom-args/isInstanceOf/t2755.scala b/tests/neg-custom-args/isInstanceOf/t2755.scala index 8d10b567346b..daba75500eaf 100644 --- a/tests/neg-custom-args/isInstanceOf/t2755.scala +++ b/tests/neg-custom-args/isInstanceOf/t2755.scala @@ -55,4 +55,7 @@ object Test { println(f3(Array(1L))) println(f3(null)) } + + def foo(x: Any): Boolean = + x.isInstanceOf[List[String]] // error } From daf226045c77aff75e65441d64bde7ee8dcf9d26 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Tue, 27 Feb 2018 16:06:38 +0100 Subject: [PATCH 09/30] add test case --- tests/neg-custom-args/isInstanceOf/3324d.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 tests/neg-custom-args/isInstanceOf/3324d.scala diff --git a/tests/neg-custom-args/isInstanceOf/3324d.scala b/tests/neg-custom-args/isInstanceOf/3324d.scala new file mode 100644 index 000000000000..e9b608a77cb1 --- /dev/null +++ b/tests/neg-custom-args/isInstanceOf/3324d.scala @@ -0,0 +1,10 @@ +class Test { + val x: Any = ??? + + x match { + case _: List[Int @unchecked] => 5 + } + + def foo(x: Any): Boolean = + x.isInstanceOf[List[String]] // error +} \ No newline at end of file From 9ee45187531aa6de6445e2b79f2454133032b0a7 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Tue, 27 Feb 2018 16:11:56 +0100 Subject: [PATCH 10/30] adapt specification --- .../src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index 0dddf7ef8325..4403004ecae6 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -46,7 +46,7 @@ object Checkable { * 1. if `P` is a singleton type, TRUE * 2. if `P` is WildcardType, TRUE * 3. if `P = T @unchecked`, TRUE - * 4. if `P` refers to an abstract type member, FALSE + * 4. if `P` refers to an abstract type member or type parameter, FALSE * 5. if `P = Array[T]`, checkable(E, T) where `E` is the element type of `X`, defaults to `Any`. * 6. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`: * (a) replace `Ts` with fresh type variables `Xs` From 545930d0c68fabf2cabe91671b193b4526db4baf Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Tue, 27 Feb 2018 16:37:15 +0100 Subject: [PATCH 11/30] tune specification --- .../dotc/transform/IsInstanceOfChecker.scala | 32 +++++++++---------- .../dotty/tools/dotc/CompilationTests.scala | 2 +- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index 4403004ecae6..800d634efdc1 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -42,20 +42,20 @@ object Checkable { import ProtoTypes._ /** Whether `(x:X).isInstanceOf[P]` can be checked at runtime? + * + * Replace `T @unchecked` and pattern binder types (e.g., `_$1`) in P with WildcardType, then check: * * 1. if `P` is a singleton type, TRUE * 2. if `P` is WildcardType, TRUE - * 3. if `P = T @unchecked`, TRUE - * 4. if `P` refers to an abstract type member or type parameter, FALSE - * 5. if `P = Array[T]`, checkable(E, T) where `E` is the element type of `X`, defaults to `Any`. - * 6. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`: + * 3. if `P` refers to an abstract type member or type parameter, FALSE + * 4. if `P = Array[T]`, checkable(E, T) where `E` is the element type of `X`, defaults to `Any`. + * 5. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`: * (a) replace `Ts` with fresh type variables `Xs` * (b) instantiate `Xs` with the constraint `pre.F[Xs] <:< X` - * (c) `pre.F[Xs] <:< P2`, where `P2` is `P` with pattern binder types (e.g., `_$1`) - * replaced with `WildcardType`. - * 7. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2). - * 8. if `P` is a refinement type, FALSE - * 9. otherwise, TRUE + * (c) `pre.F[Xs] <:< P` + * 6. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2). + * 7. if `P` is a refinement type, FALSE + * 8. otherwise, TRUE */ def checkable(X: Type, P: Type)(implicit ctx: Context): Boolean = { def Psym = P.dealias.typeSymbol @@ -73,7 +73,7 @@ object Checkable { } def isClassDetermined(tpe: AppliedType)(implicit ctx: Context) = { - val AppliedType(tycon, args) = tpe + val AppliedType(tycon, _) = tpe val tvars = tycon.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) } val P1 = tycon.appliedTo(tvars) @@ -81,15 +81,13 @@ object Checkable { debug.println("X : " + X.widen) !(P1 <:< X.widen) || { - val P2 = replaceBinderMap.apply(P) - val res = isFullyDefined(P1, ForceDegree.noBottom) && P1 <:< P2 - debug.println("P2: " + P2.show) - debug.println("P1 <:< P2 = " + res) + val res = isFullyDefined(P1, ForceDegree.noBottom) && P1 <:< tpe + debug.println("P1 <:< P = " + res) res } } - val res = P match { + val res = replaceBinderMap.apply(P) match { case _: SingletonType => true case WildcardType => true case defn.ArrayOf(tpT) => @@ -100,9 +98,9 @@ object Checkable { case tpe: AppliedType => !isAbstract && isClassDetermined(tpe) case AndType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2) case OrType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2) - case AnnotatedType(t, an) => an.symbol == defn.UncheckedAnnot || checkable(X, t) + case AnnotatedType(t, an) => checkable(X, t) case _: RefinedType => false - case _ => replaceBinderMap.apply(P) == WildcardType || !isAbstract + case _ => !isAbstract } debug.println(i"checking ${X.show} isInstanceOf ${P} = $res") diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index f87f5359954b..d7608d270910 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -190,7 +190,7 @@ class CompilationTests extends ParallelTesting { compileFile("tests/neg-custom-args/noimports2.scala", defaultOptions.and("-Yno-imports")) + compileFile("tests/neg-custom-args/i3882.scala", allowDeepSubtypes) + compileFile("tests/neg-custom-args/i1754.scala", allowDeepSubtypes) + - compileFilesInDir("tests/neg-custom-args/isInstanceOf", defaultOptions and "-Xfatal-warnings") + + compileFilesInDir("tests/neg-custom-args/isInstanceOf", allowDeepSubtypes and "-Xfatal-warnings") + compileFile("tests/neg-custom-args/i3627.scala", allowDeepSubtypes) }.checkExpectedErrors() From 8719a88e98258ef767f3ce5159aa7dc1f8d39ee0 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Wed, 28 Feb 2018 05:14:00 +0100 Subject: [PATCH 12/30] Fix #1828: add test --- tests/neg-custom-args/isInstanceOf/1828.scala | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/neg-custom-args/isInstanceOf/1828.scala diff --git a/tests/neg-custom-args/isInstanceOf/1828.scala b/tests/neg-custom-args/isInstanceOf/1828.scala new file mode 100644 index 000000000000..aeb83f1a1070 --- /dev/null +++ b/tests/neg-custom-args/isInstanceOf/1828.scala @@ -0,0 +1,9 @@ +class Test { + def remove[S](a: S | Int, f: Int => S):S = a match { + case a: S => a // error + case a: Int => f(a) + } + + val t: Int | String = 5 + val t1 = remove[String](t, _.toString) +} From 5fc5db0b5a4345d42597b81b6b9661a0c5fff51e Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Wed, 28 Feb 2018 09:36:01 +0100 Subject: [PATCH 13/30] Allow test for abstract types if it is always true --- .../dotc/transform/IsInstanceOfChecker.scala | 9 +++++---- .../neg-custom-args/isInstanceOf/3324e.scala | 20 +++++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 tests/neg-custom-args/isInstanceOf/3324e.scala diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index 800d634efdc1..079b237a48e6 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -47,7 +47,7 @@ object Checkable { * * 1. if `P` is a singleton type, TRUE * 2. if `P` is WildcardType, TRUE - * 3. if `P` refers to an abstract type member or type parameter, FALSE + * 3. if `P` refers to an abstract type member or type parameter, `X <:< P` * 4. if `P = Array[T]`, checkable(E, T) where `E` is the element type of `X`, defaults to `Any`. * 5. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`: * (a) replace `Ts` with fresh type variables `Xs` @@ -88,19 +88,20 @@ object Checkable { } val res = replaceBinderMap.apply(P) match { + case _ if isAbstract => X <:< P case _: SingletonType => true case WildcardType => true case defn.ArrayOf(tpT) => - X match { + X.widen match { case defn.ArrayOf(tpE) => checkable(tpE, tpT) case _ => checkable(defn.AnyType, tpT) } - case tpe: AppliedType => !isAbstract && isClassDetermined(tpe) + case tpe: AppliedType => isClassDetermined(tpe) case AndType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2) case OrType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2) case AnnotatedType(t, an) => checkable(X, t) case _: RefinedType => false - case _ => !isAbstract + case _ => true } debug.println(i"checking ${X.show} isInstanceOf ${P} = $res") diff --git a/tests/neg-custom-args/isInstanceOf/3324e.scala b/tests/neg-custom-args/isInstanceOf/3324e.scala new file mode 100644 index 000000000000..905c0f44d350 --- /dev/null +++ b/tests/neg-custom-args/isInstanceOf/3324e.scala @@ -0,0 +1,20 @@ +class C[T] { + val x: T = ??? + x.isInstanceOf[T] + + val y: Array[T] = ??? + + y match { + case x: Array[T] => + } + + type F[X] + + val z: F[T] = ??? + z match { + case x: F[T] => + } + + def foo(x: Any): Boolean = + x.isInstanceOf[List[String]] // error +} From d263372d7ef88c6711289ff5021287302550d321 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Wed, 28 Feb 2018 09:55:17 +0100 Subject: [PATCH 14/30] code refactoring --- .../dotc/transform/IsInstanceOfChecker.scala | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index 079b237a48e6..503cfc8253ea 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -58,9 +58,7 @@ object Checkable { * 8. otherwise, TRUE */ def checkable(X: Type, P: Type)(implicit ctx: Context): Boolean = { - def Psym = P.dealias.typeSymbol - - def isAbstract = !Psym.isClass + def isAbstract(P: Type) = !P.dealias.typeSymbol.isClass def replaceBinderMap(implicit ctx: Context) = new TypeMap { def apply(tp: Type) = tp match { @@ -72,38 +70,40 @@ object Checkable { } } - def isClassDetermined(tpe: AppliedType)(implicit ctx: Context) = { - val AppliedType(tycon, _) = tpe + def isClassDetermined(X: Type, P: AppliedType)(implicit ctx: Context) = { + val AppliedType(tycon, _) = P val tvars = tycon.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) } val P1 = tycon.appliedTo(tvars) debug.println("P1 : " + P1.show) - debug.println("X : " + X.widen) + debug.println("X : " + X.show) - !(P1 <:< X.widen) || { - val res = isFullyDefined(P1, ForceDegree.noBottom) && P1 <:< tpe + !(P1 <:< X) || { + val res = isFullyDefined(P1, ForceDegree.noBottom) && P1 <:< P debug.println("P1 <:< P = " + res) res } } - val res = replaceBinderMap.apply(P) match { - case _ if isAbstract => X <:< P + def recur(X: Type, P: Type): Boolean = P match { case _: SingletonType => true case WildcardType => true + case _ if isAbstract(P) => X <:< P case defn.ArrayOf(tpT) => - X.widen match { - case defn.ArrayOf(tpE) => checkable(tpE, tpT) - case _ => checkable(defn.AnyType, tpT) + X match { + case defn.ArrayOf(tpE) => recur(tpE, tpT) + case _ => recur(defn.AnyType, tpT) } - case tpe: AppliedType => isClassDetermined(tpe) - case AndType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2) - case OrType(tp1, tp2) => checkable(X, tp1) && checkable(X, tp2) - case AnnotatedType(t, an) => checkable(X, t) + case tpe: AppliedType => isClassDetermined(X, tpe) + case AndType(tp1, tp2) => recur(X, tp1) && recur(X, tp2) + case OrType(tp1, tp2) => recur(X, tp1) && recur(X, tp2) + case AnnotatedType(t, an) => recur(X, t) case _: RefinedType => false case _ => true } + val res = recur(X.widen, replaceBinderMap.apply(P)) + debug.println(i"checking ${X.show} isInstanceOf ${P} = $res") res From 3f12d282f497c174d050bbdaf88a9ac5835acc9b Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Wed, 28 Feb 2018 10:19:41 +0100 Subject: [PATCH 15/30] fix test for abstract types --- .../src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index 503cfc8253ea..27b535a75fba 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -88,7 +88,8 @@ object Checkable { def recur(X: Type, P: Type): Boolean = P match { case _: SingletonType => true case WildcardType => true - case _ if isAbstract(P) => X <:< P + case _: TypeProxy + if isAbstract(P) => X <:< P case defn.ArrayOf(tpT) => X match { case defn.ArrayOf(tpE) => recur(tpE, tpT) From ed13d25d29070608639609b9e0ecdfe60f891362 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Thu, 1 Mar 2018 10:56:05 +0100 Subject: [PATCH 16/30] adapt specification for unrelated types --- .../tools/dotc/transform/IsInstanceOfChecker.scala | 11 ++++++----- tests/neg-custom-args/isInstanceOf/3324f.scala | 8 ++++++++ 2 files changed, 14 insertions(+), 5 deletions(-) create mode 100644 tests/neg-custom-args/isInstanceOf/3324f.scala diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index 27b535a75fba..ac4c52b23b16 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -4,7 +4,7 @@ package transform import util.Positions._ import MegaPhase.MiniPhase import core._ -import Contexts.Context, Types._, Decorators._, Symbols._, typer._ +import Contexts.Context, Types._, Decorators._, Symbols._, typer._, ast._ import TypeUtils._, Flags._ import config.Printers.{ transforms => debug } @@ -51,8 +51,8 @@ object Checkable { * 4. if `P = Array[T]`, checkable(E, T) where `E` is the element type of `X`, defaults to `Any`. * 5. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`: * (a) replace `Ts` with fresh type variables `Xs` - * (b) instantiate `Xs` with the constraint `pre.F[Xs] <:< X` - * (c) `pre.F[Xs] <:< P` + * (b) `pre.F[Xs] <:< X` with `Xs` instantiated as `Es` + * (c) `pre.F[Es] <:< P` * 6. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2). * 7. if `P` is a refinement type, FALSE * 8. otherwise, TRUE @@ -72,13 +72,14 @@ object Checkable { def isClassDetermined(X: Type, P: AppliedType)(implicit ctx: Context) = { val AppliedType(tycon, _) = P - val tvars = tycon.typeParams.map { tparam => newTypeVar(tparam.paramInfo.bounds) } + val typeLambda = tycon.ensureHK.asInstanceOf[TypeLambda] + val tvars = constrained(typeLambda, untpd.EmptyTree, alwaysAddTypeVars = true)._2.map(_.tpe) val P1 = tycon.appliedTo(tvars) debug.println("P1 : " + P1.show) debug.println("X : " + X.show) - !(P1 <:< X) || { + (P1 <:< X) && { val res = isFullyDefined(P1, ForceDegree.noBottom) && P1 <:< P debug.println("P1 <:< P = " + res) res diff --git a/tests/neg-custom-args/isInstanceOf/3324f.scala b/tests/neg-custom-args/isInstanceOf/3324f.scala new file mode 100644 index 000000000000..f1701785bc44 --- /dev/null +++ b/tests/neg-custom-args/isInstanceOf/3324f.scala @@ -0,0 +1,8 @@ +trait C[T] +class D[T] + +class Test { + def foo[T](x: C[T]) = x match { + case _: D[T] => // error + } +} \ No newline at end of file From a90f060af152691910bc77d47c28c0d98645ecb8 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Thu, 1 Mar 2018 15:27:04 +0100 Subject: [PATCH 17/30] Fix GADT test failures --- .../dotc/transform/IsInstanceOfChecker.scala | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index ac4c52b23b16..511bc6a5cd8e 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -45,14 +45,14 @@ object Checkable { * * Replace `T @unchecked` and pattern binder types (e.g., `_$1`) in P with WildcardType, then check: * - * 1. if `P` is a singleton type, TRUE - * 2. if `P` is WildcardType, TRUE + * 1. if `X <:< P`, TRUE + * 2. if `P` is a singleton type, TRUE * 3. if `P` refers to an abstract type member or type parameter, `X <:< P` * 4. if `P = Array[T]`, checkable(E, T) where `E` is the element type of `X`, defaults to `Any`. * 5. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`: * (a) replace `Ts` with fresh type variables `Xs` - * (b) `pre.F[Xs] <:< X` with `Xs` instantiated as `Es` - * (c) `pre.F[Es] <:< P` + * (b) constrain `Xs` with `pre.F[Xs] <:< X` (may fail) + * (c) instantiate Xs and check `pre.F[Xs] <:< P` * 6. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2). * 7. if `P` is a refinement type, FALSE * 8. otherwise, TRUE @@ -76,33 +76,34 @@ object Checkable { val tvars = constrained(typeLambda, untpd.EmptyTree, alwaysAddTypeVars = true)._2.map(_.tpe) val P1 = tycon.appliedTo(tvars) + debug.println("P : " + P.show) debug.println("P1 : " + P1.show) debug.println("X : " + X.show) - (P1 <:< X) && { - val res = isFullyDefined(P1, ForceDegree.noBottom) && P1 <:< P - debug.println("P1 <:< P = " + res) - res - } + P1 <:< X // may fail, ignore + + val res = isFullyDefined(P1, ForceDegree.noBottom) && P1 <:< P + debug.println("P1 : " + P1) + debug.println("P1 <:< P = " + res) + res } - def recur(X: Type, P: Type): Boolean = P match { + def recur(X: Type, P: Type): Boolean = (X <:< P) || (P match { case _: SingletonType => true - case WildcardType => true case _: TypeProxy - if isAbstract(P) => X <:< P + if isAbstract(P) => false case defn.ArrayOf(tpT) => X match { case defn.ArrayOf(tpE) => recur(tpE, tpT) case _ => recur(defn.AnyType, tpT) } - case tpe: AppliedType => isClassDetermined(X, tpe) + case tpe: AppliedType => isClassDetermined(X, tpe)(ctx.fresh.setNewTyperState()) case AndType(tp1, tp2) => recur(X, tp1) && recur(X, tp2) case OrType(tp1, tp2) => recur(X, tp1) && recur(X, tp2) case AnnotatedType(t, an) => recur(X, t) case _: RefinedType => false case _ => true - } + }) val res = recur(X.widen, replaceBinderMap.apply(P)) From 37f4e325553d34452252672f679db486c10df458 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Thu, 1 Mar 2018 17:33:09 +0100 Subject: [PATCH 18/30] fix fromTasty test --- tests/pos/simpleCaseObject.decompiled | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/pos/simpleCaseObject.decompiled b/tests/pos/simpleCaseObject.decompiled index 1ecd3759cb65..75d05be1df78 100644 --- a/tests/pos/simpleCaseObject.decompiled +++ b/tests/pos/simpleCaseObject.decompiled @@ -7,7 +7,8 @@ package foo { override def hashCode(): Int = 1045991777 override def toString(): String = "Foo" - override def canEqual(that: Any): Boolean = that.isInstanceOf[foo.Foo] + override def canEqual(that: Any): Boolean = + that.isInstanceOf[foo.Foo @unchecked] override def productArity: Int = 0 override def productPrefix: String = "Foo" override def productElement(n: Int): Any = @@ -17,4 +18,4 @@ package foo { } } } --------------------------------------------------------------------------------- \ No newline at end of file +-------------------------------------------------------------------------------- From ac696da07a7a2cc96bc6c17a3535b46dff763c8b Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Fri, 2 Mar 2018 16:52:19 +0100 Subject: [PATCH 19/30] Adapt specification to handle narrowed GADT bounds --- .../dotc/transform/IsInstanceOfChecker.scala | 27 ++++++++++++++++--- .../isInstanceOf/enum-approx2.scala | 9 +++++++ 2 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 tests/neg-custom-args/isInstanceOf/enum-approx2.scala diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index 511bc6a5cd8e..87ef81d87a84 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -43,11 +43,18 @@ object Checkable { /** Whether `(x:X).isInstanceOf[P]` can be checked at runtime? * - * Replace `T @unchecked` and pattern binder types (e.g., `_$1`) in P with WildcardType, then check: + * First do the following substitution: + * (a) replace `T @unchecked` and pattern binder types (e.g., `_$1`) in P with WildcardType + * (b) replace pattern binder types (e.g., `_$1`) in X: + * - variance = 1 : hiBound + * - variance = -1 : loBound + * - variance = 0 : OrType(Any, Nothing) + * + * Then check: * * 1. if `X <:< P`, TRUE * 2. if `P` is a singleton type, TRUE - * 3. if `P` refers to an abstract type member or type parameter, `X <:< P` + * 3. if `P` refers to an abstract type member or type parameter, FALSE * 4. if `P = Array[T]`, checkable(E, T) where `E` is the element type of `X`, defaults to `Any`. * 5. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`: * (a) replace `Ts` with fresh type variables `Xs` @@ -60,7 +67,7 @@ object Checkable { def checkable(X: Type, P: Type)(implicit ctx: Context): Boolean = { def isAbstract(P: Type) = !P.dealias.typeSymbol.isClass - def replaceBinderMap(implicit ctx: Context) = new TypeMap { + def replaceP(implicit ctx: Context) = new TypeMap { def apply(tp: Type) = tp match { case tref: TypeRef if !tref.typeSymbol.isClass && tref.symbol.is(Case) => WildcardType @@ -70,6 +77,17 @@ object Checkable { } } + def replaceX(implicit ctx: Context) = new TypeMap { + def apply(tp: Type) = tp match { + case tref: TypeRef + if !tref.typeSymbol.isClass && tref.symbol.is(Case) => + if (variance == 1) tref.info.hiBound + else if (variance == -1) tref.info.loBound + else OrType(defn.AnyType, defn.NothingType) + case _ => mapOver(tp) + } + } + def isClassDetermined(X: Type, P: AppliedType)(implicit ctx: Context) = { val AppliedType(tycon, _) = P val typeLambda = tycon.ensureHK.asInstanceOf[TypeLambda] @@ -85,6 +103,7 @@ object Checkable { val res = isFullyDefined(P1, ForceDegree.noBottom) && P1 <:< P debug.println("P1 : " + P1) debug.println("P1 <:< P = " + res) + res } @@ -105,7 +124,7 @@ object Checkable { case _ => true }) - val res = recur(X.widen, replaceBinderMap.apply(P)) + val res = recur(replaceX.apply(X.widen), replaceP.apply(P)) debug.println(i"checking ${X.show} isInstanceOf ${P} = $res") diff --git a/tests/neg-custom-args/isInstanceOf/enum-approx2.scala b/tests/neg-custom-args/isInstanceOf/enum-approx2.scala new file mode 100644 index 000000000000..ec72af6ed9ad --- /dev/null +++ b/tests/neg-custom-args/isInstanceOf/enum-approx2.scala @@ -0,0 +1,9 @@ +sealed trait Exp[T] +case class Fun[A, B](f: Exp[A => B]) extends Exp[A => B] + +class Test { + def eval[T](e: Exp[T]) = e match { + case Fun(x: Fun[Int, Double]) => ??? // error + case Fun(x: Exp[Int => String]) => ??? // error + } +} \ No newline at end of file From e836b713df04b561e9301ec4cbea95091d4bd2dc Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Tue, 6 Mar 2018 11:39:56 +0100 Subject: [PATCH 20/30] WIP - address review --- .../src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala | 2 +- tests/neg-custom-args/isInstanceOf/3324d.scala | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index 87ef81d87a84..7a8e8a976b7e 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -48,7 +48,7 @@ object Checkable { * (b) replace pattern binder types (e.g., `_$1`) in X: * - variance = 1 : hiBound * - variance = -1 : loBound - * - variance = 0 : OrType(Any, Nothing) + * - variance = 0 : OrType(Any, Nothing) // TODO: use original type param bounds * * Then check: * diff --git a/tests/neg-custom-args/isInstanceOf/3324d.scala b/tests/neg-custom-args/isInstanceOf/3324d.scala index e9b608a77cb1..56b579279000 100644 --- a/tests/neg-custom-args/isInstanceOf/3324d.scala +++ b/tests/neg-custom-args/isInstanceOf/3324d.scala @@ -3,6 +3,7 @@ class Test { x match { case _: List[Int @unchecked] => 5 + case _: List[Int] @unchecked => 5 } def foo(x: Any): Boolean = From 7adc838bb542bda1f811c0d162b320dc82fff230 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Wed, 7 Mar 2018 09:36:12 +0100 Subject: [PATCH 21/30] add more tests --- .../dotc/transform/IsInstanceOfChecker.scala | 23 ++++++++++++++----- .../dotc/transform/SyntheticMethods.scala | 5 ++++ .../neg-custom-args/isInstanceOf/3324g.scala | 19 +++++++++++++++ .../isInstanceOf/4075.scala.ignore | 22 ++++++++++++++++++ .../neg-custom-args/isInstanceOf/Result.scala | 11 +-------- 5 files changed, 64 insertions(+), 16 deletions(-) create mode 100644 tests/neg-custom-args/isInstanceOf/3324g.scala create mode 100644 tests/neg-custom-args/isInstanceOf/4075.scala.ignore diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index 7a8e8a976b7e..14187681a562 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -4,7 +4,7 @@ package transform import util.Positions._ import MegaPhase.MiniPhase import core._ -import Contexts.Context, Types._, Decorators._, Symbols._, typer._, ast._ +import Contexts.Context, Types._, Decorators._, Symbols._, typer._, ast._, NameKinds._ import TypeUtils._, Flags._ import config.Printers.{ transforms => debug } @@ -18,7 +18,7 @@ class IsInstanceOfChecker extends MiniPhase { override def transformTypeApply(tree: TypeApply)(implicit ctx: Context): Tree = { def ensureCheckable(qual: Tree, pt: Tree): Tree = { - if (!Checkable.checkable(qual.tpe, pt.tpe)) + if (!Checkable.checkable(qual.tpe, pt.tpe, tree.pos)) ctx.warning( s"the type test for ${pt.show} cannot be checked at runtime", tree.pos @@ -58,13 +58,14 @@ object Checkable { * 4. if `P = Array[T]`, checkable(E, T) where `E` is the element type of `X`, defaults to `Any`. * 5. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`: * (a) replace `Ts` with fresh type variables `Xs` - * (b) constrain `Xs` with `pre.F[Xs] <:< X` (may fail) + * (b) constrain `Xs` with `pre.F[Xs] <:< X`, + * if `X` cannot be uniquely determined, instantiate `X` with fresh type symbol. * (c) instantiate Xs and check `pre.F[Xs] <:< P` * 6. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2). * 7. if `P` is a refinement type, FALSE * 8. otherwise, TRUE */ - def checkable(X: Type, P: Type)(implicit ctx: Context): Boolean = { + def checkable(X: Type, P: Type, pos: Position)(implicit ctx: Context): Boolean = { def isAbstract(P: Type) = !P.dealias.typeSymbol.isClass def replaceP(implicit ctx: Context) = new TypeMap { @@ -100,7 +101,17 @@ object Checkable { P1 <:< X // may fail, ignore - val res = isFullyDefined(P1, ForceDegree.noBottom) && P1 <:< P + tvars.foreach { case tvar: TypeVar => + val bounds = ctx.typerState.constraint.entry(tvar.origin) + if (bounds.loBound =:= bounds.hiBound) + tvar.instantiateWith(bounds.loBound) + else { + val wildCard = ctx.newSymbol(ctx.owner, WildcardParamName.fresh().toTypeName, Case, tvar.origin.underlying, coord = pos) + tvar.instantiateWith(wildCard.typeRef) + } + } + + val res = P1 <:< P debug.println("P1 : " + P1) debug.println("P1 <:< P = " + res) @@ -119,7 +130,7 @@ object Checkable { case tpe: AppliedType => isClassDetermined(X, tpe)(ctx.fresh.setNewTyperState()) case AndType(tp1, tp2) => recur(X, tp1) && recur(X, tp2) case OrType(tp1, tp2) => recur(X, tp1) && recur(X, tp2) - case AnnotatedType(t, an) => recur(X, t) + case AnnotatedType(t, _) => recur(X, t) case _: RefinedType => false case _ => true }) diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala index 47e97e519a68..86dcbbed2196 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala @@ -159,6 +159,9 @@ class SyntheticMethods(thisPhase: DenotTransformer) { * ``` * * If `C` is a value class the initial `eq` test is omitted. + * + * `@unchecked` is needed for parametric case classes. + * */ def equalsBody(that: Tree)(implicit ctx: Context): Tree = { val thatAsClazz = ctx.newSymbol(ctx.owner, nme.x_0, Synthetic, clazzType, coord = ctx.owner.pos) // x$0 @@ -253,6 +256,8 @@ class SyntheticMethods(thisPhase: DenotTransformer) { * ``` * def canEqual(that: Any) = that.isInstanceOf[C @unchecked] * ``` + * + * `@unchecked` is needed for parametric case classes. */ def canEqualBody(that: Tree): Tree = that.isInstance(AnnotatedType(clazzType, Annotation(defn.UncheckedAnnot))) diff --git a/tests/neg-custom-args/isInstanceOf/3324g.scala b/tests/neg-custom-args/isInstanceOf/3324g.scala new file mode 100644 index 000000000000..734c4c1bab97 --- /dev/null +++ b/tests/neg-custom-args/isInstanceOf/3324g.scala @@ -0,0 +1,19 @@ +class Test { + trait A[+T] + class B[T] extends A[T] + class C[T] extends B[Any] with A[T] + + def foo[T](c: C[T]): Unit = c match { + case _: B[T] => // error + } + + def bar[T](b: B[T]): Unit = b match { + case _: A[T] => + } + + def quux[T](a: A[T]): Unit = a match { + case _: B[T] => // error + } + + quux(new C[Int]) +} diff --git a/tests/neg-custom-args/isInstanceOf/4075.scala.ignore b/tests/neg-custom-args/isInstanceOf/4075.scala.ignore new file mode 100644 index 000000000000..4fcbaf0331fe --- /dev/null +++ b/tests/neg-custom-args/isInstanceOf/4075.scala.ignore @@ -0,0 +1,22 @@ +object Test { + trait Foo + case class One[+T](fst: T) + + def bad[T <: Foo](e: One[T])(x: T) = e match { + case foo: One[a] => + x.isInstanceOf[a] // error + val y: Any = ??? + y.isInstanceOf[a] // error + } +} + +object Test2 { + case class One[T](fst: T) + + def bad[T](e: One[T])(x: T) = e match { + case foo: One[a] => + x.isInstanceOf[a] // error + val y: Any = ??? + y.isInstanceOf[a] // error + } +} diff --git a/tests/neg-custom-args/isInstanceOf/Result.scala b/tests/neg-custom-args/isInstanceOf/Result.scala index dd91888f7475..d609bcc561a2 100644 --- a/tests/neg-custom-args/isInstanceOf/Result.scala +++ b/tests/neg-custom-args/isInstanceOf/Result.scala @@ -1,20 +1,11 @@ -import scala.util.control.NonFatal object p { + // test parametric case classes, which synthesis `canEqual` and `equals` enum Result[+T, +E] { case OK [T](x: T) extends Result[T, Nothing] case Err[E](e: E) extends Result[Nothing, E] } - type Try[T] = Result[T, Throwable] - object Try { - def apply[T](x: => T): Try[T] = - try Result.OK(x) - catch { - case NonFatal(ex) => Result.Err(ex) - } - } - def foo(x: Any): Boolean = x.isInstanceOf[List[String]] // error } From 734afb37218104f1dafab65df6f435b22955f200 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Wed, 7 Mar 2018 10:09:45 +0100 Subject: [PATCH 22/30] instantiate if current class is final --- .../src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index 14187681a562..862d89eb0123 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -105,7 +105,9 @@ object Checkable { val bounds = ctx.typerState.constraint.entry(tvar.origin) if (bounds.loBound =:= bounds.hiBound) tvar.instantiateWith(bounds.loBound) - else { + else if (tycon.classSymbol.is(Final)) // 3324g.scala cannot happen because of final + instantiateSelected(P1, tvar :: Nil) + else { // see 3324g.scala val wildCard = ctx.newSymbol(ctx.owner, WildcardParamName.fresh().toTypeName, Case, tvar.origin.underlying, coord = pos) tvar.instantiateWith(wildCard.typeRef) } From 5348b31f5384ed45876414ded770813c7c5f7ef3 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Wed, 7 Mar 2018 11:19:49 +0100 Subject: [PATCH 23/30] fix test --- .../dotc/transform/IsInstanceOfChecker.scala | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index 862d89eb0123..7e43a0b749ec 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -101,17 +101,24 @@ object Checkable { P1 <:< X // may fail, ignore - tvars.foreach { case tvar: TypeVar => - val bounds = ctx.typerState.constraint.entry(tvar.origin) - if (bounds.loBound =:= bounds.hiBound) - tvar.instantiateWith(bounds.loBound) - else if (tycon.classSymbol.is(Final)) // 3324g.scala cannot happen because of final - instantiateSelected(P1, tvar :: Nil) - else { // see 3324g.scala - val wildCard = ctx.newSymbol(ctx.owner, WildcardParamName.fresh().toTypeName, Case, tvar.origin.underlying, coord = pos) - tvar.instantiateWith(wildCard.typeRef) + // 3324g.scala cannot happen in such cases + def canInstantiate = + tycon.classSymbol.is(Final) || + !X.classSymbol.is(Trait) || + X.classSymbol.typeParams.isEmpty + + if (canInstantiate) + maximizeType(P1, pos, fromScala2x = true) // use `fromScala2x = true` to force instantiate invariant tvars + else + tvars.foreach { case tvar: TypeVar => + val bounds = ctx.typerState.constraint.entry(tvar.origin) + if (bounds.loBound =:= bounds.hiBound) + tvar.instantiateWith(bounds.loBound) + else { // 3324g.scala + val wildCard = ctx.newSymbol(ctx.owner, WildcardParamName.fresh().toTypeName, Case, tvar.origin.underlying, coord = pos) + tvar.instantiateWith(wildCard.typeRef) + } } - } val res = P1 <:< P debug.println("P1 : " + P1) From 0d84bcceac77af0e2f36956957f10880c5242423 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Thu, 8 Mar 2018 15:36:38 +0100 Subject: [PATCH 24/30] ignore GADT related issues for now --- .../dotc/transform/IsInstanceOfChecker.scala | 24 ++----------------- .../neg-custom-args/isInstanceOf/3324g.scala | 2 +- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index 7e43a0b749ec..ba49a737a093 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -58,8 +58,7 @@ object Checkable { * 4. if `P = Array[T]`, checkable(E, T) where `E` is the element type of `X`, defaults to `Any`. * 5. if `P` is `pre.F[Ts]` and `pre.F` refers to a class which is not `Array`: * (a) replace `Ts` with fresh type variables `Xs` - * (b) constrain `Xs` with `pre.F[Xs] <:< X`, - * if `X` cannot be uniquely determined, instantiate `X` with fresh type symbol. + * (b) constrain `Xs` with `pre.F[Xs] <:< X` * (c) instantiate Xs and check `pre.F[Xs] <:< P` * 6. if `P = T1 | T2` or `P = T1 & T2`, checkable(X, T1) && checkable(X, T2). * 7. if `P` is a refinement type, FALSE @@ -101,26 +100,7 @@ object Checkable { P1 <:< X // may fail, ignore - // 3324g.scala cannot happen in such cases - def canInstantiate = - tycon.classSymbol.is(Final) || - !X.classSymbol.is(Trait) || - X.classSymbol.typeParams.isEmpty - - if (canInstantiate) - maximizeType(P1, pos, fromScala2x = true) // use `fromScala2x = true` to force instantiate invariant tvars - else - tvars.foreach { case tvar: TypeVar => - val bounds = ctx.typerState.constraint.entry(tvar.origin) - if (bounds.loBound =:= bounds.hiBound) - tvar.instantiateWith(bounds.loBound) - else { // 3324g.scala - val wildCard = ctx.newSymbol(ctx.owner, WildcardParamName.fresh().toTypeName, Case, tvar.origin.underlying, coord = pos) - tvar.instantiateWith(wildCard.typeRef) - } - } - - val res = P1 <:< P + val res = isFullyDefined(P1, ForceDegree.noBottom) && P1 <:< P debug.println("P1 : " + P1) debug.println("P1 <:< P = " + res) diff --git a/tests/neg-custom-args/isInstanceOf/3324g.scala b/tests/neg-custom-args/isInstanceOf/3324g.scala index 734c4c1bab97..cd60cfed45ee 100644 --- a/tests/neg-custom-args/isInstanceOf/3324g.scala +++ b/tests/neg-custom-args/isInstanceOf/3324g.scala @@ -12,7 +12,7 @@ class Test { } def quux[T](a: A[T]): Unit = a match { - case _: B[T] => // error + case _: B[T] => // err-or: cannot handle this for now } quux(new C[Int]) From f3d1d904712d0511a07f56b6db405d1ce695c39f Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Thu, 8 Mar 2018 17:49:51 +0100 Subject: [PATCH 25/30] add classTag test --- tests/neg-custom-args/isInstanceOf/classTag.scala | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/neg-custom-args/isInstanceOf/classTag.scala diff --git a/tests/neg-custom-args/isInstanceOf/classTag.scala b/tests/neg-custom-args/isInstanceOf/classTag.scala new file mode 100644 index 000000000000..5095fa4ec30c --- /dev/null +++ b/tests/neg-custom-args/isInstanceOf/classTag.scala @@ -0,0 +1,13 @@ +import scala.reflect.ClassTag + +object IsInstanceOfClassTag { + def safeCast[T: ClassTag](x: Any): Option[T] = { + x match { + case x: T => Some(x) + case _ => None + } + } + + def foo(x: Any): Boolean = + x.isInstanceOf[List[String]] // error +} \ No newline at end of file From ac0ec6d7ac8515ac738b839f9348a374f9936fd2 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Fri, 9 Mar 2018 13:09:59 +0100 Subject: [PATCH 26/30] address review --- tests/neg-custom-args/isInstanceOf/3324g.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/neg-custom-args/isInstanceOf/3324g.scala b/tests/neg-custom-args/isInstanceOf/3324g.scala index cd60cfed45ee..423e56eee4b7 100644 --- a/tests/neg-custom-args/isInstanceOf/3324g.scala +++ b/tests/neg-custom-args/isInstanceOf/3324g.scala @@ -12,7 +12,7 @@ class Test { } def quux[T](a: A[T]): Unit = a match { - case _: B[T] => // err-or: cannot handle this for now + case _: B[T] => // should be an error!! } quux(new C[Int]) From 2b23c69bb349cd495f46b1374caf473bb2907237 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Fri, 23 Mar 2018 13:13:45 +0100 Subject: [PATCH 27/30] address review --- .../tools/dotc/transform/IsInstanceOfChecker.scala | 8 ++++---- tests/neg-custom-args/isInstanceOf/3324h.scala | 10 ++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 tests/neg-custom-args/isInstanceOf/3324h.scala diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index ba49a737a093..cce3303afef9 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -28,9 +28,8 @@ class IsInstanceOfChecker extends MiniPhase { } tree.fun match { - case fn: Select if fn.symbol == defn.Any_typeTest => - ensureCheckable(fn.qualifier, tree.args.head) - case fn: Select if fn.symbol == defn.Any_isInstanceOf => + case fn: Select + if fn.symbol == defn.Any_typeTest || fn.symbol == defn.Any_isInstanceOf => ensureCheckable(fn.qualifier, tree.args.head) case _ => tree } @@ -66,11 +65,12 @@ object Checkable { */ def checkable(X: Type, P: Type, pos: Position)(implicit ctx: Context): Boolean = { def isAbstract(P: Type) = !P.dealias.typeSymbol.isClass + def isPatternTypeSymbol(sym: Symbol) = !sym.isClass && sym.is(Case) def replaceP(implicit ctx: Context) = new TypeMap { def apply(tp: Type) = tp match { case tref: TypeRef - if !tref.typeSymbol.isClass && tref.symbol.is(Case) => WildcardType + if isPatternTypeSymbol(tref.typeSymbol) => WildcardType case AnnotatedType(_, annot) if annot.symbol == defn.UncheckedAnnot => WildcardType case _ => mapOver(tp) diff --git a/tests/neg-custom-args/isInstanceOf/3324h.scala b/tests/neg-custom-args/isInstanceOf/3324h.scala new file mode 100644 index 000000000000..50a98eddb069 --- /dev/null +++ b/tests/neg-custom-args/isInstanceOf/3324h.scala @@ -0,0 +1,10 @@ +object Test { + trait Marker + def foo[T](x: T) = x match { + case _: (T & Marker) => // no warning + // case _: T with Marker => // scalac emits a warning + case _ => + } + + def bar(x: Any) = x.isInstanceOf[List[String]] // error +} \ No newline at end of file From c31c917d54c3cc47851b402835fdfe2a66d3dffc Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Fri, 23 Mar 2018 20:33:58 +0100 Subject: [PATCH 28/30] use isPatternTypeSymbol to avoid duplicate --- .../src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala | 2 +- tests/neg-custom-args/isInstanceOf/3324h.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index cce3303afef9..0955752bebc3 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -80,7 +80,7 @@ object Checkable { def replaceX(implicit ctx: Context) = new TypeMap { def apply(tp: Type) = tp match { case tref: TypeRef - if !tref.typeSymbol.isClass && tref.symbol.is(Case) => + if isPatternTypeSymbol(tref.typeSymbol) => if (variance == 1) tref.info.hiBound else if (variance == -1) tref.info.loBound else OrType(defn.AnyType, defn.NothingType) diff --git a/tests/neg-custom-args/isInstanceOf/3324h.scala b/tests/neg-custom-args/isInstanceOf/3324h.scala index 50a98eddb069..85344561e5ab 100644 --- a/tests/neg-custom-args/isInstanceOf/3324h.scala +++ b/tests/neg-custom-args/isInstanceOf/3324h.scala @@ -2,7 +2,7 @@ object Test { trait Marker def foo[T](x: T) = x match { case _: (T & Marker) => // no warning - // case _: T with Marker => // scalac emits a warning + case _: T with Marker => // scalac emits a warning case _ => } From a05074ddfd36865855d970019e7955d4b149023a Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Mon, 26 Mar 2018 21:45:47 +0200 Subject: [PATCH 29/30] move pos test to pos-special/isInstanceOf --- compiler/test/dotty/tools/dotc/CompilationTests.scala | 1 + .../{neg-custom-args => pos-special}/isInstanceOf/3324c.scala | 2 -- .../{neg-custom-args => pos-special}/isInstanceOf/3324d.scala | 3 --- .../{neg-custom-args => pos-special}/isInstanceOf/3324e.scala | 3 --- .../{neg-custom-args => pos-special}/isInstanceOf/3324h.scala | 2 -- .../{neg-custom-args => pos-special}/isInstanceOf/Result.scala | 3 --- .../isInstanceOf/classTag.scala | 3 --- tests/{neg-custom-args => pos-special}/isInstanceOf/gadt.scala | 3 --- .../{neg-custom-args => pos-special}/isInstanceOf/t2755.scala | 3 --- 9 files changed, 1 insertion(+), 22 deletions(-) rename tests/{neg-custom-args => pos-special}/isInstanceOf/3324c.scala (73%) rename tests/{neg-custom-args => pos-special}/isInstanceOf/3324d.scala (63%) rename tests/{neg-custom-args => pos-special}/isInstanceOf/3324e.scala (72%) rename tests/{neg-custom-args => pos-special}/isInstanceOf/3324h.scala (75%) rename tests/{neg-custom-args => pos-special}/isInstanceOf/Result.scala (74%) rename tests/{neg-custom-args => pos-special}/isInstanceOf/classTag.scala (71%) rename tests/{neg-custom-args => pos-special}/isInstanceOf/gadt.scala (93%) rename tests/{neg-custom-args => pos-special}/isInstanceOf/t2755.scala (96%) diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index d7608d270910..f9f626cc4c7b 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -78,6 +78,7 @@ class CompilationTests extends ParallelTesting { ) + compileFilesInDir("tests/pos-special/spec-t5545", defaultOptions) + compileFilesInDir("tests/pos-special/strawman-collections", defaultOptions) + + compileFilesInDir("tests/pos-special/isInstanceOf", defaultOptions.and("-Xfatal-warnings")) + compileFile("scala2-library/src/library/scala/collection/immutable/IndexedSeq.scala", defaultOptions) + compileFile("scala2-library/src/library/scala/collection/parallel/mutable/ParSetLike.scala", defaultOptions) + compileList( diff --git a/tests/neg-custom-args/isInstanceOf/3324c.scala b/tests/pos-special/isInstanceOf/3324c.scala similarity index 73% rename from tests/neg-custom-args/isInstanceOf/3324c.scala rename to tests/pos-special/isInstanceOf/3324c.scala index 129d3f8d89f2..ad38af34e265 100644 --- a/tests/neg-custom-args/isInstanceOf/3324c.scala +++ b/tests/pos-special/isInstanceOf/3324c.scala @@ -5,6 +5,4 @@ class Test { def f(x: B[Int]) = x match { case _: A[Int] if true => } def g(x: A[Int]) = x match { case _: B[Int] => } - - def foo(x: Any) = x.isInstanceOf[List[String]] // error } diff --git a/tests/neg-custom-args/isInstanceOf/3324d.scala b/tests/pos-special/isInstanceOf/3324d.scala similarity index 63% rename from tests/neg-custom-args/isInstanceOf/3324d.scala rename to tests/pos-special/isInstanceOf/3324d.scala index 56b579279000..11744ae5abec 100644 --- a/tests/neg-custom-args/isInstanceOf/3324d.scala +++ b/tests/pos-special/isInstanceOf/3324d.scala @@ -5,7 +5,4 @@ class Test { case _: List[Int @unchecked] => 5 case _: List[Int] @unchecked => 5 } - - def foo(x: Any): Boolean = - x.isInstanceOf[List[String]] // error } \ No newline at end of file diff --git a/tests/neg-custom-args/isInstanceOf/3324e.scala b/tests/pos-special/isInstanceOf/3324e.scala similarity index 72% rename from tests/neg-custom-args/isInstanceOf/3324e.scala rename to tests/pos-special/isInstanceOf/3324e.scala index 905c0f44d350..b31c809eaaf7 100644 --- a/tests/neg-custom-args/isInstanceOf/3324e.scala +++ b/tests/pos-special/isInstanceOf/3324e.scala @@ -14,7 +14,4 @@ class C[T] { z match { case x: F[T] => } - - def foo(x: Any): Boolean = - x.isInstanceOf[List[String]] // error } diff --git a/tests/neg-custom-args/isInstanceOf/3324h.scala b/tests/pos-special/isInstanceOf/3324h.scala similarity index 75% rename from tests/neg-custom-args/isInstanceOf/3324h.scala rename to tests/pos-special/isInstanceOf/3324h.scala index 85344561e5ab..ec9a9d20e05c 100644 --- a/tests/neg-custom-args/isInstanceOf/3324h.scala +++ b/tests/pos-special/isInstanceOf/3324h.scala @@ -5,6 +5,4 @@ object Test { case _: T with Marker => // scalac emits a warning case _ => } - - def bar(x: Any) = x.isInstanceOf[List[String]] // error } \ No newline at end of file diff --git a/tests/neg-custom-args/isInstanceOf/Result.scala b/tests/pos-special/isInstanceOf/Result.scala similarity index 74% rename from tests/neg-custom-args/isInstanceOf/Result.scala rename to tests/pos-special/isInstanceOf/Result.scala index d609bcc561a2..c8a403735219 100644 --- a/tests/neg-custom-args/isInstanceOf/Result.scala +++ b/tests/pos-special/isInstanceOf/Result.scala @@ -5,7 +5,4 @@ object p { case OK [T](x: T) extends Result[T, Nothing] case Err[E](e: E) extends Result[Nothing, E] } - - def foo(x: Any): Boolean = - x.isInstanceOf[List[String]] // error } diff --git a/tests/neg-custom-args/isInstanceOf/classTag.scala b/tests/pos-special/isInstanceOf/classTag.scala similarity index 71% rename from tests/neg-custom-args/isInstanceOf/classTag.scala rename to tests/pos-special/isInstanceOf/classTag.scala index 5095fa4ec30c..fc8be4526958 100644 --- a/tests/neg-custom-args/isInstanceOf/classTag.scala +++ b/tests/pos-special/isInstanceOf/classTag.scala @@ -7,7 +7,4 @@ object IsInstanceOfClassTag { case _ => None } } - - def foo(x: Any): Boolean = - x.isInstanceOf[List[String]] // error } \ No newline at end of file diff --git a/tests/neg-custom-args/isInstanceOf/gadt.scala b/tests/pos-special/isInstanceOf/gadt.scala similarity index 93% rename from tests/neg-custom-args/isInstanceOf/gadt.scala rename to tests/pos-special/isInstanceOf/gadt.scala index 7a1ccc3320e4..6f661ce152c3 100644 --- a/tests/neg-custom-args/isInstanceOf/gadt.scala +++ b/tests/pos-special/isInstanceOf/gadt.scala @@ -34,7 +34,4 @@ object Test { } eval(exp)(Env.empty) - - def foo(x: Any): Boolean = - x.isInstanceOf[List[String]] // error } diff --git a/tests/neg-custom-args/isInstanceOf/t2755.scala b/tests/pos-special/isInstanceOf/t2755.scala similarity index 96% rename from tests/neg-custom-args/isInstanceOf/t2755.scala rename to tests/pos-special/isInstanceOf/t2755.scala index daba75500eaf..8d10b567346b 100644 --- a/tests/neg-custom-args/isInstanceOf/t2755.scala +++ b/tests/pos-special/isInstanceOf/t2755.scala @@ -55,7 +55,4 @@ object Test { println(f3(Array(1L))) println(f3(null)) } - - def foo(x: Any): Boolean = - x.isInstanceOf[List[String]] // error } From 2b00b7a8b352f1adbedfca70946a2170c8148b94 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Tue, 27 Mar 2018 11:26:51 +0200 Subject: [PATCH 30/30] fix tests --- .../src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala | 2 +- compiler/test/dotty/tools/dotc/CompilationTests.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala index 0955752bebc3..912be3f42756 100644 --- a/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/IsInstanceOfChecker.scala @@ -90,7 +90,7 @@ object Checkable { def isClassDetermined(X: Type, P: AppliedType)(implicit ctx: Context) = { val AppliedType(tycon, _) = P - val typeLambda = tycon.ensureHK.asInstanceOf[TypeLambda] + val typeLambda = tycon.ensureLambdaSub.asInstanceOf[TypeLambda] val tvars = constrained(typeLambda, untpd.EmptyTree, alwaysAddTypeVars = true)._2.map(_.tpe) val P1 = tycon.appliedTo(tvars) diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index f9f626cc4c7b..ca8dbd7b05ad 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -78,7 +78,7 @@ class CompilationTests extends ParallelTesting { ) + compileFilesInDir("tests/pos-special/spec-t5545", defaultOptions) + compileFilesInDir("tests/pos-special/strawman-collections", defaultOptions) + - compileFilesInDir("tests/pos-special/isInstanceOf", defaultOptions.and("-Xfatal-warnings")) + + compileFilesInDir("tests/pos-special/isInstanceOf", allowDeepSubtypes.and("-Xfatal-warnings")) + compileFile("scala2-library/src/library/scala/collection/immutable/IndexedSeq.scala", defaultOptions) + compileFile("scala2-library/src/library/scala/collection/parallel/mutable/ParSetLike.scala", defaultOptions) + compileList(