-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Prefer spans over interfaces in overload resolution
- Proposed
- Prototype: Not Started
- Implementation: Not Started
- Specification: Not Started
Summary
Overload resolution should prefer an overload where the corresponding parameter has a span type over an overload where the parameter has a collection interface type.
Motivation
For APIs that take a collection of items, it may be useful to provide overloads for collection interfaces and for spans. Supporting collection interfaces such as IEnumerable<T> is particularly useful for callers using LINQ; supporting spans such as ReadOnlySpan<T> is useful for callers focused on performance.
However, arrays are implicitly convertible to collection interfaces and spans, so calls with array arguments may be considered ambiguous with the current overload resolution rules.
var ia = ImmutableArray.CreateRange<int>(new[] { 1, 2, 3 }); // error: CreateRange() is ambiguous
public static class ImmutableArray
{
public static ImmutableArray<T> CreateRange<T>(IEnumerable<T> items) { ... }
public static ImmutableArray<T> CreateRange<T>(ReadOnlySpan<T> items) { ... }
}In cases such as arrays where both overloads are applicable, overload resolution should prefer the span overload.
Detailed design
The overload resolution rules for 11.6.4.6 better conversion target could be updated with an additional rule:
Given two types
T₁andT₂,T₁is a better conversion target thanT₂if one of the following holds:
- ...
T₁isReadOnlySpan<S₁>orSpan<S₁>, andT₂isIEnumerable<S₂>orICollection<S₂>orIList<S₂>orIReadOnlyCollection<S₂>orIReadOnlyList<S₂>, and an implicit conversion exists fromS₂toS₁
Drawbacks
-
Preferring
ReadOnlySpan<T>overIEnumerable<T>in overload resolution requires choosing a user-defined conversion over an implicit reference conversion. -
For generic overloads such as
ImmutableArray.CreateRange<T>()above, an array argument is only ambiguous when the generic type argument is explicit. By contrast, when the generic type argument is inferred, method type inference is required which will ignore conversions and fail to infer the type argument forReadOnlySpan<T>.var ia = ImmutableArray.CreateRange<int>(new[] { 1, 2, 3 }); // C#11: ambiguous, C#12: ReadOnlySpan<int> var ib = ImmutableArray.CreateRange(new[] { 1, 2, 3 }); // C#11/C#12: IEnumerable<int>
-
Overloads for collection interfaces and spans are still ambiguous for arrays when compiled with older compilers.
Mixing older compilers and newer TFMs is not strictly a supported scenario but it is used. Perhaps we could add a custom modifier to new overloads, so those overloads are ignored by older C# and VB compilers. What is the behavior from F#?
Alternatives
-
Use distinct method names rather than overloads.
This alternative is not ideal for the BCL which already uses overloads for scenarios such as collection construction where a
ReadOnlySpan<T>overload might be useful. And using distinct names will not work for constructors. -
Use an extension method to overload an instance method. The extension method will only be considered when the instance method is inapplicable which avoids ambiguities when both are applicable.
This alternative prevents the compiler from choosing the better overload, however. And using extension methods will not work for constructors.
Unresolved questions
- Should the updated behavior apply to array arguments only rather than all expressions that are implicitly convertible to both collection interfaces and spans?