Skip to content

Commit ab42870

Browse files
Merge pull request #10232 from dotty-staging/reify-null-and-unit-quotes-directly-as-literals
Encode pickled primitives directly using reflection
2 parents 3b2bcee + bff91a8 commit ab42870

File tree

5 files changed

+90
-62
lines changed

5 files changed

+90
-62
lines changed

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

Lines changed: 15 additions & 10 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 self.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

@@ -1564,7 +1567,7 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, scala.intern
15641567

15651568
type TypeRepr = dotc.core.Types.Type
15661569

1567-
object TypeRepr extends TypeModule:
1570+
object TypeRepr extends TypeReprModule:
15681571
def of[T <: AnyKind](using qtype: scala.quoted.Type[T]): TypeRepr =
15691572
qtype.asInstanceOf[scala.internal.quoted.Type[TypeTree]].typeTree.tpe
15701573
def typeConstructorOf(clazz: Class[?]): TypeRepr =
@@ -1589,7 +1592,7 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, scala.intern
15891592
dotc.core.Symbols.getClassIfDefined(clazz.getCanonicalName).typeRef
15901593
end TypeRepr
15911594

1592-
object TypeMethodsImpl extends TypeMethods:
1595+
object TypeReprMethodsImpl extends TypeReprMethods:
15931596
extension (self: TypeRepr):
15941597
def showExtractors: String =
15951598
Extractors.showType(using QuoteContextImpl.this)(self)
@@ -1600,7 +1603,9 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, scala.intern
16001603
def showAnsiColored: String =
16011604
SourceCode.showType(using QuoteContextImpl.this)(self)(SyntaxHighlight.ANSI)
16021605

1603-
def seal: scala.quoted.Type[_] =
1606+
def seal: scala.quoted.Type[_] = self.asType
1607+
1608+
def asType: scala.quoted.Type[?] =
16041609
new scala.internal.quoted.Type(Inferred(self), QuoteContextImpl.this.hashCode)
16051610

16061611
def =:=(that: TypeRepr): Boolean = self =:= that
@@ -1636,7 +1641,7 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, scala.intern
16361641
def appliedTo(targs: List[TypeRepr]): TypeRepr =
16371642
dotc.core.Types.decorateTypeApplications(self).appliedTo(targs)
16381643
end extension
1639-
end TypeMethodsImpl
1644+
end TypeReprMethodsImpl
16401645

16411646
type ConstantType = dotc.core.Types.ConstantType
16421647

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

Lines changed: 54 additions & 5 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.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("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)
@@ -170,14 +199,34 @@ class ReifyQuotes extends MacroTransform {
170199
ref(unpickleMeth).appliedToType(originalTp).appliedTo(pickledQuote)
171200
}
172201

173-
def taggedType(sym: Symbol) = ref(defn.InternalQuotedTypeModule).select(sym.name.toTermName)
202+
/** Encode quote using Reflection.TypeRepr.typeConstructorOf
203+
*
204+
* Generate the code
205+
* ```scala
206+
* qctx => qctx.reflect.TypeReprMethods.asType(
207+
* qctx.reflect.TypeRepr.typeConstructorOf(classOf[<type>]])
208+
* ).asInstanceOf[scala.quoted.Type[<type>]]
209+
* ```
210+
* this closure is always applied directly to the actual context and the BetaReduce phase removes it.
211+
*/
212+
def taggedType() =
213+
val typeType = defn.QuotedTypeClass.typeRef.appliedTo(body.tpe)
214+
val classTree = TypeApply(ref(defn.Predef_classOf.termRef), body :: Nil)
215+
val tpe = MethodType(defn.QuoteContextClass.typeRef :: Nil, typeType)
216+
val meth = newSymbol(ctx.owner, UniqueName.fresh(nme.ANON_FUN), Synthetic | Method, tpe)
217+
def mkConst(tss: List[List[Tree]]) = {
218+
val reflect = tss.head.head.select("reflect".toTermName)
219+
val typeRepr = reflect.select("TypeRepr".toTermName).select("typeConstructorOf".toTermName).appliedTo(classTree)
220+
reflect.select("TypeReprMethods".toTermName).select("asType".toTermName).appliedTo(typeRepr).asInstance(typeType)
221+
}
222+
Closure(meth, mkConst).withSpan(body.span)
174223

175224
if (isType) {
176-
if (splices.isEmpty && body.symbol.isPrimitiveValueClass) taggedType(body.symbol)
225+
if (splices.isEmpty && body.symbol.isPrimitiveValueClass) taggedType()
177226
else pickleAsTasty()
178227
}
179228
else getLiteral(body) match {
180-
case Some(lit) => pickleAsLiteral(lit)
229+
case Some(lit) => pickleAsValue(lit)
181230
case _ => pickleAsTasty()
182231
}
183232
}

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-bootstrapped/scala/internal/quoted/Type.scala

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -41,34 +41,4 @@ object Type {
4141
qctx.asInstanceOf[QuoteContextInternal].typeMatch(scrutineeType, patternType).asInstanceOf[Option[Tup]]
4242
}
4343

44-
45-
// TODO generalize following optimizations for all classes without parameters
46-
47-
def Unit: QuoteContext ?=> quoted.Type[Unit] =
48-
qctx.reflect.TypeRepr.typeConstructorOf(classOf[Unit]).seal.asInstanceOf[quoted.Type[Unit]]
49-
50-
def Boolean: QuoteContext ?=> quoted.Type[Boolean] =
51-
qctx.reflect.TypeRepr.typeConstructorOf(classOf[Boolean]).seal.asInstanceOf[quoted.Type[Boolean]]
52-
53-
def Byte: QuoteContext ?=> quoted.Type[Byte] =
54-
qctx.reflect.TypeRepr.typeConstructorOf(classOf[Byte]).seal.asInstanceOf[quoted.Type[Byte]]
55-
56-
def Char: QuoteContext ?=> quoted.Type[Char] =
57-
qctx.reflect.TypeRepr.typeConstructorOf(classOf[Char]).seal.asInstanceOf[quoted.Type[Char]]
58-
59-
def Short: QuoteContext ?=> quoted.Type[Short] =
60-
qctx.reflect.TypeRepr.typeConstructorOf(classOf[Short]).seal.asInstanceOf[quoted.Type[Short]]
61-
62-
def Int: QuoteContext ?=> quoted.Type[Int] =
63-
qctx.reflect.TypeRepr.typeConstructorOf(classOf[Int]).seal.asInstanceOf[quoted.Type[Int]]
64-
65-
def Long: QuoteContext ?=> quoted.Type[Long] =
66-
qctx.reflect.TypeRepr.typeConstructorOf(classOf[Long]).seal.asInstanceOf[quoted.Type[Long]]
67-
68-
def Float: QuoteContext ?=> quoted.Type[Float] =
69-
qctx.reflect.TypeRepr.typeConstructorOf(classOf[Float]).seal.asInstanceOf[quoted.Type[Float]]
70-
71-
def Double: QuoteContext ?=> quoted.Type[Double] =
72-
qctx.reflect.TypeRepr.typeConstructorOf(classOf[Double]).seal.asInstanceOf[quoted.Type[Double]]
73-
7444
}

library/src/scala/tasty/Reflection.scala

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

163163
/** Does this tree represent a valid expression? */
164164
def isExpr: Boolean
165+
166+
/** Convert this tree to an `quoted.Expr[Any]` if the tree is a valid expression or throws */
167+
def asExpr: scala.quoted.Expr[Any]
165168
end extension
166169

167170
/** Convert this tree to an `quoted.Expr[T]` if the tree is a valid expression or throws */
168171
extension [T](self: Tree)
172+
// FIXME: remove QuoteContext from parameters
173+
// TODO: Move Reflection inside QuoteContext as it can never be instantiated outside a QuoteContext
169174
def asExprOf(using scala.quoted.Type[T])(using QuoteContext): scala.quoted.Expr[T]
170175
}
171176

@@ -1741,20 +1746,20 @@ trait Reflection { reflection =>
17411746
/** A type, type constructors, type bounds or NoPrefix */
17421747
type TypeRepr
17431748

1744-
val TypeRepr: TypeModule
1749+
val TypeRepr: TypeReprModule
17451750

1746-
trait TypeModule { this: TypeRepr.type =>
1751+
trait TypeReprModule { this: TypeRepr.type =>
17471752
/** Returns the type or kind (TypeRepr) of T */
17481753
def of[T <: AnyKind](using qtype: scala.quoted.Type[T]): TypeRepr
17491754

17501755
/** Returns the type constructor of the runtime (erased) class */
17511756
def typeConstructorOf(clazz: Class[?]): TypeRepr
17521757
}
17531758

1754-
given TypeMethods as TypeMethods = TypeMethodsImpl
1755-
protected val TypeMethodsImpl: TypeMethods
1759+
given TypeReprMethods as TypeReprMethods = TypeReprMethodsImpl
1760+
protected val TypeReprMethodsImpl: TypeReprMethods
17561761

1757-
trait TypeMethods {
1762+
trait TypeReprMethods {
17581763
extension (self: TypeRepr):
17591764

17601765
/** Shows the tree as extractors */
@@ -1769,6 +1774,17 @@ trait Reflection { reflection =>
17691774
/** Convert `TypeRepr` to an `quoted.Type[_]` */
17701775
def seal: scala.quoted.Type[_]
17711776

1777+
/** Convert this `TypeRepr` to an `Type[?]`
1778+
*
1779+
* Usage:
1780+
* ```
1781+
* typeRepr.asType match
1782+
* case '[$t] =>
1783+
* '{ val x: t = ... }
1784+
* ```
1785+
*/
1786+
def asType: scala.quoted.Type[?]
1787+
17721788
/** Is `self` type the same as `that` type?
17731789
* This is the case iff `self <:< that` and `that <:< self`.
17741790
*/

0 commit comments

Comments
 (0)