@@ -5,7 +5,7 @@ import core._
5
5
import Decorators ._ , Flags ._ , Types ._ , Contexts ._ , Symbols ._ , Constants ._
6
6
import Flags ._
7
7
import ast .Trees ._
8
- import ast .{TreeTypeMap , untpd }
8
+ import ast .{TreeTypeMap , tpd , untpd }
9
9
import util .Positions ._
10
10
import tasty .TreePickler .Hole
11
11
import SymUtils ._
@@ -16,6 +16,7 @@ import typer.Implicits.SearchFailureType
16
16
import scala .collection .mutable
17
17
import dotty .tools .dotc .core .StdNames ._
18
18
import dotty .tools .dotc .core .quoted ._
19
+ import dotty .tools .dotc .util .SourcePosition
19
20
20
21
21
22
/** Translates quoted terms and types to `unpickle` method calls.
@@ -78,7 +79,7 @@ class ReifyQuotes extends MacroTransformWithImplicits {
78
79
if (ctx.compilationUnit.containsQuotesOrSplices) super .run
79
80
80
81
protected def newTransformer (implicit ctx : Context ): Transformer =
81
- new Reifier (inQuote = false , null , 0 , new LevelInfo , new mutable.ListBuffer [Tree ])
82
+ new Reifier (inQuote = false , null , 0 , new LevelInfo , new mutable.ListBuffer [Tree ], null )
82
83
83
84
private class LevelInfo {
84
85
/** A map from locally defined symbols to the staging levels of their definitions */
@@ -108,16 +109,22 @@ class ReifyQuotes extends MacroTransformWithImplicits {
108
109
* and `l == -1` is code inside a top level splice (in an transparent method).
109
110
* @param levels a stacked map from symbols to the levels in which they were defined
110
111
* @param embedded a list of embedded quotes (if `inSplice = true`) or splices (if `inQuote = true`
112
+ * @param inlinedAtPos if non null, the reifier is inside an inlined call with position `inlinedAtPos`
111
113
*/
112
114
private class Reifier (inQuote : Boolean , val outer : Reifier , val level : Int , levels : LevelInfo ,
113
- val embedded : mutable.ListBuffer [Tree ]) extends ImplicitsTransformer {
115
+ val embedded : mutable.ListBuffer [Tree ], inlinedAtPos : SourcePosition ) extends ImplicitsTransformer {
114
116
import levels ._
115
117
assert(level >= - 1 )
116
118
117
119
/** A nested reifier for a quote (if `isQuote = true`) or a splice (if not) */
118
120
def nested (isQuote : Boolean ): Reifier = {
119
121
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)
122
+ new Reifier (isQuote, this , if (isQuote) level + 1 else level - 1 , levels, nestedEmbedded, inlinedAtPos)
123
+ }
124
+
125
+ def inlined (inlinedAt : SourcePosition ): Reifier = {
126
+ assert(level == 0 )
127
+ new Reifier (true , this , 0 , levels, embedded, inlinedAt)
121
128
}
122
129
123
130
/** We are in a `~(...)` context that is not shadowed by a nested `'(...)` */
@@ -126,6 +133,8 @@ class ReifyQuotes extends MacroTransformWithImplicits {
126
133
/** We are not in a `~(...)` or a `'(...)` */
127
134
def isRoot : Boolean = outer == null
128
135
136
+ def isInlined : Boolean = inlinedAtPos != null
137
+
129
138
/** A map from type ref T to expressions of type `quoted.Type[T]`".
130
139
* These will be turned into splices using `addTags` and represent type variables
131
140
* that can be possibly healed.
@@ -230,10 +239,6 @@ class ReifyQuotes extends MacroTransformWithImplicits {
230
239
! sym.is(Param ) || levelOK(sym.owner)
231
240
}
232
241
233
- /** Issue a "splice outside quote" error unless we are in the body of a transparent method */
234
- def spliceOutsideQuotes (pos : Position )(implicit ctx : Context ): Unit =
235
- ctx.error(i " splice outside quotes " , pos)
236
-
237
242
/** Try to heal phase-inconsistent reference to type `T` using a local type definition.
238
243
* @return None if successful
239
244
* @return Some(msg) if unsuccessful where `msg` is a potentially empty error message
@@ -292,7 +297,7 @@ class ReifyQuotes extends MacroTransformWithImplicits {
292
297
outer.checkType(pos).foldOver(acc, tp)
293
298
}
294
299
else {
295
- if (tp.isTerm) spliceOutsideQuotes( pos)
300
+ if (tp.isTerm) ctx.error( i " splice outside quotes " , pos)
296
301
tp
297
302
}
298
303
case tp : NamedType =>
@@ -418,14 +423,34 @@ class ReifyQuotes extends MacroTransformWithImplicits {
418
423
val body1 = nested(isQuote = false ).transform(splice.qualifier)
419
424
body1.select(splice.name)
420
425
}
421
- else if (! inQuote && level == 0 && ! ctx.owner.is(Transparent )) {
422
- spliceOutsideQuotes(splice.pos)
423
- splice
424
- }
425
- else {
426
+ else if (level == 1 ) {
426
427
val (body1, quotes) = nested(isQuote = false ).split(splice.qualifier)
427
428
makeHole(body1, quotes, splice.tpe).withPos(splice.pos)
428
429
}
430
+ else if (level == - 1 ) {
431
+ // TODO add test
432
+ ctx.error(" Cannot splice inside a top-level splice" , splice.pos)
433
+ splice
434
+ }
435
+ else if (isInlined) { // level 0 in an inline call
436
+ val evaluatedSplice = Splicer .splice(splice.qualifier, inlinedAtPos, macroClassLoader).withPos(splice.pos)
437
+ if (ctx.reporter.hasErrors) splice else transform(evaluatedSplice)
438
+ } else if (ctx.owner.is(Transparent )) { // level 0 in an inline definition
439
+ if (! Splicer .canBeSpliced(splice.qualifier))
440
+ ctx.error( // TODO adapt error message
441
+ """ Malformed inline macro.
442
+ |
443
+ |Expected the ~ to be at the top of the RHS:
444
+ | inline def foo(x: X, ..., y: Y): Int = ~impl(x, ... '(y))
445
+ |
446
+ |The contents of the splice must call a static method. Arguments must be quoted or inlined.
447
+ """ .stripMargin, splice.pos)
448
+ splice
449
+ }
450
+ else { // level 0
451
+ ctx.error(i " splice outside quotes or inline method " , splice.pos)
452
+ splice
453
+ }
429
454
}
430
455
431
456
/** Transforms the contents of a nested splice
@@ -550,39 +575,12 @@ class ReifyQuotes extends MacroTransformWithImplicits {
550
575
val last = enteredSyms
551
576
stats.foreach(markDef)
552
577
mapOverTree(last)
553
- case Inlined (call, bindings, InlineSplice (spliced)) if ! call.isEmpty =>
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
578
+ case tree : Inlined if ! isInlined && level == 0 =>
579
+ inlined(tree.pos).transform(tree)
565
580
case _ : Import =>
566
581
tree
567
- case tree : DefDef if tree.symbol.is(Macro ) && level == 0 =>
568
- if (enclosingInlineds.nonEmpty)
569
- return EmptyTree // Already checked at definition site and already inlined
570
- markDef(tree)
571
- tree.rhs match {
572
- case InlineSplice (_) =>
573
- mapOverTree(enteredSyms) // Ignore output, only check PCP
574
- cpy.DefDef (tree)(rhs = defaultValue(tree.rhs.tpe))
575
- case _ =>
576
- ctx.error(
577
- """ Malformed transparent macro.
578
- |
579
- |Expected the ~ to be at the top of the RHS:
580
- | transparent def foo(x: X, ..., y: Y): Int = ~impl(x, ... '(y))
581
- |
582
- |The contents of the splice must call a static method. Arguments must be quoted or inlined.
583
- """ .stripMargin, tree.rhs.pos)
584
- EmptyTree
585
- }
582
+ case tree : DefDef if tree.symbol.is(Macro ) && level == 0 && enclosingInlineds.nonEmpty =>
583
+ EmptyTree // Already checked at definition site and already inlined
586
584
case _ =>
587
585
markDef(tree)
588
586
checkLevel(mapOverTree(enteredSyms))
@@ -616,18 +614,6 @@ class ReifyQuotes extends MacroTransformWithImplicits {
616
614
acc.select(" ::" .toTermName).appliedToType(tpe).appliedTo(x)
617
615
}
618
616
}
619
-
620
- /** InlineSplice is used to detect cases where the expansion
621
- * consists of a (possibly multiple & nested) block or a sole expression.
622
- */
623
- object InlineSplice {
624
- def unapply (tree : Tree )(implicit ctx : Context ): Option [Tree ] = tree match {
625
- case Select (qual, _) if tree.symbol.isSplice && Splicer .canBeSpliced(qual) => Some (qual)
626
- case Block (List (stat), Literal (Constant (()))) => unapply(stat)
627
- case Block (Nil , expr) => unapply(expr)
628
- case _ => None
629
- }
630
- }
631
617
}
632
618
}
633
619
0 commit comments