# C# Language Design Meeting for June 19th, 2023

## Agenda

- [Prefer spans over interfaces in overload resolution](#prefer-spans-over-interfaces-in-overload-resolution)
- [Collection literals](#collection-literals)

## Quote(s) of the Day

- "What's the second question?" "Type inference for collection literals." "Oh that's easy"
- "Second2, also known as third"
- "Oh, I thought [redacted] was making a 'type inference walks into a bar' joke"

## Discussion

### Prefer spans over interfaces in overload resolution

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.

```cs
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:

* Do we want to mimic a subtype relationship between `IEnumerable<T>` and `Span<T>` as best we can without `Span<T>` actually implementing the
  interface?
* Is `Span<T>` special here? Are there other user-defined `ref struct`s 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 struct`s
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 struct`s 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 struct`s 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 struct`s can implement interfaces and then all of these issues will work themselves out with no additional rules needed.

#### Conclusion

No language change here.


### Collection literals

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:

* Collection initializers on arrays - Arrays are not target-typed today, so conversion-from-expressions do not carry through the collection
  initializer here. We think this actually makes array collection initializers a _poor_ analogy for where to draw behavior from.
* Switch expressions - These are target typed, and arm types can influence the type inference of a method.
* Tuple literals - These might be the best analogy. They are target typed, and have a general desired behavior of "you should be able to replace
  the tuple with the elements and have the same behavior". In other words, for a signature of `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)`.

#### Conclusion

Treat collection literals transparently for type inference.
