- Partial method signature matching
- Null-conditional handling of the nullable suppression operator
- Annotating IEnumerable.Cast
- Nullability warnings in user-written record code
- Tuple deconstruction mixed assignment and declaration
- "Now with warning waves it's a foam-covered baseball bat to hit people with"
There is a question about what amount of signature matching is required for method signatures, both as part of the expanded partial methods in C# 9
and for the new nint
feature in C# 9. Currently, our rules around what has to match and what does not are confusing: tuple names must match,
dynamic
/object
do not have to match, we warn when there are unsafe nullability conversions, and other differences are allowed (including parameter
names). We could lean on a warning wave here and make our implementation more consistent with the following general rules:
- If there are differences in the CLR signature, we error (as we cannot emit code at all!)
- If there are differences in the syntactic signature, we warn (even for safe nullability changes).
While we like this proposal in general, we have a couple of concerns around compatibility. Tuple names erroring is existing behavior, and if we loosen that to a general warning that would need to be gated behind language version, as you could write code with a newer compiler in C# 8 mode that does not compile with the C# 8 compiler. This complicates implementation, and to make it simple we lean towards just leaving tuple name differences as an error. We also want to make sure that nullability is able to differ by obliviousness: a common case for generators is that either the original signature or the implementation will be unaware of nullable, and we don't want to break this scenario such that either both the user and generator must be nullable aware, or the must both be nullable unaware.
We also considered an extension to these rules where we make the new rules always apply to the new enhanced partial methods, regardless of warning level. However, we believe that this would result in a complicated user experience and would make the mental model harder to understand.
- We will keep the existing error that tuple names must match.
- We will keep the existing warnings about unsafe nullability differences.
- We will add a new warning wave warning for all other syntactic differences between partial method implementations.
a. This includes differences like parameter names and
dynamic
/object
b. This includes nullability differences where both contexts are nullable enabled, even if the difference is supposedly "safe" (acceptingnull
where it is not accepted today). c. If nullability differs by enabled state (one part is enabled, the other part is disabled), this will be allowed without warning.
This is a spec bug that shipped with C# 8, where the !
operator does not behave as a user would expect. Members of the LDT believe that this is broken
on the same level as the for
iterator variable behavior that was changed in C# 5, and we believe that we should take a similar breaking change to fix
the behavior here. We have made a grammatical proposal for adjusting how null-conditional statements are parsed, and there was general agreement that this
proposal is where we want to go. The only comment is that null_conditional_operations_no_suppression
should be renamed to avoid confusion, as there can
be a null suppression inside the term, just not at the end. A better name would be null_conditional_operations_no_final_suppression
.
Accepted, with the above rename. Will get a compiler developer assigned to implement this ASAP.
In .NET 5, the Cast<TResult>
method was annotated on the return to return IEnumerable<TResult?>
, which means that regardless of whether the input
enumerable can contain null
elements, the returned enumerable would be considered to contain null
s. This resulted in some spurious warnings when
upgrading roslyn to use a newer version of .NET 5. However, the C# in general lacks the ability to properly annotate this method for a combination of
reasons:
- There is no way to express that the nullability of one type parameter depends on the nullability of another type parameter.
- Even if there was a way to express 1,
Cast
is an extension method onIEnumerable
, notIEnumerable<T>
, because C# does not have partial type inference to make writing code in this scenario better.
Given this, we have a few options:
- Leave the method as is, and possibly enhance the compiler/language to know about this particular method. This is analogous to the changes we're
considering with
Where
, but it feels like a bad solution as it's not generalizable. - Make the method return
TResult
, unannotated. The issue with this is that it effectively means the method might actually lie: there is no way to ensure that the method actually returns a non-null result if a non-nullTResult
is provided as a type, given that nullability is erased in the implementation. We're concerned that this could make the docs appear to lie, which we think would also give a bad experience. - Convert
Cast
back to being unannotated. This seems to be compromise that both sides can agree on: analyzers can flag use of the unannotated API to help users, and spurious warnings get suppressed. It also matches the behavior ofIEnumerator.Current
, and means that the behavior offoreach
loops over such a list behave in a consistent manner.
The BCL will make Cast
and a few related APIs an oblivious API.
The question here is on whether we should warn users when manually-implemented methods and properties for well-known members in a record
should warn
when nullability is different. For example, if their Equals(R other)
does not accept null
. There was no debate on this.
we'll check user-defined EqualityContract
, Equals
, Deconstruct
, ... methods on records for nullability safety issues in their declaration. For
example, EqualityContract
should not return Type?.
We've discussed this feature in the past (https://github.com/dotnet/csharplang/blob/master/meetings/2016/LDM-2016-11-30.md#mixed-deconstruction), and we liked it then but didn't think it would fit into C# 7. It's been in Any Time since, and now we have a community PR. We have no concerns with moving forward with the feature.
Let's try and get this into C# 10.