Skip to content

Commit 72f8d32

Browse files
BillWagnerjskeet
andauthored
Edits suggested for patterns (#934)
* Edits suggested for patterns Respond to the comments from the committee on the first take at patterns. * Use the rules for `is type` for the type pattern The rules to determine if an `is` expression returns true are the same as determine if the `is` pattern matches. Also, added a note that the result is the same for a declaration pattern with a discard when the type isn't nullable. * respond to review comments * Update standard/expressions.md * Update standard/patterns.md Co-authored-by: Jon Skeet <[email protected]> --------- Co-authored-by: Jon Skeet <[email protected]>
1 parent 1d44af8 commit 72f8d32

File tree

8 files changed

+39
-34
lines changed

8 files changed

+39
-34
lines changed

standard/classes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4946,7 +4946,7 @@ A *static_constructor_declaration* may include a set of *attributes* ([§22](att
49464946

49474947
The *identifier* of a *static_constructor_declaration* shall name the class in which the static constructor is declared. If any other name is specified, a compile-time error occurs.
49484948

4949-
When a static constructor declaration includes an `extern` modifier, the static constructor is said to be an ***external static constructor***. Because an external static constructor declaration provides no actual implementation, its *static_constructor_body* consists of a semicolon. For all other static constructor declarations, the *static_constructor_body* consists of either
4949+
When a static constructor declaration includes an `extern` modifier, the static constructor is said to be an ***external static constructor***. Because an external static constructor declaration provides no actual implementation, its *static_constructor_body* consists of a semicolon. For all other static constructor declarations, the *static_constructor_body* consists of either
49504950

49514951
- a *block*, which specifies the statements to execute in order to initialize the class; or
49524952
- an expression body, which consists of `=>` followed by an *expression* and a semicolon, and denotes a single expression to execute in order to initialize the class.

standard/expressions.md

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2160,7 +2160,7 @@ A *null_conditional_element_access* expression `E` is of the form `P?[A]B`; wher
21602160
((object)P == null) ? null : P[A]B
21612161
```
21622162

2163-
Except that `P` is evaluated only once.
2163+
Except that `P` is evaluated only once.
21642164

21652165
> *Note*: In an expression of the form:
21662166
>
@@ -4019,6 +4019,8 @@ equality_expression
40194019
;
40204020
```
40214021

4022+
> *Note*: Lookup for the right operand of the `is` operator must first test as a *type*, then as an *expression* which may span multiple tokens. In the case where the operand is an *expreesion*, the pattern expression must have precedence at least as high as *shift_expression*. *end note*
4023+
40224024
The `is` operator is described in [§12.12.12](expressions.md#121212-the-is-operator) and the `as` operator is described in [§12.12.13](expressions.md#121213-the-as-operator).
40234025

40244026
The `==`, `!=`, `<`, `>`, `<=` and `>=` operators are ***comparison operators***.
@@ -4378,16 +4380,16 @@ The tuple equality operator `x != y` is evaluated as follows:
43784380

43794381
### 12.12.12 The is operator
43804382

4381-
There are two forms of the `is` operator. One is the *is-type operator*, which has a type on the right-hand-side. The other is the *is-pattern operator*, which has a pattern on the right-hand-side.
4383+
There are two forms of the `is` operator. One is the *is-type operator*, which has a type on the right-hand-side. The other is the *is-pattern operator*, which has a pattern on the right-hand-side.
43824384

43834385
#### 12.12.12.1 The is-type operator
43844386

43854387
The *is-type operator* is used to check if the run-time type of an object is compatible with a given type. The check is performed at runtime. The result of the operation `E is T`, where `E` is an expression and `T` is a type other than `dynamic`, is a Boolean value indicating whether `E` is non-null and can successfully be converted to type `T` by a reference conversion, a boxing conversion, an unboxing conversion, a wrapping conversion, or an unwrapping conversion.
43864388

43874389
The operation is evaluated as follows:
43884390

4389-
1. If `E` is an anonymous function, a compile-time error occurs
4390-
1. If `E` is a method group or the `null` literal, of if the value of `E` is `null`, the result is `false`.
4391+
1. If `E` is an anonymous function or method group, a compile-time error occurs
4392+
1. If `E` is the `null` literal, or if the value of `E` is `null`, the result is `false`.
43914393
1. Otherwise:
43924394
1. Let `R` be the runtime type of `E`.
43934395
1. Let `D` be derived from `R` as follows:
@@ -4804,7 +4806,7 @@ If `ref` is present:
48044806
If `ref` is not present, the second and third operands, `x` and `y`, of the `?:` operator control the type of the conditional expression:
48054807

48064808
- If `x` has type `X` and `y` has type `Y` then,
4807-
- If an identity conversion exists between `X` and `Y`, then the result is the best common type of a set of expressions ([§12.6.3.15](expressions.md#126315-finding-the-best-common-type-of-a-set-of-expressions)). If either type is `dynamic`, type inference prefers `dynamic` ([§8.7](types.md#87-the-dynamic-type)). If either type is a tuple type ([§8.3.11](types.md#8311-tuple-types)), type inference includes the element names when the element names in the same ordinal position match in both tuples.
4809+
- If an identity conversion exists between `X` and `Y`, then the result is the best common type of a set of expressions ([§12.6.3.15](expressions.md#126315-finding-the-best-common-type-of-a-set-of-expressions)). If either type is `dynamic`, type inference prefers `dynamic` ([§8.7](types.md#87-the-dynamic-type)). If either type is a tuple type ([§8.3.11](types.md#8311-tuple-types)), type inference includes the element names when the element names in the same ordinal position match in both tuples.
48084810
- Otherwise, if an implicit conversion ([§10.2](conversions.md#102-implicit-conversions)) exists from `X` to `Y`, but not from `Y` to `X`, then `Y` is the type of the conditional expression.
48094811
- Otherwise, if an implicit enumeration conversion ([§10.2.4](conversions.md#1024-implicit-enumeration-conversions)) exists from `X` to `Y`, then `Y` is the type of the conditional expression.
48104812
- Otherwise, if an implicit enumeration conversion ([§10.2.4](conversions.md#1024-implicit-enumeration-conversions)) exists from `Y` to `X`, then `X` is the type of the conditional expression.
@@ -4903,7 +4905,7 @@ When recognising an *anonymous_function_body* if both the *null_conditional_invo
49034905
<!-- markdownlint-disable MD028 -->
49044906
49054907
<!-- markdownlint-enable MD028 -->
4906-
> *Example*: The result type of `List<T>.Reverse` is `void`. In the following code, the body of the anonymous expression is a *null_conditional_invocation_expression*, so it is not an error.
4908+
> *Example*: The result type of `List<T>.Reverse` is `void`. In the following code, the body of the anonymous expression is a *null_conditional_invocation_expression*, so it is not an error.
49074909
>
49084910
> <!-- Example: {template:"standalone-console", name:"AnonFunctExpressions"} -->
49094911
> ```csharp
@@ -6041,11 +6043,11 @@ orderby «k1» , «k2» , ... , «kn»
60416043
is translated into
60426044

60436045
```csharp
6044-
from «x» in ( «e» ) .
6045-
OrderBy ( «x» => «k1» ) .
6046-
ThenBy ( «x» => «k2» ) .
6047-
... .
6048-
ThenBy ( «x» => «kn» )
6046+
from «x» in ( «e» ) .
6047+
OrderBy ( «x» => «k1» ) .
6048+
ThenBy ( «x» => «k2» ) .
6049+
... .
6050+
ThenBy ( «x» => «kn» )
60496051
...
60506052
```
60516053

standard/lexical-structure.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Conforming implementations shall accept Unicode compilation units encoded with t
1616
<!-- markdownlint-disable MD028 -->
1717
1818
<!-- markdownlint-enable MD028 -->
19-
> *Note*: It is beyond the scope of this specification to define how a file using a character representation other than Unicode might be transformed into a sequence of Unicode characters. During such transformation, however, it is recommended that the usual line-separating character (or sequence) in the other character set be translated to the two-character sequence consisting of the Unicode carriage-return character (U+000D) followed by Unicode line-feed character (U+000A). For the most part this transformation will have no visible effects; however, it will affect the interpretation of verbatim string literal tokens ([§6.4.5.6](lexical-structure.md#6456-string-literals)). The purpose of this recommendation is to allow a verbatim string literal to produce the same character sequence when its compilation unit is moved between systems that support differing non-Unicode character sets, in particular, those using differing character sequences for line-separation. *end note*
19+
> *Note*: It is beyond the scope of this specification to define how a file using a character representation other than Unicode might be transformed into a sequence of Unicode characters. During such transformation, however, it is recommended that the usual line-separating character (or sequence) in the other character set be translated to the two-character sequence consisting of the Unicode carriage-return character (U+000D) followed by Unicode line-feed character (U+000A). For the most part this transformation will have no visible effects; however, it will affect the interpretation of verbatim string literal tokens ([§6.4.5.6](lexical-structure.md#6456-string-literals)). The purpose of this recommendation is to allow a verbatim string literal to produce the same character sequence when its compilation unit is moved between systems that support differing non-Unicode character sets, in particular, those using differing character sequences for line-separation. *end note*
2020
2121
## 6.2 Grammars
2222

@@ -72,7 +72,7 @@ If a sequence of tokens can be parsed (in context) as a *simple_name* ([§12.8.4
7272
- A contextual query keyword appearing inside a query expression; or
7373
- In certain contexts, *identifier* is treated as a disambiguating token. Those contexts are where the sequence of tokens being disambiguated is immediately preceded by one of the keywords `is`, `case` or `out`, or arises while parsing the first element of a tuple literal (in which case the tokens are preceded by `(` or `:` and the identifier is followed by a `,`) or a subsequent element of a tuple literal.
7474
75-
If the following token is among this list, or an identifier in such a context, then the *type_argument_list* is retained as part of the *simple_name*, *member_access* or *pointer_member-access* and any other possible parse of the sequence of tokens is discarded. Otherwise, the *type_argument_list* is not considered to be part of the *simple_name*, *member_access* or *pointer_member_access*, even if there is no other possible parse of the sequence of tokens. (These rules are not applied when parsing a *type_argument_list* in a *namespace_or_type_name* [§7.8](basic-concepts.md#78-namespace-and-type-names).)
75+
If the following token is among this list, or an identifier in such a context, then the *type_argument_list* is retained as part of the *simple_name*, *member_access* or *pointer_member-access* and any other possible parse of the sequence of tokens is discarded. Otherwise, the *type_argument_list* is not considered to be part of the *simple_name*, *member_access* or *pointer_member_access*, even if there is no other possible parse of the sequence of tokens. (These rules are not applied when parsing a *type_argument_list* in a *namespace_or_type_name* [§7.8](basic-concepts.md#78-namespace-and-type-names).)
7676
7777
> *Note*: These rules are not applied when parsing a *type_argument_list* in a *namespace_or_type_name* ([§7.8](basic-concepts.md#78-namespace-and-type-names)). *end note*
7878
<!-- markdownlint-disable MD028 -->

standard/patterns.md

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ pattern
2020

2121
A *declaration_pattern* and a *var_pattern* can result in the declaration of a local variable.
2222

23-
Each pattern form defines the set of types for input values that the pattern may be applied to. A pattern `P` is *applicable to* a type `T` if `T` is among the types whose values the pattern may match. It is an error if a pattern `P` appears in a program to match a pattern input value ([§11.1](patterns.md#111-general)) of type `T` if `P` is not applicable to `T`.
23+
Each pattern form defines the set of types for input values that the pattern may be applied to. A pattern `P` is *applicable to* a type `T` if `T` is among the types whose values the pattern may match. It is a compile-time error if a pattern `P` appears in a program to match a pattern input value ([§11.1](patterns.md#111-general)) of type `T` if `P` is not applicable to `T`.
2424

25-
Each pattern form defines the set of values for which the pattern *matches* the value.
25+
Each pattern form defines the set of values for which the pattern *matches* the value at runtime.
2626

2727
### 11.2.2 Declaration pattern
2828

@@ -40,11 +40,13 @@ single_variable_designation
4040
;
4141
```
4242

43-
The runtime type of the value is tested against the *type* in the pattern. If it is of that runtime type (or some subtype), the pattern *matches* that value. This pattern form never matches a `null` value.
43+
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.12.12.1). 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). This pattern form never matches a `null` value.
44+
45+
> *Note*: The is-type expression `e is T` and the declaration pattern `e is T _` are equivalent when `T` isn't a nullable type. *end note*
4446
4547
Given a pattern input value ([§11.1](patterns.md#111-general)) *e*, if the *simple_designation* is the *identifier* `_`, it denotes a discard ([§9.2.9.1](variables.md#9291-discards)) and the value of *e* is not bound to anything. (Although a declared variable with the name `_` may be in scope at that point, that named variable is not seen in this context.) If *simple_designation* is any other identifier, a local variable ([§9.2.9](variables.md#929-local-variables)) of the given type named by the given identifier is introduced. That local variable is assigned the value of the pattern input value when the pattern *matches* the value.
4648

47-
Certain combinations of static type of the pattern input value and the given type are considered incompatible and result in a compile-time error. A value of static type `E` is said to be ***pattern compatible*** with the type `T` if there exists an identity conversion, an implicit reference conversion, a boxing conversion, an explicit reference conversion, or an unboxing conversion from `E` to `T`, or if either `E` or `T` is an open type ([§8.4.3](types.md#843-open-and-closed-types)). A declaration pattern naming a type `T` is *applicable to* every type `E` for which `E` is pattern compatible with `T`.
49+
Certain combinations of static type of the pattern input value and the given type are considered incompatible and result in a compile-time error. A value of static type `E` is said to be ***pattern compatible*** with the type `T` if there exists an identity conversion, an implicit or explicit reference conversion, a boxing conversion, or an unboxing conversion from `E` to `T`, or if either `E` or `T` is an open type ([§8.4.3](types.md#843-open-and-closed-types)). A declaration pattern naming a type `T` is *applicable to* every type `E` for which `E` is pattern compatible with `T`.
4850

4951
> *Note*: The support for open types can be most useful when checking types that may be either struct or class types, and boxing is to be avoided. *end note*
5052
<!-- markdownlint-disable MD028 -->
@@ -90,22 +92,23 @@ A constant pattern `P` is *applicable to* a type `T` if there is an implicit con
9092

9193
For a constant pattern `P`, its *converted value* is
9294

93-
- if the input expression’s type is an integral type or an enum type, the pattern’s constant value converted to that type; otherwise
94-
- if the input expression’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
95+
- 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
96+
- 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
9597
- the value of the pattern’s constant value.
9698

9799
Given a pattern input value *e* and a constant pattern `P` with converted value *v*,
98100

99101
- 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
100102
- the pattern `P` *matches* the value *e* if `object.Equals(e, v)` returns `true`.
101103

102-
> *Example*:
104+
> *Example*: The `switch` statement in the following method uses five constant patterns in its case labels.
103105
>
104106
> <!-- Example: {template:"standalone-console", name:"ConstantPattern1", replaceEllipsis:true, customEllipsisReplacements: ["\"xxx\""], ignoredWarnings:["CS8321"]} -->
105107
> ```csharp
106108
> static decimal GetGroupTicketPrice(int visitorCount)
107109
> {
108-
> switch (visitorCount) {
110+
> switch (visitorCount)
111+
> {
109112
> case 1: return 12.0m;
110113
> case 2: return 20.0m;
111114
> case 3: return 27.0m;
@@ -120,7 +123,7 @@ Given a pattern input value *e* and a constant pattern `P` with converted value
120123
121124
### 11.2.4 Var pattern
122125
123-
A *var_pattern* matches every value. That is, a pattern-matching operation with a *var_pattern* always succeeds.
126+
A *var_pattern* *matches* every value. That is, a pattern-matching operation with a *var_pattern* always succeeds.
124127
125128
A *var_pattern* is *applicable to* every type.
126129
@@ -158,7 +161,7 @@ The following rules define when a set of patterns is *exhaustive* for a type:
158161

159162
A set of patterns `Q` is *exhaustive* for a type `T` if any of the following conditions hold:
160163

161-
1. `T` is an integral or enum type, or a nullable version of one of those, and for every possible value of `T`’s underlying type, some pattern in `Q` would match that value; or
164+
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
162165
2. Some pattern in `Q` is a *var pattern*; or
163166
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`.
164167

0 commit comments

Comments
 (0)