Skip to content

Commit

Permalink
Fix validity period of derived SingleDenotations (#19983)
Browse files Browse the repository at this point in the history
When running:

    val f: ( => Int) => Int = i => i ; f(1)

twice in the REPL, the second time crashed with a ClassCastException.

The difference is that in the second run, the denotation for `f.apply`
is created from the SymDenotation for `Function1#apply` which already
exists and is known to be valid in every phase, but that doesn't mean
that the derived denotation for `f.apply` has the same validity: unlike
the SymDenotation it needs to be transformed (in particular to run the
`ElimByName` transformer).

It turns out that there were multiple places in the compiler where we
created a new denotation with a validity based on the existing one when
this was not legitimate. I've gone through all these places and replaced
them by `currentStablePeriod`.

Fixes #18756.
  • Loading branch information
odersky authored Mar 29, 2024
2 parents c251f36 + 49571fa commit fecfbda
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 12 deletions.
14 changes: 7 additions & 7 deletions compiler/src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,7 @@ object Denotations {

/** A non-overloaded denotation */
abstract class SingleDenotation(symbol: Symbol, initInfo: Type, isType: Boolean) extends Denotation(symbol, initInfo, isType) {
protected def newLikeThis(symbol: Symbol, info: Type, pre: Type, isRefinedMethod: Boolean): SingleDenotation
protected def newLikeThis(symbol: Symbol, info: Type, pre: Type, isRefinedMethod: Boolean)(using Context): SingleDenotation

final def name(using Context): Name = symbol.name

Expand Down Expand Up @@ -1162,11 +1162,11 @@ object Denotations {
prefix: Type) extends NonSymSingleDenotation(symbol, initInfo, prefix) {
validFor = initValidFor
override def hasUniqueSym: Boolean = true
protected def newLikeThis(s: Symbol, i: Type, pre: Type, isRefinedMethod: Boolean): SingleDenotation =
protected def newLikeThis(s: Symbol, i: Type, pre: Type, isRefinedMethod: Boolean)(using Context): SingleDenotation =
if isRefinedMethod then
new JointRefDenotation(s, i, validFor, pre, isRefinedMethod)
new JointRefDenotation(s, i, currentStablePeriod, pre, isRefinedMethod)
else
new UniqueRefDenotation(s, i, validFor, pre)
new UniqueRefDenotation(s, i, currentStablePeriod, pre)
}

class JointRefDenotation(
Expand All @@ -1177,15 +1177,15 @@ object Denotations {
override val isRefinedMethod: Boolean) extends NonSymSingleDenotation(symbol, initInfo, prefix) {
validFor = initValidFor
override def hasUniqueSym: Boolean = false
protected def newLikeThis(s: Symbol, i: Type, pre: Type, isRefinedMethod: Boolean): SingleDenotation =
new JointRefDenotation(s, i, validFor, pre, isRefinedMethod)
protected def newLikeThis(s: Symbol, i: Type, pre: Type, isRefinedMethod: Boolean)(using Context): SingleDenotation =
new JointRefDenotation(s, i, currentStablePeriod, pre, isRefinedMethod)
}

class ErrorDenotation(using Context) extends NonSymSingleDenotation(NoSymbol, NoType, NoType) {
override def exists: Boolean = false
override def hasUniqueSym: Boolean = false
validFor = Period.allInRun(ctx.runId)
protected def newLikeThis(s: Symbol, i: Type, pre: Type, isRefinedMethod: Boolean): SingleDenotation =
protected def newLikeThis(s: Symbol, i: Type, pre: Type, isRefinedMethod: Boolean)(using Context): SingleDenotation =
this
}

Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1624,11 +1624,11 @@ object SymDenotations {

// ----- copies and transforms ----------------------------------------

protected def newLikeThis(s: Symbol, i: Type, pre: Type, isRefinedMethod: Boolean): SingleDenotation =
protected def newLikeThis(s: Symbol, i: Type, pre: Type, isRefinedMethod: Boolean)(using Context): SingleDenotation =
if isRefinedMethod then
new JointRefDenotation(s, i, validFor, pre, isRefinedMethod)
new JointRefDenotation(s, i, currentStablePeriod, pre, isRefinedMethod)
else
new UniqueRefDenotation(s, i, validFor, pre)
new UniqueRefDenotation(s, i, currentStablePeriod, pre)

/** Copy this denotation, overriding selective fields */
final def copySymDenotation(
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -905,7 +905,7 @@ object Types extends TypeUtils {
def goSuper(tp: SuperType) = go(tp.underlying) match {
case d: JointRefDenotation =>
typr.println(i"redirecting super.$name from $tp to ${d.symbol.showLocated}")
new UniqueRefDenotation(d.symbol, tp.memberInfo(d.symbol), d.validFor, pre)
new UniqueRefDenotation(d.symbol, tp.memberInfo(d.symbol), currentStablePeriod, pre)
case d => d
}

Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/transform/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import core.Types.*
import core.Names.*
import core.StdNames.*
import core.NameOps.*
import core.Periods.currentStablePeriod
import core.NameKinds.{AdaptedClosureName, BodyRetainerName, DirectMethName}
import core.Scopes.newScopeWith
import core.Decorators.*
Expand Down Expand Up @@ -132,7 +133,7 @@ class Erasure extends Phase with DenotTransformer {
}
case ref: JointRefDenotation =>
new UniqueRefDenotation(
ref.symbol, transformInfo(ref.symbol, ref.symbol.info), ref.validFor, ref.prefix)
ref.symbol, transformInfo(ref.symbol, ref.symbol.info), currentStablePeriod, ref.prefix)
case _ =>
ref.derivedSingleDenotation(ref.symbol, transformInfo(ref.symbol, ref.symbol.info))
}
Expand Down
17 changes: 17 additions & 0 deletions compiler/test-resources/repl/i18756
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
scala> object A { val f: ( => Int) => Int = i => i ; f(1) }
// defined object A

scala> A.f(1)
val res0: Int = 1

scala> A.f(1)
val res1: Int = 1

scala> object B { val f: ( => Int) => Int = i => i ; f(1) }
// defined object B

scala> B.f(1)
val res2: Int = 1

scala> B.f(1)
val res3: Int = 1

0 comments on commit fecfbda

Please sign in to comment.