Skip to content

Commit 4b56143

Browse files
committed
Provide mirror support after inlining
Mirror support runs in PostTyper to add new members to mirrors generated during Typer. But some anonymous mirrors are generated during inlining. We need to add the missing methods for them as well. Fixes #11542 Fixes #11961 Fixes #12052
1 parent 7f25db9 commit 4b56143

File tree

10 files changed

+158
-0
lines changed

10 files changed

+158
-0
lines changed

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

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

46+
var needsMirrorSupport: Boolean = false
47+
4648
/** Will be set to `true` if contains `Quote`.
4749
* The information is used in phase `Staging` in order to avoid traversing trees that need no transformations.
4850
*/

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: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import core._
5+
import Contexts.*
6+
import DenotTransformers.IdentityDenotTransformer
7+
import Decorators.*
8+
import ast.tpd.*
9+
10+
/** A phase that adds mirror support for anonymous mirrors created at inlining. */
11+
class PostInlining extends MacroTransform, IdentityDenotTransformer:
12+
thisPhase =>
13+
14+
override def phaseName: String = PostInlining.name
15+
override def changesMembers = true
16+
17+
override def run(using Context): Unit =
18+
if ctx.compilationUnit.needsMirrorSupport then super.run
19+
20+
lazy val synthMbr: SyntheticMembers = new SyntheticMembers(thisPhase)
21+
22+
def newTransformer(using Context): Transformer = new Transformer:
23+
override def transform(tree: Tree)(using Context): Tree =
24+
super.transform(tree) match
25+
case tree1: Template => synthMbr.addMirrorSupport(tree1)
26+
case tree1 => tree1
27+
28+
object PostInlining:
29+
val name: String = "postInlining"

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)