Repository navigation
Union types reference: pet is Pet note is wrong, and the null-handling sample implies a warning that never appears #55951
Description
Activity
This matches the feature specification, which says the union instance is tested first and then its value.
Note that the design has changed and, according to the current rules, a type pattern is never matched against the union instance. See https://github.com/dotnet/csharplang/blob/main/proposals/csharp-15.0/unions.md#type-pattern
- added🏁 Release: .NET 11Work related to .NET 11Work related to .NET 11and removed⌚ Not TriagedNot triagedNot triaged
on Sep 11, 2026 - moved this to 🔖 Ready in dotnet/docs September 2026 sprint project
on Sep 11, 2026 - added🗺️ mapQUESTOnly used as a way to mark an issue as updated for quest. RepoMan should instantly remove it.Only used as a way to mark an issue as updated for quest. RepoMan should instantly remove it.
on Sep 11, 2026 - added🗺️ reQUESTTriggers an issue to be imported into Quest.Triggers an issue to be imported into Quest.
on Sep 11, 2026 - removed🗺️ mapQUESTOnly used as a way to mark an issue as updated for quest. RepoMan should instantly remove it.Only used as a way to mark an issue as updated for quest. RepoMan should instantly remove it.
on Sep 11, 2026 Thanks, you're right. Item 1 was based on RC 1 and on a copy of the spec from before dotnet/csharplang#10302. RC 1 still implements the old rule, so there
pet is Petis always true with CS0183. From dotnet/roslyn#84897 onward it is CS8121. So the wording I suggested in item 1 is wrong.The existing note is still inaccurate, though: "typically doesn't match" implies a runtime
false, but under the current rulespet is Petdoes not compile. Suggested wording: a type pattern on a union tests only itsValue, sopet is Petis a compile-time error; to check for a value, usepet.Value is not nullorpet is not null.Item 2 is unaffected.
- added📌 seQUESTeredIdentifies that an issue has been imported into Quest.Identifies that an issue has been imported into Quest.and removed🗺️ reQUESTTriggers an issue to be imported into Quest.Triggers an issue to be imported into Quest.
on Sep 12, 2026 - added🗺️ mapQUESTOnly used as a way to mark an issue as updated for quest. RepoMan should instantly remove it.Only used as a way to mark an issue as updated for quest. RepoMan should instantly remove it.
on Sep 17, 2026 - removed🗺️ mapQUESTOnly used as a way to mark an issue as updated for quest. RepoMan should instantly remove it.Only used as a way to mark an issue as updated for quest. RepoMan should instantly remove it.
on Sep 17, 2026
Metadata
Metadata
Assignees
Labels
Type
Projects
- StatusShow more project fields🔖 Ready
Page: https://learn.microsoft.com/dotnet/csharp/language-reference/builtin-types/union
1. "a pattern like
pet is Pettypically doesn't match"The note under Union pattern matching says this pattern typically doesn't match, because
Petis tested against the union's contents. On .NET 11 RC 1 the result is the opposite: it always matches, including whenValueisnull. The compiler also flags it as a constant result:This matches the feature specification, which says the union instance is tested first and then its value. The note should say that
pet is Petis always true, and so is useless as a has-a-value test.pet.Value is not nullorpet is not nullare the correct checks.2. The Union exhaustiveness null-handling sample
The text says that when
Value's null state is "maybe null", you must also handlenullto avoid a warning. It then says this can happen when the union expression is the default value, "as shown in the preceding sample" (Pet pet = default;).But in that sample (and whenever no case type is nullable), the compiler gives no warning if the
nullarm is removed. This is consistent with the Nullability rules further down the same page:Valuedefaults to "not null" when no case type is nullable. The compiler team confirmed that rule as the current design in dotnet/roslyn#85054. Without the arm, the switch compiles clean and then throws at runtime:The same holds for an array slot (
new Pet[1][0]), an unassigned field, and a parameter.CS8655appears only when a case type is nullable, or after the code has already testedis null.Suggested wording: say that the
nullarm is needed for correctness whenever a union can bedefault(fields, array elements,default(T)), and that the compiler will not ask for it unless a case type is nullable. The current text implies a safety net that doesn't exist.Environment: .NET SDK
11.0.100-rc.1.26425.128,<Nullable>enable</Nullable>, Windows 11 x64.Associated WorkItem - 632609
Both items as a runnable project: https://github.com/robertodalmonte/csharp-unions-probed/tree/main/probes/QRepro3 (
dotnet buildshows the diagnostics,dotnet runthe null-state behaviour). The RC 1 vs post-#10302 rule forpet is Petis written up at https://github.com/robertodalmonte/csharp-unions-probed/blob/main/docs/03-pattern-matching.md.