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