Skip to content

Commit 05ffaac

Browse files
committed
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.
1 parent 3d9080c commit 05ffaac

File tree

3 files changed

+33
-15
lines changed

3 files changed

+33
-15
lines changed

compiler/src/dotty/tools/dotc/transform/Erasure.scala

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -239,26 +239,44 @@ object Erasure {
239239
* Casts from and to ErasedValueType are special, see the explanation
240240
* in ExtensionMethods#transform.
241241
*/
242-
def cast(tree: Tree, pt: Type)(implicit ctx: Context): Tree = {
242+
def cast(tree: Tree, pt: Type)(implicit ctx: Context): Tree = ctx.traceIndented(i"cast ${tree.tpe.widen} --> $pt", show = true) {
243+
def wrap(tycon: TypeRef) =
244+
ref(u2evt(tycon.typeSymbol.asClass)).appliedTo(tree)
245+
def unwrap(tycon: TypeRef) =
246+
ref(evt2u(tycon.typeSymbol.asClass)).appliedTo(tree)
247+
248+
243249
assert(!pt.isInstanceOf[SingletonType], pt)
244250
if (pt isRef defn.UnitClass) unbox(tree, pt)
245-
else (tree.tpe, pt) match {
251+
else (tree.tpe.widen, pt) match {
246252
case (JavaArrayType(treeElem), JavaArrayType(ptElem))
247253
if treeElem.widen.isPrimitiveValueType && !ptElem.isPrimitiveValueType =>
248254
// See SI-2386 for one example of when this might be necessary.
249255
cast(ref(defn.runtimeMethodRef(nme.toObjectArray)).appliedTo(tree), pt)
250-
case (_, ErasedValueType(tycon, _)) =>
251-
ref(u2evt(tycon.symbol.asClass)).appliedTo(tree)
252-
case _ =>
253-
tree.tpe.widen match {
254-
case ErasedValueType(tycon, _) =>
255-
ref(evt2u(tycon.symbol.asClass)).appliedTo(tree)
256-
case _ =>
257-
if (pt.isPrimitiveValueType)
258-
primitiveConversion(tree, pt.classSymbol)
259-
else
260-
tree.asInstance(pt)
256+
257+
// When casting between two EVTs, we need to check which one underlies the other to determine
258+
// wheter u2evt or evt2u should be used.
259+
case (tp1 @ ErasedValueType(tycon1, underlying1), tp2 @ ErasedValueType(tycon2, underlying2)) =>
260+
if (tp1 <:< underlying2)
261+
// Cast EVT(tycon1, underlying1) to EVT(tycon2, EVT(tycon1, underlying1))
262+
wrap(tycon2)
263+
else {
264+
assert(underlying1 <:< tp2, i"Non-sensical cast between unrelated types $tp1 and $tp2")
265+
// Cast EVT(tycon1, EVT(tycon2, underlying2)) to EVT(tycon2, underlying2)
266+
unwrap(tycon1)
261267
}
268+
269+
// When only one type is an EVT then we already know that the other one is the underlying
270+
case (_, ErasedValueType(tycon2, _)) =>
271+
wrap(tycon2)
272+
case (ErasedValueType(tycon1, _), _) =>
273+
unwrap(tycon1)
274+
275+
case _ =>
276+
if (pt.isPrimitiveValueType)
277+
primitiveConversion(tree, pt.classSymbol)
278+
else
279+
tree.asInstance(pt)
262280
}
263281
}
264282

compiler/test/dotc/tests.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ class tests extends CompilerTest {
6868
}
6969

7070
implicit val defaultOptions: List[String] = noCheckOptions ++ {
71-
if (dotty.Properties.isRunByDrone) List("-Ycheck:tailrec,resolveSuper,mixin,getClass,restoreScopes,labelDef") // should be Ycheck:all, but #725
71+
if (dotty.Properties.isRunByDrone) List("-Ycheck:tailrec,resolveSuper,erasure,mixin,getClass,restoreScopes,labelDef") // should be Ycheck:all, but #725
7272
else List("-Ycheck:tailrec,resolveSuper,mixin,restoreScopes,labelDef,simplify")
7373
} ++ checkOptions ++ classPath
7474

compiler/test/dotty/tools/vulpix/TestConfiguration.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ object TestConfiguration {
5050
Array("-classpath", paths)
5151
}
5252

53-
private val yCheckOptions = Array("-Ycheck:tailrec,resolveSuper,mixin,getClass,restoreScopes,labelDef")
53+
private val yCheckOptions = Array("-Ycheck:tailrec,resolveSuper,erasure,mixin,getClass,restoreScopes,labelDef")
5454

5555
val defaultUnoptimised = noCheckOptions ++ checkOptions ++ yCheckOptions ++ classPath
5656
val defaultOptimised = defaultUnoptimised :+ "-optimise"

0 commit comments

Comments
 (0)