diff --git a/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala b/compiler/src/dotty/tools/dotc/core/quoted/PickledQuotes.scala index 7f6341b70b44..dd0437064bb1 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,53 @@ 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, vdef.symbol, 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, vdef.symbol, nested) + } + } + + 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 (oldOwners, newOwners) = ownerMapping.iterator.toList.unzip + ownerMapping.clear() + new TreeTypeMap(oldOwners = oldOwners, newOwners = newOwners).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/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index b6e448e2e1fd..2aa0ccd61a74 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -88,6 +88,9 @@ import dotty.tools.dotc.core.quoted._ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { import ast.tpd._ + /** Classloader used for loading macros */ + private[this] var myMacroClassLoader: java.lang.ClassLoader = _ + override def phaseName: String = "reifyQuotes" override def run(implicit ctx: Context): Unit = @@ -542,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, macroClassLoader).withPos(tree.pos) transform(cpy.Inlined(tree)(call2, bindings, spliced)) } else super.transform(tree) @@ -620,6 +623,14 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { transform(tp) } + 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) + myMacroClassLoader = new java.net.URLClassLoader(urls, getClass.getClassLoader) + } + myMacroClassLoader + } + 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 73876125afd3..134767878c19 100644 --- a/compiler/src/dotty/tools/dotc/transform/Splicer.scala +++ b/compiler/src/dotty/tools/dotc/transform/Splicer.scala @@ -30,16 +30,13 @@ object Splicer { * * 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(_, nested = false)) } /** Given the inline code and bindings, compute the lifted arguments that will be used to execute the macro @@ -98,12 +95,7 @@ object 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) { - - 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) - } + 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. 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 + } +}