Skip to content

Commit deb03a7

Browse files
committed
Add QuotedExpr to encode quotes directly in the AST
1 parent b7ca9b1 commit deb03a7

22 files changed

+115
-78
lines changed

compiler/src/dotty/tools/dotc/CompilationUnit.scala

+4-2
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,13 @@ object CompilationUnit {
154154
var containsCaptureChecking = false
155155
var containsMacroAnnotation = false
156156
def traverse(tree: Tree)(using Context): Unit = {
157-
if (tree.symbol.isQuote)
158-
containsQuote = true
159157
if tree.symbol.is(Flags.Inline) then
160158
containsInline = true
161159
tree match
160+
case tpd.QuotedExpr(_, _) =>
161+
containsQuote = true
162+
case tree: tpd.Apply if tree.symbol == defn.QuotedTypeModule_of =>
163+
containsQuote = true
162164
case Import(qual, selectors) =>
163165
tpd.languageImport(qual) match
164166
case Some(prefix) =>

compiler/src/dotty/tools/dotc/ast/TreeInfo.scala

-13
Original file line numberDiff line numberDiff line change
@@ -1026,19 +1026,6 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] =>
10261026
case t => assert(t.span.exists, i"$t")
10271027
}
10281028

1029-
/** Extractors for quotes */
1030-
object QuotedExpr {
1031-
/** Extracts the content of a quoted tree.
1032-
* The result can be the contents of a term or type quote, which
1033-
* will return a term or type tree respectively.
1034-
*/
1035-
def unapply(tree: tpd.Apply)(using Context): Option[tpd.Tree] =
1036-
if tree.symbol == defn.QuotedRuntime_exprQuote then
1037-
// quoted.runtime.Expr.quote[T](<body>)
1038-
Some(tree.args.head)
1039-
else None
1040-
}
1041-
10421029
object QuotedTypeOf {
10431030
/** Extracts the content of a quoted tree.
10441031
* The result can be the contents of a term or type quote, which

compiler/src/dotty/tools/dotc/ast/Trees.scala

+14
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,11 @@ object Trees {
677677
override def isType = expansion.isType
678678
}
679679

680+
case class QuotedExpr[+T <: Untyped] private[ast] (expr: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile)
681+
extends TermTree[T] {
682+
type ThisTree[+T <: Untyped] = QuotedExpr[T]
683+
}
684+
680685
/** A type tree that represents an existing or inferred type */
681686
case class TypeTree[+T <: Untyped]()(implicit @constructorOnly src: SourceFile)
682687
extends DenotingTree[T] with TypTree[T] {
@@ -1087,6 +1092,7 @@ object Trees {
10871092
type SeqLiteral = Trees.SeqLiteral[T]
10881093
type JavaSeqLiteral = Trees.JavaSeqLiteral[T]
10891094
type Inlined = Trees.Inlined[T]
1095+
type QuotedExpr = Trees.QuotedExpr[T]
10901096
type TypeTree = Trees.TypeTree[T]
10911097
type InferredTypeTree = Trees.InferredTypeTree[T]
10921098
type SingletonTypeTree = Trees.SingletonTypeTree[T]
@@ -1257,6 +1263,10 @@ object Trees {
12571263
case tree: Inlined if (call eq tree.call) && (bindings eq tree.bindings) && (expansion eq tree.expansion) => tree
12581264
case _ => finalize(tree, untpd.Inlined(call, bindings, expansion)(sourceFile(tree)))
12591265
}
1266+
def QuotedExpr(tree: Tree)(expr: Tree, tpt: Tree)(using Context): QuotedExpr = tree match {
1267+
case tree: QuotedExpr if (expr eq tree.expr) && (tpt eq tree.tpt) => tree
1268+
case _ => finalize(tree, untpd.QuotedExpr(expr, tpt)(sourceFile(tree)))
1269+
}
12601270
def SingletonTypeTree(tree: Tree)(ref: Tree)(using Context): SingletonTypeTree = tree match {
12611271
case tree: SingletonTypeTree if (ref eq tree.ref) => tree
12621272
case _ => finalize(tree, untpd.SingletonTypeTree(ref)(sourceFile(tree)))
@@ -1494,6 +1504,8 @@ object Trees {
14941504
case Thicket(trees) =>
14951505
val trees1 = transform(trees)
14961506
if (trees1 eq trees) tree else Thicket(trees1)
1507+
case tree @ QuotedExpr(expr, tpt) =>
1508+
cpy.QuotedExpr(tree)(transform(expr), transform(tpt))
14971509
case tree @ Hole(_, _, args, content, tpt) =>
14981510
cpy.Hole(tree)(args = transform(args), content = transform(content), tpt = transform(tpt))
14991511
case _ =>
@@ -1635,6 +1647,8 @@ object Trees {
16351647
this(this(x, arg), annot)
16361648
case Thicket(ts) =>
16371649
this(x, ts)
1650+
case QuotedExpr(expr, tpt) =>
1651+
this(this(x, expr), tpt)
16381652
case Hole(_, _, args, content, tpt) =>
16391653
this(this(this(x, args), content), tpt)
16401654
case _ =>

compiler/src/dotty/tools/dotc/ast/tpd.scala

+3
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
170170
def Inlined(call: Tree, bindings: List[MemberDef], expansion: Tree)(using Context): Inlined =
171171
ta.assignType(untpd.Inlined(call, bindings, expansion), bindings, expansion)
172172

173+
def QuotedExpr(expr: Tree, tpt: Tree)(using Context): QuotedExpr =
174+
ta.assignType(untpd.QuotedExpr(expr, tpt), tpt)
175+
173176
def TypeTree(tp: Type, inferred: Boolean = false)(using Context): TypeTree =
174177
(if inferred then untpd.InferredTypeTree() else untpd.TypeTree()).withType(tp)
175178

compiler/src/dotty/tools/dotc/ast/untpd.scala

+1
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
401401
def SeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): SeqLiteral = new SeqLiteral(elems, elemtpt)
402402
def JavaSeqLiteral(elems: List[Tree], elemtpt: Tree)(implicit src: SourceFile): JavaSeqLiteral = new JavaSeqLiteral(elems, elemtpt)
403403
def Inlined(call: tpd.Tree, bindings: List[MemberDef], expansion: Tree)(implicit src: SourceFile): Inlined = new Inlined(call, bindings, expansion)
404+
def QuotedExpr(expr: Tree, tpt: Tree)(implicit src: SourceFile): QuotedExpr = new QuotedExpr(expr, tpt)
404405
def TypeTree()(implicit src: SourceFile): TypeTree = new TypeTree()
405406
def InferredTypeTree()(implicit src: SourceFile): TypeTree = new InferredTypeTree()
406407
def SingletonTypeTree(ref: Tree)(implicit src: SourceFile): SingletonTypeTree = new SingletonTypeTree(ref)

compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala

+8
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,14 @@ class TreePickler(pickler: TastyPickler) {
665665
pickleTree(hi)
666666
pickleTree(alias)
667667
}
668+
case QuotedExpr(expr, tpt) =>
669+
pickleTree(
670+
// scala.quoted.runtime.Expr.quoted[<tpt>](<expr>)
671+
ref(defn.QuotedRuntime_exprQuote)
672+
.appliedToTypeTree(tpt)
673+
.appliedTo(expr)
674+
.withSpan(tree.span)
675+
)
668676
case Hole(_, idx, args, _, tpt) =>
669677
writeByte(HOLE)
670678
withLength {

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

+5
Original file line numberDiff line numberDiff line change
@@ -1267,6 +1267,10 @@ class TreeUnpickler(reader: TastyReader,
12671267
res.withAttachment(SuppressedApplyToNone, ())
12681268
else res
12691269

1270+
def quotedExpr(fn: Tree, args: List[Tree]): Tree =
1271+
val TypeApply(_, targs) = fn: @unchecked
1272+
QuotedExpr(args.head, targs.head)
1273+
12701274
def simplifyLub(tree: Tree): Tree =
12711275
tree.overwriteType(tree.tpe.simplified)
12721276
tree
@@ -1283,6 +1287,7 @@ class TreeUnpickler(reader: TastyReader,
12831287
val fn = readTree()
12841288
val args = until(end)(readTree())
12851289
if fn.symbol.isConstructor then constructorApply(fn, args)
1290+
else if fn.symbol == defn.QuotedRuntime_exprQuote then quotedExpr(fn, args)
12861291
else tpd.Apply(fn, args)
12871292
case TYPEAPPLY =>
12881293
tpd.TypeApply(readTree(), until(end)(readTpt()))

compiler/src/dotty/tools/dotc/inlines/Inliner.scala

+7-3
Original file line numberDiff line numberDiff line change
@@ -817,7 +817,7 @@ class Inliner(val call: tpd.Tree)(using Context):
817817
override def typedApply(tree: untpd.Apply, pt: Type)(using Context): Tree =
818818
def cancelQuotes(tree: Tree): Tree =
819819
tree match
820-
case QuotedExpr(SplicedExpr(inner)) => inner
820+
case QuotedExpr(SplicedExpr(inner), _) => inner
821821
case _ => tree
822822
val locked = ctx.typerState.ownedVars
823823
val res = cancelQuotes(constToLiteral(BetaReduce(super.typedApply(tree, pt)))) match {
@@ -835,10 +835,14 @@ class Inliner(val call: tpd.Tree)(using Context):
835835
override def typedTypeApply(tree: untpd.TypeApply, pt: Type)(using Context): Tree =
836836
val locked = ctx.typerState.ownedVars
837837
val tree1 = inlineIfNeeded(constToLiteral(BetaReduce(super.typedTypeApply(tree, pt))), pt, locked)
838-
if tree1.symbol.isQuote then
838+
if tree1.symbol == defn.QuotedTypeModule_of then
839839
ctx.compilationUnit.needsStaging = true
840840
tree1
841841

842+
override def typedQuotedExpr(tree: untpd.QuotedExpr, pt: Type)(using Context): Tree =
843+
ctx.compilationUnit.needsStaging = true
844+
super.typedQuotedExpr(tree, pt)
845+
842846
override def typedMatch(tree: untpd.Match, pt: Type)(using Context): Tree =
843847
val tree1 =
844848
if tree.isInline then
@@ -1067,7 +1071,7 @@ class Inliner(val call: tpd.Tree)(using Context):
10671071
else tree match {
10681072
case tree: RefTree if tree.isTerm && tree.symbol.isDefinedInCurrentRun && !tree.symbol.isLocal =>
10691073
foldOver(tree.symbol :: syms, tree)
1070-
case QuotedExpr(body) =>
1074+
case QuotedExpr(body, _) =>
10711075
level += 1
10721076
try apply(syms, body)
10731077
finally level -= 1

compiler/src/dotty/tools/dotc/inlines/PrepareInlineable.scala

+2-1
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ object PrepareInlineable {
9191
}
9292

9393
private def stagingContext(tree: Tree)(using Context): Context = tree match
94-
case tree: Apply if tree.symbol.isQuote => StagingLevel.quoteContext
94+
case tree: QuotedExpr => StagingLevel.quoteContext
95+
case tree: Apply if tree.symbol eq defn.QuotedTypeModule_of => StagingLevel.quoteContext
9596
case tree: Apply if tree.symbol.isExprSplice => StagingLevel.spliceContext
9697
case _ => ctx
9798
}

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

+3-2
Original file line numberDiff line numberDiff line change
@@ -432,8 +432,6 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
432432
changePrec (GlobalPrec) {
433433
keywordStr("throw ") ~ toText(args.head)
434434
}
435-
else if (!printDebug && fun.hasType && fun.symbol == defn.QuotedRuntime_exprQuote)
436-
keywordStr("'{") ~ toTextGlobal(args, ", ") ~ keywordStr("}")
437435
else if (!printDebug && fun.hasType && fun.symbol.isExprSplice)
438436
keywordStr("${") ~ toTextGlobal(args, ", ") ~ keywordStr("}")
439437
else
@@ -724,6 +722,9 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
724722
"Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}"
725723
case MacroTree(call) =>
726724
keywordStr("macro ") ~ toTextGlobal(call)
725+
case QuotedExpr(expr, tpt) =>
726+
val tptText = (keywordStr("[") ~ toTextGlobal(tpt) ~ keywordStr("]")).provided(printDebug)
727+
keywordStr("'") ~ tptText ~ keywordStr("{") ~ toTextGlobal(expr) ~ keywordStr("}")
727728
case Hole(isTermHole, idx, args, content, tpt) =>
728729
val (prefix, postfix) = if isTermHole then ("{{{", "}}}") else ("[[[", "]]]")
729730
val argsText = toTextGlobal(args, ", ")

compiler/src/dotty/tools/dotc/staging/CrossStageSafety.scala

+7-10
Original file line numberDiff line numberDiff line change
@@ -98,17 +98,14 @@ class CrossStageSafety extends TreeMapWithStages {
9898
}
9999

100100
/** Transform quoted trees while maintaining level correctness */
101-
override protected def transformQuotedExpr(body: Tree, quote: Apply)(using Context): Tree = {
101+
override protected def transformQuotedExpr(body: Tree, quote: QuotedExpr)(using Context): Tree = {
102102
if (ctx.property(InAnnotation).isDefined)
103103
report.error("Cannot have a quote in an annotation", quote.srcPos)
104-
104+
val transformedBody = transformQuoteBody(body, quote.span)
105105
val stripAnnotsDeep: TypeMap = new TypeMap:
106106
def apply(tp: Type): Type = mapOver(tp.stripAnnots)
107-
val transformedBody = transformQuoteBody(body, quote)
108-
// `quoted.runtime.Expr.quote[T](<body>)` --> `quoted.runtime.Expr.quote[T2](<body2>)`
109-
val TypeApply(fun, targs) = quote.fun: @unchecked
110-
val targs2 = targs.map(targ => TypeTree(healType(quote.fun.srcPos)(stripAnnotsDeep(targ.tpe))))
111-
cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, targs2), transformedBody :: Nil)
107+
val tpt1 = TypeTree(healType(quote.tpt.srcPos)(stripAnnotsDeep(quote.tpt.tpe)))
108+
cpy.QuotedExpr(quote)(transformedBody, tpt1)
112109
}
113110

114111
override protected def transformQuotedType(body: Tree, quote: Apply)(using Context): Tree = {
@@ -119,7 +116,7 @@ class CrossStageSafety extends TreeMapWithStages {
119116
// Optimization: `quoted.Type.of[x.Underlying](quotes)` --> `x`
120117
ref(termRef).withSpan(quote.span)
121118
case _ =>
122-
transformQuoteBody(body, quote) match
119+
transformQuoteBody(body, quote.span) match
123120
case DirectTypeOf.Healed(termRef) =>
124121
// Optimization: `quoted.Type.of[@SplicedType type T = x.Underlying; T](quotes)` --> `x`
125122
ref(termRef).withSpan(quote.span)
@@ -130,8 +127,8 @@ class CrossStageSafety extends TreeMapWithStages {
130127
cpy.Apply(quote)(cpy.TypeApply(quote.fun)(fun, transformedBody :: Nil), quotes)
131128
}
132129

133-
private def transformQuoteBody(body: Tree, quote: Apply)(using Context): Tree = {
134-
val taggedTypes = new QuoteTypeTags(quote.span)
130+
private def transformQuoteBody(body: Tree, span: Span)(using Context): Tree = {
131+
val taggedTypes = new QuoteTypeTags(span)
135132
val contextWithQuote =
136133
if level == 0 then contextWithQuoteTypeTags(taggedTypes)(using quoteContext)
137134
else quoteContext

compiler/src/dotty/tools/dotc/staging/TreeMapWithStages.scala

+4-4
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits {
2525
*
2626
* - `quoted.runtime.Expr.quote[T](<body0>)` --> `quoted.runtime.Expr.quote[T](<body>)`
2727
*/
28-
protected def transformQuotedExpr(body: Tree, quote: Apply)(using Context): Tree =
29-
cpy.Apply(quote)(quote.fun, body :: Nil)
28+
protected def transformQuotedExpr(body: Tree, quote: QuotedExpr)(using Context): Tree =
29+
cpy.QuotedExpr(quote)(body, quote.tpt)
3030

3131
/** Transform the quote `quote` which contains the quoted `body`.
3232
*
@@ -62,7 +62,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits {
6262
try transformQuotedType(quotedTree, tree)
6363
finally inQuoteOrSplice = old
6464

65-
case tree @ QuotedExpr(quotedTree) =>
65+
case tree @ QuotedExpr(quotedTree, _) =>
6666
val old = inQuoteOrSplice
6767
inQuoteOrSplice = true
6868
try dropEmptyBlocks(quotedTree) match {
@@ -79,7 +79,7 @@ abstract class TreeMapWithStages extends TreeMapWithImplicits {
7979
val old = inQuoteOrSplice
8080
inQuoteOrSplice = true
8181
try dropEmptyBlocks(splicedTree) match {
82-
case QuotedExpr(t) =>
82+
case QuotedExpr(t, _) =>
8383
// Optimization: `${ 'x }` --> `x`
8484
transform(t)
8585
case _ => transformSplice(splicedTree, tree)

compiler/src/dotty/tools/dotc/transform/Inlining.scala

+6-2
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,9 @@ class Inlining extends MacroTransform {
4646
new TreeTraverser {
4747
def traverse(tree: Tree)(using Context): Unit =
4848
tree match
49-
case _: GenericApply if tree.symbol.isQuote =>
49+
case _: QuotedExpr =>
50+
traverseChildren(tree)(using StagingLevel.quoteContext)
51+
case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of =>
5052
traverseChildren(tree)(using StagingLevel.quoteContext)
5153
case _: GenericApply if tree.symbol.isExprSplice =>
5254
traverseChildren(tree)(using StagingLevel.spliceContext)
@@ -98,7 +100,9 @@ class Inlining extends MacroTransform {
98100
val tree1 = super.transform(tree)
99101
if tree1.tpe.isError then tree1
100102
else Inlines.inlineCall(tree1)
101-
case _: GenericApply if tree.symbol.isQuote =>
103+
case _: QuotedExpr =>
104+
super.transform(tree)(using StagingLevel.quoteContext)
105+
case _: GenericApply if tree.symbol == defn.QuotedTypeModule_of =>
102106
super.transform(tree)(using StagingLevel.quoteContext)
103107
case _: GenericApply if tree.symbol.isExprSplice =>
104108
super.transform(tree)(using StagingLevel.spliceContext)

compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala

+5-4
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,10 @@ class PickleQuotes extends MacroTransform {
8383

8484
override def checkPostCondition(tree: Tree)(using Context): Unit =
8585
tree match
86+
case tree: QuotedExpr =>
87+
assert(Inlines.inInlineMethod)
8688
case tree: RefTree if !Inlines.inInlineMethod =>
87-
assert(!tree.symbol.isQuote)
89+
assert(tree.symbol != defn.QuotedTypeModule_of)
8890
assert(!tree.symbol.isExprSplice)
8991
case _ : TypeDef if !Inlines.inInlineMethod =>
9092
assert(!tree.symbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot),
@@ -97,9 +99,8 @@ class PickleQuotes extends MacroTransform {
9799
protected def newTransformer(using Context): Transformer = new Transformer {
98100
override def transform(tree: tpd.Tree)(using Context): tpd.Tree =
99101
tree match
100-
case Apply(Select(Apply(TypeApply(fn, List(tpt)), List(code)),nme.apply), List(quotes))
101-
if fn.symbol == defn.QuotedRuntime_exprQuote =>
102-
val (contents, codeWithHoles) = makeHoles(code)
102+
case Apply(Select(QuotedExpr(expr, tpt), nme.apply), List(quotes)) =>
103+
val (contents, codeWithHoles) = makeHoles(expr)
103104
val sourceRef = Inlines.inlineCallTrace(ctx.owner, tree.sourcePos)
104105
val codeWithHoles2 = Inlined(sourceRef, Nil, codeWithHoles)
105106
val pickled = PickleQuotes(quotes, codeWithHoles2, contents, tpt.tpe, false)

compiler/src/dotty/tools/dotc/transform/PostTyper.scala

+4-1
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
342342
val patterns1 = transform(patterns)
343343
cpy.UnApply(tree)(transform(fun), transform(implicits), patterns1)
344344
case tree: TypeApply =>
345-
if tree.symbol.isQuote then
345+
if tree.symbol == defn.QuotedTypeModule_of then
346346
ctx.compilationUnit.needsStaging = true
347347
if tree.symbol.is(Inline) then
348348
ctx.compilationUnit.needsInlining = true
@@ -485,6 +485,9 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
485485
)
486486
case Block(_, Closure(_, _, tpt)) if ExpandSAMs.needsWrapperClass(tpt.tpe) =>
487487
superAcc.withInvalidCurrentClass(super.transform(tree))
488+
case QuotedExpr(expr, _) =>
489+
ctx.compilationUnit.needsStaging = true
490+
super.transform(tree)
488491
case tree =>
489492
super.transform(tree)
490493
}

0 commit comments

Comments
 (0)