# C# Language Design Meeting for January 12th, 2022

## Agenda

1. [Open questions for `field`](#open-questions-for-field)
    1. [Initializers for semi-auto properties](#initializers-for-semi-auto-properties)
    2. [Definite assignment for struct types](#definite-assignment-for-struct-types)
2. [Generic Math Operator Enhancements](#generic-math-operator-enhancements)

## Quote(s) of the Day

- "I should really get a beer, but I'll wait" "Stop [redacted], it's only 10:01 here" ... "What time is it there, snark-o'clock?" "It would be if I grabbed that beer"
- "It was stupid syntax, but it was legal"

## Discussion

### Open questions for `field`

https://github.com/dotnet/csharplang/blob/4773c1bdb692aba574204a9affa890b0af3d2c05/proposals/semi-auto-properties.md#open-ldm-questions

#### Initializers for semi-auto properties

For semi-auto properties, there is a question of where initializers are allowed, and what effect they have when written. Our existing rules are pretty simple:
manually-implemented properties cannot have initializers. Auto-properties can have initializers, and the effect of the initializer is assigning to the backing field.
The `field` keyword walks a fine line here, sitting squarely between both worlds. We think there's some main issues to solve:

* If there is a manually-implemented setter, then an initializer cannot call the setter, as initializers run _before_ the constructor and setters can reference
type state. Initializers cannot reference type state, so this breaks the rules that rely on that invariant.
* On the other hand, for manually-implemented setters, if an initializer assigned directly to the backing field this would cause a very visible but non-obvious
behavior difference between putting an initializer on a property and an initializer in the constructor. It is possible to observe a difference here today, but it
required a virtual property overridden in a dervied type and is a much more niche case. We think this difference is both confusing and a potential footgun in waiting.

To solve this, we have a few options for restrictions on initializers:

1. Treat semi-auto properties as if they are manually-implemented properties. This means they would not support initializers.
2. Treat semi-auto properties with non-existent for auto-implemented setters as if they were auto properties. This means such properties would be allowed to have
initializers, and no other types of properties would be allowed to do so.
3. Allow all properties with backing fields to have initializers. These would assign to the backing field, and there would be a meaningful difference between the
initializer and assigning in the constructor.

After some discussion, we feel that 3 is a step too far. It could potentially be enabled later, if we can find a set of rules that makes sense and unifies the area,
but for the moment we don't have those rules. We also think 1 might be a bit too conservative: part of this feature is easing the cliff between full properties and
auto properties. Disallowing initializers when the setter is "trivial" seems like it goes against this desire.

There's also some potential future overlap with [property-scoped fields](https://github.com/dotnet/csharplang/issues/133). These fields will, presumably, need to be
accessible in a limited fashion from constructors or other locations that can access type state, in order to initialize them. `Lazy<T>` backing fields, for example,
will almost certainly need to be initialized with a delegate that references the current type, something that cannot be done from an initializer today. If we need
more granular control for the automatic backing field, we think we can rely on that proposal to allow this control.

##### Conclusion

We have the following formalized rule for when initializers are permitted:

* Initializers are permitted when a property has a backing field that will be emitted and the property either does not have a setter, or its setter is auto-implemented.

We have the following rule for when properties can be assigned in the constructor of a type:

* Properties can be assigned in the constructor of a type if that property has a setter, or is a get-only property whose backing field will be emitted.

#### Definite assignment for struct types

Semi-auto properties pose a very interesting challenge for definite assignment in struct types. Today, all fields in a struct type must be fully initialized by any
constructor of that type. Existing auto properties are fine here: the compiler controls the implementation of the `set` method, and we know that they are not accessing
any instance state other than the backing field, and not exposing `this` before assignment of all fields are completed. Semi-auto properties, by contrast, can have a
user-implemented `set` or `init` method, which has all the power a regular `set` or `init` method has. It can access instance state, potentially before some other part
of the instance state has been assigned. Further, there is no way to mention the backing field from outside the property (this being one of the main selling points of
the feature). We see a few potential solutions for this:

1. Require a chain to another constructor or a `this = default` before access to semi-auto properties is permitted. This a bitter pill, as property initializers run
before the constructor body. This would mean any property initializers are blown away by the `this = default;` assignment.
2. Have the compiler initialize all automatically-generated backing fields to `default`. This would allow skipping initialization of all auto and semi-auto properties,
the former of which is not allowed today. While we think this would work, it introduces inconsistencies between regular fields and backing fields, which is odd, and
there could be some clients that suffer from default initializers for all backing fields (mainly around perf).

After some discussion, we're pretty conflicted here. We'll have a small group go and explore these ideas and other potential solutions and come back to the LDM with
their findings.

##### Conclusion

No answers today.

### Generic Math Operator Enhancements

https://github.com/dotnet/csharplang/issues/4665  
https://github.com/dotnet/csharplang/issues/4666  
https://github.com/dotnet/csharplang/issues/4682

Finally, we took a look at 3 different operator-related enhancements for the generic math scenarios we're targeting for .NET 7/C# 11. We're supportive of all of them,
but the `checked` operator enhancements are definitely going to be the largest amount of work of the 3. There's new syntax and rules around what things are called when
to be hammered out.

For relaxing shift operator requirements, we think that this rule has served its purpose. It is technically possible, via external libraries, to emulate `cout` and other
shift operator semantics in C# today, but we don't see people doing this. Relaxing the restrictions will make it easier for those libraries to be written, but the general
patterns for the language have been established at this point.

#### Conclusion

These proposals are generally approved of. We'll look forward to reviewing the concrete designs soon, particularly around `checked`.
