@@ -2,10 +2,15 @@ package dotty.tools.dotc
22package transform
33
44import core ._
5- import Decorators ._ , Flags ._ , Types ._ , Contexts ._ , Symbols ._ , Constants ._
5+ import Decorators ._
6+ import Flags ._
7+ import Types ._
8+ import Contexts ._
9+ import Symbols ._
10+ import Constants ._
611import Flags ._
712import ast .Trees ._
8- import ast .{TreeTypeMap , untpd }
13+ import ast .{TreeTypeMap , tpd , untpd }
914import util .Positions ._
1015import tasty .TreePickler .Hole
1116import SymUtils ._
@@ -16,6 +21,7 @@ import typer.Implicits.SearchFailureType
1621import scala .collection .mutable
1722import dotty .tools .dotc .core .StdNames ._
1823import dotty .tools .dotc .core .quoted ._
24+ import dotty .tools .dotc .util .SourcePosition
1925
2026
2127/** Translates quoted terms and types to `unpickle` method calls.
@@ -78,7 +84,7 @@ class ReifyQuotes extends MacroTransformWithImplicits {
7884 if (ctx.compilationUnit.containsQuotesOrSplices) super .run
7985
8086 protected def newTransformer (implicit ctx : Context ): Transformer =
81- new Reifier (inQuote = false , null , 0 , new LevelInfo , new mutable.ListBuffer [Tree ])
87+ new Reifier (inQuote = false , null , 0 , new LevelInfo , new mutable.ListBuffer [Tree ], null )
8288
8389 private class LevelInfo {
8490 /** A map from locally defined symbols to the staging levels of their definitions */
@@ -108,16 +114,22 @@ class ReifyQuotes extends MacroTransformWithImplicits {
108114 * and `l == -1` is code inside a top level splice (in an inline method).
109115 * @param levels a stacked map from symbols to the levels in which they were defined
110116 * @param embedded a list of embedded quotes (if `inSplice = true`) or splices (if `inQuote = true`
117+ * @param inlinedAtPos if non null, the reifier is inside an inlined call with position `inlinedAtPos`
111118 */
112119 private class Reifier (inQuote : Boolean , val outer : Reifier , val level : Int , levels : LevelInfo ,
113- val embedded : mutable.ListBuffer [Tree ]) extends ImplicitsTransformer {
120+ val embedded : mutable.ListBuffer [Tree ], inlinedAtPos : SourcePosition ) extends ImplicitsTransformer {
114121 import levels ._
115122 assert(level >= - 1 )
116123
117124 /** A nested reifier for a quote (if `isQuote = true`) or a splice (if not) */
118125 def nested (isQuote : Boolean ): Reifier = {
119126 val nestedEmbedded = if (level > 1 || (level == 1 && isQuote)) embedded else new mutable.ListBuffer [Tree ]
120- new Reifier (isQuote, this , if (isQuote) level + 1 else level - 1 , levels, nestedEmbedded)
127+ new Reifier (isQuote, this , if (isQuote) level + 1 else level - 1 , levels, nestedEmbedded, inlinedAtPos)
128+ }
129+
130+ def inlined (inlinedAt : SourcePosition ): Reifier = {
131+ assert(level == 0 )
132+ new Reifier (true , this , 0 , levels, embedded, inlinedAt)
121133 }
122134
123135 /** We are in a `~(...)` context that is not shadowed by a nested `'(...)` */
@@ -126,6 +138,8 @@ class ReifyQuotes extends MacroTransformWithImplicits {
126138 /** We are not in a `~(...)` or a `'(...)` */
127139 def isRoot : Boolean = outer == null
128140
141+ def isInlined : Boolean = inlinedAtPos != null
142+
129143 /** A map from type ref T to expressions of type `quoted.Type[T]`".
130144 * These will be turned into splices using `addTags` and represent type variables
131145 * that can be possibly healed.
@@ -418,14 +432,34 @@ class ReifyQuotes extends MacroTransformWithImplicits {
418432 val body1 = nested(isQuote = false ).transform(splice.qualifier)
419433 body1.select(splice.name)
420434 }
421- else if (! inQuote && level == 0 && ! ctx.owner.is(Inline )) {
422- spliceOutsideQuotes(splice.pos)
423- splice
424- }
425- else {
435+ else if (level == 1 ) {
426436 val (body1, quotes) = nested(isQuote = false ).split(splice.qualifier)
427437 makeHole(body1, quotes, splice.tpe).withPos(splice.pos)
428438 }
439+ else if (level == - 1 ) {
440+ // TODO add test
441+ ctx.error(" Cannot splice inside a top-level splice" , splice.pos)
442+ splice
443+ }
444+ else if (isInlined) { // level 0 in an inline call
445+ val evaluatedSplice = Splicer .splice(splice.qualifier, inlinedAtPos, macroClassLoader).withPos(splice.pos)
446+ if (ctx.reporter.hasErrors) splice else transform(evaluatedSplice)
447+ } else if (ctx.owner.is(Inline )) { // level 0 in an inline definition
448+ if (! Splicer .canBeSpliced(splice.qualifier))
449+ ctx.error( // TODO adapt error message
450+ """ Malformed inline macro.
451+ |
452+ |Expected the ~ to be at the top of the RHS:
453+ | inline def foo(x: X, ..., y: Y): Int = ~impl(x, ... '(y))
454+ |
455+ |The contents of the splice must call a static method. Arguments must be quoted or inlined.
456+ """ .stripMargin, splice.pos)
457+ splice
458+ }
459+ else { // level 0
460+ spliceOutsideQuotes(splice.pos)
461+ splice
462+ }
429463 }
430464
431465 /** Transforms the contents of a nested splice
@@ -550,37 +584,10 @@ class ReifyQuotes extends MacroTransformWithImplicits {
550584 val last = enteredSyms
551585 stats.foreach(markDef)
552586 mapOverTree(last)
553- case Inlined (call, bindings, InlineSplice (spliced)) =>
554- val tree2 =
555- if (level == 0 ) {
556- val evaluatedSplice = Splicer .splice(spliced, tree.pos, macroClassLoader).withPos(tree.pos)
557- if (ctx.reporter.hasErrors) EmptyTree
558- else transform(cpy.Inlined (tree)(call, bindings, evaluatedSplice))
559- }
560- else super .transform(tree)
561-
562- // due to value-discarding which converts an { e } into { e; () })
563- if (tree.tpe =:= defn.UnitType ) Block (tree2 :: Nil , Literal (Constant (())))
564- else tree2
587+ case tree : Inlined if ! isInlined && level == 0 =>
588+ inlined(tree.pos).transform(tree)
565589 case _ : Import =>
566590 tree
567- case tree : DefDef if tree.symbol.is(Macro ) && level == 0 =>
568- markDef(tree)
569- tree.rhs match {
570- case InlineSplice (_) =>
571- mapOverTree(enteredSyms) // Ignore output, only check PCP
572- cpy.DefDef (tree)(rhs = defaultValue(tree.rhs.tpe))
573- case _ =>
574- ctx.error(
575- """ Malformed inline macro.
576- |
577- |Expected the ~ to be at the top of the RHS:
578- | inline def foo(x: X, ..., y: Y): Int = ~impl(x, ... '(y))
579- |
580- |The contents of the splice must call a static method. Arguments must be quoted or inlined.
581- """ .stripMargin, tree.rhs.pos)
582- EmptyTree
583- }
584591 case _ =>
585592 markDef(tree)
586593 checkLevel(mapOverTree(enteredSyms))
@@ -614,18 +621,6 @@ class ReifyQuotes extends MacroTransformWithImplicits {
614621 acc.select(" ::" .toTermName).appliedToType(tpe).appliedTo(x)
615622 }
616623 }
617-
618- /** InlineSplice is used to detect cases where the expansion
619- * consists of a (possibly multiple & nested) block or a sole expression.
620- */
621- object InlineSplice {
622- def unapply (tree : Tree )(implicit ctx : Context ): Option [Tree ] = tree match {
623- case Select (qual, _) if tree.symbol.isSplice && Splicer .canBeSpliced(qual) => Some (qual)
624- case Block (List (stat), Literal (Constant (()))) => unapply(stat)
625- case Block (Nil , expr) => unapply(expr)
626- case _ => None
627- }
628- }
629624 }
630625}
631626
0 commit comments