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

## Agenda

1. [Confirming Function Pointer Decisions](#Confirming-Function-Pointer-Decisions)
1. [Parameter Null Checking](#Parameter-Null-Checking)
1. [Interface `static` Member Variance](#Interface-`static`-Member-Variance)
1. [Property Enhancements](#Property-Enhancements)

## Quote of the Day

"It feels a little bit like we're playing code golf here"

## Discussion

### Confirming Function Pointer Decisions

https://github.com/dotnet/roslyn/issues/39865#issuecomment-647692516

There are a few open questions from a previous [LDM](LDM-2020-04-01.md) and a followup email chain
that need to be confirmed before they can be implemented. These questions center around calling
convention type lookup and how identifiers need to be written in source. The grammar we had roughly
proposed after the previous meeting is:

```antlr
func_ptr_calling_convention
   : 'managed'
   | 'unmanaged' ('[' func_ptr_callkind ']')?

func_ptr_callkind
  : 'CallConvCdecl'
  | 'CallConvStdcall'
  | 'CallConvThiscall'
  | 'CallConvFastcall'
  | identifier (',' identifier)*
```

##### Calling Convention Lookup

When attempting to bind the `identifier` used in an unmanaged calling convention, should this follow
standard lookup rules, such that the type must be in scope at the current location, or is using a
form of special lookup that disregards the types in scope at the current location? The types valid
in this location are a very specific set: they must come from the `System.Runtime.CompilerServices`
namespace, and the types must have been defined in the same assembly that defines `System.Object`,
regardless of the binding strategy used here, so it's really a question of whether the user has to
include this namespace in their current scope, adding a bunch of types that they are generally not
advised to use directly, and whether they can get an error because they defined their own calling
convention.

##### Conclusion

Given the specificness required here, we will use special name lookup.

##### Required identifiers

The previous LDM did not specify the required syntax for the identifiers quite explicitly enough for
implementation, and specified that identifiers should be lowercase while also having upper case
identifiers in some later examples. The following rules are proposed as the steps the compiler will
take to match the identifier to a type:

1. Prepend `CallConv` onto the identifier. No casemapping is performed.
2. Perform special name lookup with that typename in the `System.Runtime.CompilerServices` namespace
only considering types that are defined in the core library of the program (the library that defines
`System.Object` and has no dependencies itself).

We also reconsidered the decision from the previous LDM on using lowercase mapping for the identifier
names. There is convention for this in other languages: C/C++, for example, use `__cdecl` or similar
as their calling convention specifiers, and given that this feature will be used for native interop
with libraries doing this it would be nice to have some parity. However, this would introduce several
issues with name lookup: existing special name lookup allows us to modify the `identifier` specified
in source, but it does not allow us to modify the names of the types we're matching against, which
we would need to do here. There is certainly an algorithm that could be specified here, but we overall
felt that this was too complicated for what was a split aesthetic preference among members.

##### Conclusion

The proposed rules are accepted. As a consquence, the identifier specified in source cannot start
with `CallConv` in the name, unless the runtime were to add a type like `CallConvCallConv`.

### Parameter Null Checking

We ended the [previous meeting](LDM-2020-06-17.md) on this with two broad camps: support for the `!!`
syntax, and support for some kind of keyword. Email discussion over the remainder of the week and
polling showed that a clear majority supported the `!!` syntax.

#### Conclusion

We will be moving forward with `!!` as the syntax for parameter null checking:

```cs
public void M(Chitty chitty!!)
```

### Interface `static` Member Variance

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

We considered variance in `static` interface members. Today, for co/contravariant type parameters
used in these members, they must follow the full standard rules of variance, leading to some
inconsistency with the way that `static` fields are treated vs `static` properties or methods:

```cs
public interface I<out T>
{
    static Task<T> F = Task.FromResult(default(T)); // No problem
    static Task<T> P => Task.FromResult(default(T));   //CS1961
    static Task<T> M() => Task.FromResult(default(T));    //CS1961
    static event EventHandler<T> E; // CS1961
}
```

Because these members are `static` and non-virtual, there aren't any safety issues here: you can't
derive a looser/more restricted member in some fashion by subtyping the interface and overriding
the member. We also considered whether this could potentially interfere with some of the other
enhancements we hope to make regarding roles, type classes, and extensions. These should all be
fine: we won't be able to retcon the existing static members to be virtual-by-default for interfaces,
as that would end up being a breaking change on multiple levels, even without changing the variance
behavior here.

We also considered whether this change could be considered a bug fix on top of C# 8, meaning that
users would not have to opt into C# 9 in order to see this behavior. While the change is small and
likely very rarely needed, we would still prefer to avoid breaking downlevel compilers.

#### Conclusion

We will allow `static`, non-virtual members in interfaces to treat type parameters as invariant,
regardless of their declared variance, and will ship this change in C# 9.

### Property Enhancements

In a [previous LDM](#LDM-2020-04-01.md) we started to look at various enhancements we could make
to properties in response to customer feedback. Broadly, we feel that these can be addressed by
one or more of the following ideas:

1. Introduce a `field` contextual keyword that allows the user to refer to the backing storage of
the property in the getter/setter of that property.
    * In this proposal, we can consider all properties today as having this `field` keyword. As
    an optimization, if the user does not refer to the backing field in the property body, we
    elide emitting of the field, which happens to be the behavior of all full properties today.
    * Of the proposals, this allows the most brevity for simple scenarios, allowing some lazily-fetched
    properties to be one-liners.
    * This proposal does not move the cliff far: if you need to have a type differing from the
    type of the property, or multiple fields in a single property, then you must fall back to a full
    property and expose the backing field to the entire class.
    * There are also questions about the nullability of these backing properties: must they be
    initialized? Or should we provide a way to declare them as nullable, despite the property
    itself not being nullable?
    * There was also concern that this is adding conceptual overhead for not enough gain: education
    would be needed on when backing fields are elided and when they are not, complicated by the
    additional backcompat overhead of ensuring that `field` isn't treated as a keyword when it
    can bind to an existing name.
2. Allow field declarations in properties.
    * Conveniently for this proposal, a scoping set of braces for properties already exists.
    * This addresses the issue of properties backed by a different storage type: if you have a
    property that uses a `Lazy<T>` to initialize itself on first access, for example.
    * This also allows users to declare multiple backing fields, if they want to lock access
    to the property or combine multiple pieces of information into a single type in the public
    surface area.
    * In terms of user education, this is the simplest proposal. Since a set of braces already
    exists, the education is just "There's a new scope you can put fields in."
3. Introduce a delegated property pattern into the language.
    * There have been a few proposals for this, such as #2657. Other languages have also adopted
    this type of feature, including Kotlin and Swift.
    * This is by far the most complex of the proposals, adding new patterns to the language and
    requiring declaration of a whole new type in order to remove one or possibly 2 fields from
    a general class scope.
    * By that same token, however, this is the most broad of the proposals, allowing users to
    write reusable property implementations that could be abstracted out.

The LDM broadly viewed these proposals as increasing in scope: the `field` keyword allows the most
brief syntax, but forces users off the cliff back to full class-scoped fields immediately if their
use case is not having a single backing field of the same type. Meanwhile, property-scoped fields
don't allow for and encourage creating reusable helpers, like delegated properties would.

We also recognize that regardless of what decisions we make today, we're not done in this space.
None of these proposals are mutually exclusive, and we can "turn the crank" by introducing one, and
then adding more in a future release. There is interest among multiple LDM members in adding some
form of reusable delegated properties or property wrappers, and adding one of either the `field`
keyword or property-scoped fields does not preclude adding the other in a later release. Further,
all of these proposals are early enough that we still have a bunch of design space to work through
with them, while designing ahead enough to ensure that we don't add a wart on the language that we
will regret in the future.

#### Conclusion

A majority of the LDM members would like to start by exploring the property-scoped locals space.
We'll start by expanding that proposal with intent to include in C# 10, but will keep the other
proposals in mind as we do so.
