Skip to content

Commit d5ae77e

Browse files
authored
Merge pull request #12540 from dotty-staging/rearchitecture-quote-splitting
Re-architecture quote pickling
2 parents 9ed0762 + ca01392 commit d5ae77e

File tree

76 files changed

+1285
-562
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+1285
-562
lines changed

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

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,10 @@ class CompilationUnit protected (val source: SourceFile) {
4747
var needsMirrorSupport: Boolean = false
4848

4949
/** Will be set to `true` if contains `Quote`.
50-
* The information is used in phase `Staging` in order to avoid traversing trees that need no transformations.
50+
* The information is used in phase `Staging`/`Splicing`/`PickleQuotes` in order to avoid traversing trees that need no transformations.
5151
*/
5252
var needsStaging: Boolean = false
5353

54-
/** Will be set to `true` if contains `Quote` that needs to be pickled
55-
* The information is used in phase `PickleQuotes` in order to avoid traversing trees that need no transformations.
56-
*/
57-
var needsQuotePickling: Boolean = false
58-
5954
var suspended: Boolean = false
6055
var suspendedAtInliningPhase: Boolean = false
6156

@@ -115,7 +110,6 @@ object CompilationUnit {
115110
val force = new Force
116111
force.traverse(unit1.tpdTree)
117112
unit1.needsStaging = force.containsQuote
118-
unit1.needsQuotePickling = force.containsQuote
119113
unit1.needsInlining = force.containsInline
120114
}
121115
unit1

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) :: // Replace level 1 splices with holes
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: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,11 @@ class TreeTypeMap(
134134
val bind1 = tmap.transformSub(bind)
135135
val expr1 = tmap.transform(expr)
136136
cpy.Labeled(labeled)(bind1, expr1)
137-
case Hole(isTermHole, n, args) =>
138-
Hole(isTermHole, n, args.mapConserve(transform)).withSpan(tree.span).withType(mapType(tree.tpe))
137+
case tree @ Hole(_, _, args, content, tpt) =>
138+
val args1 = args.mapConserve(transform)
139+
val content1 = transform(content)
140+
val tpt1 = transform(tpt)
141+
cpy.Hole(tree)(args = args1, content = content1, tpt = tpt1)
139142
case lit @ Literal(Constant(tpe: Type)) =>
140143
cpy.Literal(lit)(Constant(mapType(tpe)))
141144
case tree1 =>

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

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,11 @@ object Trees {
503503
def forwardTo: Tree[T] = fun
504504
}
505505

506+
object GenericApply:
507+
def unapply[T >: Untyped](tree: Tree[T]): Option[(Tree[T], List[Tree[T]])] = tree match
508+
case tree: GenericApply[T] => Some((tree.fun, tree.args))
509+
case _ => None
510+
506511
/** The kind of application */
507512
enum ApplyKind:
508513
case Regular // r.f(x)
@@ -525,8 +530,6 @@ object Trees {
525530
attachmentOrElse(untpd.KindOfApply, ApplyKind.Regular)
526531
}
527532

528-
529-
530533
/** fun[args] */
531534
case class TypeApply[-T >: Untyped] private[ast] (fun: Tree[T], args: List[Tree[T]])(implicit @constructorOnly src: SourceFile)
532535
extends GenericApply[T] {
@@ -972,10 +975,16 @@ object Trees {
972975
def genericEmptyValDef[T >: Untyped]: ValDef[T] = theEmptyValDef.asInstanceOf[ValDef[T]]
973976
def genericEmptyTree[T >: Untyped]: Thicket[T] = theEmptyTree.asInstanceOf[Thicket[T]]
974977

975-
/** Tree that replaces a splice in pickled quotes.
976-
* It is only used when picking quotes (Will never be in a TASTy file).
978+
/** Tree that replaces a level 1 splices in pickled (level 0) quotes.
979+
* It is only used when picking quotes (will never be in a TASTy file).
980+
*
981+
* @param isTermHole If this hole is a term, otherwise it is a type hole.
982+
* @param idx The index of the hole in it's enclosing level 0 quote.
983+
* @param args The arguments of the splice to compute its content
984+
* @param content Lambda that computes the content of the hole. This tree is empty when in a quote pickle.
985+
* @param tpt Type of the hole
977986
*/
978-
case class Hole[-T >: Untyped](isTermHole: Boolean, idx: Int, args: List[Tree[T]])(implicit @constructorOnly src: SourceFile) extends Tree[T] {
987+
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] {
979988
type ThisTree[-T >: Untyped] <: Hole[T]
980989
override def isTerm: Boolean = isTermHole
981990
override def isType: Boolean = !isTermHole
@@ -1331,6 +1340,10 @@ object Trees {
13311340
case tree: Thicket if (trees eq tree.trees) => tree
13321341
case _ => finalize(tree, untpd.Thicket(trees)(sourceFile(tree)))
13331342
}
1343+
def Hole(tree: Tree)(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree, tpt: Tree)(using Context): Hole = tree match {
1344+
case tree: Hole if isTerm == tree.isTerm && idx == tree.idx && args.eq(tree.args) && content.eq(tree.content) && content.eq(tree.content) => tree
1345+
case _ => finalize(tree, untpd.Hole(isTerm, idx, args, content, tpt)(sourceFile(tree)))
1346+
}
13341347

13351348
// Copier methods with default arguments; these demand that the original tree
13361349
// is of the same class as the copy. We only include trees with more than 2 elements here.
@@ -1352,6 +1365,9 @@ object Trees {
13521365
TypeDef(tree: Tree)(name, rhs)
13531366
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 =
13541367
Template(tree: Tree)(constr, parents, derived, self, body)
1368+
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 =
1369+
Hole(tree: Tree)(isTerm, idx, args, content, tpt)
1370+
13551371
}
13561372

13571373
/** Hook to indicate that a transform of some subtree should be skipped */
@@ -1481,6 +1497,8 @@ object Trees {
14811497
case Thicket(trees) =>
14821498
val trees1 = transform(trees)
14831499
if (trees1 eq trees) tree else Thicket(trees1)
1500+
case tree @ Hole(_, _, args, content, tpt) =>
1501+
cpy.Hole(tree)(args = transform(args), content = transform(content), tpt = transform(tpt))
14841502
case _ =>
14851503
transformMoreCases(tree)
14861504
}
@@ -1620,8 +1638,8 @@ object Trees {
16201638
this(this(x, arg), annot)
16211639
case Thicket(ts) =>
16221640
this(x, ts)
1623-
case Hole(_, _, args) =>
1624-
this(x, args)
1641+
case Hole(_, _, args, content, tpt) =>
1642+
this(this(this(x, args), content), tpt)
16251643
case _ =>
16261644
foldMoreCases(x, tree)
16271645
}

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

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

380+
def Hole(isTermHole: Boolean, idx: Int, args: List[Tree], content: Tree, tpt: Tree)(using Context): Hole =
381+
ta.assignType(untpd.Hole(isTermHole, idx, args, content, tpt), tpt)
382+
380383
// ------ Making references ------------------------------------------------------
381384

382385
def prefixIsElidable(tp: NamedType)(using Context): Boolean = {
@@ -1518,10 +1521,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
15181521
* @param tpe the type of the elements of the resulting list.
15191522
*
15201523
*/
1521-
def mkList(trees: List[Tree], tpe: Tree)(using Context): Tree =
1524+
def mkList(trees: List[Tree], tpt: Tree)(using Context): Tree =
15221525
ref(defn.ListModule).select(nme.apply)
1523-
.appliedToTypeTree(tpe)
1524-
.appliedToVarargs(trees, tpe)
1526+
.appliedToTypeTree(tpt)
1527+
.appliedToVarargs(trees, tpt)
15251528

15261529

15271530
protected def FunProto(args: List[Tree], resType: Type)(using Context) =

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(isTermHole: Boolean, idx: Int, args: List[Tree], content: Tree, tpt: Tree)(implicit src: SourceFile): Hole = new Hole(isTermHole, idx, args, content, tpt)
414415

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

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

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -791,10 +791,44 @@ class Definitions {
791791
@tu lazy val QuotedExprClass: ClassSymbol = requiredClass("scala.quoted.Expr")
792792

793793
@tu lazy val QuotesClass: ClassSymbol = requiredClass("scala.quoted.Quotes")
794+
@tu lazy val Quotes_reflect: Symbol = QuotesClass.requiredValue("reflect")
795+
@tu lazy val Quotes_reflect_asTerm: Symbol = Quotes_reflect.requiredMethod("asTerm")
796+
@tu lazy val Quotes_reflect_Apply: Symbol = Quotes_reflect.requiredValue("Apply")
797+
@tu lazy val Quotes_reflect_Apply_apply: Symbol = Quotes_reflect_Apply.requiredMethod(nme.apply)
798+
@tu lazy val Quotes_reflect_TypeApply: Symbol = Quotes_reflect.requiredValue("TypeApply")
799+
@tu lazy val Quotes_reflect_TypeApply_apply: Symbol = Quotes_reflect_TypeApply.requiredMethod(nme.apply)
800+
@tu lazy val Quotes_reflect_Assign: Symbol = Quotes_reflect.requiredValue("Assign")
801+
@tu lazy val Quotes_reflect_Assign_apply: Symbol = Quotes_reflect_Assign.requiredMethod(nme.apply)
802+
@tu lazy val Quotes_reflect_Inferred: Symbol = Quotes_reflect.requiredValue("Inferred")
803+
@tu lazy val Quotes_reflect_Inferred_apply: Symbol = Quotes_reflect_Inferred.requiredMethod(nme.apply)
804+
@tu lazy val Quotes_reflect_Literal: Symbol = Quotes_reflect.requiredValue("Literal")
805+
@tu lazy val Quotes_reflect_Literal_apply: Symbol = Quotes_reflect_Literal.requiredMethod(nme.apply)
806+
@tu lazy val Quotes_reflect_TreeMethods: Symbol = Quotes_reflect.requiredMethod("TreeMethods")
807+
@tu lazy val Quotes_reflect_TreeMethods_asExpr: Symbol = Quotes_reflect_TreeMethods.requiredMethod("asExpr")
808+
@tu lazy val Quotes_reflect_TypeRepr: Symbol = Quotes_reflect.requiredValue("TypeRepr")
809+
@tu lazy val Quotes_reflect_TypeRepr_of: Symbol = Quotes_reflect_TypeRepr.requiredMethod("of")
810+
@tu lazy val Quotes_reflect_TypeRepr_typeConstructorOf: Symbol = Quotes_reflect_TypeRepr.requiredMethod("typeConstructorOf")
811+
@tu lazy val Quotes_reflect_TypeReprMethods: Symbol = Quotes_reflect.requiredValue("TypeReprMethods")
812+
@tu lazy val Quotes_reflect_TypeReprMethods_asType: Symbol = Quotes_reflect_TypeReprMethods.requiredMethod("asType")
813+
@tu lazy val Quotes_reflect_TypeTreeType: Symbol = Quotes_reflect.requiredType("TypeTree")
814+
@tu lazy val Quotes_reflect_TermType: Symbol = Quotes_reflect.requiredType("Term")
815+
@tu lazy val Quotes_reflect_BooleanConstant: Symbol = Quotes_reflect.requiredValue("BooleanConstant")
816+
@tu lazy val Quotes_reflect_ByteConstant: Symbol = Quotes_reflect.requiredValue("ByteConstant")
817+
@tu lazy val Quotes_reflect_ShortConstant: Symbol = Quotes_reflect.requiredValue("ShortConstant")
818+
@tu lazy val Quotes_reflect_IntConstant: Symbol = Quotes_reflect.requiredValue("IntConstant")
819+
@tu lazy val Quotes_reflect_LongConstant: Symbol = Quotes_reflect.requiredValue("LongConstant")
820+
@tu lazy val Quotes_reflect_FloatConstant: Symbol = Quotes_reflect.requiredValue("FloatConstant")
821+
@tu lazy val Quotes_reflect_DoubleConstant: Symbol = Quotes_reflect.requiredValue("DoubleConstant")
822+
@tu lazy val Quotes_reflect_CharConstant: Symbol = Quotes_reflect.requiredValue("CharConstant")
823+
@tu lazy val Quotes_reflect_StringConstant: Symbol = Quotes_reflect.requiredValue("StringConstant")
824+
@tu lazy val Quotes_reflect_UnitConstant: Symbol = Quotes_reflect.requiredValue("UnitConstant")
825+
@tu lazy val Quotes_reflect_NullConstant: Symbol = Quotes_reflect.requiredValue("NullConstant")
826+
@tu lazy val Quotes_reflect_ClassOfConstant: Symbol = Quotes_reflect.requiredValue("ClassOfConstant")
827+
794828

795829
@tu lazy val QuoteUnpicklerClass: ClassSymbol = requiredClass("scala.quoted.runtime.QuoteUnpickler")
796-
@tu lazy val QuoteUnpickler_unpickleExpr: Symbol = QuoteUnpicklerClass.requiredMethod("unpickleExpr")
797-
@tu lazy val QuoteUnpickler_unpickleType: Symbol = QuoteUnpicklerClass.requiredMethod("unpickleType")
830+
@tu lazy val QuoteUnpickler_unpickleExprV2: Symbol = QuoteUnpicklerClass.requiredMethod("unpickleExprV2")
831+
@tu lazy val QuoteUnpickler_unpickleTypeV2: Symbol = QuoteUnpicklerClass.requiredMethod("unpickleTypeV2")
798832

799833
@tu lazy val QuoteMatchingClass: ClassSymbol = requiredClass("scala.quoted.runtime.QuoteMatching")
800834
@tu lazy val QuoteMatching_ExprMatch: Symbol = QuoteMatchingClass.requiredMethod("ExprMatch")

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 = _
@@ -224,7 +224,7 @@ object Phases {
224224
final def sbtExtractDependenciesPhase: Phase = mySbtExtractDependenciesPhase
225225
final def picklerPhase: Phase = myPicklerPhase
226226
final def inliningPhase: Phase = myInliningPhase
227-
final def pickleQuotesPhase: Phase = myPickleQuotesPhase
227+
final def splicingPhase: Phase = mySplicingPhase
228228
final def firstTransformPhase: Phase = myFirstTransformPhase
229229
final def collectNullableFieldsPhase: Phase = myCollectNullableFieldsPhase
230230
final def refchecksPhase: Phase = myRefChecksPhase
@@ -250,7 +250,7 @@ object Phases {
250250
mySbtExtractDependenciesPhase = phaseOfClass(classOf[sbt.ExtractDependencies])
251251
myPicklerPhase = phaseOfClass(classOf[Pickler])
252252
myInliningPhase = phaseOfClass(classOf[Inlining])
253-
myPickleQuotesPhase = phaseOfClass(classOf[PickleQuotes])
253+
mySplicingPhase = phaseOfClass(classOf[Splicing])
254254
myFirstTransformPhase = phaseOfClass(classOf[FirstTransform])
255255
myCollectNullableFieldsPhase = phaseOfClass(classOf[CollectNullableFields])
256256
myRefChecksPhase = phaseOfClass(classOf[RefChecks])
@@ -426,7 +426,7 @@ object Phases {
426426
def sbtExtractDependenciesPhase(using Context): Phase = ctx.base.sbtExtractDependenciesPhase
427427
def picklerPhase(using Context): Phase = ctx.base.picklerPhase
428428
def inliningPhase(using Context): Phase = ctx.base.inliningPhase
429-
def pickleQuotesPhase(using Context): Phase = ctx.base.pickleQuotesPhase
429+
def splicingPhase(using Context): Phase = ctx.base.splicingPhase
430430
def firstTransformPhase(using Context): Phase = ctx.base.firstTransformPhase
431431
def refchecksPhase(using Context): Phase = ctx.base.refchecksPhase
432432
def elimRepeatedPhase(using Context): Phase = ctx.base.elimRepeatedPhase

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ import dotty.tools.dotc.transform.PCPCheckAndHeal
88

99
object StagingContext {
1010

11-
/** A key to be used in a context property that tracks the quoteation level */
11+
/** A key to be used in a context property that tracks the quotation level */
1212
private val QuotationLevel = new Property.Key[Int]
1313

14-
/** A key to be used in a context property that tracks the quoteation stack.
15-
* Stack containing the Quotes references recieved by the surrounding quotes.
14+
/** A key to be used in a context property that tracks the quotation stack.
15+
* Stack containing the Quotes references received by the surrounding quotes.
1616
*/
1717
private val QuotesStack = new Property.Key[List[tpd.Tree]]
1818

@@ -26,7 +26,7 @@ object StagingContext {
2626
def quoteContext(using Context): Context =
2727
ctx.fresh.setProperty(QuotationLevel, level + 1)
2828

29-
/** Context with an incremented quotation level and pushes a refecence to a Quotes on the quote context stack */
29+
/** Context with an incremented quotation level and pushes a reference to a Quotes on the quote context stack */
3030
def pushQuotes(qctxRef: tpd.Tree)(using Context): Context =
3131
val old = ctx.property(QuotesStack).getOrElse(List.empty)
3232
ctx.fresh.setProperty(QuotationLevel, level + 1)
@@ -43,7 +43,7 @@ object StagingContext {
4343
ctx.property(TaggedTypes).get
4444

4545
/** Context with a decremented quotation level and pops the Some of top of the quote context stack or None if the stack is empty.
46-
* The quotation stack could be empty if we are in a top level splice or an eroneous splice directly witin a top level splice.
46+
* The quotation stack could be empty if we are in a top level splice or an erroneous splice directly within a top level splice.
4747
*/
4848
def popQuotes()(using Context): (Option[tpd.Tree], Context) =
4949
val ctx1 = ctx.fresh.setProperty(QuotationLevel, level - 1)

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,7 @@ object StdNames {
433433
val common: N = "common"
434434
val compiletime : N = "compiletime"
435435
val conforms_ : N = "$conforms"
436+
val contents: N = "contents"
436437
val copy: N = "copy"
437438
val currentMirror: N = "currentMirror"
438439
val create: N = "create"
@@ -486,6 +487,7 @@ object StdNames {
486487
val hash_ : N = "hash"
487488
val head: N = "head"
488489
val higherKinds: N = "higherKinds"
490+
val idx: N = "idx"
489491
val identity: N = "identity"
490492
val implicitConversions: N = "implicitConversions"
491493
val implicitly: N = "implicitly"
@@ -553,6 +555,7 @@ object StdNames {
553555
val productElementName: N = "productElementName"
554556
val productIterator: N = "productIterator"
555557
val productPrefix: N = "productPrefix"
558+
val quotes : N = "quotes"
556559
val raw_ : N = "raw"
557560
val refl: N = "refl"
558561
val reflect: N = "reflect"

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -637,11 +637,11 @@ class TreePickler(pickler: TastyPickler) {
637637
pickleTree(hi)
638638
pickleTree(alias)
639639
}
640-
case Hole(_, idx, args) =>
640+
case Hole(_, idx, args, _, tpt) =>
641641
writeByte(HOLE)
642642
withLength {
643643
writeNat(idx)
644-
pickleType(tree.tpe, richTypes = true)
644+
pickleType(tpt.tpe, richTypes = true)
645645
args.foreach(pickleTree)
646646
}
647647
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,7 +1323,7 @@ class TreeUnpickler(reader: TastyReader,
13231323
val idx = readNat()
13241324
val tpe = readType()
13251325
val args = until(end)(readTerm())
1326-
Hole(true, idx, args).withType(tpe)
1326+
Hole(true, idx, args, EmptyTree, TypeTree(tpe)).withType(tpe)
13271327
case _ =>
13281328
readPathTerm()
13291329
}
@@ -1357,7 +1357,7 @@ class TreeUnpickler(reader: TastyReader,
13571357
val idx = readNat()
13581358
val tpe = readType()
13591359
val args = until(end)(readTerm())
1360-
Hole(false, idx, args).withType(tpe)
1360+
Hole(false, idx, args, EmptyTree, TypeTree(tpe)).withType(tpe)
13611361
case _ =>
13621362
if (isTypeTreeTag(nextByte)) readTerm()
13631363
else {

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -697,10 +697,12 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
697697
"Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}"
698698
case MacroTree(call) =>
699699
keywordStr("macro ") ~ toTextGlobal(call)
700-
case Hole(isTermHole, idx, args) =>
701-
val (prefix, postfix) = if isTermHole then ("{{{ ", " }}}") else ("[[[ ", " ]]]")
700+
case Hole(isTermHole, idx, args, content, tpt) =>
701+
val (prefix, postfix) = if isTermHole then ("{{{", "}}}") else ("[[[", "]]]")
702702
val argsText = toTextGlobal(args, ", ")
703-
prefix ~~ idx.toString ~~ "|" ~~ argsText ~~ postfix
703+
val contentText = toTextGlobal(content)
704+
val tptText = toTextGlobal(tpt)
705+
prefix ~~ idx.toString ~~ "|" ~~ tptText ~~ "|" ~~ argsText ~~ "|" ~~ contentText ~~ postfix
704706
case _ =>
705707
tree.fallbackToText(this)
706708
}

0 commit comments

Comments
 (0)