Skip to content

Commit aa2578b

Browse files
committed
Define splice hole in library
Fixes scala#17137
1 parent 1f574e8 commit aa2578b

File tree

6 files changed

+60
-13
lines changed

6 files changed

+60
-13
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,9 @@ class Definitions {
848848
@tu lazy val QuoteUnpicklerClass: ClassSymbol = requiredClass("scala.quoted.runtime.QuoteUnpickler")
849849
@tu lazy val QuoteUnpickler_unpickleExprV2: Symbol = QuoteUnpicklerClass.requiredMethod("unpickleExprV2")
850850
@tu lazy val QuoteUnpickler_unpickleTypeV2: Symbol = QuoteUnpicklerClass.requiredMethod("unpickleTypeV2")
851+
@tu lazy val QuoteUnpicklerModule: Symbol = requiredModule("scala.quoted.runtime.QuoteUnpickler")
852+
@tu lazy val QuoteUnpickler_exprHole : Symbol = QuoteUnpicklerModule.requiredMethod("hole")
853+
@tu lazy val QuoteUnpickler_typeHole : Symbol = QuoteUnpicklerModule.requiredType("hole")
851854

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

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

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ class TreePickler(pickler: TastyPickler) {
334334
pickleName(sym.name)
335335
pickleParams
336336
tpt match {
337-
case _: Template | _: Hole => pickleTree(tpt)
337+
case _: Template => pickleTree(tpt)
338338
case _ if tpt.isType => pickleTpt(tpt)
339339
}
340340
pickleTreeUnlessEmpty(rhs)
@@ -413,7 +413,6 @@ class TreePickler(pickler: TastyPickler) {
413413
var ename = tree.symbol.targetName
414414
val selectFromQualifier =
415415
name.isTypeName
416-
|| qual.isInstanceOf[Hole] // holes have no symbol
417416
|| sig == Signature.NotAMethod // no overload resolution necessary
418417
|| !tree.denot.symbol.exists // polymorphic function type
419418
|| tree.denot.asSingleDenotation.isRefinedMethod // refined methods have no defining class symbol
@@ -665,13 +664,6 @@ class TreePickler(pickler: TastyPickler) {
665664
pickleTree(hi)
666665
pickleTree(alias)
667666
}
668-
case Hole(_, idx, args, _, tpt) =>
669-
writeByte(HOLE)
670-
withLength {
671-
writeNat(idx)
672-
pickleType(tpt.tpe, richTypes = true)
673-
args.foreach(pickleTree)
674-
}
675667
}
676668
catch {
677669
case ex: TypeError =>

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

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package dotty.tools.dotc.quoted
33
import dotty.tools.dotc.ast.Trees._
44
import dotty.tools.dotc.ast.{TreeTypeMap, tpd}
55
import dotty.tools.dotc.config.Printers._
6+
import dotty.tools.dotc.core.Constants._
67
import dotty.tools.dotc.core.Contexts._
78
import dotty.tools.dotc.core.Decorators._
89
import dotty.tools.dotc.core.Flags._
@@ -125,6 +126,32 @@ object PickledQuotes {
125126
val quotedType = evalHole.nn.apply(idx, reifyTypeHoleArgs(args))
126127
PickledQuotes.quotedTypeToTree(quotedType)
127128
}
129+
case Apply(TypeApply(hole, List(idxTree, tpt, targs)), List(Typed(SeqLiteral(args, _), _))) if hole.symbol == defn.QuoteUnpickler_exprHole =>
130+
inContext(SpliceScope.contextWithNewSpliceScope(tree.sourcePos)) {
131+
val idx = (idxTree.tpe: @unchecked) match
132+
case ConstantType(Constant(idx: Int)) => idx
133+
134+
def kListTypes(tp: Type): List[TypeTree] = tp match
135+
case AppliedType(kCons: TypeRef, headType :: tailType :: Nil) if kCons.symbol == defn.QuoteMatching_KCons =>
136+
TypeTree(headType) :: kListTypes(tailType)
137+
case kNil: TypeRef if kNil.symbol == defn.QuoteMatching_KNil =>
138+
Nil
139+
140+
val targsList = kListTypes(targs.tpe)
141+
val reifiedTypeArgs = reifyTypeHoleArgs(targsList)
142+
val reifiedArgs = reifyTypeHoleArgs(targsList)
143+
144+
val quotedExpr = (termHole: @unchecked) match
145+
case ExprHole.V2(evalHole) =>
146+
evalHole.nn.apply(idx, reifiedTypeArgs ::: reifyExprHoleV2Args(args), QuotesImpl())
147+
148+
val filled = PickledQuotes.quotedExprToTree(quotedExpr)
149+
150+
// We need to make sure a hole is created with the source file of the surrounding context, even if
151+
// it filled with contents a different source file.
152+
if filled.source == ctx.source then filled
153+
else filled.cloneIn(ctx.source).withSpan(tree.span)
154+
}
128155
case tree =>
129156
if tree.isDef then
130157
tree.symbol.annotations = tree.symbol.annotations.map {
@@ -173,7 +200,11 @@ object PickledQuotes {
173200
// To keep for backwards compatibility. In some older version we missed the creation of some holes.
174201
tpt
175202
case TypeHole.V2(types) =>
176-
val Hole(_, idx, _, _, _) = tdef.rhs: @unchecked
203+
val idx = tdef.rhs match
204+
case Hole(_, idx, _, _, _) => idx
205+
case rhs: TypeTree =>
206+
rhs.tpe match
207+
case AppliedType(tycon, ConstantType(Constant(idx: Int)) :: _) if tycon.typeSymbol == defn.QuoteUnpickler_typeHole => idx
177208
PickledQuotes.quotedTypeToTree(types.nn.apply(idx))
178209
(tdef.symbol, tree.tpe)
179210
}.toMap

compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,13 +126,26 @@ class PickleQuotes extends MacroTransform {
126126
private val contents = List.newBuilder[Tree]
127127
override def transform(tree: tpd.Tree)(using Context): tpd.Tree =
128128
tree match
129-
case tree @ Hole(isTerm, _, _, content, _) =>
129+
case tree @ Hole(isTerm, idx, rawArgs, content, tpt) =>
130130
if !content.isEmpty then
131131
contents += content
132132
val holeType =
133133
if isTerm then getTermHoleType(tree.tpe) else getTypeHoleType(tree.tpe)
134134
val hole = cpy.Hole(tree)(content = EmptyTree, TypeTree(holeType))
135-
if isTerm then Inlined(EmptyTree, Nil, hole).withSpan(tree.span) else hole
135+
val args = rawArgs.filter(_.isTerm)
136+
val targs = rawArgs.filter(!_.isTerm)
137+
val typeArgs = tpd.hkNestedPairsTypeTree(targs).tpe
138+
val holeTypeArgs = List(ConstantType(Constant(idx)), holeType, typeArgs)
139+
val newHole =
140+
if isTerm then
141+
ref(defn.QuoteUnpickler_exprHole)
142+
.appliedToTypes(holeTypeArgs)
143+
.appliedToVarargs(args, TypeTree(defn.AnyType))
144+
.withSpan(tree.span)
145+
else
146+
TypeTree(AppliedType(defn.QuoteUnpickler_typeHole.typeRef, holeTypeArgs))
147+
.withSpan(tree.span)
148+
if isTerm then Inlined(EmptyTree, Nil, newHole).withSpan(tree.span) else newHole
136149
case tree: DefTree =>
137150
val newAnnotations = tree.symbol.annotations.mapconserve { annot =>
138151
annot.derivedAnnotation(transform(annot.tree)(using ctx.withOwner(tree.symbol)))

library/src/scala/quoted/runtime/QuoteUnpickler.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package scala.quoted.runtime
22

3+
import scala.annotation.compileTimeOnly
34
import scala.quoted.{Quotes, Expr, Type}
45

56
/** Part of the Quotes interface that needs to be implemented by the compiler but is not visible to users */
@@ -32,3 +33,10 @@ trait QuoteUnpickler:
3233
* Generated for code compiled with Scala 3.2.0+
3334
*/
3435
def unpickleTypeV2[T <: AnyKind](pickled: String | List[String], types: Null | Seq[Type[?]]): scala.quoted.Type[T]
36+
37+
object QuoteUnpickler:
38+
@compileTimeOnly("Illegal reference to `scala.quoted.runtime.Expr.hole`")
39+
def hole[Idx <: Int, T, TArgs](args: Any*): T = ???
40+
41+
@compileTimeOnly("Illegal reference to `scala.quoted.runtime.Expr.hole`")
42+
type hole[Idx <: Int, T, TArgs] <: T

tasty/src/dotty/tools/tasty/TastyFormat.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ Standard-Section: "ASTs" TopLevelStat*
122122
MATCHtpt Length bound_Term? sel_Term CaseDef* -- sel match { CaseDef } where `bound` is optional upper bound of all rhs
123123
BYNAMEtpt underlying_Term -- => underlying
124124
SHAREDterm term_ASTRef -- Link to previously serialized term
125-
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
125+
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 (only used in 3.0-3.3)
126126
127127
128128
CaseDef = CASEDEF Length pat_Term rhs_Tree guard_Tree? -- case pat if guard => rhs

0 commit comments

Comments
 (0)