# C# Language Design Meeting for December 4th, 2024

## Agenda

- [First-class span open questions](#first-class-span-open-questions)
    - [Preferring `ReadOnlySpan<T>` over `Span<T>` conversions](#preferring-readonlyspant-over-spant-conversions)
    - [Conversions in expression trees](#conversions-in-expression-trees)

## Quote of the Day

- "Anything else for you?"

## Discussion

### First-class span open questions

Champion issue: https://github.com/dotnet/csharplang/issues/8714  

#### Preferring `ReadOnlySpan<T>` over `Span<T>` conversions

Spec: https://github.com/dotnet/csharplang/blob/1754aaafffe270c19d8578af30a2eb768de3ee49/proposals/first-class-span-types.md#prefer-converting-from-t-to-readonlyspant-over-spant-in-overload-resolution

The first issue we looked at today was around how array covariance can impact conversions to `Span<T>`. Because .NET has array
covariance, the conversion from an array to a `Span<T>` has to check to make sure that the array is a `T[]`, not some subtype
of `T[]`; if this is the case, then the conversion will throw. This is a footgun for usages of this implicit conversion, and
one that was hit immediately on upgrade to C# 14 preview by Roslyn because of the triplet of `IEnumerable<T>`, `Span<T>`, and
`ReadOnlySpan<T>` in test assert APIs. It is fairly common for some expected data to be an array of some specific subtype, while
the actual data being converted is a supertype. Previously this would have gone through `IEnumerable<T>`, which was perfectly
fine with covariant arrays. `Span<T>` is now the preferred overload, and that isn't fine with covariant arrays.

This issue, where an implicit conversion can throw, isn't entirely new to the language; after all, `dynamic` can be implicitly
converted to any type, and that can throw. But it is likely more prominent than `dynamic`, as with that feature, the user usually
intentionally started at `dynamic` and went to a specific type, rather than starting at array and then invisibly going to a `Span<T>`
where `IEnumerable<T>` used to be fine. While we don't think that we have reason to entirely remove the array->`Span` conversion
from the feature, we do think that this merits some work to make sure that when there is a safer conversion available that likely
has the same intended meaning, we prefer that conversion instead.

The proposal here is to make `ReadOnlySpan<T>` preferred over `Span<T>` for first-class span. From an immediate type theory
perspective, this is an exception how most-specific type normally works. Since `Span<T>` can be converted to `ReadOnlySpan<T>`,
but not the other way around, our standard most-specific type logic would indicate that `Span<T>` should be preferrable. However,
for these types of APIs that overloaded across `Span<T>` and `ReadOnlySpan<T>`, we think that the only reason they're overloaded
is because we currently _don't_ have first-class spans as a language feature. Both overloads were added to make the API usable,
but the only thing the API needs is read access, and the `Span<T>` API usually just forwards to the `ReadOnlySpan<T>` version.
The only time this reasoning doesn't hold up is when we consider extension methods across multiple different types; such overloads
could indeed have different behavior, where one overload really does need mutable access, while the other only reads from the array.
Despite this, we think the overall feature is better served by preferring `ReadOnlySpan<T>` over `Span<T>`.

We also considered the question of whether we should take a bigger step. Our proposed rules only impact when arrays are converted
to span types. This is yet another element to a decoder ring, another special case that users need to know to understand how
overload resolution will be applied to their APIs. Could we instead make a broader, simpler rule, where we just always prefer
`ReadOnlySpan<T>` over `Span<T>`? We don't think we have enough data to make such a decision today. We don't know of any APIs that
this would negatively affect, but more investigation needs to be done before making a call.

Finally on this topic, we considered how restrictive to make the preference on type parameters. It seems a little suspicious to
us to prefer `ReadOnlySpan<object>` over `Span<string>`; can we really make the assertions we said earlier about the expected behavior
of the API for this type of shape? We'll investigate this as well, and see whether there are examples of this pattern in the wild.

##### Conclusion

We adopt the rule preferring `ReadOnlySpan<T>` over `Span<T>` for array conversions. We will investigate broader rules as well, and
their potential impact on the BCL.

#### Conversions in expression trees

Spec: https://github.com/dotnet/csharplang/blob/1754aaafffe270c19d8578af30a2eb768de3ee49/proposals/first-class-span-types.md#ignore-span-conversions-in-expression-trees

Our last topic of the day was considering whether we should try and handle the conversion specially in expression trees, as this
can be a breaking change. We had a
[similar discussion](https://github.com/dotnet/csharplang/blob/1754aaafffe270c19d8578af30a2eb768de3ee49/meetings/2024/LDM-2024-06-17.md#params-span-breaks)
with `params ReadOnlySpan`, and we continue to find our reasoning from that issue compelling. Therefore, we will not make any
changes to how binding occurs in expression trees; they will potentially pick up new span-based overloads and users may have to
insert explicit casts to get old behavior back.

##### Conclusion

No special cases for expression trees.
