Skip to content

Commit a690de2

Browse files
oderskysmarter
authored andcommitted
Invalidate some denotations in earlier phases
This addresses the specific problem raised by #10044: A denotation of a NamedType goes from a UniqueRefDenotation to a SymDenotation after erasure and is then not reset to the original since the SymDenotation is valid at all phases. We remember in this case in the NamedType the first phase in which the SymDenotaton is valid and force a recompute in earlier phases. Fixes #10044
1 parent 5a38efe commit a690de2

File tree

2 files changed

+36
-0
lines changed

2 files changed

+36
-0
lines changed

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

+24
Original file line numberDiff line numberDiff line change
@@ -2097,6 +2097,7 @@ object Types {
20972097
private var lastDenotation: Denotation | Null = null
20982098
private var lastSymbol: Symbol | Null = null
20992099
private var checkedPeriod: Period = Nowhere
2100+
private var firstValidPhaseId: Int = 0
21002101
private var myStableHash: Byte = 0
21012102
private var mySignature: Signature = _
21022103
private var mySignatureRunId: Int = NoRunId
@@ -2212,6 +2213,8 @@ object Types {
22122213
val now = ctx.period
22132214
// Even if checkedPeriod == now we still need to recheck lastDenotation.validFor
22142215
// as it may have been mutated by SymDenotation#installAfter
2216+
if firstValidPhaseId > now.phaseId then
2217+
revalidateDenot()
22152218
if (checkedPeriod != Nowhere && lastDenotation.nn.validFor.contains(now)) {
22162219
checkedPeriod = now
22172220
lastDenotation.nn
@@ -2340,6 +2343,18 @@ object Types {
23402343
def recomputeDenot()(using Context): Unit =
23412344
setDenot(memberDenot(name, allowPrivate = !symbol.exists || symbol.is(Private)))
23422345

2346+
/** Try to recompute denotation and reset `firstValidPhaseId`.
2347+
* @pre Current phase id < firstValidPhaseId
2348+
*/
2349+
def revalidateDenot()(using Context): Unit =
2350+
if (prefix ne NoPrefix) then
2351+
core.println(i"revalidate $prefix . $name, $firstValidPhaseId > ${ctx.phaseId}")
2352+
val newDenot = memberDenot(name, allowPrivate =
2353+
lastSymbol == null || !lastSymbol.nn.exists || lastSymbol.nn.is(Private))
2354+
if newDenot.exists then
2355+
setDenot(newDenot)
2356+
firstValidPhaseId = ctx.phaseId
2357+
23432358
private def setDenot(denot: Denotation)(using Context): Unit = {
23442359
if (Config.checkNoDoubleBindings)
23452360
if (ctx.settings.YnoDoubleBindings.value)
@@ -2570,6 +2585,15 @@ object Types {
25702585
|| adapted.info.eq(denot.info))
25712586
adapted
25722587
else this
2588+
val lastDenot = result.lastDenotation
2589+
if denot.isInstanceOf[SymDenotation] && lastDenot != null && !lastDenot.isInstanceOf[SymDenotation] then
2590+
// In this case the new SymDenotation might be valid for all phases, which means
2591+
// we would not recompute the denotation when travelling to an earlier phase, maybe
2592+
// in the next run. We fix that problem by recording in this case in the NamedType
2593+
// the phase from which the denotation is valid. Taking the denotation at an earlier
2594+
// phase will then lead to a `revalidateDenot`.
2595+
core.println(i"overwrite ${result.toString} / ${result.lastDenotation} with $denot")
2596+
result.firstValidPhaseId = ctx.phaseId
25732597
result.setDenot(denot)
25742598
result.asInstanceOf[ThisType]
25752599
}

compiler/test-resources/repl/i10044

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
scala> object Foo { opaque type Bar = Int; object Bar { extension (b: Bar) def flip: Bar = -b; def apply(x: Int): Bar = x }}
2+
// defined object Foo
3+
scala> val a = Foo.Bar(42)
4+
val a: Foo.Bar = 42
5+
scala> val b = a.flip
6+
val b: Foo.Bar = -42
7+
scala> val c = b.flip
8+
val c: Foo.Bar = 42
9+
scala> val d = c.flip
10+
val d: Foo.Bar = -42
11+
scala> val e = d.flip
12+
val e: Foo.Bar = 42

0 commit comments

Comments
 (0)