# C# Language Design Meeting for October 17th, 2022

## Agenda

- [Primary Constructors](#primary-constructors)
- [Permit variable declarations under disjunctive patterns](#permit-variable-declarations-under-disjunctive-patterns)

## Quote of the Day

- "I'll do my darndest not to jinx in various ways... In the first hour we'll knock this out"

## Discussion

### Primary Constructors

https://github.com/dotnet/csharplang/issues/2691

As indicated [during triage](LDM-2022-09-26.md#ungrouped), the proposal for primary constructors has been updated with the latest thinking on the scenario,
and we are now reviewing it. The new version uses capturing semantics for primary constructor parameters, and works for all types, not just records. In going
over the proposal, we had a few comments:

* [Last week](LDM-2022-10-12.md#keywordness-of-field), we talked about how knowing the backing fields of a type can be a problem for the compiler, and this
proposal would introduce a similar concept. However, we think that this is solvable: `field` specifically needs to deal with backwards compatibility, while
primary constructors are all new. We can simply say that primary constructor parameters are _always_ treated as captures for these types of purposes, and the
removal of the parameters is an optimization strategy the compiler is free to use or not as it chooses.
    * Interestingly, F# primary constructors will not optimize away the primary constructor parameters if the type is a struct to avoid similar circularity issues
    when determining whether a type supports default values, and we may end up needing to do that in C#.
* We're a little concerned about the potential for confusion when a field shadows a primary constructor parameter. For example:
```cs
class C(int i)
{
    protected int i = i; // References parameter
    public int I => i; // References field
}
```
* We definitely feel that there at least needs to be a warning for the double-capture problem. It's fairly easy to accidentally code yourself into a hole here.
* We need to think about how a `readonly` modifier on a `struct` will impact the primary constructor.
* The `{ statements }` syntax as a primary constructor body is not particularly liked. There are two leading alternatives:
    * `ClassName { }` - IE, just like a regular constructor, but minus the parentheses.
    * `init { }` - More visually different from a constructor, which helps it stand out as being part of the primary constructor.

Overall, we like the direction this proposal has taken, and would like to start playing with it. We're particularly interested in feedback on the double-capture
issue, so a preview of the feature that users can try will help inform the capturing decision here.

#### Conclusion

We will proceed with a prototype with the proposed semantics, and a warning for the double-capture problem.

### Permit variable declarations under disjunctive patterns

https://github.com/dotnet/csharplang/issues/4018

We took another look at updates to this proposal now that we have non-C# 11 time. The main thing we ended up debating was how "exact" should we make variable
redeclaration need to be, as some edge cases can be hurt depending on our decisions. Some examples are:
```cs
// Example A
if (e is C c || e is Wrapper { NullableReferenceTypeProp: var c }) { } // Should there be an error since `var` is `C?` here

// Example B
if (e is S s || e is Wrapper { NullableValueTypeProp: var s }) { } // Should there be an error since this is S vs Nullable<S>?

// Example C
if (e is C<(int a, int b)> c || e is Wrapper { Prop: var c }) { c.TupleProperty.a } // Is this ok? Where does the name a come from in the second example?

// Example D
if (e is Derived d) { d.DerivedMethod(); }
else if (e is Wrapper { BaseTypeProp: Base d }) { } // Should d be widened to `Base`? What happens to the above method access?
```

We arrived at 4 possible options for the behavior:

1. Types must exactly match.
    1. Variant 1b: types must exactly match _except_ for top-level reference nullability. Nested reference nullability differences are subject to the same
    nullability warnings as standard conversions.
2. Types must be identity-convertible. This allows for tuple-name differences.
3. We allow a special rule for `var`: `var` doesn't contribute type information when being unified across multiple declarations.
4. We use best common type across all declarations, like ternaries.

In discussion, we started with 4. However, we were dissatisfied with the behavior of example D above with that rule, as it either means that a related `case` or
`else if` can affect the meaning of the code inside the first `if` block, or it means that we need to introduce variables that change their type based on the
current execution flow. We felt that 3 wasn't particularly clear on the behavior, and 2 was potentially confusing: what if there are multiple sets of tuple names?
How should we decide which names to use? This was less strong, however, and we are open to allowing differences in the future. Ultimately, we felt that variant 1b
is the best option, and we will look for user feedback to see if we should loosen the rules more.

#### Conclusion

Variant 1b of the rules accepted.
