From 9c5383c1fb7f31309521fcab51ae08c41a16d314 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 4 May 2018 09:57:33 +0200 Subject: [PATCH 1/3] Fix #4455: Lift inlined values when quoted This is part of the transformation that allows compiling the contents of the inline macro. The alternative would be to pass each inline parameter twice, once as a value and once as it's tree. --- .../dotty/tools/dotc/core/Definitions.scala | 3 ++ .../tools/dotc/transform/ReifyQuotes.scala | 34 ++++++++++++++++--- tests/run/i4455.check | 4 +++ tests/run/i4455/Macro_1.scala | 8 +++++ tests/run/i4455/Test_2.scala | 9 +++++ 5 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 tests/run/i4455.check create mode 100644 tests/run/i4455/Macro_1.scala create mode 100644 tests/run/i4455/Test_2.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index f10c7ae75ee7..ef5d0d1f7022 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -660,6 +660,9 @@ class Definitions { lazy val QuotedType_applyR = QuotedTypeModule.requiredMethodRef(nme.apply) def QuotedType_apply(implicit ctx: Context) = QuotedType_applyR.symbol + lazy val QuotedLiftableType = ctx.requiredClassRef("scala.quoted.Liftable") + def QuotedLiftableClass(implicit ctx: Context) = QuotedLiftableType.symbol.asClass + def Unpickler_unpickleExpr = ctx.requiredMethod("scala.runtime.quoted.Unpickler.unpickleExpr") def Unpickler_liftedExpr = ctx.requiredMethod("scala.runtime.quoted.Unpickler.liftedExpr") def Unpickler_unpickleType = ctx.requiredMethod("scala.runtime.quoted.Unpickler.unpickleType") diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index ae9d63c3c222..dc871ada9a70 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -375,9 +375,15 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { } else body match { case body: RefTree if isCaptured(body, level + 1) => - // Optimization: avoid the full conversion when capturing `x` - // in '{ x } to '{ x$1.unary_~ } and go directly to `x$1` - capturers(body.symbol)(body) + if (body.symbol.is(Inline)) { + // Optimization: avoid the full conversion when capturing inlined `x` + // in '{ x } to '{ x$1.toExpr.unary_~ } and go directly to `x$1.toExpr` + liftValue(capturers(body.symbol)(body)) + } else { + // Optimization: avoid the full conversion when capturing `x` + // in '{ x } to '{ x$1.unary_~ } and go directly to `x$1` + capturers(body.symbol)(body) + } case _=> val (body1, splices) = nested(isQuote = true).split(body) pickledQuote(body1, splices, isType).withPos(quote.pos) @@ -546,8 +552,11 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { splice(tree) case tree: RefTree if isCaptured(tree, level) => val capturer = capturers(tree.symbol) - if (tree.symbol.is(Inline)) capturer(tree) - else splice(capturer(tree).select(if (tree.isTerm) nme.UNARY_~ else tpnme.UNARY_~)) + def captureAndSplice(t: Tree) = + splice(t.select(if (tree.isTerm) nme.UNARY_~ else tpnme.UNARY_~)) + if (tree.symbol.is(Inline) && level == 0) capturer(tree) + else if (tree.symbol.is(Inline)) captureAndSplice(liftValue(capturer(tree))) + else captureAndSplice(capturer(tree)) case Block(stats, _) => val last = enteredSyms stats.foreach(markDef) @@ -601,6 +610,21 @@ class ReifyQuotes extends MacroTransformWithImplicits with InfoTransformer { } } + private def liftValue(tree: Tree)(implicit ctx: Context): Tree = { + val reqType = defn.QuotedLiftableType.appliedTo(tree.tpe.widen) + val liftable = ctx.typer.inferImplicitArg(reqType, tree.pos) + liftable.tpe match { + case fail: SearchFailureType => + ctx.error(i""" + | + | The access would be accepted with the right Liftable, but + | ${ctx.typer.missingArgMsg(liftable, reqType, "")}""") + EmptyTree + case _ => + liftable.select("toExpr".toTermName).appliedTo(tree) + } + } + private def liftList(list: List[Tree], tpe: Type)(implicit ctx: Context): Tree = { list.foldRight[Tree](ref(defn.NilModule)) { (x, acc) => acc.select("::".toTermName).appliedToType(tpe).appliedTo(x) diff --git a/tests/run/i4455.check b/tests/run/i4455.check new file mode 100644 index 000000000000..b22040ee9c71 --- /dev/null +++ b/tests/run/i4455.check @@ -0,0 +1,4 @@ +1 +3 +3 +5 diff --git a/tests/run/i4455/Macro_1.scala b/tests/run/i4455/Macro_1.scala new file mode 100644 index 000000000000..a9934a97aaae --- /dev/null +++ b/tests/run/i4455/Macro_1.scala @@ -0,0 +1,8 @@ +import scala.quoted._ +object Macros { + inline def foo(inline i: Int): Int = ~bar('(i)) + def bar(x: Expr[Int]): Expr[Int] = x + + inline def foo2(inline i: Int): Int = ~bar('(i + 1)) + def bar2(x: Expr[Int]): Expr[Int] = x +} diff --git a/tests/run/i4455/Test_2.scala b/tests/run/i4455/Test_2.scala new file mode 100644 index 000000000000..18fc75a361c6 --- /dev/null +++ b/tests/run/i4455/Test_2.scala @@ -0,0 +1,9 @@ +import Macros._ +object Test { + def main(args: Array[String]): Unit = { + println(foo(1)) + println(foo(3)) + println(foo2(2)) + println(foo2(4)) + } +} From e35abf60e252b4c5793902b0cfd9012bda4fc4f5 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 4 May 2018 10:52:01 +0200 Subject: [PATCH 2/3] Remove extra bar --- tests/run/i4455/Macro_1.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/run/i4455/Macro_1.scala b/tests/run/i4455/Macro_1.scala index a9934a97aaae..9ac3070ed986 100644 --- a/tests/run/i4455/Macro_1.scala +++ b/tests/run/i4455/Macro_1.scala @@ -1,8 +1,8 @@ import scala.quoted._ object Macros { inline def foo(inline i: Int): Int = ~bar('(i)) - def bar(x: Expr[Int]): Expr[Int] = x inline def foo2(inline i: Int): Int = ~bar('(i + 1)) - def bar2(x: Expr[Int]): Expr[Int] = x + + def bar(x: Expr[Int]): Expr[Int] = x } From 25f9bbd84a19f00f5a91b0f47dfa273f96ab5837 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Fri, 4 May 2018 10:58:34 +0200 Subject: [PATCH 3/3] Update documentation --- compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala index dc871ada9a70..3751c1a4d4a7 100644 --- a/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala +++ b/compiler/src/dotty/tools/dotc/transform/ReifyQuotes.scala @@ -73,7 +73,7 @@ import dotty.tools.dotc.core.quoted._ * ... * val y1$1 = args(1).asInstanceOf[Expr[Y]] * ... - * { ... T1$1.unary_~ ... x ... '(y1$1.unary_~) ... } + * { ... x1$1 .... '{ ... T1$1.unary_~ ... x1$1.toExpr.unary_~ ... y1$1.unary_~ ... } ... } * } * ``` * Note: the parameters of `foo` are kept for simple overloading resolution but they are not used in the body of `foo`.