Skip to content

Commit 71f95dd

Browse files
Merge pull request #9948 from dotty-staging/refector-pickled-quotes-logic
Refactor pickled quotes logic
2 parents a3c3396 + b4c10e5 commit 71f95dd

File tree

8 files changed

+83
-70
lines changed

8 files changed

+83
-70
lines changed

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

+3-2
Original file line numberDiff line numberDiff line change
@@ -837,8 +837,9 @@ class Definitions {
837837

838838
@tu lazy val TastyReflectionClass: ClassSymbol = requiredClass("scala.tasty.Reflection")
839839

840-
@tu lazy val Unpickler_unpickleExpr: Symbol = requiredMethod("scala.internal.quoted.Unpickler.unpickleExpr")
841-
@tu lazy val Unpickler_unpickleType: Symbol = requiredMethod("scala.internal.quoted.Unpickler.unpickleType")
840+
@tu lazy val PickledQuote_make: Symbol = requiredMethod("scala.internal.quoted.PickledQuote.make")
841+
@tu lazy val PickledQuote_unpickleExpr: Symbol = requiredMethod("scala.internal.quoted.PickledQuote.unpickleExpr")
842+
@tu lazy val PickledQuote_unpickleType: Symbol = requiredMethod("scala.internal.quoted.PickledQuote.unpickleType")
842843

843844
@tu lazy val EqlClass: ClassSymbol = requiredClass("scala.Eql")
844845
def Eql_eqlAny(using Context): TermSymbol = EqlClass.companionModule.requiredMethod(nme.eqlAny)

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

+17-21
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,22 @@ import dotty.tools.dotc.core.tasty.DottyUnpickler
1717
import dotty.tools.dotc.core.tasty.TreeUnpickler.UnpickleMode
1818
import dotty.tools.dotc.report
1919

20-
import dotty.tools.tasty.TastyString
21-
2220
import scala.reflect.ClassTag
2321

24-
import scala.internal.quoted.Unpickler._
22+
import scala.internal.quoted.PickledQuote
2523
import scala.quoted.QuoteContext
2624
import scala.collection.mutable
2725

2826
object PickledQuotes {
2927
import tpd._
3028

3129
/** Pickle the tree of the quote into strings */
32-
def pickleQuote(tree: Tree)(using Context): PickledQuote =
30+
def pickleQuote(tree: Tree)(using Context): List[String] =
3331
if (ctx.reporter.hasErrors) Nil
3432
else {
3533
assert(!tree.isInstanceOf[Hole]) // Should not be pickled as it represents `'{$x}` which should be optimized to `x`
3634
val pickled = pickle(tree)
37-
TastyString.pickle(pickled)
35+
scala.internal.quoted.TastyString.pickle(pickled)
3836
}
3937

4038
/** Transform the expression into its fully spliced Tree */
@@ -52,27 +50,25 @@ object PickledQuotes {
5250
}
5351

5452
/** Unpickle the tree contained in the TastyExpr */
55-
def unpickleExpr(tasty: PickledQuote, splices: PickledArgs)(using Context): Tree = {
56-
val tastyBytes = TastyString.unpickle(tasty)
53+
def unpickleTerm(pickledQuote: PickledQuote)(using Context): Tree = {
5754
val unpickled = withMode(Mode.ReadPositions)(
58-
unpickle(tastyBytes, splices, isType = false))
55+
unpickle(pickledQuote, isType = false))
5956
val Inlined(call, Nil, expnasion) = unpickled
6057
val inlineCtx = inlineContext(call)
61-
val expansion1 = spliceTypes(expnasion, splices)(using inlineCtx)
62-
val expansion2 = spliceTerms(expansion1, splices)(using inlineCtx)
58+
val expansion1 = spliceTypes(expnasion, pickledQuote)(using inlineCtx)
59+
val expansion2 = spliceTerms(expansion1, pickledQuote)(using inlineCtx)
6360
cpy.Inlined(unpickled)(call, Nil, expansion2)
6461
}
6562

6663
/** Unpickle the tree contained in the TastyType */
67-
def unpickleType(tasty: PickledQuote, args: PickledArgs)(using Context): Tree = {
68-
val tastyBytes = TastyString.unpickle(tasty)
64+
def unpickleTypeTree(pickledQuote: PickledQuote)(using Context): Tree = {
6965
val unpickled = withMode(Mode.ReadPositions)(
70-
unpickle(tastyBytes, args, isType = true))
71-
spliceTypes(unpickled, args)
66+
unpickle(pickledQuote, isType = true))
67+
spliceTypes(unpickled, pickledQuote)
7268
}
7369

7470
/** Replace all term holes with the spliced terms */
75-
private def spliceTerms(tree: Tree, splices: PickledArgs)(using Context): Tree = {
71+
private def spliceTerms(tree: Tree, pickledQuote: PickledQuote)(using Context): Tree = {
7672
val evaluateHoles = new TreeMap {
7773
override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match {
7874
case Hole(isTerm, idx, args) =>
@@ -81,8 +77,7 @@ object PickledQuotes {
8177
else new scala.internal.quoted.Type(arg, QuoteContextImpl.scopeId)
8278
}
8379
if isTerm then
84-
val splice1 = splices(idx).asInstanceOf[Seq[Any] => QuoteContext ?=> quoted.Expr[?]]
85-
val quotedExpr = splice1(reifiedArgs)(using dotty.tools.dotc.quoted.QuoteContextImpl())
80+
val quotedExpr = pickledQuote.exprSplice(idx)(reifiedArgs)(dotty.tools.dotc.quoted.QuoteContextImpl())
8681
val filled = PickledQuotes.quotedExprToTree(quotedExpr)
8782

8883
// We need to make sure a hole is created with the source file of the surrounding context, even if
@@ -92,7 +87,7 @@ object PickledQuotes {
9287
else
9388
// Replaces type holes generated by ReifyQuotes (non-spliced types).
9489
// These are types defined in a quote and used at the same level in a nested quote.
95-
val quotedType = splices(idx).asInstanceOf[Seq[Any] => quoted.Type[?]](reifiedArgs)
90+
val quotedType = pickledQuote.typeSplice(idx)(reifiedArgs)
9691
PickledQuotes.quotedTypeToTree(quotedType)
9792
case tree: Select =>
9893
// Retain selected members
@@ -127,15 +122,15 @@ object PickledQuotes {
127122
}
128123

129124
/** Replace all type holes generated with the spliced types */
130-
private def spliceTypes(tree: Tree, splices: PickledArgs)(using Context): Tree = {
125+
private def spliceTypes(tree: Tree, pickledQuote: PickledQuote)(using Context): Tree = {
131126
tree match
132127
case Block(stat :: rest, expr1) if stat.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot) =>
133128
val typeSpliceMap = (stat :: rest).iterator.map {
134129
case tdef: TypeDef =>
135130
assert(tdef.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot))
136131
val tree = tdef.rhs match
137132
case TypeBoundsTree(_, Hole(_, idx, args), _) =>
138-
val quotedType = splices(idx).asInstanceOf[Seq[Any] => quoted.Type[?]](args)
133+
val quotedType = pickledQuote.typeSplice(idx)(args)
139134
PickledQuotes.quotedTypeToTree(quotedType)
140135
case TypeBoundsTree(_, tpt, _) =>
141136
tpt
@@ -181,7 +176,8 @@ object PickledQuotes {
181176
}
182177

183178
/** Unpickle TASTY bytes into it's tree */
184-
private def unpickle(bytes: Array[Byte], splices: Seq[Any], isType: Boolean)(using Context): Tree = {
179+
private def unpickle(pickledQuote: PickledQuote, isType: Boolean)(using Context): Tree = {
180+
val bytes = pickledQuote.bytes()
185181
quotePickling.println(s"**** unpickling quote from TASTY\n${new TastyPrinter(bytes).printContents()}")
186182

187183
val mode = if (isType) UnpickleMode.TypeTree else UnpickleMode.Term

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

+5-6
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import dotty.tools.dotc.core.Decorators._
1515
import scala.quoted.QuoteContext
1616
import scala.quoted.show.SyntaxHighlight
1717

18-
import scala.internal.quoted.Unpickler
18+
import scala.internal.quoted.PickledQuote
1919
import scala.tasty.reflect._
2020

2121
object QuoteContextImpl {
@@ -2613,12 +2613,11 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext:
26132613
private def withDefaultPos[T <: Tree](fn: Context ?=> T): T =
26142614
fn(using ctx.withSource(rootPosition.source)).withSpan(rootPosition.span)
26152615

2616+
def unpickleTerm(pickledQuote: PickledQuote): Term =
2617+
PickledQuotes.unpickleTerm(pickledQuote)
26162618

2617-
def unpickleExpr(repr: Unpickler.PickledQuote, args: Unpickler.PickledArgs): Term =
2618-
PickledQuotes.unpickleExpr(repr, args)
2619-
2620-
def unpickleType(repr: Unpickler.PickledQuote, args: Unpickler.PickledArgs): TypeTree =
2621-
PickledQuotes.unpickleType(repr, args)
2619+
def unpickleTypeTree(pickledQuote: PickledQuote): TypeTree =
2620+
PickledQuotes.unpickleTypeTree(pickledQuote)
26222621

26232622
def termMatch(scrutinee: Term, pattern: Term): Option[Tuple] =
26242623
treeMatch(scrutinee, pattern)

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

+4-2
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,12 @@ class ReifyQuotes extends MacroTransform {
163163
}
164164

165165
def pickleAsTasty() = {
166-
val meth = if isType then defn.Unpickler_unpickleType else defn.Unpickler_unpickleExpr
166+
val unpickleMeth = if isType then defn.PickledQuote_unpickleType else defn.PickledQuote_unpickleExpr
167167
val pickledQuoteStrings = liftList(PickledQuotes.pickleQuote(body).map(x => Literal(Constant(x))), defn.StringType)
168+
// TODO: generate an instance of PickledSplices directly instead of passing through a List
168169
val splicesList = liftList(splices, defn.FunctionType(1).appliedTo(defn.SeqType.appliedTo(defn.AnyType), defn.AnyType))
169-
ref(meth).appliedToType(originalTp).appliedTo(pickledQuoteStrings, splicesList)
170+
val pickledQuote = ref(defn.PickledQuote_make).appliedTo(pickledQuoteStrings, splicesList)
171+
ref(unpickleMeth).appliedToType(originalTp).appliedTo(pickledQuote)
170172
}
171173

172174
def taggedType(sym: Symbol) = ref(defn.InternalQuotedTypeModule).select(sym.name.toTermName)

library/src-bootstrapped/scala/internal/quoted/Unpickler.scala

-28
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package scala.internal.quoted
2+
3+
import scala.quoted._
4+
import scala.internal.tasty.CompilerInterface.quoteContextWithCompilerInterface
5+
6+
/** Pickled representation of a quoted expression or type */
7+
trait PickledQuote:
8+
9+
/** Bytes of the TASTy containing the quoted expression or type */
10+
def bytes(): Array[Byte]
11+
12+
/** Expression that will fill the hole `Hole(<idx> | <args>*)` */
13+
def exprSplice(idx: Int)(args: Seq[Any /* Expr[Any] | Type[?] */])(qctx: QuoteContext): Expr[Any]
14+
15+
/** Type that will fill the hole `Hole(<idx> | <args>*)` */
16+
def typeSplice(idx: Int)(args: Seq[Any /* Expr[Any] | Type[?] */]): Type[?]
17+
18+
object PickledQuote:
19+
20+
def unpickleExpr[T](pickledQuote: PickledQuote): QuoteContext ?=> Expr[T] =
21+
val qctx = quoteContextWithCompilerInterface(summon[QuoteContext])
22+
val tree = qctx.reflect.unpickleTerm(pickledQuote)
23+
new scala.internal.quoted.Expr(tree, qctx.hashCode).asInstanceOf[Expr[T]]
24+
25+
def unpickleType[T](pickledQuote: PickledQuote): QuoteContext ?=> Type[T] =
26+
val qctx = quoteContextWithCompilerInterface(summon[QuoteContext])
27+
val tree = qctx.reflect.unpickleTypeTree(pickledQuote)
28+
new scala.internal.quoted.Type(tree, qctx.hashCode).asInstanceOf[Type[T]]
29+
30+
/** Create an instance of PickledExpr from encoded tasty and sequence of labmdas to fill holes
31+
*
32+
* @param pickled: Bytes of tasty encoded using TastyString.pickle
33+
* @param seq: Sequence containing all the functions needed to fill the holes of the quote
34+
*/
35+
def make(pickled: List[String], seq: Seq[Seq[Any /* Expr[Any] | Type[?] */] => Any]): PickledQuote =
36+
// TODO: generate a more efficient representation
37+
// - avoid creation of lambdas
38+
// - use swich on id
39+
new PickledQuote:
40+
def bytes(): Array[Byte] =
41+
TastyString.unpickle(pickled)
42+
def exprSplice(idx: Int)(args: Seq[Any])(qctx: QuoteContext): Expr[Any] =
43+
seq(idx)(args).asInstanceOf[QuoteContext => Expr[Any]](qctx)
44+
def typeSplice(idx: Int)(args: Seq[Any]): Type[?] =
45+
seq(idx)(args).asInstanceOf[Type[?]]

compiler/src/dotty/tools/dotc/core/tasty/TastyString.scala renamed to library/src/scala/internal/quoted/TastyString.scala

+4-6
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,23 @@
1-
package dotty.tools.tasty
1+
package scala.internal.quoted
22

33
import java.io._
44
import java.util.Base64
55
import java.nio.charset.StandardCharsets.UTF_8
66

7-
import scala.internal.quoted.Unpickler.PickledQuote
8-
97
/** Utils for String representation of TASTY */
108
object TastyString {
119

1210
// Max size of a string literal in the bytecode
13-
private final val maxStringSize = 65535
11+
private inline val maxStringSize = 65535
1412

1513
/** Encode TASTY bytes into a List of String */
16-
def pickle(bytes: Array[Byte]): PickledQuote = {
14+
def pickle(bytes: Array[Byte]): List[String] = {
1715
val str = new String(Base64.getEncoder().encode(bytes), UTF_8)
1816
str.toSeq.sliding(maxStringSize, maxStringSize).map(_.unwrap).toList
1917
}
2018

2119
/** Decode the List of Strings into TASTY bytes */
22-
def unpickle(strings: PickledQuote): Array[Byte] = {
20+
def unpickle(strings: List[String]): Array[Byte] = {
2321
val string = new StringBuilder
2422
strings.foreach(string.append)
2523
Base64.getDecoder().decode(string.result().getBytes(UTF_8))

library/src/scala/internal/tasty/CompilerInterface.scala

+5-5
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package scala.internal.tasty
22

33
import scala.quoted.QuoteContext
44
import scala.tasty.reflect._
5-
import scala.internal.quoted.Unpickler
5+
import scala.internal.quoted.PickledQuote
66

77
/** Part of the reflection interface that needs to be implemented by the compiler */
88
trait CompilerInterface { self: scala.tasty.Reflection =>
@@ -12,14 +12,14 @@ trait CompilerInterface { self: scala.tasty.Reflection =>
1212
//////////////////////
1313

1414
/** Unpickle `repr` which represents a pickled `Expr` tree,
15-
* replacing splice nodes with `args`
15+
* replacing splice nodes with `holes`
1616
*/
17-
def unpickleExpr(repr: Unpickler.PickledQuote, args: Unpickler.PickledArgs): Term
17+
def unpickleTerm(pickledQuote: PickledQuote): Term
1818

1919
/** Unpickle `repr` which represents a pickled `Type` tree,
20-
* replacing splice nodes with `args`
20+
* replacing splice nodes with `holes`
2121
*/
22-
def unpickleType(repr: Unpickler.PickledQuote, args: Unpickler.PickledArgs): TypeTree
22+
def unpickleTypeTree(pickledQuote: PickledQuote): TypeTree
2323

2424
/** Pattern matches the scrutinee against the pattern and returns a tuple
2525
* with the matched holes if successful.

0 commit comments

Comments
 (0)