diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index acfb4be5c022..93268b71a477 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -112,7 +112,19 @@ object SymUtils: self.isCoDefinedGiven(res.typeSymbol) self.isAllOf(Given | Method) && isCodefined(self.info) - def useCompanionAsMirror(using Context): Boolean = self.linkedClass.exists && !self.is(Scala2x) + def useCompanionAsSumMirror(using Context): Boolean = + 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) + ) /** Is this a sealed class or trait for which a sum mirror is generated? * It must satisfy the following conditions: @@ -129,7 +141,7 @@ object SymUtils: s"it is not an abstract class" else { val children = self.children - val companionMirror = self.useCompanionAsMirror + val companionMirror = self.useCompanionAsSumMirror assert(!(companionMirror && (declScope ne self.linkedClass))) def problem(child: Symbol) = { @@ -144,7 +156,7 @@ object SymUtils: val s = child.whyNotGenericProduct if (s.isEmpty) s else if (child.is(Sealed)) { - val s = child.whyNotGenericSum(if child.useCompanionAsMirror then child.linkedClass else ctx.owner) + val s = child.whyNotGenericSum(if child.useCompanionAsSumMirror then child.linkedClass else ctx.owner) if (s.isEmpty) s else i"its child $child is not a generic sum because $s" } else i"its child $child is not a generic product because $s" diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 75e7c55ada28..2385e50519f2 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -279,7 +279,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): private def sumMirror(mirroredType: Type, formal: Type, span: Span)(using Context): Tree = val cls = mirroredType.classSymbol - val useCompanion = cls.useCompanionAsMirror + val useCompanion = cls.useCompanionAsSumMirror if cls.isGenericSum(if useCompanion then cls.linkedClass else ctx.owner) then val elemLabels = cls.children.map(c => ConstantType(Constant(c.name.toString))) diff --git a/sbt-test/scala3-backcompat/hierarchical-mirrors/app/Main.scala b/sbt-test/scala3-backcompat/hierarchical-mirrors/app/Main.scala new file mode 100644 index 000000000000..1a6691dfd280 --- /dev/null +++ b/sbt-test/scala3-backcompat/hierarchical-mirrors/app/Main.scala @@ -0,0 +1,8 @@ +package app + +import scala.deriving.Mirror + +object Main: + def main(args: Array[String]): Unit = + val mirrorTop = summon[Mirror.SumOf[lib.Top]] + assert(mirrorTop.ordinal(lib.Middle()) == 0) diff --git a/sbt-test/scala3-backcompat/hierarchical-mirrors/build.sbt b/sbt-test/scala3-backcompat/hierarchical-mirrors/build.sbt new file mode 100644 index 000000000000..82f800d59aab --- /dev/null +++ b/sbt-test/scala3-backcompat/hierarchical-mirrors/build.sbt @@ -0,0 +1,7 @@ +lazy val lib = project.in(file("lib")) + .settings( + scalaVersion := "3.0.2" + ) + +lazy val app = project.in(file("app")) + .dependsOn(lib) diff --git a/sbt-test/scala3-backcompat/hierarchical-mirrors/lib/Top.scala b/sbt-test/scala3-backcompat/hierarchical-mirrors/lib/Top.scala new file mode 100644 index 000000000000..e7e3ac40700b --- /dev/null +++ b/sbt-test/scala3-backcompat/hierarchical-mirrors/lib/Top.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/sbt-test/scala3-backcompat/hierarchical-mirrors/project/DottyInjectedPlugin.scala b/sbt-test/scala3-backcompat/hierarchical-mirrors/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..fb946c4b8c61 --- /dev/null +++ b/sbt-test/scala3-backcompat/hierarchical-mirrors/project/DottyInjectedPlugin.scala @@ -0,0 +1,11 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion") + ) +} diff --git a/sbt-test/scala3-backcompat/hierarchical-mirrors/test b/sbt-test/scala3-backcompat/hierarchical-mirrors/test new file mode 100644 index 000000000000..63092ffa4a03 --- /dev/null +++ b/sbt-test/scala3-backcompat/hierarchical-mirrors/test @@ -0,0 +1 @@ +> app/run