diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 5837b2b6904a..b26ea538646f 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -239,26 +239,44 @@ object Erasure { * Casts from and to ErasedValueType are special, see the explanation * in ExtensionMethods#transform. */ - def cast(tree: Tree, pt: Type)(implicit ctx: Context): Tree = { + def cast(tree: Tree, pt: Type)(implicit ctx: Context): Tree = ctx.traceIndented(i"cast ${tree.tpe.widen} --> $pt", show = true) { + def wrap(tycon: TypeRef) = + ref(u2evt(tycon.typeSymbol.asClass)).appliedTo(tree) + def unwrap(tycon: TypeRef) = + ref(evt2u(tycon.typeSymbol.asClass)).appliedTo(tree) + + assert(!pt.isInstanceOf[SingletonType], pt) if (pt isRef defn.UnitClass) unbox(tree, pt) - else (tree.tpe, pt) match { + else (tree.tpe.widen, pt) match { case (JavaArrayType(treeElem), JavaArrayType(ptElem)) if treeElem.widen.isPrimitiveValueType && !ptElem.isPrimitiveValueType => // See SI-2386 for one example of when this might be necessary. cast(ref(defn.runtimeMethodRef(nme.toObjectArray)).appliedTo(tree), pt) - case (_, ErasedValueType(tycon, _)) => - ref(u2evt(tycon.symbol.asClass)).appliedTo(tree) - case _ => - tree.tpe.widen match { - case ErasedValueType(tycon, _) => - ref(evt2u(tycon.symbol.asClass)).appliedTo(tree) - case _ => - if (pt.isPrimitiveValueType) - primitiveConversion(tree, pt.classSymbol) - else - tree.asInstance(pt) + + // When casting between two EVTs, we need to check which one underlies the other to determine + // wheter u2evt or evt2u should be used. + case (tp1 @ ErasedValueType(tycon1, underlying1), tp2 @ ErasedValueType(tycon2, underlying2)) => + if (tp1 <:< underlying2) + // Cast EVT(tycon1, underlying1) to EVT(tycon2, EVT(tycon1, underlying1)) + wrap(tycon2) + else { + assert(underlying1 <:< tp2, i"Non-sensical cast between unrelated types $tp1 and $tp2") + // Cast EVT(tycon1, EVT(tycon2, underlying2)) to EVT(tycon2, underlying2) + unwrap(tycon1) } + + // When only one type is an EVT then we already know that the other one is the underlying + case (_, ErasedValueType(tycon2, _)) => + wrap(tycon2) + case (ErasedValueType(tycon1, _), _) => + unwrap(tycon1) + + case _ => + if (pt.isPrimitiveValueType) + primitiveConversion(tree, pt.classSymbol) + else + tree.asInstance(pt) } } diff --git a/compiler/test/dotc/tests.scala b/compiler/test/dotc/tests.scala index 1fc88c3d003b..40b55c6a9343 100644 --- a/compiler/test/dotc/tests.scala +++ b/compiler/test/dotc/tests.scala @@ -68,7 +68,7 @@ class tests extends CompilerTest { } implicit val defaultOptions: List[String] = noCheckOptions ++ { - if (dotty.Properties.isRunByDrone) List("-Ycheck:tailrec,resolveSuper,mixin,getClass,restoreScopes,labelDef") // should be Ycheck:all, but #725 + if (dotty.Properties.isRunByDrone) List("-Ycheck:tailrec,resolveSuper,erasure,mixin,getClass,restoreScopes,labelDef") // should be Ycheck:all, but #725 else List("-Ycheck:tailrec,resolveSuper,mixin,restoreScopes,labelDef,simplify") } ++ checkOptions ++ classPath diff --git a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala index a3179d1af1c3..44dabbabc93f 100644 --- a/compiler/test/dotty/tools/vulpix/TestConfiguration.scala +++ b/compiler/test/dotty/tools/vulpix/TestConfiguration.scala @@ -50,7 +50,7 @@ object TestConfiguration { Array("-classpath", paths) } - private val yCheckOptions = Array("-Ycheck:tailrec,resolveSuper,mixin,getClass,restoreScopes,labelDef") + private val yCheckOptions = Array("-Ycheck:tailrec,resolveSuper,erasure,mixin,getClass,restoreScopes,labelDef") val defaultUnoptimised = noCheckOptions ++ checkOptions ++ yCheckOptions ++ classPath val defaultOptimised = defaultUnoptimised :+ "-optimise"