@@ -13,6 +13,7 @@ import MegaPhase.MiniPhase
13
13
import SymUtils ._
14
14
import NameKinds ._
15
15
import dotty .tools .dotc .ast .tpd .Tree
16
+ import dotty .tools .dotc .core .DenotTransformers .InfoTransformer
16
17
import typer .Implicits .SearchFailureType
17
18
18
19
import scala .collection .mutable
@@ -56,8 +57,32 @@ import dotty.tools.dotc.core.quoted._
56
57
* )
57
58
* ```
58
59
* 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.
59
84
*/
60
- class ReifyQuotes extends MacroTransformWithImplicits {
85
+ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer {
61
86
import ast .tpd ._
62
87
63
88
override def phaseName : String = " reifyQuotes"
@@ -408,13 +433,15 @@ class ReifyQuotes extends MacroTransformWithImplicits {
408
433
var i = 0
409
434
transformWithCapturer(tree)(
410
435
(captured : mutable.Map [Symbol , Tree ]) => {
411
- (tree : RefTree ) => {
436
+ (tree : Tree ) => {
412
437
def newCapture = {
438
+ val tpw = tree.tpe.widen
413
439
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)
416
443
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)
418
445
i += 1
419
446
embedded += tree
420
447
captured.put(tree.symbol, capturedArg)
@@ -432,11 +459,12 @@ class ReifyQuotes extends MacroTransformWithImplicits {
432
459
Closure (meth, tss => body(tss.head.head)(ctx.withOwner(meth)).changeOwner(ctx.owner, meth))
433
460
}
434
461
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 = {
437
463
val captured = mutable.LinkedHashMap .empty[Symbol , Tree ]
438
464
val captured2 = capturer(captured)
439
465
outer.enteredSyms.foreach(s => capturers.put(s, captured2))
466
+ if (ctx.owner.owner.is(Macro ))
467
+ outer.enteredSyms.reverse.foreach(s => captured2(ref(s)))
440
468
val tree2 = transform(tree)
441
469
capturers --= outer.enteredSyms
442
470
seq(captured.result().valuesIterator.toList, tree2)
@@ -445,8 +473,9 @@ class ReifyQuotes extends MacroTransformWithImplicits {
445
473
/** Returns true if this tree will be captured by `makeLambda` */
446
474
private def isCaptured (tree : RefTree , level : Int )(implicit ctx : Context ): Boolean = {
447
475
// 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)
450
479
}
451
480
452
481
/** Transform `tree` and return the resulting tree and all `embedded` quotes
@@ -485,7 +514,8 @@ class ReifyQuotes extends MacroTransformWithImplicits {
485
514
splice(tree)
486
515
case tree : RefTree if isCaptured(tree, level) =>
487
516
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_~ ))
489
519
case Block (stats, _) =>
490
520
val last = enteredSyms
491
521
stats.foreach(markDef)
@@ -498,7 +528,7 @@ class ReifyQuotes extends MacroTransformWithImplicits {
498
528
}
499
529
500
530
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))
502
532
else seq(stagedBindings, cpy.Select (expansion)(cpy.Inlined (tree)(call, splicedBindings, body), name))
503
533
val tree2 = transform(tree1)
504
534
@@ -510,10 +540,15 @@ class ReifyQuotes extends MacroTransformWithImplicits {
510
540
case tree : DefDef if tree.symbol.is(Macro ) && level == 0 =>
511
541
tree.rhs match {
512
542
case InlineSplice (_) =>
543
+ if (! tree.symbol.isStatic)
544
+ ctx.error(" Inline macro method must be a static method." , tree.pos)
513
545
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)
517
552
case _ =>
518
553
ctx.error(
519
554
""" Malformed inline macro.
@@ -554,6 +589,27 @@ class ReifyQuotes extends MacroTransformWithImplicits {
554
589
}
555
590
}
556
591
}
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 )
557
613
}
558
614
559
615
object ReifyQuotes {
0 commit comments