Skip to content

Fix R2R TypeMap fallback for unresolved assembly targets#126123

Draft
Copilot wants to merge 4 commits intomainfrom
copilot/fix-interop-typemapapp-test-failures
Draft

Fix R2R TypeMap fallback for unresolved assembly targets#126123
Copilot wants to merge 4 commits intomainfrom
copilot/fix-interop-typemapapp-test-failures

Conversation

Copy link
Contributor

Copilot AI commented Mar 25, 2026

Description

Interop/TypeMap/TypeMapApp/TypeMapApp.cmd fails deterministically in all R2R configurations because of a disconnect between CrossGen2 and the runtime when a TypeMapAssemblyTarget references an unresolvable assembly (e.g., "DoesNotExist").

When CrossGen2 fails to resolve an assembly target, it catches the exception and sets error stubs on the external/proxy type maps (state=0), but AddTargetModule is never called, leaving TargetModules empty. CrossGen2 then emits a TypeMapAssemblyTargets entry with count=0 while marking the external/proxy maps as invalid. At runtime, HasTypeMapAssemblyTargets finds this empty entry → returns true with count=0 → sets hasPrecachedTargets = true → skips fallback to attribute processing → FileNotFoundException is never thrown.

Primary fix (runtime — assemblynative.cpp): The outer if block already enters when any of the three precached maps (targets, external, proxy) are missing. The assembly target attribute re-processing is now unconditional within that block, since the outer condition already guarantees at least one map needs fallback. The AssemblyTargetProcessor already deduplicates via _toProcess/_processed SHash collections, so re-processing is always safe.

if ((newExternalTypeEntry != nullptr && !hasPrecachedExternal) ||
    (newProxyTypeEntry != nullptr && !hasPrecachedProxy) ||
    !hasPrecachedTargets)
{
    // Fall back to attribute parsing for the assembly targets...
    // The AssemblyTargetProcessor already deduplicates, so re-processing is safe.
    ProcessTypeMapAttribute(
        TypeMapAssemblyTargetAttributeName,
        assemblies,
        groupTypeMT,
        currAssembly);
    // ...
}

Complementary cleanup (CrossGen2 — TypeMapAssemblyTargetsNode.cs): Skip emitting TypeMapAssemblyTargets entries for groups with zero resolved target modules, avoiding useless count=0 entries in the R2R image.

foreach (var map in _assemblyTypeMaps.Maps)
{
    if (map.Value.TargetModules.Count == 0)
        continue; // runtime will fall back to attribute processing
    // ...emit entry...
}

⚡ Quickly spin up Copilot coding agent tasks from anywhere on your macOS or Windows machine with Raycast.

Copilot AI linked an issue Mar 25, 2026 that may be closed by this pull request
@github-actions github-actions bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label Mar 25, 2026
…assemblies

When CrossGen2 encounters a TypeMapAssemblyTarget pointing to an
assembly that fails to resolve (e.g. "DoesNotExist"), it catches the
exception and sets exception stubs, but AddTargetModule is never
called, leaving TargetModules empty.

Previously, TypeMapAssemblyTargetsNode still emitted an entry with
count=0 for these groups. At runtime, HasTypeMapAssemblyTargets would
find this entry, return true with count=0, setting hasPrecachedTargets
to true. This prevented the runtime from falling back to attribute
processing, which is where the FileNotFoundException would be thrown.

The fix skips emitting TypeMapAssemblyTargets entries for groups with
no resolved target modules. This makes HasTypeMapAssemblyTargets
return false for these groups, causing the runtime to correctly fall
back to attribute processing and throw FileNotFoundException.

Fixes #125941

Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/f1301246-c321-4665-a75e-28efe25a1e5e

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix test failures in Interop/TypeMap/TypeMapApp Fix CrossGen2 emitting empty TypeMapAssemblyTargets entries for unresolved assemblies Mar 25, 2026
Copilot AI requested a review from stephentoub March 25, 2026 21:02
@mangod9
Copy link
Member

mangod9 commented Mar 26, 2026

@copilot why did you not implement the option 1 as mentioned in the analysis on the issue?

…oxy maps aren't pre-cached

When CrossGen2 fails to resolve an assembly target (e.g. "DoesNotExist"),
it emits a TypeMapAssemblyTargets entry with count=0 but marks the
external/proxy type maps as invalid (state=0). Previously the runtime
only re-processed assembly target attributes when hasPrecachedTargets
was false.

With this change, the runtime also re-processes assembly target
attributes when the external or proxy type maps weren't pre-cached.
This is the more robust fix (Option 1 from the analysis) that also
handles the mixed case where some assembly targets resolve and some
don't. The AssemblyTargetProcessor already deduplicates, so
re-processing is safe.

Co-authored-by: mangod9 <61718172+mangod9@users.noreply.github.com>
Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/35d99139-2cbe-4b13-a96f-f26d5e5bd8d2
Copy link
Contributor Author

Copilot AI commented Mar 26, 2026

@copilot why did you not implement the option 1 as mentioned in the analysis on the issue?

Implemented Option 1 (the runtime fix) in 42159286. The condition in assemblynative.cpp now also re-processes assembly target attributes when the external/proxy type maps weren't pre-cached, which handles the mixed case where some assembly targets resolve and some don't. The CrossGen2 fix (Option 2) is kept as the complementary cleanup recommended in the analysis.

Copilot AI changed the title Fix CrossGen2 emitting empty TypeMapAssemblyTargets entries for unresolved assemblies Fix R2R TypeMap fallback for unresolved assembly targets Mar 26, 2026
Copilot AI requested a review from mangod9 March 26, 2026 00:27
Comment on lines +1784 to +1786
if (!hasPrecachedTargets
|| (newExternalTypeEntry != nullptr && !hasPrecachedExternal)
|| (newProxyTypeEntry != nullptr && !hasPrecachedProxy))
Copy link
Member

Choose a reason for hiding this comment

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

This condition is the same as the condition on 1772. We can remove this and unconditionally run this block (the comment is good though).

Copy link
Member

Choose a reason for hiding this comment

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

@copilot can you make the update as requested here ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed the redundant inner condition in bd14f210. The ProcessTypeMapAttribute call is now unconditional within the outer if block, with the explanatory comment preserved.

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

Labels

area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Test failure: Interop/TypeMap/TypeMapApp/TypeMapApp.cmd

4 participants