Skip to content

Commit 1e0e53e

Browse files
committed
[patterns] Explain pattern type inference in a little more detail.
1 parent 4a48c96 commit 1e0e53e

File tree

1 file changed

+115
-21
lines changed

1 file changed

+115
-21
lines changed

working/0546-patterns/patterns-feature-specification.md

Lines changed: 115 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Author: Bob Nystrom
44

55
Status: In progress
66

7-
Version 1.4 (see [CHANGELOG](#CHANGELOG) at end)
7+
Version 1.5 (see [CHANGELOG](#CHANGELOG) at end)
88

99
Note: This proposal is broken into a couple of separate documents. See also
1010
[records][] and [exhaustiveness][].
@@ -1111,25 +1111,117 @@ if (case var s? = maybeString) {
11111111

11121112
## Static semantics
11131113

1114-
A pattern always appears in the context of some value expression that it is
1115-
being matched against. In a switch statement or expression, the value expression
1116-
is the value being switched on. In an if-case statement, the value is the result
1117-
of the expression to the right of the `=`. In a variable declaration, the value
1118-
is the initializer:
1114+
### Type inference
1115+
1116+
Type inference in Dart allows type information in one part of the program to
1117+
flow over and fill in missing pieces in another part. Inference can flow
1118+
"upwards" from a subexpression to the surrounding expression:
11191119

11201120
```dart
1121-
var (a, b) = (1, 2);
1121+
[1]
11221122
```
11231123

1124-
Here, the `(a, b)` pattern is being matched against the expression `(1, 2)`.
1125-
When a pattern contains subpatterns, each subpattern is matched against a value
1126-
destructured from the value that the outer pattern is matched against. Here, `a`
1127-
is matched against `1` and `b` is matched against `2`.
1124+
Here, we infer `List<int>` for the type of the list literal based on type of its
1125+
element. Inference can flow "downwards" from an expression into its
1126+
subexpressions too:
1127+
1128+
```dart
1129+
<List<int>>[[]]
1130+
```
1131+
1132+
Here, the inner empty list literal `[]` gets type `List<int>` because the type
1133+
argument on the outer list literal is pushed into it.
1134+
1135+
Type information can flow through patterns in the same way. From subpatterns
1136+
upwards to the surrounding pattern:
1137+
1138+
```dart
1139+
var [int x] = ...
1140+
```
1141+
1142+
Here, we infer `List<int>` for the list pattern based on the type of the element
1143+
subpattern. Or downwards:
1144+
1145+
```dart
1146+
var <int>[x] = ...
1147+
```
1148+
1149+
Here, we infer `int` for the inner `x` subpattern based on the type of the
1150+
surrounding list pattern.
1151+
1152+
In variable declarations, type information can also flow between the variable
1153+
and its initializer. "Upwards" from initializer to variable:
1154+
1155+
```dart
1156+
var x = 1;
1157+
```
1158+
1159+
Here we infer `int` for `x` based on the initializer expression's type. That
1160+
upwards flow extends to patterns:
1161+
1162+
```dart
1163+
var [x] = <int>[1];
1164+
```
1165+
1166+
Here, we infer `List<int>` for the list pattern (and thus `int` for the `x`
1167+
subpattern) based on type of the initializer expression `<int>[1]`.
1168+
1169+
Types can also flow "downwards" from variable to initializer:
1170+
1171+
```dart
1172+
List<int> x = [];
1173+
```
1174+
1175+
Here, the empty list is instantiated as `List<int>` because the type annotation
1176+
on `x` gets pushed over to the initializer. That extends to patterns:
1177+
1178+
```dart
1179+
var <num>[x] = [1];
1180+
```
1181+
1182+
Here, we infer the list literal in the initializer to have type `List<num>` (and
1183+
not `List<int>`) based on the type of list pattern. All of this type flow can be
1184+
combined:
1185+
1186+
```dart
1187+
var (a, b, <double>[c], [int d]) = ([1], <List<int>>[[]], [2], [3]);
1188+
```
1189+
1190+
To orchestrate this, type inference on patterns proceeds in three phases:
1191+
1192+
1. **Calculate the pattern type schema.** Start at the top of the pattern and
1193+
recurse downwards into subpatterns using the surrounding pattern as context.
1194+
When we reach the leaves, work back upwards filling in missing pieces where
1195+
possible. When this completes, we have a type schema for the pattern. It's
1196+
a type *schema* and not a *type* because there may be holes where types
1197+
aren't known yet.
1198+
1199+
2. **Calculate the static type of the matched value.** A pattern always occurs
1200+
in the context of some matched value. For pattern variable declarations,
1201+
this is the initializer. For switches and if-case statements, it's the value
1202+
being matched.
1203+
1204+
Using the pattern's type schema as a context type, infer missing types on
1205+
the value expression. This is the existing type inference rules on
1206+
expressions. It yields a complete static type for the matched value.
1207+
1208+
3. **Calculate the static type of the pattern.** Using that value type, recurse
1209+
through the pattern again downwards to the leaf subpatterns filling in any
1210+
holes in the type schema. When that completes, we now have a full static
1211+
type for the pattern and all of its subpatterns.
1212+
1213+
The full process only comes into play for pattern variable declarations. For
1214+
switch case, and if-case statements, there is no downwards inference from
1215+
pattern to value and the first step is skipped. Instead, the type of the matched
1216+
value is inferred and we jump straight to inferring the types of the case
1217+
patterns from that context type. *The intent of a matcher pattern is to query
1218+
the type of the matched value, so it would be strange if that query affected the
1219+
value expression.*
11281220

11291221
When calculating the context type schema or static type of a pattern, any
11301222
occurrence of `typePattern` in a type is treated as `Object?`.
11311223

1132-
### Pattern context type schema
1224+
#### Pattern context type schema
11331225

11341226
In a non-pattern variable declaration, the variable's type annotation is used
11351227
for downwards inference of the initializer:
@@ -1151,8 +1243,6 @@ To support this, every pattern has a context type schema. This is a type
11511243
var (a, int b) = ... // Schema is `(?, int)`.
11521244
```
11531245

1154-
#### Named fields in type schemas
1155-
11561246
Named record fields add complexity to type inference:
11571247

11581248
```dart
@@ -1203,18 +1293,14 @@ The context type schema for a pattern `p` is:
12031293
the corresponding subpattern. (If there is no subpattern because it's
12041294
an implicit variable pattern like `(field:)`, the type schema is `?`.)
12051295

1206-
* **Variable binder**:
1207-
* If `p` has a type annotation, the context type schema is that type.
1208-
* Else it is `?`.
1209-
12101296
* **Variable matcher**:
12111297
* If `p` has a type annotation, the context type schema is `Object?`.
12121298
*It is not the annotated type because a variable matching pattern can
12131299
be used to downcast from any other type.*
12141300
* Else it is `?`.
12151301

1216-
* **Cast binder**, **wildcard matcher**, or **extractor matcher**: The context
1217-
type schema is `Object?`.
1302+
* **Cast binder**, **variable binder**, **wildcard matcher**, or **extractor
1303+
matcher**: The context type schema is `Object?`.
12181304

12191305
**TODO: Should type arguments on an extractor create a type argument
12201306
constraint?**
@@ -1243,7 +1329,7 @@ var [int a, num b] = [1, 2];
12431329
*Here, the GLB of `int` and `num` is `int`, which ensures that neither `int a`
12441330
nor `num b` need to downcast their respective fields.*
12451331

1246-
### Pattern static type
1332+
#### Pattern static type
12471333

12481334
Once the value a pattern is matched against has a static type (which means
12491335
downwards inference on it using the pattern's context type schema is complete),
@@ -1787,6 +1873,14 @@ main() {
17871873

17881874
## Changelog
17891875

1876+
### 1.5
1877+
1878+
- Introduce and clarify type inference.
1879+
1880+
- The context type schema for a variable matcher is always `Object?`, since
1881+
it's intent is to *match* a type and *cause* the expression to have some
1882+
type.
1883+
17901884
### 1.4
17911885

17921886
- Link to [exhaustiveness][] proposal.

0 commit comments

Comments
 (0)