Skip to content

Commit e68ed0d

Browse files
committed
check defined in src for cache in companion
1 parent 2ace654 commit e68ed0d

File tree

8 files changed

+63
-19
lines changed

8 files changed

+63
-19
lines changed

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

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -135,16 +135,24 @@ object Symbols {
135135
private[core] def defRunId: RunId =
136136
if (lastDenot == null) NoRunId else lastDenot.validFor.runId
137137

138+
private inline def associatedFileMatches(inline filter: AbstractFile => Boolean)(using Context): Boolean =
139+
try
140+
val file = associatedFile
141+
file != null && filter(file)
142+
catch case ex: StaleSymbol =>
143+
// can happen for constructor proxy companions. Test case is pos-macros/i9484.
144+
false
145+
138146
/** Does this symbol come from a currently compiled source file? */
139147
final def isDefinedInCurrentRun(using Context): Boolean =
140-
span.exists && defRunId == ctx.runId && {
141-
try
142-
val file = associatedFile
143-
file != null && ctx.run.files.contains(file)
144-
catch case ex: StaleSymbol =>
145-
// can happen for constructor proxy companions. Test case is pos-macros/i9484.
146-
false
147-
}
148+
span.exists && defRunId == ctx.runId && associatedFileMatches(ctx.run.files.contains)
149+
150+
/** Is this symbol valid in the current run and has an associated file that is
151+
* not a binary file. e.g. This will return true for
152+
* symbols defined by the user in a prior run of the REPL, that are still valid.
153+
*/
154+
final def isDefinedInSource(using Context): Boolean =
155+
span.exists && isValidInCurrentRun && associatedFileMatches(_.extension != "class")
148156

149157
/** Is symbol valid in current run? */
150158
final def isValidInCurrentRun(using Context): Boolean =

compiler/src/dotty/tools/dotc/transform/SymUtils.scala

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -113,18 +113,20 @@ object SymUtils:
113113
self.isAllOf(Given | Method) && isCodefined(self.info)
114114

115115
def useCompanionAsSumMirror(using Context): Boolean =
116+
def companionExtendsSum(using Context): Boolean =
117+
self.linkedClass.isSubClass(defn.Mirror_SumClass)
116118
self.linkedClass.exists
117-
&& !self.is(Scala2x)
118-
&& (
119-
// If the sum type is compiled from source, and `self` is a "generic sum"
120-
// then its companion object will become a sum mirror in `posttyper`. (This method
121-
// can be called from `typer` when summoning a Mirror.)
122-
// However if `self` is from a prior run then we should check that its companion subclasses `Mirror.Sum`.
123-
// e.g. before Scala 3.1, hierarchical sum types were not considered "generic sums", so their
124-
// companion would not cache the mirror. Companions from TASTy will already be typed as `Mirror.Sum`.
125-
self.isDefinedInCurrentRun
126-
|| self.linkedClass.isSubClass(defn.Mirror_SumClass)
127-
)
119+
&& !self.is(Scala2x)
120+
&& (
121+
// If the sum type is compiled from source, and `self` is a "generic sum"
122+
// then its companion object will become a sum mirror in `posttyper`. (This method
123+
// can be called from `typer` when summoning a Mirror.)
124+
// However if `self` is from a binary file, then we should check that its companion
125+
// subclasses `Mirror.Sum`. e.g. before Scala 3.1, hierarchical sum types were not
126+
// considered "generic sums", so their companion would not cache the mirror.
127+
// Companions from TASTy will already be typed as `Mirror.Sum`.
128+
self.isDefinedInSource || companionExtendsSum
129+
)
128130

129131
/** Is this a sealed class or trait for which a sum mirror is generated?
130132
* It must satisfy the following conditions:

compiler/test-resources/repl/i14540

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
scala> enum I14540 { case A }
2+
// defined class I14540
3+
scala> summon[scala.deriving.Mirror.SumOf[I14540]] eq I14540
4+
val res0: Boolean = true
5+
scala> enum I14540B { case A }; summon[scala.deriving.Mirror.SumOf[I14540B]] eq I14540B
6+
// defined class I14540B
7+
val res1: Boolean = true

sbt-test/scala3-backcompat/hierarchical-mirrors/app/Main.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ import scala.deriving.Mirror
55
object Main:
66
def main(args: Array[String]): Unit =
77
val mirrorTop = summon[Mirror.SumOf[lib.Top]]
8+
assert(mirrorTop ne lib.Top) // **NOT** cached in companion - previous run, pre-3.1 tasty dependency
89
assert(mirrorTop.ordinal(lib.Middle()) == 0)

tests/run/i14540-priorRun/Lib_1.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package lib
2+
3+
sealed trait Top
4+
object Top // companion is necessary
5+
6+
case class Middle() extends Top with Bottom
7+
sealed trait Bottom extends Top
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import scala.deriving.Mirror
2+
3+
@main def Test =
4+
val mirrorTop = summon[Mirror.SumOf[lib.Top]]
5+
assert(mirrorTop eq lib.Top) // cached in companion - previous run, tasty dependency
6+
assert(mirrorTop.ordinal(lib.Middle()) == 0)

tests/run/i14540-sameRun/Lib.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package lib
2+
3+
sealed trait Top
4+
object Top // companion is necessary
5+
6+
case class Middle() extends Top with Bottom
7+
sealed trait Bottom extends Top

tests/run/i14540-sameRun/Test.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import scala.deriving.Mirror
2+
3+
@main def Test =
4+
val mirrorTop = summon[Mirror.SumOf[lib.Top]]
5+
assert(mirrorTop eq lib.Top) // cached in companion - same run, source dependency
6+
assert(mirrorTop.ordinal(lib.Middle()) == 0)

0 commit comments

Comments
 (0)