Don't produce unnecessary default interface methods (#67409)
authorMichal Strehovský <MichalStrehovsky@users.noreply.github.com>
Fri, 1 Apr 2022 05:33:57 +0000 (14:33 +0900)
committerGitHub <noreply@github.com>
Fri, 1 Apr 2022 05:33:57 +0000 (14:33 +0900)
Found this because it was asserting in one of the Pri-0 tests. We were trying to generate a special instantiating stub for an interface type that was not a dynamic interface castable implementation. I'm adding a regression test too because it has a visible outside behavior besides the assert.

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/EETypeNode.cs
src/tests/nativeaot/SmokeTests/DeadCodeElimination/DeadCodeElimination.cs

index 3921769..24f0da0 100644 (file)
@@ -315,8 +315,16 @@ namespace ILCompiler.DependencyAnalysis
 
             DefType defType = _type.GetClosestDefType();
 
+            // Interfaces don't have vtables and we don't need to track their slot use.
+            // The only exception are those interfaces that provide IDynamicInterfaceCastable implementations;
+            // those have slots and we dispatch on them.
+            bool needsDependenciesForVirtualMethodImpls = !defType.IsInterface
+                || ((MetadataType)defType).IsDynamicInterfaceCastableImplementation();
+
             // If we're producing a full vtable, none of the dependencies are conditional.
-            if (!factory.VTable(defType).HasFixedSlots)
+            needsDependenciesForVirtualMethodImpls &= !factory.VTable(defType).HasFixedSlots;
+            
+            if (needsDependenciesForVirtualMethodImpls)
             {
                 foreach (MethodDesc decl in defType.EnumAllVirtualSlots())
                 {
index 73955be..a638598 100644 (file)
@@ -16,6 +16,7 @@ class Program
         TestAbstractTypeNeverDerivedVirtualsOptimization.Run();
         TestAbstractNeverDerivedWithDevirtualizedCall.Run();
         TestAbstractDerivedByUnrelatedTypeWithDevirtualizedCall.Run();
+        TestUnusedDefaultInterfaceMethod.Run();
 
         return 100;
     }
@@ -187,6 +188,38 @@ class Program
         }
     }
 
+    class TestUnusedDefaultInterfaceMethod
+    {
+        interface IFoo<T>
+        {
+            void DoSomething();
+        }
+
+        interface IBar<T> : IFoo<T>
+        {
+            void IFoo<T>.DoSomething()
+            {
+                Activator.CreateInstance(typeof(NeverReferenced));
+            }
+        }
+
+        class NeverReferenced { }
+
+        class SomeInstance : IBar<object>
+        {
+            void IFoo<object>.DoSomething() { }
+        }
+
+        static IFoo<object> s_instance = new SomeInstance();
+
+        public static void Run()
+        {
+            s_instance.DoSomething();
+
+            ThrowIfPresent(typeof(TestUnusedDefaultInterfaceMethod), nameof(NeverReferenced));
+        }
+    }
+
     [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
         Justification = "That's the point")]
     private static bool IsTypePresent(Type testType, string typeName) => testType.GetNestedType(typeName, BindingFlags.NonPublic | BindingFlags.Public) != null;