Skip to content

Commit a8df767

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

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
@@ -975,7 +975,7 @@ object Trees {
975975
/** Tree that replaces a splice in pickled quotes.
976976
* It is only used when picking quotes (Will never be in a TASTy file).
977977
*/
978-
case class Hole[-T >: Untyped](isTermHole: Boolean, idx: Int, args: List[Tree[T]])(implicit @constructorOnly src: SourceFile) extends Tree[T] {
978+
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] {
979979
type ThisTree[-T >: Untyped] <: Hole[T]
980980
override def isTerm: Boolean = isTermHole
981981
override def isType: Boolean = !isTermHole
@@ -1331,6 +1331,10 @@ object Trees {
13311331
case tree: Thicket if (trees eq tree.trees) => tree
13321332
case _ => finalize(tree, untpd.Thicket(trees)(sourceFile(tree)))
13331333
}
1334+
def Hole(tree: Tree)(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree, tpt: Tree)(using Context): Hole = tree match {
1335+
case tree: Hole if isTerm == tree.isTerm && idx == tree.idx && args.eq(tree.args) && content.eq(tree.content) && content.eq(tree.content) => tree
1336+
case _ => finalize(tree, untpd.Hole(isTerm, idx, args, content, tpt)(sourceFile(tree)))
1337+
}
13341338

13351339
// Copier methods with default arguments; these demand that the original tree
13361340
// is of the same class as the copy. We only include trees with more than 2 elements here.
@@ -1352,6 +1356,9 @@ object Trees {
13521356
TypeDef(tree: Tree)(name, rhs)
13531357
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 =
13541358
Template(tree: Tree)(constr, parents, derived, self, body)
1359+
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 =
1360+
Hole(tree: Tree)(isTerm, idx, args, content, tpt)
1361+
13551362
}
13561363

13571364
/** Hook to indicate that a transform of some subtree should be skipped */
@@ -1481,6 +1488,8 @@ object Trees {
14811488
case Thicket(trees) =>
14821489
val trees1 = transform(trees)
14831490
if (trees1 eq trees) tree else Thicket(trees1)
1491+
case Hole(isTerm, idx, args, content, tpt) =>
1492+
cpy.Hole(tree)(isTerm, idx, transform(args), transform(content), transform(tpt))
14841493
case _ =>
14851494
transformMoreCases(tree)
14861495
}
@@ -1618,8 +1627,8 @@ object Trees {
16181627
this(this(x, arg), annot)
16191628
case Thicket(ts) =>
16201629
this(x, ts)
1621-
case Hole(_, _, args) =>
1622-
this(x, args)
1630+
case Hole(_, _, args, content, tpt) =>
1631+
this(this(this(x, args), content), tpt)
16231632
case _ =>
16241633
foldMoreCases(x, tree)
16251634
}

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)