From 6f5c4ded2f8660d8bd0a4c28a546771bf2dcb22b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sun, 8 Nov 2020 13:06:02 +0100 Subject: [PATCH 1/3] 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. --- .../tools/dotc/quoted/QuoteContextImpl.scala | 15 ++++---- .../tools/dotc/transform/ReifyQuotes.scala | 35 +++++++++++++++++-- .../scala/internal/quoted/Expr.scala | 12 ------- library/src/scala/tasty/Reflection.scala | 5 +++ 4 files changed, 46 insertions(+), 21 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/quoted/QuoteContextImpl.scala b/compiler/src/dotty/tools/dotc/quoted/QuoteContextImpl.scala index 423dd30971c9..ec6184bc8ce6 100644 --- a/compiler/src/dotty/tools/dotc/quoted/QuoteContextImpl.scala +++ b/compiler/src/dotty/tools/dotc/quoted/QuoteContextImpl.scala @@ -70,15 +70,18 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, scala.intern case _: MethodType | _: PolyType => false case _ => true case _ => false + def asExpr: scala.quoted.Expr[Any] = + if self.isExpr then + new scala.internal.quoted.Expr(self, QuoteContextImpl.this.hashCode) + else self match + case TermTypeTest(self) => throw new Exception("Expected an expression. This is a partially applied Term. Try eta-expanding the term first.") + case _ => throw new Exception("Expected a Term but was: " + self) end extension - extension [T](tree: Tree) + extension [T](self: Tree) def asExprOf(using scala.quoted.Type[T])(using QuoteContext): scala.quoted.Expr[T] = - if tree.isExpr then - new scala.internal.quoted.Expr(tree, QuoteContextImpl.this.hashCode).asExprOf[T] - else tree match - case TermTypeTest(tree) => throw new Exception("Expected an expression. This is a partially applied Term. Try eta-expanding the term first.") - case _ => throw new Exception("Expected a Term but was: " + tree) + self.asExpr.asExprOf[T] + end extension end TreeMethodsImpl diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index 8af0f7acee12..cb076430d051 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -143,12 +143,41 @@ class ReifyQuotes extends MacroTransform { } private def pickledQuote(body: Tree, splices: List[Tree], originalTp: Type, isType: Boolean)(using Context) = { + /** Encode quote using Reflection.Literal + * + * Generate the code + * ```scala + * qctx => qctx.reflect.TreeMethods.asExpr( + * qctx.reflect.Literal.apply(x$1.reflect.Constant..apply()) + * ).asInstanceOf[scala.quoted.Expr[]] + * ``` + * this closure is always applied directly to the actual context and the BetaReduce phase removes it. + */ def pickleAsLiteral(lit: Literal) = { + val exprType = defn.QuotedExprClass.typeRef.appliedTo(body.tpe) + val tpe = MethodType(defn.QuoteContextClass.typeRef :: Nil, exprType) + val meth = newSymbol(ctx.owner, UniqueName.fresh(nme.ANON_FUN), Synthetic | Method, tpe) + def mkConst(tss: List[List[Tree]]) = { + val reflect = tss.head.head.select("reflect".toTermName) + val typeName = body.tpe.typeSymbol.name + val literalValue = + if lit.const.tag == Constants.NullTag || lit.const.tag == Constants.UnitTag then Nil + else List(body) + val constant = reflect.select("Constant".toTermName).select(typeName.toTermName).select(nme.apply).appliedToArgs(literalValue) + val literal = reflect.select("Literal".toTermName).select(nme.apply).appliedTo(constant) + reflect.select("TreeMethods".toTermName).select("asExpr".toTermName).appliedTo(literal).asInstance(exprType) + } + Closure(meth, mkConst).withSpan(body.span) + } + + def pickleAsValue(lit: Literal) = { + // TODO should all constants be pickled as Literals? + // Should examime the generated bytecode size to decide and performance def liftedValue(lifter: Symbol) = ref(lifter).appliedToType(originalTp).select(nme.toExpr).appliedTo(lit) lit.const.tag match { - case Constants.NullTag => ref(defn.InternalQuotedExpr_null) - case Constants.UnitTag => ref(defn.InternalQuotedExpr_unit) + case Constants.NullTag => pickleAsLiteral(lit) + case Constants.UnitTag => pickleAsLiteral(lit) case Constants.BooleanTag => liftedValue(defn.LiftableModule_BooleanLiftable) case Constants.ByteTag => liftedValue(defn.LiftableModule_ByteLiftable) case Constants.ShortTag => liftedValue(defn.LiftableModule_ShortLiftable) @@ -177,7 +206,7 @@ class ReifyQuotes extends MacroTransform { else pickleAsTasty() } else getLiteral(body) match { - case Some(lit) => pickleAsLiteral(lit) + case Some(lit) => pickleAsValue(lit) case _ => pickleAsTasty() } } diff --git a/library/src-bootstrapped/scala/internal/quoted/Expr.scala b/library/src-bootstrapped/scala/internal/quoted/Expr.scala index 59155793c59d..c72c35e75d28 100644 --- a/library/src-bootstrapped/scala/internal/quoted/Expr.scala +++ b/library/src-bootstrapped/scala/internal/quoted/Expr.scala @@ -58,16 +58,4 @@ object Expr { qctx.asInstanceOf[QuoteContextInternal].exprMatch(scrutineeExpr, patternExpr).asInstanceOf[Option[Tup]] } - /** Returns a null expresssion equivalent to `'{null}` */ - def `null`: QuoteContext ?=> quoted.Expr[Null] = (using qctx) => { - import qctx.reflect._ - Literal(Constant.Null()).seal.asInstanceOf[quoted.Expr[Null]] - } - - /** Returns a unit expresssion equivalent to `'{}` or `'{()}` */ - def Unit: QuoteContext ?=> quoted.Expr[Unit] = (using qctx) => { - import qctx.reflect._ - Literal(Constant.Unit()).seal.asInstanceOf[quoted.Expr[Unit]] - } - } diff --git a/library/src/scala/tasty/Reflection.scala b/library/src/scala/tasty/Reflection.scala index 5055e27d1cab..30c8a788c7ab 100644 --- a/library/src/scala/tasty/Reflection.scala +++ b/library/src/scala/tasty/Reflection.scala @@ -162,10 +162,15 @@ trait Reflection { reflection => /** Does this tree represent a valid expression? */ def isExpr: Boolean + + /** Convert this tree to an `quoted.Expr[Any]` if the tree is a valid expression or throws */ + def asExpr: scala.quoted.Expr[Any] end extension /** Convert this tree to an `quoted.Expr[T]` if the tree is a valid expression or throws */ extension [T](self: Tree) + // FIXME: remove QuoteContext from parameters + // TODO: Move Reflection inside QuoteContext as it can never be instantiated outside a QuoteContext def asExprOf(using scala.quoted.Type[T])(using QuoteContext): scala.quoted.Expr[T] } From f03c23d2da244a0ab5b7cc162f1d7924e0c24449 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sun, 8 Nov 2020 13:42:55 +0100 Subject: [PATCH 2/3] Align TypeRepr module and method trait names --- .../src/dotty/tools/dotc/quoted/QuoteContextImpl.scala | 6 +++--- library/src/scala/tasty/Reflection.scala | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/quoted/QuoteContextImpl.scala b/compiler/src/dotty/tools/dotc/quoted/QuoteContextImpl.scala index ec6184bc8ce6..00a524e0c29c 100644 --- a/compiler/src/dotty/tools/dotc/quoted/QuoteContextImpl.scala +++ b/compiler/src/dotty/tools/dotc/quoted/QuoteContextImpl.scala @@ -1567,7 +1567,7 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, scala.intern type TypeRepr = dotc.core.Types.Type - object TypeRepr extends TypeModule: + object TypeRepr extends TypeReprModule: def of[T <: AnyKind](using qtype: scala.quoted.Type[T]): TypeRepr = qtype.asInstanceOf[scala.internal.quoted.Type[TypeTree]].typeTree.tpe def typeConstructorOf(clazz: Class[?]): TypeRepr = @@ -1592,7 +1592,7 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, scala.intern dotc.core.Symbols.getClassIfDefined(clazz.getCanonicalName).typeRef end TypeRepr - object TypeMethodsImpl extends TypeMethods: + object TypeReprMethodsImpl extends TypeReprMethods: extension (self: TypeRepr): def showExtractors: String = Extractors.showType(using QuoteContextImpl.this)(self) @@ -1639,7 +1639,7 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, scala.intern def appliedTo(targs: List[TypeRepr]): TypeRepr = dotc.core.Types.decorateTypeApplications(self).appliedTo(targs) end extension - end TypeMethodsImpl + end TypeReprMethodsImpl type ConstantType = dotc.core.Types.ConstantType diff --git a/library/src/scala/tasty/Reflection.scala b/library/src/scala/tasty/Reflection.scala index 30c8a788c7ab..b52d52e468b4 100644 --- a/library/src/scala/tasty/Reflection.scala +++ b/library/src/scala/tasty/Reflection.scala @@ -1746,9 +1746,9 @@ trait Reflection { reflection => /** A type, type constructors, type bounds or NoPrefix */ type TypeRepr - val TypeRepr: TypeModule + val TypeRepr: TypeReprModule - trait TypeModule { this: TypeRepr.type => + trait TypeReprModule { this: TypeRepr.type => /** Returns the type or kind (TypeRepr) of T */ def of[T <: AnyKind](using qtype: scala.quoted.Type[T]): TypeRepr @@ -1756,10 +1756,10 @@ trait Reflection { reflection => def typeConstructorOf(clazz: Class[?]): TypeRepr } - given TypeMethods as TypeMethods = TypeMethodsImpl - protected val TypeMethodsImpl: TypeMethods + given TypeReprMethods as TypeReprMethods = TypeReprMethodsImpl + protected val TypeReprMethodsImpl: TypeReprMethods - trait TypeMethods { + trait TypeReprMethods { extension (self: TypeRepr): /** Shows the tree as extractors */ From bff91a804ce74692df1844f2faaf58dc9465206f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sun, 8 Nov 2020 13:38:09 +0100 Subject: [PATCH 3/3] Pickle primitive classes using `Reflection.TypeRepr.typeConstructorOf` directly This removes the need of some extra helper methods in the internal package. --- .../tools/dotc/quoted/QuoteContextImpl.scala | 4 ++- .../tools/dotc/transform/ReifyQuotes.scala | 24 +++++++++++++-- .../scala/internal/quoted/Type.scala | 30 ------------------- library/src/scala/tasty/Reflection.scala | 11 +++++++ 4 files changed, 36 insertions(+), 33 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/quoted/QuoteContextImpl.scala b/compiler/src/dotty/tools/dotc/quoted/QuoteContextImpl.scala index 00a524e0c29c..9875814ebaf2 100644 --- a/compiler/src/dotty/tools/dotc/quoted/QuoteContextImpl.scala +++ b/compiler/src/dotty/tools/dotc/quoted/QuoteContextImpl.scala @@ -1603,7 +1603,9 @@ class QuoteContextImpl private (ctx: Context) extends QuoteContext, scala.intern def showAnsiColored: String = SourceCode.showType(using QuoteContextImpl.this)(self)(SyntaxHighlight.ANSI) - def seal: scala.quoted.Type[_] = + def seal: scala.quoted.Type[_] = self.asType + + def asType: scala.quoted.Type[?] = new scala.internal.quoted.Type(Inferred(self), QuoteContextImpl.this.hashCode) def =:=(that: TypeRepr): Boolean = self =:= that diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index cb076430d051..58769748b420 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -199,10 +199,30 @@ class ReifyQuotes extends MacroTransform { ref(unpickleMeth).appliedToType(originalTp).appliedTo(pickledQuote) } - def taggedType(sym: Symbol) = ref(defn.InternalQuotedTypeModule).select(sym.name.toTermName) + /** Encode quote using Reflection.TypeRepr.typeConstructorOf + * + * Generate the code + * ```scala + * qctx => qctx.reflect.TypeReprMethods.asType( + * qctx.reflect.TypeRepr.typeConstructorOf(classOf[]]) + * ).asInstanceOf[scala.quoted.Type[]] + * ``` + * this closure is always applied directly to the actual context and the BetaReduce phase removes it. + */ + def taggedType() = + val typeType = defn.QuotedTypeClass.typeRef.appliedTo(body.tpe) + val classTree = TypeApply(ref(defn.Predef_classOf.termRef), body :: Nil) + val tpe = MethodType(defn.QuoteContextClass.typeRef :: Nil, typeType) + val meth = newSymbol(ctx.owner, UniqueName.fresh(nme.ANON_FUN), Synthetic | Method, tpe) + def mkConst(tss: List[List[Tree]]) = { + val reflect = tss.head.head.select("reflect".toTermName) + val typeRepr = reflect.select("TypeRepr".toTermName).select("typeConstructorOf".toTermName).appliedTo(classTree) + reflect.select("TypeReprMethods".toTermName).select("asType".toTermName).appliedTo(typeRepr).asInstance(typeType) + } + Closure(meth, mkConst).withSpan(body.span) if (isType) { - if (splices.isEmpty && body.symbol.isPrimitiveValueClass) taggedType(body.symbol) + if (splices.isEmpty && body.symbol.isPrimitiveValueClass) taggedType() else pickleAsTasty() } else getLiteral(body) match { diff --git a/library/src-bootstrapped/scala/internal/quoted/Type.scala b/library/src-bootstrapped/scala/internal/quoted/Type.scala index 21df0a626978..e1a45d4afa6c 100644 --- a/library/src-bootstrapped/scala/internal/quoted/Type.scala +++ b/library/src-bootstrapped/scala/internal/quoted/Type.scala @@ -41,34 +41,4 @@ object Type { qctx.asInstanceOf[QuoteContextInternal].typeMatch(scrutineeType, patternType).asInstanceOf[Option[Tup]] } - - // TODO generalize following optimizations for all classes without parameters - - def Unit: QuoteContext ?=> quoted.Type[Unit] = - qctx.reflect.TypeRepr.typeConstructorOf(classOf[Unit]).seal.asInstanceOf[quoted.Type[Unit]] - - def Boolean: QuoteContext ?=> quoted.Type[Boolean] = - qctx.reflect.TypeRepr.typeConstructorOf(classOf[Boolean]).seal.asInstanceOf[quoted.Type[Boolean]] - - def Byte: QuoteContext ?=> quoted.Type[Byte] = - qctx.reflect.TypeRepr.typeConstructorOf(classOf[Byte]).seal.asInstanceOf[quoted.Type[Byte]] - - def Char: QuoteContext ?=> quoted.Type[Char] = - qctx.reflect.TypeRepr.typeConstructorOf(classOf[Char]).seal.asInstanceOf[quoted.Type[Char]] - - def Short: QuoteContext ?=> quoted.Type[Short] = - qctx.reflect.TypeRepr.typeConstructorOf(classOf[Short]).seal.asInstanceOf[quoted.Type[Short]] - - def Int: QuoteContext ?=> quoted.Type[Int] = - qctx.reflect.TypeRepr.typeConstructorOf(classOf[Int]).seal.asInstanceOf[quoted.Type[Int]] - - def Long: QuoteContext ?=> quoted.Type[Long] = - qctx.reflect.TypeRepr.typeConstructorOf(classOf[Long]).seal.asInstanceOf[quoted.Type[Long]] - - def Float: QuoteContext ?=> quoted.Type[Float] = - qctx.reflect.TypeRepr.typeConstructorOf(classOf[Float]).seal.asInstanceOf[quoted.Type[Float]] - - def Double: QuoteContext ?=> quoted.Type[Double] = - qctx.reflect.TypeRepr.typeConstructorOf(classOf[Double]).seal.asInstanceOf[quoted.Type[Double]] - } diff --git a/library/src/scala/tasty/Reflection.scala b/library/src/scala/tasty/Reflection.scala index b52d52e468b4..2c1abf2b6c53 100644 --- a/library/src/scala/tasty/Reflection.scala +++ b/library/src/scala/tasty/Reflection.scala @@ -1774,6 +1774,17 @@ trait Reflection { reflection => /** Convert `TypeRepr` to an `quoted.Type[_]` */ def seal: scala.quoted.Type[_] + /** Convert this `TypeRepr` to an `Type[?]` + * + * Usage: + * ``` + * typeRepr.asType match + * case '[$t] => + * '{ val x: t = ... } + * ``` + */ + def asType: scala.quoted.Type[?] + /** Is `self` type the same as `that` type? * This is the case iff `self <:< that` and `that <:< self`. */