Repository navigation
crossgen2: allow struct/unmanaged-constrained generics over primitives in the version bubble - #135906
Merged
lewing merged 3 commits intoOct 19, 2026
Merged
Conversation
…s in the version bubble
ComputeInstantiationVersionsWithCode treated a primitive instantiation
argument as versioning with the generic only when the generic parameter had
no type constraints at all. C# encodes `where T : struct` and
`where T : unmanaged` as a System.ValueType type constraint, so such
instantiations (e.g. S<char>) were neither precompiled nor inlined
("cannot get method info") without --inputbubble/--opt-cross-module.
Ignore System.ValueType constraints for primitive arguments; interface and
class constraints still disqualify.
Also teach CrossModuleInliningInfoSection to name local inlinees that have
no compiled body from the image metadata, and add an R2R test.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: f5ac7e42-b3b6-4fe3-bf0b-f40970e8fae0
|
Azure Pipelines: Successfully started running 4 pipeline(s). 12 pipeline(s) were filtered out due to trigger conditions. There may be pipelines that require an authorized user to comment /azp run to run. |
Contributor
|
Tagging subscribers to this area: @dotnet/crossgen-contrib |
This was referenced Oct 16, 2026
Closed
…strained-generics # Conflicts: # src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests/TestCases/R2RTestSuites.cs
lewing
marked this pull request as ready for review
October 16, 2026 20:19
davidwrighton
approved these changes
Oct 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
crossgen2 neither precompiles nor inlines a generic method or type with a
where T : structorwhere T : unmanagedconstraint when it is instantiated over a CoreLib primitive (for exampleS<char>orGS<int>.Get) from code outside CoreLib's version bubble. The same generic without the constraint compiles and inlines fine. When inlining fails, the JIT dump shows:Root cause
ReadyToRunCompilationModuleGroupBase.ComputeInstantiationVersionsWithCodecounts a primitive type argument as part of the generic's version bubble only when the generic parameter has no type constraints at all. C# writeswhere T : structandwhere T : unmanagedinto metadata as aSystem.ValueTypetype constraint (theunmanagedmodreq is not visible in the type system). That makes the constraint list non-empty, soVersionsWithMethodBodyreturns false, and two things follow:CorInfoImpl.getMethodInfo→CanInlinereturns false, which the JIT reports as "cannot get method info".The rule dates from #1889. Its design doc already says primitives are in the bubble "as long as the generic is not constrained on an interface or class".
System.ValueTypeis just how C# encodesstruct: every primitive satisfies it, and it exposes nothing an unconstrainedTcan't already use. The runtime needs no change.ReadyToRunInfo::ComputeAlternateGenericLocationForR2RCodeskips primitive arguments, so these instantiations are looked up in the defining module, the same path the unconstrained case already uses.Fix
When the type argument is a primitive, ignore
System.ValueTypeconstraints. Any other constraint, interface or class, still disqualifies the instantiation, as before. Theobject/stringbranch is unchanged.Other changes in this PR:
ILCompiler.Reflection.ReadyToRun'sCrossModuleInliningInfoSectionnow names local inlinees that have no compiled body of their own, such asAggressiveInlininghelpers, using the image metadata. Previously they showed as<MethodDef 0x…>. The new test's inlining assertion needs this, and it also improves R2RDump output.docs/design/features/crossgen2-compilation-structure-enhancements.mdnow states the rule.R2RTestSuites.StructConstrainedPrimitiveInstantiations([Fact], fixtureTestCases/PrimitiveInstantiations/StructConstrainedGenerics.cs). It asserts thatStructConstrained<int>,UnmanagedConstrained<int>andStructConstrainedBox<int>.GetValueHashCodeare compiled, and that thestruct- andunmanaged-constrainedAggressiveInlininghelpers are inlined.Impact
Affected:
--opt-cross-module;--opt-cross-moduleor--inputbubble.These instantiations are jitted at startup instead of using R2R code, and they miss R2R inlining.
Not affected, so this change is a no-op there:
--opt-cross-module:*, used for both app publish and the shared framework;Interface constraints still disqualify primitive instantiations by design. The separate generic-math issue (calls to static virtual methods such as
INumber<T>operators that can't be resolved across the version bubble) is tracked in #134559 and is not addressed here.Related: #135899 (the same crossgen2 area, found while working on it).
Validation
All runs on osx-arm64, checked:
./build.sh clr+libs -lc Release -c checked: succeeded with 0 warnings and 0 errors../build.sh -s clr.r2rtests -lc Release -c checked -test: 51 total, 36 passed, 0 failed, 15 skipped (target-specific tests).StructConstrainedPrimitiveInstantiationsfailed withExpected compiled method 'StructConstrainedGenerics.StructConstrained<int>' not found, and passed again once the fix was restored.--targetos:osx --targetarch:arm64 -Oand--targetos:browser --targetarch:wasm --obj-format:wasm -O, with default args.SN<int>,MN<int>andGS<int>.Getare now compiled.CallS(char)andCallM(char)now have the same code size as the unconstrainedCallU(char), which shows the call is inlined.IN<int>, which has anIEquatable<T>constraint, is still not compiled, as intended.Not run: the CI browser-wasm R2R test leg (
CLR_R2R_Tests_browser_wasm), which needs a browser CoreCLR build. The fix doesn't depend on the target, and the new test is a plain[Fact], so that leg will run it.Note
This PR description and the change were generated with GitHub Copilot.