# C# Language Design Meeting for June 24th, 2024

## Agenda

- [First-Class Spans](#first-class-spans)
- [`field` questions](#field-questions)
    - [Mixing auto-accessors](#mixing-auto-accessors)
    - [Nullability](#nullability)

## Quote of the Day

- Nothing particularly amusing was said today, sorry.

## Discussion

### First-Class Spans

Champion issue: https://github.com/dotnet/csharplang/issues/7905  
Spec link: https://github.com/dotnet/csharplang/blob/dd326f7fb0c282825ed1b2ffbe8180b6c54afa1c/proposals/first-class-span-types.md#conversion-from-type-vs-from-expression

We started today by looking at an open question in the first-class span feature. The question at hand is about a minor difficulty that the compiler team has run into while implementing the feature; by making
the conversion a conversion from type, rather from an expression, we've actually run into a novel scenario in the compiler. Currently, all the special-cased types that participate in conversions from the
core library itself (ie, the assembly that defines `System.Object`), not from other assemblies. This fact has led to some of the structure of the compiler around conversions. `(ReadOnly)Span`, on the other
hand, may not come from core library, but may instead come from `System.Memory.dll`, or the user may define it themselves in source. This means we need to decide how the compiler finds the `(ReadOnly)Span`
type that considered for the conversions defined by this feature. We have a few options for this:

1. Require that the `System.Span` and `System.ReadOnlySpan` considered for the feature come from the core library, like all other types that have special conversion rules.
2. Restructure the compiler to plumb our existing logic for finding these types into the conversion logic.
3. Simply match by the full name of the type.

Option 2 is the most consistent with all the rest of the compiler's handling around `(ReadOnly)Span`, but we're also concerned about the investment cost here. It's potentially a big restructure; not impossible
by any means, but it has a fairly low return-on-investment in the long term. Option 1 is the easiest to implement, but we're somewhat concerned about the inconsistencies, particularly with other features that
will allow the user to override `System.Span` with a type defined in source. Finally, option 3 is easier to implement than 2, and likely to be the most consistent with it. It's not perfect; in particular, it
will potentially apply to _all_ definitions of `System.(ReadOnly)Span`, regardless of what assembly they come from, while things like collection expression rules only apply to the canonical definition (regardless
of whether that canonical definition comes from source, corelib, or `System.Memory.dll`). We think that, for 99.9% of users, this will result in identical behavior to 2. Given that, we think option 3 is the best
balance here.

#### Conclusion

We will go with option 3, match `System.Span` and `System.ReadOnlySpan` by full name.

### `field` questions

Champion issue: https://github.com/dotnet/csharplang/issues/140  
Questions: https://github.com/dotnet/csharplang/blob/dd326f7fb0c282825ed1b2ffbe8180b6c54afa1c/proposals/semi-auto-properties.md#open-ldm-questions

#### Mixing auto-accessors

The first question up is a simple confirmation question. While the proposal has long included the ability to have one half of a semi-auto property be a `set;` or `get;`, we've never explicitly confirmed it
in LDM. After a brief discussion, we confirmed that we do indeed want to be able to leave one half of a semi-auto property as an auto accessor.

##### Conclusion

Confirmed.

#### Nullability

The next issue took up the rest of our time today, without reaching a final conclusion. Nullability of the backing field here is very tricky: we do almost no inter-procedural analysis of methods today
(except for local functions), and it very much seems like we will need to do this type of analysis to get the nullability correct. There's also a lot of edge cases to consider, particularly around lazy
initialization. One thing that became very clear was that the proposed `[field: NotNull]` approach wasn't an acceptable tradeoff to the LDM. Nullability does make a lot of pragmatic cuts where it is possible
to observe `null` values, such as arrays. However, we think that this isn't a place where we could accept that type of tradeoff; arrays are a local and obvious source of nulls. Lazy initialization through
multiple constructors or methods is hard to get right and much subtler to debug. We are somewhat concerned about having a magic solution though, especially given our experience with `var`. While there are no
safety holes introduced from our treatment of `var` as allowing null assignments, we do know that a non-zero percentage of our users are confused by the behavior; even if it's completely safe, there's a
perception that the guardrail is too loose, and it causes these users to lose their trust of the feature. If we did fancy cross-method analysis for `field`, and allowed `null` assignments to the backing field
even when it's provably perfectly safe, is that ok? Or will that confuse users and cause them to distrust the feature? We want to kick these questions back to a small group to noodle on, so we will leave
these for now and revisit in the near future.
