Skip to content

[ILLink] Add regression test for KeyNotFoundException crash in async partial methods with capturing lambdas#126733

Draft
Copilot wants to merge 3 commits intomainfrom
copilot/reproduce-illink-trimmer-crash
Draft

[ILLink] Add regression test for KeyNotFoundException crash in async partial methods with capturing lambdas#126733
Copilot wants to merge 3 commits intomainfrom
copilot/reproduce-illink-trimmer-crash

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 9, 2026

Roslyn does not emit [AsyncStateMachineAttribute] on async partial methods. ILLink's TryGetStateMachineType relies exclusively on this attribute, so the state machine type is never added to generatedTypeToTypeArgs. When a capturing lambda inside MoveNext creates a display class whose generic parameter's Owner is the missing state machine type, MapGeneratedTypeTypeParameters crashes with KeyNotFoundException.

Changes

  • CompilerGeneratedTypes.cs — Added PartialAsyncMethodWithLambda regression test: a nested partial class with a generic async partial method that captures a lambda referencing the generic type parameter. This test reproduces the crash described in the linked issue.
partial class PartialAsyncMethodWithLambda
{
    public partial Task<T> GetData<T>() where T : new();
}
partial class PartialAsyncMethodWithLambda
{
    public async partial Task<T> GetData<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>() where T : new()
    {
        var tcs = new TaskCompletionSource<T>();
        Action<T> callback = (result) =>
        {
            _ = typeof(T).GetMethods();
            tcs.TrySetResult(result);
        };
        callback(new T());
        return await tcs.Task;
    }
}

Note

This PR was authored with the assistance of GitHub Copilot.

Original prompt [Chronological Review] 1. Initial request: User wanted to reproduce the ILLink trimmer crash from #122800 - a KeyNotFoundException in MapGeneratedTypeTypeParameters when processing compiler-generated async state machine types for generic methods.
  1. Exploration phase: Read the issue, related issues ([area-Tools-ILLink]: Key Not Found: 'QuickGraph.Collections.FibonacciHeap`2/<GetEnumerator>d__8' #109821), Kebechet/Maui.Health fix commits, and deeply analyzed CompilerGeneratedState.cs (the buggy code), CompilerGeneratedNames.cs (name matching), and existing test cases.

  2. First repro attempts: Created test project at C:\Users\angocke\tmp\link-crash with ReproLib (class library), ReproApp (console with trimming), and Inspector (reflection tool). Multiple attempts with generic async methods + lambdas did NOT crash the trimmer.

  3. Key breakthrough: Downloaded the actual Maui.Health NuGet package (v2.0.0-preview9, the pre-fix version) and inspected it with Cecil. Discovered that GetHealthData and WriteHealthData are async methods with state machines but are MISSING [AsyncStateMachineAttribute]. Only GetCumulativeHealthDataAsync (a non-partial async method) had the attribute.

  4. Root cause identified: Roslyn does NOT emit [AsyncStateMachineAttribute] on async partial methods. ILLink's TryGetStateMachineType relies exclusively on this attribute to discover state machines. Without it, the state machine type is never added to generatedTypeToTypeArgs, but the display class (created by a lambda in MoveNext) IS added — and its generic parameter's Owner references the missing state machine, causing KeyNotFoundException.

  5. Minimal repro created: A partial method declaration + async partial implementation with a generic-capturing lambda. Confirmed crash: KeyNotFoundException: The given key 'ReproLib.Service/<GetData>d__01' was not present in the dictionary.`

  6. Test case added: Added the repro as a test in CompilerGeneratedTypes.cs in the ILLink test suite.

  7. Test validation: Tried running tests. Analyzer tests (ILLink.RoslynAnalyzer.Tests) built and passed (2/2). The full linker tests (Mono.Linker.Tests) couldn't build due to missing VS C++ tools on the machine, and the prebuilt test runner had a pre-existing version mismatch error (ProducedBy not found) unrelated to the changes.

  8. Most recent: User said "cd ~/code/runtime", agent changed directory. Then user requested conversation summary.

[Intent Mapping]

[Technical Inventory]

  • ILLink trimmer (IL Linker) - the .NET assembly trimming tool
  • CompilerGeneratedState.cs - core file with the bug
  • Mono.Cecil - used for IL inspection
  • Roslyn compiler - generates the problematic codegen for async partial methods
  • .NET 11.0.100-preview.2 SDK (on this machine), bug reported on .NET 10.0.101

[Code Archaeology]

  • CompilerGeneratedState.cs: MapGeneratedTypeTypeParameters (lines 323-375), ProcessMethod (lines 131-221), TryGetStateMachineType (lines 82-102), ScanForInit (lines 377-418)
  • CompilerGeneratedNames.cs: IsStateMachineType, IsLambdaDisplayClass, IsStateMachineOrDisplayClass
  • CompilerGeneratedTypes.cs test file: Added PartialAsyncMethodWithLambda test class
  • Repro files at C:\Users\angocke\tmp\link-crash\

[Progress Assessment]

  • DONE: Root cause identified
  • DONE: Minimal repro created and validated
  • DONE: Test case added to CompilerGeneratedTypes.cs
  • DONE: Analyzer tests pass with the new test
  • NOT POSSIBLE on this machine: Full linker test run (needs VS C++ tools)

[Context Validation]
All critical information captured including exact file paths, line numbers, crash mechanism, and test code.

[Recent Commands Analysis]
The most recent commands were:

  1. cd C:\Users\angocke\code\runtime; Get-Location - Changed to the runtime directory
  2. Before that: git --no-pager diff showing the final diff of CompilerGeneratedTypes.cs
  3. Before that: Running analyzer tests (passed 2/2) and linker tests (failed due to pre-existing env issue)
  4. The user then asked for a conversation summary
1. Conversation Overview: - Primary Objectives: "I'd like to repro the user error in https://github.com//issues/122800" — reproduce the ILLink trimmer crash (KeyNotFoundException in MapGeneratedTypeTypeParameters) and "add this as a test case in ~/code/runtime/src/illink" - Session Context: Started with issue analysis → explored ILLink source code → created repro project → multiple failed attempts → downloaded actual Maui.Health NuGet and inspected IL with Cecil → discovered root cause (missing AsyncStateMachineAttribute on async partial methods) → created minimal repro → confirmed crash → added test case to CompilerGeneratedTypes.cs → validated with analyzer tests - User Intent Evolution: Started as "reproduce the crash" → evolved to "add as test case" after repro was achieved
  1. Technical Foundat...

Created from Copilot CLI via the copilot delegate command.

Copilot AI requested review from Copilot and removed request for Copilot April 9, 2026 23:17
…h capturing lambdas

Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/2e76c507-3b5f-49ad-9dc8-d7865f98a645

Co-authored-by: agocke <515774+agocke@users.noreply.github.com>
Copilot AI requested review from Copilot and removed request for Copilot April 10, 2026 00:11
Copilot AI changed the title [WIP] Reproduce ILLink trimmer crash for async state machines [ILLink] Fix KeyNotFoundException crash for async partial methods with capturing lambdas Apr 10, 2026
Copilot AI requested a review from agocke April 10, 2026 00:12
@dotnet-policy-service dotnet-policy-service bot added the linkable-framework Issues associated with delivering a linker friendly framework label Apr 10, 2026
@agocke
Copy link
Copy Markdown
Member

agocke commented Apr 10, 2026

@copilot Please revert the fix for now, just add the test

Copilot AI requested review from Copilot and removed request for Copilot April 10, 2026 01:34
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 10, 2026

@copilot Please revert the fix for now, just add the test

Done in a8f7794. CompilerGeneratedState.cs is reverted to its original state; only the regression test in CompilerGeneratedTypes.cs remains.

Copilot AI changed the title [ILLink] Fix KeyNotFoundException crash for async partial methods with capturing lambdas [ILLink] Add regression test for KeyNotFoundException crash in async partial methods with capturing lambdas Apr 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-Meta linkable-framework Issues associated with delivering a linker friendly framework

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants