Add meeting notes for 2019-02-13
This commit is contained in:
parent
4e3f2e4ea5
commit
92d6ef6cc3
178
meetings/2019/LDM-2019-02-13.md
Normal file
178
meetings/2019/LDM-2019-02-13.md
Normal file
|
@ -0,0 +1,178 @@
|
||||||
|
# C# Language Design for February 13th, 2019
|
||||||
|
|
||||||
|
## Agenda
|
||||||
|
|
||||||
|
Nullable Reference Types: Open LDM Issues https://github.com/dotnet/csharplang/issues/2201
|
||||||
|
|
||||||
|
## Discussion
|
||||||
|
|
||||||
|
### Track assignments through `ref` with conditional expressions
|
||||||
|
|
||||||
|
What is the nullability of `ref` variables when assigned through conditional expressions?
|
||||||
|
|
||||||
|
```cs
|
||||||
|
string? x = "";
|
||||||
|
string? y = "";
|
||||||
|
(b ? ref x : ref y) = null;
|
||||||
|
x.ToString(); // warning?
|
||||||
|
y.ToString(); // warning?
|
||||||
|
```
|
||||||
|
|
||||||
|
```cs
|
||||||
|
string? x = null;
|
||||||
|
string? y = null;
|
||||||
|
(b ? ref x : ref y) = "";
|
||||||
|
```
|
||||||
|
|
||||||
|
One option is to assume nullability after a `ref` has been taken to a variable. However,
|
||||||
|
that would mean that a `ref` variable declared non-nullable could become nullable, which
|
||||||
|
seems too heavy-handed.
|
||||||
|
|
||||||
|
Similarly, disabling flow analysis for variables which are taken as the target of a `ref`
|
||||||
|
feels like violating our model, which is largely based on flow analysis.
|
||||||
|
|
||||||
|
Alias analysis, on the other hand, seems to complicated and any reliable implementation
|
||||||
|
would be too difficult for users to understand.
|
||||||
|
|
||||||
|
**Conclusion**
|
||||||
|
|
||||||
|
Let's reach a middle ground. Assignment between any two identifiers copies
|
||||||
|
the state and is then tracked separately. This is also true for `ref` locals.
|
||||||
|
So,
|
||||||
|
|
||||||
|
```C#
|
||||||
|
string? x = "";
|
||||||
|
string? y = "";
|
||||||
|
(b ? ref x : ref y) = null;
|
||||||
|
x.ToString(); // warning
|
||||||
|
y.ToString(); // warning
|
||||||
|
```
|
||||||
|
|
||||||
|
But the equivalent using `ref` locals does not.
|
||||||
|
|
||||||
|
```C#
|
||||||
|
string? x = "";
|
||||||
|
string? y = "";
|
||||||
|
if (b)
|
||||||
|
{
|
||||||
|
ref string? rx = ref x;
|
||||||
|
rx = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ref string? ry = ref y;
|
||||||
|
ry = null;
|
||||||
|
}
|
||||||
|
x.ToString(); // no warning
|
||||||
|
y.ToString(); // no warning
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nullability of conditional access with unconstrained type parameters
|
||||||
|
|
||||||
|
What is the nullability of `x?.F()`?
|
||||||
|
|
||||||
|
```cs
|
||||||
|
class C<T, U>
|
||||||
|
where T : U
|
||||||
|
where U : C<T, U>?
|
||||||
|
{
|
||||||
|
static void M(U x)
|
||||||
|
{
|
||||||
|
U y = x?.F();
|
||||||
|
T z = x?.F();
|
||||||
|
}
|
||||||
|
T F() => throw null;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This question seems interesting even without the type parameters and also
|
||||||
|
contains a nested question about reachability.
|
||||||
|
|
||||||
|
Is the `null` case of `?.` reachable even if the expression is non-nullable?
|
||||||
|
And if it is, what is the null state of the LHS?
|
||||||
|
|
||||||
|
```C#
|
||||||
|
string x = "";
|
||||||
|
x?.ToString(); // warning?
|
||||||
|
```
|
||||||
|
|
||||||
|
**Conclusion**
|
||||||
|
|
||||||
|
The `null` case of `?.` is always reachable, meaning the result is always
|
||||||
|
maybe null e.g.,
|
||||||
|
|
||||||
|
```C#
|
||||||
|
var y = x?.M(); // y is maybe null here, if possible
|
||||||
|
```
|
||||||
|
|
||||||
|
Moreover, the LHS is considered maybe null in the null branch, so by normal
|
||||||
|
flow analysis, after the expression is evaluated a variable on the LHS will
|
||||||
|
be considered maybe null.
|
||||||
|
|
||||||
|
```C#
|
||||||
|
string x = "";
|
||||||
|
x?.ToString(); // warning that x is maybe null
|
||||||
|
```
|
||||||
|
|
||||||
|
### `!` operator on L-values
|
||||||
|
|
||||||
|
Where is `!` allowed?
|
||||||
|
|
||||||
|
* `M(out x!);` (note this also definitely assigns to `x` through the `!`)
|
||||||
|
|
||||||
|
* `M(out (x!));`
|
||||||
|
|
||||||
|
* `M(out (RefReturning()!));`
|
||||||
|
|
||||||
|
* `x! = y;`
|
||||||
|
|
||||||
|
* `M(out string x!);`
|
||||||
|
|
||||||
|
Current implementation is to allow in `out` scenarios, but disallow in assignment scenarios.
|
||||||
|
|
||||||
|
We dislike allowing it in regular assignment. We like allowing it in simple
|
||||||
|
`out` parameters. We're ambivalent on `M(out string x!)`, but it's
|
||||||
|
not easily representable in the syntax model and is very similar to the
|
||||||
|
related feature `parameter!`, with the opposite meaning.
|
||||||
|
|
||||||
|
**Conclusion**
|
||||||
|
|
||||||
|
Only allow `!` in simple `out` parameters with no declaration.
|
||||||
|
|
||||||
|
### `is` nullability in `false` case
|
||||||
|
|
||||||
|
See [dotnet/roslyn#30297](https://github.com/dotnet/roslyn/issues/30297)
|
||||||
|
|
||||||
|
```cs
|
||||||
|
string? s = null;
|
||||||
|
if (!(s is object)) { s.ToString(); /* could warn? */ }
|
||||||
|
if (!(s is string)) { s.ToString(); /* could warn? */ }
|
||||||
|
```
|
||||||
|
|
||||||
|
There are variants of this scenario with `string!` and `string~`. Should `is`
|
||||||
|
update the nullability in both branches or should the one branch be treated
|
||||||
|
as unreachable?
|
||||||
|
|
||||||
|
The problematic code is probably more like:
|
||||||
|
|
||||||
|
```C#
|
||||||
|
void M(string s)
|
||||||
|
{
|
||||||
|
if (s is IComparable t)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
s.ToString(); // warning
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Here the user may not have meant to do a null check, but gets the
|
||||||
|
side-effects of doing so anyway.
|
||||||
|
|
||||||
|
**Conclusion**
|
||||||
|
|
||||||
|
It seems important to respect a deliberate null check from the
|
||||||
|
user, even if the input variable is non-nullable. As a next step we
|
||||||
|
need to define exactly which tests we think are "deliberate" null
|
||||||
|
checks. For instance, `x is null` is certainly a null check, but
|
||||||
|
pattern matching may or may not be a *deliberate* null check, even
|
||||||
|
if the code contains a null check.
|
|
@ -36,14 +36,16 @@
|
||||||
|
|
||||||
- Nullable Reference Types: Open LDM Issues https://github.com/dotnet/csharplang/issues/2201
|
- Nullable Reference Types: Open LDM Issues https://github.com/dotnet/csharplang/issues/2201
|
||||||
|
|
||||||
## Feb 13, 2019
|
|
||||||
|
|
||||||
Nullable Reference Types: Open LDM Issues https://github.com/dotnet/csharplang/issues/2201
|
|
||||||
|
|
||||||
# C# Language Design Notes for 2019
|
# C# Language Design Notes for 2019
|
||||||
|
|
||||||
Overview of meetings and agendas for 2019
|
Overview of meetings and agendas for 2019
|
||||||
|
|
||||||
|
## Feb 13, 2019
|
||||||
|
|
||||||
|
[C# Language Design Notes for Feb 13, 2019](LDM-2019-02-13.md)
|
||||||
|
|
||||||
|
- Nullable Reference Types: Open LDM Issues https://github.com/dotnet/csharplang/issues/2201
|
||||||
|
|
||||||
## Jan 23, 2019
|
## Jan 23, 2019
|
||||||
|
|
||||||
[C# Language Design Notes for Jan 23, 2019](LDM-2019-01-23.md)
|
[C# Language Design Notes for Jan 23, 2019](LDM-2019-01-23.md)
|
||||||
|
|
Loading…
Reference in a new issue