Skip to content

Commit 83265b8

Browse files
committed
WIP Try to fix management of compiler instances of quoted.Expr run and show
Also fix some issues along the way.
1 parent 0fcbfdb commit 83265b8

File tree

201 files changed

+1067
-978
lines changed

Some content is hidden

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

201 files changed

+1067
-978
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ class Compiler {
4444
/** Phases dealing with TASTY tree pickling and unpickling */
4545
protected def picklerPhases: List[List[Phase]] =
4646
List(new Pickler) :: // Generate TASTY info
47-
List(new Staging) :: // Inline calls, expand macros and turn quoted trees into explicit run-time data structures
47+
List(new StagingPhase) :: // Inline calls, expand macros and turn quoted trees into explicit run-time data structures
4848
Nil
4949

5050
/** Phases dealing with the transformation from pickled trees to backend trees */

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ class ScalaSettings extends Settings.SettingGroup {
116116
val YprintDebug: Setting[Boolean] = BooleanSetting("-Yprint-debug", "when printing trees, print some extra information useful for debugging.")
117117
val YprintDebugOwners: Setting[Boolean] = BooleanSetting("-Yprint-debug-owners", "when printing trees, print owners of definitions.")
118118
val YshowPrintErrors: Setting[Boolean] = BooleanSetting("-Yshow-print-errors", "don't suppress exceptions thrown during tree printing.")
119-
val YshowRawQuoteTrees: Setting[Boolean] = BooleanSetting("-Yshow-raw-tree", "don't remove quote artifacts")
119+
val YshowRawQuoteTrees: Setting[Boolean] = BooleanSetting("-Yshow-raw-quote-tree", "don't remove quote artifacts")
120120
val YtestPickler: Setting[Boolean] = BooleanSetting("-Ytest-pickler", "self-test for pickling functionality; should be used with -Ystop-after:pickler")
121121
val YcheckReentrant: Setting[Boolean] = BooleanSetting("-Ycheck-reentrant", "check that compiled program does not contain vars that can be accessed from a global root.")
122122
val YdropComments: Setting[Boolean] = BooleanSetting("-Ydrop-comments", "Drop comments when scanning source files.")

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

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -690,16 +690,30 @@ class Definitions {
690690
lazy val QuotedLiftableType: TypeRef = ctx.requiredClassRef("scala.quoted.Liftable")
691691
def QuotedLiftableClass(implicit ctx: Context): ClassSymbol = QuotedLiftableType.symbol.asClass
692692

693-
def Unpickler_unpickleExpr: TermSymbol = ctx.requiredMethod("scala.runtime.quoted.Unpickler.unpickleExpr")
694-
def Unpickler_liftedExpr: TermSymbol = ctx.requiredMethod("scala.runtime.quoted.Unpickler.liftedExpr")
695-
def Unpickler_unpickleType: TermSymbol = ctx.requiredMethod("scala.runtime.quoted.Unpickler.unpickleType")
693+
lazy val UnpicklerModuleRef: TermRef = ctx.requiredModuleRef("scala.runtime.quoted.Unpickler")
694+
def UnpicklerModule(implicit ctx: Context): Symbol = UnpicklerModuleRef.symbol
695+
lazy val UnpicklerType: TypeRef = ctx.requiredClassRef("scala.runtime.quoted.Unpickler")
696+
def UnpicklerClass(implicit ctx: Context): ClassSymbol = UnpicklerType.symbol.asClass
697+
698+
lazy val Unpickler_unpickleExprR: TermRef = UnpicklerModule.requiredMethodRef("unpickleExpr")
699+
def Unpickler_unpickleExpr: Symbol = Unpickler_unpickleExprR.symbol
700+
lazy val Unpickler_liftedExprR: TermRef = UnpicklerModule.requiredMethodRef("liftedExpr")
701+
def Unpickler_liftedExpr: Symbol = Unpickler_liftedExprR.symbol
702+
lazy val Unpickler_unpickleTypeR: TermRef = UnpicklerModule.requiredMethodRef("unpickleType")
703+
def Unpickler_unpickleType: Symbol = Unpickler_unpickleTypeR.symbol
696704

697705
lazy val TastyTastyType: TypeRef = ctx.requiredClassRef("scala.tasty.Tasty")
698706
def TastyTastyClass(implicit ctx: Context): ClassSymbol = TastyTastyType.symbol.asClass
699707

700708
lazy val TastyTastyModule: TermSymbol = ctx.requiredModule("scala.tasty.Tasty")
701709
lazy val TastyTasty_macroContext: TermSymbol = TastyTastyModule.requiredMethod("macroContext")
702710

711+
lazy val StagingType: TypeRef = ctx.requiredClassRef("scala.quoted.Staging")
712+
def StagingClass(implicit ctx: Context): ClassSymbol = StagingType.symbol.asClass
713+
714+
lazy val StagingModule: TermSymbol = ctx.requiredModule("scala.quoted.Staging")
715+
lazy val Staging_macroContext: TermSymbol = StagingModule.requiredMethod("macroContext")
716+
703717
lazy val EqType: TypeRef = ctx.requiredClassRef("scala.Eq")
704718
def EqClass(implicit ctx: Context): ClassSymbol = EqType.symbol.asClass
705719
def EqModule(implicit ctx: Context): Symbol = EqClass.companionModule

compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,40 +34,38 @@ object PickledQuotes {
3434

3535
/** Transform the expression into its fully spliced Tree */
3636
def quotedExprToTree[T](expr: quoted.Expr[T])(implicit ctx: Context): Tree = expr match {
37-
case expr: TastyExpr[_] =>
38-
val unpickled = unpickleExpr(expr)
39-
val force = new TreeTraverser {
40-
def traverse(tree: tpd.Tree)(implicit ctx: Context): Unit = traverseChildren(tree)
41-
}
42-
force.traverse(unpickled)
43-
unpickled
4437
case expr: LiftedExpr[T] =>
4538
expr.value match {
4639
case value: Class[_] => ref(defn.Predef_classOf).appliedToType(classToType(value))
4740
case value => Literal(Constant(value))
4841
}
49-
case expr: TastyTreeExpr[Tree] @unchecked => expr.tree
42+
case expr: TastyTreeExpr[Tree] @unchecked => healOwner(expr.tree)
5043
case expr: FunctionAppliedTo[_, _] =>
5144
functionAppliedTo(quotedExprToTree(expr.f), quotedExprToTree(expr.x))
5245
}
5346

5447
/** Transform the expression into its fully spliced TypeTree */
5548
def quotedTypeToTree(expr: quoted.Type[_])(implicit ctx: Context): Tree = expr match {
56-
case expr: TastyType[_] => unpickleType(expr)
5749
case expr: TaggedType[_] => classTagToTypeTree(expr.ct)
58-
case expr: TreeType[Tree] @unchecked => expr.typeTree
50+
case expr: TreeType[Tree] @unchecked => healOwner(expr.typeTree)
5951
}
6052

6153
/** Unpickle the tree contained in the TastyExpr */
62-
private def unpickleExpr(expr: TastyExpr[_])(implicit ctx: Context): Tree = {
63-
val tastyBytes = TastyString.unpickle(expr.tasty)
64-
unpickle(tastyBytes, expr.args, isType = false)(ctx.addMode(Mode.ReadPositions))
54+
def unpickleExpr(tasty: scala.runtime.quoted.Unpickler.Pickled, args: Seq[Any])(implicit ctx: Context): Tree = {
55+
val tastyBytes = TastyString.unpickle(tasty)
56+
val unpickled = unpickle(tastyBytes, args, isType = false)(ctx.addMode(Mode.ReadPositions))
57+
// TODO force should not be done here it should be in run and show
58+
val force = new TreeTraverser {
59+
def traverse(tree: tpd.Tree)(implicit ctx: Context): Unit = traverseChildren(tree)
60+
}
61+
force.traverse(unpickled)
62+
unpickled
6563
}
6664

6765
/** Unpickle the tree contained in the TastyType */
68-
private def unpickleType(ttpe: TastyType[_])(implicit ctx: Context): Tree = {
69-
val tastyBytes = TastyString.unpickle(ttpe.tasty)
70-
unpickle(tastyBytes, ttpe.args, isType = true)(ctx.addMode(Mode.ReadPositions))
66+
def unpickleType(tasty: scala.runtime.quoted.Unpickler.Pickled, args: Seq[Any])(implicit ctx: Context): Tree = {
67+
val tastyBytes = TastyString.unpickle(tasty)
68+
unpickle(tastyBytes, args, isType = true)(ctx.addMode(Mode.ReadPositions))
7169
}
7270

7371
// TASTY picklingtests/pos/quoteTest.scala
@@ -170,4 +168,21 @@ object PickledQuotes {
170168
}
171169
} else ctx.getClassIfDefined(clazz.getCanonicalName).typeRef
172170
}
171+
172+
/** Make sure that the owner of this tree is `ctx.owner` */
173+
private def healOwner(tree: Tree)(implicit ctx: Context): Tree = {
174+
val getCurrentOwner = new TreeAccumulator[Option[Symbol]] {
175+
def apply(x: Option[Symbol], tree: tpd.Tree)(implicit ctx: Context): Option[Symbol] = {
176+
if (x.isDefined) x
177+
else tree match {
178+
case tree: DefTree => Some(tree.symbol.owner)
179+
case _ => foldOver(x, tree)
180+
}
181+
}
182+
}
183+
getCurrentOwner(None, tree) match {
184+
case Some(owner) if owner != ctx.owner => tree.changeOwner(owner, ctx.owner)
185+
case _ => tree
186+
}
187+
}
173188
}

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

Lines changed: 2 additions & 4 deletions
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.{Expr, Staging}
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 expr: Staging => Expr[Any]) extends CompilationUnit(NoSource)

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

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ import dotty.tools.dotc.core.StdNames.nme
1313
import dotty.tools.dotc.core.Symbols.defn
1414
import dotty.tools.dotc.core.Types.ExprType
1515
import dotty.tools.dotc.core.quoted.PickledQuotes
16-
import dotty.tools.dotc.transform.Staging
16+
import dotty.tools.dotc.transform.StagingPhase
17+
import dotty.tools.dotc.tastyreflect.TastyImpl
1718
import dotty.tools.dotc.typer.FrontEnd
1819
import dotty.tools.dotc.util.Positions.Position
1920
import dotty.tools.dotc.util.SourceFile
2021
import dotty.tools.io.{Path, PlainFile}
2122

22-
import scala.quoted.{Expr, Type}
23+
import scala.quoted.{Expr, Staging, Type}
24+
import scala.runtime.quoted.Unpickler.Pickled
2325

2426
/** Compiler that takes the contents of a quoted expression `expr` and produces
2527
* a class file with `class ' { def apply: Object = expr }`.
@@ -30,7 +32,7 @@ class QuoteCompiler extends Compiler {
3032
List(List(new QuotedFrontend(putInClass = true)))
3133

3234
override protected def picklerPhases: List[List[Phase]] =
33-
List(List(new Staging))
35+
List(List(new StagingPhase))
3436

3537
override def newRun(implicit ctx: Context): ExprRun = {
3638
reset()
@@ -50,7 +52,7 @@ class QuoteCompiler extends Compiler {
5052
case exprUnit: ExprCompilationUnit =>
5153
val tree =
5254
if (putInClass) inClass(exprUnit.expr)
53-
else PickledQuotes.quotedExprToTree(exprUnit.expr)
55+
else unpickleExpr(exprUnit.expr)
5456
val source = new SourceFile("", "")
5557
CompilationUnit.mkCompilationUnit(source, tree, forceTrees = true)
5658
case typeUnit: TypeCompilationUnit =>
@@ -61,11 +63,16 @@ class QuoteCompiler extends Compiler {
6163
}
6264
}
6365

66+
private def unpickleExpr(code: Staging => Expr[Any])(implicit ctx: Context) = {
67+
val expr = code(new StagingImpl)
68+
PickledQuotes.quotedExprToTree(expr)
69+
}
70+
6471
/** Places the contents of expr in a compilable tree for a class
6572
* with the following format.
6673
* `package __root__ { class ' { def apply: Any = <expr> } }`
6774
*/
68-
private def inClass(expr: Expr[_])(implicit ctx: Context): Tree = {
75+
private def inClass(code: Staging => Expr[_])(implicit ctx: Context): Tree = {
6976
val pos = Position(0)
7077
val assocFile = new PlainFile(Path("<quote>"))
7178

@@ -74,7 +81,7 @@ class QuoteCompiler extends Compiler {
7481
cls.enter(ctx.newDefaultConstructor(cls), EmptyScope)
7582
val meth = ctx.newSymbol(cls, nme.apply, Method, ExprType(defn.AnyType), coord = pos).entered
7683

77-
val quoted = PickledQuotes.quotedExprToTree(expr)(ctx.withOwner(meth))
84+
val quoted = unpickleExpr(code)(ctx.withOwner(meth))
7885

7986
val run = DefDef(meth, quoted)
8087
val classTree = ClassDef(cls, DefDef(cls.primaryConstructor.asTerm), run :: Nil)
@@ -83,7 +90,7 @@ class QuoteCompiler extends Compiler {
8390
}
8491

8592
class ExprRun(comp: Compiler, ictx: Context) extends Run(comp, ictx) {
86-
def compileExpr(expr: Expr[_]): Unit = {
93+
def compileExpr(expr: Staging => Expr[_]): Unit = {
8794
val units = new ExprCompilationUnit(expr) :: Nil
8895
compileUnits(units)
8996
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package dotty.tools.dotc.quoted
2+
3+
import dotty.tools.dotc.core.Contexts._
4+
import dotty.tools.dotc.core.quoted.PickledQuotes
5+
import dotty.tools.dotc.tastyreflect.TastyImpl
6+
7+
import scala.quoted.{Expr, Type}
8+
import scala.runtime.quoted.Unpickler.Pickled
9+
10+
class StagingImpl(implicit ctx: Context) extends scala.quoted.Staging {
11+
12+
def unpickleExpr[T](repr: Pickled, args: Seq[Any]): Expr[T] =
13+
new scala.quoted.Exprs.TastyTreeExpr(PickledQuotes.unpickleExpr(repr, args)).asInstanceOf[Expr[T]]
14+
15+
def unpickleType[T](repr: Pickled, args: Seq[Any]): Type[T] =
16+
new scala.quoted.Types.TreeType(PickledQuotes.unpickleType(repr, args)).asInstanceOf[Type[T]]
17+
18+
def show[T](expr: Expr[T]): String = {
19+
val tree = PickledQuotes.quotedExprToTree(expr)
20+
// TODO freshen names
21+
val tree1 =
22+
if (ctx.settings.YshowRawQuoteTrees.value) tree else (new TreeCleaner).transform(tree)
23+
new TastyImpl(ctx).showSourceCode.showTree(tree1)
24+
}
25+
26+
def show[T](tpe: Type[T]): String = {
27+
val tree = PickledQuotes.quotedTypeToTree(tpe)
28+
// TODO freshen names
29+
val tree1 = if (ctx.settings.YshowRawQuoteTrees.value) tree else (new TreeCleaner).transform(tree)
30+
new TastyImpl(ctx).showSourceCode.showTypeOrBoundsTree(tree1)
31+
}
32+
33+
}

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

Lines changed: 0 additions & 19 deletions
This file was deleted.

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

Lines changed: 5 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,17 @@ import dotty.tools.dotc.tastyreflect.TastyImpl
77
import dotty.tools.io.{AbstractFile, Directory, PlainDirectory, VirtualDirectory}
88
import dotty.tools.repl.AbstractFileClassLoader
99

10-
import scala.quoted.{Expr, Type}
10+
import scala.quoted.{Expr, Staging, Type}
1111
import scala.quoted.Toolbox
1212
import java.net.URLClassLoader
1313

14+
import dotty.tools.dotc.core.quoted.PickledQuotes
15+
1416
class QuoteDriver extends Driver {
15-
import tpd._
1617

1718
private[this] val contextBase: ContextBase = new ContextBase
1819

19-
def run[T](expr: Expr[T], settings: Toolbox.Settings): T = {
20+
def run[T](code: Staging => Expr[T], settings: Toolbox.Settings): T = {
2021
val outDir: AbstractFile = settings.outDir match {
2122
case Some(out) =>
2223
val dir = Directory(out)
@@ -30,7 +31,7 @@ class QuoteDriver extends Driver {
3031
val ctx = setToolboxSettings(ctx0.fresh.setSetting(ctx0.settings.outputDir, outDir), settings)
3132

3233
val driver = new QuoteCompiler
33-
driver.newRun(ctx).compileExpr(expr)
34+
driver.newRun(ctx).compileExpr(code)
3435

3536
val classLoader = new AbstractFileClassLoader(outDir, this.getClass.getClassLoader)
3637

@@ -41,41 +42,6 @@ class QuoteDriver extends Driver {
4142
method.invoke(instance).asInstanceOf[T]
4243
}
4344

44-
def show(expr: Expr[_], settings: Toolbox.Settings): String = {
45-
def show(tree: Tree, ctx: Context): String = {
46-
implicit val c: Context = ctx
47-
val tree1 =
48-
if (ctx.settings.YshowRawQuoteTrees.value) tree
49-
else (new TreeCleaner).transform(tree)
50-
new TastyImpl(ctx).showSourceCode.showTree(tree1)
51-
}
52-
withTree(expr, show, settings)
53-
}
54-
55-
def withTree[T](expr: Expr[_], f: (Tree, Context) => T, settings: Toolbox.Settings): T = {
56-
val ctx = setToolboxSettings(setup(settings.compilerArgs.toArray :+ "dummy.scala", initCtx.fresh)._2.fresh, settings)
57-
58-
var output: Option[T] = None
59-
def registerTree(tree: tpd.Tree)(ctx: Context): Unit = {
60-
assert(output.isEmpty)
61-
output = Some(f(tree, ctx))
62-
}
63-
new QuoteDecompiler(registerTree).newRun(ctx).compileExpr(expr)
64-
output.getOrElse(throw new Exception("Could not extract " + expr))
65-
}
66-
67-
def withTypeTree[T](tpe: Type[_], f: (TypTree, Context) => T, settings: Toolbox.Settings): T = {
68-
val (_, ctx: Context) = setup(settings.compilerArgs.toArray :+ "dummy.scala", initCtx.fresh)
69-
70-
var output: Option[T] = None
71-
def registerTree(tree: tpd.Tree)(ctx: Context): Unit = {
72-
assert(output.isEmpty)
73-
output = Some(f(tree.asInstanceOf[TypTree], ctx))
74-
}
75-
new QuoteDecompiler(registerTree).newRun(ctx).compileType(tpe)
76-
output.getOrElse(throw new Exception("Could not extract " + tpe))
77-
}
78-
7945
override def initCtx: Context = {
8046
val ictx = contextBase.initialCtx
8147
ictx.settings.classpath.update(QuoteDriver.currentClasspath)(ictx)

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import dotty.tools.dotc.core.NameKinds.{NumberedInfo, UniqueName}
88
import dotty.tools.dotc.core.SymDenotations.SymDenotation
99
import dotty.tools.dotc.transform.MegaPhase.MiniPhase
1010

11+
// TODO use this in some form again
1112
/** Refreshes local names starting from the second use of the name. Intended for readability of the pretty printed code. */
1213
class RefreshNames extends MiniPhase with SymTransformer {
1314

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,18 @@
11
package dotty.tools.dotc.quoted
22

3-
import dotty.tools.dotc.ast.tpd
4-
5-
import scala.quoted.Expr
6-
import scala.quoted.Exprs.{LiftedExpr, TastyTreeExpr}
3+
import scala.quoted.{Expr, Staging, Type}
74

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

128
def make(settings: scala.quoted.Toolbox.Settings): scala.quoted.Toolbox = new scala.quoted.Toolbox {
139

1410
private[this] val driver: QuoteDriver = new QuoteDriver()
1511

16-
def run[T](expr: Expr[T]): T = expr match {
17-
case expr: LiftedExpr[T] =>
18-
expr.value
19-
case expr: TastyTreeExpr[Tree] @unchecked =>
20-
throw new Exception("Cannot call `Expr.run` on an `Expr` that comes from a macro argument.")
21-
case _ =>
22-
synchronized(driver.run(expr, settings))
12+
protected def runImpl[T](code: Staging => Expr[T]): T = {
13+
// TODO check for recursion and throw if possible (i.e. run inside a run)
14+
synchronized(driver.run(code, settings))
2315
}
2416

25-
def show[T](expr: Expr[T]): String = synchronized(driver.show(expr, settings))
26-
2717
}
2818
}

0 commit comments

Comments
 (0)