diff --git a/src/libraries/System.Reflection.Context/src/ILLink/ILLink.Suppressions.xml b/src/libraries/System.Reflection.Context/src/ILLink/ILLink.Suppressions.xml
index 6e691bf6bc961d..8df6b6dd9fa8b8 100644
--- a/src/libraries/System.Reflection.Context/src/ILLink/ILLink.Suppressions.xml
+++ b/src/libraries/System.Reflection.Context/src/ILLink/ILLink.Suppressions.xml
@@ -523,5 +523,53 @@
member
M:System.Reflection.Context.Delegation.DelegatingType.GetInterface(System.String,System.Boolean)
+
+ ILLink
+ IL2094
+ member
+ M:System.Reflection.Context.Projection.ProjectingType.GetField(System.String,System.Reflection.BindingFlags)
+
+
+ ILLink
+ IL2094
+ member
+ M:System.Reflection.Context.Projection.ProjectingType.GetFields(System.Reflection.BindingFlags)
+
+
+ ILLink
+ IL2094
+ member
+ M:System.Reflection.Context.Projection.ProjectingType.GetMembers(System.Reflection.BindingFlags)
+
+
+ ILLink
+ IL2094
+ member
+ M:System.Reflection.Context.Projection.ProjectingType.GetMethods(System.Reflection.BindingFlags)
+
+
+ ILLink
+ IL2094
+ member
+ M:System.Reflection.Context.Projection.ProjectingType.GetProperties(System.Reflection.BindingFlags)
+
+
+ ILLink
+ IL2094
+ member
+ M:System.Reflection.Context.Projection.ProjectingType.GetDefaultMembers()
+
+
+ ILLink
+ IL2094
+ member
+ M:System.Reflection.Context.Custom.CustomType.GetProperties(System.Reflection.BindingFlags)
+
+
+ ILLink
+ IL2094
+ member
+ M:System.Reflection.Context.Custom.CustomType.GetMethods(System.Reflection.BindingFlags)
+
-
\ No newline at end of file
+
diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
index 280f1908f79f03..9ac54b5f36d81f 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;
@@ -738,18 +739,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)
@@ -2398,35 +2400,37 @@ void MarkNamedProperty (TypeDefinition type, string property_name, in Dependency
void MarkInterfaceImplementations (TypeDefinition type)
{
- var ifaces = Annotations.GetRecursiveInterfaces (type);
+ var ifaces = Annotations.GetRuntimeInterfaces (type);
if (ifaces is null)
return;
- foreach (var (ifaceType, impls) in ifaces) {
+ 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)
{
+ var type = runtimeInterfaceImplementation.Implementor;
+ var ifaces = runtimeInterfaceImplementation.InterfaceImplementation;
+ var ifaceType = runtimeInterfaceImplementation.InterfaceTypeDefinition;
if (ifaces.All (Annotations.IsMarked))
return false;
if (!Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, type))
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);
@@ -2509,11 +2513,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 (!(IsInterfaceImplementationMarkedRecursively (overrideInformation.RuntimeInterfaceImplementation.Implementor, @base.DeclaringType)))
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
@@ -2529,12 +2535,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)
@@ -2597,31 +2603,18 @@ void MarkCustomMarshalerGetInstance (TypeDefinition type, in DependencyInfo reas
void MarkICustomMarshalerMethods (TypeDefinition inputType, in DependencyInfo reason, MessageOrigin origin)
{
- TypeDefinition? type = inputType;
- do {
- if (!type.HasInterfaces)
- continue;
-
- 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;
- }
+ var runtimeInterfaces = Annotations.GetRuntimeInterfaces (inputType);
+ if (runtimeInterfaces is null)
+ return;
+ foreach (var runtimeInterface in runtimeInterfaces) {
- MarkMethodsIf (tdef.Methods, m => !m.IsStatic, reason, origin);
+ var iface_type = runtimeInterface.InterfaceTypeDefinition;
+ if (false == iface_type?.IsTypeOf ("System.Runtime.InteropServices", "ICustomMarshaler"))
+ continue;
+ MarkMethodsIf (iface_type!.Methods, m => !m.IsStatic, reason, origin);
- MarkInterfaceImplementation (iface, new MessageOrigin (type));
- return;
- }
- } while ((type = Context.TryResolve (type.BaseType)) != null);
+ MarkRuntimeInterfaceImplementation (runtimeInterface, new MessageOrigin (Context.Resolve (runtimeInterface.TypeWithInterfaceImplementation)));
+ }
}
bool IsNonEmptyStaticConstructor (MethodDefinition method)
@@ -3315,13 +3308,13 @@ void MarkRuntimeInterfaceImplementation (MethodDefinition method, MethodReferenc
if (!resolvedOverride.DeclaringType.IsInterface)
return;
var interfaceToBeImplemented = ov.DeclaringType;
-
- var ifaces = Annotations.GetRecursiveInterfaces (method.DeclaringType);
+ var ifaces = Annotations.GetRuntimeInterfaces (method.DeclaringType);
if (ifaces is null)
return;
+ Debug.Assert (ifaces.Value.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;
}
}
@@ -3645,7 +3638,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)
@@ -3753,9 +3746,9 @@ 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) {
+ foreach (var iface in runtimeInterface.InterfaceImplementation) {
MarkInterfaceImplementation (iface, origin, reason);
}
}
diff --git a/src/tools/illink/src/linker/Linker/Annotations.cs b/src/tools/illink/src/linker/Linker/Annotations.cs
index 68c8c320749966..cfcf350bb18040 100644
--- a/src/tools/illink/src/linker/Linker/Annotations.cs
+++ b/src/tools/illink/src/linker/Linker/Annotations.cs
@@ -31,10 +31,10 @@
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
-using System.Reflection.Metadata.Ecma335;
using ILLink.Shared.TrimAnalysis;
using Mono.Cecil;
using Mono.Cecil.Cil;
@@ -719,7 +719,7 @@ 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);
}
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 e981ce872703f7..00000000000000
--- 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/MethodBodyScanner.cs b/src/tools/illink/src/linker/Linker/MethodBodyScanner.cs
index b3a230f1947959..41e1f1464506b4 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,16 @@ 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)
+ var runtimeInterfaces = context.Annotations.GetRuntimeInterfaces (type);
+ if (runtimeInterfaces is null)
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;
+ 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 0727d5d25c19a0..6eeba6e9e41aa1 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,28 @@ 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?.InterfaceImplementation[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;
}
}
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 00000000000000..292e0da0bd426d
--- /dev/null
+++ b/src/tools/illink/src/linker/Linker/RuntimeInterfaceImplementation.cs
@@ -0,0 +1,61 @@
+// 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; }
+
+ ///
+ /// The path of .interfaceimpl on or a base type that terminates with .
+ ///
+ public ImmutableArray InterfaceImplementation { get; }
+
+ ///
+ /// The type that has the InterfaceImplementation - either the or a base type of it.
+ ///
+ public TypeReference TypeWithInterfaceImplementation { get; }
+
+ ///
+ /// The type of the interface that is implemented by .
+ /// This type may be different from the corresponding InterfaceImplementation.InterfaceType if it is generic.
+ /// Generic parameters are replaces with generic arguments from the implementing type.
+ /// Because of this, do not use this for comparisons or resolving. Use or .Last().InterfaceType instead.
+ ///
+ public TypeReference InflatedInterfaceType { get; }
+
+ ///
+ /// The resolved definition of the interface type implemented by .
+ ///
+ public TypeDefinition? InterfaceTypeDefinition { get; }
+
+ public RuntimeInterfaceImplementation (TypeDefinition implementor, TypeReference typeWithFirstIfaceImpl, IEnumerable interfaceImplementation, TypeReference inflatedInterfaceType, LinkContext resolver)
+ {
+ Implementor = implementor;
+ TypeWithInterfaceImplementation = typeWithFirstIfaceImpl;
+ InterfaceImplementation = interfaceImplementation.ToImmutableArray ();
+ InflatedInterfaceType = inflatedInterfaceType;
+ InterfaceTypeDefinition = resolver.Resolve (InterfaceImplementation[InterfaceImplementation.Length - 1].InterfaceType);
+ }
+ }
+}
diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs
index 1d7b24e070d148..4cc31ce50c01bf 100644
--- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs
+++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs
@@ -29,8 +29,8 @@
// 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;
@@ -38,7 +38,6 @@
namespace Mono.Linker
{
-
public class TypeMapInfo
{
readonly HashSet assemblies = new HashSet ();
@@ -100,28 +99,24 @@ public void EnsureProcessed (AssemblyDefinition assembly)
return ret;
}
- public void AddBaseMethod (MethodDefinition method, MethodDefinition @base, InterfaceImplementor? interfaceImplementor)
+ internal void AddBaseMethod (MethodDefinition method, MethodDefinition @base, RuntimeInterfaceImplementation? runtimeInterface)
{
- base_methods.AddToList (method, new OverrideInformation (@base, method, interfaceImplementor));
+ base_methods.AddToList (method, new OverrideInformation (@base, method, runtimeInterface));
}
- public void AddOverride (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null)
+ internal void AddOverride (MethodDefinition @base, MethodDefinition @override, RuntimeInterfaceImplementation? runtimeInterface = null)
{
- override_methods.AddToList (@base, new OverrideInformation (@base, @override, interfaceImplementor));
+ override_methods.AddToList (@base, new OverrideInformation (@base, @override, runtimeInterface));
}
- public void AddDefaultInterfaceImplementation (MethodDefinition @base, InterfaceImplementor interfaceImplementor, MethodDefinition defaultImplementationMethod)
- {
- Debug.Assert(@base.DeclaringType.IsInterface);
- default_interface_implementations.AddToList (@base, new OverrideInformation (@base, defaultImplementationMethod, interfaceImplementor));
- }
-
- Dictionary)>> interfaces = new ();
+ Dictionary> _runtimeInterfaceImpls = new ();
protected virtual void MapType (TypeDefinition type)
{
+ var runtimeInterfaces = GetRecursiveInterfaceImplementations (type);
+ if (runtimeInterfaces is not null)
+ _runtimeInterfaceImpls[type] = runtimeInterfaces.Value;
MapVirtualMethods (type);
MapInterfaceMethodsInTypeHierarchy (type);
- interfaces[type] = GetRecursiveInterfaceImplementations (type);
if (!type.HasNestedTypes)
return;
@@ -130,64 +125,71 @@ 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))
+ EnsureProcessed (type.Module.Assembly);
+ if (_runtimeInterfaceImpls.TryGetValue (type, out var value))
return value;
return null;
}
- List<(TypeReference InterfaceType, List ImplementationChain)> GetRecursiveInterfaceImplementations (TypeDefinition type)
+ ImmutableArray? GetRecursiveInterfaceImplementations (TypeDefinition originalType)
{
- List<(TypeReference, List)> firstImplementationChain = new ();
+ // We'll need at least this many interfaces, so let's preallocate
+ ImmutableArray.Builder runtimeIfaces = ImmutableArray.CreateBuilder (originalType.Interfaces.Count);
- AddRecursiveInterfaces (type, [], firstImplementationChain, context);
- Debug.Assert (firstImplementationChain.All (kvp => context.Resolve (kvp.Item1) == context.Resolve (kvp.Item2.Last ().InterfaceType)));
+ List interfaces = new List (16);
+ AddRecursiveInterfaces (originalType, interfaces);
- return firstImplementationChain;
+ if (runtimeIfaces.Count == 0)
+ return null;
+ return runtimeIfaces.ToImmutable ();
- static void AddRecursiveInterfaces (TypeReference typeRef, IEnumerable pathToType, List<(TypeReference, List)> firstImplementationChain, LinkContext Context)
+ void AddRecursiveInterfaces (TypeReference typeRef, List pathToType)
{
- var type = Context.TryResolve (typeRef);
+ var type = context.TryResolve (typeRef);
if (type is null)
return;
// Get all explicit interfaces of this type
foreach (var iface in type.Interfaces) {
- var interfaceType = iface.InterfaceType.TryInflateFrom (typeRef, Context);
+ var interfaceType = iface.InterfaceType.TryInflateFrom (typeRef, context);
if (interfaceType is null) {
continue;
}
- if (!firstImplementationChain.Any (i => TypeReferenceEqualityComparer.AreEqual (i.Item1, interfaceType, Context))) {
- firstImplementationChain.Add ((interfaceType, pathToType.Append (iface).ToList ()));
+ if (!runtimeIfaces.Any (i => TypeReferenceEqualityComparer.AreEqual (i.InflatedInterfaceType, interfaceType, context))) {
+ pathToType.Add (iface);
+ runtimeIfaces.Add (new RuntimeInterfaceImplementation (originalType, typeRef, pathToType.Append (iface), interfaceType, context));
+ pathToType.RemoveAt (pathToType.Count - 1);
}
}
+ if (type.BaseType is not null) {
+ var inflatedBaseType = type.BaseType.TryInflateFrom (typeRef, context)!;
+ AddRecursiveInterfaces (inflatedBaseType, []);
+ }
+
// Recursive interfaces after all direct interfaces to preserve Inherit/Implement tree order
foreach (var iface in type.Interfaces) {
// If we can't resolve the interface type we can't find recursive interfaces
- var ifaceDirectlyOnType = iface.InterfaceType.TryInflateFrom (typeRef, Context);
+ var ifaceDirectlyOnType = iface.InterfaceType.TryInflateFrom (typeRef, context);
if (ifaceDirectlyOnType is null) {
continue;
}
- AddRecursiveInterfaces (ifaceDirectlyOnType, pathToType.Append (iface), firstImplementationChain, Context);
+ pathToType.Add (iface);
+ AddRecursiveInterfaces (ifaceDirectlyOnType, pathToType);
+ pathToType.RemoveAt (pathToType.Count - 1);
}
}
}
void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type)
{
- if (!type.HasInterfaces)
+ if (!_runtimeInterfaceImpls.TryGetValue (type, out var runtimeInterfaceImpls))
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.
@@ -196,26 +198,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, interfaceMethod, resolvedInterfaceMethod);
}
}
}
@@ -245,7 +249,7 @@ void MapVirtualMethod (MethodDefinition method)
if (@base == null)
return;
- Debug.Assert(!@base.DeclaringType.IsInterface);
+ Debug.Assert (!@base.DeclaringType.IsInterface);
AnnotateMethods (@base, method);
}
@@ -257,17 +261,20 @@ void MapOverrides (MethodDefinition method)
if (baseMethod == null)
continue;
if (baseMethod.DeclaringType.IsInterface) {
- AnnotateMethods (baseMethod, method, InterfaceImplementor.Create (method.DeclaringType, baseMethod.DeclaringType, context));
+ var found = _runtimeInterfaceImpls.TryGetValue (method.DeclaringType, out var runtimeInterfaces);
+ Debug.Assert (found);
+ 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)
@@ -329,22 +336,27 @@ 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, MethodReference inflatedInterfaceMethod, MethodDefinition interfaceMethodDef)
{
// Go over all interfaces, trying to find a method that is an explicit MethodImpl of the
// interface method in question.
+ if (!_runtimeInterfaceImpls.TryGetValue (originalInterfaceImpl.Implementor, out var runtimeIfaces))
+ return;
- foreach (var interfaceImpl in typeThatMayHaveDIM.Interfaces) {
- var potentialImplInterface = context.TryResolve (interfaceImpl.InterfaceType);
- if (potentialImplInterface == null)
+ bool findAllPossibleImplementations = inflatedInterfaceMethod.DeclaringType is GenericInstanceType;
+ foreach (var interfaceImpl in runtimeIfaces) {
+ var potentialImplInterface = interfaceImpl.InterfaceTypeDefinition;
+ if (potentialImplInterface is null)
continue;
+ // We go by MethodDefinition here and may miss the correct implementation by exiting early after finding one.
+ // To correct for this, we'll look for all possible implementations and add them all if the inflatedIntefaceMethodReference is a genericInstance
+ // Otherwise, we're good to stop looking higher in the hierarchy after the first. Note we still need to look at other branches of the hierarchy to find any diamond cases.
bool foundImpl = false;
-
foreach (var potentialImplMethod in potentialImplInterface.Methods) {
- if (potentialImplMethod == interfaceMethodToBeImplemented &&
+ if (potentialImplMethod == interfaceMethodDef &&
!potentialImplMethod.IsAbstract) {
- AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl, interfaceMethodToBeImplemented.DeclaringType, context), potentialImplMethod);
+ AddDefaultInterfaceImplementation (interfaceMethodDef, originalInterfaceImpl, potentialImplMethod);
foundImpl = true;
break;
}
@@ -354,33 +366,29 @@ 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);
- foundImpl = true;
- break;
+ if (context.TryResolve (baseMethod) == interfaceMethodDef) {
+ AddDefaultInterfaceImplementation (interfaceMethodDef, originalInterfaceImpl, @potentialImplMethod);
+ foundImpl = true && !findAllPossibleImplementations;
}
}
-
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 6189eae6d794cb..b1c94aa57309e3 100644
--- a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs
+++ b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs
@@ -158,25 +158,6 @@ void parseArrayDimensions (ArrayType at)
return typeToInflate;
}
- public static IEnumerable<(TypeReference InflatedInterface, InterfaceImplementation OriginalImpl)> GetInflatedInterfaces (this TypeReference typeRef, ITryResolveMetadata resolver)
- {
- var typeDef = resolver.TryResolve (typeRef);
-
- if (typeDef?.HasInterfaces != true)
- yield break;
-
- if (typeRef is GenericInstanceType genericInstance) {
- foreach (var interfaceImpl in typeDef.Interfaces) {
- // InflateGenericType only returns null when inflating generic parameters (and the generic instance type doesn't resolve).
- // Here we are not inflating a generic parameter but an interface type reference.
- yield return (InflateGenericType (genericInstance, interfaceImpl.InterfaceType, resolver), interfaceImpl)!;
- }
- } else {
- foreach (var interfaceImpl in typeDef.Interfaces)
- yield return (interfaceImpl.InterfaceType, interfaceImpl);
- }
- }
-
public static TypeReference? InflateGenericType (GenericInstanceType genericInstanceProvider, TypeReference typeToInflate, ITryResolveMetadata resolver)
{
if (typeToInflate is ArrayType arrayType) {
@@ -276,7 +257,7 @@ private static GenericInstanceType MakeGenericType (GenericInstanceType genericI
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)
@@ -284,10 +265,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);
}
}
diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/AttributesTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/AttributesTests.g.cs
index a912649e9cedc5..dc69ebb930a723 100644
--- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/AttributesTests.g.cs
+++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/AttributesTests.g.cs
@@ -9,6 +9,12 @@ public sealed partial class AttributesTests : LinkerTestBase
protected override string TestSuiteName => "Attributes";
+ [Fact]
+ public Task AssemblyAttributeAccessesMembers ()
+ {
+ return RunTest (allowMissingWarnings: true);
+ }
+
[Fact]
public Task AssemblyAttributeIsRemovedIfOnlyTypesUsedInAssembly ()
{
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 19189bfcd01705..dc2440a552f56b 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 d436348e800bc7..1dbdf7bd5c9ac5 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 ()
{
@@ -21,6 +27,24 @@ public Task InterfaceImplementedRecursively ()
return RunTest (allowMissingWarnings: true);
}
+ [Fact]
+ 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/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.StaticInterfaceMethodsTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.StaticInterfaceMethodsTests.g.cs
index 12c5692019fdae..531c0cb0d7d08a 100644
--- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.StaticInterfaceMethodsTests.g.cs
+++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.StaticInterfaceMethodsTests.g.cs
@@ -7,6 +7,12 @@ namespace ILLink.RoslynAnalyzer.Tests.Inheritance.Interfaces
public sealed partial class StaticInterfaceMethodsTests : LinkerTestBase
{
+ [Fact]
+ public Task InstanceMethodsWithOverridesSwept ()
+ {
+ return RunTest (allowMissingWarnings: true);
+ }
+
[Fact]
public Task OverrideInCopyAssembly ()
{
diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/MetadataTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/MetadataTests.g.cs
index 68e0654e2a445c..9e958d384e9c52 100644
--- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/MetadataTests.g.cs
+++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/MetadataTests.g.cs
@@ -9,6 +9,12 @@ public sealed partial class MetadataTests : LinkerTestBase
protected override string TestSuiteName => "Metadata";
+ [Fact]
+ public Task DebuggerDisplayNamesAreKept ()
+ {
+ return RunTest (allowMissingWarnings: true);
+ }
+
[Fact]
public Task NamesAreKept ()
{
@@ -21,5 +27,29 @@ public Task NamesAreRemoved ()
return RunTest (allowMissingWarnings: true);
}
+ [Fact]
+ public Task RootAllAssemblyNamesAreKept ()
+ {
+ return RunTest (allowMissingWarnings: true);
+ }
+
+ [Fact]
+ public Task RootDescriptorNamesAreKept ()
+ {
+ return RunTest (allowMissingWarnings: true);
+ }
+
+ [Fact]
+ public Task RootLibraryAssemblyNamesAreKept ()
+ {
+ return RunTest (allowMissingWarnings: true);
+ }
+
+ [Fact]
+ public Task RootVisibleAssemblyNamesAreKept ()
+ {
+ return RunTest (allowMissingWarnings: true);
+ }
+
}
}
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 00000000000000..0f45b8aec3aff5
--- /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/RecursiveInterfaces/BaseTypeMarkedInterfaceDerivedNotKept.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/BaseTypeMarkedInterfaceDerivedNotKept.cs
new file mode 100644
index 00000000000000..999d0c2855d426
--- /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 00000000000000..b0cde69b93de9a
--- /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 00000000000000..9a37ab31140c38
--- /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 6e703f79d4650d..71a772029587ad 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/RecursiveGenericInterfaces.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveGenericInterfaces.cs
new file mode 100644
index 00000000000000..90c1a319c353f7
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveGenericInterfaces.cs
@@ -0,0 +1,94 @@
+// 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()")]
+
+ 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 00000000000000..28fa0a4f3e4c16
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveGenericInterfacesStatic.cs
@@ -0,0 +1,125 @@
+// 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" })]
+ [KeptAllTypesAndMembersInAssembly ("library.dll")]
+ [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()", "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()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "IMiddle`2", "IBase.GetV", "V IBase`3::GetV()")]
+ [KeptTypeInAssembly ("library.dll", "IDerived`1")]
+ [KeptMemberInAssembly ("library.dll", "IDerived`1",
+ "IBase.GetV()",
+ "IBase.GetV()",
+ "IBase.GetU()",
+ "IBase.GetU()",
+ "IBase.GetU()",
+ "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.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()")]
+ [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