diff --git a/compiler/src/dotty/tools/dotc/core/Symbols.scala b/compiler/src/dotty/tools/dotc/core/Symbols.scala index ea5ef9962722..b19c8cae3105 100644 --- a/compiler/src/dotty/tools/dotc/core/Symbols.scala +++ b/compiler/src/dotty/tools/dotc/core/Symbols.scala @@ -132,16 +132,24 @@ object Symbols { private[core] def defRunId: RunId = if (lastDenot == null) NoRunId else lastDenot.validFor.runId + private inline def associatedFileMatches(inline filter: AbstractFile => Boolean)(using Context): Boolean = + try + val file = associatedFile + file != null && filter(file) + catch case ex: StaleSymbol => + // can happen for constructor proxy companions. Test case is pos-macros/i9484. + false + /** Does this symbol come from a currently compiled source file? */ final def isDefinedInCurrentRun(using Context): Boolean = - span.exists && defRunId == ctx.runId && { - try - val file = associatedFile - file != null && ctx.run.files.contains(file) - catch case ex: StaleSymbol => - // can happen for constructor proxy companions. Test case is pos-macros/i9484. - false - } + span.exists && defRunId == ctx.runId && associatedFileMatches(ctx.run.files.contains) + + /** Is this symbol valid in the current run and has an associated file that is + * not a binary file. e.g. This will return true for + * symbols defined by the user in a prior run of the REPL, that are still valid. + */ + final def isDefinedInSource(using Context): Boolean = + span.exists && isValidInCurrentRun && associatedFileMatches(_.extension != "class") /** Is symbol valid in current run? */ final def isValidInCurrentRun(using Context): Boolean = diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index 891c3076d093..33e476829970 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -111,18 +111,20 @@ object SymUtils: self.isAllOf(Given | Method) && isCodefined(self.info) def useCompanionAsSumMirror(using Context): Boolean = + def companionExtendsSum(using Context): Boolean = + self.linkedClass.isSubClass(defn.Mirror_SumClass) self.linkedClass.exists - && !self.is(Scala2x) - && ( - // If the sum type is compiled from source, and `self` is a "generic sum" - // then its companion object will become a sum mirror in `posttyper`. (This method - // can be called from `typer` when summoning a Mirror.) - // However if `self` is from a prior run then we should check that its companion subclasses `Mirror.Sum`. - // e.g. before Scala 3.1, hierarchical sum types were not considered "generic sums", so their - // companion would not cache the mirror. Companions from TASTy will already be typed as `Mirror.Sum`. - self.isDefinedInCurrentRun - || self.linkedClass.isSubClass(defn.Mirror_SumClass) - ) + && !self.is(Scala2x) + && ( + // If the sum type is compiled from source, and `self` is a "generic sum" + // then its companion object will become a sum mirror in `posttyper`. (This method + // can be called from `typer` when summoning a Mirror.) + // However if `self` is from a binary file, then we should check that its companion + // subclasses `Mirror.Sum`. e.g. before Scala 3.1, hierarchical sum types were not + // considered "generic sums", so their companion would not cache the mirror. + // Companions from TASTy will already be typed as `Mirror.Sum`. + self.isDefinedInSource || companionExtendsSum + ) /** Is this a sealed class or trait for which a sum mirror is generated? * It must satisfy the following conditions: diff --git a/compiler/test-resources/repl/i14540 b/compiler/test-resources/repl/i14540 new file mode 100644 index 000000000000..bf5050ddfdcc --- /dev/null +++ b/compiler/test-resources/repl/i14540 @@ -0,0 +1,7 @@ +scala> enum I14540 { case A } +// defined class I14540 +scala> summon[scala.deriving.Mirror.SumOf[I14540]] eq I14540 +val res0: Boolean = true +scala> enum I14540B { case A }; summon[scala.deriving.Mirror.SumOf[I14540B]] eq I14540B +// defined class I14540B +val res1: Boolean = true diff --git a/sbt-test/scala3-backcompat/hierarchical-mirrors/app/Main.scala b/sbt-test/scala3-backcompat/hierarchical-mirrors/app/Main.scala index 1a6691dfd280..8f8127e76efe 100644 --- a/sbt-test/scala3-backcompat/hierarchical-mirrors/app/Main.scala +++ b/sbt-test/scala3-backcompat/hierarchical-mirrors/app/Main.scala @@ -5,4 +5,5 @@ import scala.deriving.Mirror object Main: def main(args: Array[String]): Unit = val mirrorTop = summon[Mirror.SumOf[lib.Top]] + assert(mirrorTop ne lib.Top) // **NOT** cached in companion - previous run, pre-3.1 tasty dependency assert(mirrorTop.ordinal(lib.Middle()) == 0) diff --git a/tests/run/i14540-priorRun/Lib_1.scala b/tests/run/i14540-priorRun/Lib_1.scala new file mode 100644 index 000000000000..e7e3ac40700b --- /dev/null +++ b/tests/run/i14540-priorRun/Lib_1.scala @@ -0,0 +1,7 @@ +package lib + +sealed trait Top +object Top // companion is necessary + +case class Middle() extends Top with Bottom +sealed trait Bottom extends Top diff --git a/tests/run/i14540-priorRun/Test_2.scala b/tests/run/i14540-priorRun/Test_2.scala new file mode 100644 index 000000000000..81e1c0c32625 --- /dev/null +++ b/tests/run/i14540-priorRun/Test_2.scala @@ -0,0 +1,6 @@ +import scala.deriving.Mirror + +@main def Test = + val mirrorTop = summon[Mirror.SumOf[lib.Top]] + assert(mirrorTop eq lib.Top) // cached in companion - previous run, tasty dependency + assert(mirrorTop.ordinal(lib.Middle()) == 0) diff --git a/tests/run/i14540-sameRun/Lib.scala b/tests/run/i14540-sameRun/Lib.scala new file mode 100644 index 000000000000..e7e3ac40700b --- /dev/null +++ b/tests/run/i14540-sameRun/Lib.scala @@ -0,0 +1,7 @@ +package lib + +sealed trait Top +object Top // companion is necessary + +case class Middle() extends Top with Bottom +sealed trait Bottom extends Top diff --git a/tests/run/i14540-sameRun/Test.scala b/tests/run/i14540-sameRun/Test.scala new file mode 100644 index 000000000000..6aba29134cc5 --- /dev/null +++ b/tests/run/i14540-sameRun/Test.scala @@ -0,0 +1,6 @@ +import scala.deriving.Mirror + +@main def Test = + val mirrorTop = summon[Mirror.SumOf[lib.Top]] + assert(mirrorTop eq lib.Top) // cached in companion - same run, source dependency + assert(mirrorTop.ordinal(lib.Middle()) == 0)