Add LDM notes for Sep 11, 2019
This commit is contained in:
parent
4da0c18183
commit
0489cb64b7
230
meetings/2019/LDM-2019-09-11.md
Normal file
230
meetings/2019/LDM-2019-09-11.md
Normal file
|
@ -0,0 +1,230 @@
|
|||
|
||||
# C# Language Design Meeting for Sep. 11, 2019
|
||||
|
||||
## Agenda
|
||||
|
||||
## Discussion
|
||||
|
||||
### Nullable attributes and flow state interaction
|
||||
|
||||
We started this discussion with an email with a proposal based on follow-up research from the
|
||||
previous meeting.
|
||||
|
||||
### Attribute interaction proposal
|
||||
|
||||
> Allowed inputs and outputs:
|
||||
>
|
||||
> First of all, I’ll try to make rules based only on “allowed inputs” and “allowed outputs” of a
|
||||
> property. I’ll use shorthands ?, ! and T for “nullable”, “nonnullable” and “unknown – depends on
|
||||
> T” respectively.
|
||||
>
|
||||
> For all the different sensible combinations of attributes, here are the “allowed inputs” and “allowed outputs”:
|
||||
|
||||
|
||||
> | | Allowed input | Allowed output |
|
||||
> |----------------------------|---------------|----------------|
|
||||
> | string | ! | ! |
|
||||
> | [AllowNull]string | ? | ! |
|
||||
> | [NotNull]string? | ? | ! |
|
||||
> | [MaybeNull]string | ! | ? |
|
||||
> | [DisallowNull]string? | ! | ? |
|
||||
> | string? | ? | ? |
|
||||
> | [DisallowNull][NotNull]T | ! | ! |
|
||||
> | [NotNull]T | T | ! |
|
||||
> | [AllowNull][NotNull]T | ? | ! |
|
||||
> | [DisallowNull]T | ! | T |
|
||||
> | T | T | T |
|
||||
> | [AllowNull]T | ? | T |
|
||||
> | [DisallowNull][MaybeNull]T | ! | ? |
|
||||
> | [MaybeNull]T | T | ? |
|
||||
> | [AllowNull][MaybeNull]T | ? | ? |
|
||||
|
||||
> There should be no surprises to anyone there. Now let’s use these to define the different behaviors around properties:
|
||||
> Ordering of states: T is stricter than ? and ! is stricter than both T and ?.
|
||||
> This is a measure of relative permissiveness of states.
|
||||
|
||||
> Initial state: The initial state of a property is its allowed output.
|
||||
> This corresponds to us knowing nothing about the property yet, beyond what it tells us through a combination of its type and its postconditions.
|
||||
|
||||
> State after null check:
|
||||
>
|
||||
> - On the non-null branch of a null check the state of the property is !.
|
||||
> - On the null branch of a pure null check the state of the property is ?.
|
||||
> - Elsewhere the state of the property is unchanged.
|
||||
>
|
||||
> These rules reflect the general benefit of a null check, as well as the overriding effect of a pure null check even of a nonnull property.
|
||||
|
||||
Note that this rules out "dangerous" properties, meaning properties that may change outside the
|
||||
scope of the nullable analysis, as in fields which may be changed by a different thread, and thus
|
||||
the null check is unreliable. We don't consider this scenario to be in scope of our current
|
||||
design and if we decide to address this, we must create a new attribute or some other mechanism.
|
||||
|
||||
There's also some problem with the state after null checks, namely that the type may not support
|
||||
the `?` state. For instance, in the following unconstrained generic,
|
||||
|
||||
```C#
|
||||
T M<T>(T t)
|
||||
{
|
||||
if (t != null)
|
||||
t.ToString();
|
||||
return t;
|
||||
}
|
||||
```
|
||||
|
||||
we should not produce a warning on the return, since the state should match the legal state of
|
||||
`T`, which is `T`, not `?`. Similarly, for non-Nullable value types, the state cannot be `?`
|
||||
after a null check, since the value cannot be null. The rule should use the `T` state.
|
||||
|
||||
> State after assignment:
|
||||
>
|
||||
> The state of the property after an assignment is
|
||||
>
|
||||
> - Its initial state if the state of the assigned value is at least as strict as the allowed input, but no stricter than the allowed output
|
||||
> - The state of the assigned value otherwise
|
||||
>
|
||||
> This rule reflects that a property is expected to “take care of things” when the state of an assigned value is valid as input but not as output. It does so by assuming that the resulting state in such situations is something that’s valid as output.
|
||||
>
|
||||
> This is probably the only rule that would differ from the rules for fields, which would continue to always use the state of the assigned value.
|
||||
|
||||
> Warnings on assignment: A warning is yielded if the state of the assigned value is less strict than the allowed input.
|
||||
> This is the same rule as all other input positions.
|
||||
|
||||
|
||||
We like this new "state after assignment" rule and think it can be implemented now.
|
||||
|
||||
However, when looking into the solution, we found that this is the current behavior for properties when annotated:
|
||||
|
||||
```C#
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
#nullable enable
|
||||
|
||||
class C<T> where T : class?
|
||||
{
|
||||
public C(T x) => f = x;
|
||||
T f;
|
||||
T P1 { get => f; set => f = value; }
|
||||
[AllowNull] T P2 { get => f; set => f = value ?? throw new ArgumentNullException(); }
|
||||
[MaybeNull] T P3 { get => default!; set => f = value; }
|
||||
|
||||
void M()
|
||||
{
|
||||
P1 = null; // Warning
|
||||
P2 = null; // No warning
|
||||
P3 = null; // Warning
|
||||
|
||||
f = P1; // No warning
|
||||
f = P2; // No warning
|
||||
f = P3; // BUG?: No warning!
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
That last line does not look right. Similar to `default(T)`, you could be producing a
|
||||
potentially nullable value, when the substituted type may not permit it. We think
|
||||
the three state domain outlined above will solve the problem, but that would
|
||||
be too extensive to change to perform in such a short period of time.
|
||||
|
||||
Alternative: whenever you introduce a value (by calling
|
||||
a property or a method), that produces a generic type annotated with `[MaybeNull]`
|
||||
in a substituted generic method, that would produce a warning. This
|
||||
matches our current behavior for `default(T)`.
|
||||
|
||||
For example,
|
||||
|
||||
```C#
|
||||
T M<T>()
|
||||
{
|
||||
_ = (new List<T>).FirstOrDefault(); // this would now produce a warning
|
||||
}
|
||||
```
|
||||
|
||||
**Conclusion**
|
||||
|
||||
Let's implement the "state after assignment" rule as defined and implement the
|
||||
"Alternative" proposal outlined above. We will consider updating to use the
|
||||
"three state domain" above later, which may have some further changes.
|
||||
|
||||
|
||||
## More triage
|
||||
|
||||
### Top-level statements and member declarations
|
||||
|
||||
We have a variety of different use cases and experimental products (C# Interactive Window,
|
||||
Jupyter projects, try.net, etc) that use the current "C# scripting" language, which is already
|
||||
effectively a dialect of C#. There's a fair amount of concern that if adoption continues, we may produce a fracturing of the C# language.
|
||||
|
||||
However, adding top-level statements and reconciling scripting in C# proper would be an expensive
|
||||
feature, in both design and implementation. It also doesn't directly impact many of the designs
|
||||
we're currently considering.
|
||||
|
||||
But there is also significant cost to doing nothing. We have not considered the semantic for many
|
||||
features in C# 8, or even if they should work in scripting (`using` declarations, notably). There
|
||||
is a significant ongoing cost here, either in considering all our designs for the scripting
|
||||
dialect, or in risk that not doing design/implementation work will cause bad experiences for
|
||||
products using the C# scripting code.
|
||||
|
||||
**Conclusion**
|
||||
|
||||
We'll schedule this for 9.0, to at least examine options.
|
||||
|
||||
### Primary constructors
|
||||
|
||||
This occupies the same design space as records, which is scheduled for 9.0, so
|
||||
we at least need to consider this feature while implementing records.
|
||||
|
||||
**Conclusion**
|
||||
|
||||
Moving to 9.0.
|
||||
|
||||
### Negated-condition if statement
|
||||
|
||||
Issue #882
|
||||
|
||||
This overlaps significantly with a "is not" pattern. We're not confident this
|
||||
feature has significant value, after the "is not" pattern is implemented.
|
||||
|
||||
**Conclusion**
|
||||
|
||||
Move to X.X to consider after "is not" has shipped and see if there are significant
|
||||
use cases that are not addressed by the "is not" pattern.
|
||||
|
||||
### Allow `default` in deconstruction
|
||||
|
||||
Issue #1394
|
||||
|
||||
The primary use case is `(x, y, z) = default;` instead of naming each variable
|
||||
individually. There are some issues around the written specification, specifically
|
||||
on what target typing `default` has.
|
||||
|
||||
**Conclusion**
|
||||
|
||||
From a consistency perspective it seems like this should work, regardless of the
|
||||
complexity in details of the specification. We'll take this Any Time whenever we have a solid
|
||||
specification and implementation.
|
||||
|
||||
### Partial type inference
|
||||
|
||||
Issue #1349
|
||||
|
||||
Nothing that's related to type inference is a tiny feature, but this is pretty
|
||||
small as type inference changes are concerned. We think the hardest problem will
|
||||
be agreeing on the syntax. Agreed that it could be useful, though.
|
||||
|
||||
**Conclusion**
|
||||
|
||||
We'll take this Any Time.
|
||||
|
||||
### Declaration expressions
|
||||
|
||||
Issue #973
|
||||
|
||||
Somewhat related is "sequence expressions". This is useful for declaring variables inline in an
|
||||
expression. There are places where statements are not possible, and this requires
|
||||
refactoring.
|
||||
|
||||
**Conclusion**
|
||||
|
||||
We don't think there's value in half measures here. We think going all the way to sequence
|
||||
expressions may have value, but then declaration expressions do not.
|
|
@ -33,12 +33,6 @@
|
|||
|
||||
## Sep 16, 2019
|
||||
|
||||
## Sep 11, 2019
|
||||
|
||||
- Close https://github.com/dotnet/roslyn/issues/37313 (Rikki, Phillip)
|
||||
- Triage new proposed [9.0 features](https://github.com/dotnet/csharplang/issues?q=is%3Aopen+is%3Aissue+no%3Amilestone+label%3A%22Proposal+champion%22)
|
||||
- Finish triaging away [8.X milestone](https://github.com/dotnet/csharplang/issues?q=is%3Aopen+is%3Aissue+label%3A%22Proposal+champion%22+milestone%3A%228.X+candidate%22)
|
||||
|
||||
## Aug 26, 2019
|
||||
|
||||
- Triage language features
|
||||
|
@ -56,9 +50,17 @@
|
|||
|
||||
Overview of meetings and agendas for 2019
|
||||
|
||||
## Sep 11, 2019
|
||||
|
||||
[C# Language Design Notes for Sep 11, 2019](LDM-2019-09-11.md)
|
||||
|
||||
1. Close https://github.com/dotnet/roslyn/issues/37313
|
||||
2. Triage new proposed [9.0 features](https://github.com/dotnet/csharplang/issues?q=is%3Aopen+is%3Aissue+no%3Amilestone+label%3A%22Proposal+champion%22)
|
||||
2. Finish triaging away [8.X milestone](https://github.com/dotnet/csharplang/issues?q=is%3Aopen+is%3Aissue+label%3A%22Proposal+champion%22+milestone%3A%228.X+candidate%22)
|
||||
|
||||
## Sep 4, 2019
|
||||
|
||||
[C# Language Design Notes for Sep 04, 2019](LDM-2019-09-04.md)
|
||||
[C# Language Design Notes for Sep 4, 2019](LDM-2019-09-04.md)
|
||||
|
||||
1. AllowNull on properties https://github.com/dotnet/roslyn/issues/37313
|
||||
|
||||
|
|
Loading…
Reference in a new issue