Skip to content

Support Union conversion to a nullable value type#81932

Merged
AlekseyTs merged 6 commits into
dotnet:features/Unionsfrom
AlekseyTs:Unions_09
Jan 13, 2026
Merged

Support Union conversion to a nullable value type#81932
AlekseyTs merged 6 commits into
dotnet:features/Unionsfrom
AlekseyTs:Unions_09

Conversation

@AlekseyTs

Copy link
Copy Markdown
Contributor

@AlekseyTs

Copy link
Copy Markdown
Contributor Author

@RikkiGibson, @333fred, @dotnet/roslyn-compiler Please review

@333fred 333fred left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Haven't reviewed the tests yet, had a few questions on binding impl.

Comment thread src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs Outdated
Comment thread src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs Outdated
Comment thread src/Compilers/CSharp/Portable/BoundTree/BoundNode.cs Outdated
Comment thread src/Compilers/CSharp/Portable/BoundTree/BoundNode.cs Outdated

@333fred 333fred left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Done review pass (commit 3). A couple of test comments/suggestions.

}
";
var comp = CreateCompilation([src, IUnionSource], options: TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: "int {10}").VerifyDiagnostics();

@333fred 333fred Jan 9, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should we log an open question for LDM to verify that this is the behavior we'd prefer to happen, rather than getting an error that explicit conversion requires a cast? #Resolved

@AlekseyTs AlekseyTs Jan 10, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Should we log an open question for LDM to verify that this is the behavior we'd prefer to happen, rather than getting an error that explicit conversion requires a cast?

I don't think so. This behavior was already discussed by the working group and was tentatively approved. The question is already in the speclet, but I am planning to update the "Converions" section as well soon.

Comment thread src/Compilers/CSharp/Test/CSharp15/UnionsTests.cs
@AlekseyTs AlekseyTs requested review from a team and 333fred January 10, 2026 20:05
@AlekseyTs

Copy link
Copy Markdown
Contributor Author

@RikkiGibson, @333fred, @dotnet/roslyn-compiler Please review

@AlekseyTs

Copy link
Copy Markdown
Contributor Author

@RikkiGibson, @dotnet/roslyn-compiler For a second review

1 similar comment
@AlekseyTs

Copy link
Copy Markdown
Contributor Author

@RikkiGibson, @dotnet/roslyn-compiler For a second review

@AlekseyTs AlekseyTs requested a review from a team January 13, 2026 14:16
@RikkiGibson

Copy link
Copy Markdown
Member

Taking a look now.

Comment thread src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs
#endif
foreach (var ctor in toType.InstanceConstructors)
{
if (ctor.OriginalDefinition == (object)constructor.OriginalDefinition)

@RikkiGibson RikkiGibson Jan 13, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why is there a need to search for a constructor at this point, instead of using the one from the analysis.Operator?

When is a constructor's OriginalDefinition != this? They can't have type parameters, right? #Resolved

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Why is there a need to search for a constructor at this point, instead of using the one from the analysis.Operator?

Type arguments of the type could be updated with nullability and that might have effect on the constructor's signature.

When is a constructor's OriginalDefinition != this?

Always, this is not a method symbol.

They can't have type parameters, right?

The containing type might.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Always, this is not a method symbol.

I was intending to refer to a case where, for example, (object)ctor.OriginalDefinition != ctor. It looks like ctor is a method symbol in that case.

It feels like if the goal is to ensure that the constructor is updated based on a type argument reinference of the toType, then, something like constructor = (MethodSymbol)AsMemberOfType(toType, constructor); would more closely resemble how we accomplish that in other places in NullableWalker.

Comment thread src/Compilers/CSharp/Test/CSharp15/UnionsTests.cs Outdated
static S1? Test2()
{
System.Console.Write(""2-"");
return (S1?)default;

@RikkiGibson RikkiGibson Jan 13, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: I think it would be good if the expectedOutput, reflected whether a "null System.Nullable", versus a "non-null System.Nullable, containing S1 with null value", is produced, in cases 2/3/4. I think I see what is happening from the IL baseline, but, some console output would be helpful, in my opinion. This is not blocking. #Resolved

var s = new S1(x);
x.Value.ToString();
}
return default;

@RikkiGibson RikkiGibson Jan 13, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should there be a warning here? It feels like if T is non-nullable reference type, then, the caller wouldn't be warned that the returned union may have a null Value, when switching on it without a null case, for example. It's possible I misunderstood something, or, that this kind of gap is expected, and we aren't able to add the desired "smarts" for a union struct type.
#Pending

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I don't think the spec requires a warning here. I'll capture this as a design question.

@RikkiGibson RikkiGibson left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM, with some non-blocking comments.

@AlekseyTs AlekseyTs enabled auto-merge (squash) January 13, 2026 19:35
@AlekseyTs AlekseyTs merged commit 99ed133 into dotnet:features/Unions Jan 13, 2026
24 of 25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants