Added notes for 6/24/2020
This commit is contained in:
parent
f4b1117610
commit
3b8500a93f
188
meetings/2020/LDM-2020-06-24.md
Normal file
188
meetings/2020/LDM-2020-06-24.md
Normal file
|
@ -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<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 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<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.
|
Loading…
Reference in a new issue