Skip to content

Commit 8212f60

Browse files
Merge pull request #6700 from dotty-staging/add-quote-context
Add quote context
2 parents c3dbd50 + 68387bb commit 8212f60

File tree

103 files changed

+874
-753
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

103 files changed

+874
-753
lines changed

compiler/src/dotty/tools/dotc/core/Definitions.scala

+1
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,7 @@ class Definitions {
773773
@threadUnsafe lazy val InternalQuoted_patternHoleR: TermRef = InternalQuotedModule.requiredMethodRef("patternHole")
774774
def InternalQuoted_patternHole(implicit ctx: Context): Symbol = InternalQuoted_patternHoleR.symbol
775775
@threadUnsafe lazy val InternalQuoted_patternBindHoleAnnot: ClassSymbol = InternalQuotedModule.requiredClass("patternBindHole")
776+
@threadUnsafe lazy val InternalQuoted_QuoteTypeTagAnnot: ClassSymbol = InternalQuotedModule.requiredClass("quoteTypeTag")
776777

777778
@threadUnsafe lazy val InternalQuotedMatcherModuleRef: TermRef = ctx.requiredModuleRef("scala.internal.quoted.Matcher")
778779
def InternalQuotedMatcherModule(implicit ctx: Context): Symbol = InternalQuotedMatcherModuleRef.symbol

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

+2-4
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ package dotty.tools.dotc.quoted
33
import dotty.tools.dotc.CompilationUnit
44
import dotty.tools.dotc.util.NoSource
55

6-
import scala.quoted.Expr
6+
import scala.quoted._
77

88
/* Compilation unit containing the contents of a quoted expression */
9-
class ExprCompilationUnit(val expr: Expr[_]) extends CompilationUnit(NoSource) {
10-
override def toString: String = s"Expr($expr)"
11-
}
9+
class ExprCompilationUnit(val exprBuilder: QuoteContext => Expr[_]) extends CompilationUnit(NoSource)

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

+53-38
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,26 @@ import dotty.tools.dotc.core.StdNames.nme
1414
import dotty.tools.dotc.core.Symbols.defn
1515
import dotty.tools.dotc.core.Types.ExprType
1616
import dotty.tools.dotc.core.quoted.PickledQuotes
17+
import dotty.tools.dotc.tastyreflect.ReflectionImpl
1718
import dotty.tools.dotc.transform.Staging
1819
import dotty.tools.dotc.util.Spans.Span
1920
import dotty.tools.dotc.util.SourceFile
2021
import 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
*/
2730
class 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
}

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

-19
This file was deleted.

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

+12-48
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import dotty.tools.dotc.tastyreflect.ReflectionImpl
77
import dotty.tools.io.{AbstractFile, Directory, PlainDirectory, VirtualDirectory}
88
import dotty.tools.repl.AbstractFileClassLoader
99
import dotty.tools.dotc.reporting._
10-
import scala.quoted.{Expr, Type}
10+
import scala.quoted._
1111
import scala.quoted.Toolbox
1212
import java.net.URLClassLoader
1313

@@ -20,7 +20,7 @@ class QuoteDriver(appClassloader: ClassLoader) extends Driver {
2020

2121
private[this] val contextBase: ContextBase = new ContextBase
2222

23-
def run[T](expr: Expr[T], settings: Toolbox.Settings): T = {
23+
def run[T](exprBuilder: QuoteContext => Expr[T], settings: Toolbox.Settings): T = {
2424
val outDir: AbstractFile = settings.outDir match {
2525
case Some(out) =>
2626
val dir = Directory(out)
@@ -33,56 +33,21 @@ class QuoteDriver(appClassloader: ClassLoader) extends Driver {
3333
val (_, ctx0: Context) = setup(settings.compilerArgs.toArray :+ "dummy.scala", initCtx.fresh)
3434
val ctx = setToolboxSettings(ctx0.fresh.setSetting(ctx0.settings.outputDir, outDir), settings)
3535

36-
val driver = new QuoteCompiler
37-
driver.newRun(ctx).compileExpr(expr)
36+
new QuoteCompiler().newRun(ctx).compileExpr(exprBuilder) match {
37+
case Right(value) =>
38+
value.asInstanceOf[T]
3839

39-
assert(!ctx.reporter.hasErrors)
40+
case Left(classname) =>
41+
assert(!ctx.reporter.hasErrors)
4042

41-
val classLoader = new AbstractFileClassLoader(outDir, appClassloader)
43+
val classLoader = new AbstractFileClassLoader(outDir, appClassloader)
4244

43-
val clazz = classLoader.loadClass(driver.outputClassName.toString)
44-
val method = clazz.getMethod("apply")
45-
val inst = clazz.getConstructor().newInstance()
45+
val clazz = classLoader.loadClass(classname)
46+
val method = clazz.getMethod("apply")
47+
val inst = clazz.getConstructor().newInstance()
4648

47-
method.invoke(inst).asInstanceOf[T]
48-
}
49-
50-
private def doShow(tree: Tree, ctx: Context): String = {
51-
implicit val c: Context = ctx
52-
val tree1 =
53-
if (ctx.settings.YshowRawQuoteTrees.value) tree
54-
else (new TreeCleaner).transform(tree)
55-
ReflectionImpl.showTree(tree1)
56-
}
57-
58-
def show(expr: Expr[_], settings: Toolbox.Settings): String =
59-
withTree(expr, doShow, settings)
60-
61-
def show(tpe: Type[_], settings: Toolbox.Settings): String =
62-
withTypeTree(tpe, doShow, settings)
63-
64-
def withTree[T](expr: Expr[_], f: (Tree, Context) => T, settings: Toolbox.Settings): T = {
65-
val ctx = setToolboxSettings(setup(settings.compilerArgs.toArray :+ "dummy.scala", initCtx.fresh)._2.fresh, settings)
66-
67-
var output: Option[T] = None
68-
def registerTree(tree: tpd.Tree)(ctx: Context): Unit = {
69-
assert(output.isEmpty)
70-
output = Some(f(tree, ctx))
71-
}
72-
new QuoteDecompiler(registerTree).newRun(ctx).compileExpr(expr)
73-
output.getOrElse(throw new Exception("Could not extract " + expr))
74-
}
75-
76-
def withTypeTree[T](tpe: Type[_], f: (TypTree, Context) => T, settings: Toolbox.Settings): T = {
77-
val ctx = setToolboxSettings(setup(settings.compilerArgs.toArray :+ "dummy.scala", initCtx.fresh)._2.fresh, settings)
78-
79-
var output: Option[T] = None
80-
def registerTree(tree: tpd.Tree)(ctx: Context): Unit = {
81-
assert(output.isEmpty)
82-
output = Some(f(tree.asInstanceOf[TypTree], ctx))
49+
method.invoke(inst).asInstanceOf[T]
8350
}
84-
new QuoteDecompiler(registerTree).newRun(ctx).compileType(tpe)
85-
output.getOrElse(throw new Exception("Could not extract " + tpe))
8651
}
8752

8853
override def initCtx: Context = {
@@ -92,7 +57,6 @@ class QuoteDriver(appClassloader: ClassLoader) extends Driver {
9257
}
9358

9459
private def setToolboxSettings(ctx: FreshContext, settings: Toolbox.Settings): ctx.type = {
95-
ctx.setSetting(ctx.settings.color, if (settings.color) "always" else "never")
9660
ctx.setSetting(ctx.settings.YshowRawQuoteTrees, settings.showRawTree)
9761
// An error in the generated code is a bug in the compiler
9862
// Setting the throwing reporter however will report any exception

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

-38
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
11
package dotty.tools.dotc.quoted
22

3-
import dotty.tools.dotc.ast.tpd
4-
53
import scala.quoted._
6-
import scala.internal.quoted.{LiftedExpr, TastyTreeExpr}
74

85
/** Default runners for quoted expressions */
96
object ToolboxImpl {
10-
import tpd._
117

128
/** Create a new instance of the toolbox using the the classloader of the application.
139
*
@@ -19,18 +15,10 @@ object ToolboxImpl {
1915

2016
private[this] val driver: QuoteDriver = new QuoteDriver(appClassloader)
2117

22-
def run[T](expr: Expr[T]): T = expr match {
23-
case expr: LiftedExpr[T] =>
24-
expr.value
25-
case expr: TastyTreeExpr[Tree] @unchecked =>
26-
throw new Exception("Cannot call `Expr.run` on an `Expr` that comes from a macro argument.")
27-
case _ =>
28-
synchronized(driver.run(expr, settings))
18+
def run[T](exprBuilder: QuoteContext => Expr[T]): T = synchronized {
19+
driver.run(exprBuilder, settings)
2920
}
3021

31-
def show[T](expr: Expr[T]): String = synchronized(driver.show(expr, settings))
32-
33-
def show[T](tpe: Type[T]): String = synchronized(driver.show(tpe, settings))
3422
}
3523

3624
}

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

-49
This file was deleted.

0 commit comments

Comments
 (0)