Skip to content

Commit 378e7f8

Browse files
committed
Add subsumption and exhaustiveness
Add the subsumption and exhaustiveness rules for the new patterns in V9. The subsumption/exhaustiveness rules in the Roslyn compiler use a BDD (binary decision diagram)-based algorithm that is more powerful than can be expressed with simple prose rules. These rules attempt to capture the *observable behavior* (what the compiler accepts/rejects) rather than the implementation algorithm. Where the compiler's algorithm is too complex to fully express (e.g., relational range analysis), the spec describes the intent and give examples.
1 parent f2f6f94 commit 378e7f8

1 file changed

Lines changed: 32 additions & 4 deletions

File tree

standard/patterns.md

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,10 @@ negated_pattern
548548

549549
A *negated_pattern* matches if the pattern being negated does not match, and vice versa. A *conjunctive_pattern* requires both patterns to match. A *disjunctive_pattern* requires either pattern to match. Unlike their language operator counterparts, `&&` and `||`, `and` and `or` are *not* short-circuiting operators.
550550

551+
It is a compile-time error for a pattern variable to be declared beneath a `not` or `or` pattern operator.
552+
553+
> *Note*: Because neither `not` nor `or` can produce a definite assignment for a pattern variable, it is an error to declare one in those positions. *end note*
554+
551555
In a *conjunctive_pattern*, the *input type* of the second pattern is narrowed by the *type narrowing* requirements of first pattern of the `and`. The *narrowed type* of a pattern `P` is defined as follows:
552556

553557
- If `P` is a type pattern, the *narrowed type* is the type of the type pattern's type.
@@ -669,13 +673,29 @@ In a switch statement ([§13.8.3](statements.md#1383-the-switch-statement)), it
669673
> *Note*: This means that any input value would have been matched by one of the previous cases or arms. *end note*
670674
The following rules define when a set of patterns subsumes a given pattern:
671675
672-
A pattern `P` *would match* a constant `K` if the specification for that pattern’s runtime behavior is that `P` matches `K`.
676+
A pattern `P` *would match* a constant `K` if any of the following conditions hold:
677+
678+
- the specification for that pattern's runtime behavior is that `P` matches `K`.
679+
- `P` is a *type_pattern* for type `T` and `K` is not `null` and the runtime type of `K` is `T` or a type derived from `T` or a type that implements `T`.
680+
- `P` is a *relational_pattern* with operator «op» and constant `v`, and the expression `K` «op» `v` would evaluate to `true`.
681+
- `P` is a *negated_pattern* `not P₁` and `P₁` would not match `K`.
682+
- `P` is a *conjunctive_pattern* `P₁ and P₂` and both `P₁` would match `K` and `P₂` would match `K`.
683+
- `P` is a *disjunctive_pattern* `P₁ or P₂` and either `P₁` would match `K` or `P₂` would match `K`.
684+
- `P` is a *discard_pattern*.
673685
674686
A set of patterns `Q` *subsumes* a pattern `P` if any of the following conditions hold:
675687
676-
- `P` is a constant pattern and any of the patterns in the set `Q` would match `P`s *converted value*
688+
- `P` is a constant pattern and any of the patterns in the set `Q` would match `P`'s *converted value*
677689
- `P` is a var pattern and the set of patterns `Q` is *exhaustive* ([§11.4](patterns.md#114-pattern-exhaustiveness)) for the type of the pattern input value ([§11.1](patterns.md#111-general)), and either the pattern input value is not of a nullable type or some pattern in `Q` would match `null`.
678690
- `P` is a declaration pattern with type `T` and the set of patterns `Q` is *exhaustive* for the type `T` ([§11.4](patterns.md#114-pattern-exhaustiveness)).
691+
- `P` is a *type_pattern* for type `T` and the set of patterns `Q` is *exhaustive* for the type `T`.
692+
- `P` is a *relational_pattern* with operator «op» and constant value `v`, and for every value of the input type satisfying the relation «op» `v`, some pattern in the set `Q` would match that value.
693+
- `P` is a *disjunctive_pattern* `P₁ or P₂` and the set of patterns `Q` subsumes `P₁` and `Q` subsumes `P₂`.
694+
- `P` is a *conjunctive_pattern* `P₁ and P₂` and at least one of the following holds: `Q` subsumes `P₁`, or `Q` subsumes `P₂`.
695+
- `P` is a *negated_pattern* `not P₁` and `Q` is *exhaustive* for the input type considering only the values not matched by `P₁`.
696+
- `P` is a *discard_pattern* and the set of patterns `Q` is *exhaustive* for the type of the pattern input value, and either the pattern input value is not of a nullable type or some pattern in `Q` would match `null`.
697+
- Some pattern in `Q` is a *disjunctive_pattern* `Q₁ or Q₂` and replacing that pattern with `Q₁` in `Q` would yield a set that subsumes `P`, or replacing it with `Q₂` would yield a set that subsumes `P`.
698+
- Some pattern in `Q` is a *negated_pattern* `not Q₁` and `P` would not match any value that `Q₁` would match.
679699
680700
> *Example*: In the following switch expression, no arm is subsumed even though arms 1, 2, and 3 share the same pattern:
681701
>
@@ -702,8 +722,16 @@ The following rules define when a set of patterns is *exhaustive* for a type:
702722
A set of patterns `Q` is *exhaustive* for a type `T` if any of the following conditions hold:
703723
704724
1. `T` is an integral or enum type, or a nullable version of one of those, and for every possible value of `T`’s non-nullable underlying type, some pattern in `Q` would match that value; or
705-
2. Some pattern in `Q` is a *var pattern*; or
706-
3. Some pattern in `Q` is a *declaration pattern* for type `D`, and there is an identity conversion, an implicit reference conversion, or a boxing conversion from `T` to `D`.
725+
1. Some pattern in `Q` is a *var pattern*; or
726+
1. Some pattern in `Q` is a *declaration pattern* for type `D`, and there is an identity conversion, an implicit reference conversion, or a boxing conversion from `T` to `D`; or
727+
1. Some pattern in `Q` is a *type_pattern* for type `D`, and there is an identity conversion, an implicit reference conversion, or a boxing conversion from `T` to `D`; or
728+
1. Some pattern in `Q` is a *discard_pattern*; or
729+
1. The patterns in `Q` include a combination of *relational_pattern*s and *constant_pattern*s whose ranges collectively cover every possible value of `T`'s non-nullable underlying type. For `float` and `double` types, this includes `System.Double.NaN` or `System.Single.NaN` respectively, since `NaN` is not matched by any relational pattern; or
730+
1. Some pattern in `Q` is a *disjunctive_pattern* `Por P₂`, and replacing that pattern with both `P₁` and `P₂` in `Q` yields a set that is *exhaustive* for `T`; or
731+
1. Some pattern in `Q` is a *negated_pattern* `not P₁`, and the patterns in `Q` together with the values not matched by `P₁` cover every possible value of `T`. A *negated_pattern* `not P₁` is exhaustive by itself when `P₁` matches no possible value of `T`; or
732+
1. Some pattern in `Q` is a *conjunctive_pattern* `Pand P₂`, and the set containing only `P₁` is *exhaustive* for `T` and the set containing only `P₂` is *exhaustive* for `T`.
733+
734+
> *Note*: For floating-point types, the combination of patterns `< 0` and `>= 0` is *not* exhaustive because neither relational pattern matches `NaN`. A correct exhaustive set would be `< 0`, `>= 0`, and `double.NaN` (or `float.NaN`). *end note*
707735
708736
> *Example*:
709737
>

0 commit comments

Comments
 (0)