# C# Language Design Meeting for September 25th, 2023

## Agenda

- [Primary constructors](#primary-constructors)
- [Defining well-defined behavior for collection expression types](#defining-well-defined-behavior-for-collection-expression-types)

## Quote(s) of the Day

- "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"

## Discussion

### Primary constructors

https://github.com/dotnet/csharplang/issues/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:

1. Adjust the rules to match the behavior of the implementation (which is the same between `record` and non-`record` types).
2. Adjust the behavior to match the rules in all cases (a possible breaking change for `record`s).
3. Disallow a primary constructor parameter to use a type parameter's name (a possible breaking change for `record`s).
4. 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.

#### Conclusion

Option 1, update the primary constructors spec to match the existing compiler behavior and align with `record`s.

### Defining well-defined behavior for collection expression types

https://github.com/dotnet/csharplang/issues/5354  
https://github.com/dotnet/csharplang/issues/7542

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.

#### Conclusion

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.

