Skip to content

Commit eb9bf1a

Browse files
committed
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 79d9a6f commit eb9bf1a

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
@@ -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,10 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
687689
tp
688690
}
689691

692+
private def underlyingOfTermRef(tp: TermRef)(using Context) = tp.widen match
693+
case tpw @ MethodType(Nil) if tp.symbol.isGetter => tpw.resultType
694+
case tpw => tpw
695+
690696
private def eraseArray(tp: Type)(using Context) = {
691697
val defn.ArrayOf(elemtp) = tp: @unchecked
692698
if (isGenericArrayElement(elemtp, isScala2 = sourceLanguage.isScala2)) defn.ObjectType
@@ -832,7 +838,7 @@ class TypeErasure(sourceLanguage: SourceLanguage, semiEraseVCs: Boolean, isConst
832838
case JavaArrayType(elem) =>
833839
sigName(elem) ++ "[]"
834840
case tp: TermRef =>
835-
sigName(tp.widen)
841+
sigName(underlyingOfTermRef(tp))
836842
case ExprType(rt) =>
837843
sigName(defn.FunctionOf(Nil, rt))
838844
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)