From 676b25048499e26be98a7878a1c1f69cc3da32c7 Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Thu, 14 Mar 2024 09:02:56 +0100 Subject: [PATCH 1/2] Detect non `Exp[..]` splice patterns We compute the pattern using `Expr[pt]` of the prototype `pt` of the splice, but this is not enough to reject non-matching patterns. The quoted splices are encoded away before we get to the pattern matching phase where we could potentially detect that they will not match. Instead we check that all quote patterns are `Expr[..]` when typing the pattern. Fixes #19941 --- compiler/src/dotty/tools/dotc/quoted/QuotePatterns.scala | 4 +++- tests/neg-macros/i19941.check | 4 ++++ tests/neg-macros/i19941.scala | 7 +++++++ tests/{pos-macros => neg-macros}/quotedPatterns-4.scala | 2 +- 4 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 tests/neg-macros/i19941.check create mode 100644 tests/neg-macros/i19941.scala rename tests/{pos-macros => neg-macros}/quotedPatterns-4.scala (64%) diff --git a/compiler/src/dotty/tools/dotc/quoted/QuotePatterns.scala b/compiler/src/dotty/tools/dotc/quoted/QuotePatterns.scala index 76961f691617..3eba9e3e12b2 100644 --- a/compiler/src/dotty/tools/dotc/quoted/QuotePatterns.scala +++ b/compiler/src/dotty/tools/dotc/quoted/QuotePatterns.scala @@ -28,7 +28,9 @@ object QuotePatterns: /** Check for restricted patterns */ def checkPattern(quotePattern: QuotePattern)(using Context): Unit = new tpd.TreeTraverser { def traverse(tree: Tree)(using Context): Unit = tree match { - case _: SplicePattern => + case tree: SplicePattern => + if !tree.body.typeOpt.derivesFrom(defn.QuotedExprClass) then + report.error(i"Spice pattern must match an Expr[...]", tree.body.srcPos) case tdef: TypeDef if tdef.symbol.isClass => val kind = if tdef.symbol.is(Module) then "objects" else "classes" report.error(em"Implementation restriction: cannot match $kind", tree.srcPos) diff --git a/tests/neg-macros/i19941.check b/tests/neg-macros/i19941.check new file mode 100644 index 000000000000..3f0da80f08fe --- /dev/null +++ b/tests/neg-macros/i19941.check @@ -0,0 +1,4 @@ +-- Error: tests/neg-macros/i19941.scala:7:14 --------------------------------------------------------------------------- +7 | case '{ ${hd *: tl} : *:[Int, EmptyTuple] } => '{ ??? } // error + | ^^^^^^^^ + | Spice pattern must match an Expr[...] diff --git a/tests/neg-macros/i19941.scala b/tests/neg-macros/i19941.scala new file mode 100644 index 000000000000..58a2275d055b --- /dev/null +++ b/tests/neg-macros/i19941.scala @@ -0,0 +1,7 @@ +import scala.quoted.* + +inline def expandMacro(inline from: Tuple): Any = ${ expandMacroImpl } + +def expandMacroImpl(using Quotes): Expr[?] = + '{ 1 *: EmptyTuple } match + case '{ ${hd *: tl} : *:[Int, EmptyTuple] } => '{ ??? } // error diff --git a/tests/pos-macros/quotedPatterns-4.scala b/tests/neg-macros/quotedPatterns-4.scala similarity index 64% rename from tests/pos-macros/quotedPatterns-4.scala rename to tests/neg-macros/quotedPatterns-4.scala index 309b026e3d54..811196bc3a3b 100644 --- a/tests/pos-macros/quotedPatterns-4.scala +++ b/tests/neg-macros/quotedPatterns-4.scala @@ -3,7 +3,7 @@ object Test { def impl(receiver: Expr[StringContext])(using qctx: scala.quoted.Quotes) = { import quotes.reflect.Repeated receiver match { - case '{ StringContext(${Repeated(parts, tpt)}*) } => // now OK + case '{ StringContext(${Repeated(parts, tpt)}*) } => // error: Repeated is not an Expr pattern } } } From 2bcf0dbb34b305e435888afd01a7d43d817b339b Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Tue, 19 Mar 2024 13:35:23 +0100 Subject: [PATCH 2/2] Apply suggestions from code review Co-authored-by: Jan Chyb <48855024+jchyb@users.noreply.github.com> --- compiler/src/dotty/tools/dotc/quoted/QuotePatterns.scala | 2 +- tests/neg-macros/i19941.check | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/quoted/QuotePatterns.scala b/compiler/src/dotty/tools/dotc/quoted/QuotePatterns.scala index 3eba9e3e12b2..1ebf2ae5714b 100644 --- a/compiler/src/dotty/tools/dotc/quoted/QuotePatterns.scala +++ b/compiler/src/dotty/tools/dotc/quoted/QuotePatterns.scala @@ -30,7 +30,7 @@ object QuotePatterns: def traverse(tree: Tree)(using Context): Unit = tree match { case tree: SplicePattern => if !tree.body.typeOpt.derivesFrom(defn.QuotedExprClass) then - report.error(i"Spice pattern must match an Expr[...]", tree.body.srcPos) + report.error(i"Splice pattern must match an Expr[...]", tree.body.srcPos) case tdef: TypeDef if tdef.symbol.isClass => val kind = if tdef.symbol.is(Module) then "objects" else "classes" report.error(em"Implementation restriction: cannot match $kind", tree.srcPos) diff --git a/tests/neg-macros/i19941.check b/tests/neg-macros/i19941.check index 3f0da80f08fe..6d39e2bef4d8 100644 --- a/tests/neg-macros/i19941.check +++ b/tests/neg-macros/i19941.check @@ -1,4 +1,4 @@ -- Error: tests/neg-macros/i19941.scala:7:14 --------------------------------------------------------------------------- 7 | case '{ ${hd *: tl} : *:[Int, EmptyTuple] } => '{ ??? } // error | ^^^^^^^^ - | Spice pattern must match an Expr[...] + | Splice pattern must match an Expr[...]