From b65ef59d9bdd6a524eaef2bc12a4e33ca1e0375b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pa=C5=82ka?= Date: Wed, 10 May 2023 13:41:35 +0200 Subject: [PATCH] Raise a warning instead of an error for a type ascription on a pattern other than a variable or a number literal. This partially reverts the changes from https://github.com/lampepfl/dotty/pull/16150. This change is motivated by not breaking source compatibility for a number of projects in the Open Community Build. --- .../dotty/tools/dotc/parsing/Parsers.scala | 10 +-- docs/_docs/internals/syntax.md | 5 +- docs/_docs/reference/syntax.md | 5 +- .../fatal-warnings}/i10994.scala | 0 .../fatal-warnings}/i15893.scala | 6 +- tests/neg/t5702-neg-bad-and-wild.check | 10 ++- tests/pending/run/i15893.scala | 6 +- tests/pos/i10994.scala | 2 + tests/pos/i15893.scala | 61 +++++++++++++++++++ 9 files changed, 83 insertions(+), 22 deletions(-) rename tests/{neg => neg-custom-args/fatal-warnings}/i10994.scala (100%) rename tests/{neg => neg-custom-args/fatal-warnings}/i15893.scala (89%) create mode 100644 tests/pos/i10994.scala create mode 100644 tests/pos/i15893.scala diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 7fecdd2c5eae..170332f7abe8 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2871,14 +2871,14 @@ object Parsers { if (isIdent(nme.raw.BAR)) { in.nextToken(); pattern1(location) :: patternAlts(location) } else Nil - /** Pattern1 ::= PatVar Ascription - * | [‘-’] integerLiteral Ascription - * | [‘-’] floatingPointLiteral Ascription - * | Pattern2 + /** Pattern1 ::= Pattern2 [Ascription] */ def pattern1(location: Location = Location.InPattern): Tree = val p = pattern2() - if (isVarPattern(p) || p.isInstanceOf[Number]) && in.isColon then + if in.isColon then + val isVariableOrNumber = isVarPattern(p) || p.isInstanceOf[Number] + if !isVariableOrNumber then + warning(em"Only variable and number literal patterns can have type ascriptions") in.nextToken() ascription(p, location) else p diff --git a/docs/_docs/internals/syntax.md b/docs/_docs/internals/syntax.md index c4e71ad11612..e667a429afed 100644 --- a/docs/_docs/internals/syntax.md +++ b/docs/_docs/internals/syntax.md @@ -314,10 +314,7 @@ TypeCaseClauses ::= TypeCaseClause { TypeCaseClause } TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi] Pattern ::= Pattern1 { ‘|’ Pattern1 } Alternative(pats) -Pattern1 ::= PatVar ‘:’ RefinedType Bind(name, Typed(Ident(wildcard), tpe)) - | [‘-’] integerLiteral ‘:’ RefinedType Typed(pat, tpe) - | [‘-’] floatingPointLiteral ‘:’ RefinedType Typed(pat, tpe) - | Pattern2 +Pattern1 ::= Pattern2 [‘:’ RefinedType] Bind(name, Typed(Ident(wildcard), tpe)) Pattern2 ::= [id ‘@’] InfixPattern [‘*’] Bind(name, pat) InfixPattern ::= SimplePattern { id [nl] SimplePattern } InfixOp(pat, op, pat) SimplePattern ::= PatVar Ident(wildcard) diff --git a/docs/_docs/reference/syntax.md b/docs/_docs/reference/syntax.md index dd2e7ce118c4..28259d94afb4 100644 --- a/docs/_docs/reference/syntax.md +++ b/docs/_docs/reference/syntax.md @@ -312,10 +312,7 @@ TypeCaseClauses ::= TypeCaseClause { TypeCaseClause } TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi] Pattern ::= Pattern1 { ‘|’ Pattern1 } -Pattern1 ::= PatVar ‘:’ RefinedType - | [‘-’] integerLiteral ‘:’ RefinedType - | [‘-’] floatingPointLiteral ‘:’ RefinedType - | Pattern2 +Pattern1 ::= Pattern2 [‘:’ RefinedType] Pattern2 ::= [id ‘@’] InfixPattern [‘*’] InfixPattern ::= SimplePattern { id [nl] SimplePattern } SimplePattern ::= PatVar diff --git a/tests/neg/i10994.scala b/tests/neg-custom-args/fatal-warnings/i10994.scala similarity index 100% rename from tests/neg/i10994.scala rename to tests/neg-custom-args/fatal-warnings/i10994.scala diff --git a/tests/neg/i15893.scala b/tests/neg-custom-args/fatal-warnings/i15893.scala similarity index 89% rename from tests/neg/i15893.scala rename to tests/neg-custom-args/fatal-warnings/i15893.scala index 997c51179099..f23e6150106a 100644 --- a/tests/neg/i15893.scala +++ b/tests/neg-custom-args/fatal-warnings/i15893.scala @@ -22,7 +22,7 @@ transparent inline def transparentInlineMod2(inline n: NatT): NatT = inline n m case Succ(Zero()) => Succ(Zero()) case Succ(Succ(predPredN)) => transparentInlineMod2(predPredN) -def dependentlyTypedMod2[N <: NatT](n: N): Mod2[N] = n match // exhaustivity warning; unexpected +def dependentlyTypedMod2[N <: NatT](n: N): Mod2[N] = n match case Zero(): Zero => Zero() // error case Succ(Zero()): Succ[Zero] => Succ(Zero()) // error case Succ(Succ(predPredN)): Succ[Succ[_]] => dependentlyTypedMod2(predPredN) // error @@ -57,5 +57,5 @@ inline def transparentInlineFoo(inline n: NatT): NatT = inline transparentInline println(transparentInlineMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected println(transparentInlineFoo(Succ(Succ(Succ(Zero()))))) // prints Zero(), as expected println(dependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // runtime error; unexpected -// println(inlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // doesn't compile; unexpected -// println(transparentInlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // doesn't compile; unexpected + println(inlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected + println(transparentInlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected diff --git a/tests/neg/t5702-neg-bad-and-wild.check b/tests/neg/t5702-neg-bad-and-wild.check index 731195411069..36ac71b2e1e7 100644 --- a/tests/neg/t5702-neg-bad-and-wild.check +++ b/tests/neg/t5702-neg-bad-and-wild.check @@ -10,10 +10,10 @@ | pattern expected | | longer explanation available when compiling with `-explain` --- [E040] Syntax Error: tests/neg/t5702-neg-bad-and-wild.scala:13:22 --------------------------------------------------- +-- [E040] Syntax Error: tests/neg/t5702-neg-bad-and-wild.scala:13:23 --------------------------------------------------- 13 | case List(1, _*3:) => // error // error - | ^ - | ')' expected, but ':' found + | ^ + | an identifier expected, but ')' found -- [E032] Syntax Error: tests/neg/t5702-neg-bad-and-wild.scala:15:18 --------------------------------------------------- 15 | case List(x*, 1) => // error: pattern expected | ^ @@ -56,6 +56,10 @@ | Recursive value $1$ needs type | | longer explanation available when compiling with `-explain` +-- Warning: tests/neg/t5702-neg-bad-and-wild.scala:13:22 --------------------------------------------------------------- +13 | case List(1, _*3:) => // error // error + | ^ + | Only variable and number literal patterns can have type ascriptions -- Warning: tests/neg/t5702-neg-bad-and-wild.scala:22:20 --------------------------------------------------------------- 22 | val K(x @ _*) = k | ^ diff --git a/tests/pending/run/i15893.scala b/tests/pending/run/i15893.scala index dedec2138f2a..d9cd2822e971 100644 --- a/tests/pending/run/i15893.scala +++ b/tests/pending/run/i15893.scala @@ -24,7 +24,7 @@ transparent inline def transparentInlineMod2(inline n: NatT): NatT = inline n m case Succ(Zero()) => Succ(Zero()) case Succ(Succ(predPredN)) => transparentInlineMod2(predPredN) */ -def dependentlyTypedMod2[N <: NatT](n: N): Mod2[N] = n match // exhaustivity warning; unexpected +def dependentlyTypedMod2[N <: NatT](n: N): Mod2[N] = n match case Zero(): Zero => Zero() case Succ(Zero()): Succ[Zero] => Succ(Zero()) case Succ(Succ(predPredN)): Succ[Succ[_]] => dependentlyTypedMod2(predPredN) @@ -61,5 +61,5 @@ inline def transparentInlineFoo(inline n: NatT): NatT = inline transparentInline println(transparentInlineFoo(Succ(Succ(Succ(Zero()))))) // prints Zero(), as expected */ println(dependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // runtime error; unexpected -// println(inlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // doesn't compile; unexpected -// println(transparentInlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // doesn't compile; unexpected +// println(inlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected +// println(transparentInlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected diff --git a/tests/pos/i10994.scala b/tests/pos/i10994.scala new file mode 100644 index 000000000000..99ae647466b1 --- /dev/null +++ b/tests/pos/i10994.scala @@ -0,0 +1,2 @@ +def foo = true match + case (b: Boolean): Boolean => () diff --git a/tests/pos/i15893.scala b/tests/pos/i15893.scala new file mode 100644 index 000000000000..af6e7ae38ad2 --- /dev/null +++ b/tests/pos/i15893.scala @@ -0,0 +1,61 @@ +sealed trait NatT +case class Zero() extends NatT +case class Succ[+N <: NatT](n: N) extends NatT + +type Mod2[N <: NatT] <: NatT = N match + case Zero => Zero + case Succ[Zero] => Succ[Zero] + case Succ[Succ[predPredN]] => Mod2[predPredN] + +def mod2(n: NatT): NatT = n match + case Zero() => Zero() + case Succ(Zero()) => Succ(Zero()) + case Succ(Succ(predPredN)) => mod2(predPredN) + +inline def inlineMod2(inline n: NatT): NatT = inline n match + case Zero() => Zero() + case Succ(Zero()) => Succ(Zero()) + case Succ(Succ(predPredN)) => inlineMod2(predPredN) + +transparent inline def transparentInlineMod2(inline n: NatT): NatT = inline n match + case Zero() => Zero() + case Succ(Zero()) => Succ(Zero()) + case Succ(Succ(predPredN)) => transparentInlineMod2(predPredN) + +def dependentlyTypedMod2[N <: NatT](n: N): Mod2[N] = n match + case Zero(): Zero => Zero() // warning + case Succ(Zero()): Succ[Zero] => Succ(Zero()) // warning + case Succ(Succ(predPredN)): Succ[Succ[_]] => dependentlyTypedMod2(predPredN) // warning + +inline def inlineDependentlyTypedMod2[N <: NatT](inline n: N): Mod2[N] = inline n match + case Zero(): Zero => Zero() // warning + case Succ(Zero()): Succ[Zero] => Succ(Zero()) // warning + case Succ(Succ(predPredN)): Succ[Succ[_]] => inlineDependentlyTypedMod2(predPredN) // warning + +transparent inline def transparentInlineDependentlyTypedMod2[N <: NatT](inline n: N): Mod2[N] = inline n match + case Zero(): Zero => Zero() // warning + case Succ(Zero()): Succ[Zero] => Succ(Zero()) // warning + case Succ(Succ(predPredN)): Succ[Succ[_]] => transparentInlineDependentlyTypedMod2(predPredN) // warning + +def foo(n: NatT): NatT = mod2(n) match + case Succ(Zero()) => Zero() + case _ => n + +inline def inlineFoo(inline n: NatT): NatT = inline inlineMod2(n) match + case Succ(Zero()) => Zero() + case _ => n + +inline def transparentInlineFoo(inline n: NatT): NatT = inline transparentInlineMod2(n) match + case Succ(Zero()) => Zero() + case _ => n + +@main def main(): Unit = + println(mod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected + println(foo(Succ(Succ(Succ(Zero()))))) // prints Zero(), as expected + println(inlineMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected + println(inlineFoo(Succ(Succ(Succ(Zero()))))) // prints Succ(Succ(Succ(Zero()))); unexpected + println(transparentInlineMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected + println(transparentInlineFoo(Succ(Succ(Succ(Zero()))))) // prints Zero(), as expected + println(dependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // runtime error; unexpected + println(inlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected + println(transparentInlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected