Skip to content

Commit 522d083

Browse files
committed
check defined in src for cache in companion
1 parent 2333316 commit 522d083

File tree

8 files changed

+63
-19
lines changed

8 files changed

+63
-19
lines changed

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

+16-8
Original file line numberDiff line numberDiff line change
@@ -132,16 +132,24 @@ object Symbols {
132132
private[core] def defRunId: RunId =
133133
if (lastDenot == null) NoRunId else lastDenot.validFor.runId
134134

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

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

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

+13-11
Original file line numberDiff line numberDiff line change
@@ -111,18 +111,20 @@ object SymUtils:
111111
self.isAllOf(Given | Method) && isCodefined(self.info)
112112

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

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

compiler/test-resources/repl/i14540

+7
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

+1
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

+7
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
+6
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

+7
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

+6
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)