Skip to content

[main] Source code updates from dotnet/dotnet - #131569

Merged
akoeplinger merged 9 commits into
mainfrom
darc-main-c918dcdc-c972-4939-bb75-49f1a6b99922
Aug 4, 2026
Merged

[main] Source code updates from dotnet/dotnet#131569
akoeplinger merged 9 commits into
mainfrom
darc-main-c918dcdc-c972-4939-bb75-49f1a6b99922

Conversation

@dotnet-maestro

Copy link
Copy Markdown
Contributor

Note

This is a codeflow update. It may contain both source code changes from
the VMR
as well as dependency updates. Learn more here.

This pull request brings the following source code changes

From https://github.com/dotnet/dotnet

New Dependencies

Removed Dependencies

  • Removed 11.0.0-beta.26365.101
    • Microsoft.DotNet.Build.Tasks.Workloads

Updated Dependencies

  • From 5.10.0-1.26365.101 to 5.10.0-1.26379.102
    • Microsoft.CodeAnalysis
    • Microsoft.CodeAnalysis.Analyzers
    • Microsoft.CodeAnalysis.BannedApiAnalyzers
    • Microsoft.CodeAnalysis.CSharp
    • Microsoft.Net.Compilers.Toolset
  • From 11.0.100-preview.7.26365.101 to 11.0.100-rc.1.26379.102
    • Microsoft.CodeAnalysis.NetAnalyzers
    • Microsoft.DotNet.ApiCompat.Task
    • Microsoft.NET.Workload.Emscripten.Current.Manifest-11.0.100.Transport
  • From 11.0.0-beta.26365.101 to 11.0.0-beta.26379.102
    • Microsoft.DotNet.Arcade.Sdk
    • Microsoft.DotNet.Build.Tasks.Archives
    • Microsoft.DotNet.Build.Tasks.Feed
    • Microsoft.DotNet.Build.Tasks.Installers
    • Microsoft.DotNet.Build.Tasks.Packaging
    • Microsoft.DotNet.Build.Tasks.TargetFramework
    • Microsoft.DotNet.Build.Tasks.Templating
    • Microsoft.DotNet.CodeAnalysis
    • Microsoft.DotNet.GenAPI
    • Microsoft.DotNet.GenFacades
    • Microsoft.DotNet.Helix.Sdk
    • Microsoft.DotNet.PackageTesting
    • Microsoft.DotNet.RemoteExecutor
    • Microsoft.DotNet.SharedFramework.Sdk
    • Microsoft.DotNet.XliffTasks
    • Microsoft.DotNet.XUnitExtensions
  • From 23.1.0-alpha.1.26357.1 to 23.1.0-alpha.1.26370.1
    • runtime.linux-arm64.Microsoft.NETCore.Runtime.JIT.Tools
    • runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang
    • runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk
    • runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools
    • runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.JIT.Tools
    • runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang
    • runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk
    • runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools
    • runtime.linux-musl-x64.Microsoft.NETCore.Runtime.JIT.Tools
    • runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang
    • runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk
    • runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools
    • runtime.linux-x64.Microsoft.NETCore.Runtime.JIT.Tools
    • runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang
    • runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk
    • runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools
    • runtime.osx-arm64.Microsoft.NETCore.Runtime.JIT.Tools
    • runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang
    • runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk
    • runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools
    • runtime.osx-x64.Microsoft.NETCore.Runtime.JIT.Tools
    • runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang
    • runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk
    • runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools
    • runtime.win-arm64.Microsoft.NETCore.Runtime.JIT.Tools
    • runtime.win-x64.Microsoft.NETCore.Runtime.JIT.Tools
    • runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang
    • runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk
    • runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools
  • From 0.11.5-preview.26365.101 to 0.11.5-preview.26379.102
    • Microsoft.DotNet.Cecil
  • From 2.9.3-beta.26365.101 to 2.9.3-beta.26379.102
    • Microsoft.DotNet.XUnitConsoleRunner
  • From 11.0.0-preview.7.26365.101 to 11.0.0-rc.1.26379.102
    • Microsoft.NET.Sdk.IL
    • Microsoft.NETCore.App.Ref
    • Microsoft.NETCore.ILAsm
    • runtime.native.System.IO.Ports
    • System.Reflection.Metadata
    • System.Reflection.MetadataLoadContext
    • System.Text.Json
  • From 7.10.0-rc.36601 to 7.10.0-rc.38002
    • NuGet.Frameworks
    • NuGet.Packaging
    • NuGet.ProjectModel
    • NuGet.Versioning
  • From 3.0.0-preview.7.26365.101 to 3.0.0-rc.1.26379.102
    • System.CommandLine
  • From 11.0.0-alpha.1.26353.1 to 11.0.0-alpha.1.26372.1
    • runtime.linux-arm64.Microsoft.NETCore.Runtime.Wasm.Node.Transport
    • runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Wasm.Node.Transport
    • runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Wasm.Node.Transport
    • runtime.linux-x64.Microsoft.NETCore.Runtime.Wasm.Node.Transport
    • runtime.osx-arm64.Microsoft.NETCore.Runtime.Wasm.Node.Transport
    • runtime.osx-x64.Microsoft.NETCore.Runtime.Wasm.Node.Transport
    • runtime.win-arm64.Microsoft.NETCore.Runtime.Wasm.Node.Transport
    • runtime.win-x64.Microsoft.NETCore.Runtime.Wasm.Node.Transport

Associated changes in source repos

Diff the source with this PR branch
darc vmr diff --name-only https://github.com/dotnet/dotnet:813f634ceb016018f5acc9bd3c2b16e17dff4686..https://github.com/dotnet/runtime:darc-main-c918dcdc-c972-4939-bb75-49f1a6b99922

dotnet-maestro Bot added 2 commits July 30, 2026 02:05
Updated Dependencies:
Microsoft.CodeAnalysis, Microsoft.CodeAnalysis.Analyzers, Microsoft.CodeAnalysis.BannedApiAnalyzers, Microsoft.CodeAnalysis.CSharp, Microsoft.Net.Compilers.Toolset (Version 5.10.0-1.26365.101 -> 5.10.0-1.26379.102)
Microsoft.CodeAnalysis.NetAnalyzers, Microsoft.DotNet.ApiCompat.Task, Microsoft.NET.Workload.Emscripten.Current.Manifest-11.0.100.Transport (Version 11.0.100-preview.7.26365.101 -> 11.0.100-rc.1.26379.102)
Microsoft.DotNet.Arcade.Sdk, Microsoft.DotNet.Build.Tasks.Archives, Microsoft.DotNet.Build.Tasks.Feed, Microsoft.DotNet.Build.Tasks.Installers, Microsoft.DotNet.Build.Tasks.Packaging, Microsoft.DotNet.Build.Tasks.TargetFramework, Microsoft.DotNet.Build.Tasks.Templating, Microsoft.DotNet.CodeAnalysis, Microsoft.DotNet.GenAPI, Microsoft.DotNet.GenFacades, Microsoft.DotNet.Helix.Sdk, Microsoft.DotNet.PackageTesting, Microsoft.DotNet.RemoteExecutor, Microsoft.DotNet.SharedFramework.Sdk, Microsoft.DotNet.XliffTasks, Microsoft.DotNet.XUnitExtensions (Version 11.0.0-beta.26365.101 -> 11.0.0-beta.26379.102)
runtime.linux-arm64.Microsoft.NETCore.Runtime.JIT.Tools, runtime.linux-x64.Microsoft.NETCore.Runtime.JIT.Tools, runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.JIT.Tools, runtime.linux-musl-x64.Microsoft.NETCore.Runtime.JIT.Tools, runtime.win-arm64.Microsoft.NETCore.Runtime.JIT.Tools, runtime.win-x64.Microsoft.NETCore.Runtime.JIT.Tools, runtime.osx-arm64.Microsoft.NETCore.Runtime.JIT.Tools, runtime.osx-x64.Microsoft.NETCore.Runtime.JIT.Tools, runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang, runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk, runtime.linux-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools, runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang, runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk, runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools, runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang, runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk, runtime.linux-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools, runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang, runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk, runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools, runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang, runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk, runtime.win-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools, runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang, runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk, runtime.osx-arm64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools, runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Libclang, runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Sdk, runtime.osx-x64.Microsoft.NETCore.Runtime.Mono.LLVM.Tools (Version 23.1.0-alpha.1.26357.1 -> 23.1.0-alpha.1.26370.1)
Microsoft.DotNet.Cecil (Version 0.11.5-preview.26365.101 -> 0.11.5-preview.26379.102)
Microsoft.DotNet.XUnitConsoleRunner (Version 2.9.3-beta.26365.101 -> 2.9.3-beta.26379.102)
Microsoft.NET.Sdk.IL, Microsoft.NETCore.App.Ref, Microsoft.NETCore.ILAsm, runtime.native.System.IO.Ports, System.Reflection.Metadata, System.Reflection.MetadataLoadContext, System.Text.Json (Version 11.0.0-preview.7.26365.101 -> 11.0.0-rc.1.26379.102)
NuGet.Frameworks, NuGet.Packaging, NuGet.ProjectModel, NuGet.Versioning (Version 7.10.0-rc.36601 -> 7.10.0-rc.38002)
System.CommandLine (Version 3.0.0-preview.7.26365.101 -> 3.0.0-rc.1.26379.102)
runtime.linux-arm64.Microsoft.NETCore.Runtime.Wasm.Node.Transport, runtime.linux-musl-arm64.Microsoft.NETCore.Runtime.Wasm.Node.Transport, runtime.linux-x64.Microsoft.NETCore.Runtime.Wasm.Node.Transport, runtime.linux-musl-x64.Microsoft.NETCore.Runtime.Wasm.Node.Transport, runtime.osx-arm64.Microsoft.NETCore.Runtime.Wasm.Node.Transport, runtime.osx-x64.Microsoft.NETCore.Runtime.Wasm.Node.Transport, runtime.win-arm64.Microsoft.NETCore.Runtime.Wasm.Node.Transport, runtime.win-x64.Microsoft.NETCore.Runtime.Wasm.Node.Transport (Version 11.0.0-alpha.1.26353.1 -> 11.0.0-alpha.1.26372.1)

Added Dependencies:
Microsoft.DotNet.Build.Tasks.FileCatalog (Version 11.0.0-beta.26379.102)

Removed Dependencies:
Microsoft.DotNet.Build.Tasks.Workloads (Version 11.0.0-beta.26365.101)
[[ commit created by automation ]]
@dotnet-maestro

Copy link
Copy Markdown
Contributor Author

@github-actions github-actions Bot added the area-codeflow for labeling automated codeflow label Jul 30, 2026
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 5 pipeline(s).
11 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

akoeplinger and others added 2 commits July 31, 2026 01:06
…rences

The new Roslyn toolset compiles `_ = ref x;` as a value discard, emitting
`ldarg; ldind.ref; pop` instead of nothing. In StructureMarshaler<T> those
statements existed only to satisfy IDE0060 for the managed fallback bodies
of JIT intrinsics, whose callers (the Marshal.LayoutTypeMarshalerMethods
delegate path) pass `ref Unsafe.NullRef<CleanupWorkListElement?>()`. The
extra dereference therefore threw NullReferenceException.

Drop the discards and suppress IDE0060 with the pragma idiom already used
elsewhere in this file.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8591ab4e-b1d7-40df-80f0-9a476a7bfd9d
For a union case whose type is the union type itself, the emitted type
pattern is trivially satisfied by the union value, so with the new Roslyn
toolset the designator binds the union instead of its payload. The emitted
deconstructor then returned the same value it was given and
JsonUnionConverter recursed until it threw "possible object cycle".

Emit an arm that reads the payload off the union's Value property instead.
The parser already requires a public `object Value` property for every
source-generated union.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8591ab4e-b1d7-40df-80f0-9a476a7bfd9d
@akoeplinger

akoeplinger commented Jul 30, 2026

Copy link
Copy Markdown
Member

Root cause analysis of the test failures in this codeflow

Both failure clusters in this PR come from the toolset bump (Microsoft.Net.Compilers.Toolset 5.10.0-1.26365.1015.10.0-1.26379.102), not from any repo source change.

The toolset delta is VMR cb8306a6…813f634c…, which maps to Roslyn 18bf2c8709264bac6615856e507eb44ba2a026e2c5198d0a809afb7937f51744e9333889ec6ed017 (60 commits).

1. _ = ref x; now emits ldarg; ldind; pop

Caused by dotnet/roslyn#84523"Adjust IL used for a null check in the compiler-synthesized inline-array helpers" (closing dotnet/roslyn#84518, follow-up to dotnet/roslyn#84488).

In src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs:

 case BoundKind.Parameter:
-    if (used)  // unused parameter has no side-effects
-        EmitParameterLoad((BoundParameter)expression);
+    EmitParameterLoad((BoundParameter)expression, used);
+if (used || parameter.ParameterSymbol.RefKind != RefKind.None)  // unused value parameter has no side-effects

The rule changed from "an unused parameter has no side effects" to "an unused value parameter has no side effects" — reading through a ref is now deliberately side-effecting, since the null check is the intended effect (that is how ThrowIfInlineArrayIsNullRef is synthesized). The PR's test baselines show the newly emitted ldind.* in exactly this position.

In StructureMarshaler<T> (src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs) the _ = ref …; statements existed only to satisfy IDE0060 in the managed fallback bodies of JIT intrinsics. Those bodies are reached through the Marshal.LayoutTypeMarshalerMethods delegate path, which passes ref Unsafe.NullRef<CleanupWorkListElement?>(), so the newly emitted dereference threw NullReferenceException.

This single root cause accounts for the large majority of the failures:

  • System.Runtime.InteropServices.TestsMarshal.Write*_BlittableObject_Roundtrips, UnsafeAddrOfPinnedArrayElement*
  • System.Reflection.Emit.TestsAssemblySaveTypeBuilderAPIsTests.DefineUninitializedDataTest
  • CoreCLR Runtime_90531
  • Interop/COM/ComWrappers/* (via StructureMarshaler.FreeFreeCore)
  • System.Net.SecurityTransportContext_*GetExpectedChannelBindings, TlsSessionTests.ServerSession_ChannelBinding_*, NegotiateAuthenticationKerberosTest.ChannelBindings_*

Fixed by dropping the discards and suppressing IDE0060 with the pragma idiom already used elsewhere in that file. Verified locally: ildasm confirms the ldind.ref is gone, and System.Runtime.InteropServices.Tests runs 2559 tests with 0 failures.

@AlekseyTs — the change looks intentional and the fix on our side is straightforward, but it does silently repurpose _ = ref x; from "no-op discard" into "null check". That idiom is used in dotnet/runtime (and, I would guess, elsewhere) purely to suppress unused-parameter diagnostics, so it may be worth calling out as a behavior change. Related open issues in the same area: dotnet/roslyn#84547 and dotnet/roslyn#84563.

2. Infinite recursion for recursive unions in the System.Text.Json source generator

Caused by the "Try-Both approach" union work that merged in the same window: dotnet/roslyn#84323 (type pattern), dotnet/roslyn#84365 (var/declaration/list patterns), dotnet/roslyn#84418 (recursive patterns), merged to main via dotnet/roslyn#84531.

For a union case whose type is the union type itself, the emitted type pattern is trivially satisfied by the union value, so the designator now binds the union rather than its payload. For example, for union RecursiveNat(bool, RecursiveNat) the generated deconstructor was:

return value switch
{
    null => ((Type?)null, (object?)null),
    bool caseValue0 => (typeof(bool), (object?)caseValue0),
    RecursiveNat caseValue1 => (typeof(RecursiveNat), (object?)caseValue1),  // binds the union, not the payload
};

The IL confirms it: the bool arm calls RecursiveNat::get_Value() and unwraps, while the recursive arm just does ldarg.1. The deconstructor therefore returned the same value it was given, and JsonUnionConverter.OnTryWrite recursed until it threw JsonException: A possible object cycle was detected.

Only the source-generated path was affected (UnionTests_Default, UnionTests_Metadata, and the AsyncStream variants); the reflection-based path passed, since it reads the payload off the Value property directly.

Fixed in the generator by emitting an arm that reads the payload off the union's Value property for that case. The parser already requires a public object Value property on every source-generated union, so this is always available. Verified locally: the full source-gen suite passes (10766 tests) as do the generator unit tests (276).

This one is a genuine semantic change in an in-development feature and is probably worth confirming with the Roslyn union folks — the generator fix is correct either way.

@jjonescz FYI

Remaining CI noise

Several entries in the failure list were explicit agent timeouts and cancellations ("ran longer than the maximum time of 150/180/200 minutes"), which are infrastructure rather than product failures.

Note

This comment was generated with the assistance of GitHub Copilot.

@dotnet-maestro

Copy link
Copy Markdown
Contributor Author

Important

While this PR was open, the source repository has received code changes from this repository (an opposite codeflow merged).
To avoid complex conflicts, the codeflow cannot continue until this PR is closed or merged.

You can continue with one of the following options:

  • Ignore this and merge this PR as usual without waiting for the new changes.
    Once merged, Maestro will create a new codeflow PR with the new changes.
  • Close this PR and wait for Maestro to open a new one with old and new changes included.
    You will lose any manual changes made in this PR.
    You can also manually trigger the new codeflow right away by running:
    darc trigger-subscriptions --id f7901f87-9f24-40d6-9bc1-564863937237
    
  • Force a codeflow into this PR at your own risk if you want the new changes.
    User commits made to this PR might be reverted.
    darc trigger-subscriptions --id f7901f87-9f24-40d6-9bc1-564863937237 --force
    

💡 You may consult the FAQ for more information or tag @dotnet/prodconsvcs for assistance.

@akoeplinger

Copy link
Copy Markdown
Member

@eiriktsarpalis would you mind taking a look at the STJ source generator change? thanks

@akoeplinger

akoeplinger commented Jul 31, 2026

Copy link
Copy Markdown
Member

Also cc @jkotas for the ref change since in #98623 (comment) you said

Filed dotnet/roslyn#72165

I do not expect it to be fixed. C# language model assumes that refs are never null and reading refs is side-effect free. We had a discussion along these lines number of times.

@jkotas

jkotas commented Jul 31, 2026

Copy link
Copy Markdown
Member

@AlekseyTs — the change looks intentional and the fix on our side is straightforward, but it does silently repurpose _ = ref x; from "no-op discard" into "null check". That idiom is used in dotnet/runtime (and, I would guess, elsewhere) purely to suppress unused-parameter diagnostics, so it may be worth calling out as a behavior change

Yes, this is a breaking behavior change in Roslyn. Even though C# language model assumes that refs are never null, null refs do exist in practice even in safe code and they need to handled in predictable deterministic way by Roslyn.

@AlekseyTs

@eiriktsarpalis

eiriktsarpalis commented Jul 31, 2026

Copy link
Copy Markdown
Member

@eiriktsarpalis would you mind taking a look at the STJ source generator change? thanks

The recent change to union pattern matching appears to have regressed handling of recursive union types. Minimal repro:

using System;

public union Nat(bool, Nat);

public static class Program
{
    private static int Depth(Nat n) => n switch
    {
        bool => 0,
        Nat succ => 1 + Depth(succ),
    };

    private static void Main()
    {
        Nat three = new Nat(new Nat(new Nat(true)));
        Console.WriteLine(Depth(three));    // expected 2, actual stack overflow
    }
}

cc @dotnet/roslyn

@AlekseyTs

Copy link
Copy Markdown
Contributor

The recent change to union pattern matching appears to have regressed handling of recursive union types.

This is an intentional and expected change in behavior. See https://github.com/dotnet/csharplang/blob/main/proposals/unions.md#type-pattern:

If the union type itself is pattern compatible with type and union Value is pattern compatible with type,
type_pattern is equivalent to type or { Value: type }, assuming that this form doesn't
use any special treatment for unions.

@eiriktsarpalis

Copy link
Copy Markdown
Member

This is an intentional and expected change in behavior.

So you're saying the only safe way to pattern match against a recursive union is to explicitly match against its .Value property? That doesn't sound right at all to me.

  • If we did start making recommendations that people use .Value explicitly wouldn't that negate the use of non-boxing access members assuming they are available?
  • If we are sticking with the current behavior, shouldn't we at least either
    1. Issue a warning when users are guided to write a pattern match that could stack overflow or
    2. Consider dropping support for recursive union case types?

@AlekseyTs

Copy link
Copy Markdown
Contributor

So you're saying the only safe way to pattern match against a recursive union is to explicitly match against its .Value property? That doesn't sound right at all to me.

I think there is nothing unsafe about the pattern. You just need to understand when does it match and act accordingly when it does. If you intend to use a type pattern that is going to match the instance itself, but you don't want to match against the instance itself, then yes, using property pattern explicitly { Value: <type> } is a way to go. Note, the bool case in your example doesn't need that because an instance can never match this pattern. The same can be said about constant patterns, etc.

  • If we did start making recommendations that people use .Value explicitly wouldn't that negate the use of non-boxing access members assuming they are available?

A property pattern form against a union instance as an input value should still take advantage of non-boxing access members. Compiler can see that this is a union "unwrapping" during matching.

  • If we are sticking with the current behavior, shouldn't we at least either

    1. Issue a warning when users are guided to write a pattern match that could stack overflow or

This is an infinite recursion through a function call, not some kind of pattern matching operation that never ends. There are many other ways to get into an infinite recursion. For example, it doesn't have to be direct, i.e. it might go through a sequence of repeated calls. Compiler almost never performs infinite recursion analysis for functions. If I remember correctly, the only exception - this(...) constructor initializers.

  1. Consider dropping support for recursive union case types?

Sometimes they are useful. As with any tool, they should be used properly.

@eiriktsarpalis

Copy link
Copy Markdown
Member

[if] you don't want to match against the instance itself, then yes, using property pattern explicitly { Value: } is a way to go. Note, the bool case in your example doesn't need that because an instance can never match this pattern.

I think this is primarily what I take issue with. Pattern matching works in a very specific way for most types except for when a case type happens to be a subtype of the union itself. I expect most casual users will get tripped up by this and for everybody else it's just another concern they will have to keep in mind.

A property pattern form against a union instance as an input value should still take advantage of non-boxing access members. Compiler can see that this is a union "unwrapping" during matching.

Presumably that would necessitate using { Value : type } forms proactively right? Switching on union.Value should still box I reckon.

This is an infinite recursion through a function call, not some kind of pattern matching operation that never ends.

Sure, but this is the canonical pattern one would expect a recursive union to fall into. If it's not recursion it would be an infinite loop instead.

@AlekseyTs

Copy link
Copy Markdown
Contributor

@AlekseyTs — the change looks intentional and the fix on our side is straightforward, but it does silently repurpose _ = ref x; from "no-op discard" into "null check". That idiom is used in dotnet/runtime (and, I would guess, elsewhere) purely to suppress unused-parameter diagnostics, so it may be worth calling out as a behavior change

Yes, this is a breaking behavior change in Roslyn. Even though C# language model assumes that refs are never null, null refs do exist in practice even in safe code and they need to handled in predictable deterministic way by Roslyn.

@AlekseyTs

I agree that in this particular scenario a dereference should not occur. I think this is another fall out from an alignment of handling between parameters and locals. A ref local is dereferenced in a similar situation. I opened an issue specifically for ref assignment to a discard, dotnet/roslyn#84724, since scenario in dotnet/roslyn#84547 goes through a different code path in compiler.

@AlekseyTs

Copy link
Copy Markdown
Contributor

I think this is primarily what I take issue with. Pattern matching works in a very specific way for most types except for when a case type happens to be a subtype of the union itself. I expect most casual users will get tripped up by this and for everybody else it's just another concern they will have to keep in mind.

You can certainly raise a concern. Perhaps, https://github.com/dotnet/csharplang or a "C# Union stakeholders sync" chat would be a better place for that, since this becomes a design discussion.

Switching on union.Value should still box I reckon.

That is correct. If code explicitly reads the value, a property getter will be called.

Comment thread src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs Outdated
@dotnet-maestro

This comment was marked as outdated.

2 similar comments
@dotnet-maestro

This comment was marked as outdated.

@dotnet-maestro

This comment was marked as outdated.

Use `{ Value: T x }` for the recursive union case instead of a bare type
pattern. The bare pattern is trivially satisfied by the union instance, so
it never verified that the payload actually had the case type and it
shadowed every arm emitted after it. Case order follows declaration order
for unrelated case types, so a union declaring its recursive case first
produced generated code that failed to compile with CS8510.

Add a union that declares the recursive case first as a regression test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8591ab4e-b1d7-40df-80f0-9a476a7bfd9d
Comment thread src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs
@akoeplinger

Copy link
Copy Markdown
Member

@jkotas @eiriktsarpalis are you ok with merging the codeflow or do you think there is something blocking?

Comment thread src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs Outdated
Comment thread src/coreclr/System.Private.CoreLib/src/System/StubHelpers.cs Outdated
@jkotas

jkotas commented Aug 3, 2026

Copy link
Copy Markdown
Member

are you ok with merging the codeflow or do you think there is something blocking?

Yes, we should get this merged and resolve the outstanding issues in a follow up

@dotnet-maestro

dotnet-maestro Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Important

While this PR was open, the source repository has received code changes from this repository (an opposite codeflow merged).
To avoid complex conflicts, the codeflow cannot continue until this PR is closed or merged.

You can continue with one of the following options:

  • Ignore this and merge this PR as usual without waiting for the new changes.
    Once merged, Maestro will create a new codeflow PR with the new changes.
  • Close this PR and wait for Maestro to open a new one with old and new changes included.
    You will lose any manual changes made in this PR.
    You can also manually trigger the new codeflow right away by running:
    darc trigger-subscriptions --id f7901f87-9f24-40d6-9bc1-564863937237
    
  • Force a codeflow into this PR at your own risk if you want the new changes.
    User commits made to this PR might be reverted.
    darc trigger-subscriptions --id f7901f87-9f24-40d6-9bc1-564863937237 --force
    

💡 You may consult the FAQ for more information or tag @dotnet/prodconsvcs for assistance.

@am11

am11 commented Aug 4, 2026

Copy link
Copy Markdown
Member

Failure is unrelated (#131665). BA has it under both known and new failures.

@akoeplinger

Copy link
Copy Markdown
Member

/ba-g unrelated issue that BA is not catching properly

@akoeplinger
akoeplinger merged commit 14f9284 into main Aug 4, 2026
215 of 218 checks passed
@akoeplinger
akoeplinger deleted the darc-main-c918dcdc-c972-4939-bb75-49f1a6b99922 branch August 4, 2026 12:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-codeflow for labeling automated codeflow

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants