From 4bdcaa85f96c5147c0886478fa853d507df22537 Mon Sep 17 00:00:00 2001 From: liu fengyun Date: Wed, 7 Mar 2018 09:36:12 +0100 Subject: [PATCH] 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 46d16ccebaa9..5f659755f34a 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 }