Skip to content

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

Draft
Nigel-Ecma wants to merge 1 commit intodotnet:draft-v8from
Nigel-Ecma:tuples-2way
Draft

Update “tuple” handling – Alternative “2-way” version of #1560#1608
Nigel-Ecma wants to merge 1 commit intodotnet:draft-v8from
Nigel-Ecma:tuples-2way

Conversation

@Nigel-Ecma
Copy link
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
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.


#### §tuple-runtime-representation Runtime representation

> *Note*: Unlike other types such as arrays, the runtime representation of tuple types is specified in terms of a set of generic value types, and a tuple may be directly referenced in terms of this representation. However the runtime representation of these generic value types remains implementation defined. *end note*
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure what this note is conveying. We've said (normatively) that a tuple's runtime representation is System.ValueTuple<...>. What is "implementation defined" about that?

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
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.

> *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
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?

### 12.23.4 Compound assignment

If the left operand of a compound assignment is of the form `E.P` or `E[Ei]` where `E` has the compile-time type `dynamic`, then the assignment is dynamically bound ([§12.3.3](expressions.md#1233-dynamic-binding)). In this case, the compile-time type of the assignment expression is `dynamic`, and the resolution described below will take place at run-time based on the run-time type of `E`. If the left operand is of the form `E[Ei]` where at least one element of `Ei` has the compile-time type `dynamic`, and the compile-time type of `E` is not an array, the resulting indexer access is dynamically bound, but with limited compile-time checking ([§12.6.5](expressions.md#1265-compile-time-checking-of-dynamic-member-invocation)).
```ANTLR
Copy link
Member

Choose a reason for hiding this comment

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

Everything here is correct, but I'm wondering why this is shown as an addition. I'm curious where it was moved from?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tuple conversions are incorrectly specified

2 participants