From 05ffaaccf4dea16281fc166b8fa10bbcf9d57078 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Thu, 27 Jul 2017 18:49:45 +0200 Subject: [PATCH] Fix #2430: Make Erasure pass Ycheck During Erasure, a value class A with underlying type U has its type semi-erased to ErasedValueType(A, [semi-erasure of U]). To make Erasure typecheck, special cast functions A.evt2u$ and A.u2evt$ are used to cast between the underlying type and the class EVT type. Before this commit, this conversion was not always correctly done for nested value classes, this did not lead to problems in practice but broke -Ycheck:erasure. --- .../dotty/tools/dotc/transform/Erasure.scala | 44 +++++++++++++------ compiler/test/dotc/tests.scala | 2 +- .../tools/vulpix/TestConfiguration.scala | 2 +- 3 files changed, 33 insertions(+), 15 deletions(-) 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"