csharplang/meetings/2020/LDM-2020-01-06.md
2020-01-13 14:08:58 -08:00

4 KiB

C# Language Design Meeting for Jan. 6, 2020

Agenda

  1. Use attribute info inside method bodies
  2. Making Task-like types covariant for nullability
  3. Casting to non-nullable reference type
  4. Triage

Discussion

Use attribute info inside method bodies

Examples:

bool TryGetValue<T>([MaybeNullWhen(false)]out T t)
{
    return other.TryGetValue(out t); // currently warns
}
[return: MaybeNull]
T GetFirstOrDefault<T>()
{
    return null; // currently warns
}

Overriding/implementation

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:

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.

Accepted for C# 9.0 investigation.