From b35f6a60d069bcf4f76de5ef31e324dfce0ff4bc Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sun, 15 Mar 2020 10:19:08 +0100 Subject: [PATCH 1/2] Fix quoted match inline bindings leak And make `inlineContext` resilient to previous errors (i.e. never add an empty call). --- compiler/src/dotty/tools/dotc/ast/tpd.scala | 15 +++++++------ .../src/scala/internal/quoted/Matcher.scala | 7 +++++-- tests/run-macros/i6253-c.check | 2 ++ tests/run-macros/i6253-c/quoted_1.scala | 21 +++++++++++++++++++ tests/run-macros/i6253-c/quoted_2.scala | 10 +++++++++ 5 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 tests/run-macros/i6253-c.check create mode 100644 tests/run-macros/i6253-c/quoted_1.scala create mode 100644 tests/run-macros/i6253-c/quoted_2.scala diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 584a46ebec2a..7ad6424682ea 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -1234,12 +1234,15 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { override def inlineContext(call: Tree)(implicit ctx: Context): Context = { // We assume enclosingInlineds is already normalized, and only process the new call with the head. val oldIC = enclosingInlineds - val newIC = (call, oldIC) match { - case (t, t1 :: ts2) if t.isEmpty => - assert(!t1.isEmpty) - ts2 - case _ => call :: oldIC - } + + val newIC = + if call.isEmpty then + oldIC match + case t1 :: ts2 => ts2 + case _ => oldIC + else + call :: oldIC + ctx.fresh.setProperty(InlinedCalls, newIC) } diff --git a/library/src/scala/internal/quoted/Matcher.scala b/library/src/scala/internal/quoted/Matcher.scala index 9b788ae93354..b59382f12a82 100644 --- a/library/src/scala/internal/quoted/Matcher.scala +++ b/library/src/scala/internal/quoted/Matcher.scala @@ -33,7 +33,7 @@ private[quoted] object Matcher { if (hasTypeSplices) { val ctx: Context = internal.Context_GADT_setFreshGADTBounds(rootContext) given Context = ctx - val matchings = scrutineeTerm.underlyingArgument =?= patternTerm.underlyingArgument + val matchings = scrutineeTerm =?= patternTerm // After matching and doing all subtype checks, we have to approximate all the type bindings // that we have found and seal them in a quoted.Type matchings.asOptionOfTuple.map { tup => @@ -44,7 +44,7 @@ private[quoted] object Matcher { } } else { - scrutineeTerm.underlyingArgument =?= patternTerm.underlyingArgument + scrutineeTerm =?= patternTerm } } @@ -286,6 +286,9 @@ private[quoted] object Matcher { case (_, Annotated(tpt, _)) => scrutinee =?= tpt + case (NamedArg(name1, arg1), NamedArg(name2, arg2)) if name1 == name2 => + arg1 =?= arg2 + // No Match case _ => if (debug) diff --git a/tests/run-macros/i6253-c.check b/tests/run-macros/i6253-c.check new file mode 100644 index 000000000000..21f071248e07 --- /dev/null +++ b/tests/run-macros/i6253-c.check @@ -0,0 +1,2 @@ +ERROR +ERROR diff --git a/tests/run-macros/i6253-c/quoted_1.scala b/tests/run-macros/i6253-c/quoted_1.scala new file mode 100644 index 000000000000..aaf8d343f86e --- /dev/null +++ b/tests/run-macros/i6253-c/quoted_1.scala @@ -0,0 +1,21 @@ +import scala.quoted._ + + +object Macros { + + // Should be: inline def (inline self: StringContext) ... + inline def (self: => StringContext) xyz(args: => String*): String = ${impl('self, 'args)} + + private def impl(self: Expr[StringContext], args: Expr[Seq[String]])(using QuoteContext): Expr[String] = { + self match { + case '{ StringContext($parts: _*) } => + '{ + val p: Seq[String] = $parts + val a: Seq[Any] = $args ++ Seq("") + p.zip(a).map(_ + _.toString).mkString + } + case _ => + '{ "ERROR" } + } + } +} diff --git a/tests/run-macros/i6253-c/quoted_2.scala b/tests/run-macros/i6253-c/quoted_2.scala new file mode 100644 index 000000000000..2a85eca35e41 --- /dev/null +++ b/tests/run-macros/i6253-c/quoted_2.scala @@ -0,0 +1,10 @@ +import Macros._ + +object Test { + + def main(args: Array[String]): Unit = { + println(xyz"Hello World") + println(xyz"Hello ${"World"}") + } + +} From 2e8fa8d23364a2e4fbfe24a3e63dfd3ead2f7003 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 17 Mar 2020 21:33:52 +0100 Subject: [PATCH 2/2] Make test intention clear --- tests/run-macros/i6253-c.check | 2 -- tests/run-macros/i6253-c/quoted_1.scala | 10 +++------- 2 files changed, 3 insertions(+), 9 deletions(-) delete mode 100644 tests/run-macros/i6253-c.check diff --git a/tests/run-macros/i6253-c.check b/tests/run-macros/i6253-c.check deleted file mode 100644 index 21f071248e07..000000000000 --- a/tests/run-macros/i6253-c.check +++ /dev/null @@ -1,2 +0,0 @@ -ERROR -ERROR diff --git a/tests/run-macros/i6253-c/quoted_1.scala b/tests/run-macros/i6253-c/quoted_1.scala index aaf8d343f86e..c7a4a08e3256 100644 --- a/tests/run-macros/i6253-c/quoted_1.scala +++ b/tests/run-macros/i6253-c/quoted_1.scala @@ -8,14 +8,10 @@ object Macros { private def impl(self: Expr[StringContext], args: Expr[Seq[String]])(using QuoteContext): Expr[String] = { self match { - case '{ StringContext($parts: _*) } => - '{ - val p: Seq[String] = $parts - val a: Seq[Any] = $args ++ Seq("") - p.zip(a).map(_ + _.toString).mkString - } + case '{ StringContext($parts: _*) } => // Should not match as the parameter is not marked as inlined + '{ ??? } case _ => - '{ "ERROR" } + '{ "Ok" } } } }