@@ -14,6 +14,40 @@ import Decorators._
14
14
import Definitions .MaxImplementedFunctionArity
15
15
import scala .annotation .tailrec
16
16
17
+ /** The language in which the definition being erased was written. */
18
+ enum SourceLanguage :
19
+ case Java , Scala2 , Scala3
20
+ def isJava : Boolean = this eq Java
21
+ def isScala2 : Boolean = this eq Scala2
22
+ def isScala3 : Boolean = this eq Scala3
23
+ object SourceLanguage :
24
+ /** The language in which `sym` was defined. */
25
+ def apply (sym : Symbol )(using Context ): SourceLanguage =
26
+ if sym.is(JavaDefined ) then
27
+ SourceLanguage .Java
28
+ // Scala 2 methods don't have Inline set, except for the ones injected with `patchStdlibClass`
29
+ // which are really Scala 3 methods.
30
+ else if sym.isClass && sym.is(Scala2x ) || (sym.maybeOwner.is(Scala2x ) && ! sym.is(Inline )) then
31
+ SourceLanguage .Scala2
32
+ else
33
+ SourceLanguage .Scala3
34
+
35
+ /** Number of bits needed to represent this enum. */
36
+ def bits : Int =
37
+ val len = values.length
38
+ val log2 = 31 - Integer .numberOfLeadingZeros(len)
39
+ if len == 1 << log2 then
40
+ log2
41
+ else
42
+ log2 + 1
43
+
44
+ /** A common language to use when matching definitions written in different
45
+ * languages.
46
+ */
47
+ def commonLanguage (x : SourceLanguage , y : SourceLanguage ): SourceLanguage =
48
+ if x.ordinal > y.ordinal then x else y
49
+ end SourceLanguage
50
+
17
51
/** Erased types are:
18
52
*
19
53
* ErasedValueType
@@ -107,28 +141,30 @@ object TypeErasure {
107
141
}
108
142
}
109
143
110
- private def erasureIdx (isJava : Boolean , semiEraseVCs : Boolean , isConstructor : Boolean , wildcardOK : Boolean ) =
111
- (if (isJava) 1 else 0 ) +
112
- (if (semiEraseVCs) 2 else 0 ) +
113
- (if (isConstructor) 4 else 0 ) +
114
- (if (wildcardOK) 8 else 0 )
144
+ private def erasureIdx (sourceLanguage : SourceLanguage , semiEraseVCs : Boolean , isConstructor : Boolean , wildcardOK : Boolean ) =
145
+ extension (b : Boolean ) def toInt = if b then 1 else 0
146
+ val sourceBits = SourceLanguage .bits
147
+ sourceLanguage.ordinal
148
+ + (semiEraseVCs.toInt << sourceBits)
149
+ + (isConstructor.toInt << (sourceBits + 1 ))
150
+ + (wildcardOK.toInt << (sourceBits + 2 ))
115
151
116
- private val erasures = new Array [TypeErasure ](16 )
152
+ private val erasures = new Array [TypeErasure ](1 << ( SourceLanguage .bits + 3 ) )
117
153
118
- for {
119
- isJava <- List ( false , true )
154
+ for
155
+ sourceLanguage <- SourceLanguage .values
120
156
semiEraseVCs <- List (false , true )
121
157
isConstructor <- List (false , true )
122
158
wildcardOK <- List (false , true )
123
- }
124
- erasures(erasureIdx(isJava , semiEraseVCs, isConstructor, wildcardOK)) =
125
- new TypeErasure (isJava , semiEraseVCs, isConstructor, wildcardOK)
159
+ do
160
+ erasures(erasureIdx(sourceLanguage , semiEraseVCs, isConstructor, wildcardOK)) =
161
+ new TypeErasure (sourceLanguage , semiEraseVCs, isConstructor, wildcardOK)
126
162
127
163
/** Produces an erasure function. See the documentation of the class [[TypeErasure ]]
128
164
* for a description of each parameter.
129
165
*/
130
- private def erasureFn (isJava : Boolean , semiEraseVCs : Boolean , isConstructor : Boolean , wildcardOK : Boolean ): TypeErasure =
131
- erasures(erasureIdx(isJava , semiEraseVCs, isConstructor, wildcardOK))
166
+ private def erasureFn (sourceLanguage : SourceLanguage , semiEraseVCs : Boolean , isConstructor : Boolean , wildcardOK : Boolean ): TypeErasure =
167
+ erasures(erasureIdx(sourceLanguage , semiEraseVCs, isConstructor, wildcardOK))
132
168
133
169
/** The current context with a phase no later than erasure */
134
170
def preErasureCtx (using Context ) =
@@ -139,25 +175,25 @@ object TypeErasure {
139
175
* @param tp The type to erase.
140
176
*/
141
177
def erasure (tp : Type )(using Context ): Type =
142
- erasureFn(isJava = false , semiEraseVCs = false , isConstructor = false , wildcardOK = false )(tp)(using preErasureCtx)
178
+ erasureFn(sourceLanguage = SourceLanguage . Scala3 , semiEraseVCs = false , isConstructor = false , wildcardOK = false )(tp)(using preErasureCtx)
143
179
144
180
/** The value class erasure of a Scala type, where value classes are semi-erased to
145
181
* ErasedValueType (they will be fully erased in [[ElimErasedValueType ]]).
146
182
*
147
183
* @param tp The type to erase.
148
184
*/
149
185
def valueErasure (tp : Type )(using Context ): Type =
150
- erasureFn(isJava = false , semiEraseVCs = true , isConstructor = false , wildcardOK = false )(tp)(using preErasureCtx)
186
+ erasureFn(sourceLanguage = SourceLanguage . Scala3 , semiEraseVCs = true , isConstructor = false , wildcardOK = false )(tp)(using preErasureCtx)
151
187
152
188
/** Like value class erasure, but value classes erase to their underlying type erasure */
153
189
def fullErasure (tp : Type )(using Context ): Type =
154
190
valueErasure(tp) match
155
191
case ErasedValueType (_, underlying) => erasure(underlying)
156
192
case etp => etp
157
193
158
- def sigName (tp : Type , isJava : Boolean )(using Context ): TypeName = {
159
- val normTp = tp.translateFromRepeated(toArray = isJava)
160
- val erase = erasureFn(isJava , semiEraseVCs = true , isConstructor = false , wildcardOK = true )
194
+ def sigName (tp : Type , sourceLanguage : SourceLanguage )(using Context ): TypeName = {
195
+ val normTp = tp.translateFromRepeated(toArray = sourceLanguage. isJava)
196
+ val erase = erasureFn(sourceLanguage , semiEraseVCs = false , isConstructor = false , wildcardOK = true )
161
197
erase.sigName(normTp)(using preErasureCtx)
162
198
}
163
199
@@ -181,15 +217,13 @@ object TypeErasure {
181
217
* - For $asInstanceOf : [T]T
182
218
* - For $isInstanceOf : [T]Boolean
183
219
* - For all abstract types : = ?
184
- * - For Java-defined symbols: : the erasure of their type with isJava = true,
185
- * semiEraseVCs = false. Semi-erasure never happens in Java.
186
- * - For all other symbols : the semi-erasure of their types, with
187
- * isJava, isConstructor set according to symbol.
220
+ *
221
+ * `sourceLanguage`, `isConstructor` and `semiEraseVCs` are set based on the symbol.
188
222
*/
189
223
def transformInfo (sym : Symbol , tp : Type )(using Context ): Type = {
190
- val isJava = sym is JavaDefined
191
- val semiEraseVCs = ! isJava
192
- val erase = erasureFn(isJava , semiEraseVCs, sym.isConstructor, wildcardOK = false )
224
+ val sourceLanguage = SourceLanguage ( sym)
225
+ val semiEraseVCs = ! sourceLanguage. isJava // Java sees our value classes as regular classes.
226
+ val erase = erasureFn(sourceLanguage , semiEraseVCs, sym.isConstructor, wildcardOK = false )
193
227
194
228
def eraseParamBounds (tp : PolyType ): Type =
195
229
tp.derivedLambdaType(
@@ -391,18 +425,20 @@ object TypeErasure {
391
425
case _ => false
392
426
}
393
427
}
428
+
394
429
import TypeErasure ._
395
430
396
431
/**
397
- * @param isJava Arguments should be treated the way Java does it
398
- * @param semiEraseVCs If true, value classes are semi-erased to ErasedValueType
399
- * (they will be fully erased in [[ElimErasedValueType ]]).
400
- * If false, they are erased like normal classes.
401
- * @param isConstructor Argument forms part of the type of a constructor
402
- * @param wildcardOK Wildcards are acceptable (true when using the erasure
403
- * for computing a signature name).
432
+ * @param sourceLanguage Adapt our erasure rules to mimic what the given language
433
+ * would do.
434
+ * @param semiEraseVCs If true, value classes are semi-erased to ErasedValueType
435
+ * (they will be fully erased in [[ElimErasedValueType ]]).
436
+ * If false, they are erased like normal classes.
437
+ * @param isConstructor Argument forms part of the type of a constructor
438
+ * @param wildcardOK Wildcards are acceptable (true when using the erasure
439
+ * for computing a signature name).
404
440
*/
405
- class TypeErasure (isJava : Boolean , semiEraseVCs : Boolean , isConstructor : Boolean , wildcardOK : Boolean ) {
441
+ class TypeErasure (sourceLanguage : SourceLanguage , semiEraseVCs : Boolean , isConstructor : Boolean , wildcardOK : Boolean ) {
406
442
407
443
/** The erasure |T| of a type T. This is:
408
444
*
@@ -450,7 +486,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
450
486
val tycon = tp.tycon
451
487
if (tycon.isRef(defn.ArrayClass )) eraseArray(tp)
452
488
else if (tycon.isRef(defn.PairClass )) erasePair(tp)
453
- else if (tp.isRepeatedParam) apply(tp.translateFromRepeated(toArray = isJava))
489
+ else if (tp.isRepeatedParam) apply(tp.translateFromRepeated(toArray = sourceLanguage. isJava))
454
490
else if (semiEraseVCs && isDerivedValueClass(tycon.classSymbol)) eraseDerivedValueClass(tp)
455
491
else apply(tp.translucentSuperType)
456
492
case _ : TermRef | _ : ThisType =>
@@ -468,12 +504,12 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
468
504
case tp : TypeProxy =>
469
505
this (tp.underlying)
470
506
case AndType (tp1, tp2) =>
471
- erasedGlb(this (tp1), this (tp2), isJava)
507
+ erasedGlb(this (tp1), this (tp2), sourceLanguage. isJava)
472
508
case OrType (tp1, tp2) =>
473
509
TypeComparer .orType(this (tp1), this (tp2), isErased = true )
474
510
case tp : MethodType =>
475
511
def paramErasure (tpToErase : Type ) =
476
- erasureFn(isJava , semiEraseVCs, isConstructor, wildcardOK)(tpToErase)
512
+ erasureFn(sourceLanguage , semiEraseVCs, isConstructor, wildcardOK)(tpToErase)
477
513
val (names, formals0) = if (tp.isErasedMethod) (Nil , Nil ) else (tp.paramNames, tp.paramInfos)
478
514
val formals = formals0.mapConserve(paramErasure)
479
515
eraseResult(tp.resultType) match {
@@ -516,8 +552,8 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
516
552
private def eraseArray (tp : Type )(using Context ) = {
517
553
val defn .ArrayOf (elemtp) = tp
518
554
if (classify(elemtp).derivesFrom(defn.NullClass )) JavaArrayType (defn.ObjectType )
519
- else if (isUnboundedGeneric(elemtp) && ! isJava) defn.ObjectType
520
- else JavaArrayType (erasureFn(isJava , semiEraseVCs = false , isConstructor, wildcardOK)(elemtp))
555
+ else if (isUnboundedGeneric(elemtp) && ! sourceLanguage. isJava) defn.ObjectType
556
+ else JavaArrayType (erasureFn(sourceLanguage , semiEraseVCs = false , isConstructor, wildcardOK)(elemtp))
521
557
}
522
558
523
559
private def erasePair (tp : Type )(using Context ): Type = {
@@ -544,7 +580,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
544
580
// See doc comment for ElimByName for speculation how we could improve this.
545
581
else
546
582
MethodType (Nil , Nil ,
547
- eraseResult(sym.info.finalResultType.translateFromRepeated(toArray = isJava)))
583
+ eraseResult(sym.info.finalResultType.translateFromRepeated(toArray = sourceLanguage. isJava)))
548
584
case tp1 : PolyType =>
549
585
eraseResult(tp1.resultType) match
550
586
case rt : MethodType => rt
@@ -596,7 +632,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
596
632
// correctly (see SIP-15 and [[Erasure.Boxing.adaptToType]]), so the result type of a
597
633
// constructor method should not be semi-erased.
598
634
if semiEraseVCs && isConstructor && ! tp.isInstanceOf [MethodOrPoly ] then
599
- erasureFn(isJava , semiEraseVCs = false , isConstructor, wildcardOK).eraseResult(tp)
635
+ erasureFn(sourceLanguage , semiEraseVCs = false , isConstructor, wildcardOK).eraseResult(tp)
600
636
else tp match
601
637
case tp : TypeRef =>
602
638
val sym = tp.symbol
0 commit comments