Skip to content

Conversation

@RexJaeschke
Copy link
Contributor

@RexJaeschke RexJaeschke commented Aug 4, 2023

Prior to V8, we have a term “switch expression” defined as the expression inside parens in a switch statement.

Now, we’re introducing a switch as an expression, and we have a new grammar rule called switch_expression. The difference between “switch expression” (without a *…* fence or separating underscore) and *switch_expression* likely is too subtle. So, how to differentiate them?

Although C calls such a switch expression a “controlling expression” (and uses that term as well for if, while, for, and do statements), we already state, “The governing type of a switch statement is established by the switch expression.”

As such, I have replaced all occurrences of “switch expression” (which exist only in the statements chapter) with “switch’s governing expression.”

Fixes #576.
Fixes #300.

@RexJaeschke RexJaeschke added the type: feature This issue describes a new feature label Aug 4, 2023
@RexJaeschke RexJaeschke added this to the C# 8.0 milestone Aug 4, 2023
@RexJaeschke RexJaeschke self-assigned this Aug 4, 2023
@RexJaeschke RexJaeschke marked this pull request as draft August 4, 2023 17:54
@BillWagner BillWagner force-pushed the add-new-pattern-kinds branch from 0d6dda3 to 237570c Compare September 25, 2023 14:15
@BillWagner
Copy link
Member

Successfully rebased on the updated draft-v8 branch on 09/25/2023

@gafter gafter self-assigned this Oct 4, 2023
@RexJaeschke RexJaeschke added the Review: pending Proposal is available for review label Oct 13, 2023
Copy link
Contributor

@Nigel-Ecma Nigel-Ecma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't managed to work my way through all of this yet but am submitting what I have so far. Overall this seems to have carried over too much non-Standardese from the original proposals and some issues repeating/rewording stuff already in the Standard.. There are also grammar issues that may break expression parsing…

Comment on lines +3622 to +3625
: switch_expression
| multiplicative_expression '*' switch_expression
| multiplicative_expression '/' switch_expression
| multiplicative_expression '%' switch_expression
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something has gone wrong here, see comment on line 3565 above

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be resolved if suggestions above are adopted.


### §positional-pattern-new-clause Positional pattern

A *positional_pattern* checks that the input value is not `null`, invokes an appropriate `Deconstruct` method ([§12.7](expressions.md#127-deconstruction)), and performs further pattern matching on the resulting values. It also supports a tuple-like pattern syntax (without the type being provided) when the type of the input value is the same as the type containing `Deconstruct`, or if the type of the input value is a tuple type, or if the type of the input value is `object` or `System.ITuple` and the runtime type of the expression implements `System.ITuple`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels like it is repeating stuff which is (or should be, e.g. System.ITuple is not in §12.7 at present) in §12.7 Deconstruction and this here should be something like:

A positional_pattern checks that the input value is not null, performs a deconstruction (§12.7), and performs further pattern matching on the resulting values.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure that ITuple should be mentioned in 12.7, as I think it's only used in positional patterns, not more general deconstruction. It's possible we should centralise its use within patterns though.

Copy link
Contributor

@jskeet jskeet Sep 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to say what the compiler invokes on ITuple? (And we need it to be in the relevant annex.) Or just "in order for the pattern to match, the number of subpatterns in the positional pattern must be the same as the number of items in the ITuple, and (something about conversions)"?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(My summary & thoughts post meeting)

The cases this covers:

  • if the input value is a tuple type: the number of subpatterns must statically match the tuple arity, subpatterns & tuple elements are matched pairwise, order not specified, match can fail without checking all subpatterns.
  • if the input value has a Deconstruct method whose number out parameters match the number of subpatterns, and the types of each parameter is compatible with the corresponding subpatterns matched pairwise then the values from invoking Deconstruct are matched pairwise, order not specified, match can fail without checking all subpatterns.
  • if the input value implements ITuple then the number of elements in the ITuple value is checked at runtime to be equal to the number of subpatterns. Then the elements of the ITuple value are matched pairwise… etc.

I’ve listed these cases in the probable order of priority, i.e. if the input value is a tuple the first case is picked even though tuples implement ITuple (if that is added to the Standard) and Deconstruct (if that is added to the Standard); next if the type of the input value implements a suitable Deconstruct then case 2 is chosen; finally, if applicable, case 3 with dynamic arity check is chosen, and a compile time error occurs if it is not applicable.

Is this ordering correct, note it does not match the current text?

Whatever the ordering it needs to be clearly specified.

@RexJaeschke RexJaeschke marked this pull request as ready for review January 11, 2024 14:08
@gafter gafter self-requested a review January 16, 2024 18:41
## 11.1 General

A ***pattern*** is a syntactic form that can be used with the `is` operator ([§12.12.12](expressions.md#121212-the-is-operator)) and in a *switch_statement* ([§13.8.3](statements.md#1383-the-switch-statement)) to express the shape of data against which incoming data is to be compared. A pattern is tested against the *expression* of a switch statement, or against a *relational_expression* that is on the left-hand side of an `is` operator, each of which is referred to as a ***pattern input value***.
A ***pattern*** is a syntactic form that can be used with the `is` operator ([§12.12.12](expressions.md#121212-the-is-operator)), in a *switch_statement* ([§13.8.3](statements.md#1383-the-switch-statement)), and in a *switch_expression* (§switch-expression-new-clause) to express the shape of data against which incoming data is to be compared. Patterns may be recursive, so that parts of the data may be matched against ***sub-patterns***. A pattern is tested against the *expression* of a switch statement, or against a *relational_expression* that is on the left-hand side of an `is` operator, each of which is referred to as a ***pattern input value***.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
A ***pattern*** is a syntactic form that can be used with the `is` operator ([§12.12.12](expressions.md#121212-the-is-operator)), in a *switch_statement* ([§13.8.3](statements.md#1383-the-switch-statement)), and in a *switch_expression* (§switch-expression-new-clause) to express the shape of data against which incoming data is to be compared. Patterns may be recursive, so that parts of the data may be matched against ***sub-patterns***. A pattern is tested against the *expression* of a switch statement, or against a *relational_expression* that is on the left-hand side of an `is` operator, each of which is referred to as a ***pattern input value***.
A ***pattern*** is a syntactic form that can be used with the `is` operator ([§12.12.12](expressions.md#121212-the-is-operator)), in a *switch_statement* ([§13.8.3](statements.md#1383-the-switch-statement)), and in a *switch_expression* (§switch-expression-new-clause) to express the shape of data against which incoming data is to be compared. Patterns may be recursive, so that parts of the data may be matched against ***sub-patterns***.
A pattern is tested against a value in a number of contexts:
- In a switch statement, the *pattern* of a case label is tested against the *expression* of the switch statement.
- In an *is-pattern* operator, the *pattern* on the right-hand-side is tested against the expression on the left.
- In a switch expression, the *pattern* of a *switch_expression_arm* is tested against the expression on the switch-expression's left-hand-side.
- In nested contexts, the *sub-pattern* is tested against values retrieved from properties, fields, or indexed from other input values, depending on the pattern form.
The value against which a pattern is tested is called the ***pattern input value*** for the pattern.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍’ed the above but a minor grammar question: should the first comma of each bullet be a semi-colon?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In nested contexts, the sub-pattern is tested against values retrieved from properties, fields, or indexed from other input values, depending on the pattern form.

When we get to C# 9 with parenthesized patterns and logical patterns, we might want to remember to come back if we don't generalize this statement now, since and takes two subpatterns that do not retrieve new values to test against.

@gafter gafter removed their assignment Dec 16, 2024
@gafter gafter added Review: complete at least one person has reviewed this and removed Review: pending Proposal is available for review labels Dec 16, 2024
gafter and others added 3 commits October 22, 2025 11:12
@gafter
Copy link
Member

gafter commented Oct 22, 2025

See also #1428

@gafter gafter self-requested a review October 22, 2025 20:10
Copy link
Member

@gafter gafter left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved, but with additional items to be addressed later.

@BillWagner
Copy link
Member

@gafter Is this now ready for me to merge? Or do you want to do more before we accept this?

@BillWagner BillWagner dismissed Nigel-Ecma’s stale review October 31, 2025 13:42

We agreed to merge with outstanding issues at our October meeting.

@BillWagner BillWagner merged commit c8a9923 into dotnet:draft-v8 Oct 31, 2025
6 checks passed
@BillWagner BillWagner deleted the add-new-pattern-kinds branch October 31, 2025 13:42
BillWagner added a commit to BillWagner/docs that referenced this pull request Oct 31, 2025
The ECMA committee merged dotnet/csharpstandard#873

Now, remove the feature spec from publication.
BillWagner added a commit to dotnet/docs that referenced this pull request Oct 31, 2025
* Unpublish C# 8 patterns

The ECMA committee merged dotnet/csharpstandard#873

Now, remove the feature spec from publication.

* Fix warnings

* Remove C# 8 feature spec references

It's all in the standard now.
Nigel-Ecma added a commit that referenced this pull request Nov 1, 2025
#873 changed `!x` to `!` on the second row of the table in §12.4.2; this PR reverts that change.

The `x` is required to indicate that this is the prefix logical negation operator, the first row of the table contains `x!` for the postfix null-forgiving operator
BillWagner pushed a commit that referenced this pull request Nov 3, 2025
#873 changed `!x` to `!` on the second row of the table in §12.4.2; this PR reverts that change.

The `x` is required to indicate that this is the prefix logical negation operator, the first row of the table contains `x!` for the postfix null-forgiving operator
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

meeting: discuss This issue should be discussed at the next TC49-TG2 meeting meeting: priority Review before meeting. Merge, merge with issues, or reject at the next TC49-TC2 meeting Review: complete at least one person has reviewed this type: feature This issue describes a new feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8.0 Tuple parentheses optional in switch expression and statement Change precedence of switch expression from relational to unary

6 participants