From 87fc2f9f13f6e7010df65a9c2c1ca07341d5d0b6 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Apr 2026 12:59:10 +0000 Subject: [PATCH 1/2] Initial plan From e933069a7f8a95855fbe37ca8c0d568450684717 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Apr 2026 13:30:15 +0000 Subject: [PATCH 2/2] Fix dataflow analysis for MakeGenericType/MakeGenericMethod in local methods Associate each runtime-determined dependency with the method it originates from (the actual IL method being scanned), rather than treating all dependencies as belonging to the parent user method. In SearchDynamicDependencies, match marked method bodies against the dependency's owning method instead of only the analyzed (parent) method. This ensures that when a local method like MakeGeneric is compiled, its MakeGenericTypeSite dependencies are correctly instantiated with the local method's generic arguments. Fixes dotnet/runtime#115552 Agent-Logs-Url: https://github.com/dotnet/runtime/sessions/81f1c59a-c2ca-4ab8-ae7d-e8edded14d1c Co-authored-by: MichalStrehovsky <13110571+MichalStrehovsky@users.noreply.github.com> --- .../Compiler/Dataflow/HandleCallAction.cs | 4 ++-- .../Compiler/Dataflow/ReflectionMarker.cs | 2 +- .../Dataflow/ReflectionMethodBodyScanner.cs | 2 +- .../DataflowAnalyzedMethodNode.cs | 19 +++++++-------- .../SmokeTests/TrimmingBehaviors/Dataflow.cs | 23 +++++++++++++++++++ 5 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs index b41dbe38d250c9..eb0c0397c42d1f 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/HandleCallAction.cs @@ -87,7 +87,7 @@ private partial bool TryHandleIntrinsic( } else { - _reflectionMarker.RuntimeDeterminedDependencies.Add(new MakeGenericTypeSite(typeInstantiated)); + _reflectionMarker.RuntimeDeterminedDependencies.Add((_callingMethod, new MakeGenericTypeSite(typeInstantiated))); } } } @@ -160,7 +160,7 @@ private partial bool TryHandleIntrinsic( } else { - _reflectionMarker.RuntimeDeterminedDependencies.Add(new MakeGenericMethodSite(methodInstantiated)); + _reflectionMarker.RuntimeDeterminedDependencies.Add((_callingMethod, new MakeGenericMethodSite(methodInstantiated))); } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs index cb05aa36cb6099..eefe7999dcb9a4 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMarker.cs @@ -30,7 +30,7 @@ public class ReflectionMarker public NodeFactory Factory { get; } public FlowAnnotations Annotations { get; } public DependencyList Dependencies { get => _dependencies; } - public List RuntimeDeterminedDependencies { get; } = new List(); + public List<(MethodDesc OwningMethod, INodeWithRuntimeDeterminedDependencies Dependency)> RuntimeDeterminedDependencies { get; } = new List<(MethodDesc, INodeWithRuntimeDeterminedDependencies)>(); internal enum AccessKind { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs index b2eb6834f97b5b..c9a3cd14f19770 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs @@ -124,7 +124,7 @@ protected override void Scan(MethodIL methodIL, ref InterproceduralState interpr base.Scan(methodIL, ref interproceduralState); } - public static DependencyList ScanAndProcessReturnValue(NodeFactory factory, FlowAnnotations annotations, Logger logger, MethodIL methodIL, out List runtimeDependencies) + public static DependencyList ScanAndProcessReturnValue(NodeFactory factory, FlowAnnotations annotations, Logger logger, MethodIL methodIL, out List<(MethodDesc OwningMethod, INodeWithRuntimeDeterminedDependencies Dependency)> runtimeDependencies) { #if !ILTRIM methodIL = AsyncMaskingILProvider.WrapIL(methodIL); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedMethodNode.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedMethodNode.cs index 026b9a54028948..07533b6b600921 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedMethodNode.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/DataflowAnalyzedMethodNode.cs @@ -19,7 +19,7 @@ namespace ILCompiler.DependencyAnalysis public class DataflowAnalyzedMethodNode : DependencyNodeCore { private readonly MethodIL _methodIL; - private List _runtimeDependencies; + private List<(MethodDesc OwningMethod, INodeWithRuntimeDeterminedDependencies Dependency)> _runtimeDependencies; public DataflowAnalyzedMethodNode(MethodIL methodIL) { @@ -39,29 +39,30 @@ public override IEnumerable GetStaticDependencies(NodeFacto { // Something wrong with the input - missing references, etc. // The method body likely won't compile either, so we don't care. - _runtimeDependencies = new List(); + _runtimeDependencies = new List<(MethodDesc, INodeWithRuntimeDeterminedDependencies)>(); return Array.Empty(); } } public override IEnumerable SearchDynamicDependencies(List> markedNodes, int firstNode, NodeFactory factory) { - MethodDesc analyzedMethod = _methodIL.OwningMethod; - - // Look for any generic specialization of this method. If any are found, specialize any dataflow dependencies. + // Look for any generic specialization of this method or its compiler-generated callees (local methods, lambdas). + // If any are found, specialize the dataflow dependencies that originated from that method. for (int i = firstNode; i < markedNodes.Count; i++) { if (markedNodes[i] is not IMethodBodyNode methodBody) continue; MethodDesc method = methodBody.Method; - if (method.GetTypicalMethodDefinition() != analyzedMethod) - continue; + MethodDesc typicalMethod = method.GetTypicalMethodDefinition(); - // Instantiate all runtime dependencies for the found generic specialization + // Instantiate runtime dependencies whose owning method matches this method body's typical definition foreach (var n in _runtimeDependencies) { - foreach (var d in n.InstantiateDependencies(factory, method.OwningType.Instantiation, method.Instantiation, isConcreteInstantiation: !method.IsSharedByGenericInstantiations)) + if (n.OwningMethod != typicalMethod) + continue; + + foreach (var d in n.Dependency.InstantiateDependencies(factory, method.OwningType.Instantiation, method.Instantiation, isConcreteInstantiation: !method.IsSharedByGenericInstantiations)) { yield return new CombinedDependencyListEntry(d.Node, null, d.Reason); } diff --git a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Dataflow.cs b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Dataflow.cs index dbdbcae08372e5..4c01d6aaeca072 100644 --- a/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Dataflow.cs +++ b/src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Dataflow.cs @@ -30,6 +30,7 @@ public static int Run() TestMakeGenericDataflowInvalid.Run(); TestMarshalIntrinsics.Run(); Regression97758.Run(); + TestMakeGenericDataflowInLocalMethod.Run(); return 100; } @@ -771,6 +772,28 @@ public static void Run() Foo.Trigger(); } } + + class TestMakeGenericDataflowInLocalMethod + { + class Gen { } + + struct Atom { } + + class GenMethods + { + public static void Bridge() { } + } + + public static void Run() + { + MakeGenericType(); + MakeGenericMethod(); + + static object MakeGenericType() => Activator.CreateInstance(typeof(Gen<>).MakeGenericType(typeof(T))); + + static void MakeGenericMethod() => typeof(GenMethods).GetMethod(nameof(GenMethods.Bridge)).MakeGenericMethod(typeof(T)).Invoke(null, []); + } + } } static class Assert