@@ -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,29 @@ 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
+ wildcardOK.toInt
147
+ + (isConstructor.toInt << 1 )
148
+ + (semiEraseVCs.toInt << 2 )
149
+ + (sourceLanguage.ordinal << 3 )
115
150
116
- private val erasures = new Array [TypeErasure ](16 )
151
+ private val erasures = new Array [TypeErasure ](1 << ( SourceLanguage .bits + 3 ) )
117
152
118
- for {
119
- isJava <- List ( false , true )
153
+ for
154
+ sourceLanguage <- SourceLanguage .values
120
155
semiEraseVCs <- List (false , true )
121
156
isConstructor <- List (false , true )
122
157
wildcardOK <- List (false , true )
123
- }
124
- erasures(erasureIdx(isJava , semiEraseVCs, isConstructor, wildcardOK)) =
125
- new TypeErasure (isJava , semiEraseVCs, isConstructor, wildcardOK)
158
+ do
159
+ erasures(erasureIdx(sourceLanguage , semiEraseVCs, isConstructor, wildcardOK)) =
160
+ new TypeErasure (sourceLanguage , semiEraseVCs, isConstructor, wildcardOK)
126
161
127
162
/** Produces an erasure function. See the documentation of the class [[TypeErasure ]]
128
163
* for a description of each parameter.
129
164
*/
130
- private def erasureFn (isJava : Boolean , semiEraseVCs : Boolean , isConstructor : Boolean , wildcardOK : Boolean ): TypeErasure =
131
- erasures(erasureIdx(isJava , semiEraseVCs, isConstructor, wildcardOK))
165
+ private def erasureFn (sourceLanguage : SourceLanguage , semiEraseVCs : Boolean , isConstructor : Boolean , wildcardOK : Boolean ): TypeErasure =
166
+ erasures(erasureIdx(sourceLanguage , semiEraseVCs, isConstructor, wildcardOK))
132
167
133
168
/** The current context with a phase no later than erasure */
134
169
def preErasureCtx (using Context ) =
@@ -139,25 +174,25 @@ object TypeErasure {
139
174
* @param tp The type to erase.
140
175
*/
141
176
def erasure (tp : Type )(using Context ): Type =
142
- erasureFn(isJava = false , semiEraseVCs = false , isConstructor = false , wildcardOK = false )(tp)(using preErasureCtx)
177
+ erasureFn(sourceLanguage = SourceLanguage . Scala3 , semiEraseVCs = false , isConstructor = false , wildcardOK = false )(tp)(using preErasureCtx)
143
178
144
179
/** The value class erasure of a Scala type, where value classes are semi-erased to
145
180
* ErasedValueType (they will be fully erased in [[ElimErasedValueType ]]).
146
181
*
147
182
* @param tp The type to erase.
148
183
*/
149
184
def valueErasure (tp : Type )(using Context ): Type =
150
- erasureFn(isJava = false , semiEraseVCs = true , isConstructor = false , wildcardOK = false )(tp)(using preErasureCtx)
185
+ erasureFn(sourceLanguage = SourceLanguage . Scala3 , semiEraseVCs = true , isConstructor = false , wildcardOK = false )(tp)(using preErasureCtx)
151
186
152
187
/** Like value class erasure, but value classes erase to their underlying type erasure */
153
188
def fullErasure (tp : Type )(using Context ): Type =
154
189
valueErasure(tp) match
155
190
case ErasedValueType (_, underlying) => erasure(underlying)
156
191
case etp => etp
157
192
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 )
193
+ def sigName (tp : Type , sourceLanguage : SourceLanguage )(using Context ): TypeName = {
194
+ val normTp = tp.translateFromRepeated(toArray = sourceLanguage. isJava)
195
+ val erase = erasureFn(sourceLanguage , semiEraseVCs = true , isConstructor = false , wildcardOK = true )
161
196
erase.sigName(normTp)(using preErasureCtx)
162
197
}
163
198
@@ -181,15 +216,13 @@ object TypeErasure {
181
216
* - For $asInstanceOf : [T]T
182
217
* - For $isInstanceOf : [T]Boolean
183
218
* - 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.
219
+ *
220
+ * `sourceLanguage`, `isConstructor` and `semiEraseVCs` are set based on the symbol.
188
221
*/
189
222
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 )
223
+ val sourceLanguage = SourceLanguage ( sym)
224
+ val semiEraseVCs = ! sourceLanguage. isJava // Java sees our value classes as regular classes.
225
+ val erase = erasureFn(sourceLanguage , semiEraseVCs, sym.isConstructor, wildcardOK = false )
193
226
194
227
def eraseParamBounds (tp : PolyType ): Type =
195
228
tp.derivedLambdaType(
@@ -391,18 +424,20 @@ object TypeErasure {
391
424
case _ => false
392
425
}
393
426
}
427
+
394
428
import TypeErasure ._
395
429
396
430
/**
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).
431
+ * @param sourceLanguage Adapt our erasure rules to mimic what the given language
432
+ * would do.
433
+ * @param semiEraseVCs If true, value classes are semi-erased to ErasedValueType
434
+ * (they will be fully erased in [[ElimErasedValueType ]]).
435
+ * If false, they are erased like normal classes.
436
+ * @param isConstructor Argument forms part of the type of a constructor
437
+ * @param wildcardOK Wildcards are acceptable (true when using the erasure
438
+ * for computing a signature name).
404
439
*/
405
- class TypeErasure (isJava : Boolean , semiEraseVCs : Boolean , isConstructor : Boolean , wildcardOK : Boolean ) {
440
+ class TypeErasure (sourceLanguage : SourceLanguage , semiEraseVCs : Boolean , isConstructor : Boolean , wildcardOK : Boolean ) {
406
441
407
442
/** The erasure |T| of a type T. This is:
408
443
*
@@ -450,7 +485,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
450
485
val tycon = tp.tycon
451
486
if (tycon.isRef(defn.ArrayClass )) eraseArray(tp)
452
487
else if (tycon.isRef(defn.PairClass )) erasePair(tp)
453
- else if (tp.isRepeatedParam) apply(tp.translateFromRepeated(toArray = isJava))
488
+ else if (tp.isRepeatedParam) apply(tp.translateFromRepeated(toArray = sourceLanguage. isJava))
454
489
else if (semiEraseVCs && isDerivedValueClass(tycon.classSymbol)) eraseDerivedValueClass(tp)
455
490
else apply(tp.translucentSuperType)
456
491
case _ : TermRef | _ : ThisType =>
@@ -468,12 +503,12 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
468
503
case tp : TypeProxy =>
469
504
this (tp.underlying)
470
505
case AndType (tp1, tp2) =>
471
- erasedGlb(this (tp1), this (tp2), isJava)
506
+ erasedGlb(this (tp1), this (tp2), sourceLanguage. isJava)
472
507
case OrType (tp1, tp2) =>
473
508
TypeComparer .orType(this (tp1), this (tp2), isErased = true )
474
509
case tp : MethodType =>
475
510
def paramErasure (tpToErase : Type ) =
476
- erasureFn(isJava , semiEraseVCs, isConstructor, wildcardOK)(tpToErase)
511
+ erasureFn(sourceLanguage , semiEraseVCs, isConstructor, wildcardOK)(tpToErase)
477
512
val (names, formals0) = if (tp.isErasedMethod) (Nil , Nil ) else (tp.paramNames, tp.paramInfos)
478
513
val formals = formals0.mapConserve(paramErasure)
479
514
eraseResult(tp.resultType) match {
@@ -516,8 +551,8 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
516
551
private def eraseArray (tp : Type )(using Context ) = {
517
552
val defn .ArrayOf (elemtp) = tp
518
553
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))
554
+ else if (isUnboundedGeneric(elemtp) && ! sourceLanguage. isJava) defn.ObjectType
555
+ else JavaArrayType (erasureFn(sourceLanguage , semiEraseVCs = false , isConstructor, wildcardOK)(elemtp))
521
556
}
522
557
523
558
private def erasePair (tp : Type )(using Context ): Type = {
@@ -544,7 +579,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
544
579
// See doc comment for ElimByName for speculation how we could improve this.
545
580
else
546
581
MethodType (Nil , Nil ,
547
- eraseResult(sym.info.finalResultType.translateFromRepeated(toArray = isJava)))
582
+ eraseResult(sym.info.finalResultType.translateFromRepeated(toArray = sourceLanguage. isJava)))
548
583
case tp1 : PolyType =>
549
584
eraseResult(tp1.resultType) match
550
585
case rt : MethodType => rt
@@ -596,7 +631,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
596
631
// correctly (see SIP-15 and [[Erasure.Boxing.adaptToType]]), so the result type of a
597
632
// constructor method should not be semi-erased.
598
633
if semiEraseVCs && isConstructor && ! tp.isInstanceOf [MethodOrPoly ] then
599
- erasureFn(isJava , semiEraseVCs = false , isConstructor, wildcardOK).eraseResult(tp)
634
+ erasureFn(sourceLanguage , semiEraseVCs = false , isConstructor, wildcardOK).eraseResult(tp)
600
635
else tp match
601
636
case tp : TypeRef =>
602
637
val sym = tp.symbol
0 commit comments