Skip to content

Conversation

@Metalaka
Copy link

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
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
var comp = CreateCompilation(src);
comp.VerifyDiagnostics(
// (11,30): warning CS8629: Nullable value type may be null.
// int nonNullableInt = result.Value; // warn expected
Copy link
Member

Choose a reason for hiding this comment

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

nit: this comment doesn't correspond to the original source (there is no // warn expected there).
This applies to a few other places in this file too

// (11,36): warning CS8600: Converting null literal or possible null value to non-nullable type.
// string nonNullableString = result; // warn
Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "result")
.WithLocation(11, 36));
Copy link
Member

Choose a reason for hiding this comment

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

Consider leaving the diagnostics formatted exactly as they come from the test output - i.e., with the .WithLocation on the same line as Diagnostic(. To keep the style consistent but also simpler to update if diagnostics change in the future (and resulting in smaller diffs).

Copy link
Author

Choose a reason for hiding this comment

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

It's now done for the entire file, Thanks

@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

@RikkiGibson
Copy link
Member

/pr-val d847a89

@github-actions
Copy link
Contributor

View PR Validation Run triggered by @RikkiGibson

Parameters
  • Validation Type: pr-val
  • Pipeline ID: 8972
  • Pipeline Version: main
  • PR Number: 82146
  • Commit SHA: d847a89b4ea648d8dc72e7c4ab587a35d42a1757
  • Source Branch: fix/76886-await-nullable-value-type
  • Target Branch: main
  • Build ID: 13213181
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.

3 participants