From 388d92e5f41b7bc4a07e1b43beae78134a630b64 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 16 Mar 2023 11:56:03 +0100 Subject: [PATCH 1/5] Make sure captured types are listed before terms --- .../src/dotty/tools/dotc/transform/Splicing.scala | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index bb82fba32a7c..7f89634ee09d 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -188,7 +188,8 @@ class Splicing extends MacroTransform: * ``` */ private class SpliceTransformer(spliceOwner: Symbol, isCaptured: Symbol => Boolean) extends Transformer: - private var refBindingMap = mutable.Map.empty[Symbol, (Tree, Symbol)] + private val typeBindingMap = mutable.Map.empty[Symbol, (Tree, Symbol)] + private val termBindingMap = mutable.Map.empty[Symbol, (Tree, Symbol)] /** Reference to the `Quotes` instance of the current level 1 splice */ private var quotes: Tree | Null = null // TODO: add to the context private var healedTypes: QuoteTypeTags | Null = null // TODO: add to the context @@ -196,7 +197,10 @@ class Splicing extends MacroTransform: def transformSplice(tree: tpd.Tree, tpe: Type, holeIdx: Int)(using Context): tpd.Tree = assert(level == 0) val newTree = transform(tree) - val (refs, bindings) = refBindingMap.values.toList.unzip + val (typeRefs, typeBindings) = typeBindingMap.values.toList.unzip + val (termRefs, termBindings) = termBindingMap.values.toList.unzip + val refs = typeRefs ::: termRefs + val bindings = typeBindings ::: termBindings val bindingsTypes = bindings.map(_.termRef.widenTermRefExpr) val methType = MethodType(bindingsTypes, newTree.tpe) val meth = newSymbol(spliceOwner, nme.ANON_FUN, Synthetic | Method, methType) @@ -348,7 +352,7 @@ class Splicing extends MacroTransform: Param, defn.QuotedExprClass.typeRef.appliedTo(tpe), ) - val bindingSym = refBindingMap.getOrElseUpdate(tree.symbol, (tree, newBinding))._2 + val bindingSym = termBindingMap.getOrElseUpdate(tree.symbol, (tree, newBinding))._2 ref(bindingSym) private def newQuotedTypeClassBinding(tpe: Type)(using Context) = @@ -361,7 +365,7 @@ class Splicing extends MacroTransform: private def capturedType(tree: Tree)(using Context): Symbol = val tpe = tree.tpe.widenTermRefExpr - val bindingSym = refBindingMap + val bindingSym = typeBindingMap .getOrElseUpdate(tree.symbol, (TypeTree(tree.tpe), newQuotedTypeClassBinding(tpe)))._2 bindingSym @@ -371,7 +375,7 @@ class Splicing extends MacroTransform: val capturePartTypes = new TypeMap { def apply(tp: Type) = tp match { case typeRef: TypeRef if containsCapturedType(typeRef) => - val termRef = refBindingMap + val termRef = typeBindingMap .getOrElseUpdate(typeRef.symbol, (TypeTree(typeRef), newQuotedTypeClassBinding(typeRef)))._2.termRef val tagRef = healedTypes.nn.getTagRef(termRef) tagRef From 67b16d0ed36e9bace82ccf5dee48152f0860f992 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Mar 2023 09:18:27 +0100 Subject: [PATCH 2/5] Add type argument list to the `Hole` tree --- .../dotty/tools/dotc/ast/TreeTypeMap.scala | 5 +-- compiler/src/dotty/tools/dotc/ast/Trees.scala | 31 ++++++++++++------- compiler/src/dotty/tools/dotc/ast/tpd.scala | 4 +-- compiler/src/dotty/tools/dotc/ast/untpd.scala | 2 +- .../tools/dotc/core/tasty/TreePickler.scala | 3 +- .../tools/dotc/core/tasty/TreeUnpickler.scala | 6 ++-- .../tools/dotc/printing/RefinedPrinter.scala | 9 +++--- .../tools/dotc/quoted/PickledQuotes.scala | 12 +++---- .../tools/dotc/transform/PickleQuotes.scala | 2 +- .../dotty/tools/dotc/transform/Splicing.scala | 5 ++- .../tools/dotc/transform/TreeChecker.scala | 6 ++-- 11 files changed, 48 insertions(+), 37 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala index faeafae97f5e..968974620e5e 100644 --- a/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala +++ b/compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -146,11 +146,12 @@ class TreeTypeMap( val bind1 = tmap.transformSub(bind) val expr1 = tmap.transform(expr) cpy.Labeled(labeled)(bind1, expr1) - case tree @ Hole(_, _, args, content, tpt) => + case tree @ Hole(_, _, targs, args, content, tpt) => + val targs1 = targs.mapConserve(transform) val args1 = args.mapConserve(transform) val content1 = transform(content) val tpt1 = transform(tpt) - cpy.Hole(tree)(args = args1, content = content1, tpt = tpt1) + cpy.Hole(tree)(targs = targs1, args = args1, content = content1, tpt = tpt1) case tree1 => super.transform(tree1) } diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index c0b5987c3875..8a600671828b 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -978,13 +978,20 @@ object Trees { /** Tree that replaces a level 1 splices in pickled (level 0) quotes. * It is only used when picking quotes (will never be in a TASTy file). * + * Hole created by this compile separate the targs from the args. Holes + * generated with 3.0-3.3 contain all type args and targs in any order in + * a single list. For backwards compatibility we read holes from tasty as + * if they had no targs and have only args. Therefore the args may contain + * type trees. + * * @param isTermHole If this hole is a term, otherwise it is a type hole. * @param idx The index of the hole in it's enclosing level 0 quote. - * @param args The arguments of the splice to compute its content - * @param content Lambda that computes the content of the hole. This tree is empty when in a quote pickle. + * @param targs The type arguments of the splice to compute its content + * @param args The term (or type) arguments of the splice to compute its content * @param tpt Type of the hole + * @param content Lambda that computes the content of the hole. This tree is empty when in a quote pickle. */ - 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] { + case class Hole[+T <: Untyped](isTermHole: Boolean, idx: Int, targs: List[Tree[T]], args: List[Tree[T]], content: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends Tree[T] { type ThisTree[+T <: Untyped] <: Hole[T] override def isTerm: Boolean = isTermHole override def isType: Boolean = !isTermHole @@ -1337,9 +1344,9 @@ object Trees { case tree: Thicket if (trees eq tree.trees) => tree case _ => finalize(tree, untpd.Thicket(trees)(sourceFile(tree))) } - def Hole(tree: Tree)(isTerm: Boolean, idx: Int, args: List[Tree], content: Tree, tpt: Tree)(using Context): Hole = tree match { - case tree: Hole if isTerm == tree.isTerm && idx == tree.idx && args.eq(tree.args) && content.eq(tree.content) && content.eq(tree.content) => tree - case _ => finalize(tree, untpd.Hole(isTerm, idx, args, content, tpt)(sourceFile(tree))) + def Hole(tree: Tree)(isTerm: Boolean, idx: Int, targs: List[Tree], args: List[Tree], content: Tree, tpt: Tree)(using Context): Hole = tree match { + case tree: Hole if isTerm == tree.isTerm && idx == tree.idx && targs.eq(tree.targs) && args.eq(tree.args) && content.eq(tree.content) && content.eq(tree.content) => tree + case _ => finalize(tree, untpd.Hole(isTerm, idx, targs, args, content, tpt)(sourceFile(tree))) } // Copier methods with default arguments; these demand that the original tree @@ -1362,8 +1369,8 @@ object Trees { TypeDef(tree: Tree)(name, rhs) def Template(tree: Template)(using Context)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, derived: List[untpd.Tree] = tree.derived, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody): Template = Template(tree: Tree)(constr, parents, derived, self, body) - 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 = - Hole(tree: Tree)(isTerm, idx, args, content, tpt) + def Hole(tree: Hole)(isTerm: Boolean = tree.isTerm, idx: Int = tree.idx, targs: List[Tree] = tree.targs, args: List[Tree] = tree.args, content: Tree = tree.content, tpt: Tree = tree.tpt)(using Context): Hole = + Hole(tree: Tree)(isTerm, idx, targs, args, content, tpt) } @@ -1494,8 +1501,8 @@ object Trees { case Thicket(trees) => val trees1 = transform(trees) if (trees1 eq trees) tree else Thicket(trees1) - case tree @ Hole(_, _, args, content, tpt) => - cpy.Hole(tree)(args = transform(args), content = transform(content), tpt = transform(tpt)) + case tree @ Hole(_, _, targs, args, content, tpt) => + cpy.Hole(tree)(targs = transform(targs), args = transform(args), content = transform(content), tpt = transform(tpt)) case _ => transformMoreCases(tree) } @@ -1635,8 +1642,8 @@ object Trees { this(this(x, arg), annot) case Thicket(ts) => this(x, ts) - case Hole(_, _, args, content, tpt) => - this(this(this(x, args), content), tpt) + case Hole(_, _, targs, args, content, tpt) => + this(this(this(this(x, targs), args), content), tpt) case _ => foldMoreCases(x, tree) } diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index d1b1cdf607b5..e9aac976d6c9 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -391,8 +391,8 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Throw(expr: Tree)(using Context): Tree = ref(defn.throwMethod).appliedTo(expr) - def Hole(isTermHole: Boolean, idx: Int, args: List[Tree], content: Tree, tpt: Tree)(using Context): Hole = - ta.assignType(untpd.Hole(isTermHole, idx, args, content, tpt), tpt) + def Hole(isTermHole: Boolean, idx: Int, targs: List[Tree], args: List[Tree], content: Tree, tpt: Tree)(using Context): Hole = + ta.assignType(untpd.Hole(isTermHole, idx, targs, args, content, tpt), tpt) // ------ Making references ------------------------------------------------------ diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index a262c3658399..28c162d4d52e 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -426,7 +426,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def Export(expr: Tree, selectors: List[ImportSelector])(implicit src: SourceFile): Export = new Export(expr, selectors) def PackageDef(pid: RefTree, stats: List[Tree])(implicit src: SourceFile): PackageDef = new PackageDef(pid, stats) def Annotated(arg: Tree, annot: Tree)(implicit src: SourceFile): Annotated = new Annotated(arg, annot) - def Hole(isTermHole: Boolean, idx: Int, args: List[Tree], content: Tree, tpt: Tree)(implicit src: SourceFile): Hole = new Hole(isTermHole, idx, args, content, tpt) + def Hole(isTermHole: Boolean, idx: Int, targs: List[Tree], args: List[Tree], content: Tree, tpt: Tree)(implicit src: SourceFile): Hole = new Hole(isTermHole, idx, targs, args, content, tpt) // ------ Additional creation methods for untyped only ----------------- diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 8a396921f32b..b73e37c3b8c5 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -665,11 +665,12 @@ class TreePickler(pickler: TastyPickler) { pickleTree(hi) pickleTree(alias) } - case Hole(_, idx, args, _, tpt) => + case Hole(_, idx, targs, args, _, tpt) => writeByte(HOLE) withLength { writeNat(idx) pickleType(tpt.tpe, richTypes = true) + targs.foreach(pickleTree) args.foreach(pickleTree) } } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 9078a8959112..8ed88740253e 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1438,8 +1438,9 @@ class TreeUnpickler(reader: TastyReader, case HOLE => val idx = readNat() val tpe = readType() + val targs = Nil // TODO read tags seprately from args val args = until(end)(readTerm()) - Hole(true, idx, args, EmptyTree, TypeTree(tpe)).withType(tpe) + Hole(true, idx, targs, args, EmptyTree, TypeTree(tpe)).withType(tpe) case _ => readPathTerm() } @@ -1472,8 +1473,9 @@ class TreeUnpickler(reader: TastyReader, val end = readEnd() val idx = readNat() val tpe = readType() + val targs = Nil // TODO read tags seprately from args val args = until(end)(readTerm()) - Hole(false, idx, args, EmptyTree, TypeTree(tpe)).withType(tpe) + Hole(false, idx, targs, args, EmptyTree, TypeTree(tpe)).withType(tpe) case _ => if (isTypeTreeTag(nextByte)) readTerm() else { diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 014e5ddf0d66..2107cabbb501 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -724,12 +724,13 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { "Thicket {" ~~ toTextGlobal(trees, "\n") ~~ "}" case MacroTree(call) => keywordStr("macro ") ~ toTextGlobal(call) - case Hole(isTermHole, idx, args, content, tpt) => + case Hole(isTermHole, idx, targs, args, content, tpt) => val (prefix, postfix) = if isTermHole then ("{{{", "}}}") else ("[[[", "]]]") - val argsText = toTextGlobal(args, ", ") - val contentText = toTextGlobal(content) + val targsText = ("[" ~ toTextGlobal(targs, ", ") ~ "]").provided(targs.nonEmpty) + val argsText = ("(" ~ toTextGlobal(args, ", ") ~ ")").provided(args.nonEmpty) val tptText = toTextGlobal(tpt) - prefix ~~ idx.toString ~~ "|" ~~ tptText ~~ "|" ~~ argsText ~~ "|" ~~ contentText ~~ postfix + val contentText = ("|" ~~ toTextGlobal(content)).provided(content ne EmptyTree) + prefix ~~ idx.toString ~ targsText ~ argsText ~ ":" ~~ tptText ~~ contentText ~~ postfix case CapturingTypeTree(refs, parent) => parent match case ImpureByNameTypeTree(bntpt) => diff --git a/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala index 20bcba417a5e..091344b2fbef 100644 --- a/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala @@ -100,14 +100,14 @@ object PickledQuotes { private def spliceTerms(tree: Tree, typeHole: TypeHole, termHole: ExprHole)(using Context): Tree = { def evaluateHoles = new TreeMap { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match { - case Hole(isTermHole, idx, args, _, _) => + case Hole(isTermHole, idx, targs, args, _, _) => inContext(SpliceScope.contextWithNewSpliceScope(tree.sourcePos)) { if isTermHole then val quotedExpr = termHole match case ExprHole.V1(evalHole) => - evalHole.nn.apply(idx, reifyExprHoleV1Args(args), QuotesImpl()) + evalHole.nn.apply(idx, reifyExprHoleV1Args(targs ::: args), QuotesImpl()) case ExprHole.V2(evalHole) => - evalHole.nn.apply(idx, reifyExprHoleV2Args(args), QuotesImpl()) + evalHole.nn.apply(idx, reifyExprHoleV2Args(targs ::: args), QuotesImpl()) val filled = PickledQuotes.quotedExprToTree(quotedExpr) @@ -165,15 +165,15 @@ object PickledQuotes { val tree = typeHole match case TypeHole.V1(evalHole) => tdef.rhs match - case TypeBoundsTree(_, Hole(_, idx, args, _, _), _) => + case TypeBoundsTree(_, Hole(_, idx, targs, args, _, _), _) => // To keep for backwards compatibility. In some older version holes where created in the bounds. - val quotedType = evalHole.nn.apply(idx, reifyTypeHoleArgs(args)) + val quotedType = evalHole.nn.apply(idx, reifyTypeHoleArgs(targs ::: args)) PickledQuotes.quotedTypeToTree(quotedType) case TypeBoundsTree(_, tpt, _) => // To keep for backwards compatibility. In some older version we missed the creation of some holes. tpt case TypeHole.V2(types) => - val Hole(_, idx, _, _, _) = tdef.rhs: @unchecked + val Hole(_, idx, _, _, _, _) = tdef.rhs: @unchecked PickledQuotes.quotedTypeToTree(types.nn.apply(idx)) (tdef.symbol, tree.tpe) }.toMap diff --git a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala index 728ee9552c81..0b496c074e7c 100644 --- a/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala @@ -126,7 +126,7 @@ class PickleQuotes extends MacroTransform { private val contents = List.newBuilder[Tree] override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match - case tree @ Hole(isTerm, _, _, content, _) => + case tree @ Hole(isTerm, _, _, _, content, _) => if !content.isEmpty then contents += content val holeType = diff --git a/compiler/src/dotty/tools/dotc/transform/Splicing.scala b/compiler/src/dotty/tools/dotc/transform/Splicing.scala index 7f89634ee09d..879b8e8f4f4b 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicing.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicing.scala @@ -132,7 +132,7 @@ class Splicing extends MacroTransform: case None => val holeIdx = numHoles numHoles += 1 - val hole = tpd.Hole(false, holeIdx, Nil, ref(qual), TypeTree(tp)) + val hole = tpd.Hole(false, holeIdx, Nil, Nil, ref(qual), TypeTree(tp)) typeHoles.put(qual.symbol, hole) hole cpy.TypeDef(tree)(rhs = hole) @@ -199,7 +199,6 @@ class Splicing extends MacroTransform: val newTree = transform(tree) val (typeRefs, typeBindings) = typeBindingMap.values.toList.unzip val (termRefs, termBindings) = termBindingMap.values.toList.unzip - val refs = typeRefs ::: termRefs val bindings = typeBindings ::: termBindings val bindingsTypes = bindings.map(_.termRef.widenTermRefExpr) val methType = MethodType(bindingsTypes, newTree.tpe) @@ -207,7 +206,7 @@ class Splicing extends MacroTransform: val ddef = DefDef(meth, List(bindings), newTree.tpe, newTree.changeOwner(ctx.owner, meth)) val fnType = defn.FunctionType(bindings.size, isContextual = false).appliedTo(bindingsTypes :+ newTree.tpe) val closure = Block(ddef :: Nil, Closure(Nil, ref(meth), TypeTree(fnType))) - tpd.Hole(true, holeIdx, refs, closure, TypeTree(tpe)) + tpd.Hole(true, holeIdx, typeRefs, termRefs, closure, TypeTree(tpe)) override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match diff --git a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala index 6d904d1f3cc6..b94ef8dc77e2 100644 --- a/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala +++ b/compiler/src/dotty/tools/dotc/transform/TreeChecker.scala @@ -653,11 +653,11 @@ object TreeChecker { super.typedPackageDef(tree) override def typedHole(tree: untpd.Hole, pt: Type)(using Context): Tree = { - val tree1 @ Hole(isTermHole, _, args, content, tpt) = super.typedHole(tree, pt): @unchecked + val tree1 @ Hole(isTermHole, _, targs, args, content, tpt) = super.typedHole(tree, pt): @unchecked // Check that we only add the captured type `T` instead of a more complex type like `List[T]`. // If we have `F[T]` with captured `F` and `T`, we should list `F` and `T` separately in the args. - for arg <- args do + for arg <- (targs ::: args) do // TODO check targs and terms separately assert(arg.isTerm || arg.tpe.isInstanceOf[TypeRef], "Expected TypeRef in Hole type args but got: " + arg.tpe) // Check result type of the hole @@ -665,7 +665,7 @@ object TreeChecker { else assert(tpt.typeOpt =:= pt) // Check that the types of the args conform to the types of the contents of the hole - val argQuotedTypes = args.map { arg => + val argQuotedTypes = (targs ::: args).map { arg => if arg.isTerm then val tpe = arg.typeOpt.widenTermRefExpr match case _: MethodicType => From 5c691104ae444d5b79d2d64d4912b166151704a3 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Mar 2023 10:03:21 +0100 Subject: [PATCH 3/5] Split Hole from PrickledHole --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 36 ++++++++++++++++--- compiler/src/dotty/tools/dotc/ast/tpd.scala | 3 ++ compiler/src/dotty/tools/dotc/ast/untpd.scala | 1 + .../tools/dotc/core/tasty/TreeUnpickler.scala | 6 ++-- .../tools/dotc/quoted/PickledQuotes.scala | 12 +++---- .../tools/dotc/typer/QuotesAndSplices.scala | 4 +++ .../dotty/tools/dotc/typer/TypeAssigner.scala | 2 ++ .../src/dotty/tools/dotc/typer/Typer.scala | 1 + 8 files changed, 50 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 8a600671828b..ef57e1121cbf 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -975,6 +975,23 @@ object Trees { def genericEmptyValDef[T <: Untyped]: ValDef[T] = theEmptyValDef.asInstanceOf[ValDef[T]] def genericEmptyTree[T <: Untyped]: Thicket[T] = theEmptyTree.asInstanceOf[Thicket[T]] + /** Tree that replaces a level 1 splices in pickled (level 0) quotes. + * It is only used when encoding pickled quotes. These will be encoded + * as PickledHole when pickled. + * + * @param isTermHole If this hole is a term, otherwise it is a type hole. + * @param idx The index of the hole in it's enclosing level 0 quote. + * @param targs The type arguments of the splice to compute its content + * @param args The term (or type) arguments of the splice to compute its content + * @param tpt Type of the hole + * @param content Lambda that computes the content of the hole. This tree is empty when in a quote pickle. + */ + case class Hole[+T <: Untyped](isTermHole: Boolean, idx: Int, targs: List[Tree[T]], args: List[Tree[T]], content: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends Tree[T] { + type ThisTree[+T <: Untyped] <: Hole[T] + override def isTerm: Boolean = isTermHole + override def isType: Boolean = !isTermHole + } + /** Tree that replaces a level 1 splices in pickled (level 0) quotes. * It is only used when picking quotes (will never be in a TASTy file). * @@ -986,13 +1003,11 @@ object Trees { * * @param isTermHole If this hole is a term, otherwise it is a type hole. * @param idx The index of the hole in it's enclosing level 0 quote. - * @param targs The type arguments of the splice to compute its content * @param args The term (or type) arguments of the splice to compute its content * @param tpt Type of the hole - * @param content Lambda that computes the content of the hole. This tree is empty when in a quote pickle. */ - case class Hole[+T <: Untyped](isTermHole: Boolean, idx: Int, targs: List[Tree[T]], args: List[Tree[T]], content: Tree[T], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends Tree[T] { - type ThisTree[+T <: Untyped] <: Hole[T] + case class PickledHole[+T <: Untyped](isTermHole: Boolean, idx: Int, args: List[Tree[T]], tpt: Tree[T])(implicit @constructorOnly src: SourceFile) extends Tree[T] { + type ThisTree[+T <: Untyped] <: PickledHole[T] override def isTerm: Boolean = isTermHole override def isType: Boolean = !isTermHole } @@ -1119,6 +1134,7 @@ object Trees { type Thicket = Trees.Thicket[T] type Hole = Trees.Hole[T] + type PickledHole = Trees.PickledHole[T] @sharable val EmptyTree: Thicket = genericEmptyTree @sharable val EmptyValDef: ValDef = genericEmptyValDef @@ -1345,9 +1361,13 @@ object Trees { case _ => finalize(tree, untpd.Thicket(trees)(sourceFile(tree))) } def Hole(tree: Tree)(isTerm: Boolean, idx: Int, targs: List[Tree], args: List[Tree], content: Tree, tpt: Tree)(using Context): Hole = tree match { - case tree: Hole if isTerm == tree.isTerm && idx == tree.idx && targs.eq(tree.targs) && args.eq(tree.args) && content.eq(tree.content) && content.eq(tree.content) => tree + case tree: Hole if isTerm == tree.isTerm && idx == tree.idx && targs.eq(tree.targs) && args.eq(tree.args) && content.eq(tree.content) && tpt.eq(tree.tpt) => tree case _ => finalize(tree, untpd.Hole(isTerm, idx, targs, args, content, tpt)(sourceFile(tree))) } + def PickledHole(tree: Tree)(isTerm: Boolean, idx: Int, args: List[Tree], tpt: Tree)(using Context): PickledHole = tree match { + case tree: PickledHole if isTerm == tree.isTerm && idx == tree.idx && args.eq(tree.args) && tpt.eq(tree.tpt) => tree + case _ => finalize(tree, untpd.PickledHole(isTerm, idx, args, tpt)(sourceFile(tree))) + } // Copier methods with default arguments; these demand that the original tree // is of the same class as the copy. We only include trees with more than 2 elements here. @@ -1371,6 +1391,8 @@ object Trees { Template(tree: Tree)(constr, parents, derived, self, body) def Hole(tree: Hole)(isTerm: Boolean = tree.isTerm, idx: Int = tree.idx, targs: List[Tree] = tree.targs, args: List[Tree] = tree.args, content: Tree = tree.content, tpt: Tree = tree.tpt)(using Context): Hole = Hole(tree: Tree)(isTerm, idx, targs, args, content, tpt) + def PickledHole(tree: PickledHole)(isTerm: Boolean = tree.isTerm, idx: Int = tree.idx, args: List[Tree] = tree.args, tpt: Tree = tree.tpt)(using Context): PickledHole = + PickledHole(tree: Tree)(isTerm, idx, args, tpt) } @@ -1503,6 +1525,8 @@ object Trees { if (trees1 eq trees) tree else Thicket(trees1) case tree @ Hole(_, _, targs, args, content, tpt) => cpy.Hole(tree)(targs = transform(targs), args = transform(args), content = transform(content), tpt = transform(tpt)) + case tree @ PickledHole(_, _, args, tpt) => + cpy.PickledHole(tree)(args = transform(args), tpt = transform(tpt)) case _ => transformMoreCases(tree) } @@ -1644,6 +1668,8 @@ object Trees { this(x, ts) case Hole(_, _, targs, args, content, tpt) => this(this(this(this(x, targs), args), content), tpt) + case PickledHole(_, _, args, tpt) => + this(this(x, args), tpt) case _ => foldMoreCases(x, tree) } diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index e9aac976d6c9..d6fd20fb496f 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -394,6 +394,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Hole(isTermHole: Boolean, idx: Int, targs: List[Tree], args: List[Tree], content: Tree, tpt: Tree)(using Context): Hole = ta.assignType(untpd.Hole(isTermHole, idx, targs, args, content, tpt), tpt) + // def PickledHole(isTermHole: Boolean, idx: Int, args: List[Tree], tpt: Tree)(using Context): PickledHole = + // ta.assignType(untpd.PickledHole(isTermHole, idx, args, tpt), tpt) + // ------ Making references ------------------------------------------------------ def prefixIsElidable(tp: NamedType)(using Context): Boolean = { diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 28c162d4d52e..34c9e6393cfc 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -427,6 +427,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def PackageDef(pid: RefTree, stats: List[Tree])(implicit src: SourceFile): PackageDef = new PackageDef(pid, stats) def Annotated(arg: Tree, annot: Tree)(implicit src: SourceFile): Annotated = new Annotated(arg, annot) def Hole(isTermHole: Boolean, idx: Int, targs: List[Tree], args: List[Tree], content: Tree, tpt: Tree)(implicit src: SourceFile): Hole = new Hole(isTermHole, idx, targs, args, content, tpt) + def PickledHole(isTermHole: Boolean, idx: Int, args: List[Tree], tpt: Tree)(implicit src: SourceFile): PickledHole = new PickledHole(isTermHole, idx, args, tpt) // ------ Additional creation methods for untyped only ----------------- diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 8ed88740253e..d84fb9dc33a1 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1438,9 +1438,8 @@ class TreeUnpickler(reader: TastyReader, case HOLE => val idx = readNat() val tpe = readType() - val targs = Nil // TODO read tags seprately from args val args = until(end)(readTerm()) - Hole(true, idx, targs, args, EmptyTree, TypeTree(tpe)).withType(tpe) + PickledHole(true, idx, args, TypeTree(tpe)).withType(tpe) case _ => readPathTerm() } @@ -1473,9 +1472,8 @@ class TreeUnpickler(reader: TastyReader, val end = readEnd() val idx = readNat() val tpe = readType() - val targs = Nil // TODO read tags seprately from args val args = until(end)(readTerm()) - Hole(false, idx, targs, args, EmptyTree, TypeTree(tpe)).withType(tpe) + PickledHole(false, idx, args, TypeTree(tpe)).withType(tpe) case _ => if (isTypeTreeTag(nextByte)) readTerm() else { diff --git a/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala index 091344b2fbef..a3a3d5d8bf16 100644 --- a/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala @@ -100,14 +100,14 @@ object PickledQuotes { private def spliceTerms(tree: Tree, typeHole: TypeHole, termHole: ExprHole)(using Context): Tree = { def evaluateHoles = new TreeMap { override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match { - case Hole(isTermHole, idx, targs, args, _, _) => + case PickledHole(isTermHole, idx, args, _) => inContext(SpliceScope.contextWithNewSpliceScope(tree.sourcePos)) { if isTermHole then val quotedExpr = termHole match case ExprHole.V1(evalHole) => - evalHole.nn.apply(idx, reifyExprHoleV1Args(targs ::: args), QuotesImpl()) + evalHole.nn.apply(idx, reifyExprHoleV1Args(args), QuotesImpl()) case ExprHole.V2(evalHole) => - evalHole.nn.apply(idx, reifyExprHoleV2Args(targs ::: args), QuotesImpl()) + evalHole.nn.apply(idx, reifyExprHoleV2Args(args), QuotesImpl()) val filled = PickledQuotes.quotedExprToTree(quotedExpr) @@ -165,15 +165,15 @@ object PickledQuotes { val tree = typeHole match case TypeHole.V1(evalHole) => tdef.rhs match - case TypeBoundsTree(_, Hole(_, idx, targs, args, _, _), _) => + case TypeBoundsTree(_, PickledHole(_, idx, args, _), _) => // To keep for backwards compatibility. In some older version holes where created in the bounds. - val quotedType = evalHole.nn.apply(idx, reifyTypeHoleArgs(targs ::: args)) + val quotedType = evalHole.nn.apply(idx, reifyTypeHoleArgs(args)) PickledQuotes.quotedTypeToTree(quotedType) case TypeBoundsTree(_, tpt, _) => // To keep for backwards compatibility. In some older version we missed the creation of some holes. tpt case TypeHole.V2(types) => - val Hole(_, idx, _, _, _, _) = tdef.rhs: @unchecked + val PickledHole(_, idx, _, _) = tdef.rhs: @unchecked PickledQuotes.quotedTypeToTree(types.nn.apply(idx)) (tdef.symbol, tree.tpe) }.toMap diff --git a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala index 8473bd168bc5..90c81cf018d3 100644 --- a/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala +++ b/compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala @@ -168,6 +168,10 @@ trait QuotesAndSplices { val tpt = typedType(tree.tpt) assignType(tree, tpt) + def typedPickledHole(tree: untpd.PickledHole, pt: Type)(using Context): Tree = + val tpt = typedType(tree.tpt) + assignType(tree, tpt) + private def checkSpliceOutsideQuote(tree: untpd.Tree)(using Context): Unit = if (level == 0 && !ctx.owner.ownersIterator.exists(_.isInlineMethod)) report.error("Splice ${...} outside quotes '{...} or inline method", tree.srcPos) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 98e9cb638c17..625153f6f93f 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -536,6 +536,8 @@ trait TypeAssigner { def assignType(tree: untpd.Hole, tpt: Tree)(using Context): Hole = tree.withType(tpt.tpe) + def assignType(tree: untpd.PickledHole, tpt: Tree)(using Context): PickledHole = + tree.withType(tpt.tpe) } object TypeAssigner extends TypeAssigner: diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 8e8c8b2da1ca..08d733af4b35 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3014,6 +3014,7 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer case tree: untpd.Splice => typedSplice(tree, pt) case tree: untpd.MacroTree => report.error("Unexpected macro", tree.srcPos); tpd.nullLiteral // ill-formed code may reach here case tree: untpd.Hole => typedHole(tree, pt) + case tree: untpd.PickledHole => typedPickledHole(tree, pt) case _ => typedUnadapted(desugar(tree, pt), pt, locked) } From 76c9c8b0b3a3a0a44e605ff8d3bbc20049d2dd8b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Mar 2023 13:15:40 +0100 Subject: [PATCH 4/5] Add new QUOTEHOLE to TASTy format This is a new encoding of HOLE that differentiates between type and term arguments of the hole. --- .../tools/dotc/core/tasty/TastyPrinter.scala | 2 +- .../tools/dotc/core/tasty/TreePickler.scala | 7 ++--- .../tools/dotc/core/tasty/TreeUnpickler.scala | 29 +++++++++++-------- tasty/src/dotty/tools/tasty/TastyFormat.scala | 12 +++++--- 4 files changed, 29 insertions(+), 21 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala index 5876b69edfde..38a1cf8bb98d 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala @@ -131,7 +131,7 @@ class TastyPrinter(bytes: Array[Byte]) { printName(); printTrees() case REFINEDtype | TERMREFin | TYPEREFin | SELECTin => printName(); printTree(); printTrees() - case RETURN | HOLE => + case RETURN | HOLE | QUOTEHOLE => printNat(); printTrees() case METHODtype | POLYtype | TYPELAMBDAtype => printTree() diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index b73e37c3b8c5..ed50b1dfd57f 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -666,14 +666,13 @@ class TreePickler(pickler: TastyPickler) { pickleTree(alias) } case Hole(_, idx, targs, args, _, tpt) => - writeByte(HOLE) + writeByte(QUOTEHOLE) withLength { writeNat(idx) - pickleType(tpt.tpe, richTypes = true) - targs.foreach(pickleTree) + pickleTree(untpd.AppliedTypeTree(tpt, targs)) args.foreach(pickleTree) } - } + } catch { case ex: TypeError => report.error(ex.toMessage, tree.srcPos.focus) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index d84fb9dc33a1..149e0ac92bc3 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1435,11 +1435,8 @@ class TreeUnpickler(reader: TastyReader, val hi = if currentAddr == end then lo else readTpt() val alias = if currentAddr == end then EmptyTree else readTpt() createNullableTypeBoundsTree(lo, hi, alias) - case HOLE => - val idx = readNat() - val tpe = readType() - val args = until(end)(readTerm()) - PickledHole(true, idx, args, TypeTree(tpe)).withType(tpe) + case HOLE | QUOTEHOLE => + readHole(tag, end, isTerm = true) case _ => readPathTerm() } @@ -1467,13 +1464,8 @@ class TreeUnpickler(reader: TastyReader, val aliases = readStats(ctx.owner, end) val tpt = typeReader.readTpt() Block(aliases, tpt) - case HOLE => - readByte() - val end = readEnd() - val idx = readNat() - val tpe = readType() - val args = until(end)(readTerm()) - PickledHole(false, idx, args, TypeTree(tpe)).withType(tpe) + case HOLE | QUOTEHOLE => + readHole(readByte(), readEnd(), isTerm = false) case _ => if (isTypeTreeTag(nextByte)) readTerm() else { @@ -1517,6 +1509,19 @@ class TreeUnpickler(reader: TastyReader, owner => new LazyReader(localReader, owner, mode, source, op) } + def readHole(tag: Int, end: Addr, isTerm: Boolean)(using Context): Tree = tag match { + case QUOTEHOLE => + val idx = readNat() + val AppliedTypeTree(tpt, targs) = readTerm(): @unchecked + val args = until(end)(readTerm()) + PickledHole(isTerm, idx, targs ::: args, tpt).withType(tpt.tpe) + case HOLE => + val idx = readNat() + val tpe = readType() + val args = until(end)(readTerm()) + PickledHole(isTerm, idx, args, TypeTree(tpe)).withType(tpe) + } + // ------ Setting positions ------------------------------------------------ /** Pickled span for `addr`. */ diff --git a/tasty/src/dotty/tools/tasty/TastyFormat.scala b/tasty/src/dotty/tools/tasty/TastyFormat.scala index 226fc14acb39..7b9b52dc51fd 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -122,9 +122,10 @@ Standard-Section: "ASTs" TopLevelStat* MATCHtpt Length bound_Term? sel_Term CaseDef* -- sel match { CaseDef } where `bound` is optional upper bound of all rhs BYNAMEtpt underlying_Term -- => underlying SHAREDterm term_ASTRef -- Link to previously serialized term + QUOTEHOLE Length idx_Nat tptTargs_Tree arg_Tree* -- Splice hole with index `idx`, the type of the hole `tpt`, type arguments `targ`s and term arguments `args`s + -- tptTargs=APPLIEDtpt(tpt,targ*), this is not a true type application. Reused for its structure. HOLE Length idx_Nat tpe_Type arg_Tree* -- Splice hole with index `idx`, the type of the hole `tpe`, type and term arguments of the hole `arg`s - CaseDef = CASEDEF Length pat_Term rhs_Tree guard_Tree? -- case pat if guard => rhs ImplicitArg = IMPLICITARG arg_Term -- implicit unapply argument @@ -586,12 +587,14 @@ object TastyFormat { final val MATCHtpt = 191 final val MATCHCASEtype = 192 - final val HOLE = 255 + final val QUOTEHOLE = 254 + final val HOLE = 255 // Used from 3.0 to 3.3, repaced with QUOTEHOLE in 3.4+ final val firstNatTreeTag = SHAREDterm final val firstASTTreeTag = THIS final val firstNatASTTreeTag = IDENT final val firstLengthTreeTag = PACKAGE + final val firstQuoteTag = QUOTEHOLE /** Useful for debugging */ def isLegalTag(tag: Int): Boolean = @@ -600,7 +603,7 @@ object TastyFormat { firstASTTreeTag <= tag && tag <= BOUNDED || firstNatASTTreeTag <= tag && tag <= NAMEDARG || firstLengthTreeTag <= tag && tag <= MATCHtpt || - tag == HOLE + firstQuoteTag <= tag && tag <= HOLE def isParamTag(tag: Int): Boolean = tag == PARAM || tag == TYPEPARAM @@ -803,6 +806,7 @@ object TastyFormat { case ANNOTATION => "ANNOTATION" case PRIVATEqualified => "PRIVATEqualified" case PROTECTEDqualified => "PROTECTEDqualified" + case QUOTEHOLE => "QUOTEHOLE" case HOLE => "HOLE" } @@ -811,7 +815,7 @@ object TastyFormat { */ def numRefs(tag: Int): Int = tag match { case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM | NAMEDARG | RETURN | BIND | - SELFDEF | REFINEDtype | TERMREFin | TYPEREFin | SELECTin | HOLE => 1 + SELFDEF | REFINEDtype | TERMREFin | TYPEREFin | SELECTin | QUOTEHOLE | HOLE => 1 case RENAMED | PARAMtype => 2 case POLYtype | TYPELAMBDAtype | METHODtype => -1 case _ => 0 From 9a610d53af7b2d745f24bddad5ee132071165939 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 17 Mar 2023 16:00:15 +0100 Subject: [PATCH 5/5] Encode new HOLE format without adding a new tag --- .../tools/dotc/core/tasty/TastyPrinter.scala | 2 +- .../tools/dotc/core/tasty/TreePickler.scala | 4 +-- .../tools/dotc/core/tasty/TreeUnpickler.scala | 32 +++++++++---------- tasty/src/dotty/tools/tasty/TastyFormat.scala | 16 ++++------ 4 files changed, 26 insertions(+), 28 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala index 38a1cf8bb98d..5876b69edfde 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyPrinter.scala @@ -131,7 +131,7 @@ class TastyPrinter(bytes: Array[Byte]) { printName(); printTrees() case REFINEDtype | TERMREFin | TYPEREFin | SELECTin => printName(); printTree(); printTrees() - case RETURN | HOLE | QUOTEHOLE => + case RETURN | HOLE => printNat(); printTrees() case METHODtype | POLYtype | TYPELAMBDAtype => printTree() diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index ed50b1dfd57f..73d2122ac93f 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -666,9 +666,9 @@ class TreePickler(pickler: TastyPickler) { pickleTree(alias) } case Hole(_, idx, targs, args, _, tpt) => - writeByte(QUOTEHOLE) + writeByte(HOLE) withLength { - writeNat(idx) + writeNat(0x0100_0000 | idx) pickleTree(untpd.AppliedTypeTree(tpt, targs)) args.foreach(pickleTree) } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 149e0ac92bc3..a271cd035fc1 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1435,8 +1435,8 @@ class TreeUnpickler(reader: TastyReader, val hi = if currentAddr == end then lo else readTpt() val alias = if currentAddr == end then EmptyTree else readTpt() createNullableTypeBoundsTree(lo, hi, alias) - case HOLE | QUOTEHOLE => - readHole(tag, end, isTerm = true) + case HOLE => + readHole(end, isTerm = true) case _ => readPathTerm() } @@ -1464,8 +1464,9 @@ class TreeUnpickler(reader: TastyReader, val aliases = readStats(ctx.owner, end) val tpt = typeReader.readTpt() Block(aliases, tpt) - case HOLE | QUOTEHOLE => - readHole(readByte(), readEnd(), isTerm = false) + case HOLE => + readByte() + readHole(readEnd(), isTerm = false) case _ => if (isTypeTreeTag(nextByte)) readTerm() else { @@ -1509,18 +1510,17 @@ class TreeUnpickler(reader: TastyReader, owner => new LazyReader(localReader, owner, mode, source, op) } - def readHole(tag: Int, end: Addr, isTerm: Boolean)(using Context): Tree = tag match { - case QUOTEHOLE => - val idx = readNat() - val AppliedTypeTree(tpt, targs) = readTerm(): @unchecked - val args = until(end)(readTerm()) - PickledHole(isTerm, idx, targs ::: args, tpt).withType(tpt.tpe) - case HOLE => - val idx = readNat() - val tpe = readType() - val args = until(end)(readTerm()) - PickledHole(isTerm, idx, args, TypeTree(tpe)).withType(tpe) - } + def readHole(end: Addr, isTerm: Boolean)(using Context): Tree = + val idx = readNat() + idx >> 24 match + case 0 => + val tpe = readType() + val args = until(end)(readTerm()) + PickledHole(isTerm, idx, args, TypeTree(tpe)).withType(tpe) + case 1 => + val AppliedTypeTree(tpt, targs) = readTerm(): @unchecked + val args = until(end)(readTerm()) + PickledHole(isTerm, idx & 0x00FF_FFFF, targs ::: args, tpt).withType(tpt.tpe) // ------ Setting positions ------------------------------------------------ diff --git a/tasty/src/dotty/tools/tasty/TastyFormat.scala b/tasty/src/dotty/tools/tasty/TastyFormat.scala index 7b9b52dc51fd..c1c0047ea4be 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -122,9 +122,10 @@ Standard-Section: "ASTs" TopLevelStat* MATCHtpt Length bound_Term? sel_Term CaseDef* -- sel match { CaseDef } where `bound` is optional upper bound of all rhs BYNAMEtpt underlying_Term -- => underlying SHAREDterm term_ASTRef -- Link to previously serialized term - QUOTEHOLE Length idx_Nat tptTargs_Tree arg_Tree* -- Splice hole with index `idx`, the type of the hole `tpt`, type arguments `targ`s and term arguments `args`s - -- tptTargs=APPLIEDtpt(tpt,targ*), this is not a true type application. Reused for its structure. - HOLE Length idx_Nat tpe_Type arg_Tree* -- Splice hole with index `idx`, the type of the hole `tpe`, type and term arguments of the hole `arg`s + HOLE Length idxVersion_Nat tpe_Type|tptTargs_Tree arg_Tree* -- Splice hole with index `idx=idxVersion & 0x00FFFFFF` and `version = idxVersion >> 24`, + -- if `version == 0`: the type of the hole `tpe`, type and term arguments `args`s + -- if `version == 1`: the type of the hole `tpt`, type arguments `targ`s and term arguments `args`s + -- tptTargs=APPLIEDtpt(tpt,targ*), this is not a true type application. Reused for its structure. CaseDef = CASEDEF Length pat_Term rhs_Tree guard_Tree? -- case pat if guard => rhs ImplicitArg = IMPLICITARG arg_Term -- implicit unapply argument @@ -587,14 +588,12 @@ object TastyFormat { final val MATCHtpt = 191 final val MATCHCASEtype = 192 - final val QUOTEHOLE = 254 - final val HOLE = 255 // Used from 3.0 to 3.3, repaced with QUOTEHOLE in 3.4+ + final val HOLE = 255 final val firstNatTreeTag = SHAREDterm final val firstASTTreeTag = THIS final val firstNatASTTreeTag = IDENT final val firstLengthTreeTag = PACKAGE - final val firstQuoteTag = QUOTEHOLE /** Useful for debugging */ def isLegalTag(tag: Int): Boolean = @@ -603,7 +602,7 @@ object TastyFormat { firstASTTreeTag <= tag && tag <= BOUNDED || firstNatASTTreeTag <= tag && tag <= NAMEDARG || firstLengthTreeTag <= tag && tag <= MATCHtpt || - firstQuoteTag <= tag && tag <= HOLE + tag == HOLE def isParamTag(tag: Int): Boolean = tag == PARAM || tag == TYPEPARAM @@ -806,7 +805,6 @@ object TastyFormat { case ANNOTATION => "ANNOTATION" case PRIVATEqualified => "PRIVATEqualified" case PROTECTEDqualified => "PROTECTEDqualified" - case QUOTEHOLE => "QUOTEHOLE" case HOLE => "HOLE" } @@ -815,7 +813,7 @@ object TastyFormat { */ def numRefs(tag: Int): Int = tag match { case VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM | NAMEDARG | RETURN | BIND | - SELFDEF | REFINEDtype | TERMREFin | TYPEREFin | SELECTin | QUOTEHOLE | HOLE => 1 + SELFDEF | REFINEDtype | TERMREFin | TYPEREFin | SELECTin | HOLE => 1 case RENAMED | PARAMtype => 2 case POLYtype | TYPELAMBDAtype | METHODtype => -1 case _ => 0