Skip to content

Commit 35dd912

Browse files
authored
Merge pull request #15751 from dotty-staging/backport-15658
Backport #15658: Harden erasure of TermRefs
2 parents 0b25662 + 8f86525 commit 35dd912

File tree

2 files changed

+28
-2
lines changed

2 files changed

+28
-2
lines changed

compiler/src/dotty/tools/dotc/core/TypeErasure.scala

+17-2
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,9 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
602602
else if (tp.isRepeatedParam) apply(tp.translateFromRepeated(toArray = sourceLanguage.isJava))
603603
else if (semiEraseVCs && isDerivedValueClass(tycon.classSymbol)) eraseDerivedValueClass(tp)
604604
else apply(tp.translucentSuperType)
605-
case _: TermRef | _: ThisType =>
605+
case tp: TermRef =>
606+
this(underlyingOfTermRef(tp))
607+
case _: ThisType =>
606608
this(tp.widen)
607609
case SuperType(thistpe, supertpe) =>
608610
SuperType(this(thistpe), this(supertpe))
@@ -687,6 +689,19 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
687689
tp
688690
}
689691

692+
/** Widen term ref, skipping any `()` parameter of an eventual getter. Used to erase a TermRef.
693+
* Since getters are introduced after erasure, one would think that erasing a TermRef
694+
* could just use `widen`. However, it's possible that the TermRef got read from a class
695+
* file after Getters (i.e. in the backend). In that case, the reference will not get
696+
* an earlier denotation even when time travelling forward to erasure. Hence, we
697+
* need to take the extra precaution of going from nullary method types to their resuls.
698+
* A test case where this is needed is pos/i15649.scala, which fails non-deterministically
699+
* if `underlyingOfTermRef` is replaced by `widen`.
700+
*/
701+
private def underlyingOfTermRef(tp: TermRef)(using Context) = tp.widen match
702+
case tpw @ MethodType(Nil) if tp.symbol.isGetter => tpw.resultType
703+
case tpw => tpw
704+
690705
private def eraseArray(tp: Type)(using Context) = {
691706
val defn.ArrayOf(elemtp) = tp: @unchecked
692707
if (isGenericArrayElement(elemtp, isScala2 = sourceLanguage.isScala2)) defn.ObjectType
@@ -832,7 +847,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
832847
case JavaArrayType(elem) =>
833848
sigName(elem) ++ "[]"
834849
case tp: TermRef =>
835-
sigName(tp.widen)
850+
sigName(underlyingOfTermRef(tp))
836851
case ExprType(rt) =>
837852
sigName(defn.FunctionOf(Nil, rt))
838853
case tp: TypeVar =>

tests/pos/i15649.scala

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
trait ConfigSourceModule:
2+
object ConfigSource:
3+
class R
4+
5+
object M extends ConfigSourceModule
6+
7+
object Foo:
8+
implicit class FromConfigSource(c: M.ConfigSource.type)
9+
10+
object FooBar: // problem disappears if we rename as `Bar`
11+
def foo: M.ConfigSource.R = new M.ConfigSource.R // problem disappears if we use `???` as rhs

0 commit comments

Comments
 (0)