diff --git a/src/tools/illink/illink.sln b/src/tools/illink/illink.sln
index 87e99d208c7fb..9b8d904942baa 100644
--- a/src/tools/illink/illink.sln
+++ b/src/tools/illink/illink.sln
@@ -223,6 +223,7 @@ Global
SolutionGuid = {E43A3901-42B0-48CA-BB36-5CD40A99A6EE}
EndGlobalSection
GlobalSection(SharedMSBuildProjectFiles) = preSolution
+ test\Trimming.Tests.Shared\Trimming.Tests.Shared.projitems*{400a1561-b6b6-482d-9e4c-3ddaede5bd07}*SharedItemsImports = 5
src\ILLink.Shared\ILLink.Shared.projitems*{dd28e2b1-057b-4b4d-a04d-b2ebd9e76e46}*SharedItemsImports = 5
src\ILLink.Shared\ILLink.Shared.projitems*{f1a44a78-34ee-408b-8285-9a26f0e7d4f2}*SharedItemsImports = 5
src\ILLink.Shared\ILLink.Shared.projitems*{ff598e93-8e9e-4091-9f50-61a7572663ae}*SharedItemsImports = 13
diff --git a/src/tools/illink/src/linker/CompatibilitySuppressions.xml b/src/tools/illink/src/linker/CompatibilitySuppressions.xml
index 4a0a6296c4e2f..7bf0a1e0ce697 100644
--- a/src/tools/illink/src/linker/CompatibilitySuppressions.xml
+++ b/src/tools/illink/src/linker/CompatibilitySuppressions.xml
@@ -253,6 +253,10 @@
CP0001
T:Mono.Linker.ILogger
+
+ CP0001
+ T:Mono.Linker.InterfaceImplementor
+
CP0001
T:Mono.Linker.InternalErrorException
@@ -1481,10 +1485,6 @@
CP0002
M:Mono.Linker.OverrideInformation.get_IsOverrideOfInterfaceMember
-
- CP0002
- M:Mono.Linker.OverrideInformation.get_IsStaticInterfaceMethodPair
-
CP0002
M:Mono.Linker.Steps.BaseStep.get_MarkingHelpers
diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
index 0d0543dcdcf56..32fd98cbe039c 100644
--- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
+++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
@@ -701,17 +701,16 @@ void ProcessVirtualMethod (MethodDefinition method)
var defaultImplementations = Annotations.GetDefaultInterfaceImplementations (method);
if (defaultImplementations is not null) {
foreach (var dimInfo in defaultImplementations) {
- ProcessDefaultImplementation (dimInfo.ImplementingType, dimInfo.InterfaceImpl, dimInfo.DefaultInterfaceMethod);
+ ProcessDefaultImplementation (dimInfo);
- var ov = new OverrideInformation (method, dimInfo.DefaultInterfaceMethod, Context, dimInfo.InterfaceImpl);
- if (IsInterfaceImplementationMethodNeededByTypeDueToInterface (ov, dimInfo.ImplementingType))
- MarkMethod (ov.Override, new DependencyInfo (DependencyKind.Override, ov.Base), ScopeStack.CurrentScope.Origin);
+ if (IsInterfaceImplementationMethodNeededByTypeDueToInterface (dimInfo))
+ MarkMethod (dimInfo.Override, new DependencyInfo (DependencyKind.Override, dimInfo.Base), ScopeStack.CurrentScope.Origin);
}
}
var overridingMethods = Annotations.GetOverrides (method);
if (overridingMethods is not null) {
- foreach (var ov in overridingMethods) {
- if (IsInterfaceImplementationMethodNeededByTypeDueToInterface (ov, ov.Override.DeclaringType))
+ foreach (OverrideInformation ov in overridingMethods) {
+ if (IsInterfaceImplementationMethodNeededByTypeDueToInterface (ov))
MarkMethod (ov.Override, new DependencyInfo (DependencyKind.Override, ov.Base), ScopeStack.CurrentScope.Origin);
}
}
@@ -819,13 +818,14 @@ bool RequiresInterfaceRecursively (TypeDefinition typeToExamine, TypeDefinition
return false;
}
- void ProcessDefaultImplementation (TypeDefinition typeWithDefaultImplementedInterfaceMethod, InterfaceImplementation implementation, MethodDefinition implementationMethod)
+ void ProcessDefaultImplementation (OverrideInformation ov)
{
- if ((!implementationMethod.IsStatic && !Annotations.IsInstantiated (typeWithDefaultImplementedInterfaceMethod))
- || implementationMethod.IsStatic && !Annotations.IsRelevantToVariantCasting (typeWithDefaultImplementedInterfaceMethod))
+ Debug.Assert (ov.IsOverrideOfInterfaceMember);
+ if ((!ov.Override.IsStatic && !Annotations.IsInstantiated (ov.InterfaceImplementor.Implementor))
+ || ov.Override.IsStatic && !Annotations.IsRelevantToVariantCasting (ov.InterfaceImplementor.Implementor))
return;
- MarkInterfaceImplementation (implementation);
+ MarkInterfaceImplementation (ov.InterfaceImplementor.InterfaceImplementation);
}
void MarkMarshalSpec (IMarshalInfoProvider spec, in DependencyInfo reason)
@@ -2549,11 +2549,11 @@ bool IsMethodNeededByTypeDueToPreservedScope (MethodDefinition method)
///
/// Returns true if the override method is required due to the interface that the base method is declared on. See doc at for explanation of logic.
///
- bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformation overrideInformation, TypeDefinition typeThatImplsInterface)
+ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformation overrideInformation)
{
var @base = overrideInformation.Base;
var method = overrideInformation.Override;
- Debug.Assert (@base.DeclaringType.IsInterface);
+ Debug.Assert (overrideInformation.IsOverrideOfInterfaceMember);
if (@base is null || method is null || @base.DeclaringType is null)
return false;
@@ -2562,7 +2562,7 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat
// If the interface implementation is not marked, do not mark the implementation method
// A type that doesn't implement the interface isn't required to have methods that implement the interface.
- InterfaceImplementation? iface = overrideInformation.MatchingInterfaceImplementation;
+ InterfaceImplementation? iface = overrideInformation.InterfaceImplementor.InterfaceImplementation;
if (!((iface is not null && Annotations.IsMarked (iface))
|| IsInterfaceImplementationMarkedRecursively (method.DeclaringType, @base.DeclaringType)))
return false;
@@ -2580,12 +2580,12 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat
// If the method is static and the implementing type is relevant to variant casting, mark the implementation method.
// A static method may only be called through a constrained call if the type is relevant to variant casting.
if (@base.IsStatic)
- return Annotations.IsRelevantToVariantCasting (typeThatImplsInterface)
+ return Annotations.IsRelevantToVariantCasting (overrideInformation.InterfaceImplementor.Implementor)
|| IgnoreScope (@base.DeclaringType.Scope);
// If the implementing type is marked as instantiated, mark the implementation method.
// If the type is not instantiated, do not mark the implementation method
- return Annotations.IsInstantiated (typeThatImplsInterface);
+ return Annotations.IsInstantiated (overrideInformation.InterfaceImplementor.Implementor);
}
static bool IsSpecialSerializationConstructor (MethodDefinition method)
@@ -3256,7 +3256,7 @@ protected virtual void ProcessMethod (MethodDefinition method, in DependencyInfo
// Only if the interface method is referenced, then all the methods which implemented must be kept, but not the other way round.
if (!markAllOverrides &&
Context.Resolve (@base) is MethodDefinition baseDefinition
- && new OverrideInformation.OverridePair (baseDefinition, method).IsStaticInterfaceMethodPair ())
+ && baseDefinition.DeclaringType.IsInterface && baseDefinition.IsStatic && method.IsStatic)
continue;
MarkMethod (@base, new DependencyInfo (DependencyKind.MethodImplOverride, method), ScopeStack.CurrentScope.Origin);
MarkExplicitInterfaceImplementation (method, @base);
diff --git a/src/tools/illink/src/linker/Linker/Annotations.cs b/src/tools/illink/src/linker/Linker/Annotations.cs
index 8f7747cba3543..a7b3198265e81 100644
--- a/src/tools/illink/src/linker/Linker/Annotations.cs
+++ b/src/tools/illink/src/linker/Linker/Annotations.cs
@@ -462,7 +462,7 @@ public bool IsPublic (IMetadataTokenProvider provider)
/// DefaultInterfaceMethod is the method that implements .
///
/// The interface method to find default implementations for
- public IEnumerable<(TypeDefinition ImplementingType, InterfaceImplementation InterfaceImpl, MethodDefinition DefaultInterfaceMethod)>? GetDefaultInterfaceImplementations (MethodDefinition method)
+ public IEnumerable? GetDefaultInterfaceImplementations (MethodDefinition method)
{
return TypeMapInfo.GetDefaultInterfaceImplementations (method);
}
diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs
new file mode 100644
index 0000000000000..e981ce872703f
--- /dev/null
+++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs
@@ -0,0 +1,59 @@
+// 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;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Mono.Cecil;
+
+namespace Mono.Linker
+{
+ public class InterfaceImplementor
+ {
+ ///
+ /// The type that implements .
+ ///
+ public TypeDefinition Implementor { get; }
+ ///
+ /// The .interfaceimpl on that points to
+ ///
+ public InterfaceImplementation InterfaceImplementation { get; }
+ ///
+ /// The type of the interface that is implemented by
+ ///
+ public TypeDefinition InterfaceType { get; }
+
+ public InterfaceImplementor (TypeDefinition implementor, InterfaceImplementation interfaceImplementation, TypeDefinition interfaceType, IMetadataResolver resolver)
+ {
+ Implementor = implementor;
+ InterfaceImplementation = interfaceImplementation;
+ InterfaceType = interfaceType;
+ Debug.Assert(resolver.Resolve (interfaceImplementation.InterfaceType) == interfaceType);
+ }
+
+ public static InterfaceImplementor Create(TypeDefinition implementor, TypeDefinition interfaceType, IMetadataResolver resolver)
+ {
+ foreach(InterfaceImplementation iface in implementor.Interfaces) {
+ if (resolver.Resolve(iface.InterfaceType) == interfaceType) {
+ return new InterfaceImplementor(implementor, iface, interfaceType, resolver);
+ }
+ }
+
+ Queue ifacesToCheck = new ();
+ ifacesToCheck.Enqueue(implementor);
+ while (ifacesToCheck.Count > 0) {
+ var currentIface = ifacesToCheck.Dequeue ();
+
+ foreach(InterfaceImplementation ifaceImpl in currentIface.Interfaces) {
+ var iface = resolver.Resolve (ifaceImpl.InterfaceType);
+ if (iface == interfaceType) {
+ return new InterfaceImplementor(implementor, ifaceImpl, interfaceType, resolver);
+ }
+ ifacesToCheck.Enqueue (iface);
+ }
+ }
+ throw new InvalidOperationException ($"Type '{implementor.FullName}' does not implement interface '{interfaceType.FullName}' directly or through any interfaces");
+ }
+ }
+}
diff --git a/src/tools/illink/src/linker/Linker/OverrideInformation.cs b/src/tools/illink/src/linker/Linker/OverrideInformation.cs
index 077353eb2ee7b..0727d5d25c19a 100644
--- a/src/tools/illink/src/linker/Linker/OverrideInformation.cs
+++ b/src/tools/illink/src/linker/Linker/OverrideInformation.cs
@@ -3,71 +3,39 @@
using System.Diagnostics;
using Mono.Cecil;
+using System.Diagnostics.CodeAnalysis;
namespace Mono.Linker
{
[DebuggerDisplay ("{Override}")]
public class OverrideInformation
{
- readonly ITryResolveMetadata resolver;
- readonly OverridePair _pair;
- private InterfaceImplementation? _matchingInterfaceImplementation;
+ public MethodDefinition Base { get; }
- public OverrideInformation (MethodDefinition @base, MethodDefinition @override, ITryResolveMetadata resolver, InterfaceImplementation? matchingInterfaceImplementation = null)
- {
- _pair = new OverridePair (@base, @override);
- _matchingInterfaceImplementation = matchingInterfaceImplementation;
- this.resolver = resolver;
- }
- public readonly record struct OverridePair (MethodDefinition Base, MethodDefinition Override)
- {
- public bool IsStaticInterfaceMethodPair () => Base.DeclaringType.IsInterface && Base.IsStatic && Override.IsStatic;
- public InterfaceImplementation? GetMatchingInterfaceImplementation (ITryResolveMetadata resolver)
- {
- if (!Base.DeclaringType.IsInterface)
- return null;
- var interfaceType = Base.DeclaringType;
- foreach (var @interface in Override.DeclaringType.Interfaces) {
- if (resolver.TryResolve (@interface.InterfaceType)?.Equals (interfaceType) == true) {
- return @interface;
- }
- }
- return null;
- }
- }
+ public MethodDefinition Override { get; }
- public MethodDefinition Base { get => _pair.Base; }
- public MethodDefinition Override { get => _pair.Override; }
- public InterfaceImplementation? MatchingInterfaceImplementation {
- get {
- if (_matchingInterfaceImplementation is not null)
- return _matchingInterfaceImplementation;
- _matchingInterfaceImplementation = _pair.GetMatchingInterfaceImplementation (resolver);
- return _matchingInterfaceImplementation;
- }
- }
+ internal InterfaceImplementor? InterfaceImplementor { get; }
- public bool IsOverrideOfInterfaceMember {
- get {
- if (MatchingInterfaceImplementation != null)
- return true;
-
- return Base.DeclaringType.IsInterface;
- }
+ internal OverrideInformation (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null)
+ {
+ Base = @base;
+ Override = @override;
+ InterfaceImplementor = interfaceImplementor;
+ // Ensure we have an interface implementation if the base method is from an interface and the override method is on a class
+ Debug.Assert(@base.DeclaringType.IsInterface && interfaceImplementor != null
+ || !@base.DeclaringType.IsInterface && interfaceImplementor == null);
+ // Ensure the interfaceImplementor is for the interface we expect
+ Debug.Assert (@base.DeclaringType.IsInterface ? interfaceImplementor!.InterfaceType == @base.DeclaringType : true);
}
- public TypeDefinition? InterfaceType {
- get {
- if (!IsOverrideOfInterfaceMember)
- return null;
+ public InterfaceImplementation? MatchingInterfaceImplementation
+ => InterfaceImplementor?.InterfaceImplementation;
- if (MatchingInterfaceImplementation != null)
- return resolver.TryResolve (MatchingInterfaceImplementation.InterfaceType);
-
- return Base.DeclaringType;
- }
- }
+ public TypeDefinition? InterfaceType
+ => InterfaceImplementor?.InterfaceType;
- public bool IsStaticInterfaceMethodPair => _pair.IsStaticInterfaceMethodPair ();
+ [MemberNotNullWhen (true, nameof (InterfaceImplementor), nameof (MatchingInterfaceImplementation))]
+ public bool IsOverrideOfInterfaceMember
+ => InterfaceImplementor != null;
}
}
diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs
index ef58e69c36fd2..bb2836a804d7b 100644
--- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs
+++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs
@@ -29,9 +29,11 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
+using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+using System.Linq;
using Mono.Cecil;
namespace Mono.Linker
@@ -43,7 +45,7 @@ public class TypeMapInfo
readonly LinkContext context;
protected readonly Dictionary> base_methods = new Dictionary> ();
protected readonly Dictionary> override_methods = new Dictionary> ();
- protected readonly Dictionary> default_interface_implementations = new Dictionary> ();
+ protected readonly Dictionary> default_interface_implementations = new Dictionary> ();
public TypeMapInfo (LinkContext context)
{
@@ -92,41 +94,41 @@ public void EnsureProcessed (AssemblyDefinition assembly)
/// DefaultInterfaceMethod is the method that implements .
///
/// The interface method to find default implementations for
- public IEnumerable<(TypeDefinition ImplementingType, InterfaceImplementation InterfaceImpl, MethodDefinition DefaultImplementationMethod)>? GetDefaultInterfaceImplementations (MethodDefinition baseMethod)
+ public IEnumerable? GetDefaultInterfaceImplementations (MethodDefinition baseMethod)
{
default_interface_implementations.TryGetValue (baseMethod, out var ret);
return ret;
}
- public void AddBaseMethod (MethodDefinition method, MethodDefinition @base, InterfaceImplementation? matchingInterfaceImplementation)
+ public void AddBaseMethod (MethodDefinition method, MethodDefinition @base, InterfaceImplementor? interfaceImplementor)
{
if (!base_methods.TryGetValue (method, out List? methods)) {
methods = new List ();
base_methods[method] = methods;
}
- methods.Add (new OverrideInformation (@base, method, context, matchingInterfaceImplementation));
+ methods.Add (new OverrideInformation (@base, method, interfaceImplementor));
}
- public void AddOverride (MethodDefinition @base, MethodDefinition @override, InterfaceImplementation? matchingInterfaceImplementation = null)
+ public void AddOverride (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null)
{
if (!override_methods.TryGetValue (@base, out List? methods)) {
methods = new List ();
override_methods.Add (@base, methods);
}
- methods.Add (new OverrideInformation (@base, @override, context, matchingInterfaceImplementation));
+ methods.Add (new OverrideInformation (@base, @override, interfaceImplementor));
}
- public void AddDefaultInterfaceImplementation (MethodDefinition @base, TypeDefinition implementingType, (InterfaceImplementation, MethodDefinition) matchingInterfaceImplementation)
+ public void AddDefaultInterfaceImplementation (MethodDefinition @base, InterfaceImplementor interfaceImplementor, MethodDefinition defaultImplementationMethod)
{
Debug.Assert(@base.DeclaringType.IsInterface);
if (!default_interface_implementations.TryGetValue (@base, out var implementations)) {
- implementations = new List<(TypeDefinition, InterfaceImplementation, MethodDefinition)> ();
+ implementations = new List ();
default_interface_implementations.Add (@base, implementations);
}
- implementations.Add ((implementingType, matchingInterfaceImplementation.Item1, matchingInterfaceImplementation.Item2));
+ implementations.Add (new (@base, defaultImplementationMethod, interfaceImplementor));
}
protected virtual void MapType (TypeDefinition type)
@@ -168,14 +170,14 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type)
// Try to find an implementation with a name/sig match on the current type
MethodDefinition? exactMatchOnType = TryMatchMethod (type, interfaceMethod);
if (exactMatchOnType != null) {
- AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType);
+ AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType, new (type, interfaceImpl.OriginalImpl, resolvedInterfaceMethod.DeclaringType, context));
continue;
}
// Next try to find an implementation with a name/sig match in the base hierarchy
var @base = GetBaseMethodInTypeHierarchy (type, interfaceMethod);
if (@base != null) {
- AnnotateMethods (resolvedInterfaceMethod, @base, interfaceImpl.OriginalImpl);
+ AnnotateMethods (resolvedInterfaceMethod, @base, new (type, interfaceImpl.OriginalImpl, resolvedInterfaceMethod.DeclaringType, context));
continue;
}
}
@@ -211,24 +213,29 @@ void MapVirtualMethod (MethodDefinition method)
if (@base == null)
return;
+ Debug.Assert(!@base.DeclaringType.IsInterface);
+
AnnotateMethods (@base, method);
}
void MapOverrides (MethodDefinition method)
{
- foreach (MethodReference override_ref in method.Overrides) {
- MethodDefinition? @override = context.TryResolve (override_ref);
- if (@override == null)
+ foreach (MethodReference baseMethodRef in method.Overrides) {
+ MethodDefinition? baseMethod = context.TryResolve (baseMethodRef);
+ if (baseMethod == null)
continue;
-
- AnnotateMethods (@override, method);
+ if (baseMethod.DeclaringType.IsInterface) {
+ AnnotateMethods (baseMethod, method, InterfaceImplementor.Create (method.DeclaringType, baseMethod.DeclaringType, context));
+ } else {
+ AnnotateMethods (baseMethod, method);
+ }
}
}
- void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, InterfaceImplementation? matchingInterfaceImplementation = null)
+ void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null)
{
- AddBaseMethod (@override, @base, matchingInterfaceImplementation);
- AddOverride (@base, @override, matchingInterfaceImplementation);
+ AddBaseMethod (@override, @base, interfaceImplementor);
+ AddOverride (@base, @override, interfaceImplementor);
}
MethodDefinition? GetBaseMethodInTypeHierarchy (MethodDefinition method)
@@ -290,7 +297,7 @@ void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, Interf
///
/// The InterfaceImplementation on that points to the DeclaringType of .
///
- void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplementsInterface, TypeDefinition typeThatMayHaveDIM, MethodDefinition interfaceMethodToBeImplemented, InterfaceImplementation implOfInterface)
+ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplementsInterface, TypeDefinition typeThatMayHaveDIM, MethodDefinition interfaceMethodToBeImplemented, InterfaceImplementation originalInterfaceImpl)
{
// Go over all interfaces, trying to find a method that is an explicit MethodImpl of the
// interface method in question.
@@ -305,7 +312,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement
foreach (var potentialImplMethod in potentialImplInterface.Methods) {
if (potentialImplMethod == interfaceMethodToBeImplemented &&
!potentialImplMethod.IsAbstract) {
- AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, typeThatImplementsInterface, (implOfInterface, potentialImplMethod));
+ AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl, interfaceMethodToBeImplemented.DeclaringType, context), potentialImplMethod);
foundImpl = true;
break;
}
@@ -314,9 +321,9 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement
continue;
// This method is an override of something. Let's see if it's the method we are looking for.
- foreach (var @override in potentialImplMethod.Overrides) {
- if (context.TryResolve (@override) == interfaceMethodToBeImplemented) {
- AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, typeThatImplementsInterface, (implOfInterface, potentialImplMethod));
+ foreach (var baseMethod in potentialImplMethod.Overrides) {
+ if (context.TryResolve (baseMethod) == interfaceMethodToBeImplemented) {
+ AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl, interfaceMethodToBeImplemented.DeclaringType, context), @potentialImplMethod);
foundImpl = true;
break;
}
@@ -330,7 +337,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement
// We haven't found a MethodImpl on the current interface, but one of the interfaces
// this interface requires could still provide it.
if (!foundImpl) {
- FindAndAddDefaultInterfaceImplementations (typeThatImplementsInterface, potentialImplInterface, interfaceMethodToBeImplemented, implOfInterface);
+ FindAndAddDefaultInterfaceImplementations (typeThatImplementsInterface, potentialImplInterface, interfaceMethodToBeImplemented, originalInterfaceImpl);
}
}
}
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 2e1a2bbcb3454..649b8449527f7 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
@@ -15,6 +15,12 @@ public Task CanDisableUnusedInterfaces ()
return RunTest (allowMissingWarnings: true);
}
+ [Fact]
+ public Task InterfaceImplementedThroughBaseInterface ()
+ {
+ return RunTest (allowMissingWarnings: true);
+ }
+
[Fact]
public Task InterfaceOnUninstantiatedTypeRemoved ()
{
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedThroughBaseInterface.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedThroughBaseInterface.il
new file mode 100644
index 0000000000000..61080f8b7d066
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedThroughBaseInterface.il
@@ -0,0 +1,48 @@
+// 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 beforefieldinit IBase
+{
+ // Methods
+ .method public hidebysig newslot abstract virtual
+ instance void M () cil managed
+ {
+ } // end of method IBase::M
+
+} // end of class IBase
+
+.class interface public auto ansi abstract beforefieldinit IDerived
+ implements IBase
+{
+} // end of class IDerived
+
+.class public auto ansi beforefieldinit C
+ extends [System.Runtime]System.Object
+ implements IDerived
+{
+ // Methods
+ .method private final hidebysig newslot virtual
+ instance void IBase.M () cil managed
+ {
+ .override method instance void IBase::M()
+ // Method begins at RVA 0x2050
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0001: ret
+ } // end of method C::IBase.M
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x2053
+ // Code size 8 (0x8)
+ .maxstack 8
+
+ IL_0007: ret
+ } // end of method C::.ctor
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedThroughBaseInterface.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedThroughBaseInterface.cs
new file mode 100644
index 0000000000000..e701fb9c28ba6
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedThroughBaseInterface.cs
@@ -0,0 +1,34 @@
+// 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
+{
+ [SetupLinkerArgument ("--skip-unresolved", "true")]
+ [SetupLinkerArgument ("-a", "test.exe", "library")]
+ [SetupLinkerArgument ("-a", "library.dll", "library")]
+ [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")]
+ [Define ("IL_ASSEMBLY_AVAILABLE")]
+ [SetupCompileBefore ("library.dll", new[] { "Dependencies/InterfaceImplementedThroughBaseInterface.il" })]
+ [SkipILVerify]
+
+#if IL_ASSEMBLY_AVAILABLE
+ [KeptMemberInAssembly ("library.dll", typeof(C), "IBase.M()")]
+#endif
+ [KeptMember(".ctor()")]
+ public class InterfaceImplementedThroughBaseInterface
+ {
+ public static void Main ()
+ {
+ }
+ }
+}
+
+