Skip to content

Commit 86d8b30

Browse files
committed
Pickle '{} and '{null} directly as Literal
This removes the need of some extra helper methods in the internal package. Also add `asExpr` to have a variant of `asExprOf[Any]` that does not need the extra type test at runtime.
1 parent ac88f0e commit 86d8b30

File tree

4 files changed

+46
-21
lines changed

4 files changed

+46
-21
lines changed

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

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,15 +70,18 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, scala.intern
7070
case _: MethodType | _: PolyType => false
7171
case _ => true
7272
case _ => false
73+
def asExpr: scala.quoted.Expr[Any] =
74+
if tree.isExpr then
75+
new scala.internal.quoted.Expr(self, QuoteContextImpl.this.hashCode)
76+
else self match
77+
case TermTypeTest(self) => throw new Exception("Expected an expression. This is a partially applied Term. Try eta-expanding the term first.")
78+
case _ => throw new Exception("Expected a Term but was: " + self)
7379
end extension
7480

75-
extension [T](tree: Tree)
81+
extension [T](self: Tree)
7682
def asExprOf(using scala.quoted.Type[T])(using QuoteContext): scala.quoted.Expr[T] =
77-
if tree.isExpr then
78-
new scala.internal.quoted.Expr(tree, QuoteContextImpl.this.hashCode).asExprOf[T]
79-
else tree match
80-
case TermTypeTest(tree) => throw new Exception("Expected an expression. This is a partially applied Term. Try eta-expanding the term first.")
81-
case _ => throw new Exception("Expected a Term but was: " + tree)
83+
self.asExpr.asExprOf[T]
84+
end extension
8285

8386
end TreeMethodsImpl
8487

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

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,41 @@ class ReifyQuotes extends MacroTransform {
143143
}
144144

145145
private def pickledQuote(body: Tree, splices: List[Tree], originalTp: Type, isType: Boolean)(using Context) = {
146+
/** Encode quote using Reflection.Literal
147+
*
148+
* Generate the code
149+
* ```scala
150+
* qctx => qctx.reflect.TreeMethods.extension_asExpr(
151+
* qctx.reflect.Literal.apply(x$1.reflect.Constant.<typeName>.apply(<literalValue>))
152+
* ).asInstanceOf[scala.quoted.Expr[<body.type>]]
153+
* ```
154+
* this closure is always applied directly to the actual context and the BetaReduce phase removes it.
155+
*/
146156
def pickleAsLiteral(lit: Literal) = {
157+
val exprType = defn.QuotedExprClass.typeRef.appliedTo(body.tpe)
158+
val tpe = MethodType(defn.QuoteContextClass.typeRef :: Nil, exprType)
159+
val meth = newSymbol(ctx.owner, UniqueName.fresh(nme.ANON_FUN), Synthetic | Method, tpe)
160+
def mkConst(tss: List[List[Tree]]) = {
161+
val reflect = tss.head.head.select("reflect".toTermName)
162+
val typeName = body.tpe.typeSymbol.name
163+
val literalValue =
164+
if lit.const.tag == Constants.NullTag || lit.const.tag == Constants.UnitTag then Nil
165+
else List(body)
166+
val constant = reflect.select("Constant".toTermName).select(typeName.toTermName).select(nme.apply).appliedToArgs(literalValue)
167+
val literal = reflect.select("Literal".toTermName).select(nme.apply).appliedTo(constant)
168+
reflect.select("TreeMethods".toTermName).select("extension_asExpr".toTermName).appliedTo(literal).asInstance(exprType)
169+
}
170+
Closure(meth, mkConst).withSpan(body.span)
171+
}
172+
173+
def pickleAsValue(lit: Literal) = {
174+
// TODO should all constants be pickled as Literals?
175+
// Should examime the generated bytecode size to decide and performance
147176
def liftedValue(lifter: Symbol) =
148177
ref(lifter).appliedToType(originalTp).select(nme.toExpr).appliedTo(lit)
149178
lit.const.tag match {
150-
case Constants.NullTag => ref(defn.InternalQuotedExpr_null)
151-
case Constants.UnitTag => ref(defn.InternalQuotedExpr_unit)
179+
case Constants.NullTag => pickleAsLiteral(lit)
180+
case Constants.UnitTag => pickleAsLiteral(lit)
152181
case Constants.BooleanTag => liftedValue(defn.LiftableModule_BooleanLiftable)
153182
case Constants.ByteTag => liftedValue(defn.LiftableModule_ByteLiftable)
154183
case Constants.ShortTag => liftedValue(defn.LiftableModule_ShortLiftable)
@@ -177,7 +206,7 @@ class ReifyQuotes extends MacroTransform {
177206
else pickleAsTasty()
178207
}
179208
else getLiteral(body) match {
180-
case Some(lit) => pickleAsLiteral(lit)
209+
case Some(lit) => pickleAsValue(lit)
181210
case _ => pickleAsTasty()
182211
}
183212
}

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

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,4 @@ object Expr {
5858
qctx.asInstanceOf[QuoteContextInternal].exprMatch(scrutineeExpr, patternExpr).asInstanceOf[Option[Tup]]
5959
}
6060

61-
/** Returns a null expresssion equivalent to `'{null}` */
62-
def `null`: QuoteContext ?=> quoted.Expr[Null] = (using qctx) => {
63-
import qctx.reflect._
64-
Literal(Constant.Null()).seal.asInstanceOf[quoted.Expr[Null]]
65-
}
66-
67-
/** Returns a unit expresssion equivalent to `'{}` or `'{()}` */
68-
def Unit: QuoteContext ?=> quoted.Expr[Unit] = (using qctx) => {
69-
import qctx.reflect._
70-
Literal(Constant.Unit()).seal.asInstanceOf[quoted.Expr[Unit]]
71-
}
72-
7361
}

library/src/scala/tasty/Reflection.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,10 +158,15 @@ trait Reflection { reflection =>
158158

159159
/** Does this tree represent a valid expression? */
160160
def isExpr: Boolean
161+
162+
/** Convert this tree to an `quoted.Expr[Any]` if the tree is a valid expression or throws */
163+
def asExpr: scala.quoted.Expr[Any]
161164
end extension
162165

163166
/** Convert this tree to an `quoted.Expr[T]` if the tree is a valid expression or throws */
164167
extension [T](self: Tree)
168+
// FIXME: remove QuoteContext from parameters
169+
// TODO: Move Reflection inside QuoteContext as it can never be instantiated outside a QuoteContext
165170
def asExprOf(using scala.quoted.Type[T])(using QuoteContext): scala.quoted.Expr[T]
166171
}
167172

0 commit comments

Comments
 (0)