Tuple conversion are incorrectly specified
Describe the bug
The compiler implements both a tuple type conversion, and a tuple literal conversion from expression. The spec currently only covers that latter conversion. As a concrete example of a feature that explicitly designed for and allowed, but the spec says is illegal:
short s = 1;
(int, int) t1 = (s, s); // As said in the spec, this is a tuple expression conversion
(short, short) t2 = (s, s);
(int, int) t3 = t2; // By the spec, there shouldn't be a conversion here, but the compiler allows a tuple type conversion from (short, short) to (int, int)
Specifically, https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/conversions.md#10213-implicit-tuple-conversions is the only conversions portion of the specification that touches tuples, and it covers tuple expressions. These are defined by https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#1286-tuple-expressions. In t3 = t2, t2 is not a tuple expression, but is instead a simple name: https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#1284-simple-names; since we're not dealing tuple expressions, implicit tuple conversions don't apply, and the spec says that no conversion should exist from t2 to t3.
Oops…
I suggest this error has crept in due to the melding of tuple construction and tuple conversion into a single operation – the implicit tuple conversion §10.2.13.
Possible fix:
- In §12.8.6 Tuple expressions define a tuple value in terms of
ValueTupleand not by referencing tuple conversion. - In §12.2.2 Values of expressions change the fourth bullet to reference §12.8.6 and not tuple conversion. Also while in this clause fix the issue that when the fourth bullet was added it wasn't included in the final sentence of the preceding paragraph which still only mentions three things.
- §10.2.13 Implicit tuple conversions can then start “An implicit conversion exists from an expression
Ewith a tuple typeSto a tuple typeTifShas the same arity asTand an implicit conversion exists from each element type inSto the corresponding element type inT.” TheValueTuplestuff then gets removed as it is now in §12.8.6.
A possible criticism of this approach is that it might define/imply (depending on wording) something like (var a, var b) = (42, 24); as creating a ValueTuple and then destroying it – but what if it does? Not doing so is a compiler optimisation surely? However if wished the Standard could make it clear that eliding temporary ValueTuples is valid, though I suspect eliding temporaries is always valid.
A possible criticism of this approach is that it might define/imply (depending on wording) something like
(var a, var b) = (42, 24);as creating aValueTupleand then destroying it – but what if it does?
FWIW, I've always viewed this operation as doing exactly that, and the compiler optimized to avoid creating the tuple. We use that exact wording when talking about expression-bodied constructors that do something like MyClass(int a, b) => (this.a, this.b) = (a, b);
Bill will have a look.
Reopened as it wasn't actually fixed.
This issue has revealed some more issues with the specification of tuples. They all need to be resolved for v8…