@@ -10,10 +10,45 @@ import transform.ExplicitOuter._
10
10
import transform .ValueClasses ._
11
11
import transform .TypeUtils ._
12
12
import transform .ContextFunctionResults ._
13
+ import unpickleScala2 .Scala2Erasure
13
14
import Decorators ._
14
15
import Definitions .MaxImplementedFunctionArity
15
16
import scala .annotation .tailrec
16
17
18
+ /** The language in which the definition being erased was written. */
19
+ enum SourceLanguage :
20
+ case Java , Scala2 , Scala3
21
+ def isJava : Boolean = this eq Java
22
+ def isScala2 : Boolean = this eq Scala2
23
+ def isScala3 : Boolean = this eq Scala3
24
+ object SourceLanguage :
25
+ /** The language in which `sym` was defined. */
26
+ def apply (sym : Symbol )(using Context ): SourceLanguage =
27
+ if sym.is(JavaDefined ) then
28
+ SourceLanguage .Java
29
+ // Scala 2 methods don't have Inline set, except for the ones injected with `patchStdlibClass`
30
+ // which are really Scala 3 methods.
31
+ else if sym.isClass && sym.is(Scala2x ) || (sym.maybeOwner.is(Scala2x ) && ! sym.is(Inline )) then
32
+ SourceLanguage .Scala2
33
+ else
34
+ SourceLanguage .Scala3
35
+
36
+ /** Number of bits needed to represent this enum. */
37
+ def bits : Int =
38
+ val len = values.length
39
+ val log2 = 31 - Integer .numberOfLeadingZeros(len)
40
+ if len == 1 << log2 then
41
+ log2
42
+ else
43
+ log2 + 1
44
+
45
+ /** A common language to use when matching definitions written in different
46
+ * languages.
47
+ */
48
+ def commonLanguage (x : SourceLanguage , y : SourceLanguage ): SourceLanguage =
49
+ if x.ordinal > y.ordinal then x else y
50
+ end SourceLanguage
51
+
17
52
/** Erased types are:
18
53
*
19
54
* ErasedValueType
@@ -107,28 +142,29 @@ object TypeErasure {
107
142
}
108
143
}
109
144
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 )
145
+ private def erasureIdx (sourceLanguage : SourceLanguage , semiEraseVCs : Boolean , isConstructor : Boolean , wildcardOK : Boolean ) =
146
+ extension (b : Boolean ) def toInt = if b then 1 else 0
147
+ wildcardOK.toInt
148
+ + (isConstructor.toInt << 1 )
149
+ + (semiEraseVCs.toInt << 2 )
150
+ + (sourceLanguage.ordinal << 3 )
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,29 @@ 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)
187
+
188
+ /** The erasure that Scala 2 would use for this type. */
189
+ def scala2Erasure (tp : Type )(using Context ): Type =
190
+ erasureFn(sourceLanguage = SourceLanguage .Scala2 , semiEraseVCs = true , isConstructor = false , wildcardOK = false )(tp)(using preErasureCtx)
151
191
152
192
/** Like value class erasure, but value classes erase to their underlying type erasure */
153
193
def fullErasure (tp : Type )(using Context ): Type =
154
194
valueErasure(tp) match
155
195
case ErasedValueType (_, underlying) => erasure(underlying)
156
196
case etp => etp
157
197
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 )
198
+ def sigName (tp : Type , sourceLanguage : SourceLanguage )(using Context ): TypeName = {
199
+ val normTp = tp.translateFromRepeated(toArray = sourceLanguage. isJava)
200
+ val erase = erasureFn(sourceLanguage , semiEraseVCs = ! sourceLanguage.isJava , isConstructor = false , wildcardOK = true )
161
201
erase.sigName(normTp)(using preErasureCtx)
162
202
}
163
203
@@ -181,15 +221,13 @@ object TypeErasure {
181
221
* - For $asInstanceOf : [T]T
182
222
* - For $isInstanceOf : [T]Boolean
183
223
* - 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.
224
+ *
225
+ * `sourceLanguage`, `isConstructor` and `semiEraseVCs` are set based on the symbol.
188
226
*/
189
227
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 )
228
+ val sourceLanguage = SourceLanguage ( sym)
229
+ val semiEraseVCs = ! sourceLanguage. isJava // Java sees our value classes as regular classes.
230
+ val erase = erasureFn(sourceLanguage , semiEraseVCs, sym.isConstructor, wildcardOK = false )
193
231
194
232
def eraseParamBounds (tp : PolyType ): Type =
195
233
tp.derivedLambdaType(
@@ -391,18 +429,20 @@ object TypeErasure {
391
429
case _ => false
392
430
}
393
431
}
432
+
394
433
import TypeErasure ._
395
434
396
435
/**
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).
436
+ * @param sourceLanguage Adapt our erasure rules to mimic what the given language
437
+ * would do.
438
+ * @param semiEraseVCs If true, value classes are semi-erased to ErasedValueType
439
+ * (they will be fully erased in [[ElimErasedValueType ]]).
440
+ * If false, they are erased like normal classes.
441
+ * @param isConstructor Argument forms part of the type of a constructor
442
+ * @param wildcardOK Wildcards are acceptable (true when using the erasure
443
+ * for computing a signature name).
404
444
*/
405
- class TypeErasure (isJava : Boolean , semiEraseVCs : Boolean , isConstructor : Boolean , wildcardOK : Boolean ) {
445
+ class TypeErasure (sourceLanguage : SourceLanguage , semiEraseVCs : Boolean , isConstructor : Boolean , wildcardOK : Boolean ) {
406
446
407
447
/** The erasure |T| of a type T. This is:
408
448
*
@@ -450,7 +490,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
450
490
val tycon = tp.tycon
451
491
if (tycon.isRef(defn.ArrayClass )) eraseArray(tp)
452
492
else if (tycon.isRef(defn.PairClass )) erasePair(tp)
453
- else if (tp.isRepeatedParam) apply(tp.translateFromRepeated(toArray = isJava))
493
+ else if (tp.isRepeatedParam) apply(tp.translateFromRepeated(toArray = sourceLanguage. isJava))
454
494
else if (semiEraseVCs && isDerivedValueClass(tycon.classSymbol)) eraseDerivedValueClass(tp)
455
495
else apply(tp.translucentSuperType)
456
496
case _ : TermRef | _ : ThisType =>
@@ -467,13 +507,16 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
467
507
this (defn.FunctionType (paramss.head.length, isContextual = res.isImplicitMethod, isErased = res.isErasedMethod))
468
508
case tp : TypeProxy =>
469
509
this (tp.underlying)
470
- case AndType (tp1, tp2) =>
471
- erasedGlb(this (tp1), this (tp2), isJava)
510
+ case tp @ AndType (tp1, tp2) =>
511
+ if sourceLanguage.isScala2 then
512
+ this (Scala2Erasure .intersectionDominator(Scala2Erasure .flattenedParents(tp)))
513
+ else
514
+ erasedGlb(this (tp1), this (tp2), isJava = sourceLanguage.isJava)
472
515
case OrType (tp1, tp2) =>
473
516
TypeComparer .orType(this (tp1), this (tp2), isErased = true )
474
517
case tp : MethodType =>
475
518
def paramErasure (tpToErase : Type ) =
476
- erasureFn(isJava , semiEraseVCs, isConstructor, wildcardOK)(tpToErase)
519
+ erasureFn(sourceLanguage , semiEraseVCs, isConstructor, wildcardOK)(tpToErase)
477
520
val (names, formals0) = if (tp.isErasedMethod) (Nil , Nil ) else (tp.paramNames, tp.paramInfos)
478
521
val formals = formals0.mapConserve(paramErasure)
479
522
eraseResult(tp.resultType) match {
@@ -516,8 +559,8 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
516
559
private def eraseArray (tp : Type )(using Context ) = {
517
560
val defn .ArrayOf (elemtp) = tp
518
561
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))
562
+ else if (isUnboundedGeneric(elemtp) && ! sourceLanguage. isJava) defn.ObjectType
563
+ else JavaArrayType (erasureFn(sourceLanguage , semiEraseVCs = false , isConstructor, wildcardOK)(elemtp))
521
564
}
522
565
523
566
private def erasePair (tp : Type )(using Context ): Type = {
@@ -544,7 +587,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
544
587
// See doc comment for ElimByName for speculation how we could improve this.
545
588
else
546
589
MethodType (Nil , Nil ,
547
- eraseResult(sym.info.finalResultType.translateFromRepeated(toArray = isJava)))
590
+ eraseResult(sym.info.finalResultType.translateFromRepeated(toArray = sourceLanguage. isJava)))
548
591
case tp1 : PolyType =>
549
592
eraseResult(tp1.resultType) match
550
593
case rt : MethodType => rt
@@ -596,7 +639,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
596
639
// correctly (see SIP-15 and [[Erasure.Boxing.adaptToType]]), so the result type of a
597
640
// constructor method should not be semi-erased.
598
641
if semiEraseVCs && isConstructor && ! tp.isInstanceOf [MethodOrPoly ] then
599
- erasureFn(isJava , semiEraseVCs = false , isConstructor, wildcardOK).eraseResult(tp)
642
+ erasureFn(sourceLanguage , semiEraseVCs = false , isConstructor, wildcardOK).eraseResult(tp)
600
643
else tp match
601
644
case tp : TypeRef =>
602
645
val sym = tp.symbol
@@ -624,7 +667,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
624
667
if (! info.exists) assert(false , i " undefined: $tp with symbol $sym" )
625
668
return sigName(info)
626
669
}
627
- if (isDerivedValueClass(sym)) {
670
+ if (semiEraseVCs && isDerivedValueClass(sym)) {
628
671
val erasedVCRef = eraseDerivedValueClass(tp)
629
672
if (erasedVCRef.exists) return sigName(erasedVCRef)
630
673
}
0 commit comments