@@ -3,12 +3,15 @@ package dotc
33package core
44
55import Symbols ._ , Types ._ , Contexts ._ , Flags ._ , Names ._ , StdNames ._ , Decorators ._ , Flags .JavaDefined
6+ import Uniques .unique
67import dotc .transform .ExplicitOuter ._
8+ import dotc .transform .ValueClasses ._
79import typer .Mode
810import util .DotClass
911
1012/** Erased types are:
1113 *
14+ * ErasedValueType
1215 * TypeRef(prefix is ignored, denot is ClassDenotation)
1316 * TermRef(prefix is ignored, denot is SymDenotation)
1417 * JavaArrayType
@@ -29,8 +32,12 @@ object TypeErasure {
2932
3033 /** A predicate that tests whether a type is a legal erased type. Only asInstanceOf and
3134 * isInstanceOf may have types that do not satisfy the predicate.
35+ * ErasedValueType is considered an erased type because it is valid after Erasure (it is
36+ * eliminated by ElimErasedValueType).
3237 */
3338 def isErasedType (tp : Type )(implicit ctx : Context ): Boolean = tp match {
39+ case _ : ErasedValueType =>
40+ true
3441 case tp : TypeRef =>
3542 tp.symbol.isClass && tp.symbol != defn.AnyClass
3643 case _ : TermRef =>
@@ -51,55 +58,68 @@ object TypeErasure {
5158 false
5259 }
5360
54- case class ErasedValueType (cls : ClassSymbol , underlying : Type ) extends CachedGroundType {
55- override def computeHash = doHash(cls, underlying)
61+ /** A type representing the semi-erasure of a derived value class, see SIP-15
62+ * where it's called "C$unboxed" for a class C.
63+ * Derived value classes are erased to this type during Erasure (when
64+ * semiEraseVCs = true) and subsequently erased to their underlying type
65+ * during ElimErasedValueType. This type is outside the normal Scala class
66+ * hierarchy: it is a subtype of no other type and is a supertype only of
67+ * Nothing. This is because this type is only useful for type adaptation (see
68+ * [[Erasure.Boxing#adaptToType ]]).
69+ *
70+ * @param cls The value class symbol
71+ * @param erasedUnderlying The erased type of the single field of the value class
72+ */
73+ abstract case class ErasedValueType (cls : ClassSymbol , erasedUnderlying : Type )
74+ extends CachedGroundType with ValueType {
75+ override def computeHash = doHash(cls, erasedUnderlying)
5676 }
5777
58- private def erasureIdx (isJava : Boolean , isSemi : Boolean , isConstructor : Boolean , wildcardOK : Boolean ) =
78+ final class CachedErasedValueType (cls : ClassSymbol , erasedUnderlying : Type )
79+ extends ErasedValueType (cls, erasedUnderlying)
80+
81+ object ErasedValueType {
82+ def apply (cls : ClassSymbol , erasedUnderlying : Type )(implicit ctx : Context ) = {
83+ unique(new CachedErasedValueType (cls, erasedUnderlying))
84+ }
85+ }
86+
87+ private def erasureIdx (isJava : Boolean , semiEraseVCs : Boolean , isConstructor : Boolean , wildcardOK : Boolean ) =
5988 (if (isJava) 1 else 0 ) +
60- (if (isSemi ) 2 else 0 ) +
89+ (if (semiEraseVCs ) 2 else 0 ) +
6190 (if (isConstructor) 4 else 0 ) +
6291 (if (wildcardOK) 8 else 0 )
6392
6493 private val erasures = new Array [TypeErasure ](16 )
6594
6695 for {
6796 isJava <- List (false , true )
68- isSemi <- List (false , true )
97+ semiEraseVCs <- List (false , true )
6998 isConstructor <- List (false , true )
7099 wildcardOK <- List (false , true )
71- } erasures(erasureIdx(isJava, isSemi , isConstructor, wildcardOK)) =
72- new TypeErasure (isJava, isSemi , isConstructor, wildcardOK)
100+ } erasures(erasureIdx(isJava, semiEraseVCs , isConstructor, wildcardOK)) =
101+ new TypeErasure (isJava, semiEraseVCs , isConstructor, wildcardOK)
73102
74- /** Produces an erasure function.
75- * @param isJava Arguments should be treated the way Java does it
76- * @param isSemi Value classes are mapped in an intermediate step to
77- * ErasedValueClass types, instead of going directly to
78- * the erasure of the underlying type.
79- * @param isConstructor Argument forms part of the type of a constructor
80- * @param wildcardOK Wildcards are acceptable (true when using the erasure
81- * for computing a signature name).
103+ /** Produces an erasure function. See the documentation of the class [[TypeErasure ]]
104+ * for a description of each parameter.
82105 */
83- private def erasureFn (isJava : Boolean , isSemi : Boolean , isConstructor : Boolean , wildcardOK : Boolean ): TypeErasure =
84- erasures(erasureIdx(isJava, isSemi, isConstructor, wildcardOK))
85-
86- private val scalaErasureFn = erasureFn(isJava = false , isSemi = false , isConstructor = false , wildcardOK = false )
87- private val scalaSigFn = erasureFn(isJava = false , isSemi = false , isConstructor = false , wildcardOK = true )
88- private val javaSigFn = erasureFn(isJava = true , isSemi = false , isConstructor = false , wildcardOK = true )
89- private val semiErasureFn = erasureFn(isJava = false , isSemi = true , isConstructor = false , wildcardOK = false )
106+ private def erasureFn (isJava : Boolean , semiEraseVCs : Boolean , isConstructor : Boolean , wildcardOK : Boolean ): TypeErasure =
107+ erasures(erasureIdx(isJava, semiEraseVCs, isConstructor, wildcardOK))
90108
91109 /** The current context with a phase no later than erasure */
92110 private def erasureCtx (implicit ctx : Context ) =
93111 if (ctx.erasedTypes) ctx.withPhase(ctx.erasurePhase).addMode(Mode .FutureDefsOK ) else ctx
94112
95- def erasure (tp : Type )(implicit ctx : Context ): Type = scalaErasureFn(tp)(erasureCtx)
96- def semiErasure (tp : Type )(implicit ctx : Context ): Type = semiErasureFn(tp)(erasureCtx)
113+ def erasure (tp : Type , semiEraseVCs : Boolean = true )(implicit ctx : Context ): Type =
114+ erasureFn(isJava = false , semiEraseVCs, isConstructor = false , wildcardOK = false )(tp)(erasureCtx)
115+
97116 def sigName (tp : Type , isJava : Boolean )(implicit ctx : Context ): TypeName = {
98117 val seqClass = if (isJava) defn.ArrayClass else defn.SeqClass
99118 val normTp =
100119 if (tp.isRepeatedParam) tp.translateParameterized(defn.RepeatedParamClass , seqClass)
101120 else tp
102- (if (isJava) javaSigFn else scalaSigFn).sigName(normTp)(erasureCtx)
121+ val erase = erasureFn(isJava, semiEraseVCs = false , isConstructor = false , wildcardOK = true )
122+ erase.sigName(normTp)(erasureCtx)
103123 }
104124
105125 /** The erasure of a top-level reference. Differs from normal erasure in that
@@ -117,29 +137,20 @@ object TypeErasure {
117137 erasure(tp)
118138 }
119139
120- /** The erasure of a symbol's info. This is different of `erasure` in the way `ExprType`s are
121- * treated. `eraseInfo` maps them them to nullary method types, whereas `erasure` maps them
122- * to `Function0`.
123- */
124- def eraseInfo (tp : Type , sym : Symbol )(implicit ctx : Context ): Type =
125- scalaErasureFn.eraseInfo(tp, sym)(erasureCtx)
126-
127- /** The erasure of a function result type. Differs from normal erasure in that
128- * Unit is kept instead of being mapped to BoxedUnit.
129- */
130- def eraseResult (tp : Type )(implicit ctx : Context ): Type =
131- scalaErasureFn.eraseResult(tp)(erasureCtx)
132-
133140 /** The symbol's erased info. This is the type's erasure, except for the following symbols:
134141 *
135142 * - For $asInstanceOf : [T]T
136143 * - For $isInstanceOf : [T]Boolean
137144 * - For all abstract types : = ?
145+ * - For COMPANION_CLASS_METHOD : the erasure of their type with semiEraseVCs = false,
146+ * this is needed to keep [[SymDenotation#companionClass ]]
147+ * working after erasure for value classes.
138148 * - For all other symbols : the semi-erasure of their types, with
139149 * isJava, isConstructor set according to symbol.
140150 */
141151 def transformInfo (sym : Symbol , tp : Type )(implicit ctx : Context ): Type = {
142- val erase = erasureFn(sym is JavaDefined , isSemi = true , sym.isConstructor, wildcardOK = false )
152+ val semiEraseVCs = sym.name ne nme.COMPANION_CLASS_METHOD
153+ val erase = erasureFn(sym is JavaDefined , semiEraseVCs, sym.isConstructor, wildcardOK = false )
143154
144155 def eraseParamBounds (tp : PolyType ): Type =
145156 tp.derivedPolyType(
@@ -148,7 +159,7 @@ object TypeErasure {
148159 if (defn.isPolymorphicAfterErasure(sym)) eraseParamBounds(sym.info.asInstanceOf [PolyType ])
149160 else if (sym.isAbstractType) TypeAlias (WildcardType )
150161 else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)(erasureCtx))
151- else eraseInfo(tp, sym)(erasureCtx) match {
162+ else erase. eraseInfo(tp, sym)(erasureCtx) match {
152163 case einfo : MethodType if sym.isGetter && einfo.resultType.isRef(defn.UnitClass ) =>
153164 defn.BoxedUnitClass .typeRef
154165 case einfo =>
@@ -241,12 +252,15 @@ object TypeErasure {
241252import TypeErasure ._
242253
243254/**
244- * This is used as the Scala erasure during the erasure phase itself
245- * It differs from normal erasure in that value classes are erased to ErasedValueTypes which
246- * are then later converted to the underlying parameter type in phase posterasure.
247- *
255+ * @param isJava Arguments should be treated the way Java does it
256+ * @param semiEraseVCs If true, value classes are semi-erased to ErasedValueType
257+ * (they will be fully erased in [[ElimErasedValueType ]]).
258+ * If false, they are erased like normal classes.
259+ * @param isConstructor Argument forms part of the type of a constructor
260+ * @param wildcardOK Wildcards are acceptable (true when using the erasure
261+ * for computing a signature name).
248262 */
249- class TypeErasure (isJava : Boolean , isSemi : Boolean , isConstructor : Boolean , wildcardOK : Boolean ) extends DotClass {
263+ class TypeErasure (isJava : Boolean , semiEraseVCs : Boolean , isConstructor : Boolean , wildcardOK : Boolean ) extends DotClass {
250264
251265 /** The erasure |T| of a type T. This is:
252266 *
@@ -279,10 +293,12 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
279293 * - For any other type, exception.
280294 */
281295 private def apply (tp : Type )(implicit ctx : Context ): Type = tp match {
296+ case _ : ErasedValueType =>
297+ tp
282298 case tp : TypeRef =>
283299 val sym = tp.symbol
284300 if (! sym.isClass) this (tp.info)
285- else if (sym. isDerivedValueClass) eraseDerivedValueClassRef(tp)
301+ else if (semiEraseVCs && isDerivedValueClass(sym) ) eraseDerivedValueClassRef(tp)
286302 else eraseNormalClassRef(tp)
287303 case tp : RefinedType =>
288304 val parent = tp.parent
@@ -291,7 +307,9 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
291307 case tp : TermRef =>
292308 this (tp.widen)
293309 case tp : ThisType =>
294- this (tp.cls.typeRef)
310+ def thisTypeErasure (tpToErase : Type ) =
311+ erasureFn(isJava, semiEraseVCs = false , isConstructor, wildcardOK)(tpToErase)
312+ thisTypeErasure(tp.cls.typeRef)
295313 case SuperType (thistpe, supertpe) =>
296314 SuperType (this (thistpe), this (supertpe))
297315 case ExprType (rt) =>
@@ -303,7 +321,8 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
303321 case OrType (tp1, tp2) =>
304322 ctx.typeComparer.orType(this (tp1), this (tp2), erased = true )
305323 case tp : MethodType =>
306- val paramErasure = erasureFn(tp.isJava, isSemi, isConstructor, wildcardOK)(_)
324+ def paramErasure (tpToErase : Type ) =
325+ erasureFn(tp.isJava, semiEraseVCs, isConstructor, wildcardOK)(tpToErase)
307326 val formals = tp.paramTypes.mapConserve(paramErasure)
308327 eraseResult(tp.resultType) match {
309328 case rt : MethodType =>
@@ -341,11 +360,17 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
341360
342361 private def eraseArray (tp : RefinedType )(implicit ctx : Context ) = {
343362 val defn .ArrayType (elemtp) = tp
363+ def arrayErasure (tpToErase : Type ) =
364+ erasureFn(isJava, semiEraseVCs = false , isConstructor, wildcardOK)(tpToErase)
344365 if (elemtp derivesFrom defn.NullClass ) JavaArrayType (defn.ObjectType )
345366 else if (isUnboundedGeneric(elemtp)) defn.ObjectType
346- else JavaArrayType (this (elemtp))
367+ else JavaArrayType (arrayErasure (elemtp))
347368 }
348369
370+ /** The erasure of a symbol's info. This is different from `apply` in the way `ExprType`s are
371+ * treated. `eraseInfo` maps them them to nullary method types, whereas `apply` maps them
372+ * to `Function0`.
373+ */
349374 def eraseInfo (tp : Type , sym : Symbol )(implicit ctx : Context ) = tp match {
350375 case ExprType (rt) =>
351376 if (sym is Param ) apply(tp)
@@ -354,22 +379,30 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
354379 // forwarders to mixin methods.
355380 // See doc comment for ElimByName for speculation how we could improve this.
356381 else MethodType (Nil , Nil , eraseResult(rt))
357- case tp => erasure(tp)
382+ case tp => this (tp)
383+ }
384+
385+ private def eraseDerivedValueClassRef (tref : TypeRef )(implicit ctx : Context ): Type = {
386+ val cls = tref.symbol.asClass
387+ val underlying = underlyingOfValueClass(cls)
388+ ErasedValueType (cls, erasure(underlying))
358389 }
359390
360- private def eraseDerivedValueClassRef (tref : TypeRef )(implicit ctx : Context ): Type =
361- unsupported(" eraseDerivedValueClass" )
362391
363392 private def eraseNormalClassRef (tref : TypeRef )(implicit ctx : Context ): Type = {
364393 val cls = tref.symbol.asClass
365394 (if (cls.owner is Package ) normalizeClass(cls) else cls).typeRef
366395 }
367396
397+ /** The erasure of a function result type. */
368398 private def eraseResult (tp : Type )(implicit ctx : Context ): Type = tp match {
369399 case tp : TypeRef =>
370400 val sym = tp.typeSymbol
371401 if (sym eq defn.UnitClass ) sym.typeRef
372- else if (sym.isDerivedValueClass) eraseNormalClassRef(tp)
402+ // For a value class V, "new V(x)" should have type V for type adaptation to work
403+ // correctly (see SIP-15 and [[Erasure.Boxing.adaptToType]]), so the return type of a
404+ // constructor method should not be semi-erased.
405+ else if (isConstructor && isDerivedValueClass(sym)) eraseNormalClassRef(tp)
373406 else this (tp)
374407 case RefinedType (parent, _) if ! (parent isRef defn.ArrayClass ) =>
375408 eraseResult(parent)
@@ -391,10 +424,12 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
391424 * Need to ensure correspondence with erasure!
392425 */
393426 private def sigName (tp : Type )(implicit ctx : Context ): TypeName = tp match {
427+ case ErasedValueType (_, underlying) =>
428+ sigName(underlying)
394429 case tp : TypeRef =>
395430 val sym = tp.symbol
396431 if (! sym.isClass) sigName(tp.info)
397- else if (sym. isDerivedValueClass) sigName(eraseDerivedValueClassRef(tp))
432+ else if (isDerivedValueClass(sym) ) sigName(eraseDerivedValueClassRef(tp))
398433 else normalizeClass(sym.asClass).fullName.asTypeName
399434 case defn.ArrayType (elem) =>
400435 sigName(this (tp))
0 commit comments