From bf6974ecc7e6d77f06088f60e30841148d04fb4f Mon Sep 17 00:00:00 2001 From: odersky Date: Sun, 25 Jun 2023 16:20:16 +0200 Subject: [PATCH 1/3] Fix syntax and parsing of vararg patterns Syntax: Remove outdated `*` after InfixPattern Parsing: Only allow vararg `*` in ArgumentPatterns Fixes #17443 --- .../dotty/tools/dotc/parsing/Parsers.scala | 22 +++++++++---------- docs/_docs/internals/syntax.md | 2 +- docs/_docs/reference/syntax.md | 2 +- tests/neg/i17443.scala | 2 ++ 4 files changed, 15 insertions(+), 13 deletions(-) create mode 100644 tests/neg/i17443.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 65e303dbaa4a..96b82d2336b9 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1666,7 +1666,7 @@ object Parsers { if in.token == LPAREN then funParamClause() :: funParamClauses() else Nil /** InfixType ::= RefinedType {id [nl] RefinedType} - * | RefinedType `^` + * | RefinedType `^` // under capture checking */ def infixType(): Tree = infixTypeRest(refinedType()) @@ -2881,13 +2881,13 @@ object Parsers { if (isIdent(nme.raw.BAR)) { in.nextToken(); pattern1(location) :: patternAlts(location) } else Nil - /** Pattern1 ::= PatVar Ascription - * | [‘-’] integerLiteral Ascription - * | [‘-’] floatingPointLiteral Ascription + /** Pattern1 ::= PatVar `:` RefinedType + * | [‘-’] integerLiteral `:` RefinedType + * | [‘-’] floatingPointLiteral `:` RefinedType * | Pattern2 */ def pattern1(location: Location = Location.InPattern): Tree = - val p = pattern2() + val p = pattern2(location) if in.isColon then val isVariableOrNumber = isVarPattern(p) || p.isInstanceOf[Number] if !isVariableOrNumber then @@ -2905,11 +2905,10 @@ object Parsers { else p /** Pattern3 ::= InfixPattern - * | PatVar ‘*’ */ - def pattern3(): Tree = + def pattern3(location: Location): Tree = val p = infixPattern() - if followingIsVararg() then + if location.inArgs && followingIsVararg() then val start = in.skipToken() p match case p @ Ident(name) if name.isVarPattern => @@ -2921,10 +2920,10 @@ object Parsers { /** Pattern2 ::= [id `@'] Pattern3 */ - val pattern2: () => Tree = () => pattern3() match + val pattern2: Location => Tree = location => pattern3(location) match case p @ Ident(name) if in.token == AT => val offset = in.skipToken() - pattern3() match { + pattern3(location) match { case pt @ Bind(nme.WILDCARD, pt1: Typed) if pt.mods.is(Given) => atSpan(startOffset(p), 0) { Bind(name, pt1).withMods(pt.mods) } case Typed(Ident(nme.WILDCARD), pt @ Ident(tpnme.WILDCARD_STAR)) => @@ -2954,6 +2953,7 @@ object Parsers { * | XmlPattern * | `(' [Patterns] `)' * | SimplePattern1 [TypeArgs] [ArgumentPatterns] + * | ‘given’ RefinedType * SimplePattern1 ::= SimpleRef * | SimplePattern1 `.' id * PatVar ::= id @@ -3597,7 +3597,7 @@ object Parsers { * VarDcl ::= id {`,' id} `:' Type */ def patDefOrDcl(start: Offset, mods: Modifiers): Tree = atSpan(start, nameStart) { - val first = pattern2() + val first = pattern2(Location.InPattern) var lhs = first match { case id: Ident if in.token == COMMA => in.nextToken() diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index ace038033f7e..9c3802a45cba 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -317,7 +317,7 @@ Pattern1 ::= PatVar ‘:’ RefinedType | [‘-’] integerLiteral ‘:’ RefinedType Typed(pat, tpe) | [‘-’] floatingPointLiteral ‘:’ RefinedType Typed(pat, tpe) | Pattern2 -Pattern2 ::= [id ‘@’] InfixPattern [‘*’] Bind(name, pat) +Pattern2 ::= [id ‘@’] InfixPattern Bind(name, pat) InfixPattern ::= SimplePattern { id [nl] SimplePattern } InfixOp(pat, op, pat) SimplePattern ::= PatVar Ident(wildcard) | Literal Bind(name, Ident(wildcard)) diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index b4b3aab6b02f..c24070652b14 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -318,7 +318,7 @@ Pattern1 ::= PatVar ‘:’ RefinedType | [‘-’] integerLiteral ‘:’ RefinedType | [‘-’] floatingPointLiteral ‘:’ RefinedType | Pattern2 -Pattern2 ::= [id ‘@’] InfixPattern [‘*’] +Pattern2 ::= [id ‘@’] InfixPattern InfixPattern ::= SimplePattern { id [nl] SimplePattern } SimplePattern ::= PatVar | Literal diff --git a/tests/neg/i17443.scala b/tests/neg/i17443.scala new file mode 100644 index 000000000000..95e963b6a1d9 --- /dev/null +++ b/tests/neg/i17443.scala @@ -0,0 +1,2 @@ +def run() = + val x = List(1) match { case (xs*) => xs } // error From 1faade510de11211c59a0e299dc0c63021885d87 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 26 Jun 2023 10:08:25 +0200 Subject: [PATCH 2/3] Reclassify test and improve error message --- .../src/dotty/tools/dotc/parsing/Parsers.scala | 18 +++++++++++------- tests/neg/i8715.check | 4 ++++ tests/neg/i8715.scala | 2 ++ tests/pos/i8715.scala | 2 -- 4 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 tests/neg/i8715.check create mode 100644 tests/neg/i8715.scala delete mode 100644 tests/pos/i8715.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 96b82d2336b9..d5dce6dc5212 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2908,14 +2908,18 @@ object Parsers { */ def pattern3(location: Location): Tree = val p = infixPattern() - if location.inArgs && followingIsVararg() then + if followingIsVararg() then val start = in.skipToken() - p match - case p @ Ident(name) if name.isVarPattern => - Typed(p, atSpan(start) { Ident(tpnme.WILDCARD_STAR) }) - case _ => - syntaxError(em"`*` must follow pattern variable", start) - p + if location.inArgs then + p match + case p @ Ident(name) if name.isVarPattern => + Typed(p, atSpan(start) { Ident(tpnme.WILDCARD_STAR) }) + case _ => + syntaxError(em"`*` must follow pattern variable", start) + p + else + syntaxError(em"bad use of `*` - sequence pattern not allowed here", start) + p else p /** Pattern2 ::= [id `@'] Pattern3 diff --git a/tests/neg/i8715.check b/tests/neg/i8715.check new file mode 100644 index 000000000000..c00e5e150193 --- /dev/null +++ b/tests/neg/i8715.check @@ -0,0 +1,4 @@ +-- Error: tests/neg/i8715.scala:2:46 ----------------------------------------------------------------------------------- +2 |def Test = List(42) match { case List(xs @ (ys*)) => xs } // error + | ^ + | bad use of `*` - sequence pattern not allowed here diff --git a/tests/neg/i8715.scala b/tests/neg/i8715.scala new file mode 100644 index 000000000000..90610fd788f8 --- /dev/null +++ b/tests/neg/i8715.scala @@ -0,0 +1,2 @@ +@main +def Test = List(42) match { case List(xs @ (ys*)) => xs } // error diff --git a/tests/pos/i8715.scala b/tests/pos/i8715.scala deleted file mode 100644 index 0490ce53c8cf..000000000000 --- a/tests/pos/i8715.scala +++ /dev/null @@ -1,2 +0,0 @@ -@main -def Test = List(42) match { case List(xs @ (ys*)) => xs } From d7ec913a5071725c930e12549dd4bdd4a0fcfee6 Mon Sep 17 00:00:00 2001 From: odersky Date: Mon, 26 Jun 2023 21:36:06 +0200 Subject: [PATCH 3/3] Fix test --- tests/neg/t5702-neg-bad-and-wild.check | 16 ++++++++-------- tests/neg/t5702-neg-bad-and-wild.scala | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/neg/t5702-neg-bad-and-wild.check b/tests/neg/t5702-neg-bad-and-wild.check index c461b76ea70b..c43ca54438d3 100644 --- a/tests/neg/t5702-neg-bad-and-wild.check +++ b/tests/neg/t5702-neg-bad-and-wild.check @@ -20,6 +20,10 @@ | pattern expected | | longer explanation available when compiling with `-explain` +-- Error: tests/neg/t5702-neg-bad-and-wild.scala:16:16 ----------------------------------------------------------------- +16 | case (1, x*) => // error: bad use of * + | ^ + | bad use of `*` - sequence pattern not allowed here -- [E031] Syntax Error: tests/neg/t5702-neg-bad-and-wild.scala:17:18 --------------------------------------------------- 17 | case (1, x: _*) => // error: bad use of _* (sequence pattern not allowed) | ^ @@ -32,6 +36,10 @@ | pattern expected | | longer explanation available when compiling with `-explain` +-- Error: tests/neg/t5702-neg-bad-and-wild.scala:25:14 ----------------------------------------------------------------- +25 | val (b, _ * ) = (5,6) // error: bad use of `*` + | ^ + | bad use of `*` - sequence pattern not allowed here -- [E161] Naming Error: tests/neg/t5702-neg-bad-and-wild.scala:24:10 --------------------------------------------------- 24 | val K(x) = k // error: x is already defined as value x | ^^^^^^^^^^^^ @@ -71,11 +79,3 @@ | If the narrowing is intentional, this can be communicated by adding `: @unchecked` after the expression, | which may result in a MatchError at runtime. | This patch can be rewritten automatically under -rewrite -source 3.2-migration. --- Warning: tests/neg/t5702-neg-bad-and-wild.scala:25:20 --------------------------------------------------------------- -25 | val (b, _ * ) = (5,6) // ok - | ^^^^^ - | pattern's type Int* does not match the right hand side expression's type Int - | - | If the narrowing is intentional, this can be communicated by adding `: @unchecked` after the expression, - | which may result in a MatchError at runtime. - | This patch can be rewritten automatically under -rewrite -source 3.2-migration. diff --git a/tests/neg/t5702-neg-bad-and-wild.scala b/tests/neg/t5702-neg-bad-and-wild.scala index 95d00c270e89..8a4f51962ff2 100644 --- a/tests/neg/t5702-neg-bad-and-wild.scala +++ b/tests/neg/t5702-neg-bad-and-wild.scala @@ -13,7 +13,7 @@ object Test { case List(1, _*3:) => // error // error case List(1, x*) => // ok case List(x*, 1) => // error: pattern expected - case (1, x*) => //ok + case (1, x*) => // error: bad use of * case (1, x: _*) => // error: bad use of _* (sequence pattern not allowed) } @@ -22,7 +22,7 @@ object Test { val K(x @ _*) = k val K(ns @ _*, xx) = k // error: pattern expected // error val K(x) = k // error: x is already defined as value x - val (b, _ * ) = (5,6) // ok + val (b, _ * ) = (5,6) // error: bad use of `*` // no longer complains //bad-and-wild.scala:15: error: ')' expected but '}' found. }