Add LDM notes for April 8, 2020
This commit is contained in:
Родитель
14cd563c27
Коммит
017870d59c
|
@ -0,0 +1,154 @@
|
|||
|
||||
# C# Language Design for April 8, 2020
|
||||
|
||||
## Agenda
|
||||
|
||||
1. `e is dynamic` pure null check
|
||||
2. Target typing `?:`
|
||||
3. Inferred type of an `or` pattern
|
||||
4. Module initializers
|
||||
|
||||
## Discussion
|
||||
|
||||
### `e is dynamic` pure null check
|
||||
|
||||
We warn that doing `e is dynamic` is equivalent to `e is object`, but `e is object` is a pure
|
||||
null check, while `e is dynamic` is not. Should we make `is dynamic` a pure null check for
|
||||
consistency?
|
||||
|
||||
**Conclusion**
|
||||
|
||||
Yes.
|
||||
|
||||
### Target-typing `?:`
|
||||
|
||||
The simplest example where we have a breaking change is
|
||||
|
||||
```C#
|
||||
void M(short)
|
||||
void M(long)
|
||||
M(b ? 1 : 2)
|
||||
```
|
||||
|
||||
Previously this would choose `long`, because the expressions are effectively typed separately,
|
||||
and when inferring `1 : 2` we would choose `int`, and then rule out `short` during overload
|
||||
resolution as invalid.
|
||||
|
||||
Target-typing converts each arm in turn to find the best possible type, selecting `short` instead
|
||||
of `long`. That means the first overload is selected instead with target-typing.
|
||||
|
||||
It's hard to easily avoid this breaking change. The obvious mechanism, running overload
|
||||
resolution twice, is problematic because having multiple arguments with `conditional` expressions
|
||||
produces exponential growth in the number of passes of overload resolution.
|
||||
|
||||
**Conclusion**
|
||||
|
||||
Let's talk about this again in a separate meeting. Since whatever we choose here will probably
|
||||
be the final decision (any further changes will be breaking), we want to be sure we're making the
|
||||
best choice.
|
||||
|
||||
### Inferred type of an `or` pattern is the common type of two inferred types
|
||||
|
||||
```C#
|
||||
object o = 1;
|
||||
if (o is (1 or 3L) and var x)
|
||||
// what is the type of `x`?
|
||||
```
|
||||
|
||||
The problem here is that the existing common type algorithm allows conversion that are forbidden
|
||||
in pattern matching. In the above case we would choose `long`, because `3L` is a long, and `1`
|
||||
can be converted to `long`. However, in pattern matching the widening conversion from `int` to
|
||||
`long` is illegal.
|
||||
|
||||
The proposal is to narrow the set of conversions only to implicit reference conversions or
|
||||
boxing conversions. Why this could be useful is the example
|
||||
|
||||
```C#
|
||||
class B { }
|
||||
class C : B { }
|
||||
if (o is (B or C) and var x)
|
||||
// x is `B`
|
||||
```
|
||||
|
||||
A follow-up question is about ordering. The proposed rules only infer types mentioned in
|
||||
the checks, meaning that the following
|
||||
|
||||
```C#
|
||||
if (o is (Derived1 or Derived2 or Base { ... }) and var x)
|
||||
```
|
||||
|
||||
looks like this today
|
||||
|
||||
```C#
|
||||
((Derived1 or Derived2) or Base)
|
||||
|
||||
-> ((object) or Base)
|
||||
|
||||
-> (object)
|
||||
```
|
||||
|
||||
On the other hand, if the example were parameterized in the opposite way,
|
||||
|
||||
```C#
|
||||
(Derived1 or (Derived2 or Base))
|
||||
|
||||
-> (Derived1 or (Base))
|
||||
|
||||
-> (Base)
|
||||
```
|
||||
|
||||
So the ordering seemingly matters if the `or` pattern is a simple binary expression.
|
||||
|
||||
The first question is if
|
||||
|
||||
```C#
|
||||
if (o is (Derived1 or Derived2 or Base { ... }))
|
||||
```
|
||||
|
||||
should produce `Base`, and the second question is if parenthesizing affects this, e.g. explicitly saying
|
||||
|
||||
```C#
|
||||
if (o is ((Derived1 or Derived2) or Base { ... }))
|
||||
```
|
||||
|
||||
produces `object`.
|
||||
|
||||
**Conclusion**
|
||||
|
||||
We're not seeing a lot of scenarios that depend on these features, but not doing it feels like
|
||||
leaving information on the table. Functionally, the compiler can infer a stronger type, so why
|
||||
not do so? The proposed modified common type algorithm is accepted.
|
||||
|
||||
We also think that type narrowing for the `or` operation should use all the `or` operations
|
||||
in a series as the arguments to the common type algorithm.
|
||||
|
||||
### Module Initializers
|
||||
|
||||
Proposal: https://github.com/RikkiGibson/csharplang/blob/module-initializers/proposals/module-initializers.md
|
||||
|
||||
There are a few places where module initializers today. The most common is a shared resource
|
||||
that is used by multiple types, but the program would like to initialize the shared resource
|
||||
before the static constructors of the types are run.
|
||||
|
||||
The question for the implementation is to how to indicate where the code for the module
|
||||
initializer will go, and how the compiler will recognize it.
|
||||
|
||||
The proposal provides a mechanism to identify the module initializer via an attribute on the module
|
||||
that points to a type, and type contains a static constructor that acts as the module initializer.
|
||||
From a conceptual level, this seems more complicated than necessary. Can we put the attribute on the
|
||||
type or, even the method, that holds the code for the module initializer?
|
||||
|
||||
If we go that route, why require the code be in the static constructor at all? Can any static method
|
||||
be a target? One reason why we may prefer a static constructor is that if the emitted module initializer
|
||||
calls the target static constructor method, it's easy to insert code before that method is invoked
|
||||
by writing a static constructor for the containing type. On the other hand, even static constructor
|
||||
can have code inserted before them, for example with static field initializers.
|
||||
|
||||
The last question is whether to allow only one module initializer method, or allow multiple and specify
|
||||
that the compiler will call them in a stable, but implementation-defined order.
|
||||
|
||||
**Conclusion**
|
||||
|
||||
Let's let any static method be a module initializer, and mark that method using a well-known attribute.
|
||||
We'll also allow multiple module initializer methods, and they will each be called in a reserved, but
|
||||
deterministic order.
|
|
@ -37,16 +37,6 @@
|
|||
|
||||
- Record Monday (Andy, Mads)
|
||||
|
||||
## April 8, 2020
|
||||
|
||||
- Is `e is dynamic` a "pure" null check? (Neal)
|
||||
- Reconsider: Target-typing ?: when the natural type isn't convertible to the target type. (Neal)
|
||||
- Reconsider: Inferred type of an or pattern is the "common type" of the two inferred types (Neal)
|
||||
- https://github.com/dotnet/csharplang/issues/2608 module initializers (Neal)
|
||||
- https://github.com/dotnet/csharplang/issues/2926 Target-typed simple identifier (Neal)
|
||||
- Triage (focus on features championed by Neal)
|
||||
- https://github.com/dotnet/csharplang/issues/2910 base(T) (Neal)
|
||||
|
||||
## April 6, 2020
|
||||
|
||||
- Record Monday: Init-only properties deep dive (Jared)
|
||||
|
@ -71,6 +61,15 @@
|
|||
|
||||
Overview of meetings and agendas for 2020
|
||||
|
||||
## April 8, 2020
|
||||
|
||||
[C# Language Design Notes for April 8, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-04-08.md)
|
||||
|
||||
1. `e is dynamic` pure null check
|
||||
2. Target typing `?:`
|
||||
3. Inferred type of an `or` pattern
|
||||
4. Module initializers
|
||||
|
||||
## April 1, 2020
|
||||
|
||||
[C# Language Design Notes for April 1, 2020](https://github.com/dotnet/csharplang/blob/master/meetings/2020/LDM-2020-04-01.md)
|
||||
|
|
Загрузка…
Ссылка в новой задаче