@@ -10,10 +10,45 @@ import transform.ExplicitOuter._
1010import transform .ValueClasses ._
1111import transform .TypeUtils ._
1212import transform .ContextFunctionResults ._
13+ import unpickleScala2 .Scala2Erasure
1314import Decorators ._
1415import Definitions .MaxImplementedFunctionArity
1516import scala .annotation .tailrec
1617
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+
1752/** Erased types are:
1853 *
1954 * ErasedValueType
@@ -107,28 +142,29 @@ object TypeErasure {
107142 }
108143 }
109144
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 )
115151
116- private val erasures = new Array [TypeErasure ](16 )
152+ private val erasures = new Array [TypeErasure ](1 << ( SourceLanguage .bits + 3 ) )
117153
118- for {
119- isJava <- List ( false , true )
154+ for
155+ sourceLanguage <- SourceLanguage .values
120156 semiEraseVCs <- List (false , true )
121157 isConstructor <- List (false , true )
122158 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)
126162
127163 /** Produces an erasure function. See the documentation of the class [[TypeErasure ]]
128164 * for a description of each parameter.
129165 */
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))
132168
133169 /** The current context with a phase no later than erasure */
134170 def preErasureCtx (using Context ) =
@@ -139,25 +175,29 @@ object TypeErasure {
139175 * @param tp The type to erase.
140176 */
141177 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)
143179
144180 /** The value class erasure of a Scala type, where value classes are semi-erased to
145181 * ErasedValueType (they will be fully erased in [[ElimErasedValueType ]]).
146182 *
147183 * @param tp The type to erase.
148184 */
149185 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)
151191
152192 /** Like value class erasure, but value classes erase to their underlying type erasure */
153193 def fullErasure (tp : Type )(using Context ): Type =
154194 valueErasure(tp) match
155195 case ErasedValueType (_, underlying) => erasure(underlying)
156196 case etp => etp
157197
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 )
161201 erase.sigName(normTp)(using preErasureCtx)
162202 }
163203
@@ -181,15 +221,13 @@ object TypeErasure {
181221 * - For $asInstanceOf : [T]T
182222 * - For $isInstanceOf : [T]Boolean
183223 * - 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.
188226 */
189227 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 )
193231
194232 def eraseParamBounds (tp : PolyType ): Type =
195233 tp.derivedLambdaType(
@@ -391,18 +429,20 @@ object TypeErasure {
391429 case _ => false
392430 }
393431}
432+
394433import TypeErasure ._
395434
396435/**
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).
404444 */
405- class TypeErasure (isJava : Boolean , semiEraseVCs : Boolean , isConstructor : Boolean , wildcardOK : Boolean ) {
445+ class TypeErasure (sourceLanguage : SourceLanguage , semiEraseVCs : Boolean , isConstructor : Boolean , wildcardOK : Boolean ) {
406446
407447 /** The erasure |T| of a type T. This is:
408448 *
@@ -450,7 +490,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
450490 val tycon = tp.tycon
451491 if (tycon.isRef(defn.ArrayClass )) eraseArray(tp)
452492 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))
454494 else if (semiEraseVCs && isDerivedValueClass(tycon.classSymbol)) eraseDerivedValueClass(tp)
455495 else apply(tp.translucentSuperType)
456496 case _ : TermRef | _ : ThisType =>
@@ -467,13 +507,16 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
467507 this (defn.FunctionType (paramss.head.length, isContextual = res.isImplicitMethod, isErased = res.isErasedMethod))
468508 case tp : TypeProxy =>
469509 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)
472515 case OrType (tp1, tp2) =>
473516 TypeComparer .orType(this (tp1), this (tp2), isErased = true )
474517 case tp : MethodType =>
475518 def paramErasure (tpToErase : Type ) =
476- erasureFn(isJava , semiEraseVCs, isConstructor, wildcardOK)(tpToErase)
519+ erasureFn(sourceLanguage , semiEraseVCs, isConstructor, wildcardOK)(tpToErase)
477520 val (names, formals0) = if (tp.isErasedMethod) (Nil , Nil ) else (tp.paramNames, tp.paramInfos)
478521 val formals = formals0.mapConserve(paramErasure)
479522 eraseResult(tp.resultType) match {
@@ -516,8 +559,8 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
516559 private def eraseArray (tp : Type )(using Context ) = {
517560 val defn .ArrayOf (elemtp) = tp
518561 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))
521564 }
522565
523566 private def erasePair (tp : Type )(using Context ): Type = {
@@ -544,7 +587,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
544587 // See doc comment for ElimByName for speculation how we could improve this.
545588 else
546589 MethodType (Nil , Nil ,
547- eraseResult(sym.info.finalResultType.translateFromRepeated(toArray = isJava)))
590+ eraseResult(sym.info.finalResultType.translateFromRepeated(toArray = sourceLanguage. isJava)))
548591 case tp1 : PolyType =>
549592 eraseResult(tp1.resultType) match
550593 case rt : MethodType => rt
@@ -596,7 +639,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
596639 // correctly (see SIP-15 and [[Erasure.Boxing.adaptToType]]), so the result type of a
597640 // constructor method should not be semi-erased.
598641 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)
600643 else tp match
601644 case tp : TypeRef =>
602645 val sym = tp.symbol
@@ -624,7 +667,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
624667 if (! info.exists) assert(false , i " undefined: $tp with symbol $sym" )
625668 return sigName(info)
626669 }
627- if (isDerivedValueClass(sym)) {
670+ if (semiEraseVCs && isDerivedValueClass(sym)) {
628671 val erasedVCRef = eraseDerivedValueClass(tp)
629672 if (erasedVCRef.exists) return sigName(erasedVCRef)
630673 }
0 commit comments