@@ -4,7 +4,7 @@ Author: Bob Nystrom
4
4
5
5
Status: In progress
6
6
7
- Version 2.6 (see [ CHANGELOG] ( #CHANGELOG ) at end)
7
+ Version 2.7 (see [ CHANGELOG] ( #CHANGELOG ) at end)
8
8
9
9
Note: This proposal is broken into a couple of separate documents. See also
10
10
[ records] [ ] and [ exhaustiveness] [ ] .
@@ -483,7 +483,7 @@ expression.
483
483
### Variable pattern
484
484
485
485
```
486
- variablePattern ::= ( 'var' | 'final' | type )? identifier
486
+ variablePattern ::= ( 'var' | 'final' | 'final'? type )? identifier
487
487
```
488
488
489
489
A variable pattern binds the matched value to a new variable. These usually
@@ -732,13 +732,13 @@ against the initializer's value.
732
732
Add this new rule:
733
733
734
734
```
735
- patternDeclaration ::= ( 'final' | 'var' ) outerPattern '=' expression
735
+ patternVariableDeclaration ::= ( 'final' | 'var' ) outerPattern '=' expression
736
736
737
- outerPattern ::= parenthesizedPattern
738
- | listPattern
739
- | mapPattern
740
- | recordPattern
741
- | extractorPattern
737
+ outerPattern ::= parenthesizedPattern
738
+ | listPattern
739
+ | mapPattern
740
+ | recordPattern
741
+ | extractorPattern
742
742
```
743
743
744
744
The ` outerPattern ` rule defines a subset of the patterns that are allowed as the
@@ -772,7 +772,7 @@ variables like so:
772
772
```
773
773
localVariableDeclaration ::=
774
774
| initializedVariableDeclaration ';' // Existing.
775
- | patternDeclaration ';' // New.
775
+ | patternVariableDeclaration ';' // New.
776
776
777
777
forLoopParts ::=
778
778
| // Existing productions...
@@ -840,9 +840,11 @@ It is a compile-time error if:
840
840
We extend switch statements to allow patterns in cases:
841
841
842
842
```
843
- switchStatement ::= 'switch' '(' expression ')' '{' switchCase* defaultCase? '}'
844
- switchCase ::= label* caseHead ':' statements
845
- caseHead ::= 'case' pattern ( 'when' expression )?
843
+ switchStatement ::= 'switch' '(' expression ')'
844
+ '{' switchStatementCase* switchStatementDefault? '}'
845
+ switchStatementCase ::= label* caseHead ':' statements
846
+ caseHead ::= 'case' pattern ( 'when' expression )?
847
+ switchStatementDefault ::= label* 'default' ':' statements
846
848
```
847
849
848
850
Allowing patterns in cases significantly increases the expressiveness of what
@@ -1070,13 +1072,13 @@ Color shiftHue(Color color) {
1070
1072
The grammar is:
1071
1073
1072
1074
```
1073
- primary ::= // Existing productions...
1074
- | switchExpression
1075
+ primary ::= // Existing productions...
1076
+ | switchExpression
1075
1077
1076
- switchExpression ::= 'switch' '(' expression ')' '{'
1077
- switchExpressionCase* defaultExpressionCase ? '}'
1078
- switchExpressionCase ::= caseHead '=>' expression ';'
1079
- defaultExpressionCase ::= 'default' '=>' expression ';'
1078
+ switchExpression ::= 'switch' '(' expression ')' '{'
1079
+ switchExpressionCase* switchExpressionDefault ? '}'
1080
+ switchExpressionCase ::= caseHead '=>' expression ';'
1081
+ switchExpressionDefault ::= 'default' '=>' expression ';'
1080
1082
```
1081
1083
1082
1084
Slotting into ` primary ` means it can be used anywhere any expression can appear,
@@ -1606,6 +1608,12 @@ To type check a pattern `p` being matched against a value of type `M`:
1606
1608
*Here, the `1` constant pattern in the case is inferred in a context type of
1607
1609
`double` to be `1.0` and so does match.*
1608
1610
1611
+ *Note that the pattern's value must be a constant, but there is no
1612
+ restriction that it must have a primitive operator `==`. Unlike switch cases
1613
+ in current Dart, you can have a constant with a user-defined operator `==`
1614
+ method. This lets you use constant patterns for user-defined types with
1615
+ custom value semantics.*
1616
+
1609
1617
* **Variable**:
1610
1618
1611
1619
1. In an assignment context, the required type of `p` is the (unpromoted)
@@ -1755,9 +1763,9 @@ The variables a patterns binds depend on what kind of pattern it is:
1755
1763
1756
1764
* **Variable**: When not in an assignment context, introduces a variable whose
1757
1765
name is the pattern's identifier. In a declaration context, the variable is
1758
- final if the surrounding `patternDeclaration ` has a `final` modifier. In a
1759
- matching context, the variable is final if the variable pattern is marked
1760
- `final` and is not otherwise.
1766
+ final if the surrounding `patternVariableDeclaration ` has a `final`
1767
+ modifier. In a matching context, the variable is final if the variable
1768
+ pattern is marked `final` and is not otherwise.
1761
1769
1762
1770
[2473]: https://github.com/dart-lang/language/issues/2473
1763
1771
@@ -1803,21 +1811,44 @@ type.**
1803
1811
1804
1812
### Exhaustiveness and reachability
1805
1813
1806
- A switch is * exhaustive* if all possible values of the matched value's type will
1807
- definitely match at least one case, or there is a default case. Dart currently
1808
- shows a warning if a switch statement on an enum type does not have cases for
1809
- all enum values (or a default). This is helpful for code maintainance: when you
1810
- add a new value to an enum type, the language shows you every switch statement
1811
- that may need a new case to handle it.
1814
+ A switch is * exhaustive* if all possible values of the matched value's static
1815
+ type will definitely match at least one case, or there is a default case. Dart
1816
+ currently shows a warning if a switch statement on an enum type does not have
1817
+ cases for all enum values (or a default). This is helpful for code maintainance:
1818
+ when you add a new value to an enum type, the language shows you every switch
1819
+ statement that may need a new case to handle it.
1812
1820
1813
1821
This checking is even more important with this proposal. Exhaustiveness checking
1814
1822
is a key part of maintaining code written in an algebraic datatype style. It's
1815
1823
the functional equivalent of the error reported when a concrete class fails to
1816
1824
implement an abstract method.
1817
1825
1818
1826
Exhaustiveness checking over arbitrarily deeply nested record and extractor
1819
- patterns can be complex, so the proposal for that is in a [ separate
1820
- document] [ exhaustiveness ] .
1827
+ patterns is complex, so the proposal to define how it works is in a [ separate
1828
+ document] [ exhaustiveness ] . That tells us if the cases in a switch statement
1829
+ or expression are exhaustive or not. Given that:
1830
+
1831
+ * It is a compile-time error if the cases in a switch expression are not
1832
+ exhaustive. * Since an expression must yield a value, the only other option
1833
+ is to throw an error and most Dart users prefer to catch those kinds of
1834
+ mistakes at compile time.*
1835
+
1836
+ * It is a compile-time error if the static type of the matched value in a
1837
+ switch statement is an * exhaustive type* and the cases are not exhaustive.
1838
+ An exhaustive type is:
1839
+
1840
+ * ` bool `
1841
+ * ` Null `
1842
+ * A type whose declaration is marked sealed
1843
+ * ` T? ` where ` T ` is exhaustive
1844
+ * ` FutureOr<T> ` for some type ` T ` that is exhaustive
1845
+ * A record type whose fields are all exhaustive types
1846
+
1847
+ ** TODO: Finalize the syntax for marking a class as a sealed family.**
1848
+
1849
+ * It is a compile-time warning if the static type of the matched value in a
1850
+ switch statement is an enum type or a nullable enum type and the cases are
1851
+ not exhaustive.
1821
1852
1822
1853
[ exhaustiveness ] : https://github.com/dart-lang/language/blob/master/working/0546-patterns/exhaustiveness.md
1823
1854
@@ -1917,7 +1948,7 @@ fail in some way.*
1917
1948
A statement of the form:
1918
1949
1919
1950
``` dart
1920
- for (<patternDeclaration >; <condition>; <increment>) <statement>
1951
+ for (<patternVariableDeclaration >; <condition>; <increment>) <statement>
1921
1952
```
1922
1953
1923
1954
Is executed similar to a traditional for loop except that multiple variables may
@@ -2038,16 +2069,38 @@ To match a pattern `p` against a value `v`:
2038
2069
2039
2070
1. Evaluate the right-hand constant expression to `c`.
2040
2071
2041
- 2. A `== c` pattern matches if `v == c` evaluates to true. *This takes into
2042
- account the built-in semantics that `null` is only equal to `null`.*
2072
+ 2. If the operator is `==`:
2073
+
2074
+ 1. Let `r` be the result of `v == c`.
2075
+
2076
+ 2. If `r` is not a Boolean then throw a runtime error. *This can
2077
+ happen if operator `==` on `v`'s type returns `dynamic`.*
2078
+
2079
+ 3. The pattern matches if `r` is true and fails otherwise. *This takes
2080
+ into account the built-in semantics that `null` is only equal to
2081
+ `null`.*
2082
+
2083
+ 2. Else if the operator is `!=`:
2084
+
2085
+ 1. Let `r` be the result of `v == c`.
2086
+
2087
+ 2. If `r` is not a Boolean then throw a runtime error. *This can
2088
+ happen if operator `==` on `v`'s type returns `dynamic`.*
2043
2089
2044
- 3. A `!= c` pattern matches if `v == e` evaluates to false . *This takes
2045
- into account the built-in semantics that `null` is not equal to anything
2046
- but `null`.*
2090
+ 3. The pattern matches if `r` is false and fails otherwise . *This takes
2091
+ into account the built-in semantics that `null` is only equal to
2092
+ `null`.*
2047
2093
2048
- 4. For any other operator, the pattern matches if calling the operator
2049
- method of the same name on the matched value, with `c` as the argument
2050
- returns true.
2094
+ 3. Else the operator is a comparison operator `op`:
2095
+
2096
+ 1. Let `r` be the result of calling `op` on `v` with argument `c`.
2097
+
2098
+ 2. If `r` is not a Boolean then throw a runtime error. *This can happen
2099
+ if the operator on `v`'s type returns `dynamic`.*
2100
+
2101
+ 3. The pattern matches if `r` is true and fails otherwise. *This takes
2102
+ into account the built-in semantics that `null` is only equal to
2103
+ `null`.*
2051
2104
2052
2105
* **Cast**:
2053
2106
@@ -2126,7 +2179,7 @@ To match a pattern `p` against a value `v`:
2126
2179
becomes a runtime exception if the map pattern is in a variable
2127
2180
declaration.*
2128
2181
2129
- 3. Otherwise, for each entry in `p`:
2182
+ 3. Otherwise, for each entry in `p`, in source order :
2130
2183
2131
2184
1. Evaluate the key `expression` to `k` and call `containsKey()` on the
2132
2185
value. If this returns `false`, the map does not match.
@@ -2137,9 +2190,6 @@ To match a pattern `p` against a value `v`:
2137
2190
2138
2191
4. The match succeeds if all entry subpatterns match.
2139
2192
2140
- *Note that, unlike with lists, a matched map may have additional entries
2141
- that are not checked by the pattern.*
2142
-
2143
2193
* **Record**:
2144
2194
2145
2195
1. If the runtime type of `v` is not a record type with the same type as
@@ -2159,9 +2209,10 @@ To match a pattern `p` against a value `v`:
2159
2209
1. If the runtime type of `v` is not a subtype of the static type of `p`
2160
2210
then the match fails.
2161
2211
2162
- 3 . Otherwise, for each field `f` in `p`:
2212
+ 2 . Otherwise, for each field `f` in `p`, in source order :
2163
2213
2164
2214
1. Call the getter with the same name as `f` on `v` to a result `r`.
2215
+ The getter may be an in-scope extension member.
2165
2216
2166
2217
2. Match the subpattern of `f` against `r`. If the match fails, the
2167
2218
extractor match fails.
@@ -2251,6 +2302,23 @@ Here is one way it could be broken down into separate pieces:
2251
2302
2252
2303
## Changelog
2253
2304
2305
+ ### 2.7
2306
+
2307
+ - Clarify that relational and extractor patterns can call extension members
2308
+ (#2457).
2309
+
2310
+ - Non-boolean results throw in relational patterns instead of failing the
2311
+ match (#2461).
2312
+
2313
+ - Specify that map and extractor subpatterns are evaluated in source order
2314
+ (#2466).
2315
+
2316
+ - Specify non-exhaustive switch errors and warnings (#2474).
2317
+
2318
+ - Allow `final` before type annotated variable patterns (#2486).
2319
+
2320
+ - Rename some grammars to align with Analyzer AST names (#2491).
2321
+
2254
2322
### 2.6
2255
2323
2256
2324
- Change logical-or and logical-and patterns to be left-associative.
0 commit comments