diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCases/TestDatabase.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCases/TestDatabase.cs index 57c3de36dd757..488a283b043db 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCases/TestDatabase.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCases/TestDatabase.cs @@ -34,6 +34,11 @@ public static IEnumerable InlineArrays () return TestNamesBySuiteName(); } + public static IEnumerable Inheritance_Interaces() + { + return TestNamesBySuiteName("Inheritance.Interfaces"); + } + public static IEnumerable Libraries() { return TestNamesBySuiteName(); diff --git a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCases/TestSuites.cs b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCases/TestSuites.cs index f8d7fb5c69d02..86b5bb49b4f3d 100644 --- a/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCases/TestSuites.cs +++ b/src/coreclr/tools/aot/ILCompiler.Trimming.Tests/TestCases/TestSuites.cs @@ -37,6 +37,20 @@ public void InlineArrays(string t) Run(t); } + [Theory] + [MemberData(nameof(TestDatabase.Inheritance_Interaces), MemberType = typeof(TestDatabase))] + public void Inheritance_Interfaces(string t) + { + switch (t) { + case ".InterfaceWithoutNewSlot": + Run (t); + break; + default: + // Skip the rest for now + break; + } + } + [Theory] [MemberData(nameof(TestDatabase.Libraries), MemberType = typeof(TestDatabase))] public void Libraries(string t) diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 79b55dbb61379..5fd3884418078 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -149,9 +149,12 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) // we shouldn't need to run the below logic. This results in ILLink potentially // keeping more methods than needed. + // Static methods on interfaces must be implemented only via explicit method-impl record + // not by a signature match. So there's no point in running the logic below for static methods. + if (!resolvedInterfaceMethod.IsVirtual || resolvedInterfaceMethod.IsFinal - || !resolvedInterfaceMethod.IsNewSlot) + || resolvedInterfaceMethod.IsStatic) continue; // Try to find an implementation with a name/sig match on the current type diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs index 3ea7c024baf6e..2e1a2bbcb3454 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs @@ -27,5 +27,11 @@ public Task InterfaceVariants () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task InterfaceWithoutNewSlot () + { + return RunTest (allowMissingWarnings: true); + } + } } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceWithoutNewSlot.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceWithoutNewSlot.il new file mode 100644 index 0000000000000..d31d535fc70e1 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceWithoutNewSlot.il @@ -0,0 +1,53 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class interface public auto ansi abstract IInterfaceWithoutNewSlot +{ + .method public hidebysig abstract virtual + instance void AbstractNoNewSlot () cil managed + { + } + + .method public hidebysig abstract virtual + instance void AbstractNoNewSlotUnused () cil managed + { + } + + .method public hidebysig newslot abstract virtual + instance void AbstractNewSlot () cil managed + { + } + + .method public hidebysig newslot abstract virtual + instance void AbstractNewSlotUnused () cil managed + { + } + + .method public hidebysig virtual + instance void ImplementedNoNewSlot () cil managed + { + ret; + } + + .method public hidebysig virtual + instance void ImplementedNoNewSlotUnused () cil managed + { + ret; + } + + .method public hidebysig newslot virtual + instance void ImplementedNewSlot () cil managed + { + ret; + } + + .method public hidebysig newslot virtual + instance void ImplementedNewSlotUnused () cil managed + { + ret; + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceWithoutNewSlot.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceWithoutNewSlot.cs new file mode 100644 index 0000000000000..1717e08aba80d --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceWithoutNewSlot.cs @@ -0,0 +1,79 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces +{ + // Tests the case where the interface method doesn't have 'newslot' flag set + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/InterfaceWithoutNewSlot.il" })] + class InterfaceWithoutNewSlot + { + public static void Main () + { + ImplementationType.Test (); + } + +#if !IL_ASSEMBLY_AVAILABLE + // Declaration for the build of the test suite + // When compiled for actual run the IL version is used instead + // This is because there's no way to express the newslot/no-newslot in C# + interface IInterfaceWithoutNewSlot + { + void AbstractNoNewSlot (); + void AbstractNoNewSlotUnused (); + void AbstractNewSlot (); + void AbstractNewSlotUnused (); + void ImplementedNoNewSlot () { } + void ImplementedNoNewSlotUnused () { } + void ImplementedNewSlot () { } + void ImplementedNewSlotUnused () { } + } +#endif + + [Kept] + [KeptInterface (typeof (IInterfaceWithoutNewSlot))] + [KeptMember (".ctor()")] + class ImplementationType : IInterfaceWithoutNewSlot + { + [Kept] + public void AbstractNoNewSlot () { } + + public void AbstractNoNewSlotUnused () { } + + [Kept] + public void AbstractNewSlot () { } + + public void AbstractNewSlotUnused () { } + + // This is not considered an implementation of the interface method + // CoreCLR doesn't treat it that way either. + public void ImplementedNoNewSlot () { } + + public void ImplementedNoNewSlotUnused () { } + + [Kept] + public void ImplementedNewSlot () { } + + public void ImplementedNewSlotUnused () { } + + [Kept] + public static void Test () + { + IInterfaceWithoutNewSlot instance = new ImplementationType (); + instance.AbstractNoNewSlot (); + instance.AbstractNewSlot (); + instance.ImplementedNoNewSlot (); + instance.ImplementedNewSlot (); + } + } + } +}