Open issue: nullability analysis of collection expressions
jcouv opened this issue · comments
In a scenario like the following:
object? maybeNull = null;
C<object> c1 = [maybeNull];
class C<T> : IEnumerable<T>
{
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null!;
IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null!;
public void Add(T t) { }
}
There are three potential reasons to warn:
- Language conversion rule: we're converting a
maybeNull
element to anobject!
iteration type - Canonical lowering: we're passing a
maybeNull
argument to theAdd(object!)
method - Potential lowering strategy: if we used some alternative lowering strategy, such as using
AddRange(IEnumerable<object!>)
orCopyTo(List<object!>, int)
, we'd be passingIEnumerable<object?>
orList<object?>
arguments to those methods.
Here's the language rule relevant to # 1:
The following implicit collection expression conversions exist from a collection expression:
...
To a struct or class type that implements System.Collections.Generic.IEnumerable where:
For each element Ei there is an implicit conversion to T.
...
We're not getting a clear agreement on prioritizing # 1 (analyze conversion rule) vs. # 2 (analyze canonical lowering), so we'll wait for a decision from LDM. Maybe we should warn for both reasons.
We're leaning to avoid # 3 (analyze optimized lowerings) as well-behaved APIs should behave like the canonical APIs.
If we just did the first check, then the following would warn:
C<object> c1 = [maybeNull]; // warning because the iteration type is `object!` but the element is maybe-null `object`
class C<T> : IEnumerable<T>
{
...
public void Add(T? t) { }
}
If we just did the second check, then the following would warn:
C<object> c1 = [maybeNull]; // warning because we're calling `Add(object!)` with a maybe-null `object` argument
class C<T> : IEnumerable<T?>
{
...
public void Add(T t) { }
}
As a user, either warnings would be fine. I'm getting a NRT warning, and it's effectively telling me that object!
and object?
are going to be a problem. That's literally all that matters, and the exact verbiage isn't really going to matter to me :)
How about a combination of option 1 with another language rule - if it has IEnumerable<T>
and void Add(T? item) { ... }
, produce a new NRT warning about inconsistent surface area?
class C<T> : IEnumerable<T>
{
...
public void Add(T? t) { }
}
C<object> c1 = [maybeNull]; // 2 warnings: the iteration type is `object!` but the element is maybe-null `object`,
// and `Add` is incompatible with `IEnumerable<>`
Discussed in LDM 11/15. In short, we're going with option 1 (check conversion of elements to iteration type).