2020-01-06 20:12:29 +01:00
|
|
|
|
|
|
|
# C# Language Design Meeting for Jan. 6, 2020
|
|
|
|
|
|
|
|
## Agenda
|
|
|
|
|
|
|
|
1. Use attribute info inside method bodies
|
|
|
|
1. Making Task-like types covariant for nullability
|
|
|
|
1. Casting to non-nullable reference type
|
|
|
|
1. Triage
|
|
|
|
|
|
|
|
## Discussion
|
|
|
|
|
|
|
|
### Use attribute info inside method bodies
|
|
|
|
|
|
|
|
Examples:
|
|
|
|
|
|
|
|
1.
|
|
|
|
|
|
|
|
```C#
|
2020-01-13 23:08:58 +01:00
|
|
|
bool TryGetValue<T>([MaybeNullWhen(false)]out T t)
|
2020-01-06 20:12:29 +01:00
|
|
|
{
|
|
|
|
return other.TryGetValue(out t); // currently warns
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
2.
|
|
|
|
|
|
|
|
```C#
|
|
|
|
[return: MaybeNull]
|
|
|
|
T GetFirstOrDefault<T>()
|
|
|
|
{
|
|
|
|
return null; // currently warns
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
3.
|
|
|
|
|
|
|
|
Overriding/implementation
|
|
|
|
|
|
|
|
```C#
|
|
|
|
class A<T>
|
|
|
|
{
|
|
|
|
[return: MaybeNull]
|
|
|
|
virtual T M();
|
|
|
|
}
|
|
|
|
|
|
|
|
class B : A<string>
|
|
|
|
{
|
|
|
|
override string? M(); // warns about no [MaybeNull]
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
We don't have a complete design here, but some cases have an intuition about the correct
|
|
|
|
behavior. In overriding, specifically, we need a specification for what it means for an
|
|
|
|
annotation to be "compatible" with each of the attributes. On the other hand, it's not clear what
|
|
|
|
the behavior of `MaybeNullWhenTrue` should be in all cases.
|
|
|
|
|
|
|
|
**Conclusion**
|
|
|
|
|
|
|
|
We'd like to do this if the return on investment seems worth it, but to fully evaluate
|
|
|
|
we need a proposal of the work.
|
|
|
|
|
|
|
|
### Making Task-like objects nullable covariant
|
|
|
|
|
|
|
|
This is a pretty common pain-point, and it's not the first time we special-cased variance,
|
|
|
|
specifically `IEquatable<T>` is treated as nullable contravariant. It's unfortunate that the CLR
|
|
|
|
doesn't have capability to make `Task<T>` full covariant, but handling even nullable alone would
|
|
|
|
be useful. Moreover, if we later get the capability to mark `Task<T>` covariant, this would not
|
|
|
|
harm the effort.
|
|
|
|
|
|
|
|
We also think that there may be some special cases introduced for overload resolution where we
|
|
|
|
consider `Task<T>` as covariant already. If we could reuse that knowledge, that would be useful.
|
|
|
|
|
|
|
|
**Conclusion**
|
|
|
|
|
|
|
|
Let's see if we can dig up the overload resolution changes for Task-like types and try to adapt
|
|
|
|
the same rule for making Task-like types nullable covariant.
|
|
|
|
|
|
|
|
### Casting to non-nullable reference type
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
```C#
|
|
|
|
BoundNode? node = ...;
|
|
|
|
if (node.Kind == BoundKind.Expression)
|
|
|
|
{
|
|
|
|
var x = (BoundExpression)node; // warning if node is nullable
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
The question is if this warning is valuable, or annoying. We've hit this most often in Roslyn
|
|
|
|
when using the pattern `(object)x == null` to do a null check while avoiding the user-defined
|
|
|
|
equality check. This is annoying in the Roslyn codebase, but not very common outside it. On the
|
|
|
|
other hand, there's feeling that when doing the BoundNode to BoundExpression check, which is less
|
|
|
|
common in Roslyn but more common generally, there's agreement that the warning is useful in
|
|
|
|
making the type annotated with the most accurate representation of the null state.
|
|
|
|
|
|
|
|
**Conclusion**
|
|
|
|
|
|
|
|
Keep the warning, no change. We think the warning is valuable for the non-null-check cases. Newer
|
|
|
|
version of C# have features that address the null check problem and Roslyn should move to use `x
|
|
|
|
is null` or similar.
|
|
|
|
|
|
|
|
## Triage
|
|
|
|
|
|
|
|
Three related proposals: #3037, #3038, #377.
|
|
|
|
|
|
|
|
These all deal with the general problem of statements in expressions, especially statements in
|
|
|
|
switch expressions, and switch expression form in statements.
|
|
|
|
|
|
|
|
They don't necessarily require each other, but they fit a lot of the same syntax and semantic
|
|
|
|
space, so we should consider them all together.
|
|
|
|
|
|
|
|
There's also a sketch for how we could unify the syntax forms of all of three proposals, with
|
|
|
|
potential syntax changes.
|
|
|
|
|
|
|
|
**Conclusion**
|
|
|
|
|
|
|
|
We agree that all of these proposals are addressing important scenarios, and some improvement
|
|
|
|
here is valuable. We're not sure where we want to go with generalized statements-in-expressions
|
|
|
|
vs. adding special syntax forms for switch expression/statement.
|
|
|
|
|
|
|
|
We're mainly concerned that if we do switch expression blocks, we want to make sure that the we
|
|
|
|
don't block a future generalization to all expressions. We need to find a generalization that we
|
|
|
|
like, reject a generalization and accept this syntax, or put these improvements on the
|
|
|
|
back-burner if we think that a generalization is possible, we just haven't found it.
|
|
|
|
|
2020-01-13 23:08:58 +01:00
|
|
|
Accepted for C# 9.0 investigation.
|