Skip to content

Commit 17771f5

Browse files
committed
WIP
1 parent 55d62d6 commit 17771f5

File tree

4 files changed

+219
-118
lines changed

4 files changed

+219
-118
lines changed

compiler/src/dotty/tools/dotc/core/quoted/Quoted.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import dotty.tools.dotc.transform.SymUtils._
1010
object Quoted {
1111

1212
/** Extracts the content of a quoted tree.
13-
* The result can be the contents of a term ot type quote, which
13+
* The result can be the contents of a term or type quote, which
1414
* will return a term or type tree respectively.
1515
*/
1616
def unapply(tree: tpd.Tree)(implicit ctx: Context): Option[tpd.Tree] = tree match {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package dotty.tools.dotc.core.quoted
2+
3+
import dotty.tools.dotc.ast.Trees._
4+
import dotty.tools.dotc.ast.tpd
5+
import dotty.tools.dotc.core.Contexts.Context
6+
import dotty.tools.dotc.core.Types.Type
7+
import dotty.tools.dotc.transform.SymUtils._
8+
9+
/** Extractors for splices */
10+
object Spliced {
11+
12+
/** Extracts the content of a spliced tree.
13+
* The result can be the contents of a term or type splice, which
14+
* will return a term or type tree respectively.
15+
*/
16+
def unapply(tree: tpd.Select)(implicit ctx: Context): Option[tpd.Tree] =
17+
if (tree.symbol.isSplice) Some(tree.qualifier) else None
18+
19+
}
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
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

Comments
 (0)