diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs
index 95ee34ab4504b..56ab55515ce19 100644
--- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs
+++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs
@@ -44,6 +44,7 @@ public override bool IsInstanceOfType([NotNullWhen(true)] object? o)
return IsAssignableFrom(objectType);
}
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
public override PropertyInfo[] GetProperties(BindingFlags bindingAttr)
{
// list of properties on this type according to the underlying ReflectionContext
@@ -139,6 +140,7 @@ public override PropertyInfo[] GetProperties(BindingFlags bindingAttr)
return binder.SelectProperty(bindingAttr, matchingProperties.ToArray(), returnType, types, modifiers);
}
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
public override MethodInfo[] GetMethods(BindingFlags bindingAttr)
{
// list of methods on this type according to the underlying ReflectionContext
diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingType.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingType.cs
index bd7ef3ff90eae..5bad1592b9ba4 100644
--- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingType.cs
+++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingType.cs
@@ -325,11 +325,17 @@ public override EventInfo[] GetEvents(BindingFlags bindingAttr)
return _typeInfo.GetEvents(bindingAttr);
}
+#if NET8_0_OR_GREATER
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)]
+#endif
public override FieldInfo? GetField(string name, BindingFlags bindingAttr)
{
return _typeInfo.GetField(name, bindingAttr);
}
+#if NET8_0_OR_GREATER
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)]
+#endif
public override FieldInfo[] GetFields(BindingFlags bindingAttr)
{
return _typeInfo.GetFields(bindingAttr);
@@ -345,6 +351,14 @@ public override Type[] GetInterfaces()
return _typeInfo.GetInterfaces();
}
+#if NET8_0_OR_GREATER
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields |
+ DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods |
+ DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents |
+ DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties |
+ DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors |
+ DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)]
+#endif
public override MemberInfo[] GetMembers(BindingFlags bindingAttr)
{
return _typeInfo.GetMembers(bindingAttr);
@@ -358,6 +372,9 @@ public override MemberInfo[] GetMembers(BindingFlags bindingAttr)
_typeInfo.GetMethod(name, bindingAttr, binder, callConvention, types, modifiers);
}
+#if NET8_0_OR_GREATER
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
+#endif
public override MethodInfo[] GetMethods(BindingFlags bindingAttr)
{
return _typeInfo.GetMethods(bindingAttr);
@@ -373,6 +390,9 @@ public override Type[] GetNestedTypes(BindingFlags bindingAttr)
return _typeInfo.GetNestedTypes(bindingAttr);
}
+#if NET8_0_OR_GREATER
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
+#endif
public override PropertyInfo[] GetProperties(BindingFlags bindingAttr)
{
return _typeInfo.GetProperties(bindingAttr);
diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingType.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingType.cs
index 0ac9bb0de89f3..f61ea789bd738 100644
--- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingType.cs
+++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingType.cs
@@ -206,11 +206,13 @@ public override EventInfo[] GetEvents(BindingFlags bindingAttr)
return _projector.Project(base.GetEvents(bindingAttr), _projector.ProjectEvent);
}
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)]
public override FieldInfo? GetField(string name, BindingFlags bindingAttr)
{
return _projector.ProjectField(base.GetField(name, bindingAttr));
}
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)]
public override FieldInfo[] GetFields(BindingFlags bindingAttr)
{
return _projector.Project(base.GetFields(bindingAttr), _projector.ProjectField);
@@ -226,6 +228,12 @@ public override Type[] GetInterfaces()
return _projector.Project(base.GetInterfaces(), _projector.ProjectType);
}
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields |
+ DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods |
+ DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents |
+ DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties |
+ DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors |
+ DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)]
public override MemberInfo[] GetMembers(BindingFlags bindingAttr)
{
MethodInfo[] methods = GetMethods(bindingAttr);
@@ -264,6 +272,7 @@ public override MemberInfo[] GetMembers(BindingFlags bindingAttr)
return _projector.ProjectMethod(base.GetMethodImpl(name, bindingAttr, binder, callConvention, types, modifiers));
}
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)]
public override MethodInfo[] GetMethods(BindingFlags bindingAttr)
{
return _projector.Project(base.GetMethods(bindingAttr), _projector.ProjectMethod);
@@ -279,6 +288,7 @@ public override Type[] GetNestedTypes(BindingFlags bindingAttr)
return _projector.Project(base.GetNestedTypes(bindingAttr), _projector.ProjectType);
}
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)]
public override PropertyInfo[] GetProperties(BindingFlags bindingAttr)
{
return _projector.Project(base.GetProperties(bindingAttr), _projector.ProjectProperty);
diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
index 32fd98cbe039c..713e1d2dcace5 100644
--- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
+++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
@@ -36,7 +36,6 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection.Runtime.TypeParsing;
-using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using ILLink.Shared;
using ILLink.Shared.TrimAnalysis;
@@ -779,45 +778,6 @@ void MarkMethodIfNeededByBaseMethod (MethodDefinition method)
}
}
- ///
- /// 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)
- {
- if (type.HasInterfaces) {
- foreach (var intf in type.Interfaces) {
- TypeDefinition? resolvedInterface = Context.Resolve (intf.InterfaceType);
- if (resolvedInterface == null)
- continue;
-
- if (Annotations.IsMarked (intf) && RequiresInterfaceRecursively (resolvedInterface, interfaceType))
- return true;
- }
- }
-
- return false;
- }
-
- bool RequiresInterfaceRecursively (TypeDefinition typeToExamine, TypeDefinition interfaceType)
- {
- if (typeToExamine == interfaceType)
- return true;
-
- if (typeToExamine.HasInterfaces) {
- foreach (var iface in typeToExamine.Interfaces) {
- var resolved = Context.TryResolve (iface.InterfaceType);
- if (resolved == null)
- continue;
-
- if (RequiresInterfaceRecursively (resolved, interfaceType))
- return true;
- }
- }
-
- return false;
- }
-
void ProcessDefaultImplementation (OverrideInformation ov)
{
Debug.Assert (ov.IsOverrideOfInterfaceMember);
@@ -825,7 +785,9 @@ void ProcessDefaultImplementation (OverrideInformation ov)
|| ov.Override.IsStatic && !Annotations.IsRelevantToVariantCasting (ov.InterfaceImplementor.Implementor))
return;
- MarkInterfaceImplementation (ov.InterfaceImplementor.InterfaceImplementation);
+ foreach (var ifaceImpl in ov.InterfaceImplementor.MostDirectInterfaceImplementationPath) {
+ MarkInterfaceImplementation (ifaceImpl);
+ }
}
void MarkMarshalSpec (IMarshalInfoProvider spec, in DependencyInfo reason)
@@ -2450,37 +2412,35 @@ void MarkNamedProperty (TypeDefinition type, string property_name, in Dependency
void MarkInterfaceImplementations (TypeDefinition type)
{
- if (!type.HasInterfaces)
- return;
-
- foreach (var iface in type.Interfaces) {
- // Only mark interface implementations of interface types that have been marked.
- // This enables stripping of interfaces that are never used
- if (ShouldMarkInterfaceImplementation (type, iface))
- MarkInterfaceImplementation (iface, new MessageOrigin (type));
+ foreach (var interfaceImplementor in Annotations.GetRecursiveInterfaces (type)) {
+ if (ShouldMarkInterfaceImplementation (interfaceImplementor)) {
+ foreach (InterfaceImplementation interfaceImpl in interfaceImplementor.MostDirectInterfaceImplementationPath) {
+ MarkInterfaceImplementation (interfaceImpl, new MessageOrigin (type));
+ }
+ }
}
}
- protected virtual bool ShouldMarkInterfaceImplementation (TypeDefinition type, InterfaceImplementation iface)
+ protected virtual bool ShouldMarkInterfaceImplementation (InterfaceImplementor interfaceImplementor)
{
- if (Annotations.IsMarked (iface))
+ if (interfaceImplementor.IsMostDirectImplementationMarked (Annotations))
return false;
- if (!Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, type))
+ if (!Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, interfaceImplementor.Implementor))
return true;
- if (Context.Resolve (iface.InterfaceType) is not TypeDefinition resolvedInterfaceType)
+ if (interfaceImplementor.InterfaceType is null)
return false;
- if (Annotations.IsMarked (resolvedInterfaceType))
+ if (Annotations.IsMarked (interfaceImplementor.InterfaceType))
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 (resolvedInterfaceType.IsImport || resolvedInterfaceType.IsWindowsRuntime)
+ if (interfaceImplementor.InterfaceType.IsImport || interfaceImplementor.InterfaceType.IsWindowsRuntime)
return true;
- return IsFullyPreserved (type);
+ return IsFullyPreserved (interfaceImplementor.Implementor);
}
void MarkGenericParameterProvider (IGenericParameterProvider provider)
@@ -2560,12 +2520,10 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat
if (Annotations.IsMarked (method))
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)))
+ // If there is no chain of interface implementations that make the type implement the interface, the method isn't required
+ if (!overrideInformation.InterfaceImplementor.IsMarked(Annotations)) {
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
// Unmarked interface methods from link assemblies will be removed so the implementing method does not need to be kept.
diff --git a/src/tools/illink/src/linker/Linker/Annotations.cs b/src/tools/illink/src/linker/Linker/Annotations.cs
index a7b3198265e81..6a21a921e76f9 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;
@@ -717,5 +718,7 @@ public void EnqueueVirtualMethod (MethodDefinition method)
if (FlowAnnotations.RequiresVirtualMethodDataFlowAnalysis (method) || HasLinkerAttribute (method))
VirtualMethodsWithAnnotationsToValidate.Add (method);
}
+
+ internal ImmutableArray GetRecursiveInterfaces (TypeDefinition type) => TypeMapInfo.GetRecursiveInterfaces (type);
}
}
diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs
index e981ce872703f..321584cb74e27 100644
--- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs
+++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs
@@ -2,9 +2,9 @@
// 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.Collections.Immutable;
using System.Diagnostics;
+using System.Linq;
using Mono.Cecil;
namespace Mono.Linker
@@ -15,45 +15,164 @@ public class InterfaceImplementor
/// The type that implements .
///
public TypeDefinition Implementor { get; }
+
///
- /// The .interfaceimpl on that points to
+ /// The type of the interface that is implemented by
+ /// Null if the type could not be resolved
///
- public InterfaceImplementation InterfaceImplementation { get; }
+ public TypeDefinition? InterfaceType { get; }
+
///
- /// The type of the interface that is implemented by
+ /// A to the with the generic parameters substituted.
///
- public TypeDefinition InterfaceType { get; }
+ public TypeReference InflatedInterface { get; }
- public InterfaceImplementor (TypeDefinition implementor, InterfaceImplementation interfaceImplementation, TypeDefinition interfaceType, IMetadataResolver resolver)
+ ///
+ /// The graphs of s that make implement the .
+ /// There can be many ways a type implements an interface if an explicit interface implementation is given for an interface that is also implemented recursively due to another interface implementation.
+ /// It will be in the following order:
+ /// 1. Explicit interface implementation on
+ /// 2. Explicit interface implementation on a base type of
+ /// 3. Recursive interface implementations on an explicitly implemented interface on or it's base types
+ ///
+ public readonly ImmutableArray InterfaceImplementationNodes;
+
+ public bool HasExplicitImplementation => InterfaceImplementationNodes[0].Length == 0;
+
+ public InterfaceImplementor (TypeDefinition implementor, TypeDefinition? interfaceType, TypeReference inflatedInterface, ImmutableArray implNode, LinkContext context)
{
Implementor = implementor;
- InterfaceImplementation = interfaceImplementation;
InterfaceType = interfaceType;
- Debug.Assert(resolver.Resolve (interfaceImplementation.InterfaceType) == interfaceType);
+ InterfaceImplementationNodes = implNode;
+ InflatedInterface = inflatedInterface;
+ Debug.Assert (context.Resolve (inflatedInterface) == interfaceType);
+ Debug.Assert (implNode.Length != 0);
+ Debug.Assert (implNode.All (i => interfaceType == context.Resolve (i.GetLast ().InterfaceType)));
+ }
+
+ ///
+ /// An Enumerable over the most direct chain to the
+ ///
+ public ShortestInterfaceImplementationChainEnumerator MostDirectInterfaceImplementationPath => new (this);
+
+ public struct ShortestInterfaceImplementationChainEnumerator
+ {
+ InterfaceImplementationNode _current;
+ bool _hasMoved;
+ public ShortestInterfaceImplementationChainEnumerator (InterfaceImplementor implementor)
+ {
+ _current = implementor.InterfaceImplementationNodes[0];
+ _hasMoved = false;
+ }
+ public ShortestInterfaceImplementationChainEnumerator GetEnumerator () => this;
+ public InterfaceImplementation Current => _current.InterfaceImplementation;
+
+ public bool MoveNext ()
+ {
+ if (!_hasMoved) {
+ _hasMoved = true;
+ return true;
+ }
+ if (_current.Next.Length == 0)
+ return false;
+ _current = _current.Next[0];
+ return true;
+ }
+ }
+
+ ///
+ /// Returns true if the most direct implementation of is marked. may still have a different recursive implementation marked.
+ ///
+ public bool IsMostDirectImplementationMarked (AnnotationStore annotations)
+ {
+ return InterfaceImplementationNodes[0].IsMostDirectImplementationMarked (annotations);
}
- public static InterfaceImplementor Create(TypeDefinition implementor, TypeDefinition interfaceType, IMetadataResolver resolver)
+ ///
+ /// Returns true if implements via any of the possible interface implementation chains.
+ ///
+ public bool IsMarked (AnnotationStore annotations)
{
- foreach(InterfaceImplementation iface in implementor.Interfaces) {
- if (resolver.Resolve(iface.InterfaceType) == interfaceType) {
- return new InterfaceImplementor(implementor, iface, interfaceType, resolver);
+ foreach (var i in InterfaceImplementationNodes) {
+ if (i.IsMarked (annotations))
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ /// Represents a node in the graph of a type implementing an interface.
+ ///
+ public sealed class InterfaceImplementationNode
+ {
+ ///
+ /// The that is on that is part of the chain of interface implementations.
+ ///
+ public InterfaceImplementation InterfaceImplementation { get; }
+
+ ///
+ /// The type that has in its .
+ ///
+ public TypeDefinition InterfaceImplementationProvider { get; }
+
+ ///
+ /// The s that are on the type pointed to by that lead to the interface type.
+ ///
+ public ImmutableArray Next { get; }
+
+ ///
+ /// The number of interface implementations on the most direct way the interface is implemented from
+ ///
+ public int Length {
+ get {
+ if (_length != -1)
+ return _length;
+ if (Next.Length == 0)
+ return _length = 0;
+ return _length = Next[0].Length + 1;
}
}
+ int _length = -1;
+
+ public InterfaceImplementationNode (InterfaceImplementation interfaceImplementation, TypeDefinition interfaceImplementationProvider, ImmutableArray next)
+ {
+ InterfaceImplementation = interfaceImplementation;
+ InterfaceImplementationProvider = interfaceImplementationProvider;
+ Next = next;
+ }
+
+ public bool IsMostDirectImplementationMarked (AnnotationStore annotations)
+ {
+ if (!annotations.IsMarked (InterfaceImplementation))
+ return false;
+ if (Next.Length == 0)
+ return true;
+ return Next[0].IsMostDirectImplementationMarked (annotations);
+ }
+
+ public bool IsMarked (AnnotationStore annotations)
+ {
+ if (!annotations.IsMarked (InterfaceImplementation))
+ return false;
+
+ if (Next.Length == 0)
+ return true;
- Queue ifacesToCheck = new ();
- ifacesToCheck.Enqueue(implementor);
- while (ifacesToCheck.Count > 0) {
- var currentIface = ifacesToCheck.Dequeue ();
+ foreach (var impl in Next) {
+ if (impl.IsMarked (annotations))
+ return true;
+ }
+ return false;
+ }
- 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);
+ public InterfaceImplementation GetLast ()
+ {
+ var curr = this;
+ while (curr.Next.Length > 0) {
+ curr = curr.Next[0];
}
+ return curr.InterfaceImplementation;
}
- throw new InvalidOperationException ($"Type '{implementor.FullName}' does not implement interface '{interfaceType.FullName}' directly or through any interfaces");
}
}
}
diff --git a/src/tools/illink/src/linker/Linker/OverrideInformation.cs b/src/tools/illink/src/linker/Linker/OverrideInformation.cs
index 0727d5d25c19a..c306a3c111eb1 100644
--- a/src/tools/illink/src/linker/Linker/OverrideInformation.cs
+++ b/src/tools/illink/src/linker/Linker/OverrideInformation.cs
@@ -10,10 +10,20 @@ namespace Mono.Linker
[DebuggerDisplay ("{Override}")]
public class OverrideInformation
{
+ ///
+ /// The method that is being overridden or implemented
+ ///
public MethodDefinition Base { get; }
+ ///
+ /// The method that overrides . For interface methods, this may be a method on a base type, or a default interface method.
+ ///
public MethodDefinition Override { get; }
+ ///
+ /// For overrides of interface methods, includes info about the interface / implementor pair that correspong to the base / override pair
+ /// The may be a method on , one of it's base types, or a default interface method on one of the interfaces implemented by it.
+ ///
internal InterfaceImplementor? InterfaceImplementor { get; }
internal OverrideInformation (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null)
@@ -22,14 +32,23 @@ internal OverrideInformation (MethodDefinition @base, MethodDefinition @override
Override = @override;
InterfaceImplementor = interfaceImplementor;
// Ensure we have an interface implementation if the base method is from an interface and the override method is on a class
- Debug.Assert(@base.DeclaringType.IsInterface && interfaceImplementor != null
+ Debug.Assert (@base.DeclaringType.IsInterface && interfaceImplementor != null
|| !@base.DeclaringType.IsInterface && interfaceImplementor == null);
// Ensure the interfaceImplementor is for the interface we expect
Debug.Assert (@base.DeclaringType.IsInterface ? interfaceImplementor!.InterfaceType == @base.DeclaringType : true);
}
- public InterfaceImplementation? MatchingInterfaceImplementation
- => InterfaceImplementor?.InterfaceImplementation;
+ public InterfaceImplementation? MatchingInterfaceImplementation {
+ get {
+ if (InterfaceImplementor is null)
+ return null;
+ InterfaceImplementation? last = null;
+ foreach (InterfaceImplementation curr in InterfaceImplementor!.MostDirectInterfaceImplementationPath) {
+ last = curr;
+ }
+ return last;
+ }
+ }
public TypeDefinition? InterfaceType
=> InterfaceImplementor?.InterfaceType;
diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs
index 7e68b71bcb7d0..a5304569192be 100644
--- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs
+++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs
@@ -29,16 +29,16 @@
// 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 static Mono.Linker.InterfaceImplementor;
namespace Mono.Linker
{
-
public class TypeMapInfo
{
readonly HashSet assemblies = new HashSet ();
@@ -46,6 +46,7 @@ public class TypeMapInfo
protected readonly Dictionary> base_methods = new Dictionary> ();
protected readonly Dictionary> override_methods = new Dictionary> ();
protected readonly Dictionary> default_interface_implementations = new Dictionary> ();
+ readonly Dictionary> _interfaces = new ();
public TypeMapInfo (LinkContext context)
{
@@ -87,6 +88,18 @@ public void EnsureProcessed (AssemblyDefinition assembly)
return bases;
}
+ InterfaceImplementor? GetInterfaceImplementor (TypeDefinition implementor, TypeDefinition interfaceType)
+ {
+ if (!_interfaces.TryGetValue (implementor, out var implrs))
+ return null;
+
+ foreach (var iface in implrs) {
+ if (iface.InterfaceType == interfaceType)
+ return iface;
+ }
+ return null;
+ }
+
///
/// Returns a list of all default interface methods that implement for a type.
/// ImplementingType is the type that implements the interface,
@@ -96,28 +109,38 @@ public void EnsureProcessed (AssemblyDefinition assembly)
/// The interface method to find default implementations for
public IEnumerable? GetDefaultInterfaceImplementations (MethodDefinition baseMethod)
{
+ EnsureProcessed (baseMethod.Module.Assembly);
default_interface_implementations.TryGetValue (baseMethod, out var ret);
return ret;
}
- public void AddBaseMethod (MethodDefinition method, MethodDefinition @base, InterfaceImplementor? interfaceImplementor)
+ internal ImmutableArray GetRecursiveInterfaces (TypeDefinition type)
+ {
+ EnsureProcessed (type.Module.Assembly);
+ return _interfaces.TryGetValue (type, out var value) ? value : [];
+ }
+
+ void AddBaseMethod (MethodDefinition method, OverrideInformation overrideInformation)
{
- base_methods.AddToList (method, new OverrideInformation (@base, method, interfaceImplementor));
+ Debug.Assert (overrideInformation.Override == method);
+ base_methods.AddToList (method, overrideInformation);
}
- public void AddOverride (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null)
+ void AddOverrideOfMethod (MethodDefinition @base, OverrideInformation overrideInformation)
{
- override_methods.AddToList (@base, new OverrideInformation (@base, @override, interfaceImplementor));
+ Debug.Assert (overrideInformation.Base == @base);
+ override_methods.AddToList (@base, overrideInformation);
}
- public void AddDefaultInterfaceImplementation (MethodDefinition @base, InterfaceImplementor interfaceImplementor, MethodDefinition defaultImplementationMethod)
+ void AddDefaultInterfaceImplementation (MethodDefinition @base, InterfaceImplementor interfaceImplementor, MethodDefinition defaultImplementationMethod)
{
- Debug.Assert(@base.DeclaringType.IsInterface);
+ Debug.Assert (@base.DeclaringType.IsInterface);
default_interface_implementations.AddToList (@base, new OverrideInformation (@base, defaultImplementationMethod, interfaceImplementor));
}
- protected virtual void MapType (TypeDefinition type)
+ void MapType (TypeDefinition type)
{
+ MapInterfacesOnType (type);
MapVirtualMethods (type);
MapInterfaceMethodsInTypeHierarchy (type);
@@ -128,6 +151,109 @@ protected virtual void MapType (TypeDefinition type)
MapType (nested);
}
+ void MapInterfacesOnType (TypeDefinition type)
+ {
+ if (_interfaces.ContainsKey (type))
+ return;
+
+ // Map from inflated interface type => list of every way to recursively implement that interface in 'type declaration order' according to ECMA 335 12.2
+ Dictionary> waysToImplementIface = new (InflatedInterfaceComparer);
+
+ // Get all explicit interfaces of this type
+ foreach (var directIface in type.Interfaces) {
+ InterfaceImplementationNode directlyImplementedNode = new InterfaceImplementationNode (directIface, type, []);
+ TypeReference inflatedDirectIface = directIface.InterfaceType.TryInflateFrom (type, context)!;
+ waysToImplementIface.AddToList (inflatedDirectIface, directlyImplementedNode);
+ }
+
+ // Add interfaces on base type
+ if (type.BaseType is { } baseType && context.Resolve (baseType) is { } baseDef) {
+ MapInterfacesOnType (baseDef);
+ var baseInterfaces = _interfaces[baseDef];
+ foreach (var item in baseInterfaces) {
+ var inflatedInterface = item.InflatedInterface.TryInflateFrom (type.BaseType, context);
+ Debug.Assert (inflatedInterface is not null);
+ foreach (var node in item.InterfaceImplementationNodes) {
+ waysToImplementIface.AddToList (inflatedInterface, node);
+ }
+ }
+ }
+
+ // Recursive interfaces next to preserve Inherit/Implement tree order
+ foreach (var directIface in type.Interfaces) {
+ // If we can't resolve the interface type we can't find recursive interfaces
+ var ifaceDirectlyOnType = context.Resolve (directIface.InterfaceType);
+ if (ifaceDirectlyOnType is null) {
+ continue;
+ }
+ MapInterfacesOnType (ifaceDirectlyOnType);
+ TypeReference inflatedDirectIface = directIface.InterfaceType.TryInflateFrom (type, context)!;
+ var recursiveInterfaces = _interfaces[ifaceDirectlyOnType];
+ foreach (var recursiveInterface in recursiveInterfaces) {
+ var implToRecursiveIfaceChain = new InterfaceImplementationNode (directIface, type, recursiveInterface.InterfaceImplementationNodes);
+ // Inflate the generic arguments up to the terminal interfaceImpl to get the inflated interface type implemented by this type
+ TypeReference inflatedRecursiveInterface = inflatedDirectIface;
+ foreach (var interfaceImpl in recursiveInterface.MostDirectInterfaceImplementationPath) {
+ inflatedRecursiveInterface = interfaceImpl.InterfaceType.TryInflateFrom (inflatedRecursiveInterface, context)!;
+ }
+ waysToImplementIface.AddToList (inflatedRecursiveInterface, implToRecursiveIfaceChain);
+ }
+ }
+ ImmutableArray.Builder builder = ImmutableArray.CreateBuilder (waysToImplementIface.Count);
+ foreach (var kvp in waysToImplementIface) {
+ builder.Add (new InterfaceImplementor (type, context.Resolve (kvp.Key), kvp.Key, kvp.Value.ToImmutableArray (), context));
+ }
+ _interfaces.Add (type, builder.MoveToImmutable ());
+ }
+
+ ///
+ /// Used only to compare inflated interfaces in MapInterfacesOnType
+ ///
+ EqualityComparer InflatedInterfaceComparer => _inflatedInterfaceComparer ??= EqualityComparer.Create (InterfaceTypeEquals, t => context.Resolve (t)?.GetHashCode () ?? 0);
+ EqualityComparer? _inflatedInterfaceComparer;
+
+ ///
+ /// Compares two TypeReferences to interface types and determines if they are equivalent references, taking into account generic arguments and element types.
+ ///
+ bool InterfaceTypeEquals (TypeReference? type, TypeReference? other)
+ {
+ Debug.Assert (type is not null && other is not null);
+ Debug.Assert (context.TryResolve (type)?.IsInterface is null or true);
+ Debug.Assert (context.TryResolve (other)?.IsInterface is null or true);
+ return TypeEquals (type, other);
+
+ bool TypeEquals (TypeReference type1, TypeReference type2)
+ {
+ if (type1 == type2)
+ return true;
+
+ if (context.TryResolve (type1) != context.TryResolve (type2))
+ return false;
+
+ if (type1 is GenericInstanceType genericInstance1) {
+ if (type2 is not GenericInstanceType genericInstance2)
+ return false;
+ if (genericInstance1.HasGenericParameters != genericInstance2.HasGenericParameters)
+ return false;
+ if (genericInstance1.GenericParameters.Count != genericInstance2.GenericParameters.Count
+ || genericInstance2.GenericArguments.Count != genericInstance2.GenericArguments.Count)
+ return false;
+ for (var i = 0; i < genericInstance1.GenericArguments.Count; ++i) {
+ if (!TypeEquals (genericInstance1.GenericArguments[i], genericInstance2.GenericArguments[i]))
+ return false;
+ }
+ return true;
+ }
+
+ if (type1 is TypeSpecification typeSpec1) {
+ if (type2 is not TypeSpecification typeSpec2)
+ return false;
+ return TypeEquals (typeSpec1.ElementType, typeSpec2.ElementType);
+ }
+ return type1.FullName == type2.FullName;
+ }
+ }
+
void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type)
{
if (!type.HasInterfaces)
@@ -135,8 +261,9 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type)
// 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)) {
+ var allInflatedInterface = _interfaces[type];
+ foreach (var interfaceImplementor in allInflatedInterface) {
+ foreach (MethodReference interfaceMethod in interfaceImplementor.InflatedInterface.GetMethods (context)) {
MethodDefinition? resolvedInterfaceMethod = context.TryResolve (interfaceMethod);
if (resolvedInterfaceMethod == null)
continue;
@@ -155,20 +282,19 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type)
// Try to find an implementation with a name/sig match on the current type
MethodDefinition? exactMatchOnType = TryMatchMethod (type, interfaceMethod);
if (exactMatchOnType != null) {
- AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType, new (type, interfaceImpl.OriginalImpl, resolvedInterfaceMethod.DeclaringType, context));
+ AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType, interfaceImplementor);
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, interfaceImplementor);
continue;
}
}
-
// Look for a default implementation last.
- FindAndAddDefaultInterfaceImplementations (type, type, resolvedInterfaceMethod, interfaceImpl.OriginalImpl);
+ FindAndAddDefaultInterfaceImplementations (type, resolvedInterfaceMethod, interfaceImplementor);
}
}
}
@@ -198,7 +324,7 @@ void MapVirtualMethod (MethodDefinition method)
if (@base == null)
return;
- Debug.Assert(!@base.DeclaringType.IsInterface);
+ Debug.Assert (!@base.DeclaringType.IsInterface);
AnnotateMethods (@base, method);
}
@@ -210,7 +336,9 @@ void MapOverrides (MethodDefinition method)
if (baseMethod == null)
continue;
if (baseMethod.DeclaringType.IsInterface) {
- AnnotateMethods (baseMethod, method, InterfaceImplementor.Create (method.DeclaringType, baseMethod.DeclaringType, context));
+ var implr = GetInterfaceImplementor (method.DeclaringType, baseMethod.DeclaringType);
+ Debug.Assert (implr != null);
+ AnnotateMethods (baseMethod, method, implr);
} else {
AnnotateMethods (baseMethod, method);
}
@@ -219,8 +347,9 @@ void MapOverrides (MethodDefinition method)
void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null)
{
- AddBaseMethod (@override, @base, interfaceImplementor);
- AddOverride (@base, @override, interfaceImplementor);
+ OverrideInformation ov = new OverrideInformation (@base, @override, interfaceImplementor);
+ AddBaseMethod (@override, ov);
+ AddOverrideOfMethod (@base, ov);
}
MethodDefinition? GetBaseMethodInTypeHierarchy (MethodDefinition method)
@@ -282,22 +411,20 @@ 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 (TypeDefinition typeThatImplementsInterface, MethodDefinition interfaceMethodToBeImplemented, InterfaceImplementor implr)
{
- // Go over all interfaces, trying to find a method that is an explicit MethodImpl of the
- // interface method in question.
-
- foreach (var interfaceImpl in typeThatMayHaveDIM.Interfaces) {
- var potentialImplInterface = context.TryResolve (interfaceImpl.InterfaceType);
- if (potentialImplInterface == null)
- continue;
-
+ foreach (var potentialDimProviderImplementation in _interfaces[typeThatImplementsInterface]) {
bool foundImpl = false;
+ if (potentialDimProviderImplementation.InterfaceType is null)
+ continue;
+ // If this interface doesn't implement the interface with the method we're looking for, it can't provide a default implementation.
+ if (GetInterfaceImplementor (potentialDimProviderImplementation.InterfaceType, interfaceMethodToBeImplemented.DeclaringType) is null)
+ continue;
- foreach (var potentialImplMethod in potentialImplInterface.Methods) {
+ foreach (var potentialImplMethod in potentialDimProviderImplementation.InterfaceType.Methods) {
if (potentialImplMethod == interfaceMethodToBeImplemented &&
!potentialImplMethod.IsAbstract) {
- AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl, interfaceMethodToBeImplemented.DeclaringType, context), potentialImplMethod);
+ AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, implr, potentialImplMethod);
foundImpl = true;
break;
}
@@ -308,22 +435,15 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement
// This method is an override of something. Let's see if it's the method we are looking for.
foreach (var baseMethod in potentialImplMethod.Overrides) {
if (context.TryResolve (baseMethod) == interfaceMethodToBeImplemented) {
- AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl, interfaceMethodToBeImplemented.DeclaringType, context), @potentialImplMethod);
+ AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, implr, @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);
- }
}
}
diff --git a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs
index 5092fe1158e34..b886ad8aaa4d8 100644
--- a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs
+++ b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs
@@ -170,6 +170,13 @@ void parseArrayDimensions (ArrayType at)
}
}
+ public static TypeReference? TryInflateFrom (this TypeReference type, TypeReference maybeGenericInstanceProvider, ITryResolveMetadata resolver)
+ {
+ if (maybeGenericInstanceProvider is GenericInstanceType git)
+ return InflateGenericType (git, type, resolver);
+ return type;
+ }
+
public static TypeReference? InflateGenericType (GenericInstanceType genericInstanceProvider, TypeReference typeToInflate, ITryResolveMetadata resolver)
{
if (typeToInflate is ArrayType arrayType) {
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 4b3f387a39015..19189bfcd0170 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
@@ -15,6 +15,12 @@ public Task DefaultInterfaceMethodCallIntoClass ()
return RunTest (allowMissingWarnings: true);
}
+ [Fact]
+ public Task DimProvidedByRecursiveInterface ()
+ {
+ return RunTest (allowMissingWarnings: true);
+ }
+
[Fact]
public Task GenericDefaultInterfaceMethods ()
{
@@ -39,6 +45,12 @@ public Task MostSpecificDefaultImplementationKeptStatic ()
return RunTest (allowMissingWarnings: true);
}
+ [Fact]
+ public Task MultipleDimsProvidedByRecursiveInterface ()
+ {
+ return RunTest (allowMissingWarnings: true);
+ }
+
[Fact]
public Task SimpleDefaultInterfaceMethod ()
{
@@ -51,6 +63,12 @@ public Task StaticDefaultInterfaceMethodOnStruct ()
return RunTest (allowMissingWarnings: true);
}
+ [Fact]
+ public Task StaticDimProvidedByUnreferencedIfaceInHierarchy ()
+ {
+ return RunTest (allowMissingWarnings: true);
+ }
+
[Fact]
public Task UnusedDefaultInterfaceImplementation ()
{
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
new file mode 100644
index 0000000000000..d436348e800bc
--- /dev/null
+++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.RecursiveInterfacesTests.g.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace ILLink.RoslynAnalyzer.Tests.Inheritance.Interfaces
+{
+ public sealed partial class RecursiveInterfacesTests : LinkerTestBase
+ {
+
+ protected override string TestSuiteName => "Inheritance.Interfaces.RecursiveInterfaces";
+
+ [Fact]
+ public Task GenericInterfaceImplementedRecursively ()
+ {
+ return RunTest (allowMissingWarnings: true);
+ }
+
+ [Fact]
+ public Task InterfaceImplementedRecursively ()
+ {
+ return RunTest (allowMissingWarnings: true);
+ }
+
+ [Fact]
+ public Task RecursiveInterfaceKept ()
+ {
+ return RunTest (allowMissingWarnings: true);
+ }
+
+ }
+}
diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs
index 649b8449527f7..2e1a2bbcb3454 100644
--- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs
+++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs
@@ -15,12 +15,6 @@ public Task CanDisableUnusedInterfaces ()
return RunTest (allowMissingWarnings: true);
}
- [Fact]
- public Task InterfaceImplementedThroughBaseInterface ()
- {
- return RunTest (allowMissingWarnings: true);
- }
-
[Fact]
public Task InterfaceOnUninstantiatedTypeRemoved ()
{
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs
index d0d236997445e..9fc2b77d7a487 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs
@@ -6,6 +6,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
+using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using ILLink.RoslynAnalyzer;
@@ -1155,7 +1156,8 @@ static void GuardedLocalFunction ()
public static void Test ()
{
- GuardInIterator ();
+ // Use the IEnumerable to mark the IEnumerable methods
+ foreach (var _ in GuardInIterator ()) ;
StateFlowsAcrossYield ();
GuardInAsync ();
StateFlowsAcrossAwait ();
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByRecursiveInterface.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByRecursiveInterface.il
new file mode 100644
index 0000000000000..c85892208989b
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByRecursiveInterface.il
@@ -0,0 +1,86 @@
+// 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 abstract sealed beforefieldinit Program
+ extends [mscorlib]System.Object
+{
+ // Nested Types
+ .class interface nested public auto ansi abstract beforefieldinit IFoo
+ {
+ // Methods
+ .method public hidebysig newslot abstract virtual
+ instance void Method () cil managed
+ {
+ } // end of method IFoo::Method
+
+ } // end of class IFoo
+
+ .class interface nested public auto ansi abstract beforefieldinit IBar
+ implements Program/IFoo
+ {
+ // Methods
+ .method public final hidebysig virtual
+ instance void Program.IFoo.Method () cil managed
+ {
+ .override method instance void Program/IFoo::Method()
+ // Method begins at RVA 0x2068
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: nop
+ IL_0001: ret
+ } // end of method IBar::Program.IFoo.Method
+
+ } // end of class IBar
+
+ .class interface nested public auto ansi abstract beforefieldinit IBaz
+ implements Program/IBar
+ {
+ } // end of class IBaz
+
+ .class nested public auto ansi beforefieldinit MyFoo
+ extends [mscorlib]System.Object
+ implements Program/IBaz
+ {
+ // Methods
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x2076
+ // Code size 8 (0x8)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Object::.ctor()
+ IL_0006: nop
+ IL_0007: ret
+ } // end of method MyFoo::.ctor
+
+ } // end of class MyFoo
+
+
+ // Methods
+ .method public hidebysig static
+ void CallMethod (
+ class Program/IFoo foo
+ ) cil managed
+ {
+ .custom instance void [mscorlib]mscorlib.CompilerServices.NullableContextAttribute::.ctor(uint8) = (
+ 01 00 01 00 00
+ )
+ // Method begins at RVA 0x2050
+ // Code size 9 (0x9)
+ .maxstack 8
+
+ IL_0000: nop
+ IL_0001: ldarg.0
+ IL_0002: callvirt instance void Program/IFoo::Method()
+ IL_0007: nop
+ IL_0008: ret
+ } // end of method Program::CallMethod
+
+} // end of class Program
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/MultipleDimsProvidedByRecursiveInterface.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/MultipleDimsProvidedByRecursiveInterface.il
new file mode 100644
index 0000000000000..4937513f28531
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/MultipleDimsProvidedByRecursiveInterface.il
@@ -0,0 +1,86 @@
+// 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 abstract sealed beforefieldinit Program
+ extends [mscorlib]System.Object
+{
+ // Nested Types
+ .class interface nested public auto ansi abstract beforefieldinit I0
+ {
+ // Methods
+ .method public hidebysig newslot abstract virtual
+ instance void Method () cil managed
+ {
+ } // end of method I0::Method
+
+ } // end of class I0
+
+ .class interface nested public auto ansi abstract beforefieldinit I00
+ implements Program/I0
+ {
+ // Methods
+ .method public final hidebysig virtual
+ instance void Program.I0.Method () cil managed
+ {
+ .override method instance void Program/I0::Method()
+ // Method begins at RVA 0x2068
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: nop
+ IL_0001: ret
+ } // end of method I00::Program.I0.Method
+ } // end of class I00
+
+ .class interface nested public auto ansi abstract beforefieldinit I01
+ implements Program/I0
+ {
+ // Methods
+ .method public final hidebysig virtual
+ instance void Program.I0.Method () cil managed
+ {
+ .override method instance void Program/I0::Method()
+ // Method begins at RVA 0x2068
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: nop
+ IL_0001: ret
+ } // end of method I00::Program.I0.Method
+ } // end of class I00
+
+ .class interface nested public auto ansi abstract beforefieldinit I000
+ implements Program/I00
+ {
+ } // end of class I000
+
+ .class interface nested public auto ansi abstract beforefieldinit I010
+ implements Program/I01
+ {
+ } // end of class I000
+
+ .class nested public auto ansi beforefieldinit MyFoo
+ extends [mscorlib]System.Object
+ implements Program/I000, Program/I010
+ {
+ // Methods
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x2076
+ // Code size 8 (0x8)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Object::.ctor()
+ IL_0006: nop
+ IL_0007: ret
+ } // end of method MyFoo::.ctor
+
+ } // end of class MyFoo
+
+} // end of class Program
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/StaticDimProvidedByUnreferencedIfaceInHierarchy.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/StaticDimProvidedByUnreferencedIfaceInHierarchy.il
new file mode 100644
index 0000000000000..949d5e6fbf4ab
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/StaticDimProvidedByUnreferencedIfaceInHierarchy.il
@@ -0,0 +1,70 @@
+// 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 abstract sealed beforefieldinit Program
+ extends [mscorlib]System.Object
+{
+ // Nested Types
+ .class interface nested public auto ansi abstract beforefieldinit IBase
+ {
+ // Methods
+ .method public hidebysig abstract virtual static
+ void Method () cil managed
+ {
+ } // end of method IBase::Method
+
+ } // end of class IBase
+
+ .class interface nested public auto ansi abstract beforefieldinit I2
+ implements Program/IBase
+ {
+ // Methods
+ .method public hidebysig static
+ void Program.IBase.Method () cil managed
+ {
+ .override method void Program/IBase::Method()
+ // Method begins at RVA 0x205f
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: nop
+ IL_0001: ret
+ } // end of method I2::Program.IBase.Method
+
+ } // end of class I2
+
+ .class interface nested public auto ansi abstract beforefieldinit I3
+ implements Program/I2
+ {
+ } // end of class I3
+
+ .class interface nested public auto ansi abstract beforefieldinit I4
+ implements Program/I3
+ {
+ } // end of class I4
+
+
+ // Methods
+ .method public hidebysig static
+ void CallMethod<(Program/IBase) T> () cil managed
+ {
+ .param constraint T, Program/IBase
+ .custom instance void [mscorlib]mscorlib.CompilerServices.NullableAttribute::.ctor(uint8) = (
+ 01 00 01 00 00
+ )
+ // Method begins at RVA 0x2050
+ // Code size 14 (0xe)
+ .maxstack 8
+
+ IL_0000: nop
+ IL_0001: constrained. !!T
+ IL_0007: call void Program/IBase::Method()
+ IL_000c: nop
+ IL_000d: ret
+ } // end of method Program::CallMethod
+
+} // end of class Program
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByRecursiveInterface.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByRecursiveInterface.cs
new file mode 100644
index 0000000000000..743df7227a80c
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByRecursiveInterface.cs
@@ -0,0 +1,67 @@
+
+
+using Mono.Linker.Tests.Cases.Expectations.Assertions;
+using Mono.Linker.Tests.Cases.Expectations.Metadata;
+
+namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods
+{
+ [SetupLinkerArgument ("--skip-unresolved", "true")]
+ [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")]
+ [Define ("IL_ASSEMBLY_AVAILABLE")]
+ [SetupCompileBefore ("library.dll", new[] { "Dependencies/DimProvidedByRecursiveInterface.il" })]
+ [SkipILVerify]
+
+#if IL_ASSEMBLY_AVAILABLE
+ [KeptMemberInAssembly ("library.dll", typeof(Program.IFoo), "Method()")]
+ [KeptTypeInAssembly ("library.dll", typeof(Program.IBar))]
+ [KeptMemberInAssembly ("library.dll", typeof(Program.IBar), "Program.IFoo.Method()")]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IBar), "library.dll", typeof (Program.IFoo))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.MyFoo), "library.dll", typeof (Program.IBaz))]
+ [KeptTypeInAssembly ("library.dll", typeof(Program.IBaz))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IBaz), "library.dll", typeof (Program.IBar))]
+ [KeptMemberInAssembly ("library.dll", typeof(Program), "CallMethod(Program/IFoo)")]
+#endif
+ class DimProvidedByRecursiveInterface
+ {
+ static void Main ()
+ {
+#if IL_ASSEMBLY_AVAILABLE
+ Program.IFoo foo = new Program.MyFoo ();
+ Program.CallMethod(foo);
+#endif
+ }
+ }
+}
+
+
+
+// public static class Program
+// {
+// [Kept]
+// interface IFoo
+// {
+// void Method();
+// }
+
+// [Kept]
+// interface IBar : IFoo
+// {
+// [Kept]
+// void IFoo.Method() { }
+// }
+
+// [Kept]
+// interface IBaz: IBar /* not IFoo */
+// {
+// }
+
+// [Kept]
+// [KeptInterface(typeof(IBaz))]
+// class MyFoo : IBaz /* not IBar, not IFoo */
+// { }
+
+// static void CallMethod(IFoo foo)
+// {
+// foo.Method();
+// }
+// }
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MultipleDimsProvidedByRecursiveInterface.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MultipleDimsProvidedByRecursiveInterface.cs
new file mode 100644
index 0000000000000..7d027825ec409
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MultipleDimsProvidedByRecursiveInterface.cs
@@ -0,0 +1,86 @@
+
+
+using Mono.Linker.Tests.Cases.Expectations.Assertions;
+using Mono.Linker.Tests.Cases.Expectations.Metadata;
+
+namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods
+{
+ [SetupLinkerArgument ("--skip-unresolved", "true")]
+ [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")]
+ [Define ("IL_ASSEMBLY_AVAILABLE")]
+ [SetupCompileBefore ("library.dll", new[] { "Dependencies/MultipleDimsProvidedByRecursiveInterface.il" })]
+ [SkipILVerify]
+
+#if IL_ASSEMBLY_AVAILABLE
+ // Both DIMs on I01 and I00 should be kept because one is not more specific than another.
+ [KeptMemberInAssembly ("library.dll", typeof(Program.I0), "Method()")]
+ [KeptTypeInAssembly ("library.dll", typeof(Program.I00))]
+ [KeptMemberInAssembly ("library.dll", typeof(Program.I00), "Program.I0.Method()")]
+ [KeptMemberInAssembly ("library.dll", typeof(Program.I01), "Program.I0.Method()")]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I00), "library.dll", typeof (Program.I0))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.MyFoo), "library.dll", typeof (Program.I000))]
+ [KeptTypeInAssembly ("library.dll", typeof(Program.I000))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I000), "library.dll", typeof (Program.I00))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.MyFoo), "library.dll", typeof (Program.I010))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I010), "library.dll", typeof (Program.I01))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I01), "library.dll", typeof (Program.I0))]
+#endif
+ class MultipleDimsProvidedByRecursiveInterface
+ {
+ static void Main ()
+ {
+#if IL_ASSEMBLY_AVAILABLE
+ Program.I0 foo = new Program.MyFoo ();
+ CallMethod(foo);
+#endif
+ }
+#if IL_ASSEMBLY_AVAILABLE
+ [Kept]
+ static void CallMethod(Program.I0 foo)
+ {
+ foo.Method();
+ }
+#endif
+ }
+}
+
+
+
+// public static class Program
+// {
+// [Kept]
+// interface I0
+// {
+// void Method();
+// }
+
+// [Kept]
+// interface I00 : I0
+// {
+// [Kept]
+// void I0.Method() { }
+// }
+
+// [Kept]
+// interface I000: I00 /* not I0 */
+// {
+// }
+
+// [Kept]
+// interface I01 : I0
+// {
+// [Kept]
+// void I0.Method() { }
+// }
+
+// [Kept]
+// interface I010: I01 /* not I0 */
+// {
+// }
+
+// [Kept]
+// [KeptInterface(typeof(I000))]
+// class MyFoo : I000, I010
+// { }
+
+// }
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs
new file mode 100644
index 0000000000000..2d10e78d146f3
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs
@@ -0,0 +1,69 @@
+
+
+using Mono.Linker.Tests.Cases.Expectations.Assertions;
+using Mono.Linker.Tests.Cases.Expectations.Metadata;
+
+namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods
+{
+ [SetupLinkerArgument ("--skip-unresolved", "true")]
+ [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")]
+ [Define ("IL_ASSEMBLY_AVAILABLE")]
+ [SetupCompileBefore ("library.dll", new[] { "Dependencies/StaticDimProvidedByUnreferencedIfaceInHierarchy.il" })]
+ [SkipILVerify]
+
+#if IL_ASSEMBLY_AVAILABLE
+ [KeptMemberInAssembly ("library.dll", typeof(Program), "CallMethod<#1>()")]
+ [KeptTypeInAssembly ("library.dll", typeof(Program.IBase))]
+ [KeptMemberInAssembly ("library.dll", typeof(Program.IBase), "Method()")]
+ [KeptTypeInAssembly ("library.dll", typeof(Program.I4))]
+ [KeptTypeInAssembly ("library.dll", typeof(Program.I2))]
+ [KeptMemberInAssembly ("library.dll", typeof(Program.I2), "Program.IBase.Method()")]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I2), "library.dll", typeof (Program.IBase))]
+ [KeptTypeInAssembly ("library.dll", typeof(Program.I3))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I3), "library.dll", typeof (Program.I2))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I4), "library.dll", typeof (Program.I3))]
+#endif
+ class StaticDimProvidedByUnreferencedIfaceInHierarchy
+ {
+ static void Main ()
+ {
+#if IL_ASSEMBLY_AVAILABLE
+ Program.CallMethod();
+#endif
+ }
+ }
+}
+
+
+
+// public static class Program
+// {
+// [Kept]
+// interface IBase
+// {
+// [Kept]
+// static abstract void Method();
+// }
+
+// [Kept]
+// [KeptInterface(typeof(IBase)]
+// interface I2 : IBase
+// {
+// [Kept]
+// static void IBase.Method() { }
+// }
+
+// [Kept]
+// [KeptInterface(typeof(I2)]
+// interface I3 : I2 { }
+
+// [Kept]
+// [KeptInterface(typeof(I3)]
+// interface I4 : I3 { }
+
+// [Kept]
+// static void CallMethod() where T : IBase
+// {
+// T.Method();
+// }
+// }
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedThroughBaseInterface.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedThroughBaseInterface.il
deleted file mode 100644
index 61080f8b7d066..0000000000000
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedThroughBaseInterface.il
+++ /dev/null
@@ -1,48 +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.
-
-.assembly extern mscorlib { }
-
-.assembly 'library' { }
-
-.class interface public auto ansi abstract beforefieldinit IBase
-{
- // Methods
- .method public hidebysig newslot abstract virtual
- instance void M () cil managed
- {
- } // end of method IBase::M
-
-} // end of class IBase
-
-.class interface public auto ansi abstract beforefieldinit IDerived
- implements IBase
-{
-} // end of class IDerived
-
-.class public auto ansi beforefieldinit C
- extends [System.Runtime]System.Object
- implements IDerived
-{
- // Methods
- .method private final hidebysig newslot virtual
- instance void IBase.M () cil managed
- {
- .override method instance void IBase::M()
- // Method begins at RVA 0x2050
- // Code size 2 (0x2)
- .maxstack 8
-
- IL_0001: ret
- } // end of method C::IBase.M
-
- .method public hidebysig specialname rtspecialname
- instance void .ctor () cil managed
- {
- // Method begins at RVA 0x2053
- // Code size 8 (0x8)
- .maxstack 8
-
- IL_0007: ret
- } // end of method C::.ctor
-}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedThroughBaseInterface.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedThroughBaseInterface.cs
deleted file mode 100644
index e701fb9c28ba6..0000000000000
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedThroughBaseInterface.cs
+++ /dev/null
@@ -1,34 +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.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-using Mono.Linker.Tests.Cases.Expectations.Assertions;
-using Mono.Linker.Tests.Cases.Expectations.Metadata;
-
-namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces
-{
- [SetupLinkerArgument ("--skip-unresolved", "true")]
- [SetupLinkerArgument ("-a", "test.exe", "library")]
- [SetupLinkerArgument ("-a", "library.dll", "library")]
- [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")]
- [Define ("IL_ASSEMBLY_AVAILABLE")]
- [SetupCompileBefore ("library.dll", new[] { "Dependencies/InterfaceImplementedThroughBaseInterface.il" })]
- [SkipILVerify]
-
-#if IL_ASSEMBLY_AVAILABLE
- [KeptMemberInAssembly ("library.dll", typeof(C), "IBase.M()")]
-#endif
- [KeptMember(".ctor()")]
- public class InterfaceImplementedThroughBaseInterface
- {
- public static void Main ()
- {
- }
- }
-}
-
-
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyVariations.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyVariations.cs
index 078f4b94c60ba..79ad8eaaa58c2 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyVariations.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyVariations.cs
@@ -68,4 +68,4 @@ class Bar
{
}
}
-}
\ No newline at end of file
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/GenericInterfaceImplementedRecursively.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/GenericInterfaceImplementedRecursively.il
new file mode 100644
index 0000000000000..7ff59e54ad065
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/GenericInterfaceImplementedRecursively.il
@@ -0,0 +1,45 @@
+// 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 abstract sealed beforefieldinit Program
+ extends [mscorlib]System.Object
+{
+ // Nested Types
+ .class interface nested public auto ansi abstract beforefieldinit IBase`1
+ {
+ } // end of class IBase
+
+ .class interface nested public auto ansi abstract beforefieldinit IMiddle`1
+ implements class Program/IBase`1
+ {
+ } // end of class IMiddle
+
+ .class interface nested public auto ansi abstract beforefieldinit IDerived`1
+ implements class Program/IMiddle`1
+ {
+ } // end of class IDerived
+
+ .class nested public auto ansi beforefieldinit C
+ extends [mscorlib]System.Object
+ implements class Program/IDerived`1
+ {
+ // Methods
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x2066
+ // Code size 8 (0x8)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Object::.ctor()
+ IL_0006: nop
+ IL_0007: ret
+ } // end of method C::.ctor
+
+ } // end of class C
+} // end of class Program
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/InterfaceImplementedRecursively.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/InterfaceImplementedRecursively.il
new file mode 100644
index 0000000000000..c1d2943997da6
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/InterfaceImplementedRecursively.il
@@ -0,0 +1,67 @@
+// 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 abstract sealed beforefieldinit Program
+ extends [System.Runtime]System.Object
+{
+ // Nested Types
+ .class interface nested public auto ansi abstract beforefieldinit IBase
+ {
+ } // end of class IBase
+
+ .class interface nested public auto ansi abstract beforefieldinit IMiddle
+ implements Program/IBase
+ {
+ } // end of class IMiddle
+
+ .class interface nested public auto ansi abstract beforefieldinit IDerived
+ implements Program/IMiddle
+ {
+ } // end of class IDerived
+
+ .class nested public auto ansi beforefieldinit C
+ extends [System.Runtime]System.Object
+ implements Program/IDerived
+ {
+ // Methods
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x2066
+ // Code size 8 (0x8)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void [System.Runtime]System.Object::.ctor()
+ IL_0006: nop
+ IL_0007: ret
+ } // end of method C::.ctor
+
+ } // end of class C
+
+
+ // Methods
+ .method public hidebysig static
+ void Main () cil managed
+ {
+ // Method begins at RVA 0x2050
+ // Code size 10 (0xa)
+ .maxstack 1
+ .locals init (
+ [0] class Program/IBase b,
+ [1] class Program/C c
+ )
+
+ IL_0000: nop
+ IL_0001: ldnull
+ IL_0002: stloc.0
+ IL_0003: newobj instance void Program/C::.ctor()
+ IL_0008: stloc.1
+ IL_0009: ret
+ } // end of method Program::Main
+
+} // end of class Program
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveInterfaceTwoImplementationPaths.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveInterfaceTwoImplementationPaths.il
new file mode 100644
index 0000000000000..854b0cd4a45b2
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveInterfaceTwoImplementationPaths.il
@@ -0,0 +1,62 @@
+
+// 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 abstract sealed beforefieldinit Library
+ extends [mscorlib]System.Object
+{
+ .class interface nested public auto ansi abstract beforefieldinit I0
+ {
+ } // end of class I0
+
+ .class interface nested public auto ansi abstract beforefieldinit I00
+ implements Library/I0
+ {
+ } // end of class I00
+
+ .class interface nested public auto ansi abstract beforefieldinit I01
+ implements Library/I0
+ {
+ } // end of class I01
+
+ .class interface nested public auto ansi abstract beforefieldinit I000
+ implements Library/I00
+ {
+ } // end of class I000
+
+ .class interface nested public auto ansi abstract beforefieldinit I010
+ implements Library/I01
+ {
+ } // end of class I010
+
+ .class interface nested public auto ansi abstract beforefieldinit I0100
+ implements Library/I010
+ {
+ } // end of class I010
+
+ .class nested public auto ansi beforefieldinit MyClass
+ extends [mscorlib]System.Object
+ implements Library/I0100,
+ Library/I000
+ {
+ // Methods
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x2076
+ // Code size 8 (0x8)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Object::.ctor()
+ IL_0006: nop
+ IL_0007: ret
+ } // end of method MyFoo::.ctor
+
+ } // end of class MyFoo
+
+} // end of class Library
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
new file mode 100644
index 0000000000000..bb0d318cac1a1
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/GenericInterfaceImplementedRecursively.cs
@@ -0,0 +1,42 @@
+// 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/GenericInterfaceImplementedRecursively.il" })]
+ [SkipILVerify]
+#if IL_ASSEMBLY_AVAILABLE
+ [KeptTypeInAssembly ("library.dll", typeof(Program.IBase<>))]
+ [KeptTypeInAssembly ("library.dll", typeof(Program.IMiddle<>))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IMiddle<>), "library.dll", typeof (Program.IBase<>))]
+ [KeptTypeInAssembly ("library.dll", typeof(Program.IDerived<>))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IDerived<>), "library.dll", typeof (Program.IMiddle<>))]
+ [KeptTypeInAssembly ("library.dll", typeof(Program.C))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.C), "library.dll", typeof (Program.IDerived))]
+#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.
+ ///
+ class GenericInterfaceImplementedRecursively
+ {
+ public static void Main()
+ {
+
+#if IL_ASSEMBLY_AVAILABLE
+ Program.IBase _ = null;
+ _ = new Program.C();
+#endif
+ }
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/InterfaceImplementedRecursively.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/InterfaceImplementedRecursively.cs
new file mode 100644
index 0000000000000..89f59777c5fcd
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/InterfaceImplementedRecursively.cs
@@ -0,0 +1,49 @@
+// 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/InterfaceImplementedRecursively.il" })]
+ [SkipILVerify]
+#if IL_ASSEMBLY_AVAILABLE
+ [KeptTypeInAssembly ("library.dll", typeof(Program.IBase))]
+ [KeptTypeInAssembly ("library.dll", typeof(Program.IMiddle))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IMiddle), "library.dll", typeof (Program.IBase))]
+ [KeptTypeInAssembly ("library.dll", typeof(Program.IDerived))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IDerived), "library.dll", typeof (Program.IMiddle))]
+ [KeptTypeInAssembly ("library.dll", typeof(Program.C))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.C), "library.dll", typeof (Program.IDerived))]
+#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.
+ ///
+ class InterfaceImplementedRecursively
+ {
+ public static void Main()
+ {
+
+#if IL_ASSEMBLY_AVAILABLE
+ Program.IBase b = null;
+ object c = new Program.C();
+
+#endif
+ }
+ }
+ // interface IBase {}
+ // interface IMiddle : IBase {}
+ // interface IDerived : IMiddle {}
+ // class C : IDerived
+ // {
+ // }
+}
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
new file mode 100644
index 0000000000000..56e08d67be74f
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveInterfaceKept.cs
@@ -0,0 +1,36 @@
+// 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 Mono.Linker.Tests.Cases.Expectations.Assertions;
+using Mono.Linker.Tests.Cases.Expectations.Metadata;
+
+namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces
+{
+ ///
+ /// This tests that when a type implements an interface recursively (via implementations on implemented interfaces),
+ /// the interface implementations kept are in type declaration order according to ECMA-335 12.2
+ ///
+ [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")]
+ [Define ("IL_ASSEMBLY_AVAILABLE")]
+ [SetupCompileBefore ("library.dll", new[] { "Dependencies/RecursiveInterfaceTwoImplementationPaths.il" })]
+ [SkipILVerify]
+#if IL_ASSEMBLY_AVAILABLE
+ [KeptTypeInAssembly ("library.dll", typeof(Library.MyClass))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.MyClass), "library.dll", typeof (Library.I0100))]
+ [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))]
+#endif
+ public class RecursiveInterfaceKept
+ {
+ public static void Main()
+ {
+#if IL_ASSEMBLY_AVAILABLE
+ Library.I0 _ = new Library.MyClass();
+#endif
+ }
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/CompilerGeneratedCodeSubstitutions.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/CompilerGeneratedCodeSubstitutions.cs
index 5b1edc37671ac..3c59c3e446aad 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/CompilerGeneratedCodeSubstitutions.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/CompilerGeneratedCodeSubstitutions.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using System.Linq;
using System.Threading.Tasks;
using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
@@ -174,7 +175,8 @@ static IEnumerable TestBranchWithYieldBefore ()
public static void Test ()
{
- TestBranchWithNormalCall ();
+ // Use the IEnumerable to mark the IEnumerable methods
+ foreach (var _ in TestBranchWithNormalCall ()) ;
TestBranchWithYieldAfter ();
TestBranchWithYieldBefore ();
}
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 8ed15724cc114..467c2272c1bed 100644
--- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs
@@ -345,7 +345,7 @@ IEnumerable VerifyInterfaces (TypeDefinition src, TypeDefinition linked)
}
if (expectedInterfaces.Any ()) {
- yield return $"Expected interfaces were not found on {src}";
+ yield return $"Expected interfaces were not found on {src}. Expected to find: \n{string.Join(Environment.NewLine, expectedInterfaces)}\n";
}
}
}