meetings/2023/LDM-2023-06-19.md
https://github.com/dotnet/csharplang/issues/7276
Today we looked at whether overload resolution should have special tie-breaking rules for the Span<T> vs IEnumerable<T> case. There are a
few cases that can this affect, and potentially blocks the runtime from adding overloads that take ReadOnlySpan<T> for fear of breaking users.
var ia1 = ImmutableArray.CreateRange<int>(new[] { 1, 2, 3 }); // error: CreateRange() is ambiguous
var ia2 = ImmutableArray.CreateRange<char>("Test"); // 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) { ... }
}
For both examples, these are types that are convertible to both a Span<T> and to IEnumerable<T>. There is no commonality between those two
types, other than object, so the overloads are ambiguous. There are two questions here:
IEnumerable<T> and Span<T> as best we can without Span<T> actually implementing the
interface?Span<T> special here? Are there other user-defined ref structs that can run into this same problem with ambiguous overloads?We're a bit concerned with chasing a subtype relationship here. Ideologically, Span<T> is indeed an IEnumerable<T>, but since ref structs
cannot implement interfaces today, the relationship isn't there. But that relationship plays in more than just overload resolution. Generics, for
example, which is another place ref structs cannot be used today. It feels like trying to mimic the behavior as best as possible would be a
large rabbit hole to go down, when we do want ref structs to be able to implement interfaces at some point. Implementing that would solve this
issue and in a more general fashion.
We also talked a bit about workaround the runtime could do for these cases. Defining more overloads is an obvious answer for some scenarios, such
as the ImmutableArray case above. That could define CreateRange<T>(T[] array) and CreateRange(string s); indeed, this is the solution that
DefaultInterpolatedStringHandler used to avoid ambiguities in AppendFormatted. But that doesn't work in every case. For example, constructors
of generic types cannot have specialized versions for a specific generic. In other words, HashSet<char> (and only HashSet<char>) cannot have
a constructor that takes a string to resolve that ambiguity. Extensions might be able to solve that, but it doesn't seem like it would give the
actual end experience we want, namely the subtype relationship between Span<T> and IEnumerable<T>. Given this, we plan on waiting until
ref structs can implement interfaces and then all of these issues will work themselves out with no additional rules needed.
No language change here.
https://github.com/dotnet/csharplang/issues/5354
https://github.com/dotnet/csharplang/pull/7284
We also looked at the proposed type inference for collection literals. We explored our existing behavior here around several areas:
M<T>((T, T) tuple), passing a tuple literal
should have the same outcome as passing each element individually to a signature of M<T>(T t1, T t2).We like the tuple analogy for collection literals. The behavior we want is the equivalent, spelled out as:
If there is a signature M<T>(T[] array), passing a collection literal of [1, 2] should have the same effect as if the signature was
M<T>(T t1, T t2).
Treat collection literals transparently for type inference.