diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index d41c57ab116e..57defeda0a10 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -24,13 +24,8 @@ class Compiler { * all refs to it would become outdated - they could not be dereferenced in the * new phase. * - * After erasure, signature changing denot-transformers are OK because erasure - * will make sure that only term refs with fixed SymDenotations survive beyond it. This - * is possible because: - * - * - splitter has run, so every ident or select refers to a unique symbol - * - after erasure, asSeenFrom is the identity, so every reference has a - * plain SymDenotation, as opposed to a UniqueRefDenotation. + * After erasure, signature changing denot-transformers are OK because signatures + * are never recomputed later than erasure. */ def phases: List[List[Phase]] = frontendPhases ::: picklerPhases ::: transformPhases ::: backendPhases diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 7d57b4eb2740..3d2a77a05c82 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -1079,17 +1079,13 @@ object Types { * @param checkClassInfo if true we check that ClassInfos are within bounds of abstract types */ final def overrides(that: Type, relaxedCheck: Boolean, matchLoosely: => Boolean, checkClassInfo: Boolean = true)(using Context): Boolean = { - def widenNullary(tp: Type) = tp match { - case tp @ MethodType(Nil) => tp.resultType - case _ => tp - } val overrideCtx = if relaxedCheck then ctx.relaxedOverrideContext else ctx inContext(overrideCtx) { !checkClassInfo && this.isInstanceOf[ClassInfo] || (this.widenExpr frozen_<:< that.widenExpr) || matchLoosely && { - val this1 = widenNullary(this) - val that1 = widenNullary(that) + val this1 = this.widenNullaryMethod + val that1 = that.widenNullaryMethod ((this1 `ne` this) || (that1 `ne` that)) && this1.overrides(that1, relaxedCheck, false, checkClassInfo) } } @@ -1326,6 +1322,11 @@ object Types { this } + /** If this is a nullary method type, its result type */ + def widenNullaryMethod(using Context): Type = this match + case tp @ MethodType(Nil) => tp.resType + case _ => this + /** The singleton types that must or may be in this type. @see Atoms. * Overridden and cached in OrType. */ @@ -2561,7 +2562,7 @@ object Types { * A test case is neg/opaque-self-encoding.scala. */ final def withDenot(denot: Denotation)(using Context): ThisType = - if (denot.exists) { + if denot.exists then val adapted = withSym(denot.symbol) val result = if (adapted.eq(this) @@ -2570,9 +2571,25 @@ object Types { || adapted.info.eq(denot.info)) adapted else this - result.setDenot(denot) + val lastDenot = result.lastDenotation + denot match + case denot: SymDenotation + if denot.validFor.firstPhaseId < ctx.phase.id + && lastDenot != null + && lastDenot.validFor.lastPhaseId > denot.validFor.firstPhaseId + && !lastDenot.isInstanceOf[SymDenotation] => + // In this case the new SymDenotation might be valid for all phases, which means + // we would not recompute the denotation when travelling to an earlier phase, maybe + // in the next run. We fix that problem by creating a UniqueRefDenotation instead. + core.println(i"overwrite ${result.toString} / ${result.lastDenotation}, ${result.lastDenotation.getClass} with $denot at ${ctx.phaseId}") + result.setDenot( + UniqueRefDenotation( + denot.symbol, denot.info, + Period(ctx.runId, ctx.phaseId, denot.validFor.lastPhaseId), + this.prefix)) + case _ => + result.setDenot(denot) result.asInstanceOf[ThisType] - } else // don't assign NoDenotation, we might need to recover later. Test case is pos/avoid.scala. this diff --git a/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala b/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala index 6ce708068393..0aa5b2b86b51 100644 --- a/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala +++ b/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala @@ -189,7 +189,9 @@ object GenericSignatures { fullNameInSig(tp.typeSymbol) builder.append(';') case _ => - boxedSig(tp) + boxedSig(tp.widenDealias.widenNullaryMethod) + // `tp` might be a singleton type referring to a getter. + // Hence the widenNullaryMethod. } if (pre.exists) { diff --git a/compiler/test-resources/repl/i10044 b/compiler/test-resources/repl/i10044 new file mode 100644 index 000000000000..3d0fe506ea32 --- /dev/null +++ b/compiler/test-resources/repl/i10044 @@ -0,0 +1,12 @@ +scala> object Foo { opaque type Bar = Int; object Bar { extension (b: Bar) def flip: Bar = -b; def apply(x: Int): Bar = x }} +// defined object Foo +scala> val a = Foo.Bar(42) +val a: Foo.Bar = 42 +scala> val b = a.flip +val b: Foo.Bar = -42 +scala> val c = b.flip +val c: Foo.Bar = 42 +scala> val d = c.flip +val d: Foo.Bar = -42 +scala> val e = d.flip +val e: Foo.Bar = 42