# C# Language Design Meeting for February 26th, 2024

## Agenda

- [`ref struct`s in generics](#ref-structs-in-generics)
- [Collection expressions](#collection-expressions)

## Quote(s) of the Day

- "Sorry, I had a meeting run over" "Unacceptable" "Yeah, that never happens here"
- "Do it British style. allouws"

## Discussion

### `ref struct`s in generics

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

We started today with a timeboxed conversation to allow the implementation work on `ref struct` in generics to proceed with parsing work, and hopefully not need to
redo much work later when the LDM makes more complete decisions. We looked at a couple different syntax proposals for how the anti-constraint of allowing `ref struct`s
could work:

* `where T : allows ref struct` - Put a new keyword inside the existing `where` clause.
* `allows T : ref struct` - Add a new clause that can go beside a `where` clause.

The crux of which form to choose comes down to the question of what we think the existing form actually means: is it _only_ narrowing information about `T`, or is more
generally "information about `T`". If we think it's the former, then a separate clause for widening `T` would be appropriate; if the latter, then we can include widening
information in the `allows`. After some quick debate, we lean very heavily towards the latter view. We also think that it will simplify reading and understanding method
definitions, as the user won't have to keep track of multiple sets of information about `T`.

We then looked at what keyword to use. We have two real options that were suggested: either `allow`, or `allows`. This choice was much murkier: does the constraint section
reflect the author saying "I allow T to be a ref struct", or is it reflecting the user reading a constraint and saying "This allows T to be a ref struct". We have a slight
lean towards the latter, but ran out of time in the box before coming to a definitive solution. The major parsing choice is the constraint form, while the keyword choice
is fairly minor and easily changed later before shipping, so we called it in favor of `allows` for now, and will revisit later.

#### Conclusion

We will go with the "put the anti-constraint in the `where` clause" form, and we will use the keyword `allows` for now.

### Collection expressions

https://github.com/dotnet/roslyn/issues/72098  
https://github.com/dotnet/csharplang/issues/5354

Finally, we looked at some of the consequences of our [previous decision](LDM-2024-01-10.md#collection-expressions-conversion-vs-construction) with how the impact a few
real-world types in WinForms and WPF. These are very old collection types; their design predates many of our modern collection designs. Specifically, they implement
`System.Collections.IList`, and not any generic version. However, they try to add a bit of type safety by hiding `IList.Add(object)`, and instead expose strongly-typed
`Add` methods that take the specific type they care about. For example, `ListView.ColumnHeaderCollection` exposes an `Add(ColumnHeader)` method. However, because it
does not implement `IList<T>`, its iteration type is `object`; this means, for the purposes of determining whether a collection expression conversion exists, we look
specifically for the `Add(object)` method and don't find it. We have a few ideas to address this:

1. Remove the restriction we added previously for `IEnumerable` cases for non-`params` scenarios.
2. Do nothing and leave the rules as they are. These collections are:
   1. Not modified, and collection expressions simply do not work for them.
   2. Updated to include `IEnumerable<T>` implementations, giving them a more specific iteration type.
3. Allow looking for `Add` methods that take a subtype of the iteration type.
4. Use the strongly-typed indexer of such types to narrow the iteration type.
5. Include explicit `Add` implementations in the search for the `Add(object)` method for the purposes of conversion.

Complicating this discussion is a further wrinkle: these collections often have other `Add` methods that take other types that can effectively be converted to the
effective iteration type of the collection. For example, `ListView.ColumnHeaderCollection` has an `Add(string)` method that constructs a `ColumnHeader` out of the `string`
for ease of use. Some of the solutions we're looking at here wouldn't solve that scenario: 1 would work, as would 3, and possibly 5, but 4 would not. 2 likely would, but
we also think it extremely unlikely that we'd be able to do 2.2, specifically adding an implicit conversion from `string` to `CollectionHeader`. Conceptually, there is
an implicit conversion there; that's what the various `Add` methods are simulating. But adding a new implicit user-defined conversion to a type is always a risk, particularly
a type that has been around as long as some of these WinForms and WPF types, and we think it's extremely unlikely that 2.2 would ever happen because of this.

One important note for all of these options around loosening the requirements is that this would be a divergence from what we want to work for `params` scenarios; because
we're very concerned about the effect of nested overload resolution on looking for unbounded `Add` methods on determining the applicability of expanded `params` methods,
we don't have any plans to adjust the behavior for that feature. It will be an unfortunate difference in behavior, but we think it's a necessary one to avoid making overload
resolution even more complex than it already is. This means that if we did option 1 or 3, there would be a difference in behavior. 4 or 5 may be able to apply to `params`, but
we'd need to consider it before confirming that it will.

We did not reach any conclusions on this topic today. We want the working group to more fully explore these options and come back with examples of where they will work, where
they will fall over, and what the impact would be to various scenarios.
