Refactor C# DIM resolution to match C++ structure and fix static virtual dispatch#125205
Merged
MichalStrehovsky merged 10 commits intodotnet:mainfrom Mar 9, 2026
Merged
Conversation
Unify ResolveInterfaceMethodToDefaultImplementationOnType and ResolveVariantInterfaceMethodToDefaultImplementationOnType into a single FindDefaultInterfaceImplementation worker with an allowVariance parameter, matching the C++ FindDefaultInterfaceImplementation / TryGetCandidateImplementation structure in methodtable.cpp. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add an early-return path for static virtual methods matching the C++ implementation in MethodTable::TryResolveConstraintMethodApprox. The C++ handles static virtuals separately by calling ResolveVirtualStaticMethod, and only does so when neither the interface method nor the interface type are shared by generic instantiations. The C# now mirrors this: when the method, interface type, or constrained type involves canonical forms, resolution is skipped. Otherwise, the method is resolved via ResolveVariantInterfaceMethodToStaticVirtualMethodOnType with fallback to ResolveVariantInterfaceMethodToDefaultImplementationOnType. This fixes a pre-existing bug where variant static virtual default interface method dispatch was not properly resolved in the NativeAOT compiler. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Rename local variables in FindDefaultInterfaceImplementation and TryGetCandidateImplementation to match the C++ implementation: - runtimeInterface -> currentMT (pCurMT) - interfaceMethodOwningType -> interfaceMT (pInterfaceMT) - candidateMethod -> candidateMD/currentMD (candidateMD/pCurMD) - bestCandidateInterface -> bestCandidateMT (pBestCandidateMT) - bestCandidateMethod -> bestCandidateMD (pBestCandidateMD) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… check Replace the over-conservative constrainedType.IsCanonicalSubtype guard with the canonicalEquivalentFound check from C++ ResolveVirtualStaticMethod. This matches the C++ behavior: only bail out when the canonical form of the interface is also in the constrained type's interface map (indicating ambiguous resolution in shared generic code). Relax Scanner and RyuJIT assertions to allow static method results on non-value types, since constrained static virtual calls can resolve to methods on reference types (e.g. GenericClass<T> implementing an interface). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move the static virtual method check before the value type check, matching the C++ ordering in MethodTable::TryResolveConstraintMethodApprox: 1. if (IsStatic()) - handle static virtuals first, return early 2. if (!IsValueType()) - then reject non-value-types for instance methods This removes the IsCanonicalDefinitionType carve-out from the value type guard since static virtuals are now fully handled before it runs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Since static virtual methods now return early at the top of TryResolveConstraintMethodApprox, the remaining isStaticVirtualMethod checks in the instance method path are unreachable dead code. Remove them and inline the check at the entry point. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add four new test cases for static virtual method dispatch through shared generic constrained calls: - StaticVirtualOnReferenceType: class-based dispatch - GenericStaticVirtualMethod: method-level generic type parameters - CanonicalEquivalentFound: canonical equivalence in shared code - StaticVirtualDIMOnReferenceType: default interface methods on classes Fix ILC Scanner crash for GVM-only interfaces by swapping the check order in ConstrainedMethodUseLookupResult.NonRelocDependenciesFromUsage. The HasInstantiation check is now evaluated before factory.VTable() to avoid calling ScannedVTableProvider.GetSlice for interfaces whose VTable was never scanned (because GVMs are excluded from VTable slots and VirtualMethodUse is not reported for them). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates NativeAOT’s type system and compilation pipeline to align C# default interface method (DIM) resolution and static virtual dispatch behavior with the C++ runtime algorithms, while also adding loader regression coverage for the affected scenarios.
Changes:
- Refactored DIM resolution in the type system to a unified worker (
FindDefaultInterfaceImplementation) with a variance flag and a shared candidate-finding helper. - Fixed/adjusted static virtual constrained-call resolution (including canonical-equivalence handling) and relaxed related Scanner/RyuJIT assertions.
- Added multiple Loader regression tests covering static virtual dispatch on reference types, generic static virtuals, canonical equivalence, and static-virtual DIMs.
Reviewed changes
Copilot reviewed 15 out of 15 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/tests/Loader/classloader/StaticVirtualMethods/Regression/StaticVirtualOnReferenceType.csproj | Adds new loader test project for static virtual dispatch on reference types. |
| src/tests/Loader/classloader/StaticVirtualMethods/Regression/StaticVirtualOnReferenceType.cs | New regression test covering static virtual constrained calls on classes and structs. |
| src/tests/Loader/classloader/StaticVirtualMethods/Regression/StaticVirtualDIMOnReferenceType.csproj | Adds new loader test project for static-virtual DIM scenarios on reference types. |
| src/tests/Loader/classloader/StaticVirtualMethods/Regression/StaticVirtualDIMOnReferenceType.cs | New regression test combining static virtual dispatch with DIM resolution on classes/structs and generics. |
| src/tests/Loader/classloader/StaticVirtualMethods/Regression/SimpleStaticVirtual.csproj | Adds/updates a simple static-virtual regression test project. |
| src/tests/Loader/classloader/StaticVirtualMethods/Regression/SimpleStaticVirtual.cs | Regression test for static virtual default implementations including variance. |
| src/tests/Loader/classloader/StaticVirtualMethods/Regression/GenericStaticVirtualMethod.csproj | Adds new loader test project for generic static virtual methods. |
| src/tests/Loader/classloader/StaticVirtualMethods/Regression/GenericStaticVirtualMethod.cs | New regression test for method-generic static virtual interface members. |
| src/tests/Loader/classloader/StaticVirtualMethods/Regression/CanonicalEquivalentFound.csproj | Adds new loader test project for canonical-equivalence scenarios. |
| src/tests/Loader/classloader/StaticVirtualMethods/Regression/CanonicalEquivalentFound.cs | New regression test exercising canonical-equivalence handling in static virtual resolution. |
| src/coreclr/tools/aot/ILCompiler.RyuJit/JitInterface/CorInfoImpl.RyuJit.cs | Relaxes an assertion to allow static methods (not only value types) in canonical-resolution paths. |
| src/coreclr/tools/aot/ILCompiler.Compiler/IL/ILImporter.Scanner.cs | Relaxes an assertion similarly in the IL scanner canonical-resolution path. |
| src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericLookupResult.cs | Fixes lookup dependency enumeration to avoid touching VTable state for GVM-only interface cases. |
| src/coreclr/tools/Common/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs | Refactors DIM resolution to a unified implementation structure aligned with the C++ runtime. |
| src/coreclr/tools/Common/Compiler/TypeExtensions.cs | Refactors/fixes constrained-call resolution to properly handle static virtual dispatch (including reference types) and canonical-equivalence rules. |
You can also share your feedback on Copilot code review. Take the survey.
src/coreclr/tools/Common/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs
Show resolved
Hide resolved
src/coreclr/tools/Common/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs
Show resolved
Hide resolved
src/coreclr/tools/Common/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs
Outdated
Show resolved
Hide resolved
src/coreclr/tools/Common/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs
Show resolved
Hide resolved
Member
Author
|
/azp run runtime-nativeaot-outerloop |
|
Azure Pipelines successfully started running 1 pipeline(s). |
MichalStrehovsky
commented
Mar 5, 2026
src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/GenericLookupResult.cs
Show resolved
Hide resolved
davidwrighton
requested changes
Mar 5, 2026
Member
davidwrighton
left a comment
There was a problem hiding this comment.
I found one issue. Please add a test to see if its actually something, or if I am just seeing things.
src/coreclr/tools/Common/TypeSystem/Common/MetadataVirtualMethodAlgorithm.cs
Show resolved
Hide resolved
The C++ FindDefaultInterfaceImplementation walks interfaces in derived-to-base order using IterateInterfaceMapFrom(dwParentInterfaces). Match this ordering in the C# implementation by partitioning RuntimeInterfaces by base type level and iterating from most-derived to least-derived. Add TypeSystem unit test validating variant instance DIM resolution picks the most-derived type's interface first. Enable DefaultImplementationsOfInterfaces in CoreTestAssembly's RuntimeFeature to support DIM test types. Co-Authored-By: Copilot <223556219+Copilot@users.noreply.github.com>
…on NativeAOT - Add third pass in DispatchResolve for variant default interface method dispatch - Filter inherited DIM entries in InterfaceDispatchMapNode to avoid redundant dispatch map entries that would cause incorrect resolution order - Add VariantDIMIterationOrder regression test - Remove ActiveIssue for NativeAOT on ComplexHierarchyPositive (dotnet#88690) and add dynamic dispatch tests via reflection Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This was referenced Mar 6, 2026
Open
Member
Author
|
/azp run runtime-nativeaot-outerloop |
|
Azure Pipelines successfully started running 1 pipeline(s). |
davidwrighton
approved these changes
Mar 6, 2026
Member
Author
|
/ba-g timeouts are unrelated |
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.
Supersedes #121167.
Fixes #88690.
This PR refactors the C# default interface method (DIM) resolution in NativeAOT's type system to match the C++ runtime implementation structure, fixes bugs in static virtual method dispatch, and adds comprehensive test coverage.
Changes
DIM resolution refactoring
FindDefaultInterfaceImplementationworker method with anallowVarianceflag, matching the C++MethodTable::FindDefaultInterfaceImplementationstructureTryGetCandidateImplementationhelper matching the C++ equivalentStatic virtual method dispatch fixes
TryResolveConstraintMethodApproxto handle static virtuals on reference types (classes), not just value typesTryResolveConstraintMethodApproxIsCanonicalDefinitionTypeguard with propercanonicalEquivalentFoundcheck matching C++ResolveVirtualStaticMethodIsValueTypetoIsValueType || Signature.IsStaticisStaticVirtualMethodcode after the early return restructuringILC Scanner crash fix
ConstrainedMethodUseLookupResult.NonRelocDependenciesFromUsagefor interfaces with only generic virtual methods (GVMs)HasInstantiationis evaluated beforefactory.VTable()to avoid callingScannedVTableProvider.GetSlicefor interfaces whose VTable was never scanned (GVMs are excluded from VTable slots)New test coverage
StaticVirtualOnReferenceType: static virtual on classes via shared generic constrained callsGenericStaticVirtualMethod: method-level generic type parametersCanonicalEquivalentFound: canonical equivalence in shared codeStaticVirtualDIMOnReferenceType: default interface methods on reference typesAll 646 Loader tests pass (up from 642).