@@ -13,6 +13,7 @@ import dotty.tools.dotc.core.Symbols._
13
13
import dotty .tools .dotc .core .Types ._
14
14
import dotty .tools .dotc .core .tasty .TreePickler .Hole
15
15
import dotty .tools .dotc .core .tasty .{ PositionPickler , TastyPickler , TastyPrinter }
16
+ import dotty .tools .dotc .core .tasty .DottyUnpickler
16
17
import dotty .tools .dotc .core .tasty .TreeUnpickler .UnpickleMode
17
18
import dotty .tools .dotc .quoted .QuoteContext
18
19
import dotty .tools .dotc .tastyreflect .{ReflectionImpl , TastyTreeExpr , TreeType }
@@ -49,56 +50,118 @@ object PickledQuotes {
49
50
healOwner(tpe1.typeTree)
50
51
}
51
52
52
- private def dealiasTypeTags (tp : Type )(implicit ctx : Context ): Type = new TypeMap () {
53
- override def apply (tp : Type ): Type = {
54
- val tp1 = tp match {
55
- case tp : TypeRef if tp.typeSymbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot ) =>
56
- tp.symbol.info.hiBound
57
- case _ => tp
58
- }
59
- mapOver(tp1)
60
- }
61
- }.apply(tp)
62
-
63
53
/** Unpickle the tree contained in the TastyExpr */
64
- def unpickleExpr (tasty : PickledQuote , args : PickledArgs )(implicit ctx : Context ): Tree = {
54
+ def unpickleExpr (tasty : PickledQuote , splices : PickledArgs )(implicit ctx : Context ): Tree = {
65
55
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)
56
+ val unpickled = unpickle(tastyBytes, splices, isType = false )(ctx.addMode(Mode .ReadPositions ))
57
+ val Inlined (call, Nil , expnasion) = unpickled
58
+ val inlineCtx = inlineContext(call)
59
+ val expansion1 = spliceTypes(expnasion, splices)(using inlineCtx)
60
+ val expansion2 = spliceTerms(expansion1, splices)(using inlineCtx)
61
+ cpy.Inlined (unpickled)(call, Nil , expansion2)
77
62
}
78
63
79
64
/** Unpickle the tree contained in the TastyType */
80
65
def unpickleType (tasty : PickledQuote , args : PickledArgs )(implicit ctx : Context ): Tree = {
81
66
val tastyBytes = TastyString .unpickle(tasty)
82
67
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
68
+ spliceTypes(unpickled, args)
69
+ }
70
+
71
+ /** Replace all term holes with the spliced terms */
72
+ private def spliceTerms (tree : Tree , splices : PickledArgs )(using Context ): Tree = {
73
+ val evaluateHoles = new TreeMap {
74
+ override def transform (tree : tpd.Tree )(implicit ctx : Context ): tpd.Tree = tree match {
75
+ case Hole (isTerm, idx, args) =>
76
+ val reifiedArgs = args.map { arg =>
77
+ if (arg.isTerm) (using qctx : scala.quoted.QuoteContext ) => new TastyTreeExpr (arg, QuoteContext .scopeId)
78
+ else new TreeType (arg, QuoteContext .scopeId)
79
+ }
80
+ if isTerm then
81
+ val splice1 = splices(idx).asInstanceOf [Seq [Any ] => scala.quoted.QuoteContext ?=> quoted.Expr [? ]]
82
+ val quotedExpr = splice1(reifiedArgs)(using dotty.tools.dotc.quoted.QuoteContext ())
83
+ val filled = PickledQuotes .quotedExprToTree(quotedExpr)
84
+
85
+ // We need to make sure a hole is created with the source file of the surrounding context, even if
86
+ // it filled with contents a different source file.
87
+ if filled.source == ctx.source then filled
88
+ else filled.cloneIn(ctx.source).withSpan(tree.span)
89
+ else
90
+ // Replaces type holes generated by ReifyQuotes (non-spliced types).
91
+ // These are types defined in a quote and used at the same level in a nested quote.
92
+ val quotedType = splices(idx).asInstanceOf [Seq [Any ] => quoted.Type [? ]](reifiedArgs)
93
+ PickledQuotes .quotedTypeToTree(quotedType)
94
+ case tree : Select =>
95
+ // Retain selected members
96
+ val qual = transform(tree.qualifier)
97
+ qual.select(tree.symbol).withSpan(tree.span)
98
+
99
+ case tree =>
100
+ if tree.isDef then
101
+ tree.symbol.annotations = tree.symbol.annotations.map {
102
+ annot => annot.derivedAnnotation(transform(annot.tree))
103
+ }
104
+ end if
105
+
106
+ val tree1 = super .transform(tree)
107
+ tree1.withType(mapAnnots(tree1.tpe))
108
+ }
109
+
110
+ // Evaluate holes in type annotations
111
+ private val mapAnnots = new TypeMap {
112
+ override def apply (tp : Type ): Type = {
113
+ tp match
114
+ case tp @ AnnotatedType (underlying, annot) =>
115
+ val underlying1 = this (underlying)
116
+ derivedAnnotatedType(tp, underlying1, annot.derivedAnnotation(transform(annot.tree)))
117
+ case _ => mapOver(tp)
118
+ }
119
+ }
93
120
}
94
- tpt.withType(dealiasTypeTags(tpt.tpe))
121
+ val tree1 = evaluateHoles.transform(tree)
122
+ quotePickling.println(i " **** evaluated quote \n $tree1" )
123
+ tree1
124
+ }
125
+
126
+ /** Replace all type holes generated with the spliced types */
127
+ private def spliceTypes (tree : Tree , splices : PickledArgs )(using Context ): Tree = {
128
+ tree match
129
+ case Block (stat :: rest, expr1) if stat.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot ) =>
130
+ val typeSpliceMap = (stat :: rest).iterator.map {
131
+ case tdef : TypeDef =>
132
+ assert(tdef.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot ))
133
+ val tree = tdef.rhs match
134
+ case TypeBoundsTree (_, Hole (_, idx, args), _) =>
135
+ val quotedType = splices(idx).asInstanceOf [Seq [Any ] => quoted.Type [? ]](args)
136
+ PickledQuotes .quotedTypeToTree(quotedType)
137
+ case TypeBoundsTree (_, tpt, _) =>
138
+ tpt
139
+ (tdef.symbol, tree.tpe)
140
+ }.toMap
141
+ class ReplaceSplicedTyped extends TypeMap () {
142
+ override def apply (tp : Type ): Type = {
143
+ val tp1 = tp match {
144
+ case tp : TypeRef =>
145
+ typeSpliceMap.get(tp.symbol) match
146
+ case Some (t) if tp.typeSymbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot ) => t
147
+ case None => tp
148
+ case _ => tp
149
+ }
150
+ mapOver(tp1)
151
+ }
152
+ }
153
+ val expansion2 = new TreeTypeMap (new ReplaceSplicedTyped ).transform(expr1)
154
+ quotePickling.println(i " **** typed quote \n ${expansion2.show}" )
155
+ expansion2
156
+ case _ =>
157
+ tree
95
158
}
96
159
97
160
// TASTY picklingtests/pos/quoteTest.scala
98
161
99
162
/** Pickle tree into it's TASTY bytes s*/
100
163
private def pickle (tree : Tree )(implicit ctx : Context ): Array [Byte ] = {
101
- quotePickling.println(i " **** pickling quote of \n ${ tree.show} " )
164
+ quotePickling.println(i " **** pickling quote of \n $tree" )
102
165
val pickler = new TastyPickler (defn.RootClass )
103
166
val treePkl = pickler.treePkl
104
167
treePkl.pickle(tree :: Nil )
@@ -118,11 +181,17 @@ object PickledQuotes {
118
181
quotePickling.println(s " **** unpickling quote from TASTY \n ${new TastyPrinter (bytes).printContents()}" )
119
182
120
183
val mode = if (isType) UnpickleMode .TypeTree else UnpickleMode .Term
121
- val unpickler = new QuoteUnpickler (bytes, splices , mode)
184
+ val unpickler = new DottyUnpickler (bytes, mode)
122
185
unpickler.enter(Set .empty)
123
186
124
187
val tree = unpickler.tree
125
- quotePickling.println(i " **** unpickle quote ${tree.show}" )
188
+
189
+ // Make sure trees and positions are fully loaded
190
+ new TreeTraverser {
191
+ def traverse (tree : Tree )(implicit ctx : Context ): Unit = traverseChildren(tree)
192
+ }.traverse(tree)
193
+
194
+ quotePickling.println(i " **** unpickled quote \n $tree" )
126
195
tree
127
196
}
128
197
0 commit comments