From b50ca0c24f69c47e83c7c9f6a606363c6be0849b Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 13 May 2026 15:58:57 -0400 Subject: [PATCH 1/6] Minimal updates This first commit makes minimal adjustments to the subsumption and exhaustiveness sections. This keeps the rules fairly high level, and provides more leeway for an implementation to devise any algorithm for managing recursive patterns. --- standard/patterns.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/standard/patterns.md b/standard/patterns.md index c21159c64..f317849fb 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -456,9 +456,11 @@ A pattern `P` *would match* a constant `K` if the specification for that pattern A set of patterns `Q` *subsumes* a pattern `P` if any of the following conditions hold: - `P` is a constant pattern and any of the patterns in the set `Q` would match `P`’s *converted value* -- `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`. +- `P` is a var pattern, 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, if the pattern input value is of a nullable type, some pattern in `Q` would match `null`. - `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)). +Subsumption involving a *positional_pattern* or a *property_pattern* is not otherwise defined by this specification; an implementation is permitted, but not required, to detect additional cases of subsumption involving such patterns. + > *Example*: In the following switch expression, no arm is subsumed even though arms 1, 2, and 3 share the same pattern: > > @@ -484,9 +486,11 @@ The following rules define when a set of patterns is *exhaustive* for a type: A set of patterns `Q` is *exhaustive* for a type `T` if any of the following conditions hold: 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 -2. Some pattern in `Q` is a *var pattern*; or +2. Some pattern in `Q` is a *var_pattern* or a *discard_pattern*; or 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`. +A set of patterns containing only *constant_pattern*s, *positional_pattern*s, or *property_pattern*s (in any combination), other than as covered by rule 1 above, is not considered exhaustive by this specification. An implementation is permitted, but not required, to recognize additional sets of patterns as exhaustive. + > *Example*: > > From 4858e6598c5ee4deac3c2d33f69e186027e0e942 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 13 May 2026 16:08:55 -0400 Subject: [PATCH 2/6] More extensive rules Provide more extensive rule definitions for recursive patterns on subsumption and exhaustiveness. This version leans more closely to the implementation, but also doesn't dive deep enough to provide more clarity on pattern forms where exhaustiveness or subsumption might not be detected. I'm tempted to use the first commit, but I want people to see both. --- standard/patterns.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/standard/patterns.md b/standard/patterns.md index f317849fb..e0d618564 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -458,8 +458,9 @@ A set of patterns `Q` *subsumes* a pattern `P` if any of the following condition - `P` is a constant pattern and any of the patterns in the set `Q` would match `P`’s *converted value* - `P` is a var pattern, 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, if the pattern input value is of a nullable type, some pattern in `Q` would match `null`. - `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)). +- `P` is a *positional_pattern* or a *property_pattern* and every value that `P` would match would also be matched by some pattern in `Q`. -Subsumption involving a *positional_pattern* or a *property_pattern* is not otherwise defined by this specification; an implementation is permitted, but not required, to detect additional cases of subsumption involving such patterns. +> *Note*: The last rule applies recursively through *sub-patterns*; for example, the set `{ (var x, var y) }` subsumes the pattern `(0, 0)` because every pair of values matched by the latter is also matched by the former. The precise extent to which an implementation detects subsumption involving *positional_pattern*s and *property_pattern*s is not specified beyond what these rules require. *end note* > *Example*: In the following switch expression, no arm is subsumed even though arms 1, 2, and 3 share the same pattern: > @@ -487,10 +488,13 @@ A set of patterns `Q` is *exhaustive* for a type `T` if any of the following con 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 2. Some pattern in `Q` is a *var_pattern* or a *discard_pattern*; or -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`. +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`; or +4. `Q` contains a set of *positional_pattern*s or *property_pattern*s such that every non-`null` value of `T` is matched by at least one pattern in that set. -A set of patterns containing only *constant_pattern*s, *positional_pattern*s, or *property_pattern*s (in any combination), other than as covered by rule 1 above, is not considered exhaustive by this specification. An implementation is permitted, but not required, to recognize additional sets of patterns as exhaustive. +> *Note*: Rule 4 applies recursively through *sub-patterns*. The precise extent to which an implementation detects exhaustiveness arising from combinations of *positional_pattern*s, *property_pattern*s, and *constant_pattern*s is not specified beyond what these rules require. *end note* + + > *Example*: > > From ac34c143a2fbb365f1818c49d20ece1c074a001e Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 11 Jun 2026 14:56:26 -0400 Subject: [PATCH 3/6] Revert "More extensive rules" This reverts commit 4858e6598c5ee4deac3c2d33f69e186027e0e942. --- standard/patterns.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/standard/patterns.md b/standard/patterns.md index e0d618564..f317849fb 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -458,9 +458,8 @@ A set of patterns `Q` *subsumes* a pattern `P` if any of the following condition - `P` is a constant pattern and any of the patterns in the set `Q` would match `P`’s *converted value* - `P` is a var pattern, 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, if the pattern input value is of a nullable type, some pattern in `Q` would match `null`. - `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)). -- `P` is a *positional_pattern* or a *property_pattern* and every value that `P` would match would also be matched by some pattern in `Q`. -> *Note*: The last rule applies recursively through *sub-patterns*; for example, the set `{ (var x, var y) }` subsumes the pattern `(0, 0)` because every pair of values matched by the latter is also matched by the former. The precise extent to which an implementation detects subsumption involving *positional_pattern*s and *property_pattern*s is not specified beyond what these rules require. *end note* +Subsumption involving a *positional_pattern* or a *property_pattern* is not otherwise defined by this specification; an implementation is permitted, but not required, to detect additional cases of subsumption involving such patterns. > *Example*: In the following switch expression, no arm is subsumed even though arms 1, 2, and 3 share the same pattern: > @@ -488,13 +487,10 @@ A set of patterns `Q` is *exhaustive* for a type `T` if any of the following con 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 2. Some pattern in `Q` is a *var_pattern* or a *discard_pattern*; or -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`; or -4. `Q` contains a set of *positional_pattern*s or *property_pattern*s such that every non-`null` value of `T` is matched by at least one pattern in that set. +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`. -> *Note*: Rule 4 applies recursively through *sub-patterns*. The precise extent to which an implementation detects exhaustiveness arising from combinations of *positional_pattern*s, *property_pattern*s, and *constant_pattern*s is not specified beyond what these rules require. *end note* - +A set of patterns containing only *constant_pattern*s, *positional_pattern*s, or *property_pattern*s (in any combination), other than as covered by rule 1 above, is not considered exhaustive by this specification. An implementation is permitted, but not required, to recognize additional sets of patterns as exhaustive. - > *Example*: > > From fef79e02f3322be9531591d50d9c055995237c7d Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 11 Jun 2026 17:25:46 -0400 Subject: [PATCH 4/6] Make exhaustiveness minimal Use a minimal definition for subsumption and exhaustiveness. --- standard/patterns.md | 59 ++------------------------------------------ 1 file changed, 2 insertions(+), 57 deletions(-) diff --git a/standard/patterns.md b/standard/patterns.md index f317849fb..6bfa3f744 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -448,64 +448,9 @@ If, after applying the preceding rule, the token `_` is still a *discard_pattern ## 11.3 Pattern subsumption In a switch statement ([§13.8.3](statements.md#1383-the-switch-statement)), it is an error if a case’s pattern is *subsumed* by the preceding set of *unguarded* ([§13.8.3](statements.md#1383-the-switch-statement)) cases. In a switch expression ([§12.11](expressions.md#1211-switch-expression)), it is an error if a *switch_expression_arm*’s pattern is *subsumed* by the preceding set of *unguarded* *switch_expression_arm*s’ patterns. -> *Note*: This means that any input value would have been matched by one of the previous cases or arms. *end note* -The following rules define when a set of patterns subsumes a given pattern: - -A pattern `P` *would match* a constant `K` if the specification for that pattern’s runtime behavior is that `P` matches `K`. - -A set of patterns `Q` *subsumes* a pattern `P` if any of the following conditions hold: - -- `P` is a constant pattern and any of the patterns in the set `Q` would match `P`’s *converted value* -- `P` is a var pattern, 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, if the pattern input value is of a nullable type, some pattern in `Q` would match `null`. -- `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)). -Subsumption involving a *positional_pattern* or a *property_pattern* is not otherwise defined by this specification; an implementation is permitted, but not required, to detect additional cases of subsumption involving such patterns. - -> *Example*: In the following switch expression, no arm is subsumed even though arms 1, 2, and 3 share the same pattern: -> -> -> ```csharp -> object x = 10; -> bool b = false; -> int y = x switch -> { -> int i when !b => 0, -> int i when b => 1, -> int i => 2, -> _ => 3 -> }; -> ``` -> -> Arms 1 and 2 have non-constant guards and so are not *unguarded*; only arm 3 is *unguarded* with pattern `int i`, which does not subsume the final `_` arm because it does not match a non-`int` value such as `null`. *end example* +> *Note*: This means that any input value would have been matched by one of the previous cases or arms. *end note* ## 11.4 Pattern exhaustiveness -Informally, a set of patterns is exhaustive for a type if, for every possible value of that type other than null, some pattern in the set is applicable. -The following rules define when a set of patterns is *exhaustive* for a type: - -A set of patterns `Q` is *exhaustive* for a type `T` if any of the following conditions hold: - -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 -2. Some pattern in `Q` is a *var_pattern* or a *discard_pattern*; or -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`. - -A set of patterns containing only *constant_pattern*s, *positional_pattern*s, or *property_pattern*s (in any combination), other than as covered by rule 1 above, is not considered exhaustive by this specification. An implementation is permitted, but not required, to recognize additional sets of patterns as exhaustive. - -> *Example*: -> -> -> ```csharp -> static void M(byte b) -> { -> switch (b) { -> case 0: case 1: case 2: ... // handle every specific value of byte -> break; -> // error: the pattern 'byte other' is subsumed by the (exhaustive) -> // previous cases -> case byte other: -> break; -> } -> } -> ``` -> -> *end example* +A set of patterns is exhaustive if, for every possible input value, some pattern in the set is applicable. When an implementation detects that a set of patterns is not exhaustive, it shall issue a warning. From ac88c846beb64126ce80131db4c557cb081708f9 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 2 Jul 2026 10:22:20 -0400 Subject: [PATCH 5/6] Apply suggestions from code review Co-authored-by: Nigel-Ecma <6654683+Nigel-Ecma@users.noreply.github.com> --- standard/patterns.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/patterns.md b/standard/patterns.md index 6bfa3f744..44ca5d2f5 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -449,7 +449,7 @@ If, after applying the preceding rule, the token `_` is still a *discard_pattern In a switch statement ([§13.8.3](statements.md#1383-the-switch-statement)), it is an error if a case’s pattern is *subsumed* by the preceding set of *unguarded* ([§13.8.3](statements.md#1383-the-switch-statement)) cases. In a switch expression ([§12.11](expressions.md#1211-switch-expression)), it is an error if a *switch_expression_arm*’s pattern is *subsumed* by the preceding set of *unguarded* *switch_expression_arm*s’ patterns. -> *Note*: This means that any input value would have been matched by one of the previous cases or arms. *end note* +A pattern `P` is *subsumed* by set of unguarded patterns `Q` if any input value matched by `P` is matched by one of the members of `Q`. ## 11.4 Pattern exhaustiveness From 2c01cab6014af937375c50294e050fe5b81cf051 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 2 Jul 2026 10:33:21 -0400 Subject: [PATCH 6/6] Edits discussed in 7/1 meeting Implement the edits discussed in the July 1st meeting. --- standard/expressions.md | 2 +- standard/patterns.md | 16 ++++++---------- standard/statements.md | 6 +++--- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/standard/expressions.md b/standard/expressions.md index 2287f2233..3f8a1ce40 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -3887,7 +3887,7 @@ If a switch expression is not subject to a *switch expression conversion*, then - The type of the *switch_expression* is the best common type [§12.6.3.16](expressions.md#126316-finding-the-best-common-type-of-a-set-of-expressions)) of the *switch_expression_arm_expression*s of the *switch_expression_arm*s, if such a type exists, and each *switch_expression_arm_expression* can be implicitly converted to that type. - It is an error if no such type exists. -It is an error if the pattern of any *switch_expression_arm* is *subsumed* by ([§11.3](patterns.md#113-pattern-subsumption)) the set of patterns of earlier *unguarded* ([§13.8.3](statements.md#1383-the-switch-statement)) *switch_expression_arm*s of the switch expression. +It is an error if the pattern of any *switch_expression_arm* is *subsumed* by (§11.1) the set of patterns of earlier *unguarded* ([§13.8.3](statements.md#1383-the-switch-statement)) *switch_expression_arm*s of the switch expression. A switch expression is *exhaustive* if every value of its input is handled by at least one arm of the switch expression. A warning may be issued if a switch expression is not exhaustive. diff --git a/standard/patterns.md b/standard/patterns.md index 44ca5d2f5..b09874dc9 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -13,6 +13,12 @@ A pattern is tested against a value in a number of contexts: The value against which a pattern is tested is called the ***pattern input value***. +A pattern `P` is *subsumed* by set of unguarded patterns `Q` if any input value matched by `P` is matched by one of the members of `Q`. + +In a switch statement ([§13.8.3](statements.md#1383-the-switch-statement)), it is an error if a case’s pattern is *subsumed* by the preceding set of *unguarded* ([§13.8.3](statements.md#1383-the-switch-statement)) cases. In a switch expression ([§12.11](expressions.md#1211-switch-expression)), it is an error if a *switch_expression_arm*’s pattern is *subsumed* by the preceding set of *unguarded* *switch_expression_arm*s’ patterns. + +A set of patterns is exhaustive if, for every possible input value, some pattern in the set is applicable. When an implementation detects that a set of patterns is not exhaustive, it shall issue a warning. + ## 11.2 Pattern forms ### 11.2.1 General @@ -444,13 +450,3 @@ If, after applying the preceding rule, the token `_` is still a *discard_pattern > ``` > > *end example* - -## 11.3 Pattern subsumption - -In a switch statement ([§13.8.3](statements.md#1383-the-switch-statement)), it is an error if a case’s pattern is *subsumed* by the preceding set of *unguarded* ([§13.8.3](statements.md#1383-the-switch-statement)) cases. In a switch expression ([§12.11](expressions.md#1211-switch-expression)), it is an error if a *switch_expression_arm*’s pattern is *subsumed* by the preceding set of *unguarded* *switch_expression_arm*s’ patterns. - -A pattern `P` is *subsumed* by set of unguarded patterns `Q` if any input value matched by `P` is matched by one of the members of `Q`. - -## 11.4 Pattern exhaustiveness - -A set of patterns is exhaustive if, for every possible input value, some pattern in the set is applicable. When an implementation detects that a set of patterns is not exhaustive, it shall issue a warning. diff --git a/standard/statements.md b/standard/statements.md index 4ef8a18e3..455cd83cd 100644 --- a/standard/statements.md +++ b/standard/statements.md @@ -753,7 +753,7 @@ There can be at most one `default` label in a `switch` statement. It is an error if the pattern of any switch label is not *applicable* ([§11.2.1](patterns.md#1121-general)) to the type of the input expression. -It is an error if the pattern of any switch label is *subsumed* by ([§11.3](patterns.md#113-pattern-subsumption)) the set of patterns of earlier *unguarded* switch labels of the switch statement. +It is an error if the pattern of any switch label is *subsumed* by (§11.1) the set of patterns of earlier *unguarded* switch labels of the switch statement. > *Example*: > @@ -949,7 +949,7 @@ A switch label is reachable if at least one of the following is true: - The switch’s *selector_expression* is not a constant value and either - the label is a `case` without a guard or with a guard whose value is not the constant false; or - it is a `default` label and - - the set of patterns appearing among the cases of the switch statement that do not have guards or have guards whose value is the constant true, is not *exhaustive* ([§11.4](patterns.md#114-pattern-exhaustiveness)) for the switch governing type; or + - the set of patterns appearing among the cases of the switch statement that do not have guards or have guards whose value is the constant true, is not *exhaustive* (§11.1) for the switch governing type; or - the switch governing type is a nullable type and the set of patterns appearing among the cases of the switch statement that do not have guards or have guards whose value is the constant true does not contain a pattern that would match the value `null`. - The switch label is referenced by a reachable `goto case` or `goto default` statement. @@ -959,7 +959,7 @@ The end point of a `switch` statement is reachable if the switch statement is re - The `switch` statement contains a reachable `break` statement that exits the `switch` statement. - No `default` label is present and either - - The switch’s *selector_expression* is a non-constant value, and the set of patterns appearing among the cases of the switch statement that do not have guards or have guards whose value is the constant true, is not *exhaustive* ([§11.4](patterns.md#114-pattern-exhaustiveness)) for the switch governing type. + - The switch’s *selector_expression* is a non-constant value, and the set of patterns appearing among the cases of the switch statement that do not have guards or have guards whose value is the constant true, is not *exhaustive* (§11.1) for the switch governing type. - The switch’s *selector_expression* is a non-constant value of a nullable type, and no pattern appearing among the cases of the switch statement that do not have guards or have guards whose value is the constant true would match the value `null`. - The switch’s *selector_expression* is a constant value and no `case` label without a guard or whose guard is the constant true would match that value.