Skip to content

Fix/76886 await nullable value type#82146

Merged
RikkiGibson merged 12 commits intodotnet:mainfrom
Metalaka:fix/76886-await-nullable-value-type
Mar 11, 2026
Merged

Fix/76886 await nullable value type#82146
RikkiGibson merged 12 commits intodotnet:mainfrom
Metalaka:fix/76886-await-nullable-value-type

Conversation

@Metalaka
Copy link
Contributor

Summary

Fixes a bug where await on a Nullable<T> (e.g., Task<int?>) was incorrectly treated as NotNull by the nullable flow analysis.

  • Actual Behavior: Awaiting a Task<int?> resulted in a NotNull flow state, causing the compiler to miss warnings when the result was potentially null.
  • Expected Behavior: The nullability of the awaited result should follow the return type of the awaiter's GetResult method.

Description

In NullableWalker.VisitAwaitExpression, the compiler previously assumed that any await result of a ValueType was inherently non-null.

if (node.Type.IsValueType || node.HasErrors || awaitableInfo.GetResult is null)
{
    SetNotNullResult(node);
}

This PR updates VisitAwaitExpression to explicitly check if the type is a Nullable<T> before deciding to mark it as NotNull. If it is a Nullable<T>, the flow analysis now correctly uses the nullability information from the GetResult method of the awaiter.

Impact on Existing Code

By correctly identifying the result of await Task<T?> as potentially null, this change will introduce new CS8629 (Nullable value type may be null) warnings in existing code where these potential nulls were previously ignored by the flow analysis. This is intended behavior to ensure correct nullable safety.

Fixed Issues

Fixes #76886

Tests Added

Added new test cases to NullableReferenceTypesTests.cs:

Regression Tests (validating the fix for #76886):

  • AwaitedNullableValueTypeAssignedToNonNullable_ProducesCS8629
  • AwaitResultNullableValueTypeAssignedToNonNullable_ProducesCS8629

Baseline Tests (validating actual/expected behavior for non-awaited types):

  • AwaitedNullableTypeAssignedToNonNullable_ProducesCS8600
  • AwaitResultNullableTypeAssignedToNonNullable_ProducesCS8600
  • NullableValueTypeAssignedToNonNullable_ProducesCS8629

@Metalaka Metalaka requested a review from a team as a code owner January 25, 2026 19:26
@dotnet-policy-service dotnet-policy-service bot added the Community The pull request was submitted by a contributor who is not a Microsoft employee. label Jan 25, 2026
@Metalaka
Copy link
Contributor Author

@dotnet-policy-service agree

Copy link
Member

@RikkiGibson RikkiGibson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM aside from suggestion to delete HasErrors check.

Co-authored-by: Rikki Gibson <rikkigibson@gmail.com>
@RikkiGibson RikkiGibson requested a review from a team January 30, 2026 01:02
@RikkiGibson
Copy link
Member

@dotnet/roslyn-compiler for second review

@RikkiGibson RikkiGibson requested a review from a team January 30, 2026 03:46
@RikkiGibson
Copy link
Member

It looks like the change revealed legitimate new nullable warnings in the compiler source code. Here are my recommendations for what to try to resolve the warnings

diff --git a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs
index f4650be4a5a..8806c2c248e 100644
--- a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs
+++ b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs
@@ -167,6 +167,7 @@ private static async Task VerifyInheritanceTargetAsync(Workspace workspace, Test
             for (var i = 0; i < actualDocumentSpans.Length; i++)
             {
                 var docSpan = await actualDocumentSpans[i].TryRehydrateAsync(workspace.CurrentSolution, CancellationToken.None);
+                Assert.True(docSpan.HasValue);
                 Assert.Equal(expectedDocumentSpans[i].SourceSpan, docSpan.Value.SourceSpan);
                 Assert.Equal(expectedDocumentSpans[i].Document.FilePath, docSpan.Value.Document.FilePath);
             }
diff --git a/src/Workspaces/Core/Portable/Rename/IRemoteRenamerService.cs b/src/Workspaces/Core/Portable/Rename/IRemoteRenamerService.cs
index 1b4348b70c3..f0984a8ae0d 100644
--- a/src/Workspaces/Core/Portable/Rename/IRemoteRenamerService.cs
+++ b/src/Workspaces/Core/Portable/Rename/IRemoteRenamerService.cs
@@ -136,7 +136,7 @@ internal sealed partial class SymbolicRenameLocations
             serializableLocations.Options,
             locations,
             implicitLocations,
-            referencedSymbols);
+            referencedSymbols!);
     }
 }
 
diff --git a/src/Workspaces/Core/Portable/Rename/LightweightRenameLocations.cs b/src/Workspaces/Core/Portable/Rename/LightweightRenameLocations.cs
index ad9b98cce7f..8891be76f91 100644
--- a/src/Workspaces/Core/Portable/Rename/LightweightRenameLocations.cs
+++ b/src/Workspaces/Core/Portable/Rename/LightweightRenameLocations.cs
@@ -63,7 +63,7 @@ internal sealed partial class LightweightRenameLocations
             Options,
             Locations,
             implicitLocations,
-            referencedSymbols);
+            referencedSymbols!);
     }
 
     /// <summary>

@RikkiGibson
Copy link
Member

@davidwengier there is a pretty good chance that when we take this change, downstream projects will get new nullable warnings. Just a heads up

@dotnet dotnet deleted a comment from github-actions bot Mar 2, 2026
@dotnet dotnet deleted a comment from github-actions bot Mar 2, 2026
@dotnet dotnet deleted a comment from github-actions bot Mar 2, 2026
@dotnet dotnet deleted a comment from github-actions bot Mar 2, 2026
@RikkiGibson
Copy link
Member

Finally got a test insertion going, am seeing new nullable warnings and pushing commits to address, to try and get the VS build all the way through and hopefully see the extent of the impact. https://dev.azure.com/devdiv/DevDiv/_git/VS/pullrequest/713230

Will want to do the same in dotnet/runtime. Have an agent following the steps to validate changes in dotnet/runtime and will see what comes up when I get back to investigating it sometime this week.

…lable-value-type

# Conflicts:
#	src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs
@RikkiGibson
Copy link
Member

Verified VS and dotnet/runtime builds. There were just a few new test warnings in VS and no new warnings in dotnet/runtime.

Co-authored-by: Metalaka <Metalaka@users.noreply.github.com>
@RikkiGibson

This comment was marked as resolved.

@RikkiGibson RikkiGibson requested a review from a team as a code owner March 4, 2026 00:35
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add an entry to our breaking change doc?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me, it's just under the bar. We have had nullable bugs that impacted accuracy before, and, in most cases never added entries to the breaking change doc for them.

However, I do know we have some deadlines for 3xx to make this week, so, to avoid potential disruption I think we should wait for the next band.

@RikkiGibson RikkiGibson enabled auto-merge (squash) March 9, 2026 19:00
@RikkiGibson RikkiGibson merged commit ff1aa5b into dotnet:main Mar 11, 2026
27 of 28 checks passed
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Mar 11, 2026
@Metalaka
Copy link
Contributor Author

Thanks so much @RikkiGibson for the great review and guidance on this 🙌 💖

@Metalaka Metalaka deleted the fix/76886-await-nullable-value-type branch March 11, 2026 23:23
@RikkiGibson
Copy link
Member

@Metalaka thanks for the contribution!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-Compilers Community The pull request was submitted by a contributor who is not a Microsoft employee. VSCode

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Nullability tracking is broken for Nullable<T> with awaits

3 participants