From 6f6a2502999b58d532ec85eb808286d672e38d92 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 19 May 2026 11:55:35 -0400 Subject: [PATCH 1/2] Dealing with var, parts 1 and 2 1. A *var_pattern* can't be used if an identifier `var` is in scope. 2. If `var` is a type in scope, it can't be used as a *declaration_pattern*. However, other "spellings" of `var` are allowed as a type. --- standard/patterns.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/standard/patterns.md b/standard/patterns.md index 72d42d398..6b1da86eb 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -84,6 +84,10 @@ single_variable_designation A *simple_designation* with the token `_` shall be considered a *discard_designation* rather than a *single_variable_designation*. +The *type* of a *declaration_pattern* cannot be the *identifier* `var`. + +> *Note*: In such a case the input may be recognisable as a *var_pattern* ([§11.2.4](patterns.md#1124-var-pattern)). The exclusion applies only to the *identifier* `var`; the verbatim identifier `@var` and Unicode-escaped equivalents are distinct identifiers and remain valid as the *type* of a *declaration_pattern* when they name a type in scope. *end note* + The runtime type of the value is tested against the *type* in the pattern using the same rules specified in the is-type operator ([§12.14.12.1](expressions.md#1214121-the-is-type-operator)). If the test succeeds, the pattern *matches* that value. It is a compile-time error if the *type* is a nullable value type ([§8.3.12](types.md#8312-nullable-value-types)) or a nullable reference type ([§8.9.3](types.md#893-nullable-reference-types)). This pattern form never matches a `null` value. > *Note*: The is-type expression `e is T` and the declaration pattern `e is T _` are equivalent when `T` is not a nullable type. *end note* @@ -172,6 +176,8 @@ A *var_pattern* *matches* every value. That is, a pattern-matching operation wit A *var_pattern* is *applicable to* every type. +A *var_pattern* cannot be used when a type named `var` is in scope. + ```ANTLR var_pattern : 'var' designation @@ -190,8 +196,6 @@ designations Given a pattern input value ([§11.1](patterns.md#111-general)) *e*, if *designation* is *discard_designation*, it denotes a discard ([§9.2.9.2](variables.md#9292-discards)), and the value of *e* is not bound to anything. (Although a declared variable with that name may be in scope at that point, that named variable is not seen in this context.) Otherwise, if *designation* is *single_variable_designation*, at runtime the value of *e* is bound to a newly introduced local variable ([§9.2.9](variables.md#929-local-variables)) of that name whose type is the static type of *e*, and the pattern input value is assigned to that local variable. -It is an error if the name `var` would bind to a type where a *var_pattern* is used. - If *designation* is a *tuple_designation*, the pattern is equivalent to a *positional_pattern* ([§11.2.5](patterns.md#1125-positional-pattern)) of the form `(var` *designation*, … `)` where the *designation*s are those found within the *tuple_designation*. For example, the pattern `var (x, (y, z))` is equivalent to `(var x, (var y, var z))`. ### 11.2.5 Positional pattern From 7d8d6278038c3bf5cf1fae2b8444ac547894c6e6 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 21 May 2026 11:55:48 -0400 Subject: [PATCH 2/2] Address Initial feedback I knew this looked to easy. First, moved "Constant Pattern" before "Declaration Pattern". Now, *declaration_pattern* and *var_pattern* are adjacent. That should make it easier to determine if we want to combine them. The flow seems reasonable. Next, handle `var` in general. I moved these rules into the "general" section, because they apply across all patterns. If `var` is an identifier, it binds to that element. If `var` is a constant, it's a constant pattern. If it's a type, it's a type pattern. If it's a variable, it's an invalid pattern. (Similarly if its a method group or namespace). Finally, add a note about how tuple literals are parsed as different patterns depending on their form. --- standard/patterns.md | 114 ++++++++++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 46 deletions(-) diff --git a/standard/patterns.md b/standard/patterns.md index 6b1da86eb..d996fef36 100644 --- a/standard/patterns.md +++ b/standard/patterns.md @@ -62,6 +62,61 @@ Each pattern form defines the set of values for which the pattern *matches* the The order of evaluation of operations and side effects during pattern-matching (calls to `Deconstruct`, property accesses, and invocations of methods in `System.ITuple`) is not specified. +The *identifier* `var` appearing in any *pattern* is bound using the normal identifier-resolution rules. The *identifier* `var` may appear in patterns only where the entity to which it resolves is valid in that position. When no declaration of the *identifier* `var` is in scope, the form `var` *designation* is recognised as a *var_pattern* ([§11.2.4](patterns.md#1124-var-pattern)); when any declaration of the *identifier* `var` is in scope, a *var_pattern* cannot be used. The verbatim identifier `@var` and Unicode-escaped equivalents are distinct identifiers from the contextual keyword `var` and are bound and used in patterns according to the entities they denote, like any other identifier. + +> *Note*: When a declaration of the *identifier* `var` is in scope, the consequences of the rule above are: +> +> - If `var` denotes a type, then `var` may be the *type* of a *declaration_pattern* ([§11.2.2](patterns.md#1122-declaration-pattern)). +> - If `var` denotes a constant, then `var` may be the *constant_expression* of a *constant_pattern* ([§11.2.3](patterns.md#1123-constant-pattern)). +> - Otherwise (for example, when `var` denotes a field, property, local variable, parameter, or any other entity that is neither a type nor a constant), `var` is not valid in pattern position. +> +> *end note* + +### 11.2.3 Constant pattern + +A *constant_pattern* is used to test the value of a pattern input value ([§11.1](patterns.md#111-general)) against the given constant value. + +```ANTLR +constant_pattern + : constant_expression + ; +``` + +When a declaration of the *identifier* `var` in scope resolves to a constant, `var` may appear as the *constant_expression* of a *constant_pattern*; the general rule for the *identifier* `var` in patterns is specified in [§11.2.1](patterns.md#1121-general). + +A constant pattern `P` is *applicable to* a type `T` if there is an implicit conversion from the constant expression of `P` to the type `T`. + +For a constant pattern `P`, its *converted value* is + +- if the pattern input value’s type is an integral type or an enum type, the pattern’s constant value converted to that type; otherwise +- if the pattern input value’s type is the nullable version of an integral type or an enum type, the pattern’s constant value converted to its underlying type; otherwise +- the value of the pattern’s constant value. + +Given a pattern input value *e* and a constant pattern `P` with converted value *v*, + +- if *e* has integral type or enum type, or a nullable form of one of those, and *v* has integral type, the pattern `P` *matches* the value *e* if result of the expression `e == v` is `true`; otherwise +- the pattern `P` *matches* the value *e* if `object.Equals(e, v)` returns `true`. + +> *Example*: The `switch` statement in the following method uses five constant patterns in its case labels. +> +> +> ```csharp +> static decimal GetGroupTicketPrice(int visitorCount) +> { +> switch (visitorCount) +> { +> case 1: return 12.0m; +> case 2: return 20.0m; +> case 3: return 27.0m; +> case 4: return 32.0m; +> case 0: return 0.0m; +> default: throw new ArgumentException(...); +> } +> } +> ``` +> +> *end example* + ### 11.2.2 Declaration pattern A *declaration_pattern* is used to test that a value has a given type and, if the test succeeds, to optionally provide the value in a variable of that type. @@ -84,9 +139,9 @@ single_variable_designation A *simple_designation* with the token `_` shall be considered a *discard_designation* rather than a *single_variable_designation*. -The *type* of a *declaration_pattern* cannot be the *identifier* `var`. +When a declaration of the *identifier* `var` in scope resolves to a type, `var` may appear as the *type* of a *declaration_pattern*; the general rule for the *identifier* `var` in patterns is specified in [§11.2.1](patterns.md#1121-general). -> *Note*: In such a case the input may be recognisable as a *var_pattern* ([§11.2.4](patterns.md#1124-var-pattern)). The exclusion applies only to the *identifier* `var`; the verbatim identifier `@var` and Unicode-escaped equivalents are distinct identifiers and remain valid as the *type* of a *declaration_pattern* when they name a type in scope. *end note* +The *type* of a *declaration_pattern* cannot be `dynamic`, because the runtime type test is defined in terms of the is-type operator ([§12.14.12.1](expressions.md#1214121-the-is-type-operator)), which does not permit `dynamic`. The runtime type of the value is tested against the *type* in the pattern using the same rules specified in the is-type operator ([§12.14.12.1](expressions.md#1214121-the-is-type-operator)). If the test succeeds, the pattern *matches* that value. It is a compile-time error if the *type* is a nullable value type ([§8.3.12](types.md#8312-nullable-value-types)) or a nullable reference type ([§8.9.3](types.md#893-nullable-reference-types)). This pattern form never matches a `null` value. @@ -127,56 +182,13 @@ Certain combinations of static type of the pattern input value and the given typ > > The condition of the `if` statement is `true` at runtime and the variable `v` holds the value `3` of type `int` inside the block. After the block the variable `v` is in scope, but not definitely assigned. *end example* -### 11.2.3 Constant pattern - -A *constant_pattern* is used to test the value of a pattern input value ([§11.1](patterns.md#111-general)) against the given constant value. - -```ANTLR -constant_pattern - : constant_expression - ; -``` - -A constant pattern `P` is *applicable to* a type `T` if there is an implicit conversion from the constant expression of `P` to the type `T`. - -For a constant pattern `P`, its *converted value* is - -- if the pattern input value’s type is an integral type or an enum type, the pattern’s constant value converted to that type; otherwise -- if the pattern input value’s type is the nullable version of an integral type or an enum type, the pattern’s constant value converted to its underlying type; otherwise -- the value of the pattern’s constant value. - -Given a pattern input value *e* and a constant pattern `P` with converted value *v*, - -- if *e* has integral type or enum type, or a nullable form of one of those, and *v* has integral type, the pattern `P` *matches* the value *e* if result of the expression `e == v` is `true`; otherwise -- the pattern `P` *matches* the value *e* if `object.Equals(e, v)` returns `true`. - -> *Example*: The `switch` statement in the following method uses five constant patterns in its case labels. -> -> -> ```csharp -> static decimal GetGroupTicketPrice(int visitorCount) -> { -> switch (visitorCount) -> { -> case 1: return 12.0m; -> case 2: return 20.0m; -> case 3: return 27.0m; -> case 4: return 32.0m; -> case 0: return 0.0m; -> default: throw new ArgumentException(...); -> } -> } -> ``` -> -> *end example* - ### 11.2.4 Var pattern A *var_pattern* *matches* every value. That is, a pattern-matching operation with a *var_pattern* always succeeds. A *var_pattern* is *applicable to* every type. -A *var_pattern* cannot be used when a type named `var` is in scope. +A *var_pattern* cannot be used when any declaration of the *identifier* `var` is in scope. See [§11.2.1](patterns.md#1121-general) for the interpretation of `var` in pattern position in that case. ```ANTLR var_pattern @@ -217,6 +229,16 @@ subpattern Given a match of an input value to the pattern *type* `(` *subpatterns* `)`, a method is selected by searching in *type* for accessible declarations of `Deconstruct` and selecting one among them using the same rules as for the deconstruction declaration. It is an error if a *positional_pattern* omits the type, has a single *subpattern* without an *identifier*, has no *property_subpattern* and has no *simple_designation*. This disambiguates between a *constant_pattern* that is parenthesized and a *positional_pattern*. + +> *Note*: A tuple literal can be matched by patterns of several different forms, which are not interchangeable: +> +> - `(int, int) x` is a *declaration_pattern* with type `(int, int)` and *simple_designation* `x`. +> - `var (x, y)` is a *var_pattern* with a *tuple_designation*. +> - `(int x, int y)` is a *positional_pattern*. +> - `(int, int) (x, y)` is not a valid pattern. +> +> *end note* + In order to extract the values to match against the patterns in the list, - If *type* is omitted and the input expression’s type is a tuple type, then the number of subpatterns shall to be the same as the cardinality of the tuple. Each tuple element is matched against the corresponding *subpattern*, and the match succeeds if all of these succeed. If any *subpattern* has an *identifier*, then that shall name a tuple element at the corresponding position in the tuple type.