diff --git a/src/coreclr/tools/Common/Compiler/MethodExtensions.cs b/src/coreclr/tools/Common/Compiler/MethodExtensions.cs index 160b685987125..8943f7b3908a1 100644 --- a/src/coreclr/tools/Common/Compiler/MethodExtensions.cs +++ b/src/coreclr/tools/Common/Compiler/MethodExtensions.cs @@ -130,6 +130,7 @@ public static bool NotCallableWithoutOwningEEType(this MethodDesc method) TypeDesc owningType = method.OwningType; return !method.Signature.IsStatic && /* Static methods don't have this */ !owningType.IsValueType && /* Value type instance methods take a ref to data */ + !owningType.IsInterface && /* Interface MethodTable can be optimized away but the instance method can still be callable (`this` is of a non-interface type) */ !owningType.IsArrayTypeWithoutGenericInterfaces() && /* Type loader can make these at runtime */ (owningType is not MetadataType mdType || !mdType.IsModuleType) && /* Compiler parks some instance methods on the type */ !method.IsSharedByGenericInstantiations; /* Current impl limitation; can be lifted */ diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs index f93de22562731..25eefff027035 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/ILScanner.cs @@ -541,16 +541,27 @@ public ScannedDevirtualizationManager(NodeFactory factory, ImmutableArray typeof(Something); + } + + sealed class Derived : Base { } + + class Unrelated : Base + { + public override Type GetSomething() => typeof(Unrelated); + } + + public static void Run() + { + TestUnrelated(new Unrelated()); + + // We were getting a scanning failure because GetSomething got devirtualized into + // Base.GetSomething, but that's unreachable. + Test(null); + + [MethodImpl(MethodImplOptions.NoInlining)] + static Type Test(Derived d) => d?.GetSomething(); + + [MethodImpl(MethodImplOptions.NoInlining)] + static Type TestUnrelated(Base d) => d?.GetSomething(); + } + } + class RegressionBug73076 { interface IFactory diff --git a/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs b/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs index 824336106ef9c..6a2ce7011def2 100644 --- a/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs +++ b/src/tests/nativeaot/SmokeTests/UnitTests/Interfaces.cs @@ -38,6 +38,8 @@ public static int Run() TestPublicAndNonpublicDifference.Run(); TestDefaultInterfaceMethods.Run(); + TestDefaultInterfaceMethodsDevirtNoInline.Run(); + TestDefaultInterfaceMethodsNoDevirt.Run(); TestDefaultInterfaceVariance.Run(); TestVariantInterfaceOptimizations.Run(); TestSharedInterfaceMethods.Run(); @@ -581,6 +583,147 @@ public static void Run() } } + class TestDefaultInterfaceMethodsDevirtNoInline + { + interface IFoo + { + [MethodImpl(MethodImplOptions.NoInlining)] + int GetNumber() => 42; + } + + interface IBar : IFoo + { + [MethodImpl(MethodImplOptions.NoInlining)] + int IFoo.GetNumber() => 43; + } + + class Foo : IFoo { } + class Bar : IBar { } + + class Baz : IFoo + { + [MethodImpl(MethodImplOptions.NoInlining)] + public int GetNumber() => 100; + } + + interface IFoo + { + [MethodImpl(MethodImplOptions.NoInlining)] + Type GetInterfaceType() => typeof(IFoo); + } + + class Foo : IFoo { } + + class Base : IFoo + { + [MethodImpl(MethodImplOptions.NoInlining)] + int IFoo.GetNumber() => 100; + } + + class Derived : Base, IBar { } + + public static void Run() + { + Console.WriteLine("Testing default interface methods that can be devirtualized but not inlined..."); + + typeof(IFoo).ToString(); + + if (((IFoo)new Foo()).GetNumber() != 42) + throw new Exception(); + + if (((IFoo)new Bar()).GetNumber() != 43) + throw new Exception(); + + if (((IFoo)new Baz()).GetNumber() != 100) + throw new Exception(); + + if (((IFoo)new Derived()).GetNumber() != 100) + throw new Exception(); + + if (((IFoo)new Foo()).GetInterfaceType() != typeof(IFoo)) + throw new Exception(); + + if (((IFoo)new Foo()).GetInterfaceType() != typeof(IFoo)) + throw new Exception(); + } + } + + class TestDefaultInterfaceMethodsNoDevirt + { + interface IFoo + { + int GetNumber() => 42; + } + + interface IBar : IFoo + { + int IFoo.GetNumber() => 43; + } + + class Foo : IFoo { } + class Bar : IBar { } + + class Baz : IFoo + { + public int GetNumber() => 100; + } + + interface IFoo + { + Type GetInterfaceType() => typeof(IFoo); + } + + class Foo : IFoo { } + + class Base : IFoo + { + int IFoo.GetNumber() => 100; + } + + class Derived : Base, IBar { } + + public static void Run() + { + Console.WriteLine("Testing default interface methods that cannot be devirtualized..."); + + if (GetFoo().GetNumber() != 42) + throw new Exception(); + + [MethodImpl(MethodImplOptions.NoInlining)] + static IFoo GetFoo() => new Foo(); + + if (GetBar().GetNumber() != 43) + throw new Exception(); + + [MethodImpl(MethodImplOptions.NoInlining)] + static IFoo GetBar() => new Bar(); + + if (GetBaz().GetNumber() != 100) + throw new Exception(); + + [MethodImpl(MethodImplOptions.NoInlining)] + static IFoo GetBaz() => new Baz(); + + if (GetDerived().GetNumber() != 100) + throw new Exception(); + + [MethodImpl(MethodImplOptions.NoInlining)] + static IFoo GetDerived() => new Derived(); + + if (GetFooObject().GetInterfaceType() != typeof(IFoo)) + throw new Exception(); + + [MethodImpl(MethodImplOptions.NoInlining)] + static IFoo GetFooObject() => new Foo(); + + if (GetFooInt().GetInterfaceType() != typeof(IFoo)) + throw new Exception(); + + [MethodImpl(MethodImplOptions.NoInlining)] + static IFoo GetFooInt() => new Foo(); + } + } + class TestDefaultInterfaceVariance { class Foo : IVariant, IVariant