@@ -3,12 +3,15 @@ package dotc
3
3
package core
4
4
5
5
import Symbols ._ , Types ._ , Contexts ._ , Flags ._ , Names ._ , StdNames ._ , Decorators ._ , Flags .JavaDefined
6
+ import Uniques .unique
6
7
import dotc .transform .ExplicitOuter ._
8
+ import dotc .transform .ValueClasses ._
7
9
import typer .Mode
8
10
import util .DotClass
9
11
10
12
/** Erased types are:
11
13
*
14
+ * ErasedValueType
12
15
* TypeRef(prefix is ignored, denot is ClassDenotation)
13
16
* TermRef(prefix is ignored, denot is SymDenotation)
14
17
* JavaArrayType
@@ -29,8 +32,12 @@ object TypeErasure {
29
32
30
33
/** A predicate that tests whether a type is a legal erased type. Only asInstanceOf and
31
34
* 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).
32
37
*/
33
38
def isErasedType (tp : Type )(implicit ctx : Context ): Boolean = tp match {
39
+ case _ : ErasedValueType =>
40
+ true
34
41
case tp : TypeRef =>
35
42
tp.symbol.isClass && tp.symbol != defn.AnyClass
36
43
case _ : TermRef =>
@@ -51,55 +58,68 @@ object TypeErasure {
51
58
false
52
59
}
53
60
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)
56
76
}
57
77
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 ) =
59
88
(if (isJava) 1 else 0 ) +
60
- (if (isSemi ) 2 else 0 ) +
89
+ (if (semiEraseVCs ) 2 else 0 ) +
61
90
(if (isConstructor) 4 else 0 ) +
62
91
(if (wildcardOK) 8 else 0 )
63
92
64
93
private val erasures = new Array [TypeErasure ](16 )
65
94
66
95
for {
67
96
isJava <- List (false , true )
68
- isSemi <- List (false , true )
97
+ semiEraseVCs <- List (false , true )
69
98
isConstructor <- List (false , true )
70
99
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)
73
102
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.
82
105
*/
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))
90
108
91
109
/** The current context with a phase no later than erasure */
92
110
private def erasureCtx (implicit ctx : Context ) =
93
111
if (ctx.erasedTypes) ctx.withPhase(ctx.erasurePhase).addMode(Mode .FutureDefsOK ) else ctx
94
112
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
+
97
116
def sigName (tp : Type , isJava : Boolean )(implicit ctx : Context ): TypeName = {
98
117
val seqClass = if (isJava) defn.ArrayClass else defn.SeqClass
99
118
val normTp =
100
119
if (tp.isRepeatedParam) tp.translateParameterized(defn.RepeatedParamClass , seqClass)
101
120
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)
103
123
}
104
124
105
125
/** The erasure of a top-level reference. Differs from normal erasure in that
@@ -117,29 +137,20 @@ object TypeErasure {
117
137
erasure(tp)
118
138
}
119
139
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
-
133
140
/** The symbol's erased info. This is the type's erasure, except for the following symbols:
134
141
*
135
142
* - For $asInstanceOf : [T]T
136
143
* - For $isInstanceOf : [T]Boolean
137
144
* - 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.
138
148
* - For all other symbols : the semi-erasure of their types, with
139
149
* isJava, isConstructor set according to symbol.
140
150
*/
141
151
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 )
143
154
144
155
def eraseParamBounds (tp : PolyType ): Type =
145
156
tp.derivedPolyType(
@@ -148,7 +159,7 @@ object TypeErasure {
148
159
if (defn.isPolymorphicAfterErasure(sym)) eraseParamBounds(sym.info.asInstanceOf [PolyType ])
149
160
else if (sym.isAbstractType) TypeAlias (WildcardType )
150
161
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 {
152
163
case einfo : MethodType if sym.isGetter && einfo.resultType.isRef(defn.UnitClass ) =>
153
164
defn.BoxedUnitClass .typeRef
154
165
case einfo =>
@@ -241,12 +252,15 @@ object TypeErasure {
241
252
import TypeErasure ._
242
253
243
254
/**
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).
248
262
*/
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 {
250
264
251
265
/** The erasure |T| of a type T. This is:
252
266
*
@@ -279,10 +293,12 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
279
293
* - For any other type, exception.
280
294
*/
281
295
private def apply (tp : Type )(implicit ctx : Context ): Type = tp match {
296
+ case _ : ErasedValueType =>
297
+ tp
282
298
case tp : TypeRef =>
283
299
val sym = tp.symbol
284
300
if (! sym.isClass) this (tp.info)
285
- else if (sym. isDerivedValueClass) eraseDerivedValueClassRef(tp)
301
+ else if (semiEraseVCs && isDerivedValueClass(sym) ) eraseDerivedValueClassRef(tp)
286
302
else eraseNormalClassRef(tp)
287
303
case tp : RefinedType =>
288
304
val parent = tp.parent
@@ -291,7 +307,9 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
291
307
case tp : TermRef =>
292
308
this (tp.widen)
293
309
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)
295
313
case SuperType (thistpe, supertpe) =>
296
314
SuperType (this (thistpe), this (supertpe))
297
315
case ExprType (rt) =>
@@ -303,7 +321,8 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
303
321
case OrType (tp1, tp2) =>
304
322
ctx.typeComparer.orType(this (tp1), this (tp2), erased = true )
305
323
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)
307
326
val formals = tp.paramTypes.mapConserve(paramErasure)
308
327
eraseResult(tp.resultType) match {
309
328
case rt : MethodType =>
@@ -341,11 +360,17 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
341
360
342
361
private def eraseArray (tp : RefinedType )(implicit ctx : Context ) = {
343
362
val defn .ArrayType (elemtp) = tp
363
+ def arrayErasure (tpToErase : Type ) =
364
+ erasureFn(isJava, semiEraseVCs = false , isConstructor, wildcardOK)(tpToErase)
344
365
if (elemtp derivesFrom defn.NullClass ) JavaArrayType (defn.ObjectType )
345
366
else if (isUnboundedGeneric(elemtp)) defn.ObjectType
346
- else JavaArrayType (this (elemtp))
367
+ else JavaArrayType (arrayErasure (elemtp))
347
368
}
348
369
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
+ */
349
374
def eraseInfo (tp : Type , sym : Symbol )(implicit ctx : Context ) = tp match {
350
375
case ExprType (rt) =>
351
376
if (sym is Param ) apply(tp)
@@ -354,22 +379,30 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
354
379
// forwarders to mixin methods.
355
380
// See doc comment for ElimByName for speculation how we could improve this.
356
381
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))
358
389
}
359
390
360
- private def eraseDerivedValueClassRef (tref : TypeRef )(implicit ctx : Context ): Type =
361
- unsupported(" eraseDerivedValueClass" )
362
391
363
392
private def eraseNormalClassRef (tref : TypeRef )(implicit ctx : Context ): Type = {
364
393
val cls = tref.symbol.asClass
365
394
(if (cls.owner is Package ) normalizeClass(cls) else cls).typeRef
366
395
}
367
396
397
+ /** The erasure of a function result type. */
368
398
private def eraseResult (tp : Type )(implicit ctx : Context ): Type = tp match {
369
399
case tp : TypeRef =>
370
400
val sym = tp.typeSymbol
371
401
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)
373
406
else this (tp)
374
407
case RefinedType (parent, _) if ! (parent isRef defn.ArrayClass ) =>
375
408
eraseResult(parent)
@@ -391,10 +424,12 @@ class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wild
391
424
* Need to ensure correspondence with erasure!
392
425
*/
393
426
private def sigName (tp : Type )(implicit ctx : Context ): TypeName = tp match {
427
+ case ErasedValueType (_, underlying) =>
428
+ sigName(underlying)
394
429
case tp : TypeRef =>
395
430
val sym = tp.symbol
396
431
if (! sym.isClass) sigName(tp.info)
397
- else if (sym. isDerivedValueClass) sigName(eraseDerivedValueClassRef(tp))
432
+ else if (isDerivedValueClass(sym) ) sigName(eraseDerivedValueClassRef(tp))
398
433
else normalizeClass(sym.asClass).fullName.asTypeName
399
434
case defn.ArrayType (elem) =>
400
435
sigName(this (tp))
0 commit comments