@@ -14,20 +14,26 @@ import dotty.tools.dotc.core.StdNames.nme
1414import dotty .tools .dotc .core .Symbols .defn
1515import dotty .tools .dotc .core .Types .ExprType
1616import dotty .tools .dotc .core .quoted .PickledQuotes
17+ import dotty .tools .dotc .tastyreflect .ReflectionImpl
1718import dotty .tools .dotc .transform .Staging
1819import dotty .tools .dotc .util .Spans .Span
1920import dotty .tools .dotc .util .SourceFile
2021import dotty .tools .io .{Path , VirtualFile }
2122
22- import scala .quoted .{Expr , Type }
23+ import scala .annotation .tailrec
24+ import scala .concurrent .Promise
25+ import scala .quoted .{Expr , QuoteContext , Type }
2326
2427/** Compiler that takes the contents of a quoted expression `expr` and produces
2528 * a class file with `class ' { def apply: Object = expr }`.
2629 */
2730class QuoteCompiler extends Compiler {
2831
32+ /** Either `Left` with name of the classfile generated or `Right` with the value contained in the expression */
33+ private [this ] var result : Either [String , Any ] = null
34+
2935 override protected def frontendPhases : List [List [Phase ]] =
30- List (List (new QuotedFrontend (putInClass = true ) ))
36+ List (List (new QuotedFrontend ))
3137
3238 override protected def picklerPhases : List [List [Phase ]] =
3339 List (List (new Staging ))
@@ -40,58 +46,67 @@ class QuoteCompiler extends Compiler {
4046 def outputClassName : TypeName = " Generated$Code$From$Quoted" .toTypeName
4147
4248 /** Frontend that receives a scala.quoted.Expr or scala.quoted.Type as input */
43- class QuotedFrontend ( putInClass : Boolean ) extends Phase {
49+ class QuotedFrontend extends Phase {
4450 import tpd ._
4551
52+
4653 def phaseName : String = " quotedFrontend"
4754
4855 override def runOn (units : List [CompilationUnit ])(implicit ctx : Context ): List [CompilationUnit ] = {
49- units.map {
56+ units.flatMap {
5057 case exprUnit : ExprCompilationUnit =>
51- val tree =
52- if (putInClass) inClass(exprUnit.expr)
53- else PickledQuotes .quotedExprToTree(exprUnit.expr)
54- val source = SourceFile .virtual(" <quoted.Expr>" , " " )
55- CompilationUnit (source, tree, forceTrees = true )
56- case typeUnit : TypeCompilationUnit =>
57- assert(! putInClass)
58- val tree = PickledQuotes .quotedTypeToTree(typeUnit.tpe)
59- val source = SourceFile .virtual(" <quoted.Type>" , " " )
60- CompilationUnit (source, tree, forceTrees = true )
58+ val pos = Span (0 )
59+ val assocFile = new VirtualFile (" <quote>" )
60+
61+ // Places the contents of expr in a compilable tree for a class with the following format.
62+ // `package __root__ { class ' { def apply: Any = <expr> } }`
63+ val cls = ctx.newCompleteClassSymbol(defn.RootClass , outputClassName, EmptyFlags ,
64+ defn.ObjectType :: Nil , newScope, coord = pos, assocFile = assocFile).entered.asClass
65+ cls.enter(ctx.newDefaultConstructor(cls), EmptyScope )
66+ val meth = ctx.newSymbol(cls, nme.apply, Method , ExprType (defn.AnyType ), coord = pos).entered
67+
68+ val quoted = PickledQuotes .quotedExprToTree(checkValidRunExpr(exprUnit.exprBuilder.apply(new QuoteContext (ReflectionImpl (ctx)))))(ctx.withOwner(meth))
69+
70+ getLiteral(quoted) match {
71+ case Some (value) =>
72+ result = Right (value)
73+ None // Stop copilation here we already have the result
74+ case None =>
75+ val run = DefDef (meth, quoted)
76+ val classTree = ClassDef (cls, DefDef (cls.primaryConstructor.asTerm), run :: Nil )
77+ val tree = PackageDef (ref(defn.RootPackage ).asInstanceOf [Ident ], classTree :: Nil ).withSpan(pos)
78+ val source = SourceFile .virtual(" <quoted.Expr>" , " " )
79+ result = Left (outputClassName.toString)
80+ Some (CompilationUnit (source, tree, forceTrees = true ))
81+ }
6182 }
6283 }
6384
64- /** Places the contents of expr in a compilable tree for a class
65- * with the following format.
66- * `package __root__ { class ' { def apply: Any = <expr> } }`
67- */
68- private def inClass (expr : Expr [_])(implicit ctx : Context ): Tree = {
69- val pos = Span (0 )
70- val assocFile = new VirtualFile (" <quote>" )
71-
72- val cls = ctx.newCompleteClassSymbol(defn.RootClass , outputClassName, EmptyFlags ,
73- defn.ObjectType :: Nil , newScope, coord = pos, assocFile = assocFile).entered.asClass
74- cls.enter(ctx.newDefaultConstructor(cls), EmptyScope )
75- val meth = ctx.newSymbol(cls, nme.apply, Method , ExprType (defn.AnyType ), coord = pos).entered
76-
77- val quoted = PickledQuotes .quotedExprToTree(expr)(ctx.withOwner(meth))
85+ private def checkValidRunExpr (expr : Expr [_]): Expr [_] = expr match {
86+ case expr : scala.internal.quoted.TastyTreeExpr [Tree ] @ unchecked =>
87+ throw new Exception (" Cannot call `Expr.run` on an `Expr` that comes from a macro argument." )
88+ case _ => expr
89+ }
7890
79- val run = DefDef (meth, quoted)
80- val classTree = ClassDef (cls, DefDef (cls.primaryConstructor.asTerm), run :: Nil )
81- PackageDef (ref(defn.RootPackage ).asInstanceOf [Ident ], classTree :: Nil ).withSpan(pos)
91+ /** Get the literal value if this tree only contains a literal tree */
92+ @ tailrec private def getLiteral (tree : Tree ): Option [Any ] = tree match {
93+ case Literal (lit) => Some (lit.value)
94+ case Block (Nil , expr) => getLiteral(expr)
95+ case Inlined (_, Nil , expr) => getLiteral(expr)
96+ case _ => None
8297 }
8398
8499 def run (implicit ctx : Context ): Unit = unsupported(" run" )
85100 }
86101
87- class ExprRun (comp : Compiler , ictx : Context ) extends Run (comp, ictx) {
88- def compileExpr (expr : Expr [_]): Unit = {
89- val units = new ExprCompilationUnit (expr) :: Nil
90- compileUnits(units)
91- }
92- def compileType (tpe : Type [_]): Unit = {
93- val units = new TypeCompilationUnit (tpe) :: Nil
102+ class ExprRun (comp : QuoteCompiler , ictx : Context ) extends Run (comp, ictx) {
103+ /** Unpickle and optionally compile the expression.
104+ * Returns either `Left` with name of the classfile generated or `Right` with the value contained in the expression.
105+ */
106+ def compileExpr (exprBuilder : QuoteContext => Expr [_]): Either [String , Any ] = {
107+ val units = new ExprCompilationUnit (exprBuilder) :: Nil
94108 compileUnits(units)
109+ result
95110 }
96111 }
97112}
0 commit comments