Skip to content

Fix rewriting of configured async enumeration and disposal - #527

Open
Frank Shearar (frankshearar) wants to merge 3 commits into
microsoft:mainfrom
frankshearar:fix/configured-async-enumeration
Open

Frank Shearar (frankshearar) wants to merge 3 commits into
microsoft:mainfrom
frankshearar:fix/configured-async-enumeration

Conversation

@frankshearar

@frankshearar Frank Shearar (frankshearar) commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Summary

Depends on #529 (fixes #528). The prerequisite is merged into this branch without
rewriting its published history; the standalone constructor correction and its
general regression tests remain reviewable in #529.

Fix a process-crashing rewrite of configured async enumeration on .NET 8, including
await foreach (var item in source.WithCancellation(token)) and
source.ConfigureAwait(...). Also handle configured async disposal, which shares
the same configured-ValueTask producer boundary.

The rewriter already replaces ConfiguredValueTaskAwaitable and its awaiters,
but did not replace the framework's configured enumerable, enumerator, or
disposable. Their methods therefore produced native structs for consumers
rewritten to expect Coyote's different struct layout. A minimal configured
async-iterator test aborts the test host with AccessViolationException on
unmodified main.

Changes

  • Map TaskAsyncEnumerableExtensions, ConfiguredCancelableAsyncEnumerable<T>,
    its nested enumerator, and ConfiguredAsyncDisposable to Coyote wrappers.
  • Route MoveNextAsync and DisposeAsync through the existing controllable
    configured-ValueTask awaitables.
  • Preserve cancellation-token replacement and configuration chaining. The
    existing awaiter behavior continues to force context capture during controlled
    testing.
  • Add 18 regression cases covering synchronous/asynchronous iteration and
    disposal, both context-capture settings, cancellation, chained configuration,
    early-exit cleanup, failure replay, configured values in generic constructors
    (including the nested enumerator), and generic/custom configuration delegates.
  • Guard the new APIs with NET, leaving the netstandard2.0 build unchanged.

This does not address CancellationTokenSource.CancelAsync interception or TPL
Dataflow scheduling support.

Validation

Before the fix, ConfiguredAsyncEnumerableTests.TestWithCancellation aborts the
test host with System.AccessViolationException.

After the fix:

dotnet build .\Tests\Tests.BugFinding\Tests.BugFinding.csproj -c Release -f net8.0 --no-restore
dotnet test .\Tests\Tests.BugFinding\Tests.BugFinding.csproj -c Release -f net8.0 --no-build --no-restore --filter "FullyQualifiedName~ConfiguredAsyncEnumerableTests|FullyQualifiedName~ValueTaskTests|FullyQualifiedName~ValueTaskTaskConfigureAwait"
# Passed: 77, Failed: 0, Skipped: 0

dotnet test .\Tests\Tests.Rewriting\Tests.Rewriting.csproj -c Release -f net8.0
# Passed: 79, Failed: 0, Skipped: 0

dotnet build .\Source\Test\Test.csproj -c Release -f netstandard2.0 --no-restore
# Build succeeded, 0 errors

The 18 configured cases pass with the constructor correction. Without it, four
of the added integration cases fail with InvalidProgramException: enumerable,
nested enumerator, disposable, and generic delegate construction. The custom
delegate control passes both versions.

The Windows CI failure on the original head was an outdated IL-diff baseline,
separate from the constructor runtime defect. Updated the inspected BugFinding
baseline to 39A1E35D...: after excluding the new configured test types and their
generated state machines, the normalized IL diff matches upstream exactly. The
four before/after integration IL changes are solely call .ctor to
newobj .ctor. The rewriting baseline is inherited from #529.

Built the complete solution for net8.0 and ran
.\Tests\compare-rewriting-diff-logs.ps1; all five hashes match.

Validated on Windows with .NET 8.0.32. Builds emitted NU1900 warnings because
NuGet vulnerability metadata could not be reached; no compiler/analyzer warnings
were reported.

Rewrite configured async-enumeration producers alongside their ValueTask consumers to avoid incompatible awaitable layouts. Cover cancellation, cleanup, context configuration, and failure replay.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3447f3e5-9328-4334-8f18-0223764709a5
Keep instance allocation when generic constructor arguments are rewritten, while retaining call for static factory replacements. Add class, struct, delegate, and factory regression coverage and update the inspected rewriting IL baseline.

Fixes microsoft#528

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3447f3e5-9328-4334-8f18-0223764709a5
…verage

Merge the prerequisite from microsoft#529 without rewriting branch history. Cover configured enumerable, nested enumerator, disposable, generic delegate, and custom delegate construction, and update the inspected BugFinding IL baseline.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3447f3e5-9328-4334-8f18-0223764709a5

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Preserve newobj when rewritten targets remain instance constructors

1 participant