Skip to content

Commit 46684e0

Browse files
authored
[patterns] Fix soundness hole in map patterns. (#2687)
* [patterns] Fix soundness hole in map patterns. Fix #2685. * Revise. * Reword how soundness is preserved.
1 parent 98de36b commit 46684e0

File tree

1 file changed

+36
-14
lines changed

1 file changed

+36
-14
lines changed

accepted/future-releases/0546-patterns/feature-specification.md

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2809,24 +2809,44 @@ To match a pattern `p` against a value `v`:
28092809
1. Evaluate the key `expression` to `k` and call `containsKey(k)` on
28102810
the value. If this returns `false`, the map does not match.
28112811
2812-
2. Otherwise, evaluate `v[k]` and match the resulting value against
2813-
this entry's value subpattern. If it does not match, the map does
2814-
not match.
2812+
2. Otherwise, evaluate `v[k]` to `r`.
28152813
2816-
A compiler is free to call `v[k]` and `containsKey()` in either order,
2817-
or to elide calling one or both if it determines that doing so will
2818-
produce the same result. It may assume that the map adheres to the
2819-
following protocol:
2814+
3. If `r != null || (null is V) && v.containsKey(k)` evaluates to
2815+
`false` then the map does not match.
28202816
2821-
* If `containsKey(k)` returns `false` for some key, then `v[k]` will
2822-
return `null`.
2817+
*Note:*
28232818
2824-
* If `containsKey(k)` returns `true` for some key, then `v[k]` returns
2825-
an instance of the map's value type.
2819+
* *When `v[k]` returns a non-null value, we know the key is
2820+
present and we short-circuit the `containsKey()` call.*
28262821
2827-
*In particular, if the map's value type is non-nullable, then when
2828-
`v[k]` returns `null`, the compiler can assume that the key is absent
2829-
and `containsKey(k)` would return `false` too.*
2822+
* *If `V` is known to be a non-nullable type, then `null is V` is
2823+
always `false` and the expression simplifies to:*
2824+
2825+
```dart
2826+
r != null
2827+
```
2828+
2829+
* *Conversely, if `V` is known to be a nullable type, then `null
2830+
is V` is always `true` and the expression simplifies to:*
2831+
2832+
```dart
2833+
r != null || v.containsKey(k)
2834+
```
2835+
2836+
* *When `V` is a potentially nullable type, the `null is V` test
2837+
must be performed but can be hoisted out and shared across all
2838+
entries since it doesn't depend on `k`.*
2839+
2840+
* *If `v` is a poorly behaved `Map` whose `v[k]` and
2841+
`containsKey(k)` results don't agree (i.e. a non-`null` `v[k]`
2842+
and `false` `containsKey(k)` or vice versa) we do not detect
2843+
that mismatch. Since badly behaved maps are rare, this is
2844+
allowed. Even if `v` is poorly behaved, a `null` value will only
2845+
be passed to the subpattern if `null is V`, so soundness is
2846+
preserved.*
2847+
2848+
4. Else, match `r` against this entry's value subpattern. If it does
2849+
not match, the map does not match.
28302850
28312851
5. The match succeeds if all entry subpatterns match.
28322852
@@ -3256,6 +3276,8 @@ Here is one way it could be broken down into separate pieces:
32563276
32573277
### 2.20
32583278
3279+
- Fix soundness hole in map patterns with badly behaved maps (#2685).
3280+
32593281
- Clarify which variables are valid in pattern assignments.
32603282
32613283
- Clarify when primitive `==` for map pattern keys comes into play (#2690).

0 commit comments

Comments
 (0)