From e6c1fb565d7eafdd1acfab52c0391f07658aac99 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 4 Jul 2019 13:28:40 +0200 Subject: [PATCH] Fix macro detection mechanism Only expand splices if they come from macros and remove buggy duplicated macro detection code. --- .../src/dotty/tools/dotc/typer/Inliner.scala | 2 +- .../tools/dotc/typer/PrepareInlineable.scala | 71 ++++++++----------- tests/neg/inline-quote.scala | 15 ++++ tests/run-with-compiler/inline-quote.check | 4 ++ tests/run-with-compiler/inline-quote.scala | 17 +++++ 5 files changed, 65 insertions(+), 44 deletions(-) create mode 100644 tests/neg/inline-quote.scala create mode 100644 tests/run-with-compiler/inline-quote.check create mode 100644 tests/run-with-compiler/inline-quote.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 6a502209e0f9..e5a5cdc5b866 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -1027,7 +1027,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = { constToLiteral(betaReduce(super.typedApply(tree, pt))) match { - case res: Apply if res.symbol == defn.InternalQuoted_exprSplice && level == 0 => + case res: Apply if res.symbol == defn.InternalQuoted_exprSplice && level == 0 && call.symbol.is(Macro) => expandMacro(res.args.head, tree.span) case res => res } diff --git a/compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala b/compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala index 61ce226a2ade..713f78f75fde 100644 --- a/compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala +++ b/compiler/src/dotty/tools/dotc/typer/PrepareInlineable.scala @@ -245,51 +245,36 @@ object PrepareInlineable { } def checkInlineMacro(sym: Symbol, rhs: Tree, pos: SourcePosition)(implicit ctx: Context) = { - if (!ctx.isAfterTyper) { - var isMacro = false - new TreeMapWithStages(freshStagingContext) { - override protected def transformSplice(body: tpd.Tree, splice: tpd.Tree)(implicit ctx: Context): tpd.Tree = { - isMacro = true - splice - } - override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = - if (isMacro) tree else super.transform(tree) - }.transform(rhs) - - if (isMacro) { - sym.setFlag(Macro) - if (level == 0) { - def isValidMacro(tree: Tree)(implicit ctx: Context): Unit = tree match { - case Spliced(code) => - if (code.symbol.flags.is(Inline)) - ctx.error("Macro cannot be implemented with an `inline` method", code.sourcePos) - Splicer.checkValidMacroBody(code) - new PCPCheckAndHeal(freshStagingContext).transform(rhs) // Ignore output, only check PCP + if (sym.is(Macro) && !ctx.isAfterTyper) { + def isValidMacro(tree: Tree)(implicit ctx: Context): Unit = tree match { + case Spliced(code) => + if (code.symbol.flags.is(Inline)) + ctx.error("Macro cannot be implemented with an `inline` method", code.sourcePos) + Splicer.checkValidMacroBody(code) + new PCPCheckAndHeal(freshStagingContext).transform(rhs) // Ignore output, only check PCP - case Block(List(stat), Literal(Constants.Constant(()))) => isValidMacro(stat) - case Block(Nil, expr) => isValidMacro(expr) - case Typed(expr, _) => isValidMacro(expr) - case Block(DefDef(nme.ANON_FUN, _, _, _, _) :: Nil, Closure(_, fn, _)) if fn.symbol.info.isImplicitMethod => - // TODO Suppot this pattern - ctx.error( - """Macros using a return type of the form `foo(): given X => Y` are not yet supported. - | - |Place the implicit as an argument (`foo() given X: Y`) to overcome this limitation. - |""".stripMargin, tree.sourcePos) - case _ => - ctx.error( - """Malformed macro. - | - |Expected the splice ${...} to be at the top of the RHS: - | inline def foo(inline x: X, ..., y: Y): Int = ${impl(x, ... '{y}}) - | - | * The contents of the splice must call a static method - | * All arguments must be quoted or inline - """.stripMargin, pos) - } - isValidMacro(rhs) - } + case Block(List(stat), Literal(Constants.Constant(()))) => isValidMacro(stat) + case Block(Nil, expr) => isValidMacro(expr) + case Typed(expr, _) => isValidMacro(expr) + case Block(DefDef(nme.ANON_FUN, _, _, _, _) :: Nil, Closure(_, fn, _)) if fn.symbol.info.isImplicitMethod => + // TODO Suppot this pattern + ctx.error( + """Macros using a return type of the form `foo(): given X => Y` are not yet supported. + | + |Place the implicit as an argument (`foo() given X: Y`) to overcome this limitation. + |""".stripMargin, tree.sourcePos) + case _ => + ctx.error( + """Malformed macro. + | + |Expected the splice ${...} to be at the top of the RHS: + | inline def foo(inline x: X, ..., y: Y): Int = ${impl(x, ... '{y}}) + | + | * The contents of the splice must call a static method + | * All arguments must be quoted or inline + """.stripMargin, pos) } + isValidMacro(rhs) } } diff --git a/tests/neg/inline-quote.scala b/tests/neg/inline-quote.scala new file mode 100644 index 000000000000..e520a3366b3b --- /dev/null +++ b/tests/neg/inline-quote.scala @@ -0,0 +1,15 @@ +import scala.quoted._ + +object Test { + + inline def foo(x: Expr[Int]) given QuoteContext: Expr[Int] = '{ // error + println("foo") + ${ + ${??? : Expr[Int]} + + x + } + } + + +} diff --git a/tests/run-with-compiler/inline-quote.check b/tests/run-with-compiler/inline-quote.check new file mode 100644 index 000000000000..32d3441c5791 --- /dev/null +++ b/tests/run-with-compiler/inline-quote.check @@ -0,0 +1,4 @@ +{ + scala.Predef.println("foo") + 45 +} diff --git a/tests/run-with-compiler/inline-quote.scala b/tests/run-with-compiler/inline-quote.scala new file mode 100644 index 000000000000..95e526a78068 --- /dev/null +++ b/tests/run-with-compiler/inline-quote.scala @@ -0,0 +1,17 @@ +import scala.quoted._ + +object Test { + + inline def foo(x: Expr[Int]) given QuoteContext: Expr[Int] = '{ + println("foo") + $x + } + + implicit val toolbox: scala.quoted.Toolbox = scala.quoted.Toolbox.make(getClass.getClassLoader) + + def main(args: Array[String]): Unit = withQuoteContext { + val y = '{45} + println(foo(y).show) + } + +}