@@ -61,44 +61,117 @@ object PickledQuotes {
61
61
}.apply(tp)
62
62
63
63
/** Unpickle the tree contained in the TastyExpr */
64
- def unpickleExpr (tasty : PickledQuote , args : PickledArgs )(implicit ctx : Context ): Tree = {
64
+ def unpickleExpr (tasty : PickledQuote , splices : PickledArgs )(implicit ctx : Context ): Tree = {
65
65
val tastyBytes = TastyString .unpickle(tasty)
66
- val unpickled = unpickle(tastyBytes, args, isType = false )(ctx.addMode(Mode .ReadPositions ))
67
- /** Force unpickling of the tree, removes the spliced type `@quotedTypeTag type` definitions and dealiases references to `@quotedTypeTag type` */
68
- val forceAndCleanArtefacts = new TreeMap {
69
- override def transform (tree : tpd.Tree )(implicit ctx : Context ): tpd.Tree = tree match {
70
- case Block (stat :: rest, expr1) if stat.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot ) =>
71
- assert(rest.forall { case tdef : TypeDef => tdef.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot ) })
72
- transform(expr1)
73
- case tree => super .transform(tree).withType(dealiasTypeTags(tree.tpe))
74
- }
75
- }
76
- forceAndCleanArtefacts.transform(unpickled)
66
+ val unpickled = unpickle(tastyBytes, splices, isType = false )(ctx.addMode(Mode .ReadPositions ))
67
+ val Inlined (call, Nil , expnasion) = unpickled
68
+ val inlineCtx = inlineContext(call)
69
+ val expansion1 = spliceTypes(expnasion, splices)(using inlineCtx)
70
+ val expansion2 = spliceTerms(expansion1, splices)(using inlineCtx)
71
+ cpy.Inlined (unpickled)(call, Nil , expansion2)
77
72
}
78
73
79
74
/** Unpickle the tree contained in the TastyType */
80
75
def unpickleType (tasty : PickledQuote , args : PickledArgs )(implicit ctx : Context ): Tree = {
81
76
val tastyBytes = TastyString .unpickle(tasty)
82
77
val unpickled = unpickle(tastyBytes, args, isType = true )(ctx.addMode(Mode .ReadPositions ))
83
- val tpt = unpickled match {
84
- case Block (aliases, tpt) =>
85
- // `@quoteTypeTag type` aliases are not required after unpickling.
86
- // Type definitions are placeholders for type holes in the pickled quote, at this point
87
- // those holes have been filled. As we already dealias al references to them in `dealiasTypeTags`
88
- // there is no need to keep their definitions in the tree. As artifacts of quote reification
89
- // they also do not have a meaningful position in the source.
90
- val aliases1 = aliases.filter(! _.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot ))
91
- seq(aliases1, tpt)
92
- case tpt => tpt
78
+ spliceTypes(unpickled, args)
79
+ }
80
+
81
+ /** Replace all term holes with the spliced terms */
82
+ private def spliceTerms (tree : Tree , splices : PickledArgs )(using Context ): Tree = {
83
+ val evaluateHoles = new TreeMap {
84
+ override def transform (tree : tpd.Tree )(implicit ctx : Context ): tpd.Tree = tree match {
85
+ case Hole (isTerm, idx, args) =>
86
+ val reifiedArgs = args.map { arg =>
87
+ if (arg.isTerm) (qctx : scala.quoted.QuoteContext ) ?=> new TastyTreeExpr (arg, QuoteContext .scopeId)
88
+ else new TreeType (arg, QuoteContext .scopeId)
89
+ }
90
+ if isTerm then
91
+ val splice1 = splices(idx).asInstanceOf [Seq [Any ] => scala.quoted.QuoteContext ?=> quoted.Expr [? ]]
92
+ val quotedExpr = splice1(reifiedArgs)(using dotty.tools.dotc.quoted.QuoteContext ())
93
+ val filled = PickledQuotes .quotedExprToTree(quotedExpr)
94
+
95
+ // We need to make sure a hole is created with the source file of the surrounding context, even if
96
+ // it filled with contents a different source file.
97
+ if filled.source == ctx.source then filled
98
+ else filled.cloneIn(ctx.source).withSpan(tree.span)
99
+ else
100
+ // Replaces type holes generated by ReifyQuotes (non-spliced types).
101
+ // These are types defined in a quote and used at the same level in a nested quote.
102
+ val quotedType = splices(idx).asInstanceOf [Seq [Any ] => quoted.Type [? ]](reifiedArgs)
103
+ PickledQuotes .quotedTypeToTree(quotedType)
104
+ case tree : Select =>
105
+ // Retain selected members
106
+ val qual = transform(tree.qualifier)
107
+ qual.select(tree.symbol).withSpan(tree.span)
108
+
109
+ case tree =>
110
+ if tree.isDef then
111
+ tree.symbol.annotations = tree.symbol.annotations.map {
112
+ annot => annot.derivedAnnotation(transform(annot.tree))
113
+ }
114
+ end if
115
+
116
+ val tree1 = super .transform(tree)
117
+ tree1.withType(mapAnnots(tree1.tpe))
118
+ }
119
+
120
+ // Evaluate holes in type annotations
121
+ private val mapAnnots = new TypeMap {
122
+ override def apply (tp : Type ): Type = {
123
+ tp match
124
+ case tp @ AnnotatedType (underlying, annot) =>
125
+ val underlying1 = this (underlying)
126
+ derivedAnnotatedType(tp, underlying1, annot.derivedAnnotation(transform(annot.tree)))
127
+ case _ => mapOver(tp)
128
+ }
129
+ }
93
130
}
94
- tpt.withType(dealiasTypeTags(tpt.tpe))
131
+ val tree1 = evaluateHoles.transform(tree)
132
+ quotePickling.println(i " **** evaluated quote \n $tree1" )
133
+ tree1
134
+ }
135
+
136
+ /** Replace all type holes generated with the spliced types */
137
+ private def spliceTypes (tree : Tree , splices : PickledArgs )(using Context ): Tree = {
138
+ tree match
139
+ case Block (stat :: rest, expr1) if stat.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot ) =>
140
+ val typeSpliceMap = (stat :: rest).iterator.map {
141
+ case tdef : TypeDef =>
142
+ assert(tdef.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot ))
143
+ val tree = tdef.rhs match
144
+ case TypeBoundsTree (_, Hole (_, idx, args), _) =>
145
+ val quotedType = splices(idx).asInstanceOf [Seq [Any ] => quoted.Type [? ]](args)
146
+ PickledQuotes .quotedTypeToTree(quotedType)
147
+ case TypeBoundsTree (_, tpt, _) =>
148
+ tpt
149
+ (tdef.symbol, tree.tpe)
150
+ }.toMap
151
+ class ReplaceSplicedTyped extends TypeMap () {
152
+ override def apply (tp : Type ): Type = {
153
+ val tp1 = tp match {
154
+ case tp : TypeRef =>
155
+ typeSpliceMap.get(tp.symbol) match
156
+ case Some (t) if tp.typeSymbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot ) => t
157
+ case None => tp
158
+ case _ => tp
159
+ }
160
+ mapOver(tp1)
161
+ }
162
+ }
163
+ val expansion2 = new TreeTypeMap (new ReplaceSplicedTyped ).transform(expr1)
164
+ quotePickling.println(i " **** typed quote \n ${expansion2.show}" )
165
+ expansion2
166
+ case _ =>
167
+ tree
95
168
}
96
169
97
170
// TASTY picklingtests/pos/quoteTest.scala
98
171
99
172
/** Pickle tree into it's TASTY bytes s*/
100
173
private def pickle (tree : Tree )(implicit ctx : Context ): Array [Byte ] = {
101
- quotePickling.println(i " **** pickling quote of \n ${ tree.show} " )
174
+ quotePickling.println(i " **** pickling quote of \n $tree" )
102
175
val pickler = new TastyPickler (defn.RootClass )
103
176
val treePkl = pickler.treePkl
104
177
treePkl.pickle(tree :: Nil )
@@ -122,7 +195,13 @@ object PickledQuotes {
122
195
unpickler.enter(Set .empty)
123
196
124
197
val tree = unpickler.tree
125
- quotePickling.println(i " **** unpickle quote ${tree.show}" )
198
+
199
+ // Make sure trees and positions are fully loaded
200
+ new TreeTraverser {
201
+ def traverse (tree : Tree )(implicit ctx : Context ): Unit = traverseChildren(tree)
202
+ }.traverse(tree)
203
+
204
+ quotePickling.println(i " **** unpickled quote \n $tree" )
126
205
tree
127
206
}
128
207
0 commit comments