From 7e8323e2abc77a583d060687aeca407eb7d3aa0a Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Tue, 19 Mar 2024 18:38:49 +0100 Subject: [PATCH] Fix validity period of derived SingleDenotations 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. [Cherry-picked 49571faa98739d209f5c3c6ef88e9c7950c50242] --- .../src/dotty/tools/dotc/core/Denotations.scala | 14 +++++++------- .../dotty/tools/dotc/core/SymDenotations.scala | 6 +++--- compiler/src/dotty/tools/dotc/core/Types.scala | 2 +- .../dotty/tools/dotc/transform/Erasure.scala | 3 ++- compiler/test-resources/repl/i18756 | 17 +++++++++++++++++ 5 files changed, 30 insertions(+), 12 deletions(-) create mode 100644 compiler/test-resources/repl/i18756 diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 6170bc97d3df..3561a93857f8 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -575,7 +575,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 @@ -1153,11 +1153,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( @@ -1168,15 +1168,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 } diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index feecb8483bcd..0a64ab7d0cdd 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1618,11 +1618,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( diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 012f63314054..764a7a84a628 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -875,7 +875,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 } diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index f8a96da1c40a..906988089caa 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -12,6 +12,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.* @@ -131,7 +132,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)) } diff --git a/compiler/test-resources/repl/i18756 b/compiler/test-resources/repl/i18756 new file mode 100644 index 000000000000..56be353e67f3 --- /dev/null +++ b/compiler/test-resources/repl/i18756 @@ -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