Skip to content

Commit ae6c7b8

Browse files
oderskyKordyjan
authored andcommitted
Harden erasure of TermRefs
If a getter TermRef has a MethodType, erase it to the erasure of the method result type. Fixes #15649 Since the fault in #15649 is highly non-deterministic, I can only guess what the reason was. I think what happened was that the TermRef was created very late, when the TermRef was already a getter. I.e. after phase `Getters`, which is itself after `Erasure`. The whole thing was launched from the code that generates static forwarders in the backend. Type erasure is run at erasure phase, but if there is no previous denotation of the `TermRef`, it will keep the first one it read, which would have the `MethodType` as underlying type. The end result was that the `TermRef` was erased to a method type, where it should have been erased to the result type. Consequently, we ended up with a MethodType as the parameter type of another method, which is unexpected. But all that is just a guess. I am not even sure this PR will fix the problem since it comes up so rarely.
1 parent fe63a15 commit ae6c7b8

File tree

2 files changed

+19
-2
lines changed

2 files changed

+19
-2
lines changed

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,9 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
599599
else if (tp.isRepeatedParam) apply(tp.translateFromRepeated(toArray = sourceLanguage.isJava))
600600
else if (semiEraseVCs && isDerivedValueClass(tycon.classSymbol)) eraseDerivedValueClass(tp)
601601
else apply(tp.translucentSuperType)
602-
case _: TermRef | _: ThisType =>
602+
case tp: TermRef =>
603+
this(underlyingOfTermRef(tp))
604+
case _: ThisType =>
603605
this(tp.widen)
604606
case SuperType(thistpe, supertpe) =>
605607
SuperType(this(thistpe), this(supertpe))
@@ -684,6 +686,10 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
684686
tp
685687
}
686688

689+
private def underlyingOfTermRef(tp: TermRef)(using Context) = tp.widen match
690+
case tpw @ MethodType(Nil) if tp.symbol.isGetter => tpw.resultType
691+
case tpw => tpw
692+
687693
private def eraseArray(tp: Type)(using Context) = {
688694
val defn.ArrayOf(elemtp) = tp: @unchecked
689695
if (isGenericArrayElement(elemtp, isScala2 = sourceLanguage.isScala2)) defn.ObjectType
@@ -829,7 +835,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
829835
case JavaArrayType(elem) =>
830836
sigName(elem) ++ "[]"
831837
case tp: TermRef =>
832-
sigName(tp.widen)
838+
sigName(underlyingOfTermRef(tp))
833839
case ExprType(rt) =>
834840
sigName(defn.FunctionOf(Nil, rt))
835841
case tp: TypeVar =>

tests/pos/i15649.scala

Lines changed: 11 additions & 0 deletions
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)