diff --git a/docs/removal-behavior.md b/docs/removal-behavior.md index 994b1d32ef66..e4e53be10501 100644 --- a/docs/removal-behavior.md +++ b/docs/removal-behavior.md @@ -42,7 +42,7 @@ public class Program ### Method call on a constrained type parameter -On a call to a static abstract interface method that is accessed through a constrained type parameter, the interface method is rooted, as well as every implementation method on every type. +On a call to a static abstract interface method that is accessed through a constrained type parameter, the interface method is kept, as is every implementation method on every kept type. Example: diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs index df91e334731c..35746e25e619 100644 --- a/src/linker/Linker.Steps/MarkStep.cs +++ b/src/linker/Linker.Steps/MarkStep.cs @@ -60,6 +60,7 @@ protected LinkContext Context { protected Queue<(MethodDefinition, DependencyInfo, MessageOrigin)> _methods; protected List<(MethodDefinition, MarkScopeStack.Scope)> _virtual_methods; + protected List<(MethodDefinition, MarkScopeStack.Scope)> _static_interface_methods; protected Queue _assemblyLevelAttributes; readonly List _ivt_attributes; protected Queue<(AttributeProviderPair, DependencyInfo, MarkScopeStack.Scope)> _lateMarkedAttributes; @@ -224,6 +225,7 @@ public MarkStep () { _methods = new Queue<(MethodDefinition, DependencyInfo, MessageOrigin)> (); _virtual_methods = new List<(MethodDefinition, MarkScopeStack.Scope)> (); + _static_interface_methods = new List<(MethodDefinition, MarkScopeStack.Scope)> (); _assemblyLevelAttributes = new Queue (); _ivt_attributes = new List (); _lateMarkedAttributes = new Queue<(AttributeProviderPair, DependencyInfo, MarkScopeStack.Scope)> (); @@ -476,6 +478,7 @@ bool ProcessPrimaryQueue () while (!QueueIsEmpty ()) { ProcessQueue (); ProcessVirtualMethods (); + ProcessStaticInterfaceMethods (); ProcessMarkedTypesWithInterfaces (); ProcessDynamicCastableImplementationInterfaces (); ProcessPendingBodies (); @@ -576,6 +579,30 @@ void ProcessVirtualMethods () } } + /// + /// Handles marking implementations of static interface methods and the interface implementations of types that implement a static interface method. + /// + void ProcessStaticInterfaceMethods () + { + foreach ((MethodDefinition method, MarkScopeStack.Scope scope) in _static_interface_methods) { + using (ScopeStack.PushScope (scope)) { + var overrides = Annotations.GetOverrides (method); + if (overrides != null) { + foreach (OverrideInformation @override in overrides) { + ProcessOverride (@override); + // We need to mark the interface implementation for static interface methods + // Explicit interface method implementations already mark the interface implementation in ProcessMethod + MarkExplicitInterfaceImplementation (@override.Override, @override.Base); + } + } + } + } + } + + /// + /// Does extra handling of marked types that have interfaces when it's necessary to know what types are marked or instantiated. + /// Right now it only marks the "implements interface" annotations and removes override annotations for static interface methods. + /// void ProcessMarkedTypesWithInterfaces () { // We may mark an interface type later on. Which means we need to reprocess any time with one or more interface implementations that have not been marked @@ -693,6 +720,9 @@ void ProcessVirtualMethod (MethodDefinition method) } } + /// + /// Handles marking overriding methods if the type with the overriding method is instantiated or if the base method is a static abstract interface method + /// void ProcessOverride (OverrideInformation overrideInformation) { var method = overrideInformation.Override; @@ -708,12 +738,12 @@ void ProcessOverride (OverrideInformation overrideInformation) var isInstantiated = Annotations.IsInstantiated (method.DeclaringType); - // We don't need to mark overrides until it is possible that the type could be instantiated + // We don't need to mark overrides until it is possible that the type could be instantiated or the method is a static interface method // Note : The base type is interface check should be removed once we have base type sweeping if (IsInterfaceOverrideThatDoesNotNeedMarked (overrideInformation, isInstantiated)) return; - // Interface static veitual methods will be abstract and will also by pass this check to get marked + // Interface static virtual methods will be abstract and will also bypass this check to get marked if (!isInstantiated && !@base.IsAbstract && Context.IsOptimizationEnabled (CodeOptimizations.OverrideRemoval, method)) return; @@ -736,8 +766,7 @@ bool IsInterfaceOverrideThatDoesNotNeedMarked (OverrideInformation overrideInfor if (!overrideInformation.IsOverrideOfInterfaceMember || isInstantiated) return false; - // This is a static interface method and these checks should all be true - if (overrideInformation.Override.IsStatic && overrideInformation.Base.IsStatic && overrideInformation.Base.IsAbstract && !overrideInformation.Override.IsVirtual) + if (overrideInformation.IsStaticInterfaceMethodPair) return false; if (overrideInformation.MatchingInterfaceImplementation != null) @@ -3049,10 +3078,18 @@ protected virtual void ProcessMethod (MethodDefinition method, in DependencyInfo } } + // Mark overridden methods and interface implementations except for static interface methods + // This will not mark implicit interface methods because they do not have a MethodImpl and aren't in the .Overrides if (method.HasOverrides) { - foreach (MethodReference ov in method.Overrides) { - MarkMethod (ov, new DependencyInfo (DependencyKind.MethodImplOverride, method), ScopeStack.CurrentScope.Origin); - MarkExplicitInterfaceImplementation (method, ov); + foreach (MethodReference @base in method.Overrides) { + // Method implementing a static interface method will have an override to it - note nonstatic methods usually don't unless they're explicit. + // Calling the implementation method directly has no impact on the interface, and as such it should not mark the interface or its method. + // Only if the interface method is referenced, then all the methods which implemented must be kept, but not the other way round. + if (Context.Resolve (@base) is MethodDefinition baseDefinition + && new OverrideInformation.OverridePair (baseDefinition, method).IsStaticInterfaceMethodPair ()) + continue; + MarkMethod (@base, new DependencyInfo (DependencyKind.MethodImplOverride, method), ScopeStack.CurrentScope.Origin); + MarkExplicitInterfaceImplementation (method, @base); } } @@ -3060,6 +3097,9 @@ protected virtual void ProcessMethod (MethodDefinition method, in DependencyInfo if (method.IsVirtual) _virtual_methods.Add ((method, ScopeStack.CurrentScope)); + if (method.IsStatic && method.IsAbstract && method.DeclaringType.IsInterface) + _static_interface_methods.Add ((method, ScopeStack.CurrentScope)); + MarkNewCodeDependencies (method); MarkBaseMethods (method); @@ -3142,9 +3182,9 @@ protected virtual IEnumerable GetRequiredMethodsForInstantiate } } - void MarkExplicitInterfaceImplementation (MethodDefinition method, MethodReference ov) + void MarkExplicitInterfaceImplementation (MethodDefinition method, MethodReference overriddenMethod) { - if (Context.Resolve (ov) is not MethodDefinition resolvedOverride) + if (Context.Resolve (overriddenMethod) is not MethodDefinition resolvedOverride) return; if (resolvedOverride.DeclaringType.IsInterface) { @@ -3416,7 +3456,7 @@ void MarkInterfacesNeededByBodyStack (MethodBody body) { // 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 - // even if the type is never instantiated + // even if the type is never instantiated. (ex. `Type1 x = null; IFoo y = (IFoo)x;`) var implementations = new InterfacesOnStackScanner (Context).GetReferencedInterfaces (body); if (implementations == null) return; diff --git a/src/linker/Linker.Steps/SweepStep.cs b/src/linker/Linker.Steps/SweepStep.cs index 246c2bfceafd..6905fa531488 100644 --- a/src/linker/Linker.Steps/SweepStep.cs +++ b/src/linker/Linker.Steps/SweepStep.cs @@ -453,6 +453,8 @@ protected virtual void SweepMethods (Collection methods) SweepCustomAttributes (method.MethodReturnType); + SweepOverrides (method); + if (!method.HasParameters) continue; @@ -467,6 +469,38 @@ protected virtual void SweepMethods (Collection methods) } } + void SweepOverrides (MethodDefinition method) + { + for (int i = 0; i < method.Overrides.Count;) { + // We can't rely on the context resolution cache anymore, since it may remember methods which are already removed + // So call the direct Resolve here and avoid the cache. + // We want to remove a method from the list of Overrides if: + // Resolve() is null + // This can happen for a couple of reasons, but it indicates the method isn't in the final assembly. + // Resolve also may return a removed value if method.Overrides[i] is a MethodDefinition. In this case, Resolve short circuits and returns `this`. + // OR + // ov.DeclaringType is null + // ov.DeclaringType may be null if Resolve short circuited and returned a removed method. In this case, we want to remove the override. + // OR + // ov is in a `link` scope and is unmarked + // ShouldRemove returns true if the method is unmarked, but we also We need to make sure the override is in a link scope. + // Only things in a link scope are marked, so ShouldRemove is only valid for items in a `link` scope. + if (method.Overrides[i].Resolve () is not MethodDefinition ov || ov.DeclaringType is null || (IsLinkScope (ov.DeclaringType.Scope) && ShouldRemove (ov))) + method.Overrides.RemoveAt (i); + else + i++; + } + } + + /// + /// Returns true if the assembly of the is set to link + /// + private bool IsLinkScope (IMetadataScope scope) + { + AssemblyDefinition? assembly = Context.Resolve (scope); + return assembly != null && Annotations.GetAction (assembly) == AssemblyAction.Link; + } + void SweepDebugInfo (Collection methods) { List? sweptScopes = null; diff --git a/src/linker/Linker/Annotations.cs b/src/linker/Linker/Annotations.cs index c2304e3792d6..c4811b662704 100644 --- a/src/linker/Linker/Annotations.cs +++ b/src/linker/Linker/Annotations.cs @@ -436,6 +436,9 @@ public bool IsPublic (IMetadataTokenProvider provider) return public_api.Contains (provider); } + /// + /// Returns an IEnumerable of the methods that override this method. Note this is different than , which returns the MethodImpl's + /// public IEnumerable? GetOverrides (MethodDefinition method) { return TypeMapInfo.GetOverrides (method); diff --git a/src/linker/Linker/OverrideInformation.cs b/src/linker/Linker/OverrideInformation.cs index 00c06f5f005b..10bca183cb3e 100644 --- a/src/linker/Linker/OverrideInformation.cs +++ b/src/linker/Linker/OverrideInformation.cs @@ -10,17 +10,22 @@ namespace Mono.Linker public class OverrideInformation { readonly ITryResolveMetadata resolver; + readonly OverridePair _pair; public OverrideInformation (MethodDefinition @base, MethodDefinition @override, ITryResolveMetadata resolver, InterfaceImplementation? matchingInterfaceImplementation = null) { - Base = @base; - Override = @override; + _pair = new OverridePair (@base, @override); MatchingInterfaceImplementation = matchingInterfaceImplementation; this.resolver = resolver; } - public MethodDefinition Base { get; } - public MethodDefinition Override { get; } + public readonly record struct OverridePair (MethodDefinition Base, MethodDefinition Override) + { + public bool IsStaticInterfaceMethodPair () => Base.DeclaringType.IsInterface && Base.IsStatic && Override.IsStatic; + } + + public MethodDefinition Base { get => _pair.Base; } + public MethodDefinition Override { get => _pair.Override; } public InterfaceImplementation? MatchingInterfaceImplementation { get; } public bool IsOverrideOfInterfaceMember { @@ -43,5 +48,7 @@ public TypeDefinition? InterfaceType { return Base.DeclaringType; } } + + public bool IsStaticInterfaceMethodPair => _pair.IsStaticInterfaceMethodPair (); } } diff --git a/test/ILLink.RoslynAnalyzer.Tests/Inheritance.Interfaces.StaticInterfaceMethodsTests.cs b/test/ILLink.RoslynAnalyzer.Tests/Inheritance.Interfaces.StaticInterfaceMethodsTests.cs new file mode 100644 index 000000000000..f1149a2f9f23 --- /dev/null +++ b/test/ILLink.RoslynAnalyzer.Tests/Inheritance.Interfaces.StaticInterfaceMethodsTests.cs @@ -0,0 +1,25 @@ +// 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.Threading.Tasks; +using Xunit; + +namespace ILLink.RoslynAnalyzer.Tests.Inheritance.Interfaces +{ + public sealed partial class StaticInterfaceMethodsTests : LinkerTestBase + { + protected override string TestSuiteName => "Inheritance.Interfaces.StaticInterfaceMethods"; + + [Fact] + public Task StaticAbstractInterfaceMethods () + { + return RunTest (nameof (StaticAbstractInterfaceMethods)); + } + + [Fact] + public Task StaticAbstractInterfaceMethodsLibrary () + { + return RunTest (nameof (StaticAbstractInterfaceMethodsLibrary)); + } + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs index bb43569b614c..1293c04732cd 100644 --- a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs +++ b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs @@ -71,53 +71,51 @@ internal class ImplementsUnusedStaticInterface // The interface methods themselves are not used, but the implementation of these methods is internal interface IStaticInterfaceMethodUnused { - // Can be removed with Static Interface trimming optimization - [Kept] static abstract void InterfaceUsedMethodNot (); } - // Can be removed with Static Interface Trimming - [Kept] internal interface IStaticInterfaceUnused { - // Can be removed with Static Interface Trimming - [Kept] static abstract void InterfaceAndMethodNoUsed (); } [Kept] - [KeptInterface (typeof (IStaticInterfaceUnused))] - [KeptInterface (typeof (IStaticInterfaceMethodUnused))] internal class InterfaceMethodUsedThroughImplementation : IStaticInterfaceMethodUnused, IStaticInterfaceUnused { [Kept] + [RemovedOverride (typeof (IStaticInterfaceMethodUnused))] public static void InterfaceUsedMethodNot () { } [Kept] + [RemovedOverride (typeof (IStaticInterfaceUnused))] public static void InterfaceAndMethodNoUsed () { } } [Kept] - [KeptInterface (typeof (IStaticInterfaceMethodUnused))] - [KeptInterface (typeof (IStaticInterfaceUnused))] internal class InterfaceMethodUnused : IStaticInterfaceMethodUnused, IStaticInterfaceUnused { - [Kept] public static void InterfaceUsedMethodNot () { } - [Kept] public static void InterfaceAndMethodNoUsed () { } } + [Kept] + // This method keeps InterfaceMethodUnused without making it 'relevant to variant casting' like + // doing a typeof or type argument would do. If the type is relevant to variant casting, + // we will keep all interface implementations for interfaces that are kept + internal static void KeepInterfaceMethodUnused (InterfaceMethodUnused x) { } + [Kept] public static void Test () { InterfaceMethodUsedThroughImplementation.InterfaceUsedMethodNot (); InterfaceMethodUsedThroughImplementation.InterfaceAndMethodNoUsed (); - Type t; - t = typeof (IStaticInterfaceMethodUnused); - t = typeof (InterfaceMethodUnused); + // The interface has to be kept this way, because if both the type and the interface may + // appear on the stack then they would be marked as relevant to variant casting and the + // interface implementation would be kept. + Type t = typeof (IStaticInterfaceMethodUnused); + KeepInterfaceMethodUnused (null); } } diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/StaticAbstractInterfaceMethods.cs b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/StaticAbstractInterfaceMethods.cs new file mode 100644 index 000000000000..463adfc510e2 --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/StaticAbstractInterfaceMethods.cs @@ -0,0 +1,794 @@ +// 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.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.StaticInterfaceMethods +{ + public class StaticAbstractInterfaceMethods + { + public static void Main () + { + InterfaceMethodsUsedThroughConstrainedType.Test (); + InterfaceWithMethodsUsedEachWay.Test (); + InterfaceMethodUsedOnConcreteType.Test (); + InterfaceMethodsKeptThroughReflection.Test (); + InterfaceHasStaticAndInstanceMethods.Test (); + StaticInterfaceInheritance.Test (); + GenericStaticInterface.Test (); + RecursiveGenericInterface.Test (); + UnusedInterfaces.Test (); + ClassInheritance.Test (); + ProcessOverrideAfterMarkedBase.Test (); + } + + [Kept] + public class InterfaceMethodsUsedThroughConstrainedType + { + [Kept] + public interface IUsedThroughConstrainedType + { + [Kept] + public static abstract int UsedThroughConstrainedType (); + } + + [Kept] + [KeptInterface (typeof (IUsedThroughConstrainedType))] + public class UsesIUsedThroughConstrainedTypeMethods : IUsedThroughConstrainedType + { + [Kept] + [KeptOverride (typeof (IUsedThroughConstrainedType))] + public static int UsedThroughConstrainedType () => 0; + } + + [Kept] + [KeptInterface (typeof (IUsedThroughConstrainedType))] + public class UnusedIUsedThroughConstrainedTypeMethods : IUsedThroughConstrainedType + { + [Kept] + [KeptOverride (typeof (IUsedThroughConstrainedType))] + public static int UsedThroughConstrainedType () => 0; + } + + [Kept] + public static void CallMethodOnConstrainedType () where T : IUsedThroughConstrainedType + { + T.UsedThroughConstrainedType (); + } + + [Kept] + public static void Test () + { + CallMethodOnConstrainedType (); + + Type t = typeof (UnusedIUsedThroughConstrainedTypeMethods); + + ExplicitImplementation.Test (); + } + + [Kept] + public class ExplicitImplementation + { + [Kept] + [KeptInterface (typeof (IUsedThroughConstrainedTypeExplicitImplementation))] + public class UsedIUsedThroughConstrainedTypeExplicitMethods : IUsedThroughConstrainedTypeExplicitImplementation + { + [Kept] + [KeptOverride (typeof (IUsedThroughConstrainedTypeExplicitImplementation))] + static int IUsedThroughConstrainedTypeExplicitImplementation.UsedThroughConstrainedType () => 0; + } + + [Kept] + [KeptInterface (typeof (IUsedThroughConstrainedTypeExplicitImplementation))] + public class UnusedIUsedThroughConstrainedTypeExplicitMethods : IUsedThroughConstrainedTypeExplicitImplementation + { + [Kept] + [KeptOverride (typeof (IUsedThroughConstrainedTypeExplicitImplementation))] + static int IUsedThroughConstrainedTypeExplicitImplementation.UsedThroughConstrainedType () => 0; + } + + [Kept] + public interface IUsedThroughConstrainedTypeExplicitImplementation + { + [Kept] + public static abstract int UsedThroughConstrainedType (); + } + + [Kept] + public static void CallTypeConstrainedMethod () where T : IUsedThroughConstrainedTypeExplicitImplementation + { + T.UsedThroughConstrainedType (); + } + + [Kept] + public static void Test () + { + CallTypeConstrainedMethod (); + + Type t = typeof (UnusedIUsedThroughConstrainedTypeExplicitMethods); + } + } + } + + [Kept] + public class InterfaceMethodUsedOnConcreteType + { + [Kept] + public class UsesIUsedOnConcreteTypeMethods : IUsedOnConcreteType + { + [Kept] + [RemovedOverride (typeof (IUsedOnConcreteType))] + public static int UsedOnConcreteType () => 0; + } + + [Kept] + public class UnusedIUsedOnConcreteTypeMethods : IUsedOnConcreteType + { + public static int UsedOnConcreteType () => 0; + } + + public interface IUsedOnConcreteType + { + public static abstract int UsedOnConcreteType (); + } + + [Kept] + public static void Test () + { + UsesIUsedOnConcreteTypeMethods.UsedOnConcreteType (); + + Type t = typeof (UnusedIUsedOnConcreteTypeMethods); + } + } + + [Kept] + public class InterfaceWithMethodsUsedEachWay + { + + [Kept] + public interface IUsedEveryWay + { + [Kept] + public static abstract int UsedThroughConstrainedType (); + + public static abstract int UsedOnConcreteType (); + + [Kept] + public static abstract int UsedThroughConstrainedTypeExplicit (); + } + + [Kept] + [KeptInterface (typeof (IUsedEveryWay))] + public class UsedIUsedEveryWay : IUsedEveryWay + { + + [Kept] + [KeptOverride (typeof (IUsedEveryWay))] + static int IUsedEveryWay.UsedThroughConstrainedTypeExplicit () => 0; + + [Kept] + [RemovedOverride (typeof (IUsedEveryWay))] + public static int UsedOnConcreteType () => 0; + + [Kept] + [KeptOverride (typeof (IUsedEveryWay))] + public static int UsedThroughConstrainedType () => 0; + } + + [Kept] + [KeptInterface (typeof (IUsedEveryWay))] + public class UnusedIUsedEveryWay : IUsedEveryWay + { + [Kept] + [KeptOverride (typeof (IUsedEveryWay))] + static int IUsedEveryWay.UsedThroughConstrainedTypeExplicit () => 0; + + public static int UsedOnConcreteType () => 0; + + [Kept] + [KeptOverride (typeof (IUsedEveryWay))] + public static int UsedThroughConstrainedType () => 0; + } + + [Kept] + public static void CallTypeConstrainedMethods () where T : IUsedEveryWay + { + T.UsedThroughConstrainedType (); + T.UsedThroughConstrainedTypeExplicit (); + } + + [Kept] + public static void Test () + { + UsedIUsedEveryWay.UsedOnConcreteType (); + CallTypeConstrainedMethods (); + + Type t = typeof (UnusedIUsedEveryWay); + } + } + + [Kept] + public class InterfaceMethodsKeptThroughReflection + { + [Kept] + public interface IMethodsKeptThroughReflection + { + [Kept] + public static abstract int UnusedMethod (); + + [Kept] + public static abstract int UsedOnConcreteType (); + + [Kept] + public static abstract int UsedOnConstrainedType (); + } + + [Kept] + [KeptInterface (typeof (IMethodsKeptThroughReflection))] + public class UsedMethodsKeptThroughtReflection : IMethodsKeptThroughReflection + { + [Kept] + [KeptOverride (typeof (IMethodsKeptThroughReflection))] + public static int UnusedMethod () => 0; + + [Kept] + [KeptOverride (typeof (IMethodsKeptThroughReflection))] + public static int UsedOnConstrainedType () => 0; + + [Kept] + [KeptOverride (typeof (IMethodsKeptThroughReflection))] + public static int UsedOnConcreteType () => 0; + } + + [Kept] + [KeptInterface (typeof (IMethodsKeptThroughReflection))] + public class UnusedMethodsKeptThroughtReflection : IMethodsKeptThroughReflection + { + [Kept] + [KeptOverride (typeof (IMethodsKeptThroughReflection))] + public static int UnusedMethod () => 0; + + [Kept] + [KeptOverride (typeof (IMethodsKeptThroughReflection))] + public static int UsedOnConstrainedType () => 0; + + [Kept] + [KeptOverride (typeof (IMethodsKeptThroughReflection))] + public static int UsedOnConcreteType () => 0; + } + + [Kept] + public static void Test () + { + typeof (IMethodsKeptThroughReflection).RequiresPublicMethods (); + UsedMethodsKeptThroughtReflection.UsedOnConcreteType (); + UseMethodThroughTypeConstraint (); + + Type t = typeof (UnusedMethodsKeptThroughtReflection); + + [Kept] + static void UseMethodThroughTypeConstraint () where T : IMethodsKeptThroughReflection + { + T.UsedOnConstrainedType (); + } + } + } + + [Kept] + public class InterfaceHasStaticAndInstanceMethods + { + [Kept] + public interface IStaticAndInstanceMethods + { + public static abstract int StaticMethodCalledOnConcreteType (); + + [Kept] + public static abstract int StaticMethodExplicitImpl (); + + [Kept] + public int InstanceMethod (); + } + + [Kept] + public static void CallExplicitImplMethod () where T : IStaticAndInstanceMethods + { + T.StaticMethodExplicitImpl (); + } + + [Kept] + [KeptMember (".ctor()")] + [KeptInterface (typeof (IStaticAndInstanceMethods))] + public class UsesAllMethods : IStaticAndInstanceMethods + { + [Kept] + [RemovedOverride (typeof (IStaticAndInstanceMethods))] + public static int StaticMethodCalledOnConcreteType () => 0; + + [Kept] + // Non-static implementation methods don't explicitly override the interface method + public int InstanceMethod () => 0; + + [Kept] + [KeptOverride (typeof (IStaticAndInstanceMethods))] + static int IStaticAndInstanceMethods.StaticMethodExplicitImpl () => 0; + + [Kept] + public static void Test () + { + UsesAllMethods.StaticMethodCalledOnConcreteType (); + var x = new UsesAllMethods (); + ((IStaticAndInstanceMethods) x).InstanceMethod (); + CallExplicitImplMethod (); + } + } + + [Kept] + [KeptInterface (typeof (IStaticAndInstanceMethods))] + public class UnusedMethods : IStaticAndInstanceMethods + { + public static int StaticMethodCalledOnConcreteType () => 0; + + [Kept] + [KeptOverride (typeof (IStaticAndInstanceMethods))] + static int IStaticAndInstanceMethods.StaticMethodExplicitImpl () => 0; + + // Bug: If .ctor is removed, we can remove unused instance methods + [Kept] + public int InstanceMethod () => 0; + + [Kept] + public static void Test () { } + } + + [Kept] + public static void Test () + { + UsesAllMethods.Test (); + UnusedMethods.Test (); + } + } + + [Kept] + public class StaticInterfaceInheritance + { + [Kept] + public interface IBase1 + { + public static abstract int UsedOnConcreteType (); + + [Kept] + public static abstract int UsedOnBaseOnlyConstrainedTypeImplicitImpl (); + + [Kept] + public static abstract int UsedOnConstrainedTypeExplicitImpl (); + public static abstract int UnusedImplicitImpl (); + public static abstract int UnusedExplicitImpl (); + } + + [Kept] + [KeptInterface (typeof (IBase1))] + public interface IInheritsFromBase : IBase1 + { + public static new abstract int UsedOnConcreteType (); + public static new abstract int UsedOnBaseOnlyConstrainedTypeImplicitImpl (); + + [Kept] + public static new abstract int UsedOnConstrainedTypeExplicitImpl (); + public static new abstract int UnusedImplicitImpl (); + public static new abstract int UnusedExplicitImpl (); + } + + [Kept] + public interface IBase2 + { + public static abstract int UsedOnConcreteType (); + + [Kept] + public static abstract int UsedOnBaseOnlyConstrainedTypeImplicitImpl (); + + [Kept] + public static abstract int UsedOnConstrainedTypeExplicitImpl (); + public static abstract int UnusedImplicitImpl (); + public static abstract int UnusedExplicitImpl (); + } + + [Kept] + [KeptInterface (typeof (IBase1))] + [KeptInterface (typeof (IBase2))] + public interface IInheritsFromMultipleBases : IBase1, IBase2, IUnusedInterface + { + public static new abstract int UsedOnConcreteType (); + public static new abstract int UsedOnBaseOnlyConstrainedTypeImplicitImpl (); + + [Kept] + public static new abstract int UsedOnConstrainedTypeExplicitImpl (); + public static new abstract int UnusedImplicitImpl (); + public static new abstract int UnusedExplicitImpl (); + } + + public interface IUnusedInterface + { + public static abstract int UsedOnConcreteType (); + + public static abstract int UnusedImplicitImpl (); + + public static abstract int UnusedExplicitImpl (); + } + + [Kept] + [KeptInterface (typeof (IBase1))] + [KeptInterface (typeof (IInheritsFromBase))] + public class ImplementsIInheritsFromBase : IInheritsFromBase + { + [Kept] + [RemovedOverride (typeof (IInheritsFromBase))] + [RemovedOverride (typeof (IBase1))] + public static int UsedOnConcreteType () => 0; + + [Kept] + [KeptOverride (typeof (IBase1))] + [RemovedOverride (typeof (IInheritsFromBase))] + public static int UsedOnBaseOnlyConstrainedTypeImplicitImpl () => 0; + + [Kept] + [KeptOverride (typeof (IInheritsFromBase))] + static int IInheritsFromBase.UsedOnConstrainedTypeExplicitImpl () => 0; + + [Kept] + [KeptOverride (typeof (IBase1))] + static int IBase1.UsedOnConstrainedTypeExplicitImpl () => 0; + + public static int UnusedImplicitImpl () => 0; + + static int IBase1.UnusedExplicitImpl () => 0; + + static int IInheritsFromBase.UnusedExplicitImpl () => 0; + + [Kept] + public static void Test () + { + ImplementsIInheritsFromBase.UsedOnConcreteType (); + CallBase1TypeConstrainedMethod (); + CallSingleInheritTypeConstrainedMethod (); + } + } + + [KeptInterface (typeof (IInheritsFromMultipleBases))] + [KeptInterface (typeof (IBase1))] + [KeptInterface (typeof (IBase2))] + // [RemovedInterface (typeof (IUnusedInterface))] + public class ImplementsIInheritsFromTwoBases : IInheritsFromMultipleBases + { + [Kept] + [RemovedOverride (typeof (IInheritsFromMultipleBases))] + [RemovedOverride (typeof (IBase1))] + [RemovedOverride (typeof (IBase2))] + [RemovedOverride (typeof (IUnusedInterface))] + public static int UsedOnConcreteType () => 0; + + [Kept] + [KeptOverride (typeof (IBase1))] + [KeptOverride (typeof (IBase2))] + [RemovedOverride (typeof (IInheritsFromMultipleBases))] + public static int UsedOnBaseOnlyConstrainedTypeImplicitImpl () => 0; + + [Kept] + [KeptOverride (typeof (IBase1))] + static int IBase1.UsedOnConstrainedTypeExplicitImpl () => 0; + + [Kept] + [KeptOverride (typeof (IBase2))] + static int IBase2.UsedOnConstrainedTypeExplicitImpl () => 0; + + [Kept] + [KeptOverride (typeof (IInheritsFromMultipleBases))] + static int IInheritsFromMultipleBases.UsedOnConstrainedTypeExplicitImpl () => 0; + + public static int UnusedImplicitImpl () => 0; + + static int IBase1.UnusedExplicitImpl () => 0; + + static int IBase2.UnusedExplicitImpl () => 0; + + static int IInheritsFromMultipleBases.UnusedExplicitImpl () => 0; + + static int IUnusedInterface.UnusedExplicitImpl () => 0; + + [Kept] + public static void Test () + { + ImplementsIInheritsFromTwoBases.UsedOnConcreteType (); + CallBase1TypeConstrainedMethod (); + CallBase2TypeConstrainedMethod (); + CallDoubleInheritTypeConstrainedMethod (); + } + } + + [Kept] + public static void CallBase1TypeConstrainedMethod () where T : IBase1 + { + T.UsedOnBaseOnlyConstrainedTypeImplicitImpl (); + T.UsedOnConstrainedTypeExplicitImpl (); + } + + [Kept] + public static void CallBase2TypeConstrainedMethod () where T : IBase2 + { + T.UsedOnBaseOnlyConstrainedTypeImplicitImpl (); + T.UsedOnConstrainedTypeExplicitImpl (); + } + + [Kept] + public static void CallSingleInheritTypeConstrainedMethod () where T : IInheritsFromBase + { + T.UsedOnConstrainedTypeExplicitImpl (); + } + + [Kept] + public static void CallDoubleInheritTypeConstrainedMethod () where T : IInheritsFromMultipleBases + { + T.UsedOnConstrainedTypeExplicitImpl (); + } + + [Kept] + public static void Test () + { + ImplementsIInheritsFromBase.Test (); + ImplementsIInheritsFromTwoBases.Test (); + } + } + + [Kept] + public class GenericStaticInterface + { + [Kept] + public interface IGenericInterface + { + public static abstract T GetT (); + [Kept] + public static abstract T GetTExplicit (); + } + + [Kept] + [KeptInterface (typeof (IGenericInterface))] + public class ImplementsGenericInterface : IGenericInterface + { + [Kept] + [RemovedOverride (typeof (IGenericInterface))] + public static int GetT () => 0; + + [Kept] + [KeptOverride (typeof (IGenericInterface))] + static int IGenericInterface.GetTExplicit () => 0; + } + + [Kept] + [KeptInterface (typeof (IGenericInterface))] + public class ImplementsGenericInterfaceUnused : IGenericInterface + { + public static int GetT () => 0; + [Kept] + [KeptOverride (typeof (IGenericInterface))] + static int IGenericInterface.GetTExplicit () => 0; + } + + [Kept] + public static void Test () + { + ImplementsGenericInterface.GetT (); + CallExplicitMethod (); + Type t = typeof (ImplementsGenericInterfaceUnused); + + } + + [Kept] + public static void CallExplicitMethod () where T : IGenericInterface + { + T.GetTExplicit (); + } + } + + [Kept] + public class RecursiveGenericInterface + { + [Kept] + public interface IGenericInterface where T : IGenericInterface + { + public static abstract T GetT (); + [Kept] + public static abstract T GetTExplicit (); + } + + [Kept] + [KeptInterface (typeof (IGenericInterface))] + public class ImplementsIGenericInterfaceOfSelf : IGenericInterface + { + [Kept] + [RemovedOverride (typeof (IGenericInterface))] + public static ImplementsIGenericInterfaceOfSelf GetT () => throw new NotImplementedException (); + + [Kept] + [KeptOverride (typeof (IGenericInterface))] + static ImplementsIGenericInterfaceOfSelf IGenericInterface.GetTExplicit () + => throw new NotImplementedException (); + } + + [Kept] + [KeptInterface (typeof (IGenericInterface))] + public class ImplementsIGenericInterfaceOfOther : IGenericInterface + { + [Kept] + [RemovedOverride (typeof (IGenericInterface))] + public static ImplementsIGenericInterfaceOfSelf GetT () => throw new NotImplementedException (); + + [Kept] + [KeptOverride (typeof (IGenericInterface))] + static ImplementsIGenericInterfaceOfSelf IGenericInterface.GetTExplicit () + => throw new NotImplementedException (); + } + + [Kept] + [KeptInterface (typeof (IGenericInterface))] + public class ImplementsIGenericInterfaceOfSelfUnused : IGenericInterface + { + public static ImplementsIGenericInterfaceOfSelfUnused GetT () => throw new NotImplementedException (); + + [Kept] + [KeptOverride (typeof (IGenericInterface))] + static ImplementsIGenericInterfaceOfSelfUnused IGenericInterface.GetTExplicit () + => throw new NotImplementedException (); + } + + [Kept] + public static void Test () + { + ImplementsIGenericInterfaceOfSelf.GetT (); + ImplementsIGenericInterfaceOfOther.GetT (); + CallExplicitGetT (); + CallExplicitGetT (); + + Type t = typeof (ImplementsIGenericInterfaceOfSelfUnused); + } + + [Kept] + public static void CallExplicitGetT () where T : IGenericInterface + { + T.GetTExplicit (); + } + } + + [Kept] + public class UnusedInterfaces + { + public interface IUnusedInterface + { + public int UnusedMethodImplicit (); + public int UnusedMethodExplicit (); + } + + [Kept] + public interface IUnusedMethods + { + public int UnusedMethodImplicit (); + public int UnusedMethodExplicit (); + } + + [Kept] + public class ImplementsUnusedInterface : IUnusedInterface + { + int IUnusedInterface.UnusedMethodExplicit () => 0; + + public int UnusedMethodImplicit () => 0; + } + + [Kept] + // In link mode, if we remove all methods from the interface, we should be able to remove the interface. We need it now since we don't remove the type constraint from UsesIUnusedMethods + [KeptInterface (typeof (IUnusedMethods))] + public class ImplementsIUnusedMethods : IUnusedMethods + { + int IUnusedMethods.UnusedMethodExplicit () => 0; + + public int UnusedMethodImplicit () => 0; + } + + [Kept] + // In link mode, if there are no constrained calls we should be able to remove the type constraint + public static void UsesIUnusedMethods () where T : IUnusedMethods { } + + [Kept] + public static void Test () + { + UsesIUnusedMethods (); + Type t = typeof (ImplementsUnusedInterface); + } + } + + [Kept] + public class ClassInheritance + { + [Kept] + public interface IBase + { + [Kept] + static abstract int ExplicitlyImplemented (); + static abstract int ImplicitlyImplementedUsedOnType (); + static abstract int ImplicitlyImplementedUsedOnInterface (); + int GetInt (); + } + + [Kept] + [KeptInterface (typeof (IBase))] + public abstract class BaseKeptOnType : IBase + { + [Kept] + [KeptOverride (typeof (IBase))] + static int IBase.ExplicitlyImplemented () => 0; + + // Don't use at all + public static int ImplicitlyImplementedUsedOnType () => 0; + + public static int ImplicitlyImplementedUsedOnInterface () => 0; + public int GetInt () => 0; + } + + [Kept] + [KeptInterface (typeof (IBase))] + [KeptBaseType (typeof (BaseKeptOnType))] + public class InheritsFromBase : BaseKeptOnType, IBase + { + // Use on this type only + // This doesn't override IBase.ImplicitlyImplementedUsedOnType + [Kept] + public static int ImplictlyImplementedUsedOnType () => 0; + } + + [Kept] + public static void CallIBaseMethod () where T : IBase + { + T.ExplicitlyImplemented (); + } + + [Kept] + public static void Test () + { + InheritsFromBase.ImplictlyImplementedUsedOnType (); + CallIBaseMethod (); + } + } + + [Kept] + public static class ProcessOverrideAfterMarkedBase + { + [Kept] + interface IFoo + { + [Kept] + static abstract int Method (); + } + + [Kept] + [KeptInterface (typeof (IFoo))] + class Foo : IFoo + { + [Kept] + [KeptOverride (typeof (IFoo))] + public static int Method () => 0; + } + + [Kept] + public static void Test () + { + typeof (Foo).RequiresPublicMethods (); + typeof (IFoo).RequiresPublicMethods (); + } + } + } +} diff --git a/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/StaticAbstractInterfaceMethodsLibrary.cs b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/StaticAbstractInterfaceMethodsLibrary.cs new file mode 100644 index 000000000000..1e2c7fcf1daa --- /dev/null +++ b/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/StaticAbstractInterfaceMethodsLibrary.cs @@ -0,0 +1,895 @@ +// 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.Helpers; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.StaticInterfaceMethods +{ + [SetupLinkerArgument ("-a", "test.exe", "library")] + public static class StaticAbstractInterfaceMethodsLibrary + { + public static void Main () + { + InterfaceMethodsUsedThroughConstrainedType.Test (); + InterfaceWithMethodsUsedEachWay.Test (); + InterfaceMethodUsedOnConcreteType.Test (); + InterfaceMethodsKeptThroughReflection.Test (); + StaticInterfaceInheritance.Test (); + GenericStaticInterface.Test (); + RecursiveGenericInterface.Test (); + UnusedInterfaces.Test (); + } + + [Kept] + public static class InterfaceMethodsUsedThroughConstrainedType + { + [Kept] + public interface IUsedThroughConstrainedType + { + [Kept] + static abstract int UsedThroughConstrainedType (); + } + + [Kept] + internal interface IUsedThroughConstrainedTypeInternal + { + static abstract int UsedThroughConstrainedType (); + } + + [Kept] + [KeptMember (".ctor()")] + [KeptInterface (typeof (IUsedThroughConstrainedType))] + [KeptInterface (typeof (IUsedThroughConstrainedTypeInternal))] + public class UsesIUsedThroughConstrainedTypeMethods : IUsedThroughConstrainedType, IUsedThroughConstrainedTypeInternal + { + [Kept] + [KeptOverride (typeof (IUsedThroughConstrainedType))] + [RemovedOverride (typeof (IUsedThroughConstrainedTypeInternal))] + public static int UsedThroughConstrainedType () => 0; + } + + [Kept] + [KeptMember (".ctor()")] + [KeptInterface (typeof (IUsedThroughConstrainedType))] + [KeptInterface (typeof (IUsedThroughConstrainedTypeInternal))] + public class UnusedIUsedThroughConstrainedTypeMethods : IUsedThroughConstrainedType, IUsedThroughConstrainedTypeInternal + { + [Kept] + [KeptOverride (typeof (IUsedThroughConstrainedType))] + [RemovedOverride (typeof (IUsedThroughConstrainedTypeInternal))] + public static int UsedThroughConstrainedType () => 0; + } + + private class UnusedIUsedThroughConstrainedTypeMethodsPrivate : IUsedThroughConstrainedType, IUsedThroughConstrainedTypeInternal + { + public static int UsedThroughConstrainedType () => 0; + } + + [Kept] + public static void CallMethodOnConstrainedType () where T : IUsedThroughConstrainedType + { + T.UsedThroughConstrainedType (); + } + + [Kept] + public static void Test () + { + CallMethodOnConstrainedType (); + Type t = typeof (UnusedIUsedThroughConstrainedTypeMethods); + + ExplicitImplementation.Test (); + } + + [Kept] + public static class ExplicitImplementation + { + [Kept] + [KeptMember (".ctor()")] + [KeptInterface (typeof (IUsedThroughConstrainedTypeExplicitImplementation))] + public class UsedIUsedThroughConstrainedTypeExplicitMethods : IUsedThroughConstrainedTypeExplicitImplementation + { + [Kept] + [KeptOverride (typeof (IUsedThroughConstrainedTypeExplicitImplementation))] + static int IUsedThroughConstrainedTypeExplicitImplementation.UsedThroughConstrainedType () => 0; + } + + [Kept] + [KeptMember (".ctor()")] + [KeptInterface (typeof (IUsedThroughConstrainedTypeExplicitImplementation))] + public class UnusedIUsedThroughConstrainedTypeExplicitMethods + : IUsedThroughConstrainedTypeExplicitImplementation + { + [Kept] + [KeptOverride (typeof (IUsedThroughConstrainedTypeExplicitImplementation))] + static int IUsedThroughConstrainedTypeExplicitImplementation.UsedThroughConstrainedType () => 0; + } + + [Kept] + public interface IUsedThroughConstrainedTypeExplicitImplementation + { + [Kept] + static abstract int UsedThroughConstrainedType (); + } + + [Kept] + public static void CallTypeConstrainedMethod () where T : IUsedThroughConstrainedTypeExplicitImplementation + { + T.UsedThroughConstrainedType (); + } + + [Kept] + public static void Test () + { + CallTypeConstrainedMethod (); + + Type t = typeof (UnusedIUsedThroughConstrainedTypeExplicitMethods); + } + } + } + + [Kept] + public static class InterfaceMethodUsedOnConcreteType + { + [Kept] + [KeptMember (".ctor()")] + [KeptInterface (typeof (IUsedOnConcreteType))] + [KeptInterface (typeof (IUsedOnConcreteTypeInternal))] + public class UsesIUsedOnConcreteTypeMethods : IUsedOnConcreteType, IUsedOnConcreteTypeInternal + { + [Kept] + [KeptOverride (typeof (IUsedOnConcreteType))] + [RemovedOverride (typeof (IUsedOnConcreteTypeInternal))] + public static int UsedOnConcreteType () => 0; + } + + [Kept] + public interface IUsedOnConcreteType + { + [Kept] + public static abstract int UsedOnConcreteType (); + } + + [Kept] + internal interface IUsedOnConcreteTypeInternal + { + static abstract int UsedOnConcreteType (); + } + + [Kept] + public static void Test () + { + UsesIUsedOnConcreteTypeMethods.UsedOnConcreteType (); + } + } + + [Kept] + public static class InterfaceWithMethodsUsedEachWay + { + + [Kept] + public interface IUsedEveryWay + { + [Kept] + public static abstract int UsedThroughConstrainedType (); + + [Kept] + public static abstract int UsedOnConcreteType (); + + [Kept] + public static abstract int UsedThroughConstrainedTypeExplicit (); + } + + [Kept] + internal interface IUsedEveryWayInternal + { + [Kept] + internal static abstract int UsedThroughConstrainedType (); + + internal static abstract int UsedOnConcreteType (); + + [Kept] + internal static abstract int UsedThroughConstrainedTypeExplicit (); + } + + [Kept] + internal interface IUnusedEveryWayInternal + { + internal static abstract int UsedThroughConstrainedType (); + + internal static abstract int UsedOnConcreteType (); + + internal static abstract int UsedThroughConstrainedTypeExplicit (); + } + + [Kept] + [KeptMember (".ctor()")] + [KeptInterface (typeof (IUsedEveryWay))] + [KeptInterface (typeof (IUsedEveryWayInternal))] + [KeptInterface (typeof (IUnusedEveryWayInternal))] + public class UsedIUsedEveryWay : IUsedEveryWay, IUsedEveryWayInternal, IUnusedEveryWayInternal + { + + [Kept] + [KeptOverride (typeof (IUsedEveryWay))] + static int IUsedEveryWay.UsedThroughConstrainedTypeExplicit () => 0; + + [Kept] + [KeptOverride (typeof (IUsedEveryWayInternal))] + static int IUsedEveryWayInternal.UsedThroughConstrainedTypeExplicit () => 0; + + static int IUnusedEveryWayInternal.UsedThroughConstrainedTypeExplicit () => 0; + + [Kept] + [KeptOverride (typeof (IUsedEveryWay))] + [RemovedOverride (typeof (IUsedEveryWayInternal))] + [RemovedOverride (typeof (IUnusedEveryWayInternal))] + public static int UsedOnConcreteType () => 0; + + [Kept] + [KeptOverride (typeof (IUsedEveryWay))] + [KeptOverride (typeof (IUsedEveryWayInternal))] + [RemovedOverride (typeof (IUnusedEveryWayInternal))] + public static int UsedThroughConstrainedType () => 0; + } + + [Kept] + [KeptInterface (typeof (IUsedEveryWay))] + [KeptInterface (typeof (IUsedEveryWayInternal))] + [KeptInterface (typeof (IUnusedEveryWayInternal))] + internal class UnusedIUsedEveryWayInternal : IUsedEveryWay, IUsedEveryWayInternal, IUnusedEveryWayInternal + { + [Kept] + [KeptOverride (typeof (IUsedEveryWay))] + static int IUsedEveryWay.UsedThroughConstrainedTypeExplicit () => 0; + + [Kept] + [KeptOverride (typeof (IUsedEveryWayInternal))] + static int IUsedEveryWayInternal.UsedThroughConstrainedTypeExplicit () => 0; + + static int IUnusedEveryWayInternal.UsedThroughConstrainedTypeExplicit () => 0; + + [Kept] + public static int UsedOnConcreteType () => 0; + + [Kept] + [KeptOverride (typeof (IUsedEveryWay))] + [KeptOverride (typeof (IUsedEveryWayInternal))] + public static int UsedThroughConstrainedType () => 0; + } + + [Kept] + public static void CallTypeConstrainedMethods () where T : IUsedEveryWay + { + T.UsedThroughConstrainedType (); + T.UsedThroughConstrainedTypeExplicit (); + } + + [Kept] + internal static void CallTypeConstrainedMethodsInternal () where T : IUsedEveryWayInternal + { + T.UsedThroughConstrainedType (); + T.UsedThroughConstrainedTypeExplicit (); + } + + [Kept] + public static void Test () + { + UsedIUsedEveryWay.UsedOnConcreteType (); + CallTypeConstrainedMethods (); + CallTypeConstrainedMethodsInternal (); + + Type t = typeof (UnusedIUsedEveryWayInternal); + } + } + + [Kept] + public static class InterfaceMethodsKeptThroughReflection + { + [Kept] + public interface IMethodsKeptThroughReflection + { + [Kept] + public static abstract int UnusedMethod (); + + [Kept] + public static abstract int UsedOnConcreteType (); + + [Kept] + public static abstract int UsedOnConstrainedType (); + } + + [Kept] + [KeptMember (".ctor()")] + [KeptInterface (typeof (IMethodsKeptThroughReflection))] + public class UsedMethodsKeptThroughtReflection : IMethodsKeptThroughReflection + { + [Kept] + [KeptOverride (typeof (IMethodsKeptThroughReflection))] + public static int UnusedMethod () => 0; + + [Kept] + [KeptOverride (typeof (IMethodsKeptThroughReflection))] + public static int UsedOnConstrainedType () => 0; + + [Kept] + [KeptOverride (typeof (IMethodsKeptThroughReflection))] + public static int UsedOnConcreteType () => 0; + } + + [Kept] + [KeptInterface (typeof (IMethodsKeptThroughReflection))] + internal class UnusedMethodsKeptThroughtReflection : IMethodsKeptThroughReflection + { + [Kept] + [KeptOverride (typeof (IMethodsKeptThroughReflection))] + public static int UnusedMethod () => 0; + + [Kept] + [KeptOverride (typeof (IMethodsKeptThroughReflection))] + public static int UsedOnConstrainedType () => 0; + + [Kept] + [KeptOverride (typeof (IMethodsKeptThroughReflection))] + public static int UsedOnConcreteType () => 0; + } + + [Kept] + public static void Test () + { + typeof (IMethodsKeptThroughReflection).RequiresPublicMethods (); + UsedMethodsKeptThroughtReflection.UsedOnConcreteType (); + UseMethodThroughTypeConstraint (); + + Type t = typeof (UnusedMethodsKeptThroughtReflection); + + [Kept] + static void UseMethodThroughTypeConstraint () where T : IMethodsKeptThroughReflection + { + T.UsedOnConstrainedType (); + } + } + } + + [Kept] + public static class InterfaceHasStaticAndInstanceMethods + { + [Kept] + public interface IStaticAndInstanceMethods + { + [Kept] + public static abstract int StaticMethodCalledOnConcreteType (); + + [Kept] + public static abstract int StaticMethodExplicitImpl (); + + [Kept] + public int InstanceMethod (); + } + + [Kept] + internal interface IStaticAndInstanceMethodsInternalUnused + { + static abstract int StaticMethodCalledOnConcreteType (); + + static abstract int StaticMethodExplicitImpl (); + + int InstanceMethod (); + } + + [Kept] + internal interface IStaticAndInstanceMethodsInternalUsed + { + static abstract int StaticMethodCalledOnConcreteType (); + + [Kept] + static abstract int StaticMethodExplicitImpl (); + + [Kept] + int InstanceMethod (); + } + + [Kept] + internal static void CallExplicitImplMethod () where T : IStaticAndInstanceMethods, new() + { + T.StaticMethodExplicitImpl (); + IStaticAndInstanceMethods x = new T (); + x.InstanceMethod (); + } + + [Kept] + internal static void CallExplicitImplMethodInternalUsed () where T : IStaticAndInstanceMethodsInternalUsed, new() + { + T.StaticMethodExplicitImpl (); + IStaticAndInstanceMethodsInternalUsed x = new T (); + x.InstanceMethod (); + } + + [Kept] + [KeptMember (".ctor()")] + [KeptInterface (typeof (IStaticAndInstanceMethods))] + [KeptInterface (typeof (IStaticAndInstanceMethodsInternalUsed))] + [KeptInterface (typeof (IStaticAndInstanceMethodsInternalUnused))] + public class UsesAllMethods : IStaticAndInstanceMethods, IStaticAndInstanceMethodsInternalUnused, IStaticAndInstanceMethodsInternalUsed + { + [Kept] + [KeptOverride (typeof (IStaticAndInstanceMethods))] + [RemovedOverride (typeof (IStaticAndInstanceMethodsInternalUsed))] + [RemovedOverride (typeof (IStaticAndInstanceMethodsInternalUnused))] + public static int StaticMethodCalledOnConcreteType () => 0; + + [Kept] + // No .override / MethodImpl for implicit instance methods + public int InstanceMethod () => 0; + + [Kept] + [KeptOverride (typeof (IStaticAndInstanceMethods))] + static int IStaticAndInstanceMethods.StaticMethodExplicitImpl () => 0; + + static int IStaticAndInstanceMethodsInternalUnused.StaticMethodExplicitImpl () => 0; + + [Kept] + [KeptOverride (typeof (IStaticAndInstanceMethodsInternalUsed))] + static int IStaticAndInstanceMethodsInternalUsed.StaticMethodExplicitImpl () => 0; + + [Kept] + public static void Test () + { + UsesAllMethods.StaticMethodCalledOnConcreteType (); + CallExplicitImplMethod (); + CallExplicitImplMethodInternalUsed (); + } + } + + [Kept] + [KeptMember (".ctor()")] + [KeptInterface (typeof (IStaticAndInstanceMethods))] + [KeptInterface (typeof (IStaticAndInstanceMethodsInternalUsed))] + [KeptInterface (typeof (IStaticAndInstanceMethodsInternalUnused))] + public class UnusedMethods : IStaticAndInstanceMethods, IStaticAndInstanceMethodsInternalUnused, IStaticAndInstanceMethodsInternalUsed + { + [Kept] + [KeptOverride (typeof (IStaticAndInstanceMethods))] + [RemovedOverride (typeof (IStaticAndInstanceMethodsInternalUsed))] + [RemovedOverride (typeof (IStaticAndInstanceMethodsInternalUnused))] + public static int StaticMethodCalledOnConcreteType () => 0; + + [Kept] + [KeptOverride (typeof (IStaticAndInstanceMethods))] + static int IStaticAndInstanceMethods.StaticMethodExplicitImpl () => 0; + + static int IStaticAndInstanceMethodsInternalUnused.StaticMethodExplicitImpl () => 0; + + [Kept] + [KeptOverride (typeof (IStaticAndInstanceMethodsInternalUsed))] + static int IStaticAndInstanceMethodsInternalUsed.StaticMethodExplicitImpl () => 0; + + [Kept] + [KeptOverride (typeof (IStaticAndInstanceMethods))] + int IStaticAndInstanceMethods.InstanceMethod () => 0; + + [Kept] + [KeptOverride (typeof (IStaticAndInstanceMethodsInternalUsed))] + int IStaticAndInstanceMethodsInternalUsed.InstanceMethod () => 0; + + int IStaticAndInstanceMethodsInternalUnused.InstanceMethod () => 0; + + [Kept] + public static void Test () { } + } + + [Kept] + public static void Test () + { + UsesAllMethods.Test (); + UnusedMethods.Test (); + } + } + + [Kept] + public static class StaticInterfaceInheritance + { + [Kept] + public interface IBase + { + [Kept] + public static abstract int UsedOnConcreteType (); + + [Kept] + public static abstract int UsedOnBaseOnlyConstrainedTypeImplicitImpl (); + + [Kept] + public static abstract int UsedOnConstrainedTypeExplicitImpl (); + + [Kept] + public static abstract int UnusedImplicitImpl (); + + [Kept] + public static abstract int UnusedExplicitImpl (); + } + + [Kept] + [KeptInterface (typeof (IBase))] + public interface IInheritsFromBase : IBase + { + [Kept] + public static abstract int UsedOnConcreteType (); + + [Kept] + public static abstract int UsedOnBaseOnlyConstrainedTypeImplicitImpl (); + + [Kept] + public static abstract int UsedOnConstrainedTypeExplicitImpl (); + + [Kept] + public static abstract int UnusedImplicitImpl (); + + [Kept] + public static abstract int UnusedExplicitImpl (); + } + + [Kept] + internal interface IBaseInternal + { + static abstract int UsedOnConcreteType (); + + [Kept] + static abstract int UsedOnBaseOnlyConstrainedTypeImplicitImpl (); + + [Kept] + static abstract int UsedOnConstrainedTypeExplicitImpl (); + + static abstract int UnusedImplicitImpl (); + + static abstract int UnusedExplicitImpl (); + } + + [Kept] + [KeptInterface (typeof (IBase))] + [KeptInterface (typeof (IBaseInternal))] + [KeptInterface (typeof (IUnusedInterface))] + internal interface IInheritsFromMultipleBases : IBase, IBaseInternal, IUnusedInterface + { + static abstract int UsedOnConcreteType (); + static abstract int UsedOnBaseOnlyConstrainedTypeImplicitImpl (); + + [Kept] + static abstract int UsedOnConstrainedTypeExplicitImpl (); + static abstract int UnusedImplicitImpl (); + static abstract int UnusedExplicitImpl (); + } + + [Kept] + internal interface IUnusedInterface + { + static abstract int UsedOnConcreteType (); + + static abstract int UnusedImplicitImpl (); + + static abstract int UnusedExplicitImpl (); + } + + [Kept] + [KeptMember (".ctor()")] + [KeptInterface (typeof (IBase))] + [KeptInterface (typeof (IInheritsFromBase))] + public class ImplementsIInheritsFromBase : IInheritsFromBase + { + [Kept] + [KeptOverride (typeof (IInheritsFromBase))] + [KeptOverride (typeof (IBase))] + public static int UsedOnConcreteType () => 0; + + [Kept] + [KeptOverride (typeof (IBase))] + [KeptOverride (typeof (IInheritsFromBase))] + public static int UsedOnBaseOnlyConstrainedTypeImplicitImpl () => 0; + + [Kept] + [KeptOverride (typeof (IInheritsFromBase))] + static int IInheritsFromBase.UsedOnConstrainedTypeExplicitImpl () => 0; + + [Kept] + [KeptOverride (typeof (IBase))] + static int IBase.UsedOnConstrainedTypeExplicitImpl () => 0; + + [Kept] + [KeptOverride (typeof (IBase))] + [KeptOverride (typeof (IInheritsFromBase))] + public static int UnusedImplicitImpl () => 0; + + [Kept] + [KeptOverride (typeof (IBase))] + static int IBase.UnusedExplicitImpl () => 0; + + [Kept] + [KeptOverride (typeof (IInheritsFromBase))] + static int IInheritsFromBase.UnusedExplicitImpl () => 0; + + [Kept] + public static void Test () + { + ImplementsIInheritsFromBase.UsedOnConcreteType (); + CallBase1TypeConstrainedMethod (); + CallSingleInheritTypeConstrainedMethod (); + } + } + + [Kept] + [KeptMember (".ctor()")] + [KeptInterface (typeof (IInheritsFromMultipleBases))] + [KeptInterface (typeof (IBase))] + [KeptInterface (typeof (IBaseInternal))] + [KeptInterface (typeof (IUnusedInterface))] + public class ImplementsIInheritsFromTwoBases : IInheritsFromMultipleBases + { + [Kept] + [RemovedOverride (typeof (IInheritsFromMultipleBases))] + [KeptOverride (typeof (IBase))] + [RemovedOverride (typeof (IBaseInternal))] + [RemovedOverride (typeof (IUnusedInterface))] + public static int UsedOnConcreteType () => 0; + + [Kept] + [KeptOverride (typeof (IBase))] + [KeptOverride (typeof (IBaseInternal))] + [RemovedOverride (typeof (IInheritsFromMultipleBases))] + public static int UsedOnBaseOnlyConstrainedTypeImplicitImpl () => 0; + + [Kept] + [KeptOverride (typeof (IBase))] + static int IBase.UsedOnConstrainedTypeExplicitImpl () => 0; + + [Kept] + [KeptOverride (typeof (IBaseInternal))] + static int IBaseInternal.UsedOnConstrainedTypeExplicitImpl () => 0; + + [Kept] + [KeptOverride (typeof (IInheritsFromMultipleBases))] + static int IInheritsFromMultipleBases.UsedOnConstrainedTypeExplicitImpl () => 0; + + [Kept] + [KeptOverride (typeof (IBase))] + public static int UnusedImplicitImpl () => 0; + + [Kept] + [KeptOverride (typeof (IBase))] + static int IBase.UnusedExplicitImpl () => 0; + + static int IBaseInternal.UnusedExplicitImpl () => 0; + + static int IInheritsFromMultipleBases.UnusedExplicitImpl () => 0; + + static int IUnusedInterface.UnusedExplicitImpl () => 0; + + [Kept] + public static void Test () + { + ImplementsIInheritsFromTwoBases.UsedOnConcreteType (); + CallBase1TypeConstrainedMethod (); + CallBase2TypeConstrainedMethod (); + CallDoubleInheritTypeConstrainedMethod (); + } + } + + [Kept] + public static void CallBase1TypeConstrainedMethod () where T : IBase + { + T.UsedOnBaseOnlyConstrainedTypeImplicitImpl (); + T.UsedOnConstrainedTypeExplicitImpl (); + } + + [Kept] + internal static void CallBase2TypeConstrainedMethod () where T : IBaseInternal + { + T.UsedOnBaseOnlyConstrainedTypeImplicitImpl (); + T.UsedOnConstrainedTypeExplicitImpl (); + } + + [Kept] + public static void CallSingleInheritTypeConstrainedMethod () where T : IInheritsFromBase + { + T.UsedOnConstrainedTypeExplicitImpl (); + } + + [Kept] + internal static void CallDoubleInheritTypeConstrainedMethod () where T : IInheritsFromMultipleBases + { + T.UsedOnConstrainedTypeExplicitImpl (); + } + + [Kept] + public static void Test () + { + ImplementsIInheritsFromBase.Test (); + ImplementsIInheritsFromTwoBases.Test (); + } + } + + [Kept] + public static class GenericStaticInterface + { + [Kept] + public interface IGenericInterface + { + [Kept] + public static abstract T GetT (); + [Kept] + public static abstract T GetTExplicit (); + } + + [Kept] + [KeptMember (".ctor()")] + [KeptInterface (typeof (IGenericInterface))] + public class ImplementsGenericInterface : IGenericInterface + { + [Kept] + [KeptOverride (typeof (IGenericInterface))] + public static int GetT () => 0; + + [Kept] + [KeptOverride (typeof (IGenericInterface))] + static int IGenericInterface.GetTExplicit () => 0; + } + + [Kept] + [KeptInterface (typeof (IGenericInterface))] + internal class ImplementsGenericInterfaceUnused : IGenericInterface + { + [Kept] + [KeptOverride (typeof (IGenericInterface))] + public static int GetT () => 0; + + [Kept] + [KeptOverride (typeof (IGenericInterface))] + static int IGenericInterface.GetTExplicit () => 0; + } + + [Kept] + public static void Test () + { + ImplementsGenericInterface.GetT (); + CallExplicitMethod (); + Type t = typeof (ImplementsGenericInterfaceUnused); + } + + [Kept] + public static void CallExplicitMethod () where T : IGenericInterface + { + T.GetTExplicit (); + } + } + + [Kept] + public static class RecursiveGenericInterface + { + [Kept] + public interface IGenericInterface where T : IGenericInterface + { + [Kept] + public static abstract T GetT (); + [Kept] + public static abstract T GetTExplicit (); + } + + [Kept] + internal interface IGenericInterfaceInternal where T : IGenericInterfaceInternal + { + static abstract T GetT (); + + static abstract T GetTExplicit (); + } + + [Kept] + [KeptMember (".ctor()")] + [KeptInterface (typeof (IGenericInterface))] + [KeptInterface (typeof (IGenericInterfaceInternal))] + public class ImplementsIGenericInterfaceOfSelf : IGenericInterface, IGenericInterfaceInternal + { + [Kept] + [KeptOverride (typeof (IGenericInterface))] + [RemovedOverride (typeof (IGenericInterfaceInternal))] + public static ImplementsIGenericInterfaceOfSelf GetT () => throw new NotImplementedException (); + + [Kept] + [KeptOverride (typeof (IGenericInterface))] + static ImplementsIGenericInterfaceOfSelf IGenericInterface.GetTExplicit () + => throw new NotImplementedException (); + + static ImplementsIGenericInterfaceOfSelf IGenericInterfaceInternal.GetTExplicit () + => throw new NotImplementedException (); + } + + [Kept] + [KeptMember (".ctor()")] + [KeptInterface (typeof (IGenericInterface))] + [KeptInterface (typeof (IGenericInterfaceInternal))] + public class ImplementsIGenericInterfaceOfOther : IGenericInterface, IGenericInterfaceInternal + { + [Kept] + [KeptOverride (typeof (IGenericInterface))] + [RemovedOverride (typeof (IGenericInterfaceInternal))] + public static ImplementsIGenericInterfaceOfSelf GetT () => throw new NotImplementedException (); + + [Kept] + [KeptOverride (typeof (IGenericInterface))] + static ImplementsIGenericInterfaceOfSelf IGenericInterface.GetTExplicit () + => throw new NotImplementedException (); + + static ImplementsIGenericInterfaceOfSelf IGenericInterfaceInternal.GetTExplicit () + => throw new NotImplementedException (); + } + + [Kept] + [KeptInterface (typeof (IGenericInterface))] + [KeptInterface (typeof (IGenericInterfaceInternal))] + internal class ImplementsIGenericInterfaceOfSelfUnused : IGenericInterface, IGenericInterfaceInternal + { + [Kept] + [KeptOverride (typeof (IGenericInterface))] + public static ImplementsIGenericInterfaceOfSelfUnused GetT () => throw new NotImplementedException (); + + [Kept] + [KeptOverride (typeof (IGenericInterface))] + static ImplementsIGenericInterfaceOfSelfUnused IGenericInterface.GetTExplicit () + => throw new NotImplementedException (); + + static ImplementsIGenericInterfaceOfSelfUnused IGenericInterfaceInternal.GetTExplicit () + => throw new NotImplementedException (); + } + + [Kept] + public static void CallExplicitGetT () where T : IGenericInterface + { + T.GetTExplicit (); + } + + [Kept] + public static void Test () + { + ImplementsIGenericInterfaceOfSelf.GetT (); + ImplementsIGenericInterfaceOfOther.GetT (); + CallExplicitGetT (); + CallExplicitGetT (); + + Type t = typeof (ImplementsIGenericInterfaceOfSelfUnused); + } + } + + [Kept] + public static class UnusedInterfaces + { + [Kept] + internal interface IUnusedInterface + { + static abstract int UnusedMethodImplicit (); + static abstract int UnusedMethodExplicit (); + } + + [Kept] + [KeptMember (".ctor()")] + [KeptInterface (typeof (IUnusedInterface))] + public class ImplementsUnusedInterface : IUnusedInterface + { + static int IUnusedInterface.UnusedMethodExplicit () => 0; + + [Kept] + [RemovedOverride (typeof (IUnusedInterface))] + public static int UnusedMethodImplicit () => 0; + } + + [Kept] + public static void Test () + { + Type t = typeof (ImplementsUnusedInterface); + } + } + } +} + diff --git a/test/Mono.Linker.Tests.Cases/Libraries/RootLibrary.cs b/test/Mono.Linker.Tests.Cases/Libraries/RootLibrary.cs index 1534573ea1bf..68e02b1ac6fb 100644 --- a/test/Mono.Linker.Tests.Cases/Libraries/RootLibrary.cs +++ b/test/Mono.Linker.Tests.Cases/Libraries/RootLibrary.cs @@ -366,7 +366,6 @@ internal interface IInternalInterface [Kept] internal interface IInternalStaticInterface { - [Kept] // https://github.com/dotnet/linker/issues/2733 static abstract void InternalStaticInterfaceMethod (); static abstract void ExplicitImplementationInternalStaticInterfaceMethod (); diff --git a/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs b/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs index 12f502b60f2b..96c037778ac4 100644 --- a/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs +++ b/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs @@ -173,12 +173,13 @@ protected virtual void VerifyTypeDefinitionKept (TypeDefinition original, TypeDe linkedMembers.Remove (f.FullName); } - foreach (var m in original.Methods) { - if (verifiedEventMethods.Contains (m.FullName)) + foreach (var originalMethod in original.Methods) { + if (verifiedEventMethods.Contains (originalMethod.FullName)) continue; - var msign = m.GetSignature (); - VerifyMethod (m, linked?.Methods.FirstOrDefault (l => msign == l.GetSignature ())); - linkedMembers.Remove (m.FullName); + var methodSignature = originalMethod.GetSignature (); + var linkedMethod = linked?.Methods.FirstOrDefault (l => methodSignature == l.GetSignature ()); + VerifyMethod (originalMethod, linkedMethod); + linkedMembers.Remove (originalMethod.FullName); } } @@ -245,15 +246,16 @@ void VerifyOverrides (MethodDefinition original, MethodDefinition linked) if (overriddenMethod.Resolve () is not MethodDefinition overriddenDefinition) { Assert.Fail ($"Method {linked.GetDisplayName ()} overrides method {overriddenMethod} which does not exist"); } else if (overriddenDefinition.DeclaringType.IsInterface) { - Assert.True (linked.DeclaringType.Interfaces.Select (i => i.InterfaceType).Contains (overriddenMethod.DeclaringType), + Assert.True (linked.DeclaringType.Interfaces.Select (i => i.InterfaceType.FullName).Contains (overriddenMethod.DeclaringType.FullName), $"Method {linked} overrides method {overriddenMethod}, but {linked.DeclaringType} does not implement interface {overriddenMethod.DeclaringType}"); } else { TypeReference baseType = linked.DeclaringType; TypeReference overriddenType = overriddenMethod.DeclaringType; while (baseType is not null) { - if (baseType.Equals (overriddenType)) + if (baseType.FullName == overriddenType.FullName) break; - if (baseType.Resolve ()?.BaseType is null) + baseType = baseType.Resolve ()?.BaseType; + if (baseType is null) Assert.Fail ($"Method {linked} overrides method {overriddenMethod} from, but {linked.DeclaringType} does not inherit from type {overriddenMethod.DeclaringType}"); } } @@ -421,6 +423,7 @@ protected virtual void VerifyMethodKept (MethodDefinition src, MethodDefinition VerifySecurityAttributes (src, linked); VerifyArrayInitializers (src, linked); VerifyMethodBody (src, linked); + VerifyOverrides (src, linked); } protected virtual void VerifyMethodBody (MethodDefinition src, MethodDefinition linked)