diff --git a/src/tools/illink/src/linker/Linker.Dataflow/FlowAnnotations.cs b/src/tools/illink/src/linker/Linker.Dataflow/FlowAnnotations.cs
index d8cee744c9c6b..b21b2aebe50ea 100644
--- a/src/tools/illink/src/linker/Linker.Dataflow/FlowAnnotations.cs
+++ b/src/tools/illink/src/linker/Linker.Dataflow/FlowAnnotations.cs
@@ -559,8 +559,8 @@ void ValidateMethodGenericParametersHaveNoAnnotations (DynamicallyAccessedMember
void LogValidationWarning (IMetadataTokenProvider provider, IMetadataTokenProvider baseProvider, OverrideInformation ov)
{
- IMemberDefinition origin = (ov.IsOverrideOfInterfaceMember && ov.InterfaceImplementor.Implementor != ov.Override.DeclaringType)
- ? ov.InterfaceImplementor.Implementor
+ IMemberDefinition origin = (ov.IsOverrideOfInterfaceMember && ov.RuntimeInterfaceImplementation.Implementor != ov.Override.DeclaringType)
+ ? ov.RuntimeInterfaceImplementation.Implementor
: ov.Override;
Debug.Assert (provider.GetType () == baseProvider.GetType ());
Debug.Assert (!(provider is GenericParameter genericParameter) || genericParameter.DeclaringMethod != null);
diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
index 2e41771ee3963..193596246d5a7 100644
--- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
+++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
@@ -32,6 +32,7 @@
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
@@ -156,6 +157,7 @@ internal DynamicallyAccessedMembersTypeHierarchy DynamicallyAccessedMembersTypeH
DependencyKind.ReturnTypeMarshalSpec,
DependencyKind.DynamicInterfaceCastableImplementation,
DependencyKind.XmlDescriptor,
+ DependencyKind.InterfaceImplementationOnType,
};
static readonly DependencyKind[] _methodReasons = new DependencyKind[] {
@@ -713,13 +715,6 @@ void MarkMethodIfNeededByBaseMethod (MethodDefinition method, MessageOrigin orig
}
}
- ///
- /// Returns true if implements and the interface implementation is marked,
- /// or if any marked interface implementations on are interfaces that implement and that interface implementation is marked
- ///
- bool IsInterfaceImplementationMarkedRecursively (TypeDefinition type, TypeDefinition interfaceType)
- => IsInterfaceImplementationMarkedRecursively (type, interfaceType, Context);
-
///
/// Returns true if implements and the interface implementation is marked,
/// or if any marked interface implementations on are interfaces that implement and that interface implementation is marked
@@ -740,18 +735,19 @@ internal static bool IsInterfaceImplementationMarkedRecursively (TypeDefinition
return true;
}
}
-
+ if (type.BaseType is not null && context.Resolve (type.BaseType) is { } baseType)
+ return IsInterfaceImplementationMarkedRecursively (baseType, interfaceType, context);
return false;
}
void ProcessDefaultImplementation (OverrideInformation ov, MessageOrigin origin)
{
Debug.Assert (ov.IsOverrideOfInterfaceMember);
- if ((!ov.Override.IsStatic && !Annotations.IsInstantiated (ov.InterfaceImplementor.Implementor))
- || ov.Override.IsStatic && !Annotations.IsRelevantToVariantCasting (ov.InterfaceImplementor.Implementor))
+ if ((!ov.Override.IsStatic && !Annotations.IsInstantiated (ov.RuntimeInterfaceImplementation.Implementor))
+ || ov.Override.IsStatic && !Annotations.IsRelevantToVariantCasting (ov.RuntimeInterfaceImplementation.Implementor))
return;
- MarkInterfaceImplementation (ov.InterfaceImplementor.InterfaceImplementation, origin);
+ MarkRuntimeInterfaceImplementation (ov.RuntimeInterfaceImplementation, origin);
}
void MarkMarshalSpec (IMarshalInfoProvider spec, in DependencyInfo reason, MessageOrigin origin)
@@ -2327,38 +2323,37 @@ void MarkNamedProperty (TypeDefinition type, string property_name, in Dependency
void MarkInterfaceImplementations (TypeDefinition type)
{
- var ifaces = Annotations.GetRecursiveInterfaces (type);
- if (ifaces is null)
- return;
- foreach (var (ifaceType, impls) in ifaces) {
+ var ifaces = Annotations.GetRuntimeInterfaces (type);
+ foreach (var runtimeInterface in ifaces) {
// Only mark interface implementations of interface types that have been marked.
// This enables stripping of interfaces that are never used
- if (ShouldMarkInterfaceImplementationList (type, impls, ifaceType))
- MarkInterfaceImplementationList (impls, new MessageOrigin (type));
+ if (ShouldMarkRuntimeInterfaceImplementation (runtimeInterface))
+ MarkRuntimeInterfaceImplementation (runtimeInterface, new MessageOrigin (type));
}
}
-
- protected virtual bool ShouldMarkInterfaceImplementationList (TypeDefinition type, List ifaces, TypeReference ifaceType)
+ internal bool ShouldMarkRuntimeInterfaceImplementation (RuntimeInterfaceImplementation runtimeInterfaceImplementation)
{
- if (ifaces.All (Annotations.IsMarked))
+ var implementingType = runtimeInterfaceImplementation.Implementor;
+ var ifaceType = runtimeInterfaceImplementation.InterfaceTypeDefinition;
+ if (Annotations.IsMarked (runtimeInterfaceImplementation))
return false;
- if (!Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, type))
+ if (!Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, implementingType))
return true;
- if (Context.Resolve (ifaceType) is not TypeDefinition resolvedInterfaceType)
+ if (ifaceType is null)
return false;
- if (Annotations.IsMarked (resolvedInterfaceType))
+ if (Annotations.IsMarked (ifaceType))
return true;
// It's hard to know if a com or windows runtime interface will be needed from managed code alone,
// so as a precaution we will mark these interfaces once the type is instantiated
- if (Context.KeepComInterfaces && (resolvedInterfaceType.IsImport || resolvedInterfaceType.IsWindowsRuntime))
+ if (Context.KeepComInterfaces && (ifaceType.IsImport || ifaceType.IsWindowsRuntime))
return true;
- return IsFullyPreserved (type);
+ return IsFullyPreserved (implementingType);
}
void MarkGenericParameterProvider (IGenericParameterProvider provider, MessageOrigin origin)
@@ -2438,11 +2433,13 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat
if (Annotations.IsMarked (method))
return false;
+ if (!Annotations.IsMarked (overrideInformation.RuntimeInterfaceImplementation.Implementor))
+ return false;
+
// 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.InterfaceImplementor.InterfaceImplementation;
- if (!((iface is not null && Annotations.IsMarked (iface))
- || IsInterfaceImplementationMarkedRecursively (method.DeclaringType, @base.DeclaringType)))
+ // We must check all possible ways the interface could be implemented by the type (through all recursive interface implementations, not just the primary one)
+ if (!overrideInformation.RuntimeInterfaceImplementation.IsAnyImplementationMarked (Annotations, Context))
return false;
// If the interface method is not marked and the interface doesn't come from a preserved scope, do not mark the implementation method
@@ -2458,12 +2455,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 (overrideInformation.InterfaceImplementor.Implementor)
+ return Annotations.IsRelevantToVariantCasting (overrideInformation.RuntimeInterfaceImplementation.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 (overrideInformation.InterfaceImplementor.Implementor);
+ return Annotations.IsInstantiated (overrideInformation.RuntimeInterfaceImplementation.Implementor);
}
static bool IsSpecialSerializationConstructor (MethodDefinition method)
@@ -2526,31 +2523,15 @@ void MarkCustomMarshalerGetInstance (TypeDefinition type, in DependencyInfo reas
void MarkICustomMarshalerMethods (TypeDefinition inputType, in DependencyInfo reason, MessageOrigin origin)
{
- TypeDefinition? type = inputType;
- do {
- if (!type.HasInterfaces)
+ var runtimeInterfaces = Annotations.GetRuntimeInterfaces (inputType);
+ foreach (var runtimeInterface in runtimeInterfaces) {
+ var iface_type = runtimeInterface.InterfaceTypeDefinition;
+ if (false == iface_type?.IsTypeOf ("System.Runtime.InteropServices", "ICustomMarshaler"))
continue;
+ MarkMethodsIf (iface_type!.Methods, m => !m.IsStatic, reason, origin);
- foreach (var iface in type.Interfaces) {
- var iface_type = iface.InterfaceType;
- if (!iface_type.IsTypeOf ("System.Runtime.InteropServices", "ICustomMarshaler"))
- continue;
-
- //
- // Instead of trying to guess where to find the interface declaration ILLink walks
- // the list of implemented interfaces and resolve the declaration from there
- //
- var tdef = Context.Resolve (iface_type);
- if (tdef == null) {
- return;
- }
-
- MarkMethodsIf (tdef.Methods, m => !m.IsStatic, reason, origin);
-
- MarkInterfaceImplementation (iface, new MessageOrigin (type));
- return;
- }
- } while ((type = Context.TryResolve (type.BaseType)) != null);
+ MarkRuntimeInterfaceImplementation (runtimeInterface, new MessageOrigin (inputType));
+ }
}
bool IsNonEmptyStaticConstructor (MethodDefinition method)
@@ -3246,13 +3227,11 @@ void MarkRuntimeInterfaceImplementation (MethodDefinition method, MethodReferenc
if (!resolvedOverride.DeclaringType.IsInterface)
return;
var interfaceToBeImplemented = ov.DeclaringType;
-
- var ifaces = Annotations.GetRecursiveInterfaces (method.DeclaringType);
- if (ifaces is null)
- return;
+ var ifaces = Annotations.GetRuntimeInterfaces (method.DeclaringType);
+ Debug.Assert (ifaces.SingleOrDefault (i => TypeReferenceEqualityComparer.AreEqual (i.InflatedInterfaceType, interfaceToBeImplemented, Context)) is not null);
foreach (var iface in ifaces) {
- if (TypeReferenceEqualityComparer.AreEqual (iface.InterfaceType, interfaceToBeImplemented, Context)) {
- MarkInterfaceImplementationList (iface.ImplementationChain, new MessageOrigin (method.DeclaringType));
+ if (TypeReferenceEqualityComparer.AreEqual (iface.InflatedInterfaceType, interfaceToBeImplemented, Context)) {
+ MarkRuntimeInterfaceImplementation (iface, new MessageOrigin (method.DeclaringType));
return;
}
}
@@ -3579,7 +3558,7 @@ void MarkInterfacesNeededByBodyStack (MethodIL methodIL)
return;
foreach (var (implementation, type) in implementations)
- MarkInterfaceImplementation (implementation, new MessageOrigin (type));
+ MarkRuntimeInterfaceImplementation (implementation, new MessageOrigin (type));
}
bool InstructionRequiresReflectionMethodBodyScannerForFieldAccess (Instruction instruction)
@@ -3687,10 +3666,13 @@ protected virtual void MarkInstruction (Instruction instruction, MethodDefinitio
}
}
- void MarkInterfaceImplementationList (List ifaces, MessageOrigin origin, DependencyInfo? reason = null)
+ void MarkRuntimeInterfaceImplementation (RuntimeInterfaceImplementation runtimeInterface, MessageOrigin origin, DependencyInfo? reason = null)
{
- foreach (var iface in ifaces) {
- MarkInterfaceImplementation (iface, origin, reason);
+ Annotations.Mark (runtimeInterface);
+ foreach (var iface in runtimeInterface.InterfaceImplementationChains) {
+ MarkType (iface.TypeWithInterfaceImplementation, reason ?? new DependencyInfo (DependencyKind.InterfaceImplementationOnType, origin.Provider), origin);
+ foreach (var impl in iface.InterfaceImplementations)
+ MarkInterfaceImplementation (impl, origin, reason);
}
}
diff --git a/src/tools/illink/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs b/src/tools/illink/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs
index aa9c7ee0d9f09..820d5ca1adc32 100644
--- a/src/tools/illink/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs
+++ b/src/tools/illink/src/linker/Linker.Steps/ValidateVirtualMethodAnnotationsStep.cs
@@ -16,6 +16,8 @@ protected override void Process ()
var baseOverrideInformations = annotations.GetBaseMethods (method);
if (baseOverrideInformations != null) {
foreach (var baseOv in baseOverrideInformations) {
+ if (baseOv.IsOverrideOfInterfaceMember && !baseOv.RuntimeInterfaceImplementation.HasExplicitImplementation)
+ continue;
annotations.FlowAnnotations.ValidateMethodAnnotationsAreSame (baseOv);
ValidateMethodRequiresUnreferencedCodeAreSame (baseOv);
}
@@ -30,6 +32,9 @@ protected override void Process ()
if (annotations.VirtualMethodsWithAnnotationsToValidate.Contains (overrideInformation.Override))
continue;
+ if (overrideInformation.IsOverrideOfInterfaceMember && !overrideInformation.RuntimeInterfaceImplementation.HasExplicitImplementation)
+ continue;
+
annotations.FlowAnnotations.ValidateMethodAnnotationsAreSame (overrideInformation);
ValidateMethodRequiresUnreferencedCodeAreSame (overrideInformation);
}
@@ -51,8 +56,8 @@ void ValidateMethodRequiresUnreferencedCodeAreSame (OverrideInformation ov)
nameof (RequiresUnreferencedCodeAttribute),
method.GetDisplayName (),
baseMethod.GetDisplayName ());
- IMemberDefinition origin = (ov.IsOverrideOfInterfaceMember && ov.InterfaceImplementor.Implementor != method.DeclaringType)
- ? ov.InterfaceImplementor.Implementor
+ IMemberDefinition origin = (ov.IsOverrideOfInterfaceMember && ov.RuntimeInterfaceImplementation.Implementor != method.DeclaringType)
+ ? ov.RuntimeInterfaceImplementation.Implementor
: method;
Context.LogWarning (origin, DiagnosticId.RequiresUnreferencedCodeAttributeMismatch, message);
}
diff --git a/src/tools/illink/src/linker/Linker/Annotations.cs b/src/tools/illink/src/linker/Linker/Annotations.cs
index bfcfe56e7d71e..ba8ad7330f12a 100644
--- a/src/tools/illink/src/linker/Linker/Annotations.cs
+++ b/src/tools/illink/src/linker/Linker/Annotations.cs
@@ -31,6 +31,7 @@
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
@@ -71,6 +72,7 @@ public partial class AnnotationStore
protected readonly HashSet indirectly_called = new HashSet ();
protected readonly HashSet types_relevant_to_variant_casting = new HashSet ();
readonly HashSet reflection_used = new ();
+ readonly HashSet _marked_runtime_interface_implementations = new ();
public AnnotationStore (LinkContext context)
{
@@ -705,9 +707,13 @@ public void EnqueueVirtualMethod (MethodDefinition method)
VirtualMethodsWithAnnotationsToValidate.Add (method);
}
- internal List<(TypeReference InterfaceType, List ImplementationChain)>? GetRecursiveInterfaces (TypeDefinition type)
+ internal ImmutableArray GetRuntimeInterfaces (TypeDefinition type)
{
return TypeMapInfo.GetRecursiveInterfaces (type);
}
+
+ internal bool IsMarked (RuntimeInterfaceImplementation runtimeInterface) => _marked_runtime_interface_implementations.Contains (runtimeInterface);
+
+ internal void Mark (RuntimeInterfaceImplementation runtimeInterface) => _marked_runtime_interface_implementations.Add (runtimeInterface);
}
}
diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementationChain.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementationChain.cs
new file mode 100644
index 0000000000000..f0e93e0668725
--- /dev/null
+++ b/src/tools/illink/src/linker/Linker/InterfaceImplementationChain.cs
@@ -0,0 +1,55 @@
+// 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.Collections.Immutable;
+using System.Diagnostics;
+using Mono.Cecil;
+
+namespace Mono.Linker
+{
+ internal sealed class InterfaceImplementationChain
+ {
+ ///
+ /// The type that has the InterfaceImplementation - either the or a base type of it.
+ ///
+ public TypeReference TypeWithInterfaceImplementation { get; }
+
+ ///
+ /// The path of .interfaceimpl on or a base type that terminates with .
+ ///
+ public ImmutableArray InterfaceImplementations { get; }
+
+ ///
+ /// Returns true if the interface implementation is directly on the implementing type.
+ ///
+ public bool IsExplicitInterfaceImplementation => InterfaceImplementations.Length == 1;
+
+ public InterfaceImplementationChain (TypeReference typeWithInterfaceImplementation, ImmutableArray interfaceImplementation)
+ {
+ Debug.Assert (interfaceImplementation.Length > 0);
+ TypeWithInterfaceImplementation = typeWithInterfaceImplementation;
+ InterfaceImplementations = interfaceImplementation;
+ }
+
+ ///
+ /// Returns true if all the .interfaceImpls in the chain and the are marked.
+ ///
+ ///
+ ///
+ public bool IsMarked (AnnotationStore annotations, ITryResolveMetadata context)
+ {
+ var typeDef = context.TryResolve (TypeWithInterfaceImplementation);
+ // If we have the .interfaceImpls on this type, it must be resolvable
+ Debug.Assert (typeDef is not null);
+ if (!annotations.IsMarked (typeDef))
+ return false;
+
+ foreach (var impl in InterfaceImplementations) {
+ if (!annotations.IsMarked (impl))
+ return false;
+ }
+
+ return true;
+ }
+ }
+}
diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs
deleted file mode 100644
index e981ce872703f..0000000000000
--- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-// 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/LinkContext.cs b/src/tools/illink/src/linker/Linker/LinkContext.cs
index 0301f76f0a9f5..57c4763cf08c4 100644
--- a/src/tools/illink/src/linker/Linker/LinkContext.cs
+++ b/src/tools/illink/src/linker/Linker/LinkContext.cs
@@ -194,7 +194,7 @@ internal TypeNameResolver TypeNameResolver
public SerializationMarker SerializationMarker { get; }
public LinkContext (Pipeline pipeline, ILogger logger, string outputDirectory)
- : this(pipeline, logger, outputDirectory, new UnintializedContextFactory ())
+ : this (pipeline, logger, outputDirectory, new UnintializedContextFactory ())
{
}
diff --git a/src/tools/illink/src/linker/Linker/MethodBodyScanner.cs b/src/tools/illink/src/linker/Linker/MethodBodyScanner.cs
index b3a230f194795..bf2fdb5d5f9a9 100644
--- a/src/tools/illink/src/linker/Linker/MethodBodyScanner.cs
+++ b/src/tools/illink/src/linker/Linker/MethodBodyScanner.cs
@@ -63,7 +63,7 @@ public InterfacesOnStackScanner (LinkContext context)
this.context = context;
}
- public IEnumerable<(InterfaceImplementation, TypeDefinition)>? GetReferencedInterfaces (MethodIL methodIL)
+ public IEnumerable<(RuntimeInterfaceImplementation, TypeDefinition)>? GetReferencedInterfaces (MethodIL methodIL)
{
var possibleStackTypes = AllPossibleStackTypes (methodIL);
if (possibleStackTypes.Count == 0)
@@ -73,7 +73,7 @@ public InterfacesOnStackScanner (LinkContext context)
if (interfaceTypes.Length == 0)
return null;
- var interfaceImplementations = new HashSet<(InterfaceImplementation, TypeDefinition)> ();
+ var interfaceImplementations = new HashSet<(RuntimeInterfaceImplementation, TypeDefinition)> ();
// If a type could be on the stack in the body and an interface it implements could be on the stack on the body
// then we need to mark that interface implementation. When this occurs it is not safe to remove the interface implementation from the type
@@ -139,31 +139,14 @@ HashSet AllPossibleStackTypes (MethodIL methodIL)
return types;
}
- void AddMatchingInterfaces (HashSet<(InterfaceImplementation, TypeDefinition)> results, TypeDefinition type, TypeDefinition[] interfaceTypes)
+ void AddMatchingInterfaces (HashSet<(RuntimeInterfaceImplementation, TypeDefinition)> results, TypeDefinition type, TypeDefinition[] interfaceTypes)
{
- if (!type.HasInterfaces)
- return;
-
- foreach (var interfaceType in interfaceTypes) {
- if (HasInterface (type, interfaceType, out InterfaceImplementation? implementation))
- results.Add ((implementation, type));
- }
- }
-
- bool HasInterface (TypeDefinition type, TypeDefinition interfaceType, [NotNullWhen (true)] out InterfaceImplementation? implementation)
- {
- implementation = null;
- if (!type.HasInterfaces)
- return false;
-
- foreach (var iface in type.Interfaces) {
- if (context.TryResolve (iface.InterfaceType) == interfaceType) {
- implementation = iface;
- return true;
+ var runtimeInterfaces = context.Annotations.GetRuntimeInterfaces (type);
+ foreach (var iface in runtimeInterfaces) {
+ if (interfaceTypes.Contains (iface.InterfaceTypeDefinition)) {
+ results.Add ((iface, type));
}
}
-
- return false;
}
void AddFromGenericInstance (HashSet set, IGenericInstance instance)
diff --git a/src/tools/illink/src/linker/Linker/OverrideInformation.cs b/src/tools/illink/src/linker/Linker/OverrideInformation.cs
index 0727d5d25c19a..fd03529a704c8 100644
--- a/src/tools/illink/src/linker/Linker/OverrideInformation.cs
+++ b/src/tools/illink/src/linker/Linker/OverrideInformation.cs
@@ -1,9 +1,10 @@
// 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.Diagnostics;
-using Mono.Cecil;
using System.Diagnostics.CodeAnalysis;
+using Mono.Cecil;
namespace Mono.Linker
{
@@ -14,28 +15,32 @@ public class OverrideInformation
public MethodDefinition Override { get; }
- internal InterfaceImplementor? InterfaceImplementor { get; }
+ internal RuntimeInterfaceImplementation? RuntimeInterfaceImplementation { get; }
- internal OverrideInformation (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null)
+ internal OverrideInformation (MethodDefinition @base, MethodDefinition @override, RuntimeInterfaceImplementation? runtimeInterface = null)
{
Base = @base;
Override = @override;
- InterfaceImplementor = interfaceImplementor;
+ RuntimeInterfaceImplementation = runtimeInterface;
// 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);
+ Debug.Assert (@base.DeclaringType.IsInterface && runtimeInterface != null
+ || !@base.DeclaringType.IsInterface && runtimeInterface == null);
// Ensure the interfaceImplementor is for the interface we expect
- Debug.Assert (@base.DeclaringType.IsInterface ? interfaceImplementor!.InterfaceType == @base.DeclaringType : true);
+ Debug.Assert (@base.DeclaringType.IsInterface ? runtimeInterface!.InterfaceTypeDefinition == @base.DeclaringType : true);
}
public InterfaceImplementation? MatchingInterfaceImplementation
- => InterfaceImplementor?.InterfaceImplementation;
+ => RuntimeInterfaceImplementation?.InterfaceImplementationChains[0].InterfaceImplementations[0];
public TypeDefinition? InterfaceType
- => InterfaceImplementor?.InterfaceType;
+ => RuntimeInterfaceImplementation?.InterfaceTypeDefinition;
- [MemberNotNullWhen (true, nameof (InterfaceImplementor), nameof (MatchingInterfaceImplementation))]
+ [MemberNotNullWhen (true, nameof (RuntimeInterfaceImplementation))]
public bool IsOverrideOfInterfaceMember
- => InterfaceImplementor != null;
+ => RuntimeInterfaceImplementation != null;
+
+ [MemberNotNullWhen (true, nameof (RuntimeInterfaceImplementation))]
+ internal bool IsInterfaceMethodProvidedByBaseType
+ => IsOverrideOfInterfaceMember && (Override.DeclaringType != RuntimeInterfaceImplementation.Implementor);
}
}
diff --git a/src/tools/illink/src/linker/Linker/RuntimeInterfaceImplementation.cs b/src/tools/illink/src/linker/Linker/RuntimeInterfaceImplementation.cs
new file mode 100644
index 0000000000000..433a39e01be94
--- /dev/null
+++ b/src/tools/illink/src/linker/Linker/RuntimeInterfaceImplementation.cs
@@ -0,0 +1,69 @@
+// 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.Collections.Immutable;
+using System.Diagnostics;
+using System.Linq;
+using Mono.Cecil;
+
+namespace Mono.Linker
+{
+ ///
+ /// Represents an implementation of an interface on a type that may be directly on the type or on a base type or implemented interface.
+ /// This type is considered to implement the interface at runtime, even though the interface may not be directly on the type.
+ ///
+ ///
+ /// This type should be used for marking, but should NOT be used to check if a runtime interface implementation has been marked.
+ /// This type represents the most direct way an interface may be implemented, but it may be implemented in a less direct way that is not represented here.
+ /// You should check all possible implementation 'paths' to determine if an interface is implemented, for example .
+ ///
+ internal sealed class RuntimeInterfaceImplementation
+ {
+ ///
+ /// The type that implements .
+ ///
+ public TypeDefinition Implementor { get; }
+
+ ///
+ /// All the chains of .interfaceImpls that cause to implement
+ ///
+ public ImmutableArray InterfaceImplementationChains { get; }
+
+ ///
+ /// The inflated interface type reference that is implemented by .
+ ///
+ public TypeReference InflatedInterfaceType { get; }
+
+ ///
+ /// The of
+ ///
+ public TypeDefinition? InterfaceTypeDefinition { get; }
+
+ ///
+ /// Returns true if an interface implementation is directly on the implementing type.
+ ///
+ public bool HasExplicitImplementation => InterfaceImplementationChains.Any (c => c.IsExplicitInterfaceImplementation && c.TypeWithInterfaceImplementation == Implementor);
+
+ public RuntimeInterfaceImplementation (TypeDefinition implementor, TypeReference interfaceType, TypeDefinition? interfaceTypeDefinition, IEnumerable interfaceImplementations)
+ {
+ Implementor = implementor;
+ InterfaceImplementationChains = interfaceImplementations.ToImmutableArray ();
+ InflatedInterfaceType = interfaceType;
+ InterfaceTypeDefinition = interfaceTypeDefinition;
+ }
+
+ public bool IsAnyImplementationMarked (AnnotationStore annotations, ITryResolveMetadata context)
+ {
+ if (annotations.IsMarked (this))
+ return true;
+ foreach (var chain in InterfaceImplementationChains) {
+ if (chain.IsMarked (annotations, context)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/src/tools/illink/src/linker/Linker/RuntimeInterfacesAlgorithm.cs b/src/tools/illink/src/linker/Linker/RuntimeInterfacesAlgorithm.cs
new file mode 100644
index 0000000000000..f83432d36cf33
--- /dev/null
+++ b/src/tools/illink/src/linker/Linker/RuntimeInterfacesAlgorithm.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.Collections.Immutable;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Mono.Cecil;
+
+namespace Mono.Linker.Linker
+{
+ internal struct RuntimeInterfacesAlgorithm
+ {
+ readonly ITryResolveMetadata _context;
+ readonly Dictionary> _runtimeInterfaceImpls = new ();
+
+ public RuntimeInterfacesAlgorithm (ITryResolveMetadata context)
+ {
+ this._context = context;
+ }
+
+ public ImmutableArray GetRuntimeInterfaceImplementations (TypeDefinition originalType)
+ {
+ if (_runtimeInterfaceImpls.TryGetValue (originalType, out var runtimeIfaces)) {
+ return runtimeIfaces;
+ }
+
+ Dictionary> interfaceTypeToImplChainMap = new (new TypeReferenceEqualityComparer (_context));
+
+ foreach (var explicitIface in originalType.Interfaces) {
+ interfaceTypeToImplChainMap.AddToList (explicitIface.InterfaceType, new InterfaceImplementationChain (originalType, [explicitIface]));
+
+ var resolvedInterfaceType = _context.TryResolve (explicitIface.InterfaceType);
+ if (resolvedInterfaceType is null) {
+ continue;
+ }
+
+ // Add the recursive interfaces for each explicit interface, prepending the explicit interface on `originalType` to the chain
+ var recursiveIFaces = GetRuntimeInterfaceImplementations (resolvedInterfaceType);
+ foreach (var runtimeImpl in recursiveIFaces) {
+ // Inflate the interface type with the explicit interfaceImpl reference
+ var inflatedInterfaceType = runtimeImpl.InflatedInterfaceType.InflateFrom (explicitIface.InterfaceType as IGenericInstance);
+ foreach (var existingImpl in runtimeImpl.InterfaceImplementationChains) {
+ interfaceTypeToImplChainMap.AddToList (inflatedInterfaceType, new InterfaceImplementationChain (originalType, existingImpl.InterfaceImplementations.Insert (0, explicitIface)));
+ }
+ }
+ }
+
+ if (originalType.BaseType is not null && _context.TryResolve (originalType.BaseType) is { } baseTypeDef) {
+ var baseTypeIfaces = GetRuntimeInterfaceImplementations (baseTypeDef);
+ foreach (var runtimeImpl in baseTypeIfaces) {
+ // Inflate the interface type with the base type reference
+ var inflatedInterfaceType = runtimeImpl.InflatedInterfaceType.InflateFrom (originalType.BaseType as IGenericInstance);
+ foreach (var impl in runtimeImpl.InterfaceImplementationChains) {
+ // Inflate the provider for the first .impl - this could be a different recursive base type for each chain
+ var inflatedImplProvider = impl.TypeWithInterfaceImplementation.InflateFrom (originalType.BaseType as IGenericInstance);
+ interfaceTypeToImplChainMap.AddToList (inflatedInterfaceType, new InterfaceImplementationChain (inflatedImplProvider, impl.InterfaceImplementations));
+ }
+ }
+ }
+
+ if (interfaceTypeToImplChainMap.Count == 0)
+ return ImmutableArray.Empty;
+
+ // Build the ImmutableArray and cache it
+ ImmutableArray.Builder builder = ImmutableArray.CreateBuilder (interfaceTypeToImplChainMap.Count);
+ foreach (var kvp in interfaceTypeToImplChainMap) {
+ builder.Add (new (originalType, kvp.Key, _context.TryResolve (kvp.Key), kvp.Value));
+ }
+ var runtimeInterfaces = builder.MoveToImmutable ();
+ _runtimeInterfaceImpls[originalType] = runtimeInterfaces;
+
+ return runtimeInterfaces;
+ }
+ }
+}
diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs
index 151a307707dd6..98fdd5b56ebea 100644
--- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs
+++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs
@@ -29,27 +29,35 @@
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Mono.Cecil;
+using Mono.Linker.Linker;
namespace Mono.Linker
{
-
public class TypeMapInfo
{
readonly HashSet assemblies = new HashSet ();
- readonly LinkContext context;
+ readonly ITryResolveMetadata context;
protected readonly Dictionary> base_methods = new Dictionary> ();
protected readonly Dictionary> override_methods = new Dictionary> ();
protected readonly Dictionary> default_interface_implementations = new Dictionary> ();
+ readonly RuntimeInterfacesAlgorithm _recursiveInterfaces;
public TypeMapInfo (LinkContext context)
{
this.context = context;
+ _recursiveInterfaces = new (context);
+ }
+
+ internal TypeMapInfo (ITryResolveMetadata context)
+ {
+ this.context = context;
+ _recursiveInterfaces = new (context);
}
public void EnsureProcessed (AssemblyDefinition assembly)
@@ -100,28 +108,20 @@ public void EnsureProcessed (AssemblyDefinition assembly)
return ret;
}
- public void AddBaseMethod (MethodDefinition method, MethodDefinition @base, InterfaceImplementor? interfaceImplementor)
- {
- base_methods.AddToList (method, new OverrideInformation (@base, method, interfaceImplementor));
- }
-
- public void AddOverride (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null)
+ internal void AddBaseMethod (MethodDefinition method, MethodDefinition @base, RuntimeInterfaceImplementation? runtimeInterface)
{
- override_methods.AddToList (@base, new OverrideInformation (@base, @override, interfaceImplementor));
+ base_methods.AddToList (method, new OverrideInformation (@base, method, runtimeInterface));
}
- public void AddDefaultInterfaceImplementation (MethodDefinition @base, InterfaceImplementor interfaceImplementor, MethodDefinition defaultImplementationMethod)
+ internal void AddOverride (MethodDefinition @base, MethodDefinition @override, RuntimeInterfaceImplementation? runtimeInterface = null)
{
- Debug.Assert(@base.DeclaringType.IsInterface);
- default_interface_implementations.AddToList (@base, new OverrideInformation (@base, defaultImplementationMethod, interfaceImplementor));
+ override_methods.AddToList (@base, new OverrideInformation (@base, @override, runtimeInterface));
}
- Dictionary)>> interfaces = new ();
protected virtual void MapType (TypeDefinition type)
{
MapVirtualMethods (type);
MapInterfaceMethodsInTypeHierarchy (type);
- interfaces[type] = GetRecursiveInterfaceImplementations (type);
if (!type.HasNestedTypes)
return;
@@ -130,58 +130,21 @@ protected virtual void MapType (TypeDefinition type)
MapType (nested);
}
- internal List<(TypeReference InterfaceType, List ImplementationChain)>? GetRecursiveInterfaces (TypeDefinition type)
+ internal ImmutableArray GetRecursiveInterfaces (TypeDefinition type)
{
- EnsureProcessed(type.Module.Assembly);
- if (interfaces.TryGetValue (type, out var value))
- return value;
- return null;
- }
-
- List<(TypeReference InterfaceType, List ImplementationChain)> GetRecursiveInterfaceImplementations (TypeDefinition type)
- {
- List<(TypeReference, List)> firstImplementationChain = new ();
-
- AddRecursiveInterfaces (type, [], firstImplementationChain, context);
- Debug.Assert (firstImplementationChain.All (kvp => context.Resolve (kvp.Item1) == context.Resolve (kvp.Item2.Last ().InterfaceType)));
-
- return firstImplementationChain;
-
- static void AddRecursiveInterfaces (TypeReference typeRef, IEnumerable pathToType, List<(TypeReference, List)> firstImplementationChain, LinkContext Context)
- {
- var type = Context.TryResolve (typeRef);
- // If we can't resolve the interface type we can't find recursive interfaces
- if (type is null)
- return;
- // Get all explicit interfaces of this type
- foreach (var iface in type.Interfaces) {
- var interfaceType = iface.InterfaceType.InflateFrom (typeRef as IGenericInstance);
- if (!firstImplementationChain.Any (i => TypeReferenceEqualityComparer.AreEqual (i.Item1, interfaceType, Context))) {
- firstImplementationChain.Add ((interfaceType, pathToType.Append (iface).ToList ()));
- }
- }
-
- // Recursive interfaces after all direct interfaces to preserve Inherit/Implement tree order
- foreach (var iface in type.Interfaces) {
- var ifaceDirectlyOnType = iface.InterfaceType.InflateFrom (typeRef as IGenericInstance);
- AddRecursiveInterfaces (ifaceDirectlyOnType, pathToType.Append (iface), firstImplementationChain, Context);
- }
- }
+ return _recursiveInterfaces.GetRuntimeInterfaceImplementations (type);
}
void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type)
{
- if (!type.HasInterfaces)
+ var runtimeInterfaceImpls = _recursiveInterfaces.GetRuntimeInterfaceImplementations (type);
+ if (runtimeInterfaceImpls.Length == 0)
return;
// Foreach interface and for each newslot virtual method on the interface, try
// to find the method implementation and record it.
- foreach (var interfaceImpl in type.GetInflatedInterfaces (context)) {
- foreach (MethodReference interfaceMethod in interfaceImpl.InflatedInterface.GetMethods (context)) {
- MethodDefinition? resolvedInterfaceMethod = context.TryResolve (interfaceMethod);
- if (resolvedInterfaceMethod == null)
- continue;
-
+ foreach (var interfaceImpl in runtimeInterfaceImpls) {
+ foreach ((MethodReference interfaceMethod, MethodDefinition resolvedInterfaceMethod) in interfaceImpl.InflatedInterfaceType.GetMethods (context)) {
// TODO-NICE: if the interface method is implemented explicitly (with an override),
// we shouldn't need to run the below logic. This results in ILLink potentially
// keeping more methods than needed.
@@ -190,26 +153,28 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type)
|| resolvedInterfaceMethod.IsFinal)
continue;
+ // Note: methods with overrides are annotated in MapVirtualMethods
+
// 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 this logic for static methods.
if (!resolvedInterfaceMethod.IsStatic) {
// 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, new (type, interfaceImpl.OriginalImpl, resolvedInterfaceMethod.DeclaringType, context));
+ AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType, interfaceImpl);
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, new (type, interfaceImpl.OriginalImpl, resolvedInterfaceMethod.DeclaringType, context));
+ AnnotateMethods (resolvedInterfaceMethod, @base, interfaceImpl);
continue;
}
}
// Look for a default implementation last.
- FindAndAddDefaultInterfaceImplementations (type, type, resolvedInterfaceMethod, interfaceImpl.OriginalImpl);
+ FindAndAddDefaultInterfaceImplementations (interfaceImpl, resolvedInterfaceMethod);
}
}
}
@@ -239,7 +204,7 @@ void MapVirtualMethod (MethodDefinition method)
if (@base == null)
return;
- Debug.Assert(!@base.DeclaringType.IsInterface);
+ Debug.Assert (!@base.DeclaringType.IsInterface);
AnnotateMethods (@base, method);
}
@@ -251,17 +216,20 @@ void MapOverrides (MethodDefinition method)
if (baseMethod == null)
continue;
if (baseMethod.DeclaringType.IsInterface) {
- AnnotateMethods (baseMethod, method, InterfaceImplementor.Create (method.DeclaringType, baseMethod.DeclaringType, context));
+ var runtimeInterfaces = _recursiveInterfaces.GetRuntimeInterfaceImplementations (method.DeclaringType);
+ Debug.Assert (runtimeInterfaces.Length > 0);
+ var runtimeInterface = runtimeInterfaces.Single (i => TypeReferenceEqualityComparer.AreEqual (i.InflatedInterfaceType, baseMethodRef.DeclaringType, context));
+ AnnotateMethods (baseMethod, method, runtimeInterface);
} else {
AnnotateMethods (baseMethod, method);
}
}
}
- void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null)
+ void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, RuntimeInterfaceImplementation? runtimeInterface = null)
{
- AddBaseMethod (@override, @base, interfaceImplementor);
- AddOverride (@base, @override, interfaceImplementor);
+ AddBaseMethod (@override, @base, runtimeInterface);
+ AddOverride (@base, @override, runtimeInterface);
}
MethodDefinition? GetBaseMethodInTypeHierarchy (MethodDefinition method)
@@ -323,23 +291,23 @@ 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 originalInterfaceImpl)
+ void FindAndAddDefaultInterfaceImplementations (RuntimeInterfaceImplementation originalInterfaceImpl, MethodDefinition interfaceMethodDef)
{
// Go over all interfaces, trying to find a method that is an explicit MethodImpl of the
// interface method in question.
+ var runtimeIfaces = _recursiveInterfaces.GetRuntimeInterfaceImplementations (originalInterfaceImpl.Implementor);
+ if (runtimeIfaces.Length == 0)
+ return;
- foreach (var interfaceImpl in typeThatMayHaveDIM.Interfaces) {
- var potentialImplInterface = context.TryResolve (interfaceImpl.InterfaceType);
- if (potentialImplInterface == null)
+ foreach (var runtimeIface in runtimeIfaces) {
+ var potentialImplInterface = runtimeIface.InterfaceTypeDefinition;
+ if (potentialImplInterface is null)
continue;
- bool foundImpl = false;
-
foreach (var potentialImplMethod in potentialImplInterface.Methods) {
- if (potentialImplMethod == interfaceMethodToBeImplemented &&
- !potentialImplMethod.IsAbstract) {
- AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl, interfaceMethodToBeImplemented.DeclaringType, context), potentialImplMethod);
- foundImpl = true;
+ if (potentialImplMethod == interfaceMethodDef && !potentialImplMethod.IsAbstract) {
+ // The interface method definition provides a default implementation.
+ AddDefaultInterfaceImplementation (interfaceMethodDef, originalInterfaceImpl, potentialImplMethod);
break;
}
@@ -347,34 +315,31 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement
continue;
// This method is an override of something. Let's see if it's the method we are looking for.
+ bool foundImpl = false;
foreach (var baseMethod in potentialImplMethod.Overrides) {
- if (context.TryResolve (baseMethod) == interfaceMethodToBeImplemented) {
- AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl, interfaceMethodToBeImplemented.DeclaringType, context), @potentialImplMethod);
+ if (context.TryResolve (baseMethod) == interfaceMethodDef) {
+ AddDefaultInterfaceImplementation (interfaceMethodDef, originalInterfaceImpl, @potentialImplMethod);
foundImpl = true;
- break;
}
}
-
if (foundImpl) {
break;
}
}
+ }
- // 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, originalInterfaceImpl);
- }
+ void AddDefaultInterfaceImplementation (MethodDefinition @base, RuntimeInterfaceImplementation runtimeInterface, MethodDefinition defaultImplementationMethod)
+ {
+ Debug.Assert (@base.DeclaringType.IsInterface);
+ default_interface_implementations.AddToList (@base, new OverrideInformation (@base, defaultImplementationMethod, runtimeInterface));
}
}
MethodDefinition? TryMatchMethod (TypeReference type, MethodReference method)
{
- foreach (var candidate in type.GetMethods (context)) {
- var md = context.TryResolve (candidate);
- if (md?.IsVirtual != true)
+ foreach (var (candidate, md) in type.GetMethods (context)) {
+ if (!md.IsVirtual)
continue;
-
if (MethodMatch (candidate, method))
return md;
}
diff --git a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs
index c5704fbb04142..9ef8418023d64 100644
--- a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs
+++ b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs
@@ -279,7 +279,7 @@ private static GenericInstanceType MakeGenericType (IGenericInstance genericInst
return result;
}
- public static IEnumerable GetMethods (this TypeReference type, ITryResolveMetadata resolver)
+ public static IEnumerable<(MethodReference, MethodDefinition)> GetMethods (this TypeReference type, ITryResolveMetadata resolver)
{
TypeDefinition? typeDef = resolver.TryResolve (type);
if (typeDef?.HasMethods != true)
@@ -287,10 +287,10 @@ public static IEnumerable GetMethods (this TypeReference type,
if (type is GenericInstanceType genericInstanceType) {
foreach (var methodDef in typeDef.Methods)
- yield return MakeMethodReferenceForGenericInstanceType (genericInstanceType, methodDef);
+ yield return (MakeMethodReferenceForGenericInstanceType (genericInstanceType, methodDef), methodDef);
} else {
foreach (var method in typeDef.Methods)
- yield return method;
+ yield return (method, method);
}
}
@@ -414,7 +414,8 @@ public static TypeReference WithoutModifiers (this TypeReference type)
// Check whether this type represents a "named type" (i.e. a type that has a name and can be resolved to a TypeDefinition),
// not an array, pointer, byref, or generic parameter. Conceptually this is supposed to represent the same idea as Roslyn's
// INamedTypeSymbol, or ILC's DefType/MetadataType.
- public static bool IsNamedType (this TypeReference typeReference) {
+ public static bool IsNamedType (this TypeReference typeReference)
+ {
if (typeReference.IsDefinition || typeReference.IsGenericInstance)
return true;
diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs
index 3f722ad634e3d..db08bb45f445a 100644
--- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs
+++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/RequiresCapabilityTests.cs
@@ -123,5 +123,11 @@ public Task SuppressRequires ()
{
return RunTest (nameof (SuppressRequires));
}
+
+ [Fact]
+ public Task BaseProvidesInterfaceMethodRequiresMismatch ()
+ {
+ return RunTest (nameof (BaseProvidesInterfaceMethodRequiresMismatch));
+ }
}
}
diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs
index 19189bfcd0170..dc2440a552f56 100644
--- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs
+++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs
@@ -63,6 +63,12 @@ public Task StaticDefaultInterfaceMethodOnStruct ()
return RunTest (allowMissingWarnings: true);
}
+ [Fact]
+ public Task StaticDimOnDerivedInterface ()
+ {
+ return RunTest (allowMissingWarnings: true);
+ }
+
[Fact]
public Task StaticDimProvidedByUnreferencedIfaceInHierarchy ()
{
diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.RecursiveInterfacesTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.RecursiveInterfacesTests.g.cs
index 35cc0b65c0417..1dbdf7bd5c9ac 100644
--- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.RecursiveInterfacesTests.g.cs
+++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.RecursiveInterfacesTests.g.cs
@@ -9,6 +9,12 @@ public sealed partial class RecursiveInterfacesTests : LinkerTestBase
protected override string TestSuiteName => "Inheritance.Interfaces.RecursiveInterfaces";
+ [Fact]
+ public Task BaseTypeMarkedInterfaceDerivedNotKept ()
+ {
+ return RunTest (allowMissingWarnings: true);
+ }
+
[Fact]
public Task GenericInterfaceImplementedRecursively ()
{
@@ -27,6 +33,18 @@ public Task OverrideOfRecursiveInterfaceIsRemoved ()
return RunTest (allowMissingWarnings: true);
}
+ [Fact]
+ public Task RecursiveGenericInterfaces ()
+ {
+ return RunTest (allowMissingWarnings: true);
+ }
+
+ [Fact]
+ public Task RecursiveGenericInterfacesStatic ()
+ {
+ return RunTest (allowMissingWarnings: true);
+ }
+
[Fact]
public Task RecursiveInterfaceKept ()
{
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/BaseTypeMapInfoAttribute.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/BaseTypeMapInfoAttribute.cs
new file mode 100644
index 0000000000000..a9bcbcd5b341e
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/BaseTypeMapInfoAttribute.cs
@@ -0,0 +1,7 @@
+// 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.
+
+namespace Mono.Linker.Tests.Cases.Expectations.Assertions
+{
+ public class BaseTypeMapInfoAttribute : BaseExpectedLinkedBehaviorAttribute { }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/HasRuntimeInterfaceAttribute.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/HasRuntimeInterfaceAttribute.cs
new file mode 100644
index 0000000000000..8d77a80e146a7
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/HasRuntimeInterfaceAttribute.cs
@@ -0,0 +1,23 @@
+// 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;
+
+namespace Mono.Linker.Tests.Cases.Expectations.Assertions
+{
+ [AttributeUsage (AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
+ public class HasRuntimeInterfaceAttribute : Attribute
+ {
+ public HasRuntimeInterfaceAttribute (Type interfaceType, params Type[] implementationChains)
+ {
+ }
+
+ public HasRuntimeInterfaceAttribute (string interfaceType, params string[] implementationChains)
+ {
+ }
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/IsOverrideOfAttribute.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/IsOverrideOfAttribute.cs
new file mode 100644
index 0000000000000..e468492a000b3
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/IsOverrideOfAttribute.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Mono.Linker.Tests.Cases.Expectations.Assertions
+{
+ [AttributeUsage (AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
+ public class IsOverrideOfAttribute : BaseTypeMapInfoAttribute
+ {
+ public IsOverrideOfAttribute (string methodFullName)
+ { }
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/MethodOverridesMethodInAssemblyAttribute.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/MethodOverridesMethodInAssemblyAttribute.cs
new file mode 100644
index 0000000000000..7760f981833c4
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/MethodOverridesMethodInAssemblyAttribute.cs
@@ -0,0 +1,22 @@
+// 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;
+
+namespace Mono.Linker.Tests.Cases.Expectations.Assertions
+{
+ class MethodOverridesMethodInAssemblyAttribute : BaseTypeMapInfoAttribute
+ {
+ public MethodOverridesMethodInAssemblyAttribute (
+ string overridingAssemblyName,
+ string overridingTypeName,
+ string overridingMethodName,
+ string baseAssemblyName,
+ string baseMethodName)
+ { }
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RuntimeInterfaceOnTypeInAssembly.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RuntimeInterfaceOnTypeInAssembly.cs
new file mode 100644
index 0000000000000..b0425ffc221d9
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RuntimeInterfaceOnTypeInAssembly.cs
@@ -0,0 +1,15 @@
+// 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;
+
+namespace Mono.Linker.Tests.Cases.Expectations.Assertions
+{
+ [AttributeUsage (AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
+ public class RuntimeInterfaceOnTypeInAssembly : BaseTypeMapInfoAttribute
+ {
+ public RuntimeInterfaceOnTypeInAssembly (string dllName, string type, string interfaceType, params string[] implementationChains)
+ {
+ }
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimOnDerivedInterface.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimOnDerivedInterface.cs
new file mode 100644
index 0000000000000..0f45b8aec3aff
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimOnDerivedInterface.cs
@@ -0,0 +1,57 @@
+// 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;
+
+namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods
+{
+ class StaticDimOnDerivedInterface
+ {
+ [Kept]
+ public static void Main ()
+ {
+ ITest testImpl = new TestClass ();
+ testImpl.Invoke ();
+
+ CallStaticInvoke ();
+ }
+
+ [Kept]
+ public interface ITestBase
+ {
+ [Kept]
+ static virtual string InvokeStatic () => throw new NotSupportedException ();
+
+ [Kept]
+ string Invoke () => throw new NotSupportedException ();
+ }
+
+ [Kept]
+ [KeptInterface (typeof (ITestBase))]
+ public interface ITest : ITestBase
+ {
+ [Kept]
+ static string ITestBase.InvokeStatic () => "Test";
+
+ [Kept]
+ string ITestBase.Invoke () => "Test";
+ }
+
+ [Kept]
+ [KeptInterface (typeof (ITest))]
+ [KeptInterface (typeof (ITestBase))]
+ public class TestClass : ITest
+ {
+ [Kept]
+ public TestClass () { }
+ }
+
+ [Kept]
+ public static string CallStaticInvoke () where T : ITestBase => T.InvokeStatic ();
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/UnusedDefaultInterfaceImplementation.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/UnusedDefaultInterfaceImplementation.cs
index 23b029f2a3477..b89a50708476c 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/UnusedDefaultInterfaceImplementation.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/UnusedDefaultInterfaceImplementation.cs
@@ -20,8 +20,11 @@ interface IFoo
void InterfaceMethod ();
}
+ [Kept] // Should be removable
+ [KeptInterface (typeof (IFoo))]
interface IDefaultImpl : IFoo
{
+ [Kept]
void IFoo.InterfaceMethod ()
{
}
@@ -29,6 +32,7 @@ void IFoo.InterfaceMethod ()
[Kept]
[KeptInterface (typeof (IFoo))]
+ [KeptInterface (typeof (IDefaultImpl))] // Should be removable
class Foo : IDefaultImpl
{
[Kept]
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs
index b19208bd563d6..26519f7f37e71 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs
@@ -136,15 +136,19 @@ public class UninstantiatedPublicClassWithInterface :
internal UninstantiatedPublicClassWithInterface () { }
[Kept]
+ [IsOverrideOf ("System.Void Mono.Linker.Tests.Cases.Inheritance.Interfaces.InterfaceVariants/IPublicInterface::PublicInterfaceMethod()")]
public void PublicInterfaceMethod () { }
[Kept]
+ [IsOverrideOf ("System.Void Mono.Linker.Tests.Cases.Inheritance.Interfaces.InterfaceVariants/IPublicInterface::ExplicitImplementationPublicInterfaceMethod()")]
void IPublicInterface.ExplicitImplementationPublicInterfaceMethod () { }
[Kept]
+ [IsOverrideOf ("System.Void Mono.Linker.Tests.Cases.Inheritance.Interfaces.InterfaceVariants/IPublicStaticInterface::PublicStaticInterfaceMethod()")]
public static void PublicStaticInterfaceMethod () { }
[Kept]
+ [IsOverrideOf ("System.Void Mono.Linker.Tests.Cases.Inheritance.Interfaces.InterfaceVariants/IPublicStaticInterface::ExplicitImplementationPublicStaticInterfaceMethod()")]
static void IPublicStaticInterface.ExplicitImplementationPublicStaticInterfaceMethod () { }
public void InternalInterfaceMethod () { }
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyBaseInterfaces.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyBaseInterfaces.cs
index f9b338ead868e..c85b9871a2f58 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyBaseInterfaces.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyBaseInterfaces.cs
@@ -17,10 +17,15 @@ interface IFoo
void Method (T arg);
}
+ [Kept] // Should be able to be removed
+ [KeptInterface (typeof (IFoo<>))]
interface IFoo2 : IFoo
{
}
+ [Kept] // Should be able to be removed
+ [KeptInterface (typeof (IFoo<>))]
+ [KeptInterface (typeof (IFoo2<>))]
interface IFoo3 : IFoo2
{
}
@@ -39,8 +44,10 @@ public void Method (object arg)
[KeptMember (".ctor()")]
[KeptBaseType (typeof (BaseFoo))]
[KeptInterface (typeof (IFoo
[Kept]
[KeptInterface (typeof (IBase3))]
+ [KeptInterface (typeof (IBase2))] // Should be removable
[KeptInterface (typeof (IBase))]
class Foo : IBase3
{
@@ -55,4 +56,4 @@ public void Method ()
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/NestedInterfaces2.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/NestedInterfaces2.cs
index 93f50accac117..a23078cd33c29 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/NestedInterfaces2.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/NestedInterfaces2.cs
@@ -25,6 +25,8 @@ interface IBase
{
}
+ [Kept] // Should be removable
+ [KeptInterface (typeof (IBase))]
interface IBase2 : IBase
{
void Method ();
@@ -32,12 +34,14 @@ interface IBase2 : IBase
[Kept]
[KeptInterface (typeof (IBase))]
+ [KeptInterface (typeof (IBase2))] // Should be removable
interface IBase3 : IBase2
{
}
[Kept]
[KeptInterface (typeof (IBase3))]
+ [KeptInterface (typeof (IBase2))] // Should be removable
[KeptInterface (typeof (IBase))]
class Foo : IBase3
{
@@ -46,4 +50,4 @@ public void Method ()
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/NestedInterfaces3.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/NestedInterfaces3.cs
index 5bc09dc62f22b..40f259dd79da9 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/NestedInterfaces3.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/NoKeptCtorButInterfaceNeeded/NestedInterfaces3.cs
@@ -25,6 +25,8 @@ interface IBase
{
}
+ [Kept] // Should be removable
+ [KeptInterface (typeof (IBase))]
interface IBase2 : IBase
{
void Method ();
@@ -32,12 +34,14 @@ interface IBase2 : IBase
[Kept]
[KeptInterface (typeof (IBase))]
+ [KeptInterface (typeof (IBase2))] // Should be removable
interface IBase3 : IBase2
{
}
[Kept]
[KeptInterface (typeof (IBase))]
+ [KeptInterface (typeof (IBase2))] // Should be removable
class Base : IBase2
{
public void Method ()
@@ -48,6 +52,7 @@ public void Method ()
[Kept]
[KeptBaseType (typeof (Base))]
[KeptInterface (typeof (IBase3))]
+ [KeptInterface (typeof (IBase2))] // Should be removable
[KeptInterface (typeof (IBase))]
class Foo : Base, IBase3, IBase2
{
@@ -56,4 +61,4 @@ public void Method ()
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnValueType/StructImplementingInterfaceMethodsNested.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnValueType/StructImplementingInterfaceMethodsNested.cs
index 8e438aeda7c8e..8f685def3fa92 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnValueType/StructImplementingInterfaceMethodsNested.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnValueType/StructImplementingInterfaceMethodsNested.cs
@@ -17,6 +17,8 @@ interface IFoo
void Foo ();
}
+ [Kept]
+ [KeptInterface (typeof (IFoo))]
interface IBar : IFoo
{
void Bar ();
@@ -24,6 +26,7 @@ interface IBar : IFoo
[Kept]
[KeptInterface (typeof (IFoo))]
+ [KeptInterface (typeof (IBar))]
struct A : IBar
{
[Kept]
@@ -36,4 +39,4 @@ public void Bar ()
}
}
}
-}
\ No newline at end of file
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/BaseTypeMarkedInterfaceDerivedNotKept.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/BaseTypeMarkedInterfaceDerivedNotKept.cs
new file mode 100644
index 0000000000000..999d0c2855d42
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/BaseTypeMarkedInterfaceDerivedNotKept.cs
@@ -0,0 +1,56 @@
+// 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;
+
+namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces
+{
+ class BaseTypeMarkedInterfaceDerivedNotKept
+ {
+ [Kept]
+ public static void Main ()
+ {
+ IFoo foo = new UsedDerived ();
+ foo.Method ();
+ }
+
+ [Kept]
+ [KeptInterface (typeof (IFoo))]
+ [KeptMember (".ctor()")]
+ abstract class BaseType : IFoo
+ {
+ [Kept]
+ public abstract void Method ();
+ }
+
+ [Kept]
+ [KeptBaseType (typeof (BaseType))]
+ [KeptMember (".ctor()")]
+ class UsedDerived : BaseType
+ {
+ [Kept]
+ public override void Method ()
+ {
+ }
+ }
+
+ class UnusedDerived : BaseType
+ {
+ public override void Method ()
+ {
+ }
+ }
+
+ [Kept]
+ interface IFoo
+ {
+ [Kept]
+ void Method ();
+ }
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveGenericInterfaces.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveGenericInterfaces.il
new file mode 100644
index 0000000000000..b0cde69b93de9
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveGenericInterfaces.il
@@ -0,0 +1,322 @@
+.assembly extern mscorlib { }
+
+.assembly 'library' { }
+
+.class interface public auto ansi abstract beforefieldinit IBase`3
+{
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
+ 01 00 01 00 00
+ )
+ .param type T
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = (
+ 01 00 02 00 00
+ )
+ .param type U
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = (
+ 01 00 02 00 00
+ )
+ .param type V
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = (
+ 01 00 02 00 00
+ )
+ // Methods
+ .method public hidebysig newslot abstract virtual
+ instance !T GetT () cil managed
+ {
+ } // end of method IBase`3::GetT
+
+ .method public hidebysig newslot virtual
+ instance !U GetU () cil managed
+ {
+ // Method begins at RVA 0x2050
+ // Code size 10 (0xa)
+ .maxstack 1
+ .locals init (
+ [0] !U
+ )
+
+ IL_0000: ldloca.s 0
+ IL_0002: initobj !U
+ IL_0008: ldloc.0
+ IL_0009: ret
+ } // end of method IBase`3::GetU
+
+ .method public hidebysig newslot virtual
+ instance !V GetV () cil managed
+ {
+ // Method begins at RVA 0x2068
+ // Code size 10 (0xa)
+ .maxstack 1
+ .locals init (
+ [0] !V
+ )
+
+ IL_0000: ldloca.s 0
+ IL_0002: initobj !V
+ IL_0008: ldloc.0
+ IL_0009: ret
+ } // end of method IBase`3::GetV
+
+} // end of class IBase`3
+
+.class interface public auto ansi abstract beforefieldinit IMiddle`2
+ implements class IBase`3,
+ class IBase`3
+{
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
+ 01 00 02 00 00
+ )
+ // Methods
+ .method private hidebysig virtual
+ instance int32 'IBase.GetV' () cil managed
+ {
+ .override method instance !2 class IBase`3::GetV()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method IMiddle`2::'IBase.GetV'
+
+ .method private hidebysig virtual
+ instance float32 'IBase.GetV' () cil managed
+ {
+ .override method instance !2 class IBase`3::GetV()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method IMiddle`2::'IBase.GetV'
+
+} // end of class IMiddle`2
+
+.class interface public auto ansi abstract beforefieldinit IDerived`1
+ implements class IMiddle`2,
+ class IMiddle`2
+ {
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
+ 01 00 02 00 00
+ )
+ // Methods
+ .method private hidebysig virtual
+ instance int32 'IBase.GetV' () cil managed
+ {
+ .override method instance !2 class IBase`3::GetV()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method IDerived`1::'IBase.GetV'
+
+ .method private hidebysig virtual
+ instance float32 'IBase.GetV' () cil managed
+ {
+ .override method instance !2 class IBase`3::GetV()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method IDerived`1::'IBase.GetV'
+
+ .method private hidebysig virtual
+ instance int64 'IBase.GetU' () cil managed
+ {
+ .override method instance !1 class IBase`3::GetU()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method IDerived`1::'IBase.GetU'
+
+ .method private hidebysig virtual
+ instance int64 'IBase.GetU' () cil managed
+ {
+ .override method instance !1 class IBase`3::GetU()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method IDerived`1::'IBase.GetU'
+
+ .method private hidebysig virtual
+ instance float64 'IBase.GetU' () cil managed
+ {
+ .override method instance !1 class IBase`3::GetU()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method IDerived`1::'IBase.GetU'
+
+ .method private hidebysig virtual
+ instance float64 'IBase.GetU' () cil managed
+ {
+ .override method instance !1 class IBase`3::GetU()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method IDerived`1::'IBase.GetU'
+
+} // end of class IDerived`1
+
+.class public auto ansi beforefieldinit MyClass
+ extends [mscorlib]System.Object
+ implements class IDerived`1,
+ class IDerived`1
+{
+ // Methods
+ .method private hidebysig newslot virtual
+ instance string 'IBase.GetT' () cil managed
+ {
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
+ 01 00 01 00 00
+ )
+ .override method instance !0 class IBase`3::GetT()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method MyClass::'IBase.GetT'
+
+ .method private hidebysig newslot virtual
+ instance string 'IBase.GetT' () cil managed
+ {
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
+ 01 00 01 00 00
+ )
+ .override method instance !0 class IBase`3::GetT()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method MyClass::'IBase.GetT'
+
+ .method private hidebysig newslot virtual
+ instance string 'IBase.GetT' () cil managed
+ {
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
+ 01 00 01 00 00
+ )
+ .override method instance !0 class IBase`3::GetT()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method MyClass::'IBase.GetT'
+
+ .method private hidebysig newslot virtual
+ instance string 'IBase.GetT' () cil managed
+ {
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
+ 01 00 01 00 00
+ )
+ .override method instance !0 class IBase`3::GetT()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method MyClass::'IBase.GetT'
+
+ .method private hidebysig newslot virtual
+ instance char 'IBase.GetT' () cil managed
+ {
+ .override method instance !0 class IBase`3::GetT()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method MyClass::'IBase.GetT'
+
+ .method private hidebysig newslot virtual
+ instance char 'IBase.GetT' () cil managed
+ {
+ .override method instance !0 class IBase`3::GetT()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method MyClass::'IBase.GetT'
+
+ .method private hidebysig newslot virtual
+ instance char 'IBase.GetT' () cil managed
+ {
+ .override method instance !0 class IBase`3::GetT()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method MyClass::'IBase.GetT'
+
+ .method private hidebysig newslot virtual
+ instance char 'IBase.GetT' () cil managed
+ {
+ .override method instance !0 class IBase`3::GetT()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method MyClass::'IBase.GetT'
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x2081
+ // Code size 7 (0x7)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Object::.ctor()
+ IL_0006: ret
+ } // end of method MyClass::.ctor
+
+} // end of class MyClass
+
+.class public auto ansi beforefieldinit MyDerivedClass
+ extends MyClass
+{
+ // Methods
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x20c0
+ // Code size 7 (0x7)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void MyClass::.ctor()
+ IL_0006: ret
+ } // end of method MyDerivedClass::.ctor
+
+} // end of class MyDerivedClass
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveGenericInterfacesStatic.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveGenericInterfacesStatic.il
new file mode 100644
index 0000000000000..9a37ab31140c3
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveGenericInterfacesStatic.il
@@ -0,0 +1,322 @@
+.assembly extern mscorlib { }
+
+.assembly 'library' { }
+
+.class interface public auto ansi abstract beforefieldinit IBase`3
+{
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
+ 01 00 01 00 00
+ )
+ .param type T
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = (
+ 01 00 02 00 00
+ )
+ .param type U
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = (
+ 01 00 02 00 00
+ )
+ .param type V
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = (
+ 01 00 02 00 00
+ )
+ // Methods
+ .method public hidebysig newslot abstract virtual
+ static !T GetT () cil managed
+ {
+ } // end of method IBase`3::GetT
+
+ .method public hidebysig newslot virtual
+ static !U GetU () cil managed
+ {
+ // Method begins at RVA 0x2050
+ // Code size 10 (0xa)
+ .maxstack 1
+ .locals init (
+ [0] !U
+ )
+
+ IL_0000: ldloca.s 0
+ IL_0002: initobj !U
+ IL_0008: ldloc.0
+ IL_0009: ret
+ } // end of method IBase`3::GetU
+
+ .method public hidebysig newslot virtual
+ static !V GetV () cil managed
+ {
+ // Method begins at RVA 0x2068
+ // Code size 10 (0xa)
+ .maxstack 1
+ .locals init (
+ [0] !V
+ )
+
+ IL_0000: ldloca.s 0
+ IL_0002: initobj !V
+ IL_0008: ldloc.0
+ IL_0009: ret
+ } // end of method IBase`3::GetV
+
+} // end of class IBase`3
+
+.class interface public auto ansi abstract beforefieldinit IMiddle`2
+ implements class IBase`3,
+ class IBase`3
+{
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
+ 01 00 02 00 00
+ )
+ // Methods
+ .method private hidebysig
+ static int32 'IBase.GetV' () cil managed
+ {
+ .override method !2 class IBase`3::GetV()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method IMiddle`2::'IBase.GetV'
+
+ .method private hidebysig
+ static float32 'IBase.GetV' () cil managed
+ {
+ .override method !2 class IBase`3::GetV()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method IMiddle`2::'IBase.GetV'
+
+} // end of class IMiddle`2
+
+.class interface public auto ansi abstract beforefieldinit IDerived`1
+ implements class IMiddle`2,
+ class IMiddle`2
+ {
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
+ 01 00 02 00 00
+ )
+ // Methods
+ .method private hidebysig
+ static int32 'IBase.GetV' () cil managed
+ {
+ .override method !2 class IBase`3::GetV()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method IDerived`1::'IBase.GetV'
+
+ .method private hidebysig
+ static float32 'IBase.GetV' () cil managed
+ {
+ .override method !2 class IBase`3::GetV()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method IDerived`1::'IBase.GetV'
+
+ .method private hidebysig
+ static int64 'IBase.GetU' () cil managed
+ {
+ .override method !1 class IBase`3::GetU()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method IDerived`1::'IBase.GetU'
+
+ .method private hidebysig
+ static int64 'IBase.GetU' () cil managed
+ {
+ .override method !1 class IBase`3::GetU()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method IDerived`1::'IBase.GetU'
+
+ .method private hidebysig
+ static float64 'IBase.GetU' () cil managed
+ {
+ .override method !1 class IBase`3::GetU()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method IDerived`1::'IBase.GetU'
+
+ .method private hidebysig
+ static float64 'IBase.GetU' () cil managed
+ {
+ .override method !1 class IBase`3::GetU()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method IDerived`1::'IBase.GetU'
+
+} // end of class IDerived`1
+
+.class public auto ansi beforefieldinit MyClass
+ extends [mscorlib]System.Object
+ implements class IDerived`1,
+ class IDerived`1
+{
+ // Methods
+ .method private hidebysig newslot
+ static string 'IBase.GetT' () cil managed
+ {
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
+ 01 00 01 00 00
+ )
+ .override method !0 class IBase`3::GetT()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method MyClass::'IBase.GetT'
+
+ .method private hidebysig newslot
+ static string 'IBase.GetT' () cil managed
+ {
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
+ 01 00 01 00 00
+ )
+ .override method !0 class IBase`3::GetT()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method MyClass::'IBase.GetT'
+
+ .method private hidebysig newslot
+ static string 'IBase.GetT' () cil managed
+ {
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
+ 01 00 01 00 00
+ )
+ .override method !0 class IBase`3::GetT()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method MyClass::'IBase.GetT'
+
+ .method private hidebysig newslot
+ static string 'IBase.GetT' () cil managed
+ {
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
+ 01 00 01 00 00
+ )
+ .override method !0 class IBase`3::GetT()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method MyClass::'IBase.GetT'
+
+ .method private hidebysig newslot
+ static char 'IBase.GetT' () cil managed
+ {
+ .override method !0 class IBase`3::GetT()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method MyClass::'IBase.GetT'
+
+ .method private hidebysig newslot
+ static char 'IBase.GetT' () cil managed
+ {
+ .override method !0 class IBase`3::GetT()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method MyClass::'IBase.GetT'
+
+ .method private hidebysig newslot
+ static char 'IBase.GetT' () cil managed
+ {
+ .override method !0 class IBase`3::GetT()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method MyClass::'IBase.GetT'
+
+ .method private hidebysig newslot
+ static char 'IBase.GetT' () cil managed
+ {
+ .override method !0 class IBase`3::GetT()
+ // Method begins at RVA 0x207e
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldnull
+ IL_0001: throw
+ } // end of method MyClass::'IBase.GetT'
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x2081
+ // Code size 7 (0x7)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Object::.ctor()
+ IL_0006: ret
+ } // end of method MyClass::.ctor
+
+} // end of class MyClass
+
+.class public auto ansi beforefieldinit MyDerivedClass
+ extends MyClass
+{
+ // Methods
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x20c0
+ // Code size 7 (0x7)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void MyClass::.ctor()
+ IL_0006: ret
+ } // end of method MyDerivedClass::.ctor
+
+} // end of class MyDerivedClass
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/GenericInterfaceImplementedRecursively.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/GenericInterfaceImplementedRecursively.cs
index 6e703f79d4650..71a772029587a 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/GenericInterfaceImplementedRecursively.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/GenericInterfaceImplementedRecursively.cs
@@ -16,14 +16,14 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces
[Define ("IL_ASSEMBLY_AVAILABLE")]
[SetupCompileBefore ("library.dll", new[] { "Dependencies/GenericInterfaceImplementedRecursively.il" })]
[SkipILVerify]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", "Program/IMiddle`1", "library.dll", "Program/IBase`1")]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", "Program/IDerived`1", "library.dll", "Program/IMiddle`1")]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", "Program/C", "library.dll", "Program/IDerived`1")]
#if IL_ASSEMBLY_AVAILABLE
[KeptTypeInAssembly ("library.dll", typeof(Program.IBase<>))]
[KeptTypeInAssembly ("library.dll", typeof(Program.IMiddle<>))]
- [KeptInterfaceOnTypeInAssembly ("library.dll", "Program/IMiddle`1", "library.dll", "Program/IBase`1")]
[KeptTypeInAssembly ("library.dll", typeof(Program.IDerived<>))]
- [KeptInterfaceOnTypeInAssembly ("library.dll", "Program/IDerived`1", "library.dll", "Program/IMiddle`1")]
[KeptTypeInAssembly ("library.dll", typeof(Program.C))]
- [KeptInterfaceOnTypeInAssembly ("library.dll", "Program/C", "library.dll", "Program/IDerived`1")]
#endif
///
/// This test case is to verify that the linker will keep all the metadata necessary for C to implement IBase when an interfaceImpl isn't directly on C.
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/OverrideOfRecursiveInterfaceIsRemoved.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/OverrideOfRecursiveInterfaceIsRemoved.cs
index 4beafaa1d9d96..6ef9d8848beef 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/OverrideOfRecursiveInterfaceIsRemoved.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/OverrideOfRecursiveInterfaceIsRemoved.cs
@@ -30,6 +30,9 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces
[RemovedOverrideOnMethodInAssembly ("library.dll", "Program/A", "O", "System.Void Program/IMiddleUnused::O()")]
[RemovedMemberInAssembly ("library.dll", "Program/IBaseUnused", "M()")]
[RemovedMemberInAssembly ("library.dll", "Program/IMiddleUnused", "O()")]
+ [RuntimeInterfaceOnTypeInAssembly ("library.dll", "Program/A", "Program/IDerived", new string[] { "Program/IDerived" })]
+ [RuntimeInterfaceOnTypeInAssembly ("library.dll", "Program/A", "Program/IBaseUsed", new string[] { "Program/IDerived", "Program/IMiddleUnused", "Program/IBaseUsed" })]
+ [RuntimeInterfaceOnTypeInAssembly ("library.dll", "Program/A", "Program/IBaseUnused", new string[] { "Program/IDerived", "Program/IMiddleUnused", "Program/IBaseUnused" })]
public class OverrideOfRecursiveInterfaceIsRemoved
{
[Kept]
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveGenericInterfaces.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveGenericInterfaces.cs
new file mode 100644
index 0000000000000..398e0fbdb5ee0
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveGenericInterfaces.cs
@@ -0,0 +1,95 @@
+// 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.RecursiveInterfaces
+{
+ [SetupLinkerArgument ("--skip-unresolved", "true")]
+ [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")]
+ [Define ("IL_ASSEMBLY_AVAILABLE")]
+ [SetupCompileBefore ("library.dll", new[] { "Dependencies/RecursiveGenericInterfaces.il" })]
+ [KeptAllTypesAndMembersInAssembly ("library.dll")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "IMiddle`2", "IBase.GetV", "V IBase`3::GetV()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "IMiddle`2", "IBase.GetV", "V IBase`3::GetV()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "IDerived`1", "IBase.GetV", "V IBase`3::GetV()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "IDerived`1", "IBase.GetV", "V IBase`3::GetV()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "IDerived`1", "IBase.GetU", "U IBase`3::GetU()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "IDerived`1", "IBase.GetU", "U IBase`3::GetU()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "IDerived`1", "IBase.GetU", "U IBase`3::GetU()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "IDerived`1", "IBase.GetU", "U IBase`3::GetU()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")]
+ [RuntimeInterfaceOnTypeInAssembly ("library.dll", "MyClass", "IBase`3", ["IDerived`1", "IMiddle`2", "IBase`3"])]
+ [RuntimeInterfaceOnTypeInAssembly ("library.dll", "MyClass", "IBase`3", ["IDerived`1", "IMiddle`2", "IBase`3"])]
+ public class RecursiveGenericInterfaces
+ {
+ [Kept]
+ public static void Main ()
+ {
+#if IL_ASSEMBLY_AVAILABLE
+ UseIBase (new MyDerivedClass ());
+ }
+
+ [Kept]
+ public static void UseIBase (IBase myBase)
+ {
+ myBase.GetT ();
+ myBase.GetU ();
+ myBase.GetV ();
+#endif
+ }
+
+ //public interface IBase
+ //{
+ // T GetT ();
+ // U GetU () => default;
+ // V GetV () => default;
+ //}
+
+ //public interface IMiddle : IBase, IBase
+ //{
+ // int IBase.GetV () => 12;
+ // float IBase.GetV () => 12.0f;
+ //}
+
+ //public interface IDerived : IMiddle, IMiddle
+ //{
+ // int IBase.GetV () => 12;
+ // float IBase.GetV () => 12.0f;
+
+ // long IBase.GetU () => 12;
+ // long IBase.GetU () => 12;
+
+ // double IBase.GetU () => 12;
+ // double IBase.GetU () => 12;
+ //}
+
+ //public class MyClass : IDerived, IDerived
+ //{
+ // string IBase.GetT () => throw new NotImplementedException ();
+ // string IBase.GetT () => throw new NotImplementedException ();
+ // string IBase.GetT () => throw new NotImplementedException ();
+ // string IBase.GetT () => throw new NotImplementedException ();
+ // char IBase.GetT () => throw new NotImplementedException ();
+ // char IBase.GetT () => throw new NotImplementedException ();
+ // char IBase.GetT () => throw new NotImplementedException ();
+ // char IBase.GetT () => throw new NotImplementedException ();
+ //}
+
+ //public class MyDerivedClass : MyClass
+ //{ }
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveGenericInterfacesStatic.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveGenericInterfacesStatic.cs
new file mode 100644
index 0000000000000..b01ee019c6b93
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveGenericInterfacesStatic.cs
@@ -0,0 +1,115 @@
+// 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.RecursiveInterfaces
+{
+ [SetupLinkerArgument ("--skip-unresolved", "true")]
+ [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods | TestRunCharacteristics.SupportsStaticInterfaceMethods, "Requires support for default and static interface methods")]
+ [Define ("IL_ASSEMBLY_AVAILABLE")]
+ [SetupCompileBefore ("library.dll", new[] { "Dependencies/RecursiveGenericInterfacesStatic.il" })]
+ [KeptMemberInAssembly ("library.dll", "IBase`3", "GetT()", "GetU()", "GetV()")]
+ [KeptTypeInAssembly ("library.dll", "IMiddle`2")]
+ // Below isn't strictly necessary, but we keep them since the interface is generic and we haven't hardened generic interface handling to only keep the single closest DIM.
+ // We use method definition to match the .override to the required DIM. However, one DIM might be for a different generic instance than we are searching for.
+ // Because of this, we keep all generic interface DIMs that may be the DIM we need.
+ [KeptMemberInAssembly ("library.dll", "IMiddle`2", "IBase.GetV()")]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", "IMiddle`2", "library.dll", "IBase`3")]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", "IMiddle`2", "library.dll", "IBase`3")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "IMiddle`2", "IBase.GetV", "V IBase`3::GetV()")]
+ [KeptTypeInAssembly ("library.dll", "IDerived`1")]
+ [KeptMemberInAssembly ("library.dll", "IDerived`1",
+ "IBase.GetV()",
+ "IBase.GetU()")]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", "IDerived`1", "library.dll", "IMiddle`2")]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", "IDerived`1", "library.dll", "IMiddle`2")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "IDerived`1", "IBase.GetV", "V IBase`3::GetV()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "IDerived`1", "IBase.GetU", "U IBase`3::GetU()")]
+ [KeptTypeInAssembly ("library.dll", "MyClass")]
+ [KeptMemberInAssembly ("library.dll", "MyClass",
+ "IBase.GetT()",
+ "IBase.GetT()",
+ "IBase.GetT()",
+ "IBase.GetT()",
+ "IBase.GetT()",
+ "IBase.GetT()",
+ "IBase.GetT()",
+ "IBase.GetT()")]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", "MyClass", "library.dll", "IDerived`1")]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", "MyClass", "library.dll", "IDerived`1")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "MyClass", "IBase.GetT", "T IBase`3::GetT()")]
+ public class RecursiveGenericInterfacesStatic
+ {
+ [Kept]
+ public static void Main ()
+ {
+
+#if IL_ASSEMBLY_AVAILABLE
+ UseIBase ();
+ _ = new MyDerivedClass ();
+ }
+
+ [Kept]
+ public static void UseIBase () where TBase: IBase
+ {
+ TBase.GetT ();
+ TBase.GetU ();
+ TBase.GetV ();
+#endif
+ }
+
+ //public interface IBase
+ //{
+ // static abstract T GetT ();
+ // static virtual U GetU () => default;
+ // static virtual V GetV () => default;
+ //}
+
+ //public interface IMiddle : IBase, IBase
+ //{
+ // static int IBase.GetV () => 12;
+ // static float IBase.GetV () => 12.0f;
+ //}
+
+ //public interface IDerived : IMiddle, IMiddle
+ //{
+ // static int IBase.GetV () => 12;
+ // static float IBase.GetV () => 12.0f;
+
+ // static long IBase.GetU () => 12;
+ // static long IBase.GetU () => 12;
+
+ // static double IBase.GetU () => 12;
+ // static double IBase.GetU () => 12;
+ //}
+
+ //public class MyClass : IDerived, IDerived
+ //{
+ // static string IBase.GetT () => throw new NotImplementedException ();
+ // static string IBase.GetT () => throw new NotImplementedException ();
+ // static string IBase.GetT () => throw new NotImplementedException ();
+ // static string IBase.GetT () => throw new NotImplementedException ();
+ // static char IBase.GetT () => throw new NotImplementedException ();
+ // static char IBase.GetT () => throw new NotImplementedException ();
+ // static char IBase.GetT () => throw new NotImplementedException ();
+ // static char IBase.GetT () => throw new NotImplementedException ();
+ //}
+
+ //public class MyDerivedClass : MyClass
+ //{ }
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveInterfaceKept.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveInterfaceKept.cs
index 56e08d67be74f..833ace2676c41 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveInterfaceKept.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveInterfaceKept.cs
@@ -20,13 +20,13 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces
[KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.I0100), "library.dll", typeof (Library.I010))]
[KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.I010), "library.dll", typeof (Library.I01))]
[KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.I01), "library.dll", typeof (Library.I0))]
- [RemovedTypeInAssembly("library.dll", typeof(Library.I00))]
- [RemovedTypeInAssembly("library.dll", typeof(Library.I000))]
- [RemovedInterfaceOnTypeInAssembly("library.dll", typeof (Library.MyClass), "library.dll", typeof (Library.I000))]
+ [KeptTypeInAssembly("library.dll", typeof(Library.I00))]
+ [KeptTypeInAssembly("library.dll", typeof(Library.I000))]
+ [KeptInterfaceOnTypeInAssembly("library.dll", typeof (Library.MyClass), "library.dll", typeof (Library.I000))]
#endif
public class RecursiveInterfaceKept
{
- public static void Main()
+ public static void Main ()
{
#if IL_ASSEMBLY_AVAILABLE
Library.I0 _ = new Library.MyClass();
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/BaseProvidesInterfaceMethod.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/BaseProvidesInterfaceMethod.cs
index 081430458926b..6fe4ac5d8fd55 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/BaseProvidesInterfaceMethod.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/BaseProvidesInterfaceMethod.cs
@@ -11,6 +11,7 @@ class BaseProvidesInterfaceMethod
public static void Main ()
{
CallMethod ();
+ CallMN ();
}
[Kept]
@@ -38,5 +39,40 @@ class Base
// Compiler generates private explicit implementation that calls Base.Method()
class Derived : Base, IFoo
{ }
+
+ [Kept]
+ static void CallMN () where T : I
+ {
+ T.M ();
+ T.N ();
+ }
+
+ [Kept]
+ interface I
+ {
+ [Kept]
+ static abstract string M ();
+ [Kept]
+ static abstract string N ();
+ }
+
+ [Kept]
+ [KeptInterface (typeof (I))]
+ class B : I
+ {
+ [Kept]
+ public static string M () => "B.M";
+ [Kept]
+ static string I.N () => "B's I.N";
+ }
+
+ [Kept]
+ [KeptBaseType (typeof (B))]
+ [KeptInterface (typeof (I))]
+ class D : B, I
+ {
+ [Kept]
+ static string I.N () => "D's I.N";
+ }
}
}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/RemovedInterfaceImplementationRemovedOverride.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/RemovedInterfaceImplementationRemovedOverride.cs
index 01541dd73a17b..499a08d521857 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/RemovedInterfaceImplementationRemovedOverride.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/RemovedInterfaceImplementationRemovedOverride.cs
@@ -22,6 +22,8 @@ public static void Main ()
GenericInterfaceGenericType.Test ();
PrivateExplicitImplementationReflectedOver.Test ();
InheritedInterfaces.Test ();
+ DimWithOverride.Test ();
+ Generics.Test ();
}
[Kept]
@@ -274,5 +276,92 @@ class UsedDirectly : IDerived
public static void M () { }
}
}
+
+ [Kept]
+ public class DimWithOverride
+ {
+ [Kept]
+ public static void Test ()
+ {
+ CallM ();
+ }
+
+ [Kept]
+ static void CallM () where T : IBase
+ {
+ T.M ();
+ }
+
+ [Kept]
+ interface IBase
+ {
+ [Kept]
+ static abstract void M ();
+ }
+
+ [Kept]
+ [KeptInterface (typeof (IBase))]
+ interface IDerived : IBase
+ {
+ [Kept]
+ static void IBase.M () { }
+ }
+
+ [Kept]
+ [KeptInterface (typeof (IDerived))]
+ [KeptInterface (typeof (IBase))]
+ class MyClass : IDerived
+ {
+ }
+
+ [Kept]
+ [KeptBaseType (typeof (MyClass))]
+ [KeptInterface (typeof (IBase))]
+ class MyDerivedClass : MyClass, IBase
+ {
+ }
+ }
+
+ [Kept]
+ public class Generics
+ {
+ [Kept]
+ public static void Test ()
+ {
+ UseIGeneric ();
+ }
+
+ [Kept]
+ static void UseIGeneric () where T : IGeneric
+ {
+ T.GetT ();
+ }
+
+ [Kept]
+ interface IGeneric
+ {
+ [Kept]
+ static abstract T GetT ();
+ }
+
+ [Kept]
+ [KeptInterface (typeof (IGeneric))]
+ [KeptInterface (typeof (IGeneric))]
+ [KeptInterface (typeof (IGeneric))]
+ class MyType : IGeneric, IGeneric, IGeneric
+ {
+ [Kept]
+ [KeptOverride (typeof (IGeneric))]
+ static int IGeneric.GetT () => 0;
+
+ [Kept]
+ [KeptOverride (typeof (IGeneric))]
+ static float IGeneric.GetT () => 0;
+
+ [Kept]
+ [KeptOverride (typeof (IGeneric))]
+ static string IGeneric.GetT () => "";
+ }
+ }
}
}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/BaseProvidesInterfaceMethodRequiresMismatch.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/BaseProvidesInterfaceMethodRequiresMismatch.cs
new file mode 100644
index 0000000000000..0ee0ad00f44e8
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/BaseProvidesInterfaceMethodRequiresMismatch.cs
@@ -0,0 +1,168 @@
+// 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.Diagnostics.CodeAnalysis;
+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.RequiresCapability
+{
+ [ExpectedNoWarnings]
+ [SkipKeptItemsValidation]
+ [SetupCompileBefore ("library.dll", new[] { "Dependencies/AbstractBaseWithNoMethodsImplementsInterface.il" })]
+ [Define ("IL_ASSEMBLY_AVAILABLE")]
+ //
+ [LogContains ("IL2046: AbstractBaseWithNoMethodsImplementsInterface.Derived.M()", ProducedBy = Tool.None)]
+ class BaseProvidesInterfaceMethodRequiresMismatch
+ {
+ [RequiresUnreferencedCode ("Message")]
+ public static void Main ()
+ {
+ BaseDoesNotImplementINoRuc.Run ();
+ BaseDoesNotImplementIRuc.Run ();
+ BaseImplementsINoRuc.Run ();
+ ThreeLevelsInheritance.Run ();
+ DerivedOverridesImplementingMethod.Run ();
+#if IL_ASSEMBLY_AVAILABLE
+ AbstractBaseWithNoMethodsImplementsInterface.Run ();
+#endif
+ }
+
+ interface INoRuc
+ {
+ void M ();
+ }
+
+ interface IRuc
+ {
+ [RequiresUnreferencedCode ("Message")]
+ void MRuc ();
+ }
+
+ public class BaseDoesNotImplementINoRuc
+ {
+ public class C
+ {
+ [RequiresUnreferencedCode ("Message")]
+ public void M ()
+ {
+ }
+ }
+
+ [ExpectedWarning ("IL2046", "C.M()", "INoRuc.M()")]
+ public class D : C, INoRuc
+ {
+ }
+
+ public static void Run ()
+ {
+ ((INoRuc) new D ()).M ();
+ }
+ }
+
+ public class BaseDoesNotImplementIRuc
+ {
+ public class C
+ {
+ public void MRuc () { }
+ }
+
+ [ExpectedWarning ("IL2046", "C.MRuc()", "IRuc.MRuc()")]
+ public class D : C, IRuc
+ {
+ }
+
+ [RequiresUnreferencedCode ("Message")]
+ public static void Run ()
+ {
+ ((IRuc) new D ()).MRuc ();
+ }
+ }
+
+ public class BaseImplementsINoRuc
+ {
+ class C : INoRuc
+ {
+ [RequiresUnreferencedCode ("Message")]
+ [ExpectedWarning ("IL2046", "C.M()", "INoRuc.M()")]
+ public void M ()
+ {
+ }
+ }
+
+ [ExpectedWarning ("IL2046", "C.M()", "INoRuc.M()")]
+ class D : C, INoRuc
+ {
+ }
+
+ class D2 : C
+ {
+ }
+
+ public static void Run ()
+ {
+ ((INoRuc) new D ()).M ();
+ ((INoRuc) new D2 ()).M ();
+ }
+ }
+
+ public class ThreeLevelsInheritance
+ {
+ public class Base
+ {
+ [RequiresUnreferencedCode ("Message")]
+ public void M () { }
+ }
+
+ [ExpectedWarning ("IL2046", "Base.M()", "INoRuc.M()")]
+ public class Middle : Base, INoRuc
+ {
+ }
+
+ public class Derived : Middle
+ {
+ }
+
+ public static void Run ()
+ {
+ ((INoRuc) new Derived ()).M ();
+ }
+ }
+
+ public class DerivedOverridesImplementingMethod
+ {
+ class C : INoRuc
+ {
+ [RequiresUnreferencedCode ("Message")]
+ [ExpectedWarning ("IL2046", "C.M()", "INoRuc.M()")]
+ public virtual void M ()
+ {
+ }
+ }
+
+ class D : C, INoRuc
+ {
+ [RequiresUnreferencedCode ("Message")]
+ [ExpectedWarning ("IL2046", "D.M()", "INoRuc.M()")]
+ public override void M () { }
+ }
+
+ class D2 : C
+ {
+ [RequiresUnreferencedCode ("Message")]
+ public override void M () { }
+ }
+
+ public static void Run ()
+ {
+ ((INoRuc) new D ()).M ();
+ ((INoRuc) new D2 ()).M ();
+ }
+ }
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/Dependencies/AbstractBaseWithNoMethodsImplementsInterface.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/Dependencies/AbstractBaseWithNoMethodsImplementsInterface.il
new file mode 100644
index 0000000000000..cb8797d62f91a
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/RequiresCapability/Dependencies/AbstractBaseWithNoMethodsImplementsInterface.il
@@ -0,0 +1,101 @@
+// 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 public auto ansi beforefieldinit AbstractBaseWithNoMethodsImplementsInterface
+ extends [mscorlib]System.Object
+{
+ // Methods
+ .method public hidebysig static
+ void Run () cil managed
+ {
+ .custom instance void [System.Runtime]System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute::.ctor(string) = (
+ 01 00 03 52 55 43 00 00
+ )
+ // Method begins at RVA 0x2050
+ // Code size 16 (0x10)
+ .maxstack 8
+
+ IL_0000: newobj instance void AbstractBaseWithNoMethodsImplementsInterface/Derived::.ctor()
+ IL_0005: castclass AbstractBaseWithNoMethodsImplementsInterface/I
+ IL_000a: callvirt instance void AbstractBaseWithNoMethodsImplementsInterface/I::M()
+ IL_000f: ret
+ } // end of method Program::Main
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x2061
+ // Code size 7 (0x7)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Object::.ctor()
+ IL_0006: ret
+ } // end of method Program::.ctor
+
+
+ // Nested Types
+ .class nested assembly auto ansi abstract beforefieldinit Base
+ extends [mscorlib]System.Object
+ implements AbstractBaseWithNoMethodsImplementsInterface/I
+ {
+ // Methods
+ .method family hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x2061
+ // Code size 7 (0x7)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Object::.ctor()
+ IL_0006: ret
+ } // end of method Base::.ctor
+
+ } // end of class Base
+
+ .class interface nested assembly auto ansi abstract beforefieldinit I
+ {
+ // Methods
+ .method public hidebysig newslot abstract virtual
+ instance void M () cil managed
+ {
+ .custom instance void [mscorlib]System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute::.ctor(string) = (
+ 01 00 03 52 55 43 00 00 // "RUC"
+ )
+ } // end of method I::M
+
+ } // end of class I
+
+ .class nested assembly auto ansi beforefieldinit Derived
+ extends AbstractBaseWithNoMethodsImplementsInterface/Base
+ {
+ // Methods
+ .method public virtual hidebysig
+ instance void M () cil managed
+ {
+ // Method begins at RVA 0x2069
+ .maxstack 8
+
+ IL_0000: ret
+ } // end of method Derived::M
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x2075
+ // Code size 7 (0x7)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void AbstractBaseWithNoMethodsImplementsInterface/Base::.ctor()
+ IL_0006: ret
+ } // end of method Derived::.ctor
+
+ } // end of class Derived
+
+} // end of class Program
diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs
index 7d984ef2e4863..893aca48a5eb4 100644
--- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs
@@ -17,6 +17,7 @@ namespace Mono.Linker.Tests.TestCasesRunner
partial class AssemblyChecker
{
readonly AssemblyDefinition originalAssembly, linkedAssembly;
+ TypeMapInfo _originalTypeMapInfo = new TypeMapInfo (new TestResolver ());
readonly TrimmedTestCaseResult linkedTestCase;
HashSet linkedMembers;
@@ -25,11 +26,12 @@ partial class AssemblyChecker
readonly HashSet