@@ -8,7 +8,8 @@ import ast.Trees._
8
8
import MegaPhase .MiniPhase
9
9
import scala .collection .mutable
10
10
11
- /** Translates quoted terms and types to reify method calls.
11
+ /** Translates quoted terms and types to `unpickle` method calls.
12
+ * Checks that the phase consistency principle (PCP) holds.
12
13
*/
13
14
class ReifyQuotes extends MacroTransform {
14
15
import ast .tpd ._
@@ -33,39 +34,108 @@ class ReifyQuotes extends MacroTransform {
33
34
34
35
private class Reifier extends Transformer {
35
36
37
+ /** The current staging level */
38
+ private var currentLevel = 0
39
+
40
+ /** The splices encountered so far, indexed by staging level */
41
+ private val splicesAtLevel = mutable.ArrayBuffer (new mutable.ListBuffer [Tree ])
42
+
43
+ // Invariant: -1 <= currentLevel <= splicesAtLevel.length
44
+
45
+ /** A map from locally defined symbol's to the staging levels of their definitions */
46
+ private val levelOf = new mutable.HashMap [Symbol , Int ]
47
+
48
+ /** A stack of entered symbol's, to be unwound after block exit */
49
+ private var enteredSyms : List [Symbol ] = Nil
50
+
51
+ /** Enter staging level of symbol defined by `tree`, if applicable. */
52
+ def markDef (tree : Tree )(implicit ctx : Context ) = tree match {
53
+ case tree : MemberDef if ! levelOf.contains(tree.symbol) =>
54
+ levelOf(tree.symbol) = currentLevel
55
+ enteredSyms = tree.symbol :: enteredSyms
56
+ case _ =>
57
+ }
58
+
59
+ /** If reference is to a locally defined symbol, check that its staging level
60
+ * matches the current level.
61
+ */
62
+ def checkLevel (tree : Tree )(implicit ctx : Context ): Unit = {
63
+
64
+ def check (sym : Symbol , show : Symbol => String ): Unit =
65
+ if (levelOf.getOrElse(sym, currentLevel) != currentLevel)
66
+ ctx.error(em """ access to ${show(sym)} from wrong staging level:
67
+ | - the definition is at level ${levelOf(sym)},
68
+ | - but the access is at level $currentLevel. """ , tree.pos)
69
+
70
+ def showThis (sym : Symbol ) = i " ${sym.name}.this "
71
+
72
+ val sym = tree.symbol
73
+ if (sym.exists)
74
+ if (tree.isInstanceOf [This ]) check(sym, showThis)
75
+ else if (sym.owner.isType) check(sym.owner, showThis)
76
+ else check(sym, _.show)
77
+ }
78
+
36
79
/** Turn `body` of quote into a call of `scala.meta.Unpickler.unpickleType` or
37
80
* `scala.meta.Unpickler.unpickleExpr` depending onwhether `isType` is true or not.
38
81
* The arguments to the method are:
39
82
*
40
83
* - the serialized `body`, as returned from `pickleTree`
41
84
* - all splices found in `body`
42
85
*/
43
- private def reifyCall (body : Tree , isType : Boolean )(implicit ctx : Context ) = {
44
-
45
- object collectSplices extends TreeAccumulator [mutable.ListBuffer [Tree ]] {
46
- override def apply (splices : mutable.ListBuffer [Tree ], tree : Tree )(implicit ctx : Context ) = tree match {
47
- case tree @ Select (qual, _)
48
- if tree.symbol == defn.MetaExpr_~ || tree.symbol == defn.MetaType_~ =>
49
- splices += transform(qual)
50
- case _ =>
51
- foldOver(splices, tree)
52
- }
53
- }
54
- val splices = collectSplices(new mutable.ListBuffer [Tree ], body).toList
55
- val reified = pickleTree(body, isType)
56
-
86
+ private def reifyCall (body : Tree , isType : Boolean )(implicit ctx : Context ) =
57
87
ref(if (isType) defn.Unpickler_unpickleType else defn.Unpickler_unpickleExpr )
58
88
.appliedToType(if (isType) body.tpe else body.tpe.widen)
59
89
.appliedTo(
60
- Literal (Constant (reified)),
61
- SeqLiteral (splices, TypeTree (defn.MetaQuotedType )))
90
+ Literal (Constant (pickleTree(body, isType))),
91
+ SeqLiteral (splicesAtLevel(currentLevel).toList, TypeTree (defn.MetaQuotedType )))
92
+
93
+ /** Perform operation `op` in quoted context */
94
+ private def inQuote (op : => Tree )(implicit ctx : Context ) = {
95
+ currentLevel += 1
96
+ if (currentLevel == splicesAtLevel.length) splicesAtLevel += null
97
+ val savedSplices = splicesAtLevel(currentLevel)
98
+ splicesAtLevel(currentLevel) = new mutable.ListBuffer [Tree ]
99
+ try op
100
+ finally {
101
+ splicesAtLevel(currentLevel) = savedSplices
102
+ currentLevel -= 1
103
+ }
62
104
}
63
105
64
106
override def transform (tree : Tree )(implicit ctx : Context ): Tree = tree match {
65
107
case Apply (fn, arg :: Nil ) if fn.symbol == defn.quoteMethod =>
66
- reifyCall(arg, isType = false )
108
+ inQuote( reifyCall(transform( arg) , isType = false ) )
67
109
case TypeApply (fn, arg :: Nil ) if fn.symbol == defn.typeQuoteMethod =>
68
- reifyCall(arg, isType = true )
110
+ inQuote(reifyCall(transform(arg), isType = true ))
111
+ case Select (body, name)
112
+ if tree.symbol == defn.MetaExpr_~ || tree.symbol == defn.MetaType_~ =>
113
+ currentLevel -= 1
114
+ val body1 = try transform(body) finally currentLevel += 1
115
+ if (currentLevel > 0 ) {
116
+ splicesAtLevel(currentLevel) += body1
117
+ tree
118
+ }
119
+ else {
120
+ if (currentLevel < 0 )
121
+ ctx.error(i " splice ~ not allowed under toplevel splice " , tree.pos)
122
+ cpy.Select (tree)(body1, name)
123
+ }
124
+ case (_ : Ident ) | (_ : This ) =>
125
+ checkLevel(tree)
126
+ super .transform(tree)
127
+ case _ : MemberDef =>
128
+ markDef(tree)
129
+ super .transform(tree)
130
+ case Block (stats, _) =>
131
+ val last = enteredSyms
132
+ stats.foreach(markDef)
133
+ try super .transform(tree)
134
+ finally
135
+ while (enteredSyms ne last) {
136
+ levelOf -= enteredSyms.head
137
+ enteredSyms = enteredSyms.tail
138
+ }
69
139
case _ =>
70
140
super .transform(tree)
71
141
}
0 commit comments