@@ -4,7 +4,7 @@ Author: Bob Nystrom
4
4
5
5
Status: Accepted
6
6
7
- Version 2.17 (see [ CHANGELOG] ( #CHANGELOG ) at end)
7
+ Version 2.18 (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] [ ] .
@@ -214,8 +214,8 @@ Here is the overall grammar for the different kinds of patterns:
214
214
```
215
215
pattern ::= logicalOrPattern
216
216
217
- logicalOrPattern ::= ( logicalOrPattern '||' )? logicalAndPattern
218
- logicalAndPattern ::= ( logicalAndPattern '&&' )? relationalPattern
217
+ logicalOrPattern ::= logicalOrPattern ( '||' logicalAndPattern )*
218
+ logicalAndPattern ::= logicalAndPattern ( '&&' relationalPattern )*
219
219
relationalPattern ::= ( equalityOperator | relationalOperator) bitwiseOrExpression
220
220
| unaryPattern
221
221
@@ -429,8 +429,9 @@ switch (row) {
429
429
```
430
430
constantPattern ::= booleanLiteral
431
431
| nullLiteral
432
- | numericLiteral
432
+ | '-'? numericLiteral
433
433
| stringLiteral
434
+ | symbolLiteral
434
435
| identifier
435
436
| qualifiedName
436
437
| constObjectExpression
@@ -445,7 +446,10 @@ syntactically overlap other kinds of patterns. We avoid ambiguity while
445
446
supporting terse forms of the most common constant expressions like so:
446
447
447
448
* Simple "primitive" literals like Booleans and numbers are valid patterns
448
- since they aren't ambiguous.
449
+ since they aren't ambiguous. We also allow unary ` - ` expressions on
450
+ numeric literals since users think of ` -2 ` as a single literal and not the
451
+ literal ` 2 ` with a unary ` - ` applied to it (which is how the language
452
+ views it).
449
453
450
454
* Named constants are also allowed because they aren't ambiguous. That
451
455
includes simple identifiers like ` someConstant ` , prefixed constants like
@@ -887,6 +891,34 @@ It is a compile-time error if:
887
891
[a, a, a] = [1, 2, 3];
888
892
```
889
893
894
+ #### Map patterns in pattern assignments
895
+
896
+ The language specifies:
897
+
898
+ > An expression statement consists of an expression that does not begin with a
899
+ > '{' character.
900
+
901
+ This avoids an ambiguity between blocks and map literals. But with map patterns
902
+ in assignments, it is useful to have an expression statement that begins with
903
+ `{`:
904
+
905
+ ```dart
906
+ var map = {'a': 1, 'b': 2};
907
+ int a, b;
908
+ // more code...
909
+
910
+ // later...
911
+ {'a': a, 'b': b} = map;
912
+ ```
913
+
914
+ To support this while still avoiding the ambiguity between blocks and map
915
+ literals, we change the above rule to:
916
+
917
+ The expression of a statement expression cannot start with a ` { ` token which
918
+ starts a set or map literal. It may start with a ` { ` only if that starts a map
919
+ pattern of a pattern assignment expression, in which case the corresponding
920
+ closing ` } ` must be immediately followed by a ` = ` .
921
+
890
922
### Switch statement
891
923
892
924
We extend switch statements to allow patterns in cases:
@@ -976,7 +1008,9 @@ The specific kinds of switches whose behavior changes are:
976
1008
These nine cases represent 0.009% of the cases found.
977
1009
978
1010
For any switch case that is broken by this proposal, you can revert back to the
979
- original behavior by prefixing the case expression (now pattern) with `const`:
1011
+ original behavior by prefixing the case expression (now pattern) with `const`
1012
+ and wrapping it in parentheses if the expression is not a collection literal
1013
+ or const constructor call:
980
1014
981
1015
```dart
982
1016
// List or map literal:
@@ -986,15 +1020,15 @@ case const [a, b]:
986
1020
case const SomeClass(1, 2):
987
1021
988
1022
// Other constant expression:
989
- case const A + A:
990
- case const A + 'b':
991
- case const -ERR_LDS_ICAO_SIGNED_DATA_SIGNER_INFOS_EMPTY:
992
- case const -sigkill:
993
- case const List<RPChoice>:
994
- case const 720 * 1280:
995
- case const 1080 * 1920:
996
- case const 1440 * 2560:
997
- case const 2160 * 3840:
1023
+ case const ( A + A) :
1024
+ case const ( A + 'b') :
1025
+ case const ( -ERR_LDS_ICAO_SIGNED_DATA_SIGNER_INFOS_EMPTY) :
1026
+ case const ( -sigkill) :
1027
+ case const ( List<RPChoice>) :
1028
+ case const ( 720 * 1280) :
1029
+ case const ( 1080 * 1920) :
1030
+ case const ( 1440 * 2560) :
1031
+ case const ( 2160 * 3840) :
998
1032
```
999
1033
1000
1034
We can determine syntactically whether an existing switch case's behavior will
@@ -2462,10 +2496,11 @@ the pattern may also *destructure* data from the object or *bind* variables.
2462
2496
2463
2497
Refutable patterns usually occur in a context where match refutation causes
2464
2498
execution to skip over the body of code where any variables bound by the pattern
2465
- are in scope. If a pattern match failure occurs in irrefutable context, a
2499
+ are in scope. If a pattern match failure occurs in an irrefutable context, a
2466
2500
runtime error is thrown. *This can happen when matching against a value of type
2467
- `dynamic`, or when a list pattern in a variable declaration is matched against a
2468
- list of a different length.*
2501
+ `dynamic`, when a list pattern in a variable declaration is matched against a
2502
+ list of a different length, when a map pattern in a pattern assignment is
2503
+ matched against a map that lacks some of the destructured keys, etc.*
2469
2504
2470
2505
To match a pattern `p` against a value `v`:
2471
2506
@@ -2557,6 +2592,9 @@ To match a pattern `p` against a value `v`:
2557
2592
2558
2593
2. If the runtime type of `v` is not a subtype of `T` then the match fails.
2559
2594
2595
+ *This type test may get elided. See "Pointless type tests and legacy
2596
+ types" below.*
2597
+
2560
2598
3. Otherwise, store `v` in `p`'s variable and the match succeeds.
2561
2599
2562
2600
* **Parenthesized**: Match the subpattern against `v` and succeed if it
@@ -2569,6 +2607,9 @@ To match a pattern `p` against a value `v`:
2569
2607
some `T` determined either by the pattern's explicit type argument or
2570
2608
inferred from the matched value type.*
2571
2609
2610
+ *This type test may get elided. See "Pointless type tests and legacy
2611
+ types" below.*
2612
+
2572
2613
2. Let `l` be the length of the list determined by calling `length` on `v`.
2573
2614
2574
2615
3. Let `h` be the number of non-rest element subpatterns preceding the rest
@@ -2618,6 +2659,9 @@ To match a pattern `p` against a value `v`:
2618
2659
some `K` and `V` determined either by the pattern's explicit type
2619
2660
arguments or inferred from the matched value type.*
2620
2661
2662
+ *This type test may get elided. See "Pointless type tests and legacy
2663
+ types" below.*
2664
+
2621
2665
2. Let `l` be the length of the map determined by calling `length` on `v`.
2622
2666
2623
2667
3. If `p` has no rest element and `l` is not equal to the number of
@@ -2628,20 +2672,38 @@ To match a pattern `p` against a value `v`:
2628
2672
2629
2673
4. Otherwise, for each (non-rest) entry in `p`, in source order:
2630
2674
2631
- 1. Evaluate the key `expression` to `k` and call `containsKey()` on the
2632
- value. If this returns `false`, the map does not match.
2675
+ 1. Evaluate the key `expression` to `k` and call `containsKey(k )` on
2676
+ the value. If this returns `false`, the map does not match.
2633
2677
2634
2678
2. Otherwise, evaluate `v[k]` and match the resulting value against
2635
2679
this entry's value subpattern. If it does not match, the map does
2636
2680
not match.
2637
2681
2682
+ A compiler is free to call `v[k]` and `containsKey()` in either order,
2683
+ or to elide calling one or both if it determines that doing so will
2684
+ produce the same result. It may assume that the map adheres to the
2685
+ following protocol:
2686
+
2687
+ * If `containsKey(k)` returns `false` for some key, then `v[k]` will
2688
+ return `null`.
2689
+
2690
+ * If `containsKey(k)` returns `true` for some key, then `v[k]` returns
2691
+ an instance of the map's value type.
2692
+
2693
+ *In particular, if the map's value type is non-nullable, then when
2694
+ `v[k]` returns `null`, the compiler can assume that the key is absent
2695
+ and `containsKey(k)` would return `false` too.*
2696
+
2638
2697
5. The match succeeds if all entry subpatterns match.
2639
2698
2640
2699
* **Record**:
2641
2700
2642
2701
1. If the runtime type of `v` is not a subtype of the required type of `p`,
2643
2702
then the match fails.
2644
2703
2704
+ *This type test may get elided. See "Pointless type tests and legacy
2705
+ types" below.*
2706
+
2645
2707
2. For each field `f` in `p`, in source order:
2646
2708
2647
2709
1. Access the corresponding field in record `v` as `r`.
@@ -2656,6 +2718,9 @@ To match a pattern `p` against a value `v`:
2656
2718
1. If the runtime type of `v` is not a subtype of the required type of `p`
2657
2719
then the match fails.
2658
2720
2721
+ *This type test may get elided. See "Pointless type tests and legacy
2722
+ types" below.*
2723
+
2659
2724
2. Otherwise, for each field `f` in `p`, in source order:
2660
2725
2661
2726
1. Call the getter with the same name as `f` on `v`, and let the result
@@ -2666,6 +2731,50 @@ To match a pattern `p` against a value `v`:
2666
2731
2667
2732
3. The match succeeds if all field subpatterns match.
2668
2733
2734
+ ### Pointless type tests and legacy types
2735
+
2736
+ Variable, map, list, record, and object patterns all do a runtime type test on
2737
+ the matched object against the pattern's static type (variables and wildcards)
2738
+ or required type (maps, lists, records, and objects). If the matched value's
2739
+ static type is a subtype of the pattern's static or required type, then no
2740
+ runtime type test is performed.
2741
+
2742
+ *When the pattern's type is a supertype of the matched value's static type, then
2743
+ it seems like the runtime type test is guaranteed to pass. That implies there's
2744
+ no need to _specify_ that the check is elided. But these otherwise pointless
2745
+ runtime type tests _can_ fail in a mixed-mode program if a legacy typed value
2746
+ flows into a pattern. For example:*
2747
+
2748
+ ```dart
2749
+ // legacy.dart
2750
+ int legacyInt = null;
2751
+
2752
+ // current.dart
2753
+ import 'legacy.dart';
2754
+
2755
+ f(int i) {
2756
+ if (i case _) { // Wildcard has inferred static type non-legacy int.
2757
+ print('matched');
2758
+ } else {
2759
+ print('unreachable');
2760
+ }
2761
+ }
2762
+
2763
+ main() {
2764
+ f(legacyInt);
2765
+ }
2766
+ ```
2767
+
2768
+ * If we always require the type test, then this would print "unreachable". But
2769
+ that would require inserting type tests which are especially confusing in
2770
+ wildcard patterns which users expect should always match. Instead, we allow the
2771
+ value to flow through instead of forcing the compiler to insert runtime checks
2772
+ that are otherwise pointless and costly in terms of code size. This program
2773
+ should print "matched".*
2774
+
2775
+ * In a fully null-safe program, these type tests can never fail and it is not
2776
+ user-visible whether or not an implementation elides them.*
2777
+
2669
2778
### Side effects and exhaustiveness
2670
2779
2671
2780
You might expect this to be soundly exhaustive:
@@ -3011,6 +3120,21 @@ Here is one way it could be broken down into separate pieces:
3011
3120
3012
3121
## Changelog
3013
3122
3123
+ ### 2.18
3124
+
3125
+ - Support negative number literals in patterns (#2663).
3126
+
3127
+ - Allow map patterns in pattern assignments in expression statements (#2662).
3128
+
3129
+ - Remove left recursion in grammar for `||` and `&&` (#2636). (The syntax and
3130
+ semantics are unchanged, it's just specified differently.)
3131
+
3132
+ - Allow symbol literals in patterns (#2636).
3133
+
3134
+ - Give compilers more leeway on the runtime semantics of map patterns (#2634).
3135
+
3136
+ - Elide type tests that can only fail on legacy types (#2619).
3137
+
3014
3138
### 2.17
3015
3139
3016
3140
- Change logical pattern syntax to `||` and `&&` (#2501).
0 commit comments