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.GetU () => 12; + // static long IBase.GetU () => 12; + + // static double IBase.GetU () => 12; + // static double IBase.GetU () => 12; + //} + + //public class MyClass : IDerived, IDerived + //{ + // static string IBase.GetT () => throw new NotImplementedException (); + // static string IBase.GetT () => throw new NotImplementedException (); + // static string IBase.GetT () => throw new NotImplementedException (); + // static string IBase.GetT () => throw new NotImplementedException (); + // static char IBase.GetT () => throw new NotImplementedException (); + // static char IBase.GetT () => throw new NotImplementedException (); + // static char IBase.GetT () => throw new NotImplementedException (); + // static char IBase.GetT () => throw new NotImplementedException (); + //} + + //public class MyDerivedClass : MyClass + //{ } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/RemovedInterfaceImplementationRemovedOverride.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/RemovedInterfaceImplementationRemovedOverride.cs index 01541dd73a17b8..499a08d5218570 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/RemovedInterfaceImplementationRemovedOverride.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/RemovedInterfaceImplementationRemovedOverride.cs @@ -22,6 +22,8 @@ public static void Main () GenericInterfaceGenericType.Test (); PrivateExplicitImplementationReflectedOver.Test (); InheritedInterfaces.Test (); + DimWithOverride.Test (); + Generics.Test (); } [Kept] @@ -274,5 +276,92 @@ class UsedDirectly : IDerived public static void M () { } } } + + [Kept] + public class DimWithOverride + { + [Kept] + public static void Test () + { + CallM (); + } + + [Kept] + static void CallM () where T : IBase + { + T.M (); + } + + [Kept] + interface IBase + { + [Kept] + static abstract void M (); + } + + [Kept] + [KeptInterface (typeof (IBase))] + interface IDerived : IBase + { + [Kept] + static void IBase.M () { } + } + + [Kept] + [KeptInterface (typeof (IDerived))] + [KeptInterface (typeof (IBase))] + class MyClass : IDerived + { + } + + [Kept] + [KeptBaseType (typeof (MyClass))] + [KeptInterface (typeof (IBase))] + class MyDerivedClass : MyClass, IBase + { + } + } + + [Kept] + public class Generics + { + [Kept] + public static void Test () + { + UseIGeneric (); + } + + [Kept] + static void UseIGeneric () where T : IGeneric + { + T.GetT (); + } + + [Kept] + interface IGeneric + { + [Kept] + static abstract T GetT (); + } + + [Kept] + [KeptInterface (typeof (IGeneric))] + [KeptInterface (typeof (IGeneric))] + [KeptInterface (typeof (IGeneric))] + class MyType : IGeneric, IGeneric, IGeneric + { + [Kept] + [KeptOverride (typeof (IGeneric))] + static int IGeneric.GetT () => 0; + + [Kept] + [KeptOverride (typeof (IGeneric))] + static float IGeneric.GetT () => 0; + + [Kept] + [KeptOverride (typeof (IGeneric))] + static string IGeneric.GetT () => ""; + } + } } }