Skip to content

Type inference does not conform to collection expressions spec #80142

@jnm2

Description

@jnm2

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 arity N and elements Eᵢ, and T is a tuple type with arity N with corresponding element types Tₑ or T is a nullable value type T0? and T0 is a tuple type with arity N that has a corresponding element type Tₑ, then for each Eᵢ, an input type inference is made from Eᵢ to Tₑ.

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 type T in the following way:

  • If E is a collection expression with elements Eᵢ, and T is a type with an element type Tₑ or T is a nullable value type T0? and T0 has an element type Tₑ, then for each Eᵢ:
    • If Eᵢ is an expression element, then an input type inference is made from Eᵢ to Tₑ.
    • If Eᵢ is a spread element with an iteration type Sᵢ, then a lower-bound inference is made from Sᵢ to Tₑ.
  • [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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions