Skip to content

Commit 8c58fc2

Browse files
Fix generic dataflow for generic virtual methods (#80009)
Generic virtual method analysis happens on top of canonical forms, so we would miss the fact that `IFoo.SomeMethod<SomeType>` was called and only see `IFoo.SomeMethod<__Canon>`.
1 parent 4131a83 commit 8c58fc2

File tree

3 files changed

+33
-2
lines changed

3 files changed

+33
-2
lines changed

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/RuntimeMethodHandleNode.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ protected override DependencyList ComputeNonRelocationBasedDependencies(NodeFact
5353
{
5454
dependencies ??= new DependencyList();
5555
dependencies.Add(factory.GVMDependencies(_targetMethod.GetCanonMethodTarget(CanonicalFormKind.Specific)), "GVM dependencies for runtime method handle");
56+
57+
// GVM analysis happens on canonical forms, but this is potentially injecting new genericness
58+
// into the system. Ensure reflection analysis can still see this.
59+
if (_targetMethod.IsAbstract)
60+
factory.MetadataManager.GetDependenciesDueToMethodCodePresence(ref dependencies, factory, _targetMethod, methodIL: null);
5661
}
5762

5863
factory.MetadataManager.GetDependenciesDueToLdToken(ref dependencies, factory, _targetMethod);

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -567,9 +567,9 @@ protected override void GetDependenciesDueToMethodCodePresenceInternal(ref Depen
567567

568568
Debug.Assert(methodIL != null || method.IsAbstract || method.IsPInvoke || method.IsInternalCall);
569569

570-
if (methodIL != null && scanReflection)
570+
if (scanReflection)
571571
{
572-
if (FlowAnnotations.RequiresDataflowAnalysis(method))
572+
if (methodIL != null && FlowAnnotations.RequiresDataflowAnalysis(method))
573573
{
574574
AddDataflowDependency(ref dependencies, factory, methodIL, "Method has annotated parameters");
575575
}

src/tests/nativeaot/SmokeTests/TrimmingBehaviors/Dataflow.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,13 @@ public static void AlsoKeptMethod() { }
243243
private static void RemovedMethod() { }
244244
}
245245

246+
class Type4WithPublicKept
247+
{
248+
public static void KeptMethod() { }
249+
public static void AlsoKeptMethod() { }
250+
private static void RemovedMethod() { }
251+
}
252+
246253
struct Struct1WithPublicKept
247254
{
248255
public static void KeptMethod() { }
@@ -276,6 +283,21 @@ public static void Keep<U>()
276283
}
277284
}
278285

286+
static IKeepPublicThroughGvm s_keepPublicThroughGvm = new KeepPublicThroughGvm();
287+
288+
interface IKeepPublicThroughGvm
289+
{
290+
void Keep<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>();
291+
}
292+
293+
class KeepPublicThroughGvm : IKeepPublicThroughGvm
294+
{
295+
public void Keep<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>()
296+
{
297+
Assert.NotNull(typeof(T).GetMethod("KeptMethod", BindingFlags.Public | BindingFlags.Static));
298+
}
299+
}
300+
279301
public static void Run()
280302
{
281303
new KeepsNonPublic();
@@ -293,6 +315,10 @@ public static void Run()
293315
KeepsPublic<Type3WithPublicKept>.Keep<object>();
294316
Assert.Equal(2, typeof(Type3WithPublicKept).CountMethods());
295317
Assert.Equal(2, typeof(Type3WithPublicKept).CountPublicMethods());
318+
319+
s_keepPublicThroughGvm.Keep<Type4WithPublicKept>();
320+
Assert.Equal(2, typeof(Type4WithPublicKept).CountMethods());
321+
Assert.Equal(2, typeof(Type4WithPublicKept).CountPublicMethods());
296322
}
297323
}
298324

0 commit comments

Comments
 (0)