1
+ package dotty .tools .dotc
2
+ package transform
3
+
4
+ import core ._
5
+ import Decorators ._
6
+ import Flags ._
7
+ import Types ._
8
+ import Contexts ._
9
+ import Symbols ._
10
+ import Constants ._
11
+ import ast .Trees ._
12
+ import ast .{TreeTypeMap , tpd , untpd }
13
+ import util .Spans ._
14
+ import util .SourcePosition
15
+ import tasty .TreePickler .Hole
16
+ import SymUtils ._
17
+ import NameKinds ._
18
+ import typer .Implicits .SearchFailureType
19
+
20
+ import scala .collection .mutable
21
+ import dotty .tools .dotc .core .StdNames ._
22
+ import dotty .tools .dotc .core .quoted ._
23
+ import dotty .tools .dotc .typer .Inliner
24
+ import dotty .tools .dotc .util .SourcePosition
25
+
26
+ /** A Macrotransform that maintains the necessary infrastructore to support
27
+ * contxtual staging levels delimited by quotes and splices.
28
+ */
29
+ abstract class MacroStagingTransform extends MacroTransformWithImplicits {
30
+ import tpd ._
31
+
32
+ /** The main transformer class
33
+ * @param level the current level, where quotes add one and splices subtract one level.
34
+ * The initial level is 0, a level `l` where `l > 0` implies code has been quoted `l` times
35
+ * and `l == -1` is code inside a top level splice (in an inline method).
36
+ * @param levels a stacked map from symbols to the levels in which they were defined
37
+ */
38
+ abstract class StagingTransformer extends ImplicitsTransformer {
39
+
40
+ /** A map from locally defined symbols to the staging levels of their definitions */
41
+ val levelOf = new mutable.HashMap [Symbol , Int ]
42
+
43
+ /** A stack of entered symbols, to be unwound after scope exit */
44
+ var enteredSyms : List [Symbol ] = Nil
45
+
46
+ /** Enter staging level of symbol defined by `tree`, if applicable. */
47
+ def markDef (tree : Tree )(implicit ctx : Context ): Unit = tree match {
48
+ case tree : DefTree =>
49
+ val sym = tree.symbol
50
+ if ((sym.isClass || ! sym.maybeOwner.isType) && ! levelOf.contains(sym)) {
51
+ levelOf(sym) = quotationLevel
52
+ enteredSyms = sym :: enteredSyms
53
+ }
54
+ case _ =>
55
+ }
56
+
57
+ /** If `tree` refers to a locally defined symbol (either directly, or in a pickled type),
58
+ * check that its staging level matches the current level. References to types
59
+ * that are phase-incorrect can still be healed as follows:
60
+ *
61
+ * If `T` is a reference to a type at the wrong level, heal it by setting things up
62
+ * so that we later add a type definition
63
+ *
64
+ * type T' = ~quoted.Type[T]
65
+ *
66
+ * to the quoted text and rename T to T' in it. This is done later in `reify` via
67
+ * `addTags`. `checkLevel` itself only records what needs to be done in the
68
+ * `typeTagOfRef` field of the current `Splice` structure.
69
+ */
70
+ protected def checkLevel (tree : Tree )(implicit ctx : Context ): Tree
71
+
72
+ /** Split `body` into a core and a list of embedded splices.
73
+ * Then if inside a splice, make a hole from these parts.
74
+ * If outside a splice, generate a call tp `scala.quoted.Unpickler.unpickleType` or
75
+ * `scala.quoted.Unpickler.unpickleExpr` that matches `tpe` with
76
+ * core and splices as arguments.
77
+ */
78
+ protected def quotation (body : Tree , quote : Tree )(implicit ctx : Context ): Tree
79
+
80
+ /** If inside a quote, split the body of the splice into a core and a list of embedded quotes
81
+ * and make a hole from these parts. Otherwise issue an error, unless we
82
+ * are in the body of an inline method.
83
+ */
84
+ protected def splice (splice : Select )(implicit ctx : Context ): Tree
85
+
86
+ override def transform (tree : Tree )(implicit ctx : Context ): Tree =
87
+ reporting.trace(i " reify $tree at $quotationLevel" , show = true ) {
88
+ def mapOverTree (lastEntered : List [Symbol ]) =
89
+ try super .transform(tree)
90
+ finally
91
+ while (enteredSyms ne lastEntered) {
92
+ levelOf -= enteredSyms.head
93
+ enteredSyms = enteredSyms.tail
94
+ }
95
+ tree match {
96
+ case Quoted (Spliced (t)) =>
97
+ transform(t) // '(~x) --> x
98
+
99
+ case Quoted (quotedTree) =>
100
+ quotation(quotedTree, tree)
101
+
102
+ case Spliced (Quoted (quotedTree)) =>
103
+ transform(quotedTree) // ~('x) --> x
104
+
105
+ case tree @ Spliced (_) =>
106
+ splice(tree)
107
+
108
+ case tree : RefTree if tree.symbol.is(Inline ) && tree.symbol.is(Param ) => // TODO remove this case?
109
+ tree
110
+
111
+ case Block (stats, _) =>
112
+ val last = enteredSyms
113
+ stats.foreach(markDef)
114
+ mapOverTree(last)
115
+
116
+ case CaseDef (pat, guard, body) =>
117
+ val last = enteredSyms
118
+ // mark all bindings
119
+ new TreeTraverser {
120
+ def traverse (tree : Tree )(implicit ctx : Context ): Unit = {
121
+ markDef(tree)
122
+ traverseChildren(tree)
123
+ }
124
+ }.traverse(pat)
125
+ mapOverTree(last)
126
+
127
+ case _ : Import =>
128
+ tree
129
+
130
+ case _ =>
131
+ markDef(tree)
132
+ checkLevel(mapOverTree(enteredSyms))
133
+ }
134
+ }
135
+
136
+ }
137
+ }
0 commit comments