Skip to content

Commit 47f3340

Browse files
committed
Fix #3853: Compile code in the macro definition and remove interpreter
The only interpreted code remaining is a call to a static method containing a lambda which can compute the resulting inlined quoted expression. Arguments to the macro are trivially computed from the inlined call and bindings.
1 parent e4ddb5b commit 47f3340

19 files changed

+343
-316
lines changed

compiler/src/dotty/tools/dotc/interpreter/Interpreter.scala

Lines changed: 0 additions & 222 deletions
This file was deleted.

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,9 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
231231
// be duplicated
232232
// 2. To enable correct pickling (calls can share symbols with the inlined code, which
233233
// would trigger an assertion when pickling).
234-
val callTrace = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos)
234+
val callTrace =
235+
if (call.symbol.is(Macro)) call
236+
else Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos)
235237
cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion))
236238
case tree: Template =>
237239
withNoCheckNews(tree.parents.flatMap(newPart)) {

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

Lines changed: 70 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import MegaPhase.MiniPhase
1313
import SymUtils._
1414
import NameKinds._
1515
import dotty.tools.dotc.ast.tpd.Tree
16+
import dotty.tools.dotc.core.DenotTransformers.InfoTransformer
1617
import typer.Implicits.SearchFailureType
1718

1819
import scala.collection.mutable
@@ -56,8 +57,32 @@ import dotty.tools.dotc.core.quoted._
5657
* )
5758
* ```
5859
* and then performs the same transformation on `'{ ... x1$1.unary_~ ... x2$1.unary_~ ...}`.
60+
*
61+
*
62+
* For inline macro definitions we assume that we have a single ~ directly as the RHS.
63+
* We will transform the definition from
64+
* ```
65+
* inline def foo[T1, ...](inline x1: X, ..., y1: Y, ....): Z = ~{ ... T1 ... x ... '(y) ... }
66+
* ```
67+
* to
68+
* ```
69+
* inline def foo[T1, ...](inline x1: X, ..., y1: Y, ....): Seq[Any] => Object = { (args: Seq[Any]) => {
70+
* val T1$1 = args(0).asInstanceOf[Type[T1]]
71+
* ...
72+
* val x1$1 = args(0).asInstanceOf[X]
73+
* ...
74+
* val y1$1 = args(1).asInstanceOf[Expr[Y]]
75+
* ...
76+
* { ... T1$1.unary_~ ... x ... '(y1$1.unary_~) ... }
77+
* }
78+
* ```
79+
* Note: the parameters of `foo` are kept for simple overloading resolution but they are not used in the body of `foo`.
80+
*
81+
* At inline site we will call reflectively the static method `foo` with dummy parameters, which will return a
82+
* precompiled version of the function that will evaluate the `Expr[Z]` that `foo` produces. The lambda is then called
83+
* at the inline site with the lifted arguments of the inlined call.
5984
*/
60-
class ReifyQuotes extends MacroTransformWithImplicits {
85+
class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer {
6186
import ast.tpd._
6287

6388
override def phaseName: String = "reifyQuotes"
@@ -408,13 +433,15 @@ class ReifyQuotes extends MacroTransformWithImplicits {
408433
var i = 0
409434
transformWithCapturer(tree)(
410435
(captured: mutable.Map[Symbol, Tree]) => {
411-
(tree: RefTree) => {
436+
(tree: Tree) => {
412437
def newCapture = {
438+
val tpw = tree.tpe.widen
413439
val argTpe =
414-
if (tree.isTerm) defn.QuotedExprType.appliedTo(tree.tpe.widen)
415-
else defn.QuotedTypeType.appliedTo(defn.AnyType)
440+
if (tree.isType) defn.QuotedTypeType.appliedTo(tpw)
441+
else if (tree.symbol.is(Inline)) tpw // inlined term
442+
else defn.QuotedExprType.appliedTo(tpw)
416443
val selectArg = arg.select(nme.apply).appliedTo(Literal(Constant(i))).asInstance(argTpe)
417-
val capturedArg = SyntheticValDef(UniqueName.fresh(tree.name.toTermName).toTermName, selectArg)
444+
val capturedArg = SyntheticValDef(UniqueName.fresh(tree.symbol.name.toTermName).toTermName, selectArg)
418445
i += 1
419446
embedded += tree
420447
captured.put(tree.symbol, capturedArg)
@@ -432,11 +459,12 @@ class ReifyQuotes extends MacroTransformWithImplicits {
432459
Closure(meth, tss => body(tss.head.head)(ctx.withOwner(meth)).changeOwner(ctx.owner, meth))
433460
}
434461

435-
private def transformWithCapturer(tree: Tree)(
436-
capturer: mutable.Map[Symbol, Tree] => RefTree => Tree)(implicit ctx: Context): Tree = {
462+
private def transformWithCapturer(tree: Tree)(capturer: mutable.Map[Symbol, Tree] => Tree => Tree)(implicit ctx: Context): Tree = {
437463
val captured = mutable.LinkedHashMap.empty[Symbol, Tree]
438464
val captured2 = capturer(captured)
439465
outer.enteredSyms.foreach(s => capturers.put(s, captured2))
466+
if (ctx.owner.owner.is(Macro))
467+
outer.enteredSyms.reverse.foreach(s => captured2(ref(s)))
440468
val tree2 = transform(tree)
441469
capturers --= outer.enteredSyms
442470
seq(captured.result().valuesIterator.toList, tree2)
@@ -445,8 +473,9 @@ class ReifyQuotes extends MacroTransformWithImplicits {
445473
/** Returns true if this tree will be captured by `makeLambda` */
446474
private def isCaptured(tree: RefTree, level: Int)(implicit ctx: Context): Boolean = {
447475
// Check phase consistency and presence of capturer
448-
level == 1 && !tree.symbol.is(Inline) && levelOf.get(tree.symbol).contains(1) &&
449-
capturers.contains(tree.symbol)
476+
( (level == 1 && levelOf.get(tree.symbol).contains(1)) ||
477+
(level == 0 && tree.symbol.is(Inline))
478+
) && capturers.contains(tree.symbol)
450479
}
451480

452481
/** Transform `tree` and return the resulting tree and all `embedded` quotes
@@ -485,7 +514,8 @@ class ReifyQuotes extends MacroTransformWithImplicits {
485514
splice(tree)
486515
case tree: RefTree if isCaptured(tree, level) =>
487516
val capturer = capturers(tree.symbol)
488-
splice(capturer(tree).select(if (tree.isTerm) nme.UNARY_~ else tpnme.UNARY_~))
517+
if (tree.symbol.is(Inline)) capturer(tree)
518+
else splice(capturer(tree).select(if (tree.isTerm) nme.UNARY_~ else tpnme.UNARY_~))
489519
case Block(stats, _) =>
490520
val last = enteredSyms
491521
stats.foreach(markDef)
@@ -498,7 +528,7 @@ class ReifyQuotes extends MacroTransformWithImplicits {
498528
}
499529

500530
val tree1 =
501-
if (level == 0) cpy.Inlined(tree)(call, stagedBindings, Splicer.splice(seq(splicedBindings, body).withPos(tree.pos)))
531+
if (level == 0) cpy.Inlined(tree)(call, stagedBindings, Splicer.splice(body, call, splicedBindings, tree.pos).withPos(tree.pos))
502532
else seq(stagedBindings, cpy.Select(expansion)(cpy.Inlined(tree)(call, splicedBindings, body), name))
503533
val tree2 = transform(tree1)
504534

@@ -510,10 +540,15 @@ class ReifyQuotes extends MacroTransformWithImplicits {
510540
case tree: DefDef if tree.symbol.is(Macro) && level == 0 =>
511541
tree.rhs match {
512542
case InlineSplice(_) =>
543+
if (!tree.symbol.isStatic)
544+
ctx.error("Inline macro method must be a static method.", tree.pos)
513545
markDef(tree)
514-
nested(isQuote = true).transform(tree)
515-
// check macro code as it if appeared in a quoted context
516-
cpy.DefDef(tree)(rhs = EmptyTree)
546+
val reifier = nested(isQuote = true)
547+
reifier.transform(tree) // Ignore output, we only need the its embedding
548+
assert(reifier.embedded.size == 1)
549+
val lambda = reifier.embedded.head
550+
// replace macro code by lambda used to evaluate the macro expansion
551+
cpy.DefDef(tree)(tpt = TypeTree(macroReturnType), rhs = lambda)
517552
case _ =>
518553
ctx.error(
519554
"""Malformed inline macro.
@@ -554,6 +589,27 @@ class ReifyQuotes extends MacroTransformWithImplicits {
554589
}
555590
}
556591
}
592+
593+
def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = {
594+
/** Transforms the return type of
595+
* inline def foo(...): X = ~(...)
596+
* to
597+
* inline def foo(...): Seq[Any] => Expr[Any] = (args: Seq[Any]) => ...
598+
*/
599+
def transform(tp: Type): Type = tp match {
600+
case tp: MethodType => MethodType(tp.paramNames, tp.paramInfos, transform(tp.resType))
601+
case tp: PolyType => PolyType(tp.paramNames, tp.paramInfos, transform(tp.resType))
602+
case tp: ExprType => ExprType(transform(tp.resType))
603+
case _ => macroReturnType
604+
}
605+
transform(tp)
606+
}
607+
608+
override protected def mayChange(sym: Symbol)(implicit ctx: Context): Boolean = sym.is(Macro)
609+
610+
/** Returns the type of the compiled macro as a lambda: Seq[Any] => Object */
611+
private def macroReturnType(implicit ctx: Context): Type =
612+
defn.FunctionType(1).appliedTo(defn.SeqType.appliedTo(defn.AnyType), defn.ObjectType)
557613
}
558614

559615
object ReifyQuotes {

0 commit comments

Comments
 (0)