Skip to content

Commit

Permalink
Avoid retraversing parts of the tree that do not contain Quote trees (#…
Browse files Browse the repository at this point in the history
…17407)

Also, remove an illegal use of `WildcardType`.
  • Loading branch information
nicolasstucki authored May 16, 2023
2 parents d7c0cb0 + b7cc753 commit 9923e29
Showing 1 changed file with 22 additions and 28 deletions.
50 changes: 22 additions & 28 deletions compiler/src/dotty/tools/dotc/transform/PickleQuotes.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ import scala.annotation.constructorOnly
* val x1: U1 = ???
* val x2: U2 = ???
* ...
* {{{ 3 | x1 | contents0 | T0 }}} // hole
* {{{ 3 | x1 | holeContents0 | T0 }}} // hole
* ...
* {{{ 4 | x2 | contents1 | T1 }}} // hole
* {{{ 4 | x2 | holeContents1 | T1 }}} // hole
* ...
* {{{ 5 | x1, x2 | contents2 | T2 }}} // hole
* {{{ 5 | x1, x2 | holeContents2 | T2 }}} // hole
* ...
* }
* ```
Expand All @@ -59,9 +59,9 @@ import scala.annotation.constructorOnly
* ]],
* typeHole = Seq(a, b),
* termHole = (idx: Int, args: List[Any], quotes: Quotes) => idx match {
* case 3 => content0.apply(args(0).asInstanceOf[Expr[U1]]).apply(quotes) // beta reduced
* case 4 => content1.apply(args(0).asInstanceOf[Expr[U2]]).apply(quotes) // beta reduced
* case 5 => content2.apply(args(0).asInstanceOf[Expr[U1]], args(1).asInstanceOf[Expr[U2]]).apply(quotes) // beta reduced
* case 3 => holeContents0.apply(args(0).asInstanceOf[Expr[U1]]).apply(quotes) // beta reduced
* case 4 => holeContents1.apply(args(0).asInstanceOf[Expr[U2]]).apply(quotes) // beta reduced
* case 5 => holeContents2.apply(args(0).asInstanceOf[Expr[U1]], args(1).asInstanceOf[Expr[U2]]).apply(quotes) // beta reduced
* },
* )
* ```
Expand Down Expand Up @@ -93,26 +93,25 @@ class PickleQuotes extends MacroTransform {
override def transform(tree: tpd.Tree)(using Context): tpd.Tree =
tree match
case Apply(Select(quote: Quote, nme.apply), List(quotes)) =>
val (contents, quote1) = makeHoles(quote)
val (holeContents, quote1) = extractHolesContents(quote)
val quote2 = encodeTypeArgs(quote1)
val contents1 = contents ::: quote.tags
val pickled = PickleQuotes.pickle(quote2, quotes, contents1)
transform(pickled) // pickle quotes that are in the contents
val holeContents1 = holeContents.map(transform(_))
PickleQuotes.pickle(quote2, quotes, holeContents1)
case tree: DefDef if !tree.rhs.isEmpty && tree.symbol.isInlineMethod =>
tree
case _ =>
super.transform(tree)
}

private def makeHoles(quote: tpd.Quote)(using Context): (List[Tree], tpd.Quote) =
private def extractHolesContents(quote: tpd.Quote)(using Context): (List[Tree], tpd.Quote) =
class HoleContentExtractor extends Transformer:
private val contents = List.newBuilder[Tree]
private val holeContents = List.newBuilder[Tree]
override def transform(tree: tpd.Tree)(using Context): tpd.Tree =
tree match
case tree @ Hole(isTerm, _, _, content) =>
assert(isTerm)
assert(!content.isEmpty)
contents += content
holeContents += content
val holeType = getTermHoleType(tree.tpe)
val hole = untpd.cpy.Hole(tree)(content = EmptyTree).withType(holeType)
cpy.Inlined(tree)(EmptyTree, Nil, hole)
Expand Down Expand Up @@ -148,10 +147,10 @@ class PickleQuotes extends MacroTransform {
mapOver(tp)
}

/** Get the contents of the transformed tree */
/** Get the holeContents of the transformed tree */
def getContents() =
val res = contents.result
contents.clear()
val res = holeContents.result
holeContents.clear()
res
end HoleContentExtractor

Expand All @@ -160,7 +159,7 @@ class PickleQuotes extends MacroTransform {
val quote1 = cpy.Quote(quote)(body1, quote.tags)

(holeMaker.getContents(), quote1)
end makeHoles
end extractHolesContents

/** Encode quote tags as holes in the quote body.
*
Expand Down Expand Up @@ -237,7 +236,7 @@ object PickleQuotes {
val name: String = "pickleQuotes"
val description: String = "turn quoted trees into explicit run-time data structures"

def pickle(quote: Quote, quotes: Tree, contents: List[Tree])(using Context) = {
def pickle(quote: Quote, quotes: Tree, holeContents: List[Tree])(using Context) = {
val body = quote.body
val bodyType = quote.bodyType

Expand Down Expand Up @@ -335,27 +334,22 @@ object PickleQuotes {
case x :: Nil => Literal(Constant(x))
case xs => tpd.mkList(xs.map(x => Literal(Constant(x))), TypeTree(defn.StringType))

// TODO split holes earlier into types and terms. This all holes in each category can have consecutive indices
val (typeSplices, termSplices) = contents.zipWithIndex.partition {
_._1.tpe.derivesFrom(defn.QuotedTypeClass)
}

// This and all closures in typeSplices are removed by the BetaReduce phase
val types =
if typeSplices.isEmpty then Literal(Constant(null)) // keep pickled quote without contents as small as possible
else SeqLiteral(typeSplices.map(_._1), TypeTree(defn.QuotedTypeClass.typeRef.appliedTo(WildcardType)))
if quote.tags.isEmpty then Literal(Constant(null)) // keep pickled quote without holeContents as small as possible
else SeqLiteral(quote.tags, TypeTree(defn.QuotedTypeClass.typeRef.appliedTo(TypeBounds.emptyPolyKind)))

// 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 contents as small as possible
if holeContents.isEmpty then Literal(Constant(null)) // keep pickled quote without holeContents as small as possible
else
Lambda(
MethodType(
List(nme.idx, nme.contents, nme.quotes).map(name => UniqueName.fresh(name).toTermName),
List(defn.IntType, defn.SeqType.appliedTo(defn.AnyType), defn.QuotesClass.typeRef),
defn.QuotedExprClass.typeRef.appliedTo(defn.AnyType)),
args =>
val cases = termSplices.map { case (splice, idx) =>
val cases = holeContents.zipWithIndex.map { case (splice, idx) =>
val defn.FunctionOf(argTypes, defn.FunctionOf(quotesType :: _, _, _), _) = splice.tpe: @unchecked
val rhs = {
val spliceArgs = argTypes.zipWithIndex.map { (argType, i) =>
Expand Down Expand Up @@ -414,7 +408,7 @@ object PickleQuotes {
case _ => None

if body.isType then
if contents.isEmpty && body.symbol.isPrimitiveValueClass then taggedType()
if holeContents.isEmpty && body.symbol.isPrimitiveValueClass then taggedType()
else pickleAsTasty()
else
getLiteral(body) match
Expand Down

0 comments on commit 9923e29

Please sign in to comment.