Skip to content

Latest commit

 

History

History
81 lines (55 loc) · 4.78 KB

LDM-2023-09-20.md

File metadata and controls

81 lines (55 loc) · 4.78 KB

C# Language Design Meeting for September 20th, 2023

Agenda

Quote of the Day

  • "I live in a double-wide future"

Discussion

Collection expressions

#5354
#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:

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:

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:

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.