Skip to content

Latest commit

 

History

History
74 lines (54 loc) · 5.85 KB

LDM-2024-02-26.md

File metadata and controls

74 lines (54 loc) · 5.85 KB

C# Language Design Meeting for February 26th, 2024

Agenda

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 structs in generics

#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 structs 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

dotnet/roslyn#72098
#5354

Finally, we looked at some of the consequences of our previous decision 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.