Skip to content

Commit 5ee38b9

Browse files
committed
Re-architecture quote pickling
Split cross quote reference handling from pickling Fixes #8100 Fixes #12440 Fixes #13563
1 parent 1ed25ce commit 5ee38b9

23 files changed

+662
-469
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class Compiler {
5454
List(new Inlining) :: // Inline and execute macros
5555
List(new PostInlining) :: // Add mirror support for inlined code
5656
List(new Staging) :: // Check staging levels and heal staged types
57+
List(new Splicing) :: // TODO
5758
List(new PickleQuotes) :: // Turn quoted trees into explicit run-time data structures
5859
Nil
5960

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,8 +133,8 @@ class TreeTypeMap(
133133
val bind1 = tmap.transformSub(bind)
134134
val expr1 = tmap.transform(expr)
135135
cpy.Labeled(labeled)(bind1, expr1)
136-
case Hole(isTermHole, n, args) =>
137-
Hole(isTermHole, n, args.mapConserve(transform)).withSpan(tree.span).withType(mapType(tree.tpe))
136+
case Hole(isTerm, n, args, content, tpt) =>
137+
Hole(isTerm, n, args.mapConserve(transform), transform(content), transform(tpt)).withSpan(tree.span).withType(mapType(tree.tpe))
138138
case lit @ Literal(Constant(tpe: Type)) =>
139139
cpy.Literal(lit)(Constant(mapType(tpe)))
140140
case tree1 =>

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -982,7 +982,7 @@ object Trees {
982982
/** Tree that replaces a splice in pickled quotes.
983983
* It is only used when picking quotes (Will never be in a TASTy file).
984984
*/
985-
case class Hole[-T >: Untyped](isTermHole: Boolean, idx: Int, args: List[Tree[T]])(implicit @constructorOnly src: SourceFile) extends Tree[T] {
985+
case class Hole[-T >: Untyped](isTermHole: Boolean, idx: Int, args: List[Tree[T]], content: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends Tree[T] {
986986
type ThisTree[-T >: Untyped] <: Hole[T]
987987
override def isTerm: Boolean = isTermHole
988988
override def isType: Boolean = !isTermHole
@@ -1338,6 +1338,10 @@ object Trees {
13381338
case tree: Thicket if (trees eq tree.trees) => tree
13391339
case _ => finalize(tree, untpd.Thicket(trees)(sourceFile(tree)))
13401340
}
1341+
def Hole(tree: Tree)(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree, tpt: Tree)(using Context): Hole = tree match {
1342+
case tree: Hole if isTerm == tree.isTerm && idx == tree.idx && args.eq(tree.args) && content.eq(tree.content) && content.eq(tree.content) => tree
1343+
case _ => finalize(tree, untpd.Hole(isTerm, idx, args, content, tpt)(sourceFile(tree)))
1344+
}
13411345

13421346
// Copier methods with default arguments; these demand that the original tree
13431347
// is of the same class as the copy. We only include trees with more than 2 elements here.
@@ -1359,6 +1363,9 @@ object Trees {
13591363
TypeDef(tree: Tree)(name, rhs)
13601364
def Template(tree: Template)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, derived: List[untpd.Tree] = tree.derived, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody)(using Context): Template =
13611365
Template(tree: Tree)(constr, parents, derived, self, body)
1366+
def Hole(tree: Hole)(isTerm: Boolean = tree.isTerm, idx: Int = tree.idx, args: List[Tree] = tree.args, content: Tree = tree.content, tpt: Tree = tree.tpt)(using Context): Hole =
1367+
Hole(tree: Tree)(isTerm, idx, args, content, tpt)
1368+
13621369
}
13631370

13641371
/** Hook to indicate that a transform of some subtree should be skipped */
@@ -1488,6 +1495,8 @@ object Trees {
14881495
case Thicket(trees) =>
14891496
val trees1 = transform(trees)
14901497
if (trees1 eq trees) tree else Thicket(trees1)
1498+
case Hole(isTerm, idx, args, content, tpt) =>
1499+
cpy.Hole(tree)(isTerm, idx, transform(args), transform(content), transform(tpt))
14911500
case _ =>
14921501
transformMoreCases(tree)
14931502
}
@@ -1625,8 +1634,8 @@ object Trees {
16251634
this(this(x, arg), annot)
16261635
case Thicket(ts) =>
16271636
this(x, ts)
1628-
case Hole(_, _, args) =>
1629-
this(x, args)
1637+
case Hole(_, _, args, content, tpt) =>
1638+
this(this(this(x, args), content), tpt)
16301639
case _ =>
16311640
foldMoreCases(x, tree)
16321641
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
373373
def Throw(expr: Tree)(using Context): Tree =
374374
ref(defn.throwMethod).appliedTo(expr)
375375

376+
def Hole(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree, tpt: Tree)(using Context): Hole =
377+
ta.assignType(untpd.Hole(isTerm, idx, args, content, tpt), tpt)
378+
376379
// ------ Making references ------------------------------------------------------
377380

378381
def prefixIsElidable(tp: NamedType)(using Context): Boolean = {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
411411
def Export(expr: Tree, selectors: List[ImportSelector])(implicit src: SourceFile): Export = new Export(expr, selectors)
412412
def PackageDef(pid: RefTree, stats: List[Tree])(implicit src: SourceFile): PackageDef = new PackageDef(pid, stats)
413413
def Annotated(arg: Tree, annot: Tree)(implicit src: SourceFile): Annotated = new Annotated(arg, annot)
414+
def Hole(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree, tpt: Tree)(implicit src: SourceFile): Hole = new Hole(isTerm, idx, args, content, tpt)
414415

415416
// ------ Additional creation methods for untyped only -----------------
416417

compiler/src/dotty/tools/dotc/core/Phases.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ object Phases {
201201
private var mySbtExtractDependenciesPhase: Phase = _
202202
private var myPicklerPhase: Phase = _
203203
private var myInliningPhase: Phase = _
204-
private var myPickleQuotesPhase: Phase = _
204+
private var mySplicingPhase: Phase = _
205205
private var myFirstTransformPhase: Phase = _
206206
private var myCollectNullableFieldsPhase: Phase = _
207207
private var myRefChecksPhase: Phase = _
@@ -223,7 +223,7 @@ object Phases {
223223
final def sbtExtractDependenciesPhase: Phase = mySbtExtractDependenciesPhase
224224
final def picklerPhase: Phase = myPicklerPhase
225225
final def inliningPhase: Phase = myInliningPhase
226-
final def pickleQuotesPhase: Phase = myPickleQuotesPhase
226+
final def splicingPhase: Phase = mySplicingPhase
227227
final def firstTransformPhase: Phase = myFirstTransformPhase
228228
final def collectNullableFieldsPhase: Phase = myCollectNullableFieldsPhase
229229
final def refchecksPhase: Phase = myRefChecksPhase
@@ -248,7 +248,7 @@ object Phases {
248248
mySbtExtractDependenciesPhase = phaseOfClass(classOf[sbt.ExtractDependencies])
249249
myPicklerPhase = phaseOfClass(classOf[Pickler])
250250
myInliningPhase = phaseOfClass(classOf[Inlining])
251-
myPickleQuotesPhase = phaseOfClass(classOf[PickleQuotes])
251+
mySplicingPhase = phaseOfClass(classOf[Splicing])
252252
myFirstTransformPhase = phaseOfClass(classOf[FirstTransform])
253253
myCollectNullableFieldsPhase = phaseOfClass(classOf[CollectNullableFields])
254254
myRefChecksPhase = phaseOfClass(classOf[RefChecks])
@@ -423,7 +423,7 @@ object Phases {
423423
def sbtExtractDependenciesPhase(using Context): Phase = ctx.base.sbtExtractDependenciesPhase
424424
def picklerPhase(using Context): Phase = ctx.base.picklerPhase
425425
def inliningPhase(using Context): Phase = ctx.base.inliningPhase
426-
def pickleQuotesPhase(using Context): Phase = ctx.base.pickleQuotesPhase
426+
def splicingPhase(using Context): Phase = ctx.base.splicingPhase
427427
def firstTransformPhase(using Context): Phase = ctx.base.firstTransformPhase
428428
def refchecksPhase(using Context): Phase = ctx.base.refchecksPhase
429429
def elimRepeatedPhase(using Context): Phase = ctx.base.elimRepeatedPhase

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -644,11 +644,11 @@ class TreePickler(pickler: TastyPickler) {
644644
pickleTree(hi)
645645
pickleTree(alias)
646646
}
647-
case Hole(_, idx, args) =>
647+
case Hole(_, idx, args, _, tpt) =>
648648
writeByte(HOLE)
649649
withLength {
650650
writeNat(idx)
651-
pickleType(tree.tpe, richTypes = true)
651+
pickleType(tpt.tpe, richTypes = true)
652652
args.foreach(pickleTree)
653653
}
654654
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1288,7 +1288,7 @@ class TreeUnpickler(reader: TastyReader,
12881288
val idx = readNat()
12891289
val tpe = readType()
12901290
val args = until(end)(readTerm())
1291-
Hole(true, idx, args).withType(tpe)
1291+
Hole(true, idx, args, EmptyTree, TypeTree(tpe)).withType(tpe)
12921292
case _ =>
12931293
readPathTerm()
12941294
}
@@ -1324,7 +1324,7 @@ class TreeUnpickler(reader: TastyReader,
13241324
val idx = readNat()
13251325
val tpe = readType()
13261326
val args = until(end)(readTerm())
1327-
Hole(false, idx, args).withType(tpe)
1327+
Hole(false, idx, args, EmptyTree, TypeTree(tpe)).withType(tpe)
13281328
case _ =>
13291329
if (isTypeTreeTag(nextByte)) readTerm()
13301330
else {

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -699,10 +699,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
699699
"Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}"
700700
case MacroTree(call) =>
701701
keywordStr("macro ") ~ toTextGlobal(call)
702-
case Hole(isTermHole, idx, args) =>
703-
val (prefix, postfix) = if isTermHole then ("{{{ ", " }}}") else ("[[[ ", " ]]]")
702+
case Hole(isTerm, idx, args, content, tpt) =>
703+
val (prefix, postfix) = if isTerm then ("{{{", "}}}") else ("[[[", "]]]")
704704
val argsText = toTextGlobal(args, ", ")
705-
prefix ~~ idx.toString ~~ "|" ~~ argsText ~~ postfix
705+
val contentText = toTextGlobal(content)
706+
val tptText = toTextGlobal(tpt)
707+
prefix ~~ idx.toString ~~ "|" ~~ tptText ~~ "|" ~~ argsText ~~ "|" ~~ contentText ~~ postfix
706708
case _ =>
707709
tree.fallbackToText(this)
708710
}

compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,9 @@ object PickledQuotes {
5454
/** Unpickle the tree contained in the TastyExpr */
5555
def unpickleTerm(pickled: String | List[String], typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Any], scala.quoted.Quotes) => scala.quoted.Expr[?])(using Context): Tree = {
5656
val unpickled = withMode(Mode.ReadPositions)(unpickle(pickled, isType = false))
57-
val Inlined(call, Nil, expnasion) = unpickled
57+
val Inlined(call, Nil, expansion0) = unpickled
5858
val inlineCtx = inlineContext(call)
59-
val expansion1 = spliceTypes(expnasion, typeHole, termHole)(using inlineCtx)
59+
val expansion1 = spliceTypes(expansion0, typeHole, termHole)(using inlineCtx)
6060
val expansion2 = spliceTerms(expansion1, typeHole, termHole)(using inlineCtx)
6161
cpy.Inlined(unpickled)(call, Nil, expansion2)
6262
}
@@ -71,10 +71,10 @@ object PickledQuotes {
7171
private def spliceTerms(tree: Tree, typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Any], scala.quoted.Quotes) => scala.quoted.Expr[?])(using Context): Tree = {
7272
val evaluateHoles = new TreeMap {
7373
override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match {
74-
case Hole(isTerm, idx, args) =>
74+
case Hole(isTerm, idx, args, _, _) =>
7575
inContext(SpliceScope.contextWithNewSpliceScope(tree.sourcePos)) {
7676
val reifiedArgs = args.map { arg =>
77-
if (arg.isTerm) (q: Quotes) ?=> new ExprImpl(arg, SpliceScope.getCurrent)
77+
if (arg.isTerm) new ExprImpl(arg, SpliceScope.getCurrent)
7878
else new TypeImpl(arg, SpliceScope.getCurrent)
7979
}
8080
if isTerm then
@@ -131,11 +131,14 @@ object PickledQuotes {
131131
case tdef: TypeDef =>
132132
assert(tdef.symbol.hasAnnotation(defn.QuotedRuntime_SplicedTypeAnnot))
133133
val tree = tdef.rhs match
134-
case TypeBoundsTree(_, Hole(_, idx, args), _) =>
134+
case TypeBoundsTree(_, Hole(_, idx, args, _, _), _) => // keep for backwards compat
135135
val quotedType = typeHole(idx, args)
136136
PickledQuotes.quotedTypeToTree(quotedType)
137-
case TypeBoundsTree(_, tpt, _) =>
137+
case TypeBoundsTree(_, tpt, _) => // keep for backwards compat
138138
tpt
139+
case Hole(_, idx, args, _, _) =>
140+
val quotedType = typeHole(idx, args)
141+
PickledQuotes.quotedTypeToTree(quotedType)
139142
(tdef.symbol, tree.tpe)
140143
}.toMap
141144
class ReplaceSplicedTyped extends TypeMap() {

0 commit comments

Comments
 (0)