-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Description
Version Used: 17.14.13 Preview 1.0
Demonstration
The following code fails to compile. It should infer <byte>
for both methods (https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/collection-expressions.md#type-inference, explained below):
ReadOnlySpan<byte> a = [5];
// The type arguments for method 'ReadOnlySpans<T>(ReadOnlySpan<T>, ReadOnlySpan<T>)' cannot be inferred
// from the usage. Try specifying the type arguments explicitly.
// ↓
ReadOnlySpans(a, [5]);
void ReadOnlySpans<T>(ReadOnlySpan<T> a, ReadOnlySpan<T> b) { }
Same thing with an alternate diagnostic location for some reason:
Span<byte> a = [5];
/* The type arguments for method 'Spans<T>(Span<T>, Span<T>)' cannot be inferred from the usage. Try specifying
the type arguments explicitly.
↓↓↓↓↓ */
Spans(a, [5]);
void Spans<T>(Span<T> a, Span<T> b) { }
Spec explanation
Collection expression type inference was modeled after tuple type inference. Tuple type inference correctly infers the (5, 5)
inside the method call to be a tuple of bytes, not ints:
(byte, byte) a = (5, 5);
Tuples(a, (5, 5));
void Tuples<T>((T, T) a, (T, T) b) { }
The spec was recently updated in draft-v8 to appropriately capture this behavior (https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#12637-input-type-inferences):
- If
E
is a tuple expression (§12.8.6) with arityN
and elementsEᵢ
, andT
is a tuple type with arityN
with corresponding element typesTₑ
orT
is a nullable value typeT0?
andT0
is a tuple type with arityN
that has a corresponding element typeTₑ
, then for eachEᵢ
, an input type inference is made fromEᵢ
toTₑ
.
Collection expressions introduces parallel wording, and should gain the parallel inference behavior (https://github.com/dotnet/csharplang/blob/main/proposals/csharp-12.0/collection-expressions.md#type-inference). A new rule was added at the top of the list of input type inference rules:
An input type inference is made from an expression
E
to a typeT
in the following way:
- If
E
is a collection expression with elementsEᵢ
, andT
is a type with an element typeTₑ
orT
is a nullable value typeT0?
andT0
has an element typeTₑ
, then for eachEᵢ
:
- If
Eᵢ
is an expression element, then an input type inference is made fromEᵢ
toTₑ
.- If
Eᵢ
is a spread element with an iteration typeSᵢ
, then a lower-bound inference is made fromSᵢ
toTₑ
.- [existing rules from first phase] ...
Thus, an input type inference should be made from 5
directly to the type parameter in the collection expression case, exactly as is done in the tuple expression case. For the purposes of inferring the type argument, int
should not come into play any more than it does in the tuple case.