@@ -13,6 +13,7 @@ import dotty.tools.dotc.core.Symbols._
1313import dotty .tools .dotc .core .Types ._
1414import dotty .tools .dotc .core .tasty .TreePickler .Hole
1515import dotty .tools .dotc .core .tasty .{ PositionPickler , TastyPickler , TastyPrinter }
16+ import dotty .tools .dotc .core .tasty .DottyUnpickler
1617import dotty .tools .dotc .core .tasty .TreeUnpickler .UnpickleMode
1718import dotty .tools .dotc .quoted .QuoteContext
1819import dotty .tools .dotc .tastyreflect .{ReflectionImpl , TastyTreeExpr , TreeType }
@@ -49,56 +50,118 @@ object PickledQuotes {
4950 healOwner(tpe1.typeTree)
5051 }
5152
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-
6353 /** 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 = {
6555 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)
7762 }
7863
7964 /** Unpickle the tree contained in the TastyType */
8065 def unpickleType (tasty : PickledQuote , args : PickledArgs )(implicit ctx : Context ): Tree = {
8166 val tastyBytes = TastyString .unpickle(tasty)
8267 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+ }
93120 }
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
95158 }
96159
97160 // TASTY picklingtests/pos/quoteTest.scala
98161
99162 /** Pickle tree into it's TASTY bytes s*/
100163 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" )
102165 val pickler = new TastyPickler (defn.RootClass )
103166 val treePkl = pickler.treePkl
104167 treePkl.pickle(tree :: Nil )
@@ -118,11 +181,17 @@ object PickledQuotes {
118181 quotePickling.println(s " **** unpickling quote from TASTY \n ${new TastyPrinter (bytes).printContents()}" )
119182
120183 val mode = if (isType) UnpickleMode .TypeTree else UnpickleMode .Term
121- val unpickler = new QuoteUnpickler (bytes, splices , mode)
184+ val unpickler = new DottyUnpickler (bytes, mode)
122185 unpickler.enter(Set .empty)
123186
124187 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" )
126195 tree
127196 }
128197
0 commit comments