diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index a4f219c88510..e858e01efc48 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -43,6 +43,9 @@ class CompilationUnit protected (val source: SourceFile) { */ var needsInlining: Boolean = false + /** Set to `true` if inliner added anonymous mirrors that need to be completed */ + var needsMirrorSupport: Boolean = false + /** Will be set to `true` if contains `Quote`. * The information is used in phase `Staging` in order to avoid traversing trees that need no transformations. */ diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 4818fca07d0b..ef0acb58492b 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -50,6 +50,7 @@ class Compiler { protected def picklerPhases: List[List[Phase]] = List(new Pickler) :: // Generate TASTY info List(new Inlining) :: // Inline and execute macros + List(new PostInlining) :: // Add mirror support for inlined code List(new Staging) :: // Check staging levels and heal staged types List(new PickleQuotes) :: // Turn quoted trees into explicit run-time data structures Nil diff --git a/compiler/src/dotty/tools/dotc/transform/PostInlining.scala b/compiler/src/dotty/tools/dotc/transform/PostInlining.scala new file mode 100644 index 000000000000..54e654781aed --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/PostInlining.scala @@ -0,0 +1,34 @@ +package dotty.tools.dotc +package transform + +import core._ +import Contexts.* +import DenotTransformers.IdentityDenotTransformer +import Decorators.* +import SyntheticMembers.* +import ast.tpd.* + +/** A phase that adds mirror support for anonymous mirrors created at inlining. */ +class PostInlining extends MacroTransform, IdentityDenotTransformer: + thisPhase => + + override def phaseName: String = PostInlining.name + override def changesMembers = true + + override def run(using Context): Unit = + if ctx.compilationUnit.needsMirrorSupport then super.run + + lazy val synthMbr: SyntheticMembers = new SyntheticMembers(thisPhase) + + def newTransformer(using Context): Transformer = new Transformer: + override def transform(tree: Tree)(using Context): Tree = + super.transform(tree) match + case tree1: Template + if tree1.hasAttachment(ExtendsSingletonMirror) + || tree1.hasAttachment(ExtendsProductMirror) + || tree1.hasAttachment(ExtendsSumMirror) => + synthMbr.addMirrorSupport(tree1) + case tree1 => tree1 + +object PostInlining: + val name: String = "postInlining" \ No newline at end of file diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index d964e5a6c585..817be9cba633 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -552,7 +552,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { def addMethod(name: TermName, info: Type, cls: Symbol, body: (Symbol, Tree) => Context ?=> Tree): Unit = { val meth = newSymbol(clazz, name, Synthetic | Method, info, coord = clazz.coord) if (!existingDef(meth, clazz).exists) { - meth.entered + meth.enteredAfter(thisPhase) newBody = newBody :+ synthesizeDef(meth, vrefss => body(cls, vrefss.head.head)) } @@ -565,7 +565,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) { val monoType = newSymbol(clazz, tpnme.MirroredMonoType, Synthetic, TypeAlias(linked.reachableRawTypeRef), coord = clazz.coord) newBody = newBody :+ TypeDef(monoType).withSpan(ctx.owner.span.focus) - monoType.entered + monoType.enteredAfter(thisPhase) } } def makeSingletonMirror() = diff --git a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala index 76c8523adb14..0406c98d790b 100644 --- a/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Synthesizer.scala @@ -180,6 +180,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context): * and mark it with given attachment so that it is made into a mirror at PostTyper. */ private def anonymousMirror(monoType: Type, attachment: Property.StickyKey[Unit], span: Span)(using Context) = + if ctx.isAfterTyper then ctx.compilationUnit.needsMirrorSupport = true val monoTypeDef = untpd.TypeDef(tpnme.MirroredMonoType, untpd.TypeTree(monoType)) val newImpl = untpd.Template( constr = untpd.emptyConstructor, diff --git a/tests/run/i11542.scala b/tests/run/i11542.scala new file mode 100644 index 000000000000..90c8471bdf03 --- /dev/null +++ b/tests/run/i11542.scala @@ -0,0 +1,31 @@ +object demo { + + trait Reader[A] + + given Reader[Int] with {} + + inline def summonReader[T <: Tuple]: List[Reader[_]] = inline compiletime.erasedValue[T] match { + case _: EmptyTuple => Nil + case _: (t *: ts) => compiletime.summonInline[Reader[t]] :: summonReader[ts] + } + + class CombinedReader[A]( + m: deriving.Mirror.ProductOf[A], + childReaders: List[Reader[_]] + ) extends Reader[A] + + inline given rdr[A <: Tuple](using m: deriving.Mirror.ProductOf[A]): Reader[A] = { + new CombinedReader(m, summonReader[m.MirroredElemTypes]) + } + +} + +@main def Test() = { + // OK + //summon[demo.Reader[(Int, Int, Int)]] + + // Exception in thread "main" java.lang.ClassCastException: class main$package$$anon$2 cannot be cast to class scala.deriving.Mirror$Product (main$package$$anon$2 and scala.deriving.Mirror$Product are in unnamed module of loader 'app') + // at main$package$.run(main.scala:25) + // at run.main(main.scala:23) + summon[demo.Reader[(Int, (Int, Int))]] +} \ No newline at end of file diff --git a/tests/run/i11542a.scala b/tests/run/i11542a.scala new file mode 100644 index 000000000000..db4142fb1a86 --- /dev/null +++ b/tests/run/i11542a.scala @@ -0,0 +1,6 @@ +type Foo = Tuple2[Int, Int] +// case class Foo(x: Int, y: Int) // works +class Reader(m: deriving.Mirror.ProductOf[Foo]) +given reader1(using m: deriving.Mirror.ProductOf[Foo]): Reader = new Reader(m) +inline def summonReader(): Reader = compiletime.summonInline[Reader] +@main def Test() = summonReader() diff --git a/tests/run/i11961.check b/tests/run/i11961.check new file mode 100644 index 000000000000..a9eee48cacc5 --- /dev/null +++ b/tests/run/i11961.check @@ -0,0 +1,4 @@ +STRING +BOOLEAN +STRING +BOOLEAN diff --git a/tests/run/i11961.scala b/tests/run/i11961.scala new file mode 100644 index 000000000000..f289f6b415b6 --- /dev/null +++ b/tests/run/i11961.scala @@ -0,0 +1,41 @@ +import scala.deriving.* +import scala.compiletime.{erasedValue, summonInline} + +case class Simple(a: String, b: Boolean) derives Printable +case class SimpleT(a: (String, Boolean)) derives Printable + +@main def Test: Unit = { + + summon[Printable[Simple]].print // Prints STRING BOOLEAN as expected + + summon[Printable[SimpleT]].print // java.lang.ClassCastException: SimpleT$$anon$1 cannot be cast to scala.deriving.Mirror$Product + +} + +trait Printable[T]: + def print: Unit + +object Printable: + + given Printable[String] with + def print: Unit = println("STRING") + + given Printable[Boolean] with + def print: Unit = println("BOOLEAN") + + def printProduct[T](p: Mirror.ProductOf[T], elems: => List[Printable[_]]): Printable[T] = + new Printable[T]: + def print: Unit = + elems.foreach(_.print) + + inline given derived[T](using m: Mirror.Of[T]): Printable[T] = + val elemInstances = summonAllPrintable[m.MirroredElemTypes] + inline m match + case p: Mirror.ProductOf[T] => printProduct(p, elemInstances) + +end Printable + +inline def summonAllPrintable[T <: Tuple]: List[Printable[_]] = + inline erasedValue[T] match + case _: EmptyTuple => Nil + case _: (t *: ts) => summonInline[Printable[t]] :: summonAllPrintable[ts] diff --git a/tests/run/i12052/MirrorType.scala b/tests/run/i12052/MirrorType.scala new file mode 100644 index 000000000000..b8b0b442690e --- /dev/null +++ b/tests/run/i12052/MirrorType.scala @@ -0,0 +1,34 @@ +import scala.quoted._ +import scala.deriving._ +import scala.compiletime.{erasedValue, constValue, summonFrom, summonInline} + +class MyContext { + implicit inline def autoMirrorType[T]: MirrorType[T] = MirrorType.generic +} + +trait MirrorType[T] { + def mirrorType: String +} + +object MirrorType { + class Container[T] + + inline def decode[T]: String = + summonFrom { + case ev: Mirror.ProductOf[T] => + s"Product-${new Container[ev.MirroredElemLabels]}" // This is the part that splices in the cast + case m: Mirror.SumOf[T] => + "Sum" + } + + inline def generic[T]: MirrorType[T] = + new MirrorType[T] { + def mirrorType: String = decode[T] + } + + extension[T](inline value: T) + inline def mirrorType = summonFrom { + case mt: MirrorType[T] => mt.mirrorType + case _ => "mirror not found" + } +} \ No newline at end of file diff --git a/tests/run/i12052/Test.scala b/tests/run/i12052/Test.scala new file mode 100644 index 000000000000..07ffb2a98f3e --- /dev/null +++ b/tests/run/i12052/Test.scala @@ -0,0 +1,9 @@ +import MirrorType._ +object Test { + def main(args: Array[String]): Unit = { + val ctx = new MyContext(); + import ctx._ + val tup = ("foo", 1) + assert(tup.mirrorType.isInstanceOf[String]) + } +} \ No newline at end of file