Skip to content

Commit 08be314

Browse files
authored
Merge pull request #12062 from dotty-staging/fix-11542
Provide mirror support after inlining
2 parents b0dee7b + cfff8c4 commit 08be314

File tree

11 files changed

+166
-2
lines changed

11 files changed

+166
-2
lines changed

compiler/src/dotty/tools/dotc/CompilationUnit.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ class CompilationUnit protected (val source: SourceFile) {
4343
*/
4444
var needsInlining: Boolean = false
4545

46+
/** Set to `true` if inliner added anonymous mirrors that need to be completed */
47+
var needsMirrorSupport: Boolean = false
48+
4649
/** Will be set to `true` if contains `Quote`.
4750
* The information is used in phase `Staging` in order to avoid traversing trees that need no transformations.
4851
*/

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class Compiler {
5050
protected def picklerPhases: List[List[Phase]] =
5151
List(new Pickler) :: // Generate TASTY info
5252
List(new Inlining) :: // Inline and execute macros
53+
List(new PostInlining) :: // Add mirror support for inlined code
5354
List(new Staging) :: // Check staging levels and heal staged types
5455
List(new PickleQuotes) :: // Turn quoted trees into explicit run-time data structures
5556
Nil
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import core._
5+
import Contexts.*
6+
import DenotTransformers.IdentityDenotTransformer
7+
import Decorators.*
8+
import SyntheticMembers.*
9+
import ast.tpd.*
10+
11+
/** A phase that adds mirror support for anonymous mirrors created at inlining. */
12+
class PostInlining extends MacroTransform, IdentityDenotTransformer:
13+
thisPhase =>
14+
15+
override def phaseName: String = PostInlining.name
16+
override def changesMembers = true
17+
18+
override def run(using Context): Unit =
19+
if ctx.compilationUnit.needsMirrorSupport then super.run
20+
21+
lazy val synthMbr: SyntheticMembers = new SyntheticMembers(thisPhase)
22+
23+
def newTransformer(using Context): Transformer = new Transformer:
24+
override def transform(tree: Tree)(using Context): Tree =
25+
super.transform(tree) match
26+
case tree1: Template
27+
if tree1.hasAttachment(ExtendsSingletonMirror)
28+
|| tree1.hasAttachment(ExtendsProductMirror)
29+
|| tree1.hasAttachment(ExtendsSumMirror) =>
30+
synthMbr.addMirrorSupport(tree1)
31+
case tree1 => tree1
32+
33+
object PostInlining:
34+
val name: String = "postInlining"

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
552552
def addMethod(name: TermName, info: Type, cls: Symbol, body: (Symbol, Tree) => Context ?=> Tree): Unit = {
553553
val meth = newSymbol(clazz, name, Synthetic | Method, info, coord = clazz.coord)
554554
if (!existingDef(meth, clazz).exists) {
555-
meth.entered
555+
meth.enteredAfter(thisPhase)
556556
newBody = newBody :+
557557
synthesizeDef(meth, vrefss => body(cls, vrefss.head.head))
558558
}
@@ -565,7 +565,7 @@ class SyntheticMembers(thisPhase: DenotTransformer) {
565565
val monoType =
566566
newSymbol(clazz, tpnme.MirroredMonoType, Synthetic, TypeAlias(linked.reachableRawTypeRef), coord = clazz.coord)
567567
newBody = newBody :+ TypeDef(monoType).withSpan(ctx.owner.span.focus)
568-
monoType.entered
568+
monoType.enteredAfter(thisPhase)
569569
}
570570
}
571571
def makeSingletonMirror() =

compiler/src/dotty/tools/dotc/typer/Synthesizer.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
180180
* and mark it with given attachment so that it is made into a mirror at PostTyper.
181181
*/
182182
private def anonymousMirror(monoType: Type, attachment: Property.StickyKey[Unit], span: Span)(using Context) =
183+
if ctx.isAfterTyper then ctx.compilationUnit.needsMirrorSupport = true
183184
val monoTypeDef = untpd.TypeDef(tpnme.MirroredMonoType, untpd.TypeTree(monoType))
184185
val newImpl = untpd.Template(
185186
constr = untpd.emptyConstructor,

tests/run/i11542.scala

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
object demo {
2+
3+
trait Reader[A]
4+
5+
given Reader[Int] with {}
6+
7+
inline def summonReader[T <: Tuple]: List[Reader[_]] = inline compiletime.erasedValue[T] match {
8+
case _: EmptyTuple => Nil
9+
case _: (t *: ts) => compiletime.summonInline[Reader[t]] :: summonReader[ts]
10+
}
11+
12+
class CombinedReader[A](
13+
m: deriving.Mirror.ProductOf[A],
14+
childReaders: List[Reader[_]]
15+
) extends Reader[A]
16+
17+
inline given rdr[A <: Tuple](using m: deriving.Mirror.ProductOf[A]): Reader[A] = {
18+
new CombinedReader(m, summonReader[m.MirroredElemTypes])
19+
}
20+
21+
}
22+
23+
@main def Test() = {
24+
// OK
25+
//summon[demo.Reader[(Int, Int, Int)]]
26+
27+
// 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')
28+
// at main$package$.run(main.scala:25)
29+
// at run.main(main.scala:23)
30+
summon[demo.Reader[(Int, (Int, Int))]]
31+
}

tests/run/i11542a.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
type Foo = Tuple2[Int, Int]
2+
// case class Foo(x: Int, y: Int) // works
3+
class Reader(m: deriving.Mirror.ProductOf[Foo])
4+
given reader1(using m: deriving.Mirror.ProductOf[Foo]): Reader = new Reader(m)
5+
inline def summonReader(): Reader = compiletime.summonInline[Reader]
6+
@main def Test() = summonReader()

tests/run/i11961.check

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
STRING
2+
BOOLEAN
3+
STRING
4+
BOOLEAN

tests/run/i11961.scala

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import scala.deriving.*
2+
import scala.compiletime.{erasedValue, summonInline}
3+
4+
case class Simple(a: String, b: Boolean) derives Printable
5+
case class SimpleT(a: (String, Boolean)) derives Printable
6+
7+
@main def Test: Unit = {
8+
9+
summon[Printable[Simple]].print // Prints STRING BOOLEAN as expected
10+
11+
summon[Printable[SimpleT]].print // java.lang.ClassCastException: SimpleT$$anon$1 cannot be cast to scala.deriving.Mirror$Product
12+
13+
}
14+
15+
trait Printable[T]:
16+
def print: Unit
17+
18+
object Printable:
19+
20+
given Printable[String] with
21+
def print: Unit = println("STRING")
22+
23+
given Printable[Boolean] with
24+
def print: Unit = println("BOOLEAN")
25+
26+
def printProduct[T](p: Mirror.ProductOf[T], elems: => List[Printable[_]]): Printable[T] =
27+
new Printable[T]:
28+
def print: Unit =
29+
elems.foreach(_.print)
30+
31+
inline given derived[T](using m: Mirror.Of[T]): Printable[T] =
32+
val elemInstances = summonAllPrintable[m.MirroredElemTypes]
33+
inline m match
34+
case p: Mirror.ProductOf[T] => printProduct(p, elemInstances)
35+
36+
end Printable
37+
38+
inline def summonAllPrintable[T <: Tuple]: List[Printable[_]] =
39+
inline erasedValue[T] match
40+
case _: EmptyTuple => Nil
41+
case _: (t *: ts) => summonInline[Printable[t]] :: summonAllPrintable[ts]

tests/run/i12052/MirrorType.scala

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import scala.quoted._
2+
import scala.deriving._
3+
import scala.compiletime.{erasedValue, constValue, summonFrom, summonInline}
4+
5+
class MyContext {
6+
implicit inline def autoMirrorType[T]: MirrorType[T] = MirrorType.generic
7+
}
8+
9+
trait MirrorType[T] {
10+
def mirrorType: String
11+
}
12+
13+
object MirrorType {
14+
class Container[T]
15+
16+
inline def decode[T]: String =
17+
summonFrom {
18+
case ev: Mirror.ProductOf[T] =>
19+
s"Product-${new Container[ev.MirroredElemLabels]}" // This is the part that splices in the cast
20+
case m: Mirror.SumOf[T] =>
21+
"Sum"
22+
}
23+
24+
inline def generic[T]: MirrorType[T] =
25+
new MirrorType[T] {
26+
def mirrorType: String = decode[T]
27+
}
28+
29+
extension[T](inline value: T)
30+
inline def mirrorType = summonFrom {
31+
case mt: MirrorType[T] => mt.mirrorType
32+
case _ => "mirror not found"
33+
}
34+
}

tests/run/i12052/Test.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import MirrorType._
2+
object Test {
3+
def main(args: Array[String]): Unit = {
4+
val ctx = new MyContext();
5+
import ctx._
6+
val tup = ("foo", 1)
7+
assert(tup.mirrorType.isInstanceOf[String])
8+
}
9+
}

0 commit comments

Comments
 (0)