# C# Language Design Meeting for September 20th, 2023

## Agenda

- [Collection expressions](#collection-expressions)
    - [Type inference from spreads](#type-inference-from-spreads)
    - [Overload resolution fallbacks](#overload-resolution-fallbacks)

## Quote of the Day

- "I live in a double-wide future"

## Discussion

### Collection expressions

https://github.com/dotnet/csharplang/issues/5354  
https://github.com/dotnet/csharplang/issues/7542

#### Type inference from spreads

Our first question for today is around type inference from spread elements contributing to inferring the element type of a collection expression. This is how the working group spec'd the feature,
and how it was implemented, but we're confirming with LDM that the rules behave as we'd desire. We do have some concerns with the specificity needed for this feature: it seems like we have a similar
feature in tuples, and it would be great if there was a general rule about inferring element types from nested element expressions that subsumed all of these cases. We do generally think that behavior
specified is correct, however, and will proceed with spec'ing it as having a single set of rules that can affect all the scenarios like this in the language.

##### Conclusion

Behavior is approved. We will work on making a general spec rule that covers this, tuple cases, and other similar scenarios without having to make bespoke rules for each location.

#### Overload resolution fallbacks

Our second and final topic today relates to overload resolution in ambiguous scenarios with the ref struct preferencing rule. For example, this scenario is ambiguous today:

```cs
static void A(Span<object> value) { }
static void A(string[] value)     { }

A([string.Empty]); // error: ambiguous
```

There's some precedent for this type of behavior in the language, with how conversions are handled between an API like this:

```cs
static void B(IReadOnlyList<object> e) => Console.WriteLine("List<object>");
static void B(IEnumerable<string> e) => Console.WriteLine("IEnumerable<string>");

B(new List<string>() { "one", "two" });
```

For both of these scenarios, the LDM is in general agreement that we don't want the scenario to work. It's not immediately obvious to anyone, reader or compiler, which of these two overloads is the
better one to call. We also don't believe API patterns like this are realistic. However, there are similar scenarios where we _do_ need to have some sort of element convertibility rule in order to
have the right API chosen. As an example of this:

```cs
static void C(ReadOnlySpan<string> r);
static void C(object[] r);

C(["1", "2"]); // Ambiguous today, because object[] and ROS<string> cannot be converted between
```

In this scenario, the LDM believes it's perfectly reasonable for such an API pattern to arise as a library evolves. We also believe that it's unambiguous which one is the "better" API; the
`ReadOnlySpan<string>` can be allocation free, and the inner type is also more specific than the element type of `object[]`. However, since we cannot convert between `ReadOnlySpan<string>` and
`object[]`, the rules as written today would make this ambiguous. We have a few different rule proposals that might make this possible, given our previous rule of preferencing ref structs over
other types:

1. Require that there be an identity conversion between the iteration types. This would enable `string[]` vs `ReadOnlySpan<string>`, but would prevent `object[]` and `ReadOnlySpan<string>`.
2. Require that there be an implicit conversion from the ref struct's iteration type to the other type's iteration type. This would enable `object[]` vs `ReadOnlySpan<string>`, but not
   `string[]` vs `ReadOnlySpan<object>`, as there is no implicit conversion from `object` to `string`.
3. Always prefer the ref struct, no consideration of element type convertibility at all.
4. No special casing whatsoever.

After some deliberation, we settled on 2, feeling that it's a good match for user expectations of the feature. However, we then looked at how far we want to apply this rule. So far, all of the
examples we've looked at relate to `(ReadOnly)Span` vs one of the interfaces that arrays implement. We're concerned that applying the rule more broadly, particularly with so little time left in C#
12, will have some unintended consequences. We don't think we can categorically say that all ref structs should be preferred over non-ref struct counterparts, so we'll restrict the rule to just
being for `(ReadOnly)Span` vs arrays and all the interfaces arrays implement.

##### Conclusion

We will prefer `(ReadOnly)Span` over arrays and interfaces implemented by arrays in overload resolution for an argument with a collection expression conversion when there is an implicit conversion from the element type of the `Span` to the element type
of the array or array interface.
