diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index da4dc455f0abca..bb5e95e0a38dd0 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -65,7 +65,6 @@ protected LinkContext Context { readonly List _ivt_attributes; protected Queue<(AttributeProviderPair, DependencyInfo, MarkScopeStack.Scope)> _lateMarkedAttributes; protected List<(TypeDefinition, MarkScopeStack.Scope)> _typesWithInterfaces; - protected HashSet<(OverrideInformation, MarkScopeStack.Scope)> _interfaceOverrides; protected HashSet _dynamicInterfaceCastableImplementationTypesDiscovered; protected List _dynamicInterfaceCastableImplementationTypes; protected List<(MethodBody, MarkScopeStack.Scope)> _unreachableBodies; @@ -226,7 +225,6 @@ public MarkStep () _ivt_attributes = new List (); _lateMarkedAttributes = new Queue<(AttributeProviderPair, DependencyInfo, MarkScopeStack.Scope)> (); _typesWithInterfaces = new List<(TypeDefinition, MarkScopeStack.Scope)> (); - _interfaceOverrides = new HashSet<(OverrideInformation, MarkScopeStack.Scope)> (); _dynamicInterfaceCastableImplementationTypesDiscovered = new HashSet (); _dynamicInterfaceCastableImplementationTypes = new List (); _unreachableBodies = new List<(MethodBody, MarkScopeStack.Scope)> (); @@ -573,9 +571,10 @@ protected virtual void EnqueueMethod (MethodDefinition method, in DependencyInfo void ProcessVirtualMethods () { - foreach ((MethodDefinition method, MarkScopeStack.Scope scope) in _virtual_methods) { - using (ScopeStack.PushScope (scope)) + foreach ((var method, var scope) in _virtual_methods) { + using (ScopeStack.PushScope (scope)) { ProcessVirtualMethod (method); + } } } @@ -603,26 +602,19 @@ void ProcessMarkedTypesWithInterfaces () !unusedInterfacesOptimizationEnabled) { MarkInterfaceImplementations (type); } - // OverrideInformation for interfaces in PreservedScope aren't added yet + // Interfaces in PreservedScope should have their methods added to _virtual_methods so that they are properly processed foreach (var method in type.Methods) { - var baseOverrideInformations = Annotations.GetBaseMethods (method); - if (baseOverrideInformations is null) + var baseMethods = Annotations.GetBaseMethods (method); + if (baseMethods is null) continue; - foreach (var ov in baseOverrideInformations) { - if (ov.Base.DeclaringType is not null && ov.Base.DeclaringType.IsInterface && IgnoreScope (ov.Base.DeclaringType.Scope)) - _interfaceOverrides.Add ((ov, ScopeStack.CurrentScope)); + foreach (var ov in baseMethods) { + if (ov.Base.DeclaringType is not null && ov.Base.DeclaringType.IsInterface && IgnoreScope (ov.Base.DeclaringType.Scope)) { + _virtual_methods.Add ((ov.Base, ScopeStack.CurrentScope)); + } } } } } - - var interfaceOverrides = _interfaceOverrides.ToArray (); - foreach ((var overrideInformation, var scope) in interfaceOverrides) { - using (ScopeStack.PushScope (scope)) { - if (IsInterfaceImplementationMethodNeededByTypeDueToInterface (overrideInformation)) - MarkMethod (overrideInformation.Override, new DependencyInfo (DependencyKind.Override, overrideInformation.Base), scope.Origin); - } - } } void DiscoverDynamicCastableImplementationInterfaces () @@ -705,10 +697,23 @@ void ProcessVirtualMethod (MethodDefinition method) { Annotations.EnqueueVirtualMethod (method); - var defaultImplementations = Annotations.GetDefaultInterfaceImplementations (method); - if (defaultImplementations != null) { - foreach (var defaultImplementationInfo in defaultImplementations) { - ProcessDefaultImplementation (defaultImplementationInfo.InstanceType, defaultImplementationInfo.ProvidingInterface); + if (method.DeclaringType.IsInterface) { + var defaultImplementations = Annotations.GetDefaultInterfaceImplementations (method); + if (defaultImplementations is not null) { + foreach (var dimInfo in defaultImplementations) { + ProcessDefaultImplementation (dimInfo.ImplementingType, dimInfo.InterfaceImpl, dimInfo.DefaultInterfaceMethod); + + var ov = new OverrideInformation (method, dimInfo.DefaultInterfaceMethod, Context); + if (IsInterfaceImplementationMethodNeededByTypeDueToInterface (ov, dimInfo.ImplementingType)) + MarkMethod (ov.Override, new DependencyInfo (DependencyKind.Override, ov.Base), ScopeStack.CurrentScope.Origin); + } + } + var overridingMethods = Annotations.GetOverrides (method); + if (overridingMethods is not null) { + foreach (var ov in overridingMethods) { + if (IsInterfaceImplementationMethodNeededByTypeDueToInterface (ov, ov.Override.DeclaringType)) + MarkMethod (ov.Override, new DependencyInfo (DependencyKind.Override, ov.Base), ScopeStack.CurrentScope.Origin); + } } } } @@ -724,10 +729,8 @@ bool ShouldMarkOverrideForBase (OverrideInformation overrideInformation) Debug.Assert (Annotations.IsMarked (overrideInformation.Base) || IgnoreScope (overrideInformation.Base.DeclaringType.Scope)); if (!Annotations.IsMarked (overrideInformation.Override.DeclaringType)) return false; - if (overrideInformation.IsOverrideOfInterfaceMember) { - _interfaceOverrides.Add ((overrideInformation, ScopeStack.CurrentScope)); + if (overrideInformation.IsOverrideOfInterfaceMember) return false; - } if (!Context.IsOptimizationEnabled (CodeOptimizations.OverrideRemoval, overrideInformation.Override)) return true; @@ -816,9 +819,10 @@ bool RequiresInterfaceRecursively (TypeDefinition typeToExamine, TypeDefinition return false; } - void ProcessDefaultImplementation (TypeDefinition typeWithDefaultImplementedInterfaceMethod, InterfaceImplementation implementation) + void ProcessDefaultImplementation (TypeDefinition typeWithDefaultImplementedInterfaceMethod, InterfaceImplementation implementation, MethodDefinition implementationMethod) { - if (!Annotations.IsInstantiated (typeWithDefaultImplementedInterfaceMethod)) + if ((!implementationMethod.IsStatic && !Annotations.IsInstantiated (typeWithDefaultImplementedInterfaceMethod)) + || implementationMethod.IsStatic && !Annotations.IsRelevantToVariantCasting (typeWithDefaultImplementedInterfaceMethod)) return; MarkInterfaceImplementation (implementation); @@ -2275,9 +2279,9 @@ void MarkTypeWithDebuggerDisplayAttribute (TypeDefinition type, CustomAttribute // Record a logical dependency on the attribute so that we can blame it for the kept members below. Tracer.AddDirectDependency (attribute, new DependencyInfo (DependencyKind.CustomAttribute, type), marked: false); - MarkTypeWithDebuggerDisplayAttributeValue(type, attribute, (string) attribute.ConstructorArguments[0].Value); + MarkTypeWithDebuggerDisplayAttributeValue (type, attribute, (string) attribute.ConstructorArguments[0].Value); if (attribute.HasProperties) { - foreach (var property in attribute.Properties) { + foreach (var property in attribute.Properties) { if (property.Name is "Name" or "Type") { MarkTypeWithDebuggerDisplayAttributeValue (type, attribute, (string) property.Argument.Value); } @@ -2545,19 +2549,17 @@ bool IsMethodNeededByTypeDueToPreservedScope (MethodDefinition method) /// /// Returns true if the override method is required due to the interface that the base method is declared on. See doc at for explanation of logic. /// - bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformation overrideInformation) + bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformation overrideInformation, TypeDefinition typeThatImplsInterface) { var @base = overrideInformation.Base; var method = overrideInformation.Override; + Debug.Assert (@base.DeclaringType.IsInterface); if (@base is null || method is null || @base.DeclaringType is null) return false; if (Annotations.IsMarked (method)) return false; - if (!@base.DeclaringType.IsInterface) - 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.MatchingInterfaceImplementation; @@ -2578,12 +2580,12 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat // If the method is static and the implementing type is relevant to variant casting, mark the implementation method. // A static method may only be called through a constrained call if the type is relevant to variant casting. if (@base.IsStatic) - return Annotations.IsRelevantToVariantCasting (method.DeclaringType) + return Annotations.IsRelevantToVariantCasting (typeThatImplsInterface) || 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 (method.DeclaringType); + return Annotations.IsInstantiated (typeThatImplsInterface); } static bool IsSpecialSerializationConstructor (MethodDefinition method) @@ -3231,7 +3233,7 @@ protected virtual void ProcessMethod (MethodDefinition method, in DependencyInfo } else if (method.TryGetProperty (out PropertyDefinition? property)) MarkProperty (property, new DependencyInfo (PropagateDependencyKindToAccessors (reason.Kind, DependencyKind.PropertyOfPropertyMethod), method)); else if (method.TryGetEvent (out EventDefinition? @event)) { - MarkEvent (@event, new DependencyInfo (PropagateDependencyKindToAccessors(reason.Kind, DependencyKind.EventOfEventMethod), method)); + MarkEvent (@event, new DependencyInfo (PropagateDependencyKindToAccessors (reason.Kind, DependencyKind.EventOfEventMethod), method)); } if (method.HasMetadataParameters ()) { @@ -3315,7 +3317,7 @@ protected virtual void DoAdditionalMethodProcessing (MethodDefinition method) { } - static DependencyKind PropagateDependencyKindToAccessors(DependencyKind parentDependencyKind, DependencyKind kind) + static DependencyKind PropagateDependencyKindToAccessors (DependencyKind parentDependencyKind, DependencyKind kind) { switch (parentDependencyKind) { // If the member is marked due to descriptor or similar, propagate the original reason to suppress some warnings correctly @@ -3335,11 +3337,11 @@ void MarkImplicitlyUsedFields (TypeDefinition type) return; // keep fields for types with explicit layout, for enums and for InlineArray types - if (!type.IsAutoLayout || type.IsEnum || TypeIsInlineArrayType(type)) + if (!type.IsAutoLayout || type.IsEnum || TypeIsInlineArrayType (type)) MarkFields (type, includeStatic: type.IsEnum, reason: new DependencyInfo (DependencyKind.MemberOfType, type)); } - static bool TypeIsInlineArrayType(TypeDefinition type) + static bool TypeIsInlineArrayType (TypeDefinition type) { if (!type.IsValueType) return false; @@ -3584,7 +3586,7 @@ protected internal virtual void MarkEvent (EventDefinition evt, in DependencyInf MarkCustomAttributes (evt, new DependencyInfo (DependencyKind.CustomAttribute, evt)); - DependencyKind dependencyKind = PropagateDependencyKindToAccessors(reason.Kind, DependencyKind.EventMethod); + DependencyKind dependencyKind = PropagateDependencyKindToAccessors (reason.Kind, DependencyKind.EventMethod); MarkMethodIfNotNull (evt.AddMethod, new DependencyInfo (dependencyKind, evt), ScopeStack.CurrentScope.Origin); MarkMethodIfNotNull (evt.InvokeMethod, new DependencyInfo (dependencyKind, evt), ScopeStack.CurrentScope.Origin); MarkMethodIfNotNull (evt.RemoveMethod, new DependencyInfo (dependencyKind, evt), ScopeStack.CurrentScope.Origin); diff --git a/src/tools/illink/src/linker/Linker/Annotations.cs b/src/tools/illink/src/linker/Linker/Annotations.cs index ec507ea25c0a5e..8f7747cba3543a 100644 --- a/src/tools/illink/src/linker/Linker/Annotations.cs +++ b/src/tools/illink/src/linker/Linker/Annotations.cs @@ -447,14 +447,22 @@ public bool IsPublic (IMetadataTokenProvider provider) } /// - /// Returns a list of all known methods that override . The list may be incomplete if other overrides exist in assemblies that haven't been processed by TypeMapInfo yet + /// Returns a list of all known methods that override . + /// The list may be incomplete if other overrides exist in assemblies that haven't been processed by TypeMapInfo yet /// public IEnumerable? GetOverrides (MethodDefinition method) { return TypeMapInfo.GetOverrides (method); } - public IEnumerable<(TypeDefinition InstanceType, InterfaceImplementation ProvidingInterface)>? GetDefaultInterfaceImplementations (MethodDefinition method) + /// + /// Returns a list of all default interface methods that implement for a type. + /// ImplementingType is the type that implements the interface, + /// InterfaceImpl is the for the interface is declared on, and + /// DefaultInterfaceMethod is the method that implements . + /// + /// The interface method to find default implementations for + public IEnumerable<(TypeDefinition ImplementingType, InterfaceImplementation InterfaceImpl, MethodDefinition DefaultInterfaceMethod)>? GetDefaultInterfaceImplementations (MethodDefinition method) { return TypeMapInfo.GetDefaultInterfaceImplementations (method); } @@ -462,7 +470,7 @@ public bool IsPublic (IMetadataTokenProvider provider) /// /// Returns all base methods that overrides. /// This includes methods on 's declaring type's base type (but not methods higher up in the type hierarchy), - /// methods on an interface that 's delcaring type implements, + /// methods on an interface that 's declaring type implements, /// and methods an interface implemented by a derived type of 's declaring type if the derived type uses as the implementing method. /// The list may be incomplete if there are derived types in assemblies that havent been processed yet that use to implement an interface. /// diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 61f44d75e5329f..a2f118adf9fb78 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -30,6 +30,7 @@ // using System.Collections.Generic; +using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using Mono.Cecil; @@ -42,7 +43,7 @@ public class TypeMapInfo readonly LinkContext context; protected readonly Dictionary> base_methods = new Dictionary> (); protected readonly Dictionary> override_methods = new Dictionary> (); - protected readonly Dictionary> default_interface_implementations = new Dictionary> (); + protected readonly Dictionary> default_interface_implementations = new Dictionary> (); public TypeMapInfo (LinkContext context) { @@ -84,9 +85,16 @@ public void EnsureProcessed (AssemblyDefinition assembly) return bases; } - public IEnumerable<(TypeDefinition InstanceType, InterfaceImplementation ProvidingInterface)>? GetDefaultInterfaceImplementations (MethodDefinition method) + /// + /// Returns a list of all default interface methods that implement for a type. + /// ImplementingType is the type that implements the interface, + /// InterfaceImpl is the for the interface is declared on, and + /// DefaultInterfaceMethod is the method that implements . + /// + /// The interface method to find default implementations for + public IEnumerable<(TypeDefinition ImplementingType, InterfaceImplementation InterfaceImpl, MethodDefinition DefaultImplementationMethod)>? GetDefaultInterfaceImplementations (MethodDefinition baseMethod) { - default_interface_implementations.TryGetValue (method, out var ret); + default_interface_implementations.TryGetValue (baseMethod, out var ret); return ret; } @@ -110,14 +118,15 @@ public void AddOverride (MethodDefinition @base, MethodDefinition @override, Int methods.Add (new OverrideInformation (@base, @override, context, matchingInterfaceImplementation)); } - public void AddDefaultInterfaceImplementation (MethodDefinition @base, TypeDefinition implementingType, InterfaceImplementation matchingInterfaceImplementation) + public void AddDefaultInterfaceImplementation (MethodDefinition @base, TypeDefinition implementingType, (InterfaceImplementation, MethodDefinition) matchingInterfaceImplementation) { + Debug.Assert(@base.DeclaringType.IsInterface); if (!default_interface_implementations.TryGetValue (@base, out var implementations)) { - implementations = new List<(TypeDefinition, InterfaceImplementation)> (); + implementations = new List<(TypeDefinition, InterfaceImplementation, MethodDefinition)> (); default_interface_implementations.Add (@base, implementations); } - implementations.Add ((implementingType, matchingInterfaceImplementation)); + implementations.Add ((implementingType, matchingInterfaceImplementation.Item1, matchingInterfaceImplementation.Item2)); } protected virtual void MapType (TypeDefinition type) @@ -278,6 +287,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition type, MethodDefin { // Go over all interfaces, trying to find a method that is an explicit MethodImpl of the // interface method in question. + foreach (var interfaceImpl in type.Interfaces) { var potentialImplInterface = context.TryResolve (interfaceImpl.InterfaceType); if (potentialImplInterface == null) @@ -288,7 +298,9 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition type, MethodDefin foreach (var potentialImplMethod in potentialImplInterface.Methods) { if (potentialImplMethod == interfaceMethod && !potentialImplMethod.IsAbstract) { - AddDefaultInterfaceImplementation (interfaceMethod, type, interfaceImpl); + AddDefaultInterfaceImplementation (interfaceMethod, type, (interfaceImpl, potentialImplMethod)); + foundImpl = true; + break; } if (!potentialImplMethod.HasOverrides) @@ -297,7 +309,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition type, MethodDefin // This method is an override of something. Let's see if it's the method we are looking for. foreach (var @override in potentialImplMethod.Overrides) { if (context.TryResolve (@override) == interfaceMethod) { - AddDefaultInterfaceImplementation (interfaceMethod, type, interfaceImpl); + AddDefaultInterfaceImplementation (interfaceMethod, type, (interfaceImpl, @potentialImplMethod)); foundImpl = true; break; } 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 98b66ac5299824..4b3f387a390151 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 @@ -27,12 +27,30 @@ public Task InterfaceWithAttributeOnImplementation () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task MostSpecificDefaultImplementationKeptInstance () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task MostSpecificDefaultImplementationKeptStatic () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task SimpleDefaultInterfaceMethod () { return RunTest (allowMissingWarnings: true); } + [Fact] + public Task StaticDefaultInterfaceMethodOnStruct () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task UnusedDefaultInterfaceImplementation () { diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/LibrariesTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/LibrariesTests.g.cs index e5f8e41b03e867..03c76d0f1ee9de 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/LibrariesTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/LibrariesTests.g.cs @@ -39,6 +39,18 @@ public Task LibraryWithUnresolvedInterfaces () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task RootAllLibraryBehavior () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task RootAllLibraryCopyBehavior () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task RootLibrary () { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MostSpecificDefaultImplementationKeptInstance.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MostSpecificDefaultImplementationKeptInstance.cs new file mode 100644 index 00000000000000..6f5c02b5fad53d --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MostSpecificDefaultImplementationKeptInstance.cs @@ -0,0 +1,77 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods +{ + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + class MostSpecificDefaultImplementationKeptInstance + { + [Kept] + public static void Main () + { + M (new UsedAsIBase()); + } + + [Kept] + static int M (IBase ibase) + { + return ibase.Value; + } + + [Kept] + interface IBase + { + [Kept] + int Value { + [Kept] + get => 0; + } + + int Value2 { + get => 0; + } + } + + [Kept] + [KeptInterface (typeof (IBase))] + interface IMiddle : IBase + { + [Kept] + int IBase.Value { + [Kept] + get => 1; + } + + int Value2 { + get => 0; + } + } + + [Kept] + [KeptInterface (typeof (IBase))] + [KeptInterface (typeof (IMiddle))] + interface IDerived : IMiddle + { + [Kept] + int IBase.Value { + [Kept] + get => 2; + } + + int Value2 { + get => 0; + } + } + + interface INotReferenced + { } + + [Kept] + [KeptInterface (typeof (IDerived))] + [KeptInterface (typeof (IMiddle))] + [KeptInterface (typeof (IBase))] + [KeptMember(".ctor()")] + class UsedAsIBase : IDerived, INotReferenced + { + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MostSpecificDefaultImplementationKeptStatic.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MostSpecificDefaultImplementationKeptStatic.cs new file mode 100644 index 00000000000000..39e4ae308f65a1 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MostSpecificDefaultImplementationKeptStatic.cs @@ -0,0 +1,157 @@ +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods +{ + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + class MostSpecificDefaultImplementationKeptStatic + { + [Kept] + public static void Main () + { +#if SUPPORTS_DEFAULT_INTERFACE_METHODS + M (); + NotUsedInGeneric.Keep (); + GenericType.M (); + GenericType2.Keep (); +#endif + } + +#if SUPPORTS_DEFAULT_INTERFACE_METHODS + + [Kept] + static int M () where T : IBase + { + return T.Value; + } + + [Kept] + interface IBase + { + [Kept] + static virtual int Value { + [Kept] + get => 0; + } + + static virtual int Value2 { + get => 0; + } + } + + [Kept] + [KeptInterface (typeof (IBase))] + interface IMiddle : IBase + { + [Kept] + static int IBase.Value { + [Kept] + get => 1; + } + + static int IBase.Value2 { + get => 1; + } + } + + [Kept] + [KeptInterface (typeof (IBase))] + [KeptInterface (typeof (IMiddle))] + interface IDerived : IMiddle + { + [Kept] + static int IBase.Value { + [Kept] + get => 2; + } + + static int IBase.Value2 { + get => 1; + } + } + + [Kept] + [KeptInterface (typeof (IBase))] + [KeptInterface (typeof (IMiddle))] + interface IDerived2 : IMiddle + { + // https://github.com/dotnet/runtime/issues/97798 + // This shouldn't need to be kept. Implementor UsedInUnconstrainedGeneric is not passed as a constrained generic + [Kept] + static int IBase.Value { + [Kept] + get => 2; + } + } + + interface INotReferenced + { } + + [Kept] + [KeptInterface (typeof (IDerived))] + [KeptInterface (typeof (IMiddle))] + [KeptInterface (typeof (IBase))] + class UsedAsIBase : IDerived, INotReferenced + { + } + + [Kept] + class NotUsedInGeneric : IDerived, INotReferenced + { + [Kept] + public static void Keep () { } + } + + public interface IBaseUnused + { + public static virtual int Value { + get => 0; + } + } + + public interface IMiddleUnused : IBaseUnused + { + static int IBaseUnused.Value { + get => 1; + } + } + + public interface IDerivedUnused : IMiddleUnused + { + static int IBaseUnused.Value { + get => 2; + } + } + + [Kept] + [KeptInterface (typeof (IBase))] + [KeptInterface (typeof (IMiddle))] + [KeptInterface (typeof (IDerived2))] + class UsedInUnconstrainedGeneric : IDerived2, INotReferenced, IDerivedUnused + { + } + + + [Kept] + class GenericType where T : IBase + { + [Kept] + public static int M () => T.Value; + } + + [Kept] + class GenericType2 + { + [Kept] + public static void Keep() { } + } + + [Kept] + [KeptInterface (typeof (IDerived))] + [KeptInterface (typeof (IMiddle))] + [KeptInterface (typeof (IBase))] + class UsedAsIBase2 : IDerived + { + } +#endif + } +}