Skip to content

Commit c5181b9

Browse files
authored
Restrict syntax of typed patterns (#16150)
Fix undefined Fixes undefined
2 parents 671b1f5 + 27ba2e1 commit c5181b9

File tree

14 files changed

+97
-29
lines changed

14 files changed

+97
-29
lines changed

compiler/src/dotty/tools/dotc/core/Types.scala

+8-5
Original file line numberDiff line numberDiff line change
@@ -1277,11 +1277,14 @@ object Types {
12771277
* then the top-level union isn't widened. This is needed so that type inference can infer nullable types.
12781278
*/
12791279
def widenUnion(using Context): Type = widen match
1280-
case tp @ OrNull(tp1): OrType =>
1281-
// Don't widen `T|Null`, since otherwise we wouldn't be able to infer nullable unions.
1282-
val tp1Widen = tp1.widenUnionWithoutNull
1283-
if (tp1Widen.isRef(defn.AnyClass)) tp1Widen
1284-
else tp.derivedOrType(tp1Widen, defn.NullType)
1280+
case tp: OrType => tp match
1281+
case OrNull(tp1) =>
1282+
// Don't widen `T|Null`, since otherwise we wouldn't be able to infer nullable unions.
1283+
val tp1Widen = tp1.widenUnionWithoutNull
1284+
if (tp1Widen.isRef(defn.AnyClass)) tp1Widen
1285+
else tp.derivedOrType(tp1Widen, defn.NullType)
1286+
case _ =>
1287+
tp.widenUnionWithoutNull
12851288
case tp =>
12861289
tp.widenUnionWithoutNull
12871290

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

+5-2
Original file line numberDiff line numberDiff line change
@@ -2822,11 +2822,14 @@ object Parsers {
28222822
if (isIdent(nme.raw.BAR)) { in.nextToken(); pattern1(location) :: patternAlts(location) }
28232823
else Nil
28242824

2825-
/** Pattern1 ::= Pattern2 [Ascription]
2825+
/** Pattern1 ::= PatVar Ascription
2826+
* | [‘-’] integerLiteral Ascription
2827+
* | [‘-’] floatingPointLiteral Ascription
2828+
* | Pattern2
28262829
*/
28272830
def pattern1(location: Location = Location.InPattern): Tree =
28282831
val p = pattern2()
2829-
if in.isColon then
2832+
if (isVarPattern(p) || p.isInstanceOf[Number]) && in.isColon then
28302833
in.nextToken()
28312834
ascription(p, location)
28322835
else p

compiler/src/dotty/tools/dotc/typer/Typer.scala

+4-2
Original file line numberDiff line numberDiff line change
@@ -476,13 +476,15 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
476476
* (x: T | Null) => x.$asInstanceOf$[x.type & T]
477477
*/
478478
def toNotNullTermRef(tree: Tree, pt: Type)(using Context): Tree = tree.tpe match
479-
case ref @ OrNull(tpnn) : TermRef
479+
case ref: TermRef
480480
if pt != AssignProto && // Ensure it is not the lhs of Assign
481481
ctx.notNullInfos.impliesNotNull(ref) &&
482482
// If a reference is in the context, it is already trackable at the point we add it.
483483
// Hence, we don't use isTracked in the next line, because checking use out of order is enough.
484484
!ref.usedOutOfOrder =>
485-
tree.cast(AndType(ref, tpnn))
485+
ref match
486+
case OrNull(tpnn) => tree.cast(AndType(ref, tpnn))
487+
case _ => tree
486488
case _ =>
487489
tree
488490

docs/_docs/internals/syntax.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,10 @@ TypeCaseClauses ::= TypeCaseClause { TypeCaseClause }
318318
TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi]
319319
320320
Pattern ::= Pattern1 { ‘|’ Pattern1 } Alternative(pats)
321-
Pattern1 ::= Pattern2 [‘:’ RefinedType] Bind(name, Typed(Ident(wildcard), tpe))
321+
Pattern1 ::= PatVar ‘:’ RefinedType Bind(name, Typed(Ident(wildcard), tpe))
322+
| [‘-’] integerLiteral ‘:’ RefinedType Typed(pat, tpe)
323+
| [‘-’] floatingPointLiteral ‘:’ RefinedType Typed(pat, tpe)
324+
| Pattern2
322325
Pattern2 ::= [id ‘@’] InfixPattern [‘*’] Bind(name, pat)
323326
InfixPattern ::= SimplePattern { id [nl] SimplePattern } InfixOp(pat, op, pat)
324327
SimplePattern ::= PatVar Ident(wildcard)

docs/_docs/reference/syntax.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,10 @@ TypeCaseClauses ::= TypeCaseClause { TypeCaseClause }
312312
TypeCaseClause ::= ‘case’ (InfixType | ‘_’) ‘=>’ Type [semi]
313313
314314
Pattern ::= Pattern1 { ‘|’ Pattern1 }
315-
Pattern1 ::= Pattern2 [‘:’ RefinedType]
315+
Pattern1 ::= PatVar ‘:’ RefinedType
316+
| [‘-’] integerLiteral ‘:’ RefinedType
317+
| [‘-’] floatingPointLiteral ‘:’ RefinedType
318+
| Pattern2
316319
Pattern2 ::= [id ‘@’] InfixPattern [‘*’]
317320
InfixPattern ::= SimplePattern { id [nl] SimplePattern }
318321
SimplePattern ::= PatVar

tests/neg/i10994.scala

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
def foo = true match
2+
case (b: Boolean): Boolean => () // error

tests/neg/i15893.scala

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
sealed trait NatT
2+
case class Zero() extends NatT
3+
case class Succ[+N <: NatT](n: N) extends NatT
4+
5+
type Mod2[N <: NatT] <: NatT = N match
6+
case Zero => Zero
7+
case Succ[Zero] => Succ[Zero]
8+
case Succ[Succ[predPredN]] => Mod2[predPredN]
9+
10+
def mod2(n: NatT): NatT = n match
11+
case Zero() => Zero()
12+
case Succ(Zero()) => Succ(Zero())
13+
case Succ(Succ(predPredN)) => mod2(predPredN)
14+
15+
inline def inlineMod2(inline n: NatT): NatT = inline n match
16+
case Zero() => Zero()
17+
case Succ(Zero()) => Succ(Zero())
18+
case Succ(Succ(predPredN)) => inlineMod2(predPredN)
19+
20+
transparent inline def transparentInlineMod2(inline n: NatT): NatT = inline n match
21+
case Zero() => Zero()
22+
case Succ(Zero()) => Succ(Zero())
23+
case Succ(Succ(predPredN)) => transparentInlineMod2(predPredN)
24+
25+
def dependentlyTypedMod2[N <: NatT](n: N): Mod2[N] = n match // exhaustivity warning; unexpected
26+
case Zero(): Zero => Zero() // error
27+
case Succ(Zero()): Succ[Zero] => Succ(Zero()) // error
28+
case Succ(Succ(predPredN)): Succ[Succ[_]] => dependentlyTypedMod2(predPredN) // error
29+
30+
inline def inlineDependentlyTypedMod2[N <: NatT](inline n: N): Mod2[N] = inline n match
31+
case Zero(): Zero => Zero() // error
32+
case Succ(Zero()): Succ[Zero] => Succ(Zero()) // error
33+
case Succ(Succ(predPredN)): Succ[Succ[_]] => inlineDependentlyTypedMod2(predPredN) // error
34+
35+
transparent inline def transparentInlineDependentlyTypedMod2[N <: NatT](inline n: N): Mod2[N] = inline n match
36+
case Zero(): Zero => Zero() // error
37+
case Succ(Zero()): Succ[Zero] => Succ(Zero()) // error
38+
case Succ(Succ(predPredN)): Succ[Succ[_]] => transparentInlineDependentlyTypedMod2(predPredN) // error
39+
40+
def foo(n: NatT): NatT = mod2(n) match
41+
case Succ(Zero()) => Zero()
42+
case _ => n
43+
44+
inline def inlineFoo(inline n: NatT): NatT = inline inlineMod2(n) match
45+
case Succ(Zero()) => Zero()
46+
case _ => n
47+
48+
inline def transparentInlineFoo(inline n: NatT): NatT = inline transparentInlineMod2(n) match
49+
case Succ(Zero()) => Zero()
50+
case _ => n
51+
52+
@main def main(): Unit =
53+
println(mod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected
54+
println(foo(Succ(Succ(Succ(Zero()))))) // prints Zero(), as expected
55+
println(inlineMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected
56+
println(inlineFoo(Succ(Succ(Succ(Zero()))))) // prints Succ(Succ(Succ(Zero()))); unexpected
57+
println(transparentInlineMod2(Succ(Succ(Succ(Zero()))))) // prints Succ(Zero()), as expected
58+
println(transparentInlineFoo(Succ(Succ(Succ(Zero()))))) // prints Zero(), as expected
59+
println(dependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // runtime error; unexpected
60+
// println(inlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // doesn't compile; unexpected
61+
// println(transparentInlineDependentlyTypedMod2(Succ(Succ(Succ(Zero()))))) // doesn't compile; unexpected

tests/neg/t5702-neg-bad-and-wild.check

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
| pattern expected
1111
|
1212
| longer explanation available when compiling with `-explain`
13-
-- [E040] Syntax Error: tests/neg/t5702-neg-bad-and-wild.scala:13:23 ---------------------------------------------------
13+
-- [E040] Syntax Error: tests/neg/t5702-neg-bad-and-wild.scala:13:22 ---------------------------------------------------
1414
13 | case List(1, _*3:) => // error // error
15-
| ^
16-
| an identifier expected, but ')' found
15+
| ^
16+
| ')' expected, but ':' found
1717
-- [E032] Syntax Error: tests/neg/t5702-neg-bad-and-wild.scala:15:18 ---------------------------------------------------
1818
15 | case List(x*, 1) => // error: pattern expected
1919
| ^

tests/pos-macros/i11211.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def takeOptionImpl2[T](using Quotes, Type[T]): Unit = '{
1212
def takeOptionImpl[T](o: Expr[Option[T]], default: Expr[T])(using Quotes, Type[T]): Expr[T] = '{
1313
$o match {
1414
case Some(t1) => t1
15-
case None: Option[T] => $default
15+
case None => $default
1616
}
1717
}
1818

tests/pos-special/fatal-warnings/i10994.scala

-2
This file was deleted.

tests/pos/patmat.scala

+1-6
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ object Test {
1616
}
1717

1818
(xs.length, xs) match {
19-
case (0, Nil: List[Int]) => println("1")
19+
case (0, Nil) => println("1")
2020
case (_, Nil) => println("2")
2121
case (0, _) => println("3")
2222
case (x, y) => println("4")
@@ -46,9 +46,4 @@ object Test {
4646
case Some(s) => println(s)
4747
case None => println("nothing")
4848
}
49-
50-
type IntPair = (Int, Int)
51-
??? match {
52-
case (x, y): IntPair => x * y
53-
}
5449
}

tests/semanticdb/expect/ValPattern.expect.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class ValPattern/*<-example::ValPattern#*/ {
66
val Some/*->scala::Some.*/(number1/*<-example::ValPattern#number1.*/) =
77
Some/*->scala::Some.*/(1)
88

9-
val List/*->scala::package.List.*/(Some/*->scala::Some.*/(q1/*<-example::ValPattern#q1.*/), None/*->scala::None.*/: None/*->scala::None.*/.type, None/*->scala::None.*/) = ???/*->scala::Predef.`???`().*/
9+
val List/*->scala::package.List.*/(Some/*->scala::Some.*/(q1/*<-example::ValPattern#q1.*/), None/*->scala::None.*/) = ???/*->scala::Predef.`???`().*/
1010

1111
var (leftVar/*<-example::ValPattern#leftVar().*/, rightVar/*<-example::ValPattern#rightVar().*/) = (1, 2)
1212
var Some/*->scala::Some.*/(number1Var/*<-example::ValPattern#number1Var().*/) =

tests/semanticdb/expect/ValPattern.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ class ValPattern {
66
val Some(number1) =
77
Some(1)
88

9-
val List(Some(q1), None: None.type, None) = ???
9+
val List(Some(q1), None) = ???
1010

1111
var (leftVar, rightVar) = (1, 2)
1212
var Some(number1Var) =

tests/semanticdb/metac.expect

+2-4
Original file line numberDiff line numberDiff line change
@@ -3493,7 +3493,7 @@ Uri => ValPattern.scala
34933493
Text => empty
34943494
Language => Scala
34953495
Symbols => 22 entries
3496-
Occurrences => 46 entries
3496+
Occurrences => 44 entries
34973497
Synthetics => 11 entries
34983498

34993499
Symbols:
@@ -3532,9 +3532,7 @@ Occurrences:
35323532
[8:11..8:15): Some -> scala/Some.
35333533
[8:16..8:18): q1 <- example/ValPattern#q1.
35343534
[8:21..8:25): None -> scala/None.
3535-
[8:27..8:31): None -> scala/None.
3536-
[8:38..8:42): None -> scala/None.
3537-
[8:46..8:49): ??? -> scala/Predef.`???`().
3535+
[8:29..8:32): ??? -> scala/Predef.`???`().
35383536
[10:7..10:14): leftVar <- example/ValPattern#leftVar().
35393537
[10:16..10:24): rightVar <- example/ValPattern#rightVar().
35403538
[11:6..11:10): Some -> scala/Some.

0 commit comments

Comments
 (0)