From d9859036ed9f4e2905583ba0c31fd14c756b53d8 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 3 Apr 2018 13:29:16 +0200 Subject: [PATCH 1/6] Cache classloaders in Splicer --- compiler/src/dotty/tools/dotc/transform/Splicer.scala | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 73876125afd3..e90b70a384f8 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -93,6 +93,8 @@ object Splicer { } } + private val classLoaders = scala.collection.mutable.HashMap.empty[String, URLClassLoader] + /** Tree interpreter that can interpret calls to static methods with it's default arguments * * The interpreter assumes that all calls in the trees are to code that was @@ -101,8 +103,12 @@ object Splicer { private class Interpreter(pos: Position)(implicit ctx: Context) { private[this] val classLoader = { - val urls = ctx.settings.classpath.value.split(':').map(cp => java.nio.file.Paths.get(cp).toUri.toURL) - new URLClassLoader(urls, getClass.getClassLoader) + val cp = ctx.settings.classpath.value + def newClassLoader = { + val urls = cp.split(':').map(cp => java.nio.file.Paths.get(cp).toUri.toURL) + new URLClassLoader(urls, getClass.getClassLoader) + } + classLoaders.getOrElseUpdate(cp, newClassLoader) } /** Returns the interpreted result of interpreting the code a call to the symbol with default arguments. From 04c18a570a04b82471832bd1ded7faba2d77a587 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 3 Apr 2018 16:28:23 +0200 Subject: [PATCH 2/6] Make splicer only valid for this run --- .../dotty/tools/dotc/transform/ReifyQuotes.scala | 4 +++- .../src/dotty/tools/dotc/transform/Splicer.scala | 16 ++++++---------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index b6e448e2e1fd..f6ab3abc8b84 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -88,6 +88,8 @@ import dotty.tools.dotc.core.quoted._ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { import ast.tpd._ + private lazy val splicer: Splicer = new Splicer + override def phaseName: String = "reifyQuotes" override def run(implicit ctx: Context): Unit = @@ -542,7 +544,7 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { // Simplification of the call done in PostTyper for non-macros can also be performed now // see PostTyper `case Inlined(...) =>` for description of the simplification val call2 = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos) - val spliced = Splicer.splice(body, call, bindings, tree.pos).withPos(tree.pos) + val spliced = splicer.splice(body, call, bindings, tree.pos).withPos(tree.pos) transform(cpy.Inlined(tree)(call2, bindings, spliced)) } else super.transform(tree) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index e90b70a384f8..14da8ccb2d3b 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -21,9 +21,11 @@ import dotty.tools.dotc.util.Positions.Position import scala.reflect.ClassTag /** Utility class to splice quoted expressions */ -object Splicer { +class Splicer { import tpd._ + private var classLoader: URLClassLoader = _ + /** Splice the Tree for a Quoted expression. `~'(xyz)` becomes `xyz` * and for `~xyz` the tree of `xyz` is interpreted for which the * resulting expression is returned as a `Tree` @@ -93,8 +95,6 @@ object Splicer { } } - private val classLoaders = scala.collection.mutable.HashMap.empty[String, URLClassLoader] - /** Tree interpreter that can interpret calls to static methods with it's default arguments * * The interpreter assumes that all calls in the trees are to code that was @@ -102,13 +102,9 @@ object Splicer { */ private class Interpreter(pos: Position)(implicit ctx: Context) { - private[this] val classLoader = { - val cp = ctx.settings.classpath.value - def newClassLoader = { - val urls = cp.split(':').map(cp => java.nio.file.Paths.get(cp).toUri.toURL) - new URLClassLoader(urls, getClass.getClassLoader) - } - classLoaders.getOrElseUpdate(cp, newClassLoader) + if (classLoader == null) { + val urls = ctx.settings.classpath.value.split(':').map(cp => java.nio.file.Paths.get(cp).toUri.toURL) + classLoader = new URLClassLoader(urls, getClass.getClassLoader) } /** Returns the interpreted result of interpreting the code a call to the symbol with default arguments. From 0d1243a29746f4e2dab41da092c0aee68dfa652a Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 3 Apr 2018 17:36:11 +0200 Subject: [PATCH 3/6] Cache classloader directly in ReifyQuotes --- .../tools/dotc/transform/ReifyQuotes.scala | 13 ++++++++-- .../dotty/tools/dotc/transform/Splicer.scala | 26 ++++++------------- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index f6ab3abc8b84..d672ff746d75 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -88,7 +88,8 @@ import dotty.tools.dotc.core.quoted._ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { import ast.tpd._ - private lazy val splicer: Splicer = new Splicer + /** Classloader used for loading macros */ + private var macroClassLoader: java.lang.ClassLoader = _ override def phaseName: String = "reifyQuotes" @@ -544,7 +545,7 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { // Simplification of the call done in PostTyper for non-macros can also be performed now // see PostTyper `case Inlined(...) =>` for description of the simplification val call2 = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos) - val spliced = splicer.splice(body, call, bindings, tree.pos).withPos(tree.pos) + val spliced = Splicer.splice(body, call, bindings, tree.pos, getMacroClassLoader).withPos(tree.pos) transform(cpy.Inlined(tree)(call2, bindings, spliced)) } else super.transform(tree) @@ -622,6 +623,14 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { transform(tp) } + private def getMacroClassLoader(implicit ctx: Context): ClassLoader = { + if (macroClassLoader == null) { + val urls = ctx.settings.classpath.value.split(':').map(cp => java.nio.file.Paths.get(cp).toUri.toURL) + macroClassLoader = new java.net.URLClassLoader(urls, getClass.getClassLoader) + } + macroClassLoader + } + override protected def mayChange(sym: Symbol)(implicit ctx: Context): Boolean = sym.is(Macro) /** Returns the type of the compiled macro as a lambda: Seq[Any] => Object */ diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 14da8ccb2d3b..1d9acc0324d7 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -21,27 +21,22 @@ import dotty.tools.dotc.util.Positions.Position import scala.reflect.ClassTag /** Utility class to splice quoted expressions */ -class Splicer { +object Splicer { import tpd._ - private var classLoader: URLClassLoader = _ - /** Splice the Tree for a Quoted expression. `~'(xyz)` becomes `xyz` * and for `~xyz` the tree of `xyz` is interpreted for which the * resulting expression is returned as a `Tree` * * See: `ReifyQuotes` */ - def splice(tree: Tree, call: Tree, bindings: List[Tree], pos: Position)(implicit ctx: Context): Tree = tree match { + def splice(tree: Tree, call: Tree, bindings: List[Tree], pos: Position, classLoader: ClassLoader)(implicit ctx: Context): Tree = tree match { case Quoted(quotedTree) => quotedTree - case _ => reflectiveSplice(tree, call, bindings, pos) - } - - private def reflectiveSplice(tree: Tree, call: Tree, bindings: List[Tree], pos: Position)(implicit ctx: Context): Tree = { - val liftedArgs = getLiftedArgs(call, bindings) - val interpreter = new Interpreter(pos) - val interpreted = interpreter.interpretCallToSymbol[Seq[Any] => Object](call.symbol) - interpreted.flatMap(lambda => evaluateLambda(lambda, liftedArgs, pos)).fold(tree)(PickledQuotes.quotedExprToTree) + case _ => + val liftedArgs = getLiftedArgs(call, bindings) + val interpreter = new Interpreter(pos, classLoader) + val interpreted = interpreter.interpretCallToSymbol[Seq[Any] => Object](call.symbol) + interpreted.flatMap(lambda => evaluateLambda(lambda, liftedArgs, pos)).fold(tree)(PickledQuotes.quotedExprToTree) } /** Given the inline code and bindings, compute the lifted arguments that will be used to execute the macro @@ -100,12 +95,7 @@ class Splicer { * The interpreter assumes that all calls in the trees are to code that was * previously compiled and is present in the classpath of the current context. */ - private class Interpreter(pos: Position)(implicit ctx: Context) { - - if (classLoader == null) { - val urls = ctx.settings.classpath.value.split(':').map(cp => java.nio.file.Paths.get(cp).toUri.toURL) - classLoader = new URLClassLoader(urls, getClass.getClassLoader) - } + private class Interpreter(pos: Position, classLoader: ClassLoader)(implicit ctx: Context) { /** Returns the interpreted result of interpreting the code a call to the symbol with default arguments. * Return Some of the result or None if some error happen during the interpretation. From 69383b0a270b08bf945593bcecd6cea4f2aed93f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 3 Apr 2018 20:01:27 +0200 Subject: [PATCH 4/6] Add private[this] and rename field --- .../src/dotty/tools/dotc/transform/ReifyQuotes.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index d672ff746d75..2aa0ccd61a74 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -89,7 +89,7 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { import ast.tpd._ /** Classloader used for loading macros */ - private var macroClassLoader: java.lang.ClassLoader = _ + private[this] var myMacroClassLoader: java.lang.ClassLoader = _ override def phaseName: String = "reifyQuotes" @@ -545,7 +545,7 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { // Simplification of the call done in PostTyper for non-macros can also be performed now // see PostTyper `case Inlined(...) =>` for description of the simplification val call2 = Ident(call.symbol.topLevelClass.typeRef).withPos(call.pos) - val spliced = Splicer.splice(body, call, bindings, tree.pos, getMacroClassLoader).withPos(tree.pos) + val spliced = Splicer.splice(body, call, bindings, tree.pos, macroClassLoader).withPos(tree.pos) transform(cpy.Inlined(tree)(call2, bindings, spliced)) } else super.transform(tree) @@ -623,12 +623,12 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { transform(tp) } - private def getMacroClassLoader(implicit ctx: Context): ClassLoader = { - if (macroClassLoader == null) { + private def macroClassLoader(implicit ctx: Context): ClassLoader = { + if (myMacroClassLoader == null) { val urls = ctx.settings.classpath.value.split(':').map(cp => java.nio.file.Paths.get(cp).toUri.toURL) - macroClassLoader = new java.net.URLClassLoader(urls, getClass.getClassLoader) + myMacroClassLoader = new java.net.URLClassLoader(urls, getClass.getClassLoader) } - macroClassLoader + myMacroClassLoader } override protected def mayChange(sym: Symbol)(implicit ctx: Context): Boolean = sym.is(Macro) From d7308154892d6cd9f8c949a6ecee2134e6764b46 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 3 Apr 2018 15:42:55 +0200 Subject: [PATCH 5/6] Change owners only once for nested unpickled quotes --- .../dotc/core/quoted/PickledQuotes.scala | 39 +++++++++++++------ .../tools/dotc/core/tasty/TreeUnpickler.scala | 4 +- .../tools/dotc/quoted/ExprCompiler.scala | 4 +- .../dotty/tools/dotc/transform/Splicer.scala | 2 +- 4 files changed, 33 insertions(+), 16 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala index 7f6341b70b44..dac8596c3efd 100644 --- a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala @@ -19,6 +19,8 @@ import scala.quoted.Exprs._ import scala.reflect.ClassTag +import scala.collection.mutable + object PickledQuotes { import tpd._ @@ -34,43 +36,58 @@ object PickledQuotes { } /** Transform the expression into its fully spliced Tree */ - def quotedExprToTree[T](expr: quoted.Expr[T])(implicit ctx: Context): Tree = expr match { - case expr: TastyExpr[_] => unpickleExpr(expr) + def quotedExprToTree[T](expr: quoted.Expr[T], nested: Boolean)(implicit ctx: Context): Tree = expr match { + case expr: TastyExpr[_] => unpickleExpr(expr, nested) case expr: LiftedExpr[T] => expr.value match { case value: Class[_] => ref(defn.Predef_classOf).appliedToType(classToType(value)) - case value=> Literal(Constant(value)) + case value => Literal(Constant(value)) } case expr: TreeExpr[Tree] @unchecked => expr.tree case expr: FunctionAppliedTo[_, _] => - functionAppliedTo(quotedExprToTree(expr.f), quotedExprToTree(expr.x)) + functionAppliedTo(quotedExprToTree(expr.f, nested), quotedExprToTree(expr.x, nested)) } /** Transform the expression into its fully spliced TypeTree */ - def quotedTypeToTree(expr: quoted.Type[_])(implicit ctx: Context): Tree = expr match { - case expr: TastyType[_] => unpickleType(expr) + def quotedTypeToTree(expr: quoted.Type[_], nested: Boolean)(implicit ctx: Context): Tree = expr match { + case expr: TastyType[_] => unpickleType(expr, nested) case expr: TaggedType[_] => classTagToTypeTree(expr.ct) case expr: TreeType[Tree] @unchecked => expr.tree } /** Unpickle the tree contained in the TastyExpr */ - private def unpickleExpr(expr: TastyExpr[_])(implicit ctx: Context): Tree = { + private def unpickleExpr(expr: TastyExpr[_], nested: Boolean)(implicit ctx: Context): Tree = { val tastyBytes = TastyString.unpickle(expr.tasty) val unpickled = unpickle(tastyBytes, expr.args) unpickled match { case PackageDef(_, (vdef: ValDef) :: Nil) => - vdef.rhs.changeOwner(vdef.symbol, ctx.owner) + changeQuoteOwners(vdef.rhs, nested) } } /** Unpickle the tree contained in the TastyType */ - private def unpickleType(ttpe: TastyType[_])(implicit ctx: Context): Tree = { + private def unpickleType(ttpe: TastyType[_], nested: Boolean)(implicit ctx: Context): Tree = { val tastyBytes = TastyString.unpickle(ttpe.tasty) val unpickled = unpickle(tastyBytes, ttpe.args) unpickled match { case PackageDef(_, (vdef: ValDef) :: Nil) => - vdef.rhs.asInstanceOf[TypeApply].args.head - .changeOwner(vdef.symbol, ctx.owner) + changeQuoteOwners(vdef.rhs.asInstanceOf[TypeApply].args.head, nested) + } + } + + private def changeQuoteOwners(tree: Tree, nested: Boolean)(implicit ctx: Context): Tree = { + if (nested) tree + else { + val set = mutable.HashSet.empty[Symbol] + new TreeTraverser { + override def traverse(tree: tpd.Tree)(implicit ctx: Context): Unit = { + if (tree.symbol.exists && tree.symbol.owner.name == "$quote".toTermName) + set += tree.symbol.owner + traverseChildren(tree) + } + }.traverse(tree) + val owners = set.toList + new TreeTypeMap(oldOwners = owners, newOwners = owners.map(_ => ctx.owner)).apply(tree) } } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 6dca21f68b26..30ed41d4e6cb 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1145,12 +1145,12 @@ class TreeUnpickler(reader: TastyReader, val quotedType = if (reifiedArgs.isEmpty) splice.asInstanceOf[quoted.Type[_]] else splice.asInstanceOf[Seq[Any] => quoted.Type[_]](reifiedArgs) - PickledQuotes.quotedTypeToTree(quotedType) + PickledQuotes.quotedTypeToTree(quotedType, nested = true) } else { val quotedExpr = if (reifiedArgs.isEmpty) splice.asInstanceOf[quoted.Expr[_]] else splice.asInstanceOf[Seq[Any] => quoted.Expr[_]](reifiedArgs) - PickledQuotes.quotedExprToTree(quotedExpr) + PickledQuotes.quotedExprToTree(quotedExpr, nested = true) } } diff --git a/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala b/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala index fb782c031543..7c77535b784d 100644 --- a/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala +++ b/compiler/src/dotty/tools/dotc/quoted/ExprCompiler.scala @@ -61,7 +61,7 @@ class ExprCompiler(directory: AbstractFile) extends Compiler { case exprUnit: ExprCompilationUnit => val tree = if (putInClass) inClass(exprUnit.expr) - else PickledQuotes.quotedExprToTree(exprUnit.expr) + else PickledQuotes.quotedExprToTree(exprUnit.expr, nested = false) val source = new SourceFile("", Seq()) CompilationUnit.mkCompilationUnit(source, tree, forceTrees = true) } @@ -80,7 +80,7 @@ class ExprCompiler(directory: AbstractFile) extends Compiler { cls.enter(ctx.newDefaultConstructor(cls), EmptyScope) val meth = ctx.newSymbol(cls, nme.apply, Method, ExprType(defn.AnyType), coord = pos).entered - val quoted = PickledQuotes.quotedExprToTree(expr)(ctx.withOwner(meth)) + val quoted = PickledQuotes.quotedExprToTree(expr, nested = false)(ctx.withOwner(meth)) val run = DefDef(meth, quoted) val classTree = ClassDef(cls, DefDef(cls.primaryConstructor.asTerm), run :: Nil) diff --git a/compiler/src/dotty/tools/dotc/transform/Splicer.scala b/compiler/src/dotty/tools/dotc/transform/Splicer.scala index 1d9acc0324d7..134767878c19 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -36,7 +36,7 @@ object Splicer { val liftedArgs = getLiftedArgs(call, bindings) val interpreter = new Interpreter(pos, classLoader) val interpreted = interpreter.interpretCallToSymbol[Seq[Any] => Object](call.symbol) - interpreted.flatMap(lambda => evaluateLambda(lambda, liftedArgs, pos)).fold(tree)(PickledQuotes.quotedExprToTree) + interpreted.flatMap(lambda => evaluateLambda(lambda, liftedArgs, pos)).fold(tree)(PickledQuotes.quotedExprToTree(_, nested = false)) } /** Given the inline code and bindings, compute the lifted arguments that will be used to execute the macro From f57ab64098f855cbb81d648cc995891e3f535173 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Wed, 4 Apr 2018 09:05:22 +0200 Subject: [PATCH 6/6] WIP --- .../dotc/core/quoted/PickledQuotes.scala | 21 +++++++----------- tests/run-with-compiler/quote-owners.check | 9 ++++++++ tests/run-with-compiler/quote-owners.scala | 22 +++++++++++++++++++ 3 files changed, 39 insertions(+), 13 deletions(-) create mode 100644 tests/run-with-compiler/quote-owners.check create mode 100644 tests/run-with-compiler/quote-owners.scala diff --git a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala index dac8596c3efd..dd0437064bb1 100644 --- a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala +++ b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala @@ -61,7 +61,7 @@ object PickledQuotes { val unpickled = unpickle(tastyBytes, expr.args) unpickled match { case PackageDef(_, (vdef: ValDef) :: Nil) => - changeQuoteOwners(vdef.rhs, nested) + changeQuoteOwners(vdef.rhs, vdef.symbol, nested) } } @@ -71,23 +71,18 @@ object PickledQuotes { val unpickled = unpickle(tastyBytes, ttpe.args) unpickled match { case PackageDef(_, (vdef: ValDef) :: Nil) => - changeQuoteOwners(vdef.rhs.asInstanceOf[TypeApply].args.head, nested) + changeQuoteOwners(vdef.rhs.asInstanceOf[TypeApply].args.head, vdef.symbol, nested) } } - private def changeQuoteOwners(tree: Tree, nested: Boolean)(implicit ctx: Context): Tree = { + private[this] val ownerMapping = scala.collection.mutable.LinkedHashMap.empty[Symbol, Symbol] + private def changeQuoteOwners(tree: Tree, owner: Symbol, nested: Boolean)(implicit ctx: Context): Tree = { + ownerMapping.put(owner, ctx.owner) if (nested) tree else { - val set = mutable.HashSet.empty[Symbol] - new TreeTraverser { - override def traverse(tree: tpd.Tree)(implicit ctx: Context): Unit = { - if (tree.symbol.exists && tree.symbol.owner.name == "$quote".toTermName) - set += tree.symbol.owner - traverseChildren(tree) - } - }.traverse(tree) - val owners = set.toList - new TreeTypeMap(oldOwners = owners, newOwners = owners.map(_ => ctx.owner)).apply(tree) + val (oldOwners, newOwners) = ownerMapping.iterator.toList.unzip + ownerMapping.clear() + new TreeTypeMap(oldOwners = oldOwners, newOwners = newOwners).apply(tree) } } diff --git a/tests/run-with-compiler/quote-owners.check b/tests/run-with-compiler/quote-owners.check new file mode 100644 index 000000000000..8b1f473d369a --- /dev/null +++ b/tests/run-with-compiler/quote-owners.check @@ -0,0 +1,9 @@ +9 +{ + def ff: Int = + { + val a: Int = 9 + a.+(0) + } + ff: Int +} diff --git a/tests/run-with-compiler/quote-owners.scala b/tests/run-with-compiler/quote-owners.scala new file mode 100644 index 000000000000..73694b45b696 --- /dev/null +++ b/tests/run-with-compiler/quote-owners.scala @@ -0,0 +1,22 @@ +import quoted._ +import dotty.tools.dotc.quoted.Toolbox._ + +object Test { + def main(args: Array[String]): Unit = { + val q = f + println(q.run) + println(q.show) + } + + def f: Expr[Int] = '{ + def ff : Int = { + ~g + } + ff + } + + def g: Expr[Int] = '{ + val a = 9 + a + 0 + } +}