diff --git a/meetings/2020/LDM-2020-06-24.md b/meetings/2020/LDM-2020-06-24.md new file mode 100644 index 0000000..a857335 --- /dev/null +++ b/meetings/2020/LDM-2020-06-24.md @@ -0,0 +1,188 @@ +# 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 +{ + static Task F = Task.FromResult(default(T)); // No problem + static Task P => Task.FromResult(default(T)); //CS1961 + static Task M() => Task.FromResult(default(T)); //CS1961 + static event EventHandler 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 retconn 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` 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.