@@ -1165,6 +1165,66 @@ That is less important here since the only other case is the else branch.
1165
1165
1166
1166
## Static semantics
1167
1167
1168
+ ### Refutable and irrefutable patterns and contexts
1169
+
1170
+ Patterns appear inside a number of other constructs in the language. This
1171
+ proposal extends Dart to allow patterns in:
1172
+
1173
+ * Local variable declarations.
1174
+ * For loop variable declarations.
1175
+ * Assignment expressions.
1176
+ * Switch statement cases.
1177
+ * A new switch expression form's cases.
1178
+ * A new pattern-if statement.
1179
+
1180
+ When a pattern appears in a switch case, any variables bound by the pattern are
1181
+ only in scope in that case's body. If the pattern fails to match, the case body
1182
+ is skipped. This ensures that the variables can't be used when the pattern
1183
+ failed to match and they have no defined value. Likewise, the variables bound by
1184
+ a pattern-if statement's pattern are only in scope in the then branch. That
1185
+ branch is skipped if the pattern fails to match.
1186
+
1187
+ When patterns appear in variable declarations and assignments, there is no
1188
+ natural control flow that can skip over uses of the variables if the pattern
1189
+ fails to match. For example:
1190
+
1191
+ ``` dart
1192
+ main() {
1193
+ var (a, 5) = (1, 2);
1194
+ print(a + b);
1195
+ }
1196
+ ```
1197
+
1198
+ What happens when ` a ` is printed in the example above?
1199
+
1200
+ To avoid that, we restrict which patterns can be used in variable declarations
1201
+ and pattern assignments. Only * irrefutable* patterns that never fail to match
1202
+ are allowed in contexts where match failure can't be handled. Since a literal
1203
+ pattern like ` 5 ` in the above example might fail to match, it's prohibited and
1204
+ the above example has a compile error.
1205
+
1206
+ We define an * irrefutable context* as the pattern in a
1207
+ ` localVariableDeclaration ` , ` forLoopParts ` , or ` patternAssignment ` or any of its
1208
+ subpatterns. It is a compile-time error if any of these patterns appear in an
1209
+ irrefutable context:
1210
+
1211
+ * Logical-or
1212
+ * Relational
1213
+ * Null-check
1214
+ * Literal
1215
+ * Constant
1216
+
1217
+ * Logical-or patterns are refutable because there is no point in using one with
1218
+ an irrefutable left operand. We could make null-check patterns irrefutable if
1219
+ ` V ` is assignable to its static type, but whenever that is true the pattern does
1220
+ nothing useful since its only behavior is a type test.*
1221
+
1222
+ * The remaining patterns are allowed syntactically to appear in a refutable
1223
+ context. Patterns that do type tests like variables and lists produce a
1224
+ compile-time error when used in an irrefutable context if the matched value
1225
+ isn't assignable to their required type. This error is specified under type
1226
+ checking.*
1227
+
1168
1228
### Type inference
1169
1229
1170
1230
Type inference in Dart allows type information in one part of the program to
@@ -1380,15 +1440,16 @@ The context type schema for a pattern `p` is:
1380
1440
// ^-- Infer Foo<num>.
1381
1441
```
1382
1442
1383
- #### Type checking and pattern static type
1443
+ #### Type checking and pattern required type
1384
1444
1385
1445
Once the value a pattern is matched against has a static type (which means
1386
1446
downwards inference on it using the pattern's context type schema is complete),
1387
- we can type check the pattern. We also calculate a static type for the patterns
1388
- that only match certain types: null-check, variable, list, map, record, and
1389
- extractor.
1447
+ we can type check the pattern.
1390
1448
1391
- Some examples and the corresponding pattern static types:
1449
+ Also variable, list, map, record, and extractor patterns only match a value of a
1450
+ certain *required type*. These patterns are prohibited in an irrefutable context
1451
+ if the matched value isn't assignable to that type. We define the required type
1452
+ for those patterns here. Some examples and the corresponding required types:
1392
1453
1393
1454
```dart
1394
1455
var <int>[a, b] = <num>[1, 2]; // List<int> (and compile error).
@@ -1413,8 +1474,6 @@ To type check a pattern `p` being matched against a value of type `M`:
1413
1474
1414
1475
2 . Type-check the subpattern using ` N ` as the matched value type.
1415
1476
1416
- 3 . If ` p ` is a null-check pattern, then the static type of ` p ` is ` N ` .
1417
-
1418
1477
[ nonnull ] : https://github.com/dart-lang/language/blob/master/accepted/2.12/nnbd/feature-specification.md#null-promotion
1419
1478
1420
1479
* ** Literal** or ** constant** : Type check the pattern's value expression in
@@ -1473,7 +1532,7 @@ To type check a pattern `p` being matched against a value of type `M`:
1473
1532
1474
1533
*both `a` and `b` use `num` as their matched value type.*
1475
1534
1476
- 3. The static type of `p` is `List<S>` where:
1535
+ 3. The required type of `p` is `List<S>` where:
1477
1536
1478
1537
1. If `p` has a type argument, `S` is that type. *If the list pattern
1479
1538
has an explicit type argument, that wins.*
@@ -1500,7 +1559,7 @@ To type check a pattern `p` being matched against a value of type `M`:
1500
1559
1501
1560
*Here, both `a` and `b` use `Object` as the matched value type.*
1502
1561
1503
- 3. The static type of `p` is `Map<L, W>` where:
1562
+ 3. The required type of `p` is `Map<L, W>` where:
1504
1563
1505
1564
1. If `p` has type arguments, `L` and `W` are those type arguments.
1506
1565
*If the map pattern is explicitly typed, that wins.*
@@ -1520,7 +1579,7 @@ To type check a pattern `p` being matched against a value of type `M`:
1520
1579
corresponding named field on `M` as the matched value type or `Object?`
1521
1580
if `M` is not a record type with the corresponding field.
1522
1581
1523
- 3. The static type of `p` is a record type with the same shape as `p` and
1582
+ 3. The required type of `p` is a record type with the same shape as `p` and
1524
1583
`Object?` for all fields. *If the matched value's type is `dynamic` or
1525
1584
some record supertype like `Object`, then the record pattern should
1526
1585
match any record with the right shape and then delegate to its field
@@ -1537,107 +1596,22 @@ To type check a pattern `p` being matched against a value of type `M`:
1537
1596
a compile-time error if `X` does not have a getter whose name matches
1538
1597
the subpattern's field name.
1539
1598
1540
- 2. The static type of `p` is `X`.
1599
+ 2. The required type of `p` is `X`.
1541
1600
1542
- It is a compile-time error if the type of an expression in a guard clause is not
1543
- `bool` or `dynamic`.
1601
+ It is a compile-time error if:
1602
+
1603
+ * The type of an expression in a guard clause is not `bool` or `dynamic`.
1604
+
1605
+ * `p` is in an irrefutable context, it has a required type `T`, and `M` is not
1606
+ assignable to `T`. *Destructuring and variable patterns can only be used in
1607
+ declarations and assignments if we can statically tell that the
1608
+ destructuring and variable binding won't fail.*
1544
1609
1545
1610
### Switch expression type
1546
1611
1547
1612
The static type of a switch expression is the least upper bound of the static
1548
1613
types of all of the case expressions.
1549
1614
1550
- ## Refutable and irrefutable patterns
1551
-
1552
- Patterns appear inside a number of other constructs in the language. This
1553
- proposal extends Dart to allow patterns in:
1554
-
1555
- * Local variable declarations.
1556
- * For loop variable declarations.
1557
- * Assignment expressions.
1558
- * Switch statement cases.
1559
- * A new switch expression form's cases.
1560
- * A new pattern-if statement.
1561
-
1562
- When a pattern appears in a switch case, any variables bound by the pattern are
1563
- only in scope in that case's body. If the pattern fails to match, the case body
1564
- is skipped. This ensures that the variables can't be used when the pattern
1565
- failed to match and they have no defined value. Likewise, the variables bound by
1566
- a pattern-if statement's pattern are only in scope in the then branch. That
1567
- branch is skipped if the pattern fails to match.
1568
-
1569
- The other places patterns can appear are various kinds of variable declarations,
1570
- like:
1571
-
1572
- ```dart
1573
- main() {
1574
- var (a, b) = (1, 2);
1575
- print(a + b);
1576
- }
1577
- ```
1578
-
1579
- Variable declarations have no natural control flow attached to them, so what
1580
- happens if the pattern fails to match? What happens when ` a ` is printed in the
1581
- example above?
1582
-
1583
- To avoid that, we restrict which patterns can be used in variable declarations.
1584
- Only * irrefutable* patterns that never fail to match are allowed in contexts
1585
- where match failure can't be handled. For example, this is an error:
1586
-
1587
- ``` dart
1588
- main() {
1589
- var (== 2, == 3) = (1, 2);
1590
- }
1591
- ```
1592
-
1593
- We define an * irrefutable context* as the pattern in a
1594
- ` localVariableDeclaration ` , ` forLoopParts ` , or ` patternAssignment ` . A * refutable
1595
- context* is the pattern in a ` caseHead ` or ` ifCondition ` .
1596
-
1597
- Refutability is not just a property of the pattern itself. It also depends on
1598
- the static type of the value being matched. Consider:
1599
-
1600
- ``` dart
1601
- irrefutable((int, int) obj) {
1602
- var (a, b) = obj;
1603
- }
1604
-
1605
- refutable(Object obj) {
1606
- var (a, b) = obj;
1607
- }
1608
- ```
1609
-
1610
- In the first function, the ` (a, b) ` pattern will always successfully destructure
1611
- the record because ` obj ` is known to be a record type of the right shape. But in
1612
- the second function, ` obj ` may fail to match because the value may not be a
1613
- record. * This implies that we can't determine whether a pattern in a variable
1614
- declaration is incorrectly refutable until after type checking.*
1615
-
1616
- Refutability of a pattern ` p ` matching a value of type ` V ` is:
1617
-
1618
- * ** Logical-and** , ** parenthesized** , ** null-assert** , or ** cast** :
1619
- Irrefutable if and only if all subpatterns are irrefutable.
1620
-
1621
- * ** Logical-or** , ** relational** , ** null-check** , ** literal** , or
1622
- ** constant** : Always refutable. * Logical-or patterns are refutable because
1623
- there is no point in using one with an irrefutable left operand. We could
1624
- make null-check patterns irrefutable if ` V ` is assignable to its static
1625
- type, but whenever that is true the pattern does nothing useful since its
1626
- only behavior is a type test.*
1627
-
1628
- * ** variable** , ** list** , ** map** , ** record** , or ** extractor** : Irrefutable
1629
- if and only if ` V ` is assignable to the static type of ` p ` and all
1630
- subpatterns are irrefutable. * If ` p ` is a variable pattern with no type
1631
- annotation, the type is inferred from ` V ` , so it is never refutable.*
1632
-
1633
- It is a compile-time error if a refutable pattern appears in an irrefutable
1634
- context. * This means that the explicit predicate patterns like constants and
1635
- literals can never appear in pattern variable declarations or pattern
1636
- assignments. The patterns that do type tests directly or implicitly can appear
1637
- in variable declarations or assignments only if the tested type is assignable
1638
- from the value type. In other words, any pattern that needs to "downcast" to
1639
- match is refutable.*
1640
-
1641
1615
### Variables and scope
1642
1616
1643
1617
Patterns often exist to introduce new variable bindings. A "wildcard" identifier
@@ -1949,8 +1923,8 @@ To match a pattern `p` against a value `v`:
1949
1923
* **Null-assert**:
1950
1924
1951
1925
1. If `v` is null then throw a runtime exception. *Note that we throw even
1952
- if this appears in a refutable context. The intent of this pattern is to
1953
- assert that a value *must* not be null.*
1926
+ if this appears outside of a refutable context. The intent of this
1927
+ pattern is to assert that a value *must* not be null.*
1954
1928
1955
1929
2. Otherwise, match the inner pattern against `v`.
1956
1930
@@ -1970,8 +1944,8 @@ To match a pattern `p` against a value `v`:
1970
1944
1971
1945
1. If the runtime type of `v` is not a subtype of the static type of `p`
1972
1946
then throw a runtime exception. *Note that we throw even if this appears
1973
- in a refutable context. The intent of this pattern is to assert that a
1974
- value *must* have some type.*
1947
+ outside of a refutable context. The intent of this pattern is to assert
1948
+ that a value *must* have some type.*
1975
1949
1976
1950
2. Otherwise, bind the variable's identifier to `v` and the match succeeds.
1977
1951
0 commit comments