Skip to content

Update “tuple” handling – Alternative “2-way” version of #1560#1608

Merged
jskeet merged 3 commits intodotnet:draft-v8from
Nigel-Ecma:tuples-2way
Apr 8, 2026
Merged

Update “tuple” handling – Alternative “2-way” version of #1560#1608
jskeet merged 3 commits intodotnet:draft-v8from
Nigel-Ecma:tuples-2way

Conversation

@Nigel-Ecma
Copy link
Copy Markdown
Contributor

Fixes: #1155
Replaces: PR #1366, #1538, #1570
Alternative to: #1560

This replaces #PR1560’s 3-way division of tuples & deconstruction; into tuple_literal, deconstructing_assignment and local_deconstructing_declaration; with a 2-way division; into tuple_literal and deconstructing_assignment.

The distinction between a deconstruction assigning to existing vs. new variables is not syntactic in this version. Instead a textual list of restrictions is specified which; by governing what a deconstructor can contain based on its position within the code; distinguish the two cases.

Other changes since PR1560 include:

  • Renamed abridged_* rules per suggestion.
  • Inclusion of declaration_expression as an option in a for_initializer (this was an accidental omission in PR1560).
  • Other editorial changes.

This topic is not yet complete: The deconstructing foreach stattement, which was missing in v7, is not yet included. This will either be added in an update to this PR, or as a new PR building on this one. For this reason this PR is marked as draft.

This replaces the 3-way division of tuples & deconstructing; into *tuple_literal*, *deconstructing_assignment* and *local_deconstructing_declaration*; with a 2-way division; into *tuple_literal* and *deconstructing_assignment*.

The distinction between deconstruction assigning to *existing* vs. *new* variables is not syntactical in this version, even though C# distinguishes the two cases. Instead a textual list of restrictions is provided which govern what a *deconstructor* can contain based on its position within the code.

Other changes since dotnet#1560 include:

- Renamed abridged_* rules per suggestion.
- Inclusion of *declaration_expression* as an option in a *for_initializer* (this was an accidental omission in dotnet#1560).

This topic is not yet complete: deconstructing foreach, which was missing in v7, is not yet included. This will either be added in an update to this PR, or as a new PR building on this one.
@Nigel-Ecma Nigel-Ecma added this to the C# 8.0 milestone Mar 16, 2026
@Nigel-Ecma Nigel-Ecma self-assigned this Mar 16, 2026
Copy link
Copy Markdown
Member

@BillWagner BillWagner left a comment

Choose a reason for hiding this comment

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

This is great @Nigel-Ecma

I had a few small questions / nits in text to consider. One may even be just the result of my failing eyesight.

Comment on lines +1274 to +1284
The result of the deconstruction is the *tuple-literal* formed from the values returned via the out parameters of a call to `E.Deconstruct(...)`. The result is semantically equivalent to replacing `E` with the following pseudo-code:

>```csharp
> E.Deconstruct(out T1 v1, ..., out TN vn) andThen (v1, ..., vn);
>```

Where `andThen` is a pseudo C# operation which performs its left-hand operand and then returns its right-operand as the result.
<!-- markdownlint-disable MD028 -->

<!-- markdownlint-enable MD028 -->
>> *Note*: `andThen` is the equivalent of C & C++’s comma operator. *end note*
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we want all these lines to be indented, as they are part of the bullet item on 1272. (I apologize if they already are, that's just hard are to see.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Correct the bullet on 1272 is a multi-paragraph one and also contains one code and one note “display”. They mostly look correctly indented to me, the exception being the Note on 1284 which GitHub is showing with the wrong indentation and two left side bars. My editor shows the Note correctly indented (i.e. indented the same as the code E.Deconstruct…), but to achieve that I had to insert a double >> which seems logically wrong… But then what is logical about markdown markup… 😉

I think we need a GitHub markdown whisperer on this one, @RexJaeschke or @jskeet can either of you suggest the required incantation?

> *Note*: ANTLR grammar semantics enforce this requirement due to the ordering of the alternatives. *Semantically* there is no overlap between the four alternatives, this is a syntactic disambiguation.

The `+=` and `-=` operators with an event access expression as the left operand are called the ***event assignment operator***s. No other assignment operator is valid with an event access as the left operand. The event assignment operators are described in [§12.23.5](expressions.md#12235-event-assignment).
The *simple_assignment* and *compound_assignment* expressions assign a new value to a variable, a property, or an indexer element. Event assignment ([§12.23.5](expressions.md#12235-event-assignment)), a subset of *compound_assignment*, assigns a new value to an event. The *ref_assignment* expression assigns a variable reference ([§9.5](variables.md#95-variable-references)) to a reference variable ([§9.7](variables.md#97-reference-variables-and-returns)). The *deconstructing_assignment* assigns new values to one or more *variable_reference*s.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: Would deconstructing_assignment require "two or more variable_references?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No, but we’re both wrong! There are two errors in the line:

  • It is zero or more – a deconstructor can contain only discards (_) (and nested deconstructors with only discards…). Thinking two as you did makes sense, tuples must have at least two elements, why I wrote one for the 3-way I cannot remember offhand but I’m “sure” there was a good reason that applies in the 3-way case… 😉
  • The line hasn’t been changed from the 3-way where it does indeed assign “new values” to existing variables (or variable_references). However in the 2-way it can also declare new variables and assign them initial values…

The next commit will contain:

The simple_assignment and compound_assignment expressions assign a new value to a variable, a property, or an indexer element. Event assignment (§12.23.5), a subset of compound_assignment, assigns a new value to an event. The ref_assignment expression assigns a variable reference (§9.5) to a reference variable (§9.7). The deconstructing_assignment assigns values to zero or more variable_references.

This is only an overview para so I skipped the distinction between existing and newly declared variables, that is already covered in the full text.

> *Note*: An expression of type `dynamic` cannot be deconstructed. *end note*
- Otherwise if there is a unique instance or extension method `S.Deconstruct`; with `n ≥ 2` output parameters, with types `T₁` to `Tₙ`, and no other parameters; then `E` can be deconstructed.

The result of the deconstruction is the *tuple-literal* formed from the values returned via the out parameters of a call to `E.Deconstruct(...)`. The result is semantically equivalent to replacing `E` with the following pseudo-code:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

As per email, I think this is where it would be good to re-emphasize in the first sentence that this is effectively a source-to-source transform. That would mitigate my concerns around using tuple-literal.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The clause has been edited in the next commit, the changes are not large but hopefully sufficient.

@jskeet jskeet added the meeting: priority Review before meeting. Merge, merge with issues, or reject at the next TC49-TC2 meeting label Apr 1, 2026
Copy link
Copy Markdown
Contributor

@jskeet jskeet left a comment

Choose a reason for hiding this comment

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

Various comments and a few typo fixes (which I'm sure Nigel only left in so that I could feel useful) but I hope we're able to merge this in our meeting, creating follow-up issues for minor formatting and examples.

<!-- markdownlint-disable MD028 -->

<!-- markdownlint-enable MD028 -->
>> *Note*: `andThen` is equivalent to C & C++’s comma operator. *end note*
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This still looks weird in GitHub, but I'd propose that we don't block this PR on it. (We can file a new issue and fix it separately. We'll need to see what it looks like in Word too.)

Copy link
Copy Markdown
Contributor

@jskeet jskeet Apr 8, 2026

Choose a reason for hiding this comment

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

I'll file an issue tomorrow.

- **is not** at the start of a statement, or
- **is** at the start of a statement and contains no *declaration_expression*

For these restrictions:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we should have a follow-up PR with examples of these restrictions. We can file an issue for that before merging this PR.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'll file an issue in the morning.

Co-authored-by: Jon Skeet <skeet@pobox.com>
Copy link
Copy Markdown
Contributor

@jskeet jskeet left a comment

Choose a reason for hiding this comment

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

After discussion, we'll make this ready to review, and merge it. We'll file follow-up issues for:

  • deconstructing foreach
  • assignment (simple or deconstructing) to discards - search for "zero or more"
  • examples of deconstructing assignment restrictions
  • markdown handling of nested notes

@jskeet jskeet marked this pull request as ready for review April 8, 2026 19:31
@jskeet
Copy link
Copy Markdown
Contributor

jskeet commented Apr 8, 2026

Merging despite the failing test, which is about our tools rather than the standard.

Copy link
Copy Markdown
Member

@BillWagner BillWagner left a comment

Choose a reason for hiding this comment

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

This LGTM.

@jskeet jskeet merged commit 7f2b274 into dotnet:draft-v8 Apr 8, 2026
6 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

meeting: priority Review before meeting. Merge, merge with issues, or reject at the next TC49-TC2 meeting

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tuple conversions are incorrectly specified

3 participants