Skip to content

Commit 911e388

Browse files
Merge pull request #4103 from dotty-staging/encode-quoted-values
Encode quoted values
2 parents 7cb10a8 + 47a68d6 commit 911e388

File tree

9 files changed

+114
-46
lines changed

9 files changed

+114
-46
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,9 @@ class Definitions {
625625
lazy val QuotedExpr_runR = QuotedExprClass.requiredMethodRef(nme.run)
626626
def QuotedExpr_run(implicit ctx: Context) = QuotedExpr_runR.symbol
627627

628+
lazy val QuotedExprsModule = ctx.requiredModule("scala.quoted.Exprs")
629+
def QuotedExprsClass(implicit ctx: Context) = QuotedExprsModule.symbol.asClass
630+
628631
lazy val QuotedTypeType = ctx.requiredClassRef("scala.quoted.Type")
629632
def QuotedTypeClass(implicit ctx: Context) = QuotedTypeType.symbol.asClass
630633

@@ -636,6 +639,7 @@ class Definitions {
636639
def QuotedType_apply(implicit ctx: Context) = QuotedType_applyR.symbol
637640

638641
def Unpickler_unpickleExpr = ctx.requiredMethod("scala.runtime.quoted.Unpickler.unpickleExpr")
642+
def Unpickler_liftedExpr = ctx.requiredMethod("scala.runtime.quoted.Unpickler.liftedExpr")
639643
def Unpickler_unpickleType = ctx.requiredMethod("scala.runtime.quoted.Unpickler.unpickleType")
640644

641645
lazy val EqType = ctx.requiredClassRef("scala.Eq")

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ object PickledQuotes {
3333
/** Transform the expression into its fully spliced Tree */
3434
def quotedExprToTree(expr: quoted.Expr[_])(implicit ctx: Context): Tree = expr match {
3535
case expr: TastyExpr[_] => unpickleExpr(expr)
36-
case expr: ValueExpr[_] => Literal(Constant(expr.value))
36+
case expr: LiftedExpr[_] => Literal(Constant(expr.value))
3737
case expr: TreeExpr[Tree] @unchecked => expr.tree
3838
case expr: FunctionAppliedTo[_, _] =>
3939
functionAppliedTo(quotedExprToTree(expr.f), quotedExprToTree(expr.x))

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import dotty.tools.dotc.printing.RefinedPrinter
77

88
import scala.quoted.Expr
99
import scala.runtime.BoxedUnit
10-
import scala.quoted.Exprs.ValueExpr
10+
import scala.quoted.Exprs.LiftedExpr
1111
import scala.runtime.quoted._
1212

1313
/** Default runners for quoted expressions */
@@ -23,12 +23,12 @@ object Toolbox {
2323
): Toolbox[T] = new Toolbox[T] {
2424

2525
def run(expr: Expr[T]): T = expr match {
26-
case expr: ValueExpr[T] => expr.value
26+
case expr: LiftedExpr[T] => expr.value
2727
case _ => new QuoteDriver().run(expr, runSettings)
2828
}
2929

3030
def show(expr: Expr[T]): String = expr match {
31-
case expr: ValueExpr[T] =>
31+
case expr: LiftedExpr[T] =>
3232
implicit val ctx = new QuoteDriver().initCtx
3333
if (showSettings.compilerArgs.contains("-color:never"))
3434
ctx.settings.color.update("never")
@@ -46,7 +46,7 @@ object Toolbox {
4646
case _ => None
4747
}
4848
expr match {
49-
case expr: ValueExpr[T] => Some(expr.value)
49+
case expr: LiftedExpr[T] => Some(expr.value)
5050
case _ => new QuoteDriver().withTree(expr, (tree, _) => toConstantOpt(tree), Settings.run())
5151
}
5252
}

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

Lines changed: 51 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import tasty.TreePickler.Hole
1212
import MegaPhase.MiniPhase
1313
import SymUtils._
1414
import NameKinds._
15+
import dotty.tools.dotc.ast.tpd.Tree
1516
import typer.Implicits.SearchFailureType
1617

1718
import scala.collection.mutable
@@ -72,19 +73,19 @@ class ReifyQuotes extends MacroTransformWithImplicits {
7273
val levelOf = new mutable.HashMap[Symbol, Int]
7374

7475
/** Register a reference defined in a quote but used in another quote nested in a splice.
75-
* Returns a lifted version of the reference that needs to be used in its place.
76+
* Returns a version of the reference that needs to be used in its place.
7677
* '{
7778
* val x = ???
7879
* { ... '{ ... x ... } ... }.unary_~
7980
* }
80-
* Lifting the `x` in `{ ... '{ ... x ... } ... }.unary_~` will return a `x$1.unary_~` for which the `x$1`
81+
* Eta expanding the `x` in `{ ... '{ ... x ... } ... }.unary_~` will return a `x$1.unary_~` for which the `x$1`
8182
* be created by some outer reifier.
8283
*
8384
* This transformation is only applied to definitions at staging level 1.
8485
*
85-
* See `needsLifting`
86+
* See `isCaptured`
8687
*/
87-
val lifters = new mutable.HashMap[Symbol, RefTree => Tree]
88+
val capturers = new mutable.HashMap[Symbol, RefTree => Tree]
8889
}
8990

9091
/** The main transformer class
@@ -330,12 +331,25 @@ class ReifyQuotes extends MacroTransformWithImplicits {
330331
}
331332
else {
332333
val (body1, splices) = nested(isQuote = true).split(body)
334+
pickledQuote(body1, splices, isType).withPos(quote.pos)
335+
}
336+
}
337+
338+
private def pickledQuote(body: Tree, splices: List[Tree], isType: Boolean)(implicit ctx: Context) = {
339+
def pickleAsValue[T](value: T) =
340+
ref(defn.Unpickler_liftedExpr).appliedToType(body.tpe.widen).appliedTo(Literal(Constant(value)))
341+
def pickleAsTasty() = {
333342
val meth =
334-
if (isType) ref(defn.Unpickler_unpickleType).appliedToType(body1.tpe)
335-
else ref(defn.Unpickler_unpickleExpr).appliedToType(body1.tpe.widen)
343+
if (isType) ref(defn.Unpickler_unpickleType).appliedToType(body.tpe)
344+
else ref(defn.Unpickler_unpickleExpr).appliedToType(body.tpe.widen)
336345
meth.appliedTo(
337-
liftList(PickledQuotes.pickleQuote(body1).map(x => Literal(Constant(x))), defn.StringType),
338-
liftList(splices, defn.AnyType)).withPos(quote.pos)
346+
liftList(PickledQuotes.pickleQuote(body).map(x => Literal(Constant(x))), defn.StringType),
347+
liftList(splices, defn.AnyType))
348+
}
349+
if (splices.nonEmpty) pickleAsTasty()
350+
else ReifyQuotes.toValue(body) match {
351+
case Some(value) => pickleAsValue(value)
352+
case _ => pickleAsTasty()
339353
}
340354
}
341355

@@ -374,7 +388,7 @@ class ReifyQuotes extends MacroTransformWithImplicits {
374388
* { ... '{ ... x$1.unary_~ ... y$1.unary_~ ... } ... }
375389
* }
376390
*
377-
* See: `lift`
391+
* See: `capture`
378392
*
379393
* At the same time register `embedded` trees `x` and `y` to place as arguments of the hole
380394
* placed in the original code.
@@ -387,17 +401,17 @@ class ReifyQuotes extends MacroTransformWithImplicits {
387401
private def makeLambda(tree: Tree)(implicit ctx: Context): Tree = {
388402
def body(arg: Tree)(implicit ctx: Context): Tree = {
389403
var i = 0
390-
transformWithLifter(tree)(
391-
(lifted: mutable.ListBuffer[Tree]) => (tree: RefTree) => {
404+
transformWithCapturer(tree)(
405+
(captured: mutable.ListBuffer[Tree]) => (tree: RefTree) => {
392406
val argTpe =
393407
if (tree.isTerm) defn.QuotedExprType.appliedTo(tree.tpe.widen)
394408
else defn.QuotedTypeType.appliedTo(defn.AnyType)
395409
val selectArg = arg.select(nme.apply).appliedTo(Literal(Constant(i))).asInstance(argTpe)
396-
val liftedArg = SyntheticValDef(UniqueName.fresh(tree.name.toTermName).toTermName, selectArg)
410+
val capturedArg = SyntheticValDef(UniqueName.fresh(tree.name.toTermName).toTermName, selectArg)
397411
i += 1
398412
embedded += tree
399-
lifted += liftedArg
400-
ref(liftedArg.symbol)
413+
captured += capturedArg
414+
ref(capturedArg.symbol)
401415
}
402416
)
403417
}
@@ -408,21 +422,21 @@ class ReifyQuotes extends MacroTransformWithImplicits {
408422
Closure(meth, tss => body(tss.head.head)(ctx.withOwner(meth)).changeOwner(ctx.owner, meth))
409423
}
410424

411-
private def transformWithLifter(tree: Tree)(
412-
lifter: mutable.ListBuffer[Tree] => RefTree => Tree)(implicit ctx: Context): Tree = {
413-
val lifted = new mutable.ListBuffer[Tree]
414-
val lifter2 = lifter(lifted)
415-
outer.enteredSyms.foreach(s => lifters.put(s, lifter2))
425+
private def transformWithCapturer(tree: Tree)(
426+
capturer: mutable.ListBuffer[Tree] => RefTree => Tree)(implicit ctx: Context): Tree = {
427+
val captured = new mutable.ListBuffer[Tree]
428+
val captured2 = capturer(captured)
429+
outer.enteredSyms.foreach(s => capturers.put(s, captured2))
416430
val tree2 = transform(tree)
417-
lifters --= outer.enteredSyms
418-
seq(lifted.result(), tree2)
431+
capturers --= outer.enteredSyms
432+
seq(captured.result(), tree2)
419433
}
420434

421-
/** Returns true if this tree will be lifted by `makeLambda` */
422-
private def needsLifting(tree: RefTree)(implicit ctx: Context): Boolean = {
423-
// Check phase consistency and presence of lifter
435+
/** Returns true if this tree will be captured by `makeLambda` */
436+
private def isCaptured(tree: RefTree)(implicit ctx: Context): Boolean = {
437+
// Check phase consistency and presence of capturer
424438
level == 1 && !tree.symbol.is(Inline) && levelOf.get(tree.symbol).contains(1) &&
425-
lifters.contains(tree.symbol)
439+
capturers.contains(tree.symbol)
426440
}
427441

428442
/** Transform `tree` and return the resulting tree and all `embedded` quotes
@@ -459,9 +473,9 @@ class ReifyQuotes extends MacroTransformWithImplicits {
459473
splice(ref(splicedType).select(tpnme.UNARY_~))
460474
case tree: Select if tree.symbol.isSplice =>
461475
splice(tree)
462-
case tree: RefTree if needsLifting(tree) =>
463-
val lift = lifters(tree.symbol)
464-
splice(lift(tree).select(if (tree.isTerm) nme.UNARY_~ else tpnme.UNARY_~))
476+
case tree: RefTree if isCaptured(tree) =>
477+
val capturer = capturers(tree.symbol)
478+
splice(capturer(tree).select(if (tree.isTerm) nme.UNARY_~ else tpnme.UNARY_~))
465479
case Block(stats, _) =>
466480
val last = enteredSyms
467481
stats.foreach(markDef)
@@ -515,3 +529,12 @@ class ReifyQuotes extends MacroTransformWithImplicits {
515529
}
516530
}
517531
}
532+
533+
object ReifyQuotes {
534+
def toValue(tree: Tree): Option[Any] = tree match {
535+
case Literal(Constant(c)) => Some(c)
536+
case Block(Nil, e) => toValue(e)
537+
case Inlined(_, Nil, e) => toValue(e)
538+
case _ => None
539+
}
540+
}

library/src/scala/quoted/Expr.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ object Exprs {
2626
override def toString: String = s"Expr(<pickled>)"
2727
}
2828

29-
/** An Expr backed by a value.
29+
/** An Expr backed by a lifted value.
3030
* Values can only be of type Boolean, Byte, Short, Char, Int, Long, Float, Double, Unit, String or Null.
3131
*/
32-
final class ValueExpr[T](val value: T) extends Expr[T] {
32+
final class LiftedExpr[T](val value: T) extends Expr[T] {
3333
override def toString: String = s"Expr($value)"
3434
}
3535

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package scala.quoted
22

3-
import scala.quoted.Exprs.ValueExpr
3+
import scala.runtime.quoted.Unpickler.liftedExpr
44

55
/** A typeclass for types that can be turned to `quoted.Expr[T]`
66
* without going through an explicit `'(...)` operation.
@@ -15,14 +15,14 @@ abstract class Liftable[T] {
1515
* gives an alternative implementation using just the basic staging system.
1616
*/
1717
object Liftable {
18-
implicit def BooleanIsLiftable: Liftable[Boolean] = (x: Boolean) => new ValueExpr(x)
19-
implicit def ByteLiftable: Liftable[Byte] = (x: Byte) => new ValueExpr(x)
20-
implicit def CharIsLiftable: Liftable[Char] = (x: Char) => new ValueExpr(x)
21-
implicit def ShortIsLiftable: Liftable[Short] = (x: Short) => new ValueExpr(x)
22-
implicit def IntIsLiftable: Liftable[Int] = (x: Int) => new ValueExpr(x)
23-
implicit def LongIsLiftable: Liftable[Long] = (x: Long) => new ValueExpr(x)
24-
implicit def FloatIsLiftable: Liftable[Float] = (x: Float) => new ValueExpr(x)
25-
implicit def DoubleIsLiftable: Liftable[Double] = (x: Double) => new ValueExpr(x)
18+
implicit def BooleanIsLiftable: Liftable[Boolean] = (x: Boolean) => liftedExpr(x)
19+
implicit def ByteLiftable: Liftable[Byte] = (x: Byte) => liftedExpr(x)
20+
implicit def CharIsLiftable: Liftable[Char] = (x: Char) => liftedExpr(x)
21+
implicit def ShortIsLiftable: Liftable[Short] = (x: Short) => liftedExpr(x)
22+
implicit def IntIsLiftable: Liftable[Int] = (x: Int) => liftedExpr(x)
23+
implicit def LongIsLiftable: Liftable[Long] = (x: Long) => liftedExpr(x)
24+
implicit def FloatIsLiftable: Liftable[Float] = (x: Float) => liftedExpr(x)
25+
implicit def DoubleIsLiftable: Liftable[Double] = (x: Double) => liftedExpr(x)
2626

27-
implicit def StringIsLiftable: Liftable[String] = (x: String) => new ValueExpr(x)
27+
implicit def StringIsLiftable: Liftable[String] = (x: String) => liftedExpr(x)
2828
}

library/src/scala/runtime/quoted/Unpickler.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package scala.runtime.quoted
22

33
import scala.quoted.Types.TastyType
4-
import scala.quoted.Exprs.TastyExpr
4+
import scala.quoted.Exprs.{LiftedExpr, TastyExpr}
55
import scala.quoted.{Expr, Type}
66

77
/** Provides methods to unpickle `Expr` and `Type` trees. */
@@ -17,6 +17,11 @@ object Unpickler {
1717
*/
1818
def unpickleExpr[T](repr: Pickled, args: Seq[Any]): Expr[T] = new TastyExpr[T](repr, args)
1919

20+
/** Lift the `value` to an `Expr` tree.
21+
* Values can only be of type Boolean, Byte, Short, Char, Int, Long, Float, Double, Unit, String or Null.
22+
*/
23+
def liftedExpr[T](value: T): LiftedExpr[T] = new LiftedExpr[T](value)
24+
2025
/** Unpickle `repr` which represents a pickled `Type` tree,
2126
* replacing splice nodes with `args`
2227
*/
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
class scala.quoted.Exprs$LiftedExpr
2+
class scala.quoted.Exprs$LiftedExpr
3+
class scala.quoted.Exprs$LiftedExpr
4+
class scala.quoted.Exprs$LiftedExpr
5+
class scala.quoted.Exprs$LiftedExpr
6+
class scala.quoted.Exprs$LiftedExpr
7+
class scala.quoted.Exprs$LiftedExpr
8+
class scala.quoted.Exprs$LiftedExpr
9+
class scala.quoted.Exprs$LiftedExpr
10+
class scala.quoted.Exprs$LiftedExpr
11+
class scala.quoted.Exprs$LiftedExpr
12+
class scala.quoted.Exprs$LiftedExpr
13+
class scala.quoted.Exprs$LiftedExpr
14+
class scala.quoted.Exprs$LiftedExpr
15+
class scala.quoted.Exprs$LiftedExpr
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import scala.quoted._
2+
3+
object Test {
4+
def main(args: Array[String]): Unit = {
5+
println(('(true)).getClass)
6+
println(('{ 'a' }).getClass)
7+
println(('{ '\n' }).getClass)
8+
println(('{ '"' }).getClass)
9+
println(('{ '\'' }).getClass)
10+
println(('{ '\\' }).getClass)
11+
println(('(1)).getClass)
12+
println(('( { { 2 } } )).getClass)
13+
println(('(3L)).getClass)
14+
println(('(4.0f)).getClass)
15+
println(('(5.0d)).getClass)
16+
println(('("xyz")).getClass)
17+
println(('()).getClass)
18+
println(('{()}).getClass)
19+
println(('{{()}}).getClass)
20+
}
21+
}

0 commit comments

Comments
 (0)