Skip to content

Commit

Permalink
Provide mirror support after inlining
Browse files Browse the repository at this point in the history
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 scala#11542
Fixes scala#11961
Fixes scala#12052
  • Loading branch information
odersky authored and michelou committed Apr 14, 2021
1 parent 56ffbda commit d002a5d
Show file tree
Hide file tree
Showing 10 changed files with 158 additions and 0 deletions.
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/CompilationUnit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class CompilationUnit protected (val source: SourceFile) {
*/
var needsInlining: Boolean = false

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.
*/
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 29 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/PostInlining.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package dotty.tools.dotc
package transform

import core._
import Contexts.*
import DenotTransformers.IdentityDenotTransformer
import Decorators.*
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 => synthMbr.addMirrorSupport(tree1)
case tree1 => tree1

object PostInlining:
val name: String = "postInlining"
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/typer/Synthesizer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
31 changes: 31 additions & 0 deletions tests/run/i11542.scala
Original file line number Diff line number Diff line change
@@ -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))]]
}
6 changes: 6 additions & 0 deletions tests/run/i11542a.scala
Original file line number Diff line number Diff line change
@@ -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()
4 changes: 4 additions & 0 deletions tests/run/i11961.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
STRING
BOOLEAN
STRING
BOOLEAN
41 changes: 41 additions & 0 deletions tests/run/i11961.scala
Original file line number Diff line number Diff line change
@@ -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]
34 changes: 34 additions & 0 deletions tests/run/i12052/MirrorType.scala
Original file line number Diff line number Diff line change
@@ -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"
}
}
9 changes: 9 additions & 0 deletions tests/run/i12052/Test.scala
Original file line number Diff line number Diff line change
@@ -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])
}
}

0 comments on commit d002a5d

Please sign in to comment.