Skip to content

Commit

Permalink
Fix validity period of derived SingleDenotations
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
smarter committed Mar 19, 2024
1 parent 3d5cf9c commit e7a32f9
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 13 deletions.
18 changes: 10 additions & 8 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 @@ -1104,7 +1104,9 @@ object Denotations {
&& (derivedInfo eq info) && !needsPrefix then
this
else
derivedSingleDenotation(symbol, derivedInfo, pre)
val d = derivedSingleDenotation(symbol, derivedInfo, pre)
d.validFor = currentStablePeriod
d
end derived

// Tt could happen that we see the symbol with prefix `this` as a member a different class
Expand Down Expand Up @@ -1162,11 +1164,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 +1179,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 e7a32f9

Please sign in to comment.