diff --git a/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs b/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs index 9e23da8fb606b3..840dc86281783e 100644 --- a/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs +++ b/src/coreclr/tools/Common/Compiler/DevirtualizationManager.cs @@ -100,7 +100,9 @@ protected virtual MethodDesc ResolveVirtualMethod(MethodDesc declMethod, DefType return null; } - impl = implType.ResolveInterfaceMethodTargetWithVariance(declMethod); + // Strip the method instantation as interface slot resolution works on method definitions. + // Method instantiation will be applied again after resolution. + impl = implType.ResolveInterfaceMethodTargetWithVariance(declMethod.GetMethodDefinition()); if (impl != null) { diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DevirtualizationTests.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DevirtualizationTests.cs index 206007612e6aaa..ee667e641a9115 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DevirtualizationTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/DevirtualizationTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Linq; using Internal.IL; using Internal.TypeSystem; @@ -66,5 +67,40 @@ public void TestDevirtualizeAbstract() Assert.False(scanDevirt.IsEffectivelySealed(testType.GetNestedType("Abstract"u8))); } + + [Fact] + public void TestVariantInterfaceGenericMethodResolution() + { + DefType objectType = _context.GetWellKnownType(WellKnownType.Object); + MetadataType baseType = _testModule.GetType("GvmVariantInterface"u8, "GvmVariantBase"u8); + MetadataType derivedType = _testModule.GetType("GvmVariantInterface"u8, "GvmVariantDerived"u8); + MetadataType implementationType = _testModule.GetType("GvmVariantInterface"u8, "ClassWithVariantGvms"u8); + + MetadataType inVariantType = _testModule.GetType("GvmVariantInterface"u8, "IInVariantGvm`1"u8); + MetadataType outVariantType = _testModule.GetType("GvmVariantInterface"u8, "IOutVariantGvm`1"u8); + + MethodDesc inObjectMethod = inVariantType.MakeInstantiatedType(objectType).GetMethods().Single(m => m.GetName() == "Func").MakeInstantiatedMethod(objectType); + MethodDesc inBaseMethod = inVariantType.MakeInstantiatedType(baseType).GetMethods().Single(m => m.GetName() == "Func").MakeInstantiatedMethod(objectType); + MethodDesc inDerivedMethod = inVariantType.MakeInstantiatedType(derivedType).GetMethods().Single(m => m.GetName() == "Func").MakeInstantiatedMethod(objectType); + MethodDesc outObjectMethod = outVariantType.MakeInstantiatedType(objectType).GetMethods().Single(m => m.GetName() == "Func").MakeInstantiatedMethod(objectType); + MethodDesc outBaseMethod = outVariantType.MakeInstantiatedType(baseType).GetMethods().Single(m => m.GetName() == "Func").MakeInstantiatedMethod(objectType); + MethodDesc outDerivedMethod = outVariantType.MakeInstantiatedType(derivedType).GetMethods().Single(m => m.GetName() == "Func").MakeInstantiatedMethod(objectType); + + MethodImplRecord[] methodImpls = implementationType.FindMethodsImplWithMatchingDeclName(inObjectMethod.Name); + Assert.NotNull(methodImpls); + + MethodDesc inObjectImpl = methodImpls.Single(m => m.Decl.GetMethodDefinition() == inObjectMethod.GetMethodDefinition()).Body.MakeInstantiatedMethod(objectType); + MethodDesc inBaseImpl = methodImpls.Single(m => m.Decl.GetMethodDefinition() == inBaseMethod.GetMethodDefinition()).Body.MakeInstantiatedMethod(objectType); + MethodDesc outDerivedImpl = methodImpls.Single(m => m.Decl.GetMethodDefinition() == outDerivedMethod.GetMethodDefinition()).Body.MakeInstantiatedMethod(objectType); + MethodDesc outBaseImpl = methodImpls.Single(m => m.Decl.GetMethodDefinition() == outBaseMethod.GetMethodDefinition()).Body.MakeInstantiatedMethod(objectType); + + var devirt = new DevirtualizationManager(); + Assert.Equal(inObjectImpl, devirt.ResolveVirtualMethod(inObjectMethod, implementationType, out _)); + Assert.Equal(inBaseImpl, devirt.ResolveVirtualMethod(inBaseMethod, implementationType, out _)); + Assert.Equal(inObjectImpl, devirt.ResolveVirtualMethod(inDerivedMethod, implementationType, out _)); + Assert.Equal(outDerivedImpl, devirt.ResolveVirtualMethod(outObjectMethod, implementationType, out _)); + Assert.Equal(outBaseImpl, devirt.ResolveVirtualMethod(outBaseMethod, implementationType, out _)); + Assert.Equal(outDerivedImpl, devirt.ResolveVirtualMethod(outDerivedMethod, implementationType, out _)); + } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/GvmVariantInterface.cs b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/GvmVariantInterface.cs new file mode 100644 index 00000000000000..4dea83eecf5b22 --- /dev/null +++ b/src/coreclr/tools/aot/ILCompiler.Compiler.Tests/ILCompiler.Compiler.Tests.Assets/GvmVariantInterface.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace GvmVariantInterface +{ + class GvmVariantBase + { + } + + class GvmVariantDerived : GvmVariantBase + { + } + + interface IInVariantGvm + { + string Func(T t); + } + + interface IOutVariantGvm + { + string Func(); + } + + class ClassWithVariantGvms : IInVariantGvm, IInVariantGvm, IOutVariantGvm, IOutVariantGvm + { + string IInVariantGvm.Func(object t) => "CallOnObject"; + string IInVariantGvm.Func(GvmVariantBase t) => "CallOnBase"; + string IOutVariantGvm.Func() => "CallOnDerived"; + string IOutVariantGvm.Func() => "CallOnBase"; + } +} diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/CoreTestAssembly/CoreTestAssembly.csproj b/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/CoreTestAssembly/CoreTestAssembly.csproj index 3edc005bedc6b4..961a4bf4b8a448 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/CoreTestAssembly/CoreTestAssembly.csproj +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/CoreTestAssembly/CoreTestAssembly.csproj @@ -16,4 +16,8 @@ false + + + + diff --git a/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/VirtualFunctionOverrideTests.cs b/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/VirtualFunctionOverrideTests.cs index b6ddb314c70dfc..65d89061179851 100644 --- a/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/VirtualFunctionOverrideTests.cs +++ b/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests/VirtualFunctionOverrideTests.cs @@ -72,6 +72,40 @@ public void TestGenericMethodInterfaceMethodImplOverride() Assert.Equal(expectedVirtualMethod, objectType.ResolveInterfaceMethodToVirtualMethodOnType(interfaceMethod)); } + [Fact] + public void TestVariantInterfaceGenericMethodResolution() + { + DefType objectType = _context.GetWellKnownType(WellKnownType.Object); + MetadataType baseType = _testModule.GetType("GvmVariantInterface"u8, "GvmVariantBase"u8); + MetadataType derivedType = _testModule.GetType("GvmVariantInterface"u8, "GvmVariantDerived"u8); + MetadataType implementationType = _testModule.GetType("GvmVariantInterface"u8, "ClassWithVariantGvms"u8); + + MetadataType inVariantType = _testModule.GetType("GvmVariantInterface"u8, "IInVariantGvm`1"u8); + MetadataType outVariantType = _testModule.GetType("GvmVariantInterface"u8, "IOutVariantGvm`1"u8); + + MethodDesc inObjectMethod = inVariantType.MakeInstantiatedType(objectType).GetMethods().Single(m => m.GetName() == "Func"); + MethodDesc inBaseMethod = inVariantType.MakeInstantiatedType(baseType).GetMethods().Single(m => m.GetName() == "Func"); + MethodDesc inDerivedMethod = inVariantType.MakeInstantiatedType(derivedType).GetMethods().Single(m => m.GetName() == "Func"); + MethodDesc outObjectMethod = outVariantType.MakeInstantiatedType(objectType).GetMethods().Single(m => m.GetName() == "Func"); + MethodDesc outBaseMethod = outVariantType.MakeInstantiatedType(baseType).GetMethods().Single(m => m.GetName() == "Func"); + MethodDesc outDerivedMethod = outVariantType.MakeInstantiatedType(derivedType).GetMethods().Single(m => m.GetName() == "Func"); + + MethodImplRecord[] methodImpls = implementationType.FindMethodsImplWithMatchingDeclName(inObjectMethod.Name); + Assert.NotNull(methodImpls); + + MethodDesc inObjectImpl = methodImpls.Single(m => m.Decl.GetMethodDefinition() == inObjectMethod.GetMethodDefinition()).Body; + MethodDesc inBaseImpl = methodImpls.Single(m => m.Decl.GetMethodDefinition() == inBaseMethod.GetMethodDefinition()).Body; + MethodDesc outDerivedImpl = methodImpls.Single(m => m.Decl.GetMethodDefinition() == outDerivedMethod.GetMethodDefinition()).Body; + MethodDesc outBaseImpl = methodImpls.Single(m => m.Decl.GetMethodDefinition() == outBaseMethod.GetMethodDefinition()).Body; + + Assert.Equal(inObjectImpl, implementationType.ResolveVariantInterfaceMethodToVirtualMethodOnType(inObjectMethod)); + Assert.Equal(inBaseImpl, implementationType.ResolveVariantInterfaceMethodToVirtualMethodOnType(inBaseMethod)); + Assert.Equal(inObjectImpl, implementationType.ResolveVariantInterfaceMethodToVirtualMethodOnType(inDerivedMethod)); + Assert.Equal(outDerivedImpl, implementationType.ResolveVariantInterfaceMethodToVirtualMethodOnType(outObjectMethod)); + Assert.Equal(outBaseImpl, implementationType.ResolveVariantInterfaceMethodToVirtualMethodOnType(outBaseMethod)); + Assert.Equal(outDerivedImpl, implementationType.ResolveVariantInterfaceMethodToVirtualMethodOnType(outDerivedMethod)); + } + [Fact] public void TestVirtualDispatchOnGenericType() { diff --git a/src/tests/JIT/Generics/Variance/variance_generic_virtual.cs b/src/tests/JIT/Generics/Variance/variance_generic_virtual.cs new file mode 100644 index 00000000000000..44b59919f09e10 --- /dev/null +++ b/src/tests/JIT/Generics/Variance/variance_generic_virtual.cs @@ -0,0 +1,62 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// + +using Xunit; + +class Base +{ + +} + +class Derived : Base +{ + +} + +interface IInVariant +{ + string Func(T t); +} + +interface IOutVariant +{ + string Func(); +} + +class ClassWithVariantGvms : IInVariant, IInVariant, IOutVariant, IOutVariant +{ + string IInVariant.Func(object t) + { + return "CallOnObject"; + } + string IInVariant.Func(Base t) + { + return "CallOnBase"; + } + string IOutVariant.Func() + { + return "CallOnDerived"; + } + string IOutVariant.Func() + { + return "CallOnBase"; + } +} + +public class VarianceGenericVirtual +{ + [Fact] + public static void TestEntryPoint() + { + ClassWithVariantGvms testClass = new ClassWithVariantGvms(); + + Assert.Equal("CallOnObject", ((IInVariant)testClass).Func(new Derived())); + Assert.Equal("CallOnBase", ((IInVariant)testClass).Func(new Derived())); + Assert.Equal("CallOnObject", ((IInVariant)testClass).Func(new Derived())); + + Assert.Equal("CallOnDerived", ((IOutVariant)testClass).Func()); + Assert.Equal("CallOnBase", ((IOutVariant)testClass).Func()); + Assert.Equal("CallOnDerived", ((IOutVariant)testClass).Func()); + } +} diff --git a/src/tests/JIT/Generics/Variance/variance_generic_virtual.csproj b/src/tests/JIT/Generics/Variance/variance_generic_virtual.csproj new file mode 100644 index 00000000000000..d46f3d457f1a61 --- /dev/null +++ b/src/tests/JIT/Generics/Variance/variance_generic_virtual.csproj @@ -0,0 +1,10 @@ + + + PdbOnly + 1 + + + + + + diff --git a/src/tests/JIT/Generics/Variance/variance_generic_virtual_o.csproj b/src/tests/JIT/Generics/Variance/variance_generic_virtual_o.csproj new file mode 100644 index 00000000000000..76a31cdd0d2547 --- /dev/null +++ b/src/tests/JIT/Generics/Variance/variance_generic_virtual_o.csproj @@ -0,0 +1,11 @@ + + + PdbOnly + True + 1 + + + + + +