Skip to content

Remove PickledQuote and TastyString from library #10262

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -796,8 +796,10 @@ class Definitions {

@tu lazy val QuoteContextClass: ClassSymbol = requiredClass("scala.quoted.QuoteContext")
@tu lazy val QuoteContextInternalClass: ClassSymbol = requiredClass("scala.internal.quoted.QuoteContextInternal")
@tu lazy val QuoteContextInternalClass_ExprMatch: Symbol = QuoteContextInternalClass.requiredMethod("ExprMatch")
@tu lazy val QuoteContextInternalClass_TypeMatch: Symbol = QuoteContextInternalClass.requiredMethod("TypeMatch")
@tu lazy val QuoteContextInternal_unpickleExpr: Symbol = QuoteContextInternalClass.requiredMethod("unpickleExpr")
@tu lazy val QuoteContextInternal_unpickleType: Symbol = QuoteContextInternalClass.requiredMethod("unpickleType")
@tu lazy val QuoteContextInternal_ExprMatch: Symbol = QuoteContextInternalClass.requiredMethod("ExprMatch")
@tu lazy val QuoteContextInternal_TypeMatch: Symbol = QuoteContextInternalClass.requiredMethod("TypeMatch")

@tu lazy val LiftableModule: Symbol = requiredModule("scala.quoted.Liftable")
@tu lazy val LiftableModule_BooleanLiftable: Symbol = LiftableModule.requiredMethod("BooleanLiftable")
Expand Down Expand Up @@ -831,8 +833,6 @@ class Definitions {

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

@tu lazy val PickledQuote_make: Symbol = requiredMethod("scala.internal.quoted.PickledQuote.make")

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

Expand Down
36 changes: 18 additions & 18 deletions compiler/src/dotty/tools/dotc/quoted/PickledQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import dotty.tools.dotc.report

import scala.reflect.ClassTag

import scala.internal.quoted.PickledQuote
import scala.quoted.QuoteContext
import scala.collection.mutable

Expand All @@ -34,7 +33,7 @@ object PickledQuotes {
else {
assert(!tree.isInstanceOf[Hole]) // Should not be pickled as it represents `'{$x}` which should be optimized to `x`
val pickled = pickle(tree)
scala.internal.quoted.TastyString.pickle(pickled)
TastyString.pickle(pickled)
}

/** Transform the expression into its fully spliced Tree */
Expand All @@ -52,25 +51,23 @@ object PickledQuotes {
}

/** Unpickle the tree contained in the TastyExpr */
def unpickleTerm(pickledQuote: PickledQuote)(using Context): Tree = {
val unpickled = withMode(Mode.ReadPositions)(
unpickle(pickledQuote, isType = false))
def unpickleTerm(pickled: String | List[String], typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Any], scala.quoted.QuoteContext) => scala.quoted.Expr[?])(using Context): Tree = {
val unpickled = withMode(Mode.ReadPositions)(unpickle(pickled, isType = false))
val Inlined(call, Nil, expnasion) = unpickled
val inlineCtx = inlineContext(call)
val expansion1 = spliceTypes(expnasion, pickledQuote)(using inlineCtx)
val expansion2 = spliceTerms(expansion1, pickledQuote)(using inlineCtx)
val expansion1 = spliceTypes(expnasion, typeHole, termHole)(using inlineCtx)
val expansion2 = spliceTerms(expansion1, typeHole, termHole)(using inlineCtx)
cpy.Inlined(unpickled)(call, Nil, expansion2)
}

/** Unpickle the tree contained in the TastyType */
def unpickleTypeTree(pickledQuote: PickledQuote)(using Context): Tree = {
val unpickled = withMode(Mode.ReadPositions)(
unpickle(pickledQuote, isType = true))
spliceTypes(unpickled, pickledQuote)
def unpickleTypeTree(pickled: String | List[String], typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Any], scala.quoted.QuoteContext) => scala.quoted.Expr[?])(using Context): Tree = {
val unpickled = withMode(Mode.ReadPositions)(unpickle(pickled, isType = true))
spliceTypes(unpickled, typeHole, termHole)
}

/** Replace all term holes with the spliced terms */
private def spliceTerms(tree: Tree, pickledQuote: PickledQuote)(using Context): Tree = {
private def spliceTerms(tree: Tree, typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Any], scala.quoted.QuoteContext) => scala.quoted.Expr[?])(using Context): Tree = {
val evaluateHoles = new TreeMap {
override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match {
case Hole(isTerm, idx, args) =>
Expand All @@ -79,7 +76,7 @@ object PickledQuotes {
else new scala.quoted.internal.Type(arg, QuoteContextImpl.scopeId)
}
if isTerm then
val quotedExpr = pickledQuote.exprSplice(idx)(reifiedArgs)(dotty.tools.dotc.quoted.QuoteContextImpl())
val quotedExpr = termHole(idx, reifiedArgs, dotty.tools.dotc.quoted.QuoteContextImpl())
val filled = PickledQuotes.quotedExprToTree(quotedExpr)

// We need to make sure a hole is created with the source file of the surrounding context, even if
Expand All @@ -89,7 +86,7 @@ object PickledQuotes {
else
// Replaces type holes generated by PickleQuotes (non-spliced types).
// These are types defined in a quote and used at the same level in a nested quote.
val quotedType = pickledQuote.typeSplice(idx)(reifiedArgs)
val quotedType = typeHole(idx, reifiedArgs)
PickledQuotes.quotedTypeToTree(quotedType)
case tree: Select =>
// Retain selected members
Expand Down Expand Up @@ -124,15 +121,15 @@ object PickledQuotes {
}

/** Replace all type holes generated with the spliced types */
private def spliceTypes(tree: Tree, pickledQuote: PickledQuote)(using Context): Tree = {
private def spliceTypes(tree: Tree, typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Int], scala.quoted.QuoteContext) => Any)(using Context): Tree = {
tree match
case Block(stat :: rest, expr1) if stat.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot) =>
val typeSpliceMap = (stat :: rest).iterator.map {
case tdef: TypeDef =>
assert(tdef.symbol.hasAnnotation(defn.InternalQuoted_QuoteTypeTagAnnot))
val tree = tdef.rhs match
case TypeBoundsTree(_, Hole(_, idx, args), _) =>
val quotedType = pickledQuote.typeSplice(idx)(args)
val quotedType = typeHole(idx, args)
PickledQuotes.quotedTypeToTree(quotedType)
case TypeBoundsTree(_, tpt, _) =>
tpt
Expand Down Expand Up @@ -178,8 +175,11 @@ object PickledQuotes {
}

/** Unpickle TASTY bytes into it's tree */
private def unpickle(pickledQuote: PickledQuote, isType: Boolean)(using Context): Tree = {
val bytes = pickledQuote.bytes()
private def unpickle(pickled: String | List[String], isType: Boolean)(using Context): Tree = {
val bytes = pickled match
case pickled: String => TastyString.unpickle(pickled)
case pickled: List[String] => TastyString.unpickle(pickled)

quotePickling.println(s"**** unpickling quote from TASTY\n${TastyPrinter.show(bytes)}")

val mode = if (isType) UnpickleMode.TypeTree else UnpickleMode.Term
Expand Down
9 changes: 4 additions & 5 deletions compiler/src/dotty/tools/dotc/quoted/QuoteContextImpl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import dotty.tools.dotc.core.Decorators._
import scala.quoted.QuoteContext
import dotty.tools.dotc.quoted.printers.{Extractors, SourceCode, SyntaxHighlight}

import scala.internal.quoted.PickledQuote
import scala.tasty.reflect._

object QuoteContextImpl {
Expand Down Expand Up @@ -2622,12 +2621,12 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, scala.intern

end reflect

def unpickleExpr[T](pickledQuote: PickledQuote): scala.quoted.Expr[T] =
val tree = PickledQuotes.unpickleTerm(pickledQuote)(using reflect.rootContext)
def unpickleExpr[T](pickled: String | List[String], typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Any], scala.quoted.QuoteContext) => scala.quoted.Expr[?]): scala.quoted.Expr[T] =
val tree = PickledQuotes.unpickleTerm(pickled, typeHole, termHole)(using reflect.rootContext)
new scala.quoted.internal.Expr(tree, hash).asInstanceOf[scala.quoted.Expr[T]]

def unpickleType[T <: AnyKind](pickledQuote: PickledQuote): scala.quoted.Type[T] =
val tree = PickledQuotes.unpickleTypeTree(pickledQuote)(using reflect.rootContext)
def unpickleType[T <: AnyKind](pickled: String | List[String], typeHole: (Int, Seq[Any]) => scala.quoted.Type[?], termHole: (Int, Seq[Any], scala.quoted.QuoteContext) => scala.quoted.Expr[?]): scala.quoted.Type[T] =
val tree = PickledQuotes.unpickleTypeTree(pickled, typeHole, termHole)(using reflect.rootContext)
new scala.quoted.internal.Type(tree, hash).asInstanceOf[scala.quoted.Type[T]]

object ExprMatch extends ExprMatchModule:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package scala.internal.quoted
package dotty.tools.dotc.quoted

import java.io._
import java.util.Base64
Expand All @@ -22,4 +22,9 @@ object TastyString {
strings.foreach(string.append)
Base64.getDecoder().decode(string.result().getBytes(UTF_8))
}

/** Decode the Strings into TASTY bytes */
def unpickle(string: String): Array[Byte] =
Base64.getDecoder().decode(string.getBytes(UTF_8))

}
79 changes: 63 additions & 16 deletions compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import dotty.tools.dotc.typer.Inliner
import scala.annotation.constructorOnly


/** Translates quoted terms and types to `unpickle` method calls.
/** Translates quoted terms and types to `unpickleExpr` or `unpickleType` method calls.
*
* Transforms top level quote
* ```
Expand All @@ -42,23 +42,27 @@ import scala.annotation.constructorOnly
* ```
* to
* ```
* unpickle(
* [[ // PICKLED TASTY
* unpickleExpr(
* pickled = [[ // PICKLED TASTY
* ...
* val x1 = ???
* val x2 = ???
* ...
* Hole(0 | x1, x2)
* Hole(<i> | x1, x2)
* ...
* ]],
* List(
* (args: Seq[Any]) => {
* typeHole = (idx: Int, args: List[Any]) => idx match {
* case 0 => ...
* },
* termHole = (idx: Int, args: List[Any], qctx: QuoteContext) => idx match {
* case 0 => ...
* ...
* case <i> =>
* val x1$1 = args(0).asInstanceOf[Expr[T]]
* val x2$1 = args(1).asInstanceOf[Expr[T]] // can be asInstanceOf[Type[T]]
* ...
* ...
* { ... '{ ... ${x1$1} ... ${x2$1} ...} ... }
* }
* )
* },
* )
* ```
* and then performs the same transformation on `'{ ... ${x1$1} ... ${x2$1} ...}`.
Expand Down Expand Up @@ -194,23 +198,66 @@ class PickleQuotes extends MacroTransform {
* Generate the code
* ```scala
* qctx => qctx.asInstanceOf[QuoteContextInternal].<unpickleExpr|unpickleType>[<type>](
* <pickledQuote>
* <pickledQuote>,
* <typeHole>,
* <termHole>,
* )
* ```
* this closure is always applied directly to the actual context and the BetaReduce phase removes it.
*/
def pickleAsTasty() = {
val pickledQuoteStrings = liftList(PickledQuotes.pickleQuote(body).map(x => Literal(Constant(x))), defn.StringType)
// TODO: generate an instance of PickledSplices directly instead of passing through a List
val splicesList = liftList(splices, defn.FunctionType(1).appliedTo(defn.SeqType.appliedTo(defn.AnyType), defn.AnyType))
val pickledQuote = ref(defn.PickledQuote_make).appliedTo(pickledQuoteStrings, splicesList)
val pickleQuote = PickledQuotes.pickleQuote(body)
val pickledQuoteStrings = pickleQuote match
case x :: Nil => Literal(Constant(x))
case xs => liftList(xs.map(x => Literal(Constant(x))), defn.StringType)

// TODO split holes earlier into types and terms. This all holes in each category can have consecutive indices
val (typeSplices, termSplices) = splices.zipWithIndex.partition { case (splice, _) =>
splice.tpe match
case defn.FunctionOf(_, res, _, _) => res.typeSymbol == defn.QuotedTypeClass
}

// This and all closures in typeSplices are removed by the BetaReduce phase
val typeHoles =
if typeSplices.isEmpty then Literal(Constant(null)) // keep pickled quote without splices as small as possible
else
Lambda(
MethodType(
List(defn.IntType, defn.SeqType.appliedTo(defn.AnyType)),
defn.QuotedTypeClass.typeRef.appliedTo(WildcardType)),
args => {
val cases = typeSplices.map { case (splice, idx) =>
CaseDef(Literal(Constant(idx)), EmptyTree, splice.select(nme.apply).appliedTo(args(1)))
}
Match(args(0).annotated(New(ref(defn.UncheckedAnnot.typeRef))), cases)
}
)

// This and all closures in termSplices are removed by the BetaReduce phase
val termHoles =
if termSplices.isEmpty then Literal(Constant(null)) // keep pickled quote without splices as small as possible
else
Lambda(
MethodType(
List(defn.IntType, defn.SeqType.appliedTo(defn.AnyType), defn.QuoteContextClass.typeRef),
defn.QuotedExprClass.typeRef.appliedTo(defn.AnyType)),
args => {
val cases = termSplices.map { case (splice, idx) =>
val defn.FunctionOf(_, defn.FunctionOf(qctxType :: _, _, _, _), _, _) = splice.tpe
val rhs = splice.select(nme.apply).appliedTo(args(1)).select(nme.apply).appliedTo(args(2).asInstance(qctxType))
CaseDef(Literal(Constant(idx)), EmptyTree, rhs)
}
Match(args(0).annotated(New(ref(defn.UncheckedAnnot.typeRef))), cases)
}
)

val quoteClass = if isType then defn.QuotedTypeClass else defn.QuotedExprClass
val quotedType = quoteClass.typeRef.appliedTo(originalTp)
val lambdaTpe = MethodType(defn.QuoteContextClass.typeRef :: Nil, quotedType)
def callUnpickle(ts: List[Tree]) = {
val qctx = ts.head.asInstance(defn.QuoteContextInternalClass.typeRef)
val unpickleMethName = if isType then "unpickleType" else "unpickleExpr"
qctx.select(unpickleMethName.toTermName).appliedToType(originalTp).appliedTo(pickledQuote)
val unpickleMeth = if isType then defn.QuoteContextInternal_unpickleType else defn.QuoteContextInternal_unpickleExpr
qctx.select(unpickleMeth).appliedToType(originalTp).appliedTo(pickledQuoteStrings, typeHoles, termHoles)
}
Lambda(lambdaTpe, callUnpickle).withSpan(body.span)
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/QuotesAndSplices.scala
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@ trait QuotesAndSplices {
if (tree.quoted.isTerm) ref(defn.InternalQuoted_exprQuote.termRef).appliedToType(defn.AnyType).appliedTo(shape).select(nme.apply).appliedTo(qctx)
else ref(defn.QuotedTypeModule_apply.termRef).appliedToTypeTree(shape).select(nme.apply).appliedTo(qctx)

val matchModule = if tree.quoted.isTerm then defn.QuoteContextInternalClass_ExprMatch else defn.QuoteContextInternalClass_TypeMatch
val matchModule = if tree.quoted.isTerm then defn.QuoteContextInternal_ExprMatch else defn.QuoteContextInternal_TypeMatch
val unapplyFun = qctx.asInstance(defn.QuoteContextInternalClass.typeRef).select(matchModule).select(nme.unapply)

UnApply(
Expand Down
34 changes: 0 additions & 34 deletions library/src/scala/internal/quoted/PickledQuote.scala

This file was deleted.

5 changes: 2 additions & 3 deletions library/src/scala/internal/quoted/QuoteContextInternal.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@ package scala.internal.quoted

import scala.quoted.{QuoteContext, Expr, Type}
import scala.tasty.reflect._
import scala.internal.quoted.PickledQuote

/** Part of the QuoteContext interface that needs to be implemented by the compiler but is not visible to users */
trait QuoteContextInternal { self: QuoteContext =>

/** Unpickle `repr` which represents a pickled `Expr` tree,
* replacing splice nodes with `holes`
*/
def unpickleExpr[T](pickledQuote: PickledQuote): scala.quoted.Expr[T]
def unpickleExpr[T](pickled: String | List[String], typeHole: (Int, Seq[Any]) => Type[?], termHole: (Int, Seq[Any], QuoteContext) => Expr[?]): scala.quoted.Expr[T]

/** Unpickle `repr` which represents a pickled `Type` tree,
* replacing splice nodes with `holes`
*/
def unpickleType[T <: AnyKind](pickledQuote: PickledQuote): scala.quoted.Type[T]
def unpickleType[T <: AnyKind](pickled: String | List[String], typeHole: (Int, Seq[Any]) => Type[?], termHole: (Int, Seq[Any], QuoteContext) => Expr[?]): scala.quoted.Type[T]

val ExprMatch: ExprMatchModule

Expand Down