@@ -14,20 +14,26 @@ import dotty.tools.dotc.core.StdNames.nme
14
14
import dotty .tools .dotc .core .Symbols .defn
15
15
import dotty .tools .dotc .core .Types .ExprType
16
16
import dotty .tools .dotc .core .quoted .PickledQuotes
17
+ import dotty .tools .dotc .tastyreflect .ReflectionImpl
17
18
import dotty .tools .dotc .transform .Staging
18
19
import dotty .tools .dotc .util .Spans .Span
19
20
import dotty .tools .dotc .util .SourceFile
20
21
import dotty .tools .io .{Path , VirtualFile }
21
22
22
- import scala .quoted .{Expr , Type }
23
+ import scala .annotation .tailrec
24
+ import scala .concurrent .Promise
25
+ import scala .quoted .{Expr , QuoteContext , Type }
23
26
24
27
/** Compiler that takes the contents of a quoted expression `expr` and produces
25
28
* a class file with `class ' { def apply: Object = expr }`.
26
29
*/
27
30
class QuoteCompiler extends Compiler {
28
31
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
+
29
35
override protected def frontendPhases : List [List [Phase ]] =
30
- List (List (new QuotedFrontend (putInClass = true ) ))
36
+ List (List (new QuotedFrontend ))
31
37
32
38
override protected def picklerPhases : List [List [Phase ]] =
33
39
List (List (new Staging ))
@@ -40,58 +46,67 @@ class QuoteCompiler extends Compiler {
40
46
def outputClassName : TypeName = " Generated$Code$From$Quoted" .toTypeName
41
47
42
48
/** 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 {
44
50
import tpd ._
45
51
52
+
46
53
def phaseName : String = " quotedFrontend"
47
54
48
55
override def runOn (units : List [CompilationUnit ])(implicit ctx : Context ): List [CompilationUnit ] = {
49
- units.map {
56
+ units.flatMap {
50
57
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
+ }
61
82
}
62
83
}
63
84
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
+ }
78
90
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
82
97
}
83
98
84
99
def run (implicit ctx : Context ): Unit = unsupported(" run" )
85
100
}
86
101
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
94
108
compileUnits(units)
109
+ result
95
110
}
96
111
}
97
112
}
0 commit comments