# C# Language Design Meeting for May 13th, 2024

## Agenda

- First-class span types questions
    - Delegate conversions
    - Variant conversion existence
- Overload resolution priority questions
    - Attribute shape and inheritance
    - Extension overload resolution

## Quote(s) of the Day

- "That's my interpretation, and you can't check my math anyways."
- "[Who's on first](https://www.youtube.com/watch?v=sShMA85pv8M) should be on the citizenship test"

## Discussion

### First-class span types questions

https://github.com/dotnet/csharplang/issues/7905

#### Delegate conversions

https://github.com/dotnet/csharplang/blob/4e88fd3f9cb305b467bdc9c4f54f691733093cf5/proposals/first-class-span-types.md#delegate-signature-matching

First up, we considered whether we should try and replicate covariance in method group assignment conversions. On the one hand, the example code seems sensible enough, and would certainly work today
with arrays. However, we don't have precedent  for emitting thunks for delegate assignments. Further, while we could make the initial conversion work easily enough, we couldn't then make converting from
`Func<ReadOnlySpan<string>>` to `Func<ReadOnlySpan<object>>` transparent, like converting from `Func<string[]>` to `Func<object[]>` is seamless and preserves reference identity. Looking at the BCL use cases
for this, we don't see anything that is planning to take advantage of this type of method-group variance; given these caveats, we have decided to not support variance in method-group assignment here.

##### Conclusion

Variance in delegate assignment is not supported.

#### Variant conversion existence

We next considered whether we should support covariance in `ReadOnlySpan<T>` when the helper method, `ReadOnlySpan<T>.CastUp<TDerived>`, is not present. Supporting this down-level could be potentially quite
complicated. We considered a few different approaches:

1. We could consider the conversion to only exist when `CastUp<TDerived>` exists. This ensures that if anyone depends on the behavior, it must be present, but it causes overload resolution to be unstable
   within a single language version, and increases the decoder ring of what a user would need to know to understand why a particular overload was or wasn't picked. It avoids breaking changes when a user
   turns on C# 13 and doesn't upgrade to .NET 9, but the complexity tradeoff seems like it will not be worth it.
2. We could consider the conversion to always exist, but issue an error when we cannot find the required `CastUp<TDerived>` method. This is fairly in-line with what we do for other language features that
   require specific features, but we're concerned about the breaking change potential here. When a user upgrades to C# 13, these new conversions may cause overload resolution to pick a new member. That
   resolution could then cause an error because `CastUp<TDerived>` doesn't exist.
3. We could generate a `<PrivateImplementationDetails>` method for converting the span. On platforms with built-in `ReadOnlySpan<T>` support, and in particular .NET Core 2.2 and later, this seems like it would
   work fine. However, we're concerned about other platforms; users can bring their own `ReadOnlySpan<T>` implementations, and there's no guarantee that whatever bit-blit strategy the compiler choses would be
   safe for that implementation. We also aren't prepared to make strong statements on whether this would always be safe on the .NET Framework implementation of `ReadOnlySpan<T>`.
4. We thought about whether we could ship an update to `System.Memory`. However, this would be quite complicated; we'd actually need to ship a new package, perhaps `System.MemoryEx` or `System.Memory2`,
   otherwise code that compiled against .NET Standard 2.0 and depended on the new functionality might break when run on .NET 8.
5. We considered whether we should abandon variance entirely. After all, the main thrust of the proposal is not variance; that's a "nice to have" on top of the type inference and extension method features. However,
   we think it would be a shame to avoid a useful language feature because we may issue an error down-level; especially given that the feature is a corner case scenario and users that want to use newer C# features
   on down-level platforms have to contend with far more obvious missing things.

Given all these options, we eventually settled on 2: when in C# 13+, the conversion will always exist, but the compiler will issue an error when it cannot find `CastUp<TDerived>`.

Finally, we thought about whether extensions should be able to provide `ReadOnlySpan<T>.CastUp<TDerived>`. That method is a static method on `ReadOnlySpan<T>`, so it could theoretically be provided by extension
types. However, we don't think we're ready to decide, in case or in general. We'll have to consider extension types more generally with compiler pattern matching; for today, we'll simply say it cannot be provided
by an extension type, and revisit when we're ready to consider the question holistically across C#.

##### Conclusion

In C# 13, the variance conversion will always exist, and the compiler will issue an error if it cannot find `ReadOnlySpan<T>.CastUp<TDerived>`.

### Overload resolution priority questions

https://github.com/dotnet/csharplang/issues/7706

#### Attribute shape and inheritance

https://github.com/dotnet/csharplang/blob/4e88fd3f9cb305b467bdc9c4f54f691733093cf5/proposals/overload-resolution-priority.md#systemruntimecompilerservicesoverloadresolutionpriorityattribute

First up in overload resolution priority, we considered the shape of the attribute. As part of this, we need to consider the inheritance behavior, to at least some degree, in order to decide what the `Inherited`
attribute usage property should be. For this, we looked to prior art in C#: method types, `params`, parameter names, and default values. Unfortunately, there's no singular consistent rule here, but we do have
the principles to guide us; most things look at the least-derived members. For parameter names and default values, however, we look at the most-derived, which keeps us consistent with the semantics VB already
had for these scenarios. We don't think that there's reason to use that most-derived logic here; if anything, we think that would run counter to the proposal itself, which is designed to only be used within a
single type. Allowing overrides to change the priority of a member would defeat this; derived types can always hide the member and create their own version if they wish to adjust priority, as they can today.
Given this, `Inherited` should be false, as we don't want it to show up in reflection on overriding members when it's not permitted in source.

##### Conclusion

We are satisfied with the proposed shape of the attribute. Overload resolution will always look at the least-derived member definition for its priority. We still have open questions on whether we should hard-block
the attribute application on overrides, and what we will do for implementations of interface members that have a priority.

#### Extension overload resolution

https://github.com/dotnet/csharplang/blob/4e88fd3f9cb305b467bdc9c4f54f691733093cf5/proposals/overload-resolution-priority.md#overload-resolution-priority-1

Finally today, we considered the question of whether overload resolution should behave identically for instance and extension lookups, or if we should not group extension methods by type before doing overload
resolution priority sorting. We don't have any current use-cases for inter-extension priority, and we also think that not grouping would again run against the "this is a tool within a type only" principle from
the proposal.

##### Conclusion

We will always group by declaring type before removing lower priority overloads. The given example will print `"Ext2 ReadOnlySpan"`.
