- "So we're finally going to have the highlander betterness rule?"
- "Let them live in fear if they're going to do something like this"
#2691
https://github.com/dotnet/csharplang/blob/960ffda90c05c212870d509a45651197a6cd1791/proposals/csharp-12.0/primary-constructors.md#lookup-order-for-type-parameters
First up today we looked at an issue with lookup order of type parameters, and how our existing record
s work conflicts with the rules we decided on for non-record
primary constructors.
We have 4 options for addressing this discrepancy:
- Adjust the rules to match the behavior of the implementation (which is the same between
record
and non-record
types). - Adjust the behavior to match the rules in all cases (a possible breaking change for
record
s). - Disallow a primary constructor parameter to use a type parameter's name (a possible breaking change for
record
s). - Do nothing, accept the inconsistency between the spec and implementation.
We quickly determined that we don't think 2 or 4 are the correct answers, and instead felt divided between 1 and 3. Importantly, the LDM feels that this scenario is a pathological case, and not representative of C# code outside the compiler's test suite, so we don't feel the cost of a breaking change is hard. However, because this case is so pathological, we also are unsure whether it's worth spending the effort that taking a breaking change would require: updating the compiler error detection code, documenting the breaking change, and dealing any fallout from the change, and all this within the next month when C# 12 releases. We ultimately think that the best choice we can make here is to simply update the spec to match the behavior of the compiler, and revisit in the future if we have sufficient motivation from real world scenarios.
Option 1, update the primary constructors spec to match the existing compiler behavior and align with record
s.
For our second and last issue today, we looked at defining what behavior the compiler can assume from a collection type. This is important for allowing the compiler room to change emit
of collection expressions for performance, and there's a tension here between what could be reasonably observed by a user program vs what should be able t reordered. One thing we think is
iron-clad is that anything the user has explicitly written needs to be evaluated in traditional C# fashion. It is reasonable that [i++, i++, i++]
would result in a collection of
[0, 1, 2]
with i == 3
afterwards, and not any other order. However, enumeration of nested collections is not an explicit user action, and we want to able to control the location that
occurs in; enumeration should be able to occur before evaluating subsequent elements, or after evaluating subsequent elements, as needed by the compiler to support efficient intermediate
storage allocation.
We like the spirit of the proposed rules in 7542 but think that they need to explicitly spell out these conditions. Right now, they imply that the compiler can reorder the location that
nested enumeration can occur, but don't explicitly state it. Rather than leaving this as undefined and letting the compiler fill in the blanks, we'd rather explicitly carve it out that
the compiler is free to optimize assuming that IEnumerable
instances can be enumerated at any time, so long as the element expressions that the user writes are evaluated in left-to-right
order. We also don't feel that we should specify that the compile needs to enumerate spreads in the order they appear; while we currently don't have any plans for enumerating a later spread
before an earlier spread, we don't think that we should restrict ourselves from optimizations in the space in the future if we find them. We support spreading IEnumerable
s, not
IEnumerator
s, and those should be iterable multiple times without producing different results each time.
The compiler is allowed to reorder spread enumerations assuming that IEnumerable
implementations don't have side effects, and the rules should be updated to explicitly call this out.