dotnet / dotnet/roslyn

Incorrect nullable type inference in collection expressions

Open
#79,807 4 comments 0 reactions 1 assignee Claimed by @RikkiGibson View on GitHub
Area-Compilers Bug Feature - Extension Everything Feature - Nullable Reference Types
Dominant language
C#
Stars
20.7k
Forks
4.3k
PR merge metrics
PR metrics pending

Description

**Version Used**: 2e23a0df5bf8d49f6bf856287b0eb10de08f97d9

**Steps to Reproduce**:

Compile and run the following code:
```csharp
using System;
using System.Collections;
using System.Collections.Generic;
#nullable enable

List obj = new() { null };
C c = [obj]; // no warnings

foreach (List list in c)
foreach (string s in list)
{
Console.WriteLine(s.Length); // NullReferenceException crash
}

// both lines below would report
// CS8620 Argument of type 'List' cannot be used for parameter 't' of type 'List' in 'void Ext.Add(C c, List t)' due to differences in the nullability of reference types.
//c.Add(obj);
//c = new() { obj };

class C : IEnumerable>
{
internal List _list = new();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator> GetEnumerator() => new List>() { _list }.GetEnumerator();
}

static class Ext
{
public static void Add(this C c, List t) => c._list.AddRange(t);
}
```

**Diagnostic Id**:

`CS8620`

**Expected Behavior**:

A nullability mismatch warning is reported for the collection expression.
The warning is the same for both implicit `.Add` call in the collection expression and explicit `.Add` call.

**Actual Behavior**:
No warnings at all are reported in the code above. It crashes with a `NullReferenceException` at runtime.

**Notes**:
I believe that the issue is incorrect nullability type reinference for the implicit `.Add` call in the collection expression. It looks like Roslyn infers the call to be `Ext.Add(C c, List t)` which makes the element argument match. The incorrect inference should result in an mismatching receiver nullability, but Roslyn fails to verify it as reported in https://github.com/dotnet/roslyn/issues/79803

Here's a slightly modified example that led me to this conclusion - Roslyn correctly reports `CS8620` if the parameter is fixed to the correct nullability:
```csharp
using System;
using System.Collections;
using System.Collections.Generic;
#nullable enable

List obj = new() { null };
C c2 = [obj]; // CS8620

class C : IEnumerable>
{
internal List _list = new();
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator> GetEnumerator() => new List>() { _list }.GetEnumerator();
}

static class Ext
{
public static void Add(this C c, List t) => c._list.AddRange(t);
}
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.