Skip to content

Commit 65c1446

Browse files
committed
Change top-level ~ evaluation scheme
* top-level ~ must contain a call to a static method * arguments must be quoted * inline arguments can be passed directly * removed dependency on the original inlined code (removed in PostTyper) * ReifyQuotes is no longer an InfoTransformer * Splicer implements both checking and evaluation of splices in a single abstraction * Fix #4773 * Fix #4735
1 parent 89ab499 commit 65c1446

File tree

29 files changed

+269
-221
lines changed

29 files changed

+269
-221
lines changed

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

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -227,12 +227,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
227227
// be duplicated
228228
// 2. To enable correct pickling (calls can share symbols with the inlined code, which
229229
// would trigger an assertion when pickling).
230-
// In the case of macros we keep the call to be able to reconstruct the parameters that
231-
// are passed to the macro. This same simplification is applied in ReifiedQuotes when the
232-
// macro splices are evaluated.
233-
val callTrace =
234-
if (call.symbol.is(Macro)) call
235-
else Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos)
230+
val callTrace = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos)
236231
cpy.Inlined(tree)(callTrace, transformSub(bindings), transform(expansion))
237232
case tree: Template =>
238233
withNoCheckNews(tree.parents.flatMap(newPart)) {

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

Lines changed: 10 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,10 @@ import Flags._
77
import ast.Trees._
88
import ast.{TreeTypeMap, untpd}
99
import util.Positions._
10-
import StdNames._
1110
import tasty.TreePickler.Hole
12-
import MegaPhase.MiniPhase
1311
import SymUtils._
1412
import NameKinds._
1513
import dotty.tools.dotc.ast.tpd.Tree
16-
import dotty.tools.dotc.core.DenotTransformers.InfoTransformer
1714
import typer.Implicits.SearchFailureType
1815

1916
import scala.collection.mutable
@@ -60,33 +57,9 @@ import dotty.tools.dotc.core.quoted._
6057
*
6158
*
6259
* 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-
* { ... x1$1 .... '{ ... T1$1.unary_~ ... x1$1.toExpr.unary_~ ... y1$1.unary_~ ... } ... }
77-
* }
78-
* ```
79-
* Where `inline` parameters with type Boolean, Byte, Short, Int, Long, Float, Double, Char and String are
80-
* passed as their actual runtime value. See `isStage0Value`. Other `inline` arguments such as functions are handled
81-
* like `y1: Y`.
82-
*
83-
* Note: the parameters of `foo` are kept for simple overloading resolution but they are not used in the body of `foo`.
84-
*
85-
* At inline site we will call reflectively the static method `foo` with dummy parameters, which will return a
86-
* precompiled version of the function that will evaluate the `Expr[Z]` that `foo` produces. The lambda is then called
87-
* at the inline site with the lifted arguments of the inlined call.
60+
* The Splicer is used to check that the RHS will be interpretable (with the `Splicer`) once inlined.
8861
*/
89-
class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer {
62+
class ReifyQuotes extends MacroTransformWithImplicits {
9063
import ast.tpd._
9164

9265
/** Classloader used for loading macros */
@@ -582,16 +555,12 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer {
582555
val last = enteredSyms
583556
stats.foreach(markDef)
584557
mapOverTree(last)
585-
case Inlined(call, bindings, InlineSplice(expansion @ Select(body, name))) =>
586-
assert(call.symbol.is(Macro))
558+
case Inlined(call, bindings, InlineSplice(spliced)) =>
587559
val tree2 =
588560
if (level == 0) {
589-
// Simplification of the call done in PostTyper for non-macros can also be performed now
590-
// see PostTyper `case Inlined(...) =>` for description of the simplification
591-
val call2 = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos)
592-
val spliced = Splicer.splice(body, call, bindings, tree.pos, macroClassLoader).withPos(tree.pos)
561+
val evaluatedSplice = Splicer.splice(spliced, tree.pos, macroClassLoader).withPos(tree.pos)
593562
if (ctx.reporter.hasErrors) EmptyTree
594-
else transform(cpy.Inlined(tree)(call2, bindings, spliced))
563+
else transform(cpy.Inlined(tree)(call, bindings, evaluatedSplice))
595564
}
596565
else super.transform(tree)
597566

@@ -607,22 +576,14 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer {
607576
ctx.error("Inline macro method must be a static method.", tree.pos)
608577
markDef(tree)
609578
val reifier = nested(isQuote = true)
610-
reifier.transform(tree) // Ignore output, we only need the its embedding
611-
assert(reifier.embedded.size == 1)
612-
val lambda = reifier.embedded.head
613-
// replace macro code by lambda used to evaluate the macro expansion
614-
cpy.DefDef(tree)(tpt = TypeTree(macroReturnType), rhs = lambda)
579+
reifier.transform(tree) // Ignore output, only check PCP
580+
cpy.DefDef(tree)(rhs = defaultValue(tree.rhs.tpe))
615581
case _ =>
616582
ctx.error(
617583
"""Malformed inline macro.
618584
|
619585
|Expected the ~ to be at the top of the RHS:
620-
| inline def foo(...): Int = ~impl(...)
621-
|or
622-
| inline def foo(...): Int = ~{
623-
| val x = 1
624-
| impl(... x ...)
625-
| }
586+
| inline def foo(x: X, ..., y: Y): Int = ~impl(x, ... '(y))
626587
""".stripMargin, tree.rhs.pos)
627588
EmptyTree
628589
}
@@ -664,38 +625,16 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer {
664625
* consists of a (possibly multiple & nested) block or a sole expression.
665626
*/
666627
object InlineSplice {
667-
def unapply(tree: Tree)(implicit ctx: Context): Option[Select] = {
628+
def unapply(tree: Tree)(implicit ctx: Context): Option[Tree] = {
668629
tree match {
669-
case expansion: Select if expansion.symbol.isSplice => Some(expansion)
630+
case Select(qual, _) if tree.symbol.isSplice && Splicer.canBeSpliced(qual) => Some(qual)
670631
case Block(List(stat), Literal(Constant(()))) => unapply(stat)
671632
case Block(Nil, expr) => unapply(expr)
672633
case _ => None
673634
}
674635
}
675636
}
676637
}
677-
678-
def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = {
679-
/** Transforms the return type of
680-
* inline def foo(...): X = ~(...)
681-
* to
682-
* inline def foo(...): Seq[Any] => Expr[Any] = (args: Seq[Any]) => ...
683-
*/
684-
def transform(tp: Type): Type = tp match {
685-
case tp: MethodType => MethodType(tp.paramNames, tp.paramInfos, transform(tp.resType))
686-
case tp: PolyType => PolyType(tp.paramNames, tp.paramInfos, transform(tp.resType))
687-
case tp: ExprType => ExprType(transform(tp.resType))
688-
case _ => macroReturnType
689-
}
690-
transform(tp)
691-
}
692-
693-
override protected def mayChange(sym: Symbol)(implicit ctx: Context): Boolean =
694-
ctx.compilationUnit.containsQuotesOrSplices && sym.isTerm && sym.is(Macro)
695-
696-
/** Returns the type of the compiled macro as a lambda: Seq[Any] => Object */
697-
private def macroReturnType(implicit ctx: Context): Type =
698-
defn.FunctionType(1).appliedTo(defn.SeqType.appliedTo(defn.AnyType), defn.ObjectType)
699638
}
700639

701640
object ReifyQuotes {

0 commit comments

Comments
 (0)