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..ef57e1121cbf 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -976,20 +976,42 @@ object Trees { 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 picking quotes (will never be in a TASTy file). + * 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 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 } + /** 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 term (or type) arguments of the splice to compute its content + * @param tpt Type of the hole + */ + 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 + } + def flatten[T <: Untyped](trees: List[Tree[T]]): List[Tree[T]] = { def recur(buf: ListBuffer[Tree[T]] | Null, remaining: List[Tree[T]]): ListBuffer[Tree[T]] | Null = remaining match { @@ -1112,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 @@ -1337,9 +1360,13 @@ 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) && 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 @@ -1362,8 +1389,10 @@ 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) + 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) } @@ -1494,8 +1523,10 @@ 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 tree @ PickledHole(_, _, args, tpt) => + cpy.PickledHole(tree)(args = transform(args), tpt = transform(tpt)) case _ => transformMoreCases(tree) } @@ -1635,8 +1666,10 @@ 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 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 d1b1cdf607b5..d6fd20fb496f 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -391,8 +391,11 @@ 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) + + // 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 ------------------------------------------------------ diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index a262c3658399..34c9e6393cfc 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -426,7 +426,8 @@ 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) + 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/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 8a396921f32b..73d2122ac93f 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -665,14 +665,14 @@ 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) + writeNat(0x0100_0000 | idx) + 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 9078a8959112..a271cd035fc1 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1436,10 +1436,7 @@ class TreeUnpickler(reader: TastyReader, 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()) - Hole(true, idx, args, EmptyTree, TypeTree(tpe)).withType(tpe) + readHole(end, isTerm = true) case _ => readPathTerm() } @@ -1469,11 +1466,7 @@ class TreeUnpickler(reader: TastyReader, Block(aliases, tpt) case HOLE => readByte() - val end = readEnd() - val idx = readNat() - val tpe = readType() - val args = until(end)(readTerm()) - Hole(false, idx, args, EmptyTree, TypeTree(tpe)).withType(tpe) + readHole(readEnd(), isTerm = false) case _ => if (isTypeTreeTag(nextByte)) readTerm() else { @@ -1517,6 +1510,18 @@ class TreeUnpickler(reader: TastyReader, owner => new LazyReader(localReader, owner, mode, source, op) } + 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 ------------------------------------------------ /** Pickled span for `addr`. */ 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..a3a3d5d8bf16 100644 --- a/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala @@ -100,7 +100,7 @@ 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 PickledHole(isTermHole, idx, args, _) => inContext(SpliceScope.contextWithNewSpliceScope(tree.sourcePos)) { if isTermHole then val quotedExpr = termHole match @@ -165,7 +165,7 @@ object PickledQuotes { val tree = typeHole match case TypeHole.V1(evalHole) => tdef.rhs match - case TypeBoundsTree(_, Hole(_, idx, 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(args)) PickledQuotes.quotedTypeToTree(quotedType) @@ -173,7 +173,7 @@ object PickledQuotes { // 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/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 bb82fba32a7c..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) @@ -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,14 +197,16 @@ 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 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) 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 @@ -348,7 +351,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 +364,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 +374,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 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 => 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) } diff --git a/tasty/src/dotty/tools/tasty/TastyFormat.scala b/tasty/src/dotty/tools/tasty/TastyFormat.scala index 226fc14acb39..c1c0047ea4be 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -122,8 +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 - 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