diff --git a/meetings/2019/LDM-2019-02-13.md b/meetings/2019/LDM-2019-02-13.md new file mode 100644 index 0000000..10071c6 --- /dev/null +++ b/meetings/2019/LDM-2019-02-13.md @@ -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 + where T : U + where U : C? +{ + 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. \ No newline at end of file diff --git a/meetings/2019/README.md b/meetings/2019/README.md index a18d630..23d5aa3 100644 --- a/meetings/2019/README.md +++ b/meetings/2019/README.md @@ -36,14 +36,16 @@ - 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 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 [C# Language Design Notes for Jan 23, 2019](LDM-2019-01-23.md)