# C# Language Design Meeting for March 29th, 2021

## Agenda

1. [Parameterless struct constructors](#parameterless-struct-constructors)
2. [AsyncMethodBuilder](#asyncmethodbuilder)

## Quote of the Day

- "I told people to question my vague memory and now I'm upset that you did"

## Discussion

### Parameterless struct constructors

https://github.com/dotnet/csharplang/blob/struct-ctor-more/proposals/csharp-10.0/parameterless-struct-constructors.md

Today, we took a look at open questions in this proposal.

#### Field initializers and implicit constructors

The main question on our mind here is, what does this code print?

```cs
// Is this `default(S)`, or does `Field` have a value of 42?
var s = new S();
System.Console.WriteLine(s.Field);

public struct S {
    public int Field = 42;
    
    public S(int i)
    {
        // C# executes:
        //     Field = 42
    }
}
```

There are a few possibilities for what this can imply:

1. There is an implicit parameterless constructor that runs the field initializer. The code sample would print 42.
2. No implicit parameterless constructors are synthesized. We warn on the field initializer if an explicit parameterless constructor
is not provided. The code sample would print 0.
3. An implicit parameterless constructor is synthesized _when no other explicit constructors are provided_. The code sample would
print 0.

The complication here comes from the fact that `new S()` was and will continue to be legal, even when there is no parameterless
constructor. This differs from class types, which do not have an implicit parameterless constructor that would run such a field
initializer. Therefore, one way that we could look at the problem is under the lens of "We've always had this constructor, it's
just been omitted as an optimization". By that rule, option 1 should be the tack we take. However, we are also concerned that some
users will have taken a dependency on that parameterless constructor meaning a 0-initialized struct.

Another complication was raised in conjunction with record struct primary constructors. In record classes today, we require that if
the record has a primary constructor, all other constructors must chain to it. This would mean that the parameterless constructor
needs to chain to the primary constructor, which wouldn't work for an implicit parameterless constructor. We could require that record
structs with field initializers and a primary constructor provide a parameterless constructor that chains, but that also brings up
concerns about nullable annotations of parameters. In C# today, we generally advise struct authors to annotate for typical use, not
for all the technical possibilities. That means that if users shouldn't be using a default struct, we advise that a reference field
could be annotated not null. That would interact poorly with a requirement to define the primary constructor here, as there could
very well be _no_ valid default to specify.

##### Conclusion

The points raised today around interactions with primary constructors need more thought. We'll take this back for more workshopping,
and bring it to LDM again later.

#### Other conclusions

We also came to a couple other conclusions this session, affirming the proposed behavior of:

* Definite assignment simplification: the proposed rules are accepted. Further general relaxation of definite assignment might be
interesting, but require a separate proposal.
* Some of the language around how the C# compiler handles private and internal constructors today needs to be modified a bit to
reflect more nuances, but we approve of the general goal: keep behavior unchanged.

### AsyncMethodBuilder

https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/async-method-builders.md

We started today by questioning whether we can simplify this proposal a bit. Today, the proposal covers both a scoped version and
a single-method version. The scoped version is complicated, and has a number of downsides:

* Diagnostic story is unclear. If the user thought they were applying the builder to all methods with a specific return type but
did it incorrectly for a subset of the locations, we have no good way to let them know they made an error.
* Relatedly, a scoped version would need a good amount of IDE work to help users understand what builder type this method was actually
using.

When we look at our use cases for this attribute, we generally believe that there are a couple of main ones. First, and the use case
the runtime is interested in, is pooling. This usecase is unlikely to want module-wide application because pooling is potentially
harmful for infrequently-used APIs, and so users would want to be more precise about which specific methods in a type are high
usage and which are not. The other big use case we can see is builders that responsible for tracing. These builders could potentially
be wanted module-wide so that tracing could be added everywhere, but we are again worried about the diagnostic and user-info story
for this.

#### Conclusion

We will remove the scoped version of this proposal for now, and use the existing `AsyncMethodBuilder` attribute on an individual method,
local function, or lambda, to control the builder type for that function. If we hear demand for a module-scoped version in the future,
we can use a new attribute designed for that case and think about the diagnostic situation at that point.
