From 8454427af39d41ba40968e9ac2e1ada59a0ceb19 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Sat, 10 Feb 2024 16:08:32 -0800 Subject: [PATCH 01/34] Refactor OverrideInformation to include interface implementing type --- .../src/linker/CompatibilitySuppressions.xml | 4 -- .../src/linker/Linker.Steps/MarkStep.cs | 30 ++++----- .../illink/src/linker/Linker/Annotations.cs | 2 +- .../src/linker/Linker/InterfaceImplementor.cs | 21 +++++++ .../src/linker/Linker/OverrideInformation.cs | 61 +++++-------------- .../illink/src/linker/Linker/TypeMapInfo.cs | 53 ++++++++++------ 6 files changed, 86 insertions(+), 85 deletions(-) create mode 100644 src/tools/illink/src/linker/Linker/InterfaceImplementor.cs diff --git a/src/tools/illink/src/linker/CompatibilitySuppressions.xml b/src/tools/illink/src/linker/CompatibilitySuppressions.xml index 4a0a6296c4e2f..fd0dbb2635cb2 100644 --- a/src/tools/illink/src/linker/CompatibilitySuppressions.xml +++ b/src/tools/illink/src/linker/CompatibilitySuppressions.xml @@ -1481,10 +1481,6 @@ CP0002 M:Mono.Linker.OverrideInformation.get_IsOverrideOfInterfaceMember - - CP0002 - M:Mono.Linker.OverrideInformation.get_IsStaticInterfaceMethodPair - CP0002 M:Mono.Linker.Steps.BaseStep.get_MarkingHelpers diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index bb5e95e0a38dd..a6886000b66ea 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -701,17 +701,16 @@ void ProcessVirtualMethod (MethodDefinition method) var defaultImplementations = Annotations.GetDefaultInterfaceImplementations (method); if (defaultImplementations is not null) { foreach (var dimInfo in defaultImplementations) { - ProcessDefaultImplementation (dimInfo.ImplementingType, dimInfo.InterfaceImpl, dimInfo.DefaultInterfaceMethod); + ProcessDefaultImplementation (dimInfo); - 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); + if (IsInterfaceImplementationMethodNeededByTypeDueToInterface (dimInfo)) + MarkMethod (dimInfo.Override, new DependencyInfo (DependencyKind.Override, dimInfo.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)) + foreach (OverrideInformation ov in overridingMethods) { + if (IsInterfaceImplementationMethodNeededByTypeDueToInterface (ov)) MarkMethod (ov.Override, new DependencyInfo (DependencyKind.Override, ov.Base), ScopeStack.CurrentScope.Origin); } } @@ -819,13 +818,14 @@ bool RequiresInterfaceRecursively (TypeDefinition typeToExamine, TypeDefinition return false; } - void ProcessDefaultImplementation (TypeDefinition typeWithDefaultImplementedInterfaceMethod, InterfaceImplementation implementation, MethodDefinition implementationMethod) + void ProcessDefaultImplementation (OverrideInformation ov) { - if ((!implementationMethod.IsStatic && !Annotations.IsInstantiated (typeWithDefaultImplementedInterfaceMethod)) - || implementationMethod.IsStatic && !Annotations.IsRelevantToVariantCasting (typeWithDefaultImplementedInterfaceMethod)) + Debug.Assert (ov.IsOverrideOfInterfaceMember); + if ((!ov.Override.IsStatic && !Annotations.IsInstantiated (ov.InterfaceImplementor!.Implementor)) + || ov.Override.IsStatic && !Annotations.IsRelevantToVariantCasting (ov.InterfaceImplementor!.Implementor)) return; - MarkInterfaceImplementation (implementation); + MarkInterfaceImplementation (ov.InterfaceImplementor!.InterfaceImplementation); } void MarkMarshalSpec (IMarshalInfoProvider spec, in DependencyInfo reason) @@ -2549,7 +2549,7 @@ 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, TypeDefinition typeThatImplsInterface) + bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformation overrideInformation) { var @base = overrideInformation.Base; var method = overrideInformation.Override; @@ -2562,7 +2562,7 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat // 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; + InterfaceImplementation? iface = overrideInformation.InterfaceImplementor!.InterfaceImplementation; if (!((iface is not null && Annotations.IsMarked (iface)) || IsInterfaceImplementationMarkedRecursively (method.DeclaringType, @base.DeclaringType))) return false; @@ -2580,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 (typeThatImplsInterface) + return Annotations.IsRelevantToVariantCasting (overrideInformation.InterfaceImplementor.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 (typeThatImplsInterface); + return Annotations.IsInstantiated (overrideInformation.InterfaceImplementor.Implementor); } static bool IsSpecialSerializationConstructor (MethodDefinition method) @@ -3256,7 +3256,7 @@ protected virtual void ProcessMethod (MethodDefinition method, in DependencyInfo // Only if the interface method is referenced, then all the methods which implemented must be kept, but not the other way round. if (!markAllOverrides && Context.Resolve (@base) is MethodDefinition baseDefinition - && new OverrideInformation.OverridePair (baseDefinition, method).IsStaticInterfaceMethodPair ()) + && baseDefinition.DeclaringType.IsInterface && baseDefinition.IsStatic && method.IsStatic) continue; MarkMethod (@base, new DependencyInfo (DependencyKind.MethodImplOverride, method), ScopeStack.CurrentScope.Origin); MarkExplicitInterfaceImplementation (method, @base); diff --git a/src/tools/illink/src/linker/Linker/Annotations.cs b/src/tools/illink/src/linker/Linker/Annotations.cs index 8f7747cba3543..a7b3198265e81 100644 --- a/src/tools/illink/src/linker/Linker/Annotations.cs +++ b/src/tools/illink/src/linker/Linker/Annotations.cs @@ -462,7 +462,7 @@ public bool IsPublic (IMetadataTokenProvider provider) /// 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) + public IEnumerable? GetDefaultInterfaceImplementations (MethodDefinition method) { return TypeMapInfo.GetDefaultInterfaceImplementations (method); } diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs new file mode 100644 index 0000000000000..e2beddb001684 --- /dev/null +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs @@ -0,0 +1,21 @@ +// 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.Diagnostics; +using Mono.Cecil; + +namespace Mono.Linker +{ + public class InterfaceImplementor + { + public TypeDefinition Implementor { get; } + public InterfaceImplementation InterfaceImplementation { get; } + + public InterfaceImplementor (TypeDefinition implementor, InterfaceImplementation interfaceImplementation) + { + Debug.Assert(implementor.Interfaces.Contains (interfaceImplementation)); + Implementor = implementor; + InterfaceImplementation = interfaceImplementation; + } + } +} diff --git a/src/tools/illink/src/linker/Linker/OverrideInformation.cs b/src/tools/illink/src/linker/Linker/OverrideInformation.cs index 077353eb2ee7b..bca81f39bedbb 100644 --- a/src/tools/illink/src/linker/Linker/OverrideInformation.cs +++ b/src/tools/illink/src/linker/Linker/OverrideInformation.cs @@ -9,65 +9,32 @@ namespace Mono.Linker [DebuggerDisplay ("{Override}")] public class OverrideInformation { - readonly ITryResolveMetadata resolver; - readonly OverridePair _pair; - private InterfaceImplementation? _matchingInterfaceImplementation; + public MethodDefinition Base { get; } - public OverrideInformation (MethodDefinition @base, MethodDefinition @override, ITryResolveMetadata resolver, InterfaceImplementation? matchingInterfaceImplementation = null) + public MethodDefinition Override { get; } + + public InterfaceImplementor? InterfaceImplementor { get; } + + public OverrideInformation (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null) { - _pair = new OverridePair (@base, @override); - _matchingInterfaceImplementation = matchingInterfaceImplementation; - this.resolver = resolver; - } - public readonly record struct OverridePair (MethodDefinition Base, MethodDefinition Override) - { - public bool IsStaticInterfaceMethodPair () => Base.DeclaringType.IsInterface && Base.IsStatic && Override.IsStatic; - public InterfaceImplementation? GetMatchingInterfaceImplementation (ITryResolveMetadata resolver) - { - if (!Base.DeclaringType.IsInterface) - return null; - var interfaceType = Base.DeclaringType; - foreach (var @interface in Override.DeclaringType.Interfaces) { - if (resolver.TryResolve (@interface.InterfaceType)?.Equals (interfaceType) == true) { - return @interface; - } - } - return null; - } + Base = @base; + Override = @override; + InterfaceImplementor = interfaceImplementor; + // Ensure we have an interface implementation if the base method is from an interface and the override method is on a class + Debug.Assert(@base.DeclaringType.IsInterface && (interfaceImplementor != null || @override.DeclaringType.IsInterface) + || !@base.DeclaringType.IsInterface && interfaceImplementor == null); } - public MethodDefinition Base { get => _pair.Base; } - public MethodDefinition Override { get => _pair.Override; } public InterfaceImplementation? MatchingInterfaceImplementation { get { - if (_matchingInterfaceImplementation is not null) - return _matchingInterfaceImplementation; - _matchingInterfaceImplementation = _pair.GetMatchingInterfaceImplementation (resolver); - return _matchingInterfaceImplementation; + return InterfaceImplementor?.InterfaceImplementation; } } public bool IsOverrideOfInterfaceMember { get { - if (MatchingInterfaceImplementation != null) - return true; - - return Base.DeclaringType.IsInterface; + return InterfaceImplementor != null; } } - - public TypeDefinition? InterfaceType { - get { - if (!IsOverrideOfInterfaceMember) - return null; - - if (MatchingInterfaceImplementation != null) - return resolver.TryResolve (MatchingInterfaceImplementation.InterfaceType); - - return Base.DeclaringType; - } - } - - public bool IsStaticInterfaceMethodPair => _pair.IsStaticInterfaceMethodPair (); } } diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index a2f118adf9fb7..92611e649ebc1 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -32,6 +32,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Linq; using Mono.Cecil; namespace Mono.Linker @@ -43,7 +44,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) { @@ -92,41 +93,41 @@ public void EnsureProcessed (AssemblyDefinition assembly) /// 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) + public IEnumerable? GetDefaultInterfaceImplementations (MethodDefinition baseMethod) { default_interface_implementations.TryGetValue (baseMethod, out var ret); return ret; } - public void AddBaseMethod (MethodDefinition method, MethodDefinition @base, InterfaceImplementation? matchingInterfaceImplementation) + public void AddBaseMethod (MethodDefinition method, MethodDefinition @base, InterfaceImplementor? interfaceImplementor) { if (!base_methods.TryGetValue (method, out List? methods)) { methods = new List (); base_methods[method] = methods; } - methods.Add (new OverrideInformation (@base, method, context, matchingInterfaceImplementation)); + methods.Add (new OverrideInformation (@base, method, interfaceImplementor)); } - public void AddOverride (MethodDefinition @base, MethodDefinition @override, InterfaceImplementation? matchingInterfaceImplementation = null) + public void AddOverride (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null) { if (!override_methods.TryGetValue (@base, out List? methods)) { methods = new List (); override_methods.Add (@base, methods); } - methods.Add (new OverrideInformation (@base, @override, context, matchingInterfaceImplementation)); + methods.Add (new OverrideInformation (@base, @override, interfaceImplementor)); } - public void AddDefaultInterfaceImplementation (MethodDefinition @base, TypeDefinition implementingType, (InterfaceImplementation, MethodDefinition) matchingInterfaceImplementation) + public void AddDefaultInterfaceImplementation (MethodDefinition @base, InterfaceImplementor interfaceImplementor, MethodDefinition defaultImplementationMethod) { Debug.Assert(@base.DeclaringType.IsInterface); if (!default_interface_implementations.TryGetValue (@base, out var implementations)) { - implementations = new List<(TypeDefinition, InterfaceImplementation, MethodDefinition)> (); + implementations = new List (); default_interface_implementations.Add (@base, implementations); } - implementations.Add ((implementingType, matchingInterfaceImplementation.Item1, matchingInterfaceImplementation.Item2)); + implementations.Add (new (@base, defaultImplementationMethod, interfaceImplementor)); } protected virtual void MapType (TypeDefinition type) @@ -168,14 +169,14 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) // Try to find an implementation with a name/sig match on the current type MethodDefinition? exactMatchOnType = TryMatchMethod (type, interfaceMethod); if (exactMatchOnType != null) { - AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType); + AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType, new (type, interfaceImpl.OriginalImpl)); 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, interfaceImpl.OriginalImpl); + AnnotateMethods (resolvedInterfaceMethod, @base, new (type, interfaceImpl.OriginalImpl)); continue; } } @@ -211,6 +212,8 @@ void MapVirtualMethod (MethodDefinition method) if (@base == null) return; + Debug.Assert(!@base.DeclaringType.IsInterface); + AnnotateMethods (@base, method); } @@ -220,15 +223,29 @@ void MapOverrides (MethodDefinition method) MethodDefinition? @override = context.TryResolve (override_ref); if (@override == null) continue; - - AnnotateMethods (@override, method); + if (@override.DeclaringType.IsInterface) + { + // Find the matching interface implementation if the base is an interface method + foreach (var @interface in method.DeclaringType.Interfaces) + { + if (context.TryResolve (@interface.InterfaceType) == @override.DeclaringType) + { + AnnotateMethods(@override, method, new InterfaceImplementor(method.DeclaringType, @interface)); + break; + } + } + } + else + { + AnnotateMethods (@override, method); + } } } - void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, InterfaceImplementation? matchingInterfaceImplementation = null) + void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null) { - AddBaseMethod (@override, @base, matchingInterfaceImplementation); - AddOverride (@base, @override, matchingInterfaceImplementation); + AddBaseMethod (@override, @base, interfaceImplementor); + AddOverride (@base, @override, interfaceImplementor); } MethodDefinition? GetBaseMethodInTypeHierarchy (MethodDefinition method) @@ -298,7 +315,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition type, MethodDefin foreach (var potentialImplMethod in potentialImplInterface.Methods) { if (potentialImplMethod == interfaceMethod && !potentialImplMethod.IsAbstract) { - AddDefaultInterfaceImplementation (interfaceMethod, type, (interfaceImpl, potentialImplMethod)); + AddDefaultInterfaceImplementation (interfaceMethod, new (type, interfaceImpl), potentialImplMethod); foundImpl = true; break; } @@ -309,7 +326,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, @potentialImplMethod)); + AddDefaultInterfaceImplementation (interfaceMethod, new (type, interfaceImpl), @potentialImplMethod); foundImpl = true; break; } From 54c326d8b64f5f71650c78a0c37ef3a2b6948375 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Sat, 10 Feb 2024 16:27:08 -0800 Subject: [PATCH 02/34] Add NotNullWhenAttribute for InterfaceImplementor --- src/tools/illink/src/linker/Linker.Steps/MarkStep.cs | 10 +++++----- .../illink/src/linker/Linker/OverrideInformation.cs | 2 ++ 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index a6886000b66ea..5585c67b3d31d 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -821,11 +821,11 @@ bool RequiresInterfaceRecursively (TypeDefinition typeToExamine, TypeDefinition void ProcessDefaultImplementation (OverrideInformation ov) { 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.InterfaceImplementor.Implementor)) + || ov.Override.IsStatic && !Annotations.IsRelevantToVariantCasting (ov.InterfaceImplementor.Implementor)) return; - MarkInterfaceImplementation (ov.InterfaceImplementor!.InterfaceImplementation); + MarkInterfaceImplementation (ov.InterfaceImplementor.InterfaceImplementation); } void MarkMarshalSpec (IMarshalInfoProvider spec, in DependencyInfo reason) @@ -2553,7 +2553,7 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat { var @base = overrideInformation.Base; var method = overrideInformation.Override; - Debug.Assert (@base.DeclaringType.IsInterface); + Debug.Assert (overrideInformation.IsOverrideOfInterfaceMember); if (@base is null || method is null || @base.DeclaringType is null) return false; @@ -2562,7 +2562,7 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat // 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; + InterfaceImplementation? iface = overrideInformation.InterfaceImplementor.InterfaceImplementation; if (!((iface is not null && Annotations.IsMarked (iface)) || IsInterfaceImplementationMarkedRecursively (method.DeclaringType, @base.DeclaringType))) return false; diff --git a/src/tools/illink/src/linker/Linker/OverrideInformation.cs b/src/tools/illink/src/linker/Linker/OverrideInformation.cs index bca81f39bedbb..ea0d0e47e31a3 100644 --- a/src/tools/illink/src/linker/Linker/OverrideInformation.cs +++ b/src/tools/illink/src/linker/Linker/OverrideInformation.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using Mono.Cecil; +using System.Diagnostics.CodeAnalysis; namespace Mono.Linker { @@ -31,6 +32,7 @@ public InterfaceImplementation? MatchingInterfaceImplementation { } } + [MemberNotNullWhen (true, nameof (InterfaceImplementor), nameof (MatchingInterfaceImplementation))] public bool IsOverrideOfInterfaceMember { get { return InterfaceImplementor != null; From 6bfc58a8f94821292b7a1842b9f337a364a36e19 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Sat, 10 Feb 2024 16:50:49 -0800 Subject: [PATCH 03/34] Don't allow null interfaceImpl when base and override are interface methods in OverrideInformation --- src/tools/illink/src/linker/Linker/OverrideInformation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/illink/src/linker/Linker/OverrideInformation.cs b/src/tools/illink/src/linker/Linker/OverrideInformation.cs index ea0d0e47e31a3..930b8c3c407b0 100644 --- a/src/tools/illink/src/linker/Linker/OverrideInformation.cs +++ b/src/tools/illink/src/linker/Linker/OverrideInformation.cs @@ -22,7 +22,7 @@ public OverrideInformation (MethodDefinition @base, MethodDefinition @override, Override = @override; InterfaceImplementor = interfaceImplementor; // Ensure we have an interface implementation if the base method is from an interface and the override method is on a class - Debug.Assert(@base.DeclaringType.IsInterface && (interfaceImplementor != null || @override.DeclaringType.IsInterface) + Debug.Assert(@base.DeclaringType.IsInterface && interfaceImplementor != null || !@base.DeclaringType.IsInterface && interfaceImplementor == null); } From ecc5d0c4398b200190664e19f0e952cff11e3803 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Sun, 11 Feb 2024 11:40:44 -0800 Subject: [PATCH 04/34] Add InterfaceType to InterfaceImplementor --- .../src/linker/Linker/InterfaceImplementor.cs | 4 +++- .../src/linker/Linker/OverrideInformation.cs | 17 +++++++---------- .../illink/src/linker/Linker/TypeMapInfo.cs | 10 +++++----- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs index e2beddb001684..aba6c3b44ce0b 100644 --- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs @@ -10,12 +10,14 @@ public class InterfaceImplementor { public TypeDefinition Implementor { get; } public InterfaceImplementation InterfaceImplementation { get; } + public TypeDefinition? InterfaceType { get; } - public InterfaceImplementor (TypeDefinition implementor, InterfaceImplementation interfaceImplementation) + public InterfaceImplementor (TypeDefinition implementor, InterfaceImplementation interfaceImplementation, TypeDefinition? interfaceType) { Debug.Assert(implementor.Interfaces.Contains (interfaceImplementation)); Implementor = implementor; InterfaceImplementation = interfaceImplementation; + InterfaceType = interfaceType; } } } diff --git a/src/tools/illink/src/linker/Linker/OverrideInformation.cs b/src/tools/illink/src/linker/Linker/OverrideInformation.cs index 930b8c3c407b0..a80983802bf65 100644 --- a/src/tools/illink/src/linker/Linker/OverrideInformation.cs +++ b/src/tools/illink/src/linker/Linker/OverrideInformation.cs @@ -26,17 +26,14 @@ public OverrideInformation (MethodDefinition @base, MethodDefinition @override, || !@base.DeclaringType.IsInterface && interfaceImplementor == null); } - public InterfaceImplementation? MatchingInterfaceImplementation { - get { - return InterfaceImplementor?.InterfaceImplementation; - } - } + public InterfaceImplementation? MatchingInterfaceImplementation + => InterfaceImplementor?.InterfaceImplementation; + + public TypeDefinition? InterfaceType + => InterfaceImplementor?.InterfaceType; [MemberNotNullWhen (true, nameof (InterfaceImplementor), nameof (MatchingInterfaceImplementation))] - public bool IsOverrideOfInterfaceMember { - get { - return InterfaceImplementor != null; - } - } + public bool IsOverrideOfInterfaceMember + => InterfaceImplementor != null; } } diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 92611e649ebc1..42f8708b36df5 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -169,14 +169,14 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) // Try to find an implementation with a name/sig match on the current type MethodDefinition? exactMatchOnType = TryMatchMethod (type, interfaceMethod); if (exactMatchOnType != null) { - AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType, new (type, interfaceImpl.OriginalImpl)); + AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType, new (type, interfaceImpl.OriginalImpl, context.Resolve(interfaceImpl.OriginalImpl.InterfaceType))); 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)); + AnnotateMethods (resolvedInterfaceMethod, @base, new (type, interfaceImpl.OriginalImpl, context.Resolve(interfaceImpl.OriginalImpl.InterfaceType))); continue; } } @@ -230,7 +230,7 @@ void MapOverrides (MethodDefinition method) { if (context.TryResolve (@interface.InterfaceType) == @override.DeclaringType) { - AnnotateMethods(@override, method, new InterfaceImplementor(method.DeclaringType, @interface)); + AnnotateMethods(@override, method, new InterfaceImplementor(method.DeclaringType, @interface, context.Resolve(@interface.InterfaceType))); break; } } @@ -315,7 +315,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition type, MethodDefin foreach (var potentialImplMethod in potentialImplInterface.Methods) { if (potentialImplMethod == interfaceMethod && !potentialImplMethod.IsAbstract) { - AddDefaultInterfaceImplementation (interfaceMethod, new (type, interfaceImpl), potentialImplMethod); + AddDefaultInterfaceImplementation (interfaceMethod, new (type, interfaceImpl, context.Resolve(interfaceImpl.InterfaceType)), potentialImplMethod); foundImpl = true; break; } @@ -326,7 +326,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, new (type, interfaceImpl), @potentialImplMethod); + AddDefaultInterfaceImplementation (interfaceMethod, new (type, interfaceImpl, context.Resolve(interfaceImpl.InterfaceType)), @potentialImplMethod); foundImpl = true; break; } From 310de3004103e2f834ea63e1503c7625fa9d88bc Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Tue, 13 Feb 2024 13:50:38 -0800 Subject: [PATCH 05/34] Make new members internal for compat warnings --- src/tools/illink/src/linker/Linker/OverrideInformation.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/illink/src/linker/Linker/OverrideInformation.cs b/src/tools/illink/src/linker/Linker/OverrideInformation.cs index a80983802bf65..04d42bb888bb5 100644 --- a/src/tools/illink/src/linker/Linker/OverrideInformation.cs +++ b/src/tools/illink/src/linker/Linker/OverrideInformation.cs @@ -14,9 +14,9 @@ public class OverrideInformation public MethodDefinition Override { get; } - public InterfaceImplementor? InterfaceImplementor { get; } + internal InterfaceImplementor? InterfaceImplementor { get; } - public OverrideInformation (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null) + internal OverrideInformation (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null) { Base = @base; Override = @override; From 77195a3e14f75192c9d16184e567304675ee8d2e Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 14 Feb 2024 10:04:27 -0800 Subject: [PATCH 06/34] Make InterfaceImplementor internal for API compat --- src/tools/illink/src/linker/Linker/InterfaceImplementor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs index aba6c3b44ce0b..3f019a5487659 100644 --- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs @@ -6,7 +6,7 @@ namespace Mono.Linker { - public class InterfaceImplementor + internal class InterfaceImplementor { public TypeDefinition Implementor { get; } public InterfaceImplementation InterfaceImplementation { get; } From a73cc1f0a2ac77ea416db290da30adab7ca3aaf3 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 14 Feb 2024 10:18:36 -0800 Subject: [PATCH 07/34] Make TypeMapInfo internal for API Compat --- src/tools/illink/src/linker/Linker/TypeMapInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 42f8708b36df5..019daa858dac2 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -38,7 +38,7 @@ namespace Mono.Linker { - public class TypeMapInfo + internal class TypeMapInfo { readonly HashSet assemblies = new HashSet (); readonly LinkContext context; From 76bc2554b2062c381e23772faa388c851ec08c83 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 14 Feb 2024 10:26:33 -0800 Subject: [PATCH 08/34] Add InterfaceImplementor to suppressions.xml --- src/tools/illink/src/linker/CompatibilitySuppressions.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/illink/src/linker/CompatibilitySuppressions.xml b/src/tools/illink/src/linker/CompatibilitySuppressions.xml index fd0dbb2635cb2..7bf0a1e0ce697 100644 --- a/src/tools/illink/src/linker/CompatibilitySuppressions.xml +++ b/src/tools/illink/src/linker/CompatibilitySuppressions.xml @@ -253,6 +253,10 @@ CP0001 T:Mono.Linker.ILogger + + CP0001 + T:Mono.Linker.InterfaceImplementor + CP0001 T:Mono.Linker.InternalErrorException From 11577fa557b9bf028216128dd4018143fab04a3f Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 14 Feb 2024 10:27:27 -0800 Subject: [PATCH 09/34] Make types public again --- src/tools/illink/src/linker/Linker/InterfaceImplementor.cs | 2 +- src/tools/illink/src/linker/Linker/TypeMapInfo.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs index 3f019a5487659..aba6c3b44ce0b 100644 --- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs @@ -6,7 +6,7 @@ namespace Mono.Linker { - internal class InterfaceImplementor + public class InterfaceImplementor { public TypeDefinition Implementor { get; } public InterfaceImplementation InterfaceImplementation { get; } diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 019daa858dac2..42f8708b36df5 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -38,7 +38,7 @@ namespace Mono.Linker { - internal class TypeMapInfo + public class TypeMapInfo { readonly HashSet assemblies = new HashSet (); readonly LinkContext context; From 5fb3256d18dd379e4fc138a3de5f8880f6090d7c Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 15 Feb 2024 10:55:12 -0800 Subject: [PATCH 10/34] Use correct InterfaceImplementor in FindAndAddDims --- src/tools/illink/src/linker/Linker/TypeMapInfo.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 42f8708b36df5..929e1e2d54120 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -182,7 +182,7 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) } // Look for a default implementation last. - FindAndAddDefaultInterfaceImplementations (type, resolvedInterfaceMethod); + FindAndAddDefaultInterfaceImplementations (type, type, resolvedInterfaceMethod); } } } @@ -300,12 +300,12 @@ void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, Interf // Note that this returns a list to potentially cover the diamond case (more than one // most specific implementation of the given interface methods). ILLink needs to preserve // all the implementations so that the proper exception can be thrown at runtime. - void FindAndAddDefaultInterfaceImplementations (TypeDefinition type, MethodDefinition interfaceMethod) + void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplementsInterface, TypeDefinition typeThatMayHaveDimProvidingInterface, MethodDefinition interfaceMethod) { // 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) { + foreach (var interfaceImpl in typeThatMayHaveDimProvidingInterface.Interfaces) { var potentialImplInterface = context.TryResolve (interfaceImpl.InterfaceType); if (potentialImplInterface == null) continue; @@ -315,7 +315,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition type, MethodDefin foreach (var potentialImplMethod in potentialImplInterface.Methods) { if (potentialImplMethod == interfaceMethod && !potentialImplMethod.IsAbstract) { - AddDefaultInterfaceImplementation (interfaceMethod, new (type, interfaceImpl, context.Resolve(interfaceImpl.InterfaceType)), potentialImplMethod); + AddDefaultInterfaceImplementation (interfaceMethod, new (typeThatImplementsInterface, interfaceImpl, context.Resolve(interfaceImpl.InterfaceType)), potentialImplMethod); foundImpl = true; break; } @@ -326,7 +326,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, new (type, interfaceImpl, context.Resolve(interfaceImpl.InterfaceType)), @potentialImplMethod); + AddDefaultInterfaceImplementation (interfaceMethod, new (typeThatImplementsInterface, interfaceImpl, context.Resolve(interfaceImpl.InterfaceType)), @potentialImplMethod); foundImpl = true; break; } @@ -340,7 +340,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition type, MethodDefin // 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 (potentialImplInterface, interfaceMethod); + FindAndAddDefaultInterfaceImplementations (typeThatImplementsInterface, potentialImplInterface, interfaceMethod); } } } From 3b4a69d8878959874dd64c4456686c1717a0194c Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 15 Feb 2024 13:13:45 -0800 Subject: [PATCH 11/34] InterfaceImplementor doesn't need to directly implement the interface --- src/tools/illink/src/linker/Linker/InterfaceImplementor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs index aba6c3b44ce0b..854860adba55d 100644 --- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs @@ -14,7 +14,6 @@ public class InterfaceImplementor public InterfaceImplementor (TypeDefinition implementor, InterfaceImplementation interfaceImplementation, TypeDefinition? interfaceType) { - Debug.Assert(implementor.Interfaces.Contains (interfaceImplementation)); Implementor = implementor; InterfaceImplementation = interfaceImplementation; InterfaceType = interfaceType; From cae111aa2e68b57c9aea73b22c0bb5f3ff814fed Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 15 Feb 2024 20:16:23 -0800 Subject: [PATCH 12/34] Add test case for interfaces without impls for whole hierarchy --- src/tools/illink/illink.sln | 1 + .../illink/src/linker/Linker/TypeMapInfo.cs | 34 ++++++-- .../linker/Linker/TypeReferenceExtensions.cs | 31 ++++--- ...terfaces.DefaultInterfaceMethodsTests.g.cs | 6 ++ ...mProvidedByUnreferencedIfaceInHierarchy.il | 87 +++++++++++++++++++ ...mProvidedByUnreferencedIfaceInHierarchy.il | 70 +++++++++++++++ ...mProvidedByUnreferencedIfaceInHierarchy.cs | 68 +++++++++++++++ ...mProvidedByUnreferencedIfaceInHierarchy.cs | 71 +++++++++++++++ 8 files changed, 350 insertions(+), 18 deletions(-) create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByUnreferencedIfaceInHierarchy.il create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/StaticDimProvidedByUnreferencedIfaceInHierarchy.il create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByUnreferencedIfaceInHierarchy.cs create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs diff --git a/src/tools/illink/illink.sln b/src/tools/illink/illink.sln index 87e99d208c7fb..9b8d904942baa 100644 --- a/src/tools/illink/illink.sln +++ b/src/tools/illink/illink.sln @@ -223,6 +223,7 @@ Global SolutionGuid = {E43A3901-42B0-48CA-BB36-5CD40A99A6EE} EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution + test\Trimming.Tests.Shared\Trimming.Tests.Shared.projitems*{400a1561-b6b6-482d-9e4c-3ddaede5bd07}*SharedItemsImports = 5 src\ILLink.Shared\ILLink.Shared.projitems*{dd28e2b1-057b-4b4d-a04d-b2ebd9e76e46}*SharedItemsImports = 5 src\ILLink.Shared\ILLink.Shared.projitems*{f1a44a78-34ee-408b-8285-9a26f0e7d4f2}*SharedItemsImports = 5 src\ILLink.Shared\ILLink.Shared.projitems*{ff598e93-8e9e-4091-9f50-61a7572663ae}*SharedItemsImports = 13 diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index a2f118adf9fb7..f326af5d7a0a1 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -141,6 +141,24 @@ protected virtual void MapType (TypeDefinition type) MapType (nested); } + static HashSet GetRecursiveInterfaces (TypeDefinition type) + { + if (!type.HasInterfaces) + return []; + + HashSet interfaces = new HashSet (); + GetInterfacesOnType (type, interfaces); + return interfaces; + + static void GetInterfacesOnType (TypeDefinition type, HashSet interfaces) + { + foreach (var iface in type.Interfaces) { + interfaces.Add (iface.InterfaceType); + GetInterfacesOnType(type, interfaces); + } + } + } + void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) { if (!type.HasInterfaces) @@ -181,7 +199,7 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) } // Look for a default implementation last. - FindAndAddDefaultInterfaceImplementations (type, resolvedInterfaceMethod); + FindAndAddDefaultInterfaceImplementations (type, type, resolvedInterfaceMethod); } } } @@ -283,12 +301,12 @@ void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, Interf // Note that this returns a list to potentially cover the diamond case (more than one // most specific implementation of the given interface methods). ILLink needs to preserve // all the implementations so that the proper exception can be thrown at runtime. - void FindAndAddDefaultInterfaceImplementations (TypeDefinition type, MethodDefinition interfaceMethod) + void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplementsInterface, TypeDefinition typeThatMayHaveDIM, MethodDefinition interfaceMethodToBeImplemented) { // 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) { + foreach (var interfaceImpl in typeThatMayHaveDIM.Interfaces) { var potentialImplInterface = context.TryResolve (interfaceImpl.InterfaceType); if (potentialImplInterface == null) continue; @@ -296,9 +314,9 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition type, MethodDefin bool foundImpl = false; foreach (var potentialImplMethod in potentialImplInterface.Methods) { - if (potentialImplMethod == interfaceMethod && + if (potentialImplMethod == interfaceMethodToBeImplemented && !potentialImplMethod.IsAbstract) { - AddDefaultInterfaceImplementation (interfaceMethod, type, (interfaceImpl, potentialImplMethod)); + AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, typeThatImplementsInterface, (interfaceImpl, potentialImplMethod)); foundImpl = true; break; } @@ -308,8 +326,8 @@ 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, @potentialImplMethod)); + if (context.TryResolve (@override) == interfaceMethodToBeImplemented) { + AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, typeThatImplementsInterface, (interfaceImpl, @potentialImplMethod)); foundImpl = true; break; } @@ -323,7 +341,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition type, MethodDefin // 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 (potentialImplInterface, interfaceMethod); + FindAndAddDefaultInterfaceImplementations (typeThatImplementsInterface, potentialImplInterface, interfaceMethodToBeImplemented); } } } diff --git a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs index 5092fe1158e34..28680c1b644e3 100644 --- a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs +++ b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs @@ -151,22 +151,33 @@ void parseArrayDimensions (ArrayType at) return null; } - public static IEnumerable<(TypeReference InflatedInterface, InterfaceImplementation OriginalImpl)> GetInflatedInterfaces (this TypeReference typeRef, ITryResolveMetadata resolver) + public static HashSet<(TypeReference InflatedInterface, InterfaceImplementation OriginalImpl)> GetInflatedInterfaces (this TypeReference typeRef, ITryResolveMetadata resolver) { var typeDef = resolver.TryResolve (typeRef); if (typeDef?.HasInterfaces != true) - yield break; + return []; + HashSet<(TypeReference, InterfaceImplementation)> inflatedInterfaces = new (); + AddInflatedInterfacesRecursively (typeRef, typeDef, resolver, inflatedInterfaces); + return inflatedInterfaces; - 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)!; + static void AddInflatedInterfacesRecursively(TypeReference typeRef, TypeDefinition interfaceProvider, ITryResolveMetadata resolver, HashSet<(TypeReference, InterfaceImplementation)> inflatedInterfaces) + { + if (typeRef is GenericInstanceType genericInstance) { + foreach (var interfaceImpl in interfaceProvider.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. + inflatedInterfaces.Add((InflateGenericType (genericInstance, interfaceImpl.InterfaceType, resolver), interfaceImpl)!); + if (resolver.TryResolve (interfaceImpl.InterfaceType) is { } baseIface) + AddInflatedInterfacesRecursively (typeRef, baseIface, resolver, inflatedInterfaces); + } + } else { + foreach (var interfaceImpl in interfaceProvider.Interfaces) { + inflatedInterfaces.Add ((interfaceImpl.InterfaceType, interfaceImpl)); + if (resolver.TryResolve (interfaceImpl.InterfaceType) is { } baseIface) + AddInflatedInterfacesRecursively (typeRef, baseIface, resolver, inflatedInterfaces); + } } - } else { - foreach (var interfaceImpl in typeDef.Interfaces) - yield return (interfaceImpl.InterfaceType, interfaceImpl); } } diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs index 4b3f387a39015..f89602a195055 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs @@ -15,6 +15,12 @@ public Task DefaultInterfaceMethodCallIntoClass () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task DimProvidedByUnreferencedIfaceInHierarchy () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task GenericDefaultInterfaceMethods () { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByUnreferencedIfaceInHierarchy.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByUnreferencedIfaceInHierarchy.il new file mode 100644 index 0000000000000..416f1202c682c --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByUnreferencedIfaceInHierarchy.il @@ -0,0 +1,87 @@ + +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class public auto ansi abstract sealed beforefieldinit Program + extends [mscorlib]System.Object +{ + // Nested Types + .class interface nested public auto ansi abstract beforefieldinit IFoo + { + // Methods + .method public hidebysig newslot abstract virtual + instance void Method () cil managed + { + } // end of method IFoo::Method + + } // end of class IFoo + + .class interface nested public auto ansi abstract beforefieldinit IBar + implements Program/IFoo + { + // Methods + .method public final hidebysig virtual + instance void Program.IFoo.Method () cil managed + { + .override method instance void Program/IFoo::Method() + // Method begins at RVA 0x2068 + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: nop + IL_0001: ret + } // end of method IBar::Program.IFoo.Method + + } // end of class IBar + + .class interface nested public auto ansi abstract beforefieldinit IBaz + implements Program/IBar + { + } // end of class IBaz + + .class nested public auto ansi beforefieldinit MyFoo + extends [mscorlib]System.Object + implements Program/IBaz + { + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2076 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method MyFoo::.ctor + + } // end of class MyFoo + + + // Methods + .method public hidebysig static + void CallMethod ( + class Program/IFoo foo + ) cil managed + { + .custom instance void [mscorlib]mscorlib.CompilerServices.NullableContextAttribute::.ctor(uint8) = ( + 01 00 01 00 00 + ) + // Method begins at RVA 0x2050 + // Code size 9 (0x9) + .maxstack 8 + + IL_0000: nop + IL_0001: ldarg.0 + IL_0002: callvirt instance void Program/IFoo::Method() + IL_0007: nop + IL_0008: ret + } // end of method Program::CallMethod + +} // end of class Program diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/StaticDimProvidedByUnreferencedIfaceInHierarchy.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/StaticDimProvidedByUnreferencedIfaceInHierarchy.il new file mode 100644 index 0000000000000..949d5e6fbf4ab --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/StaticDimProvidedByUnreferencedIfaceInHierarchy.il @@ -0,0 +1,70 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class public auto ansi abstract sealed beforefieldinit Program + extends [mscorlib]System.Object +{ + // Nested Types + .class interface nested public auto ansi abstract beforefieldinit IBase + { + // Methods + .method public hidebysig abstract virtual static + void Method () cil managed + { + } // end of method IBase::Method + + } // end of class IBase + + .class interface nested public auto ansi abstract beforefieldinit I2 + implements Program/IBase + { + // Methods + .method public hidebysig static + void Program.IBase.Method () cil managed + { + .override method void Program/IBase::Method() + // Method begins at RVA 0x205f + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: nop + IL_0001: ret + } // end of method I2::Program.IBase.Method + + } // end of class I2 + + .class interface nested public auto ansi abstract beforefieldinit I3 + implements Program/I2 + { + } // end of class I3 + + .class interface nested public auto ansi abstract beforefieldinit I4 + implements Program/I3 + { + } // end of class I4 + + + // Methods + .method public hidebysig static + void CallMethod<(Program/IBase) T> () cil managed + { + .param constraint T, Program/IBase + .custom instance void [mscorlib]mscorlib.CompilerServices.NullableAttribute::.ctor(uint8) = ( + 01 00 01 00 00 + ) + // Method begins at RVA 0x2050 + // Code size 14 (0xe) + .maxstack 8 + + IL_0000: nop + IL_0001: constrained. !!T + IL_0007: call void Program/IBase::Method() + IL_000c: nop + IL_000d: ret + } // end of method Program::CallMethod + +} // end of class Program diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByUnreferencedIfaceInHierarchy.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByUnreferencedIfaceInHierarchy.cs new file mode 100644 index 0000000000000..cfc9e4a367d23 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByUnreferencedIfaceInHierarchy.cs @@ -0,0 +1,68 @@ + + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods +{ + [SetupLinkerArgument ("--skip-unresolved", "true")] + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/DimProvidedByUnreferencedIfaceInHierarchy.il" })] + [SkipILVerify] + +#if IL_ASSEMBLY_AVAILABLE + [KeptMemberInAssembly ("library.dll", typeof(Program.IBar), "Program.IFoo.Method()")] + [KeptMemberInAssembly ("library.dll", typeof(Program.IFoo), "Method()")] + [KeptTypeInAssembly ("library.dll", typeof(Program.IBar))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IBar), "library.dll", typeof (Program.IFoo))] + // https://github.com/dotnet/runtime/issues/98536 + //[KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.MyFoo), "library.dll", typeof (Program.IBaz))] + //[KeptTypeInAssembly ("library.dll", typeof(Program.IBaz))] + //[KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IBaz), "library.dll", typeof (Program.IBar))] + [KeptMemberInAssembly ("library.dll", typeof(Program), "CallMethod(Program/IFoo)")] +#endif + class DimProvidedByUnreferencedIfaceInHierarchy + { + static void Main () + { +#if IL_ASSEMBLY_AVAILABLE + Program.IFoo foo = new Program.MyFoo (); + Program.CallMethod(foo); +#endif + } + } +} + + + +// public static class Program +// { +// [Kept] +// interface IFoo +// { +// void Method(); +// } + +// [Kept] +// interface IBar : IFoo +// { +// [Kept] +// void IFoo.Method() { } +// } + +// [Kept] +// interface IBaz: IBar /* not IFoo */ +// { +// } + +// [Kept] +// [KeptInterface(typeof(IBaz))] +// class MyFoo : IBaz /* not IBar, not IFoo */ +// { } + +// static void CallMethod(IFoo foo) +// { +// foo.Method(); +// } +// } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs new file mode 100644 index 0000000000000..a65ad7d2f754c --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs @@ -0,0 +1,71 @@ + + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods +{ + [SetupLinkerArgument ("--skip-unresolved", "true")] + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/StaticDimProvidedByUnreferencedIfaceInHierarchy.il" })] + [SkipILVerify] + +#if IL_ASSEMBLY_AVAILABLE + [KeptMemberInAssembly ("library.dll", typeof(Program), "CallMethod<#1>()")] + [KeptTypeInAssembly ("library.dll", typeof(Program.IBase))] + [KeptMemberInAssembly ("library.dll", typeof(Program.IBase), "Method()")] + [KeptTypeInAssembly ("library.dll", typeof(Program.I2))] + [KeptMemberInAssembly ("library.dll", typeof(Program.I2), "Program.IBase.Method()")] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I2), "library.dll", typeof (Program.IBase))] + // https://github.com/dotnet/runtime/issues/98536 + //[KeptTypeInAssembly ("library.dll", typeof(Program.I3))] + //[KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I3), "library.dll", typeof (Program.I2))] + [KeptTypeInAssembly ("library.dll", typeof(Program.I4))] + // https://github.com/dotnet/runtime/issues/98536 + //[KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I4), "library.dll", typeof (Program.I3))] +#endif + class StaticDimProvidedByUnreferencedIfaceInHierarchy + { + static void Main () + { +#if IL_ASSEMBLY_AVAILABLE + Program.CallMethod(); +#endif + } + } +} + + + +// public static class Program +// { +// [Kept] +// interface IBase +// { +// [Kept] +// static abstract void Method(); +// } + +// [Kept] +// [KeptInterface(typeof(IBase)] +// interface I2 : IBase +// { +// [Kept] +// static void IBase.Method() { } +// } + +// [Kept] +// [KeptInterface(typeof(I2)] +// interface I3 : I2 { } + +// [Kept] +// [KeptInterface(typeof(I3)] +// interface I4 : I3 { } + +// [Kept] +// static void CallMethod() where T : IBase +// { +// T.Method(); +// } +// } From 00d42b7af56d5d6e15f6147cabe500012e0f8113 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 15 Feb 2024 20:18:04 -0800 Subject: [PATCH 13/34] Undo source changes --- src/tools/illink/illink.sln | 1 - .../illink/src/linker/Linker/TypeMapInfo.cs | 34 +++++-------------- .../linker/Linker/TypeReferenceExtensions.cs | 31 ++++++----------- 3 files changed, 18 insertions(+), 48 deletions(-) diff --git a/src/tools/illink/illink.sln b/src/tools/illink/illink.sln index 9b8d904942baa..87e99d208c7fb 100644 --- a/src/tools/illink/illink.sln +++ b/src/tools/illink/illink.sln @@ -223,7 +223,6 @@ Global SolutionGuid = {E43A3901-42B0-48CA-BB36-5CD40A99A6EE} EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution - test\Trimming.Tests.Shared\Trimming.Tests.Shared.projitems*{400a1561-b6b6-482d-9e4c-3ddaede5bd07}*SharedItemsImports = 5 src\ILLink.Shared\ILLink.Shared.projitems*{dd28e2b1-057b-4b4d-a04d-b2ebd9e76e46}*SharedItemsImports = 5 src\ILLink.Shared\ILLink.Shared.projitems*{f1a44a78-34ee-408b-8285-9a26f0e7d4f2}*SharedItemsImports = 5 src\ILLink.Shared\ILLink.Shared.projitems*{ff598e93-8e9e-4091-9f50-61a7572663ae}*SharedItemsImports = 13 diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index f326af5d7a0a1..a2f118adf9fb7 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -141,24 +141,6 @@ protected virtual void MapType (TypeDefinition type) MapType (nested); } - static HashSet GetRecursiveInterfaces (TypeDefinition type) - { - if (!type.HasInterfaces) - return []; - - HashSet interfaces = new HashSet (); - GetInterfacesOnType (type, interfaces); - return interfaces; - - static void GetInterfacesOnType (TypeDefinition type, HashSet interfaces) - { - foreach (var iface in type.Interfaces) { - interfaces.Add (iface.InterfaceType); - GetInterfacesOnType(type, interfaces); - } - } - } - void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) { if (!type.HasInterfaces) @@ -199,7 +181,7 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) } // Look for a default implementation last. - FindAndAddDefaultInterfaceImplementations (type, type, resolvedInterfaceMethod); + FindAndAddDefaultInterfaceImplementations (type, resolvedInterfaceMethod); } } } @@ -301,12 +283,12 @@ void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, Interf // Note that this returns a list to potentially cover the diamond case (more than one // most specific implementation of the given interface methods). ILLink needs to preserve // all the implementations so that the proper exception can be thrown at runtime. - void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplementsInterface, TypeDefinition typeThatMayHaveDIM, MethodDefinition interfaceMethodToBeImplemented) + void FindAndAddDefaultInterfaceImplementations (TypeDefinition type, MethodDefinition interfaceMethod) { // Go over all interfaces, trying to find a method that is an explicit MethodImpl of the // interface method in question. - foreach (var interfaceImpl in typeThatMayHaveDIM.Interfaces) { + foreach (var interfaceImpl in type.Interfaces) { var potentialImplInterface = context.TryResolve (interfaceImpl.InterfaceType); if (potentialImplInterface == null) continue; @@ -314,9 +296,9 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement bool foundImpl = false; foreach (var potentialImplMethod in potentialImplInterface.Methods) { - if (potentialImplMethod == interfaceMethodToBeImplemented && + if (potentialImplMethod == interfaceMethod && !potentialImplMethod.IsAbstract) { - AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, typeThatImplementsInterface, (interfaceImpl, potentialImplMethod)); + AddDefaultInterfaceImplementation (interfaceMethod, type, (interfaceImpl, potentialImplMethod)); foundImpl = true; break; } @@ -326,8 +308,8 @@ 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 @override in potentialImplMethod.Overrides) { - if (context.TryResolve (@override) == interfaceMethodToBeImplemented) { - AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, typeThatImplementsInterface, (interfaceImpl, @potentialImplMethod)); + if (context.TryResolve (@override) == interfaceMethod) { + AddDefaultInterfaceImplementation (interfaceMethod, type, (interfaceImpl, @potentialImplMethod)); foundImpl = true; break; } @@ -341,7 +323,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement // 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); + FindAndAddDefaultInterfaceImplementations (potentialImplInterface, interfaceMethod); } } } diff --git a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs index 28680c1b644e3..5092fe1158e34 100644 --- a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs +++ b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs @@ -151,33 +151,22 @@ void parseArrayDimensions (ArrayType at) return null; } - public static HashSet<(TypeReference InflatedInterface, InterfaceImplementation OriginalImpl)> GetInflatedInterfaces (this TypeReference typeRef, ITryResolveMetadata resolver) + public static IEnumerable<(TypeReference InflatedInterface, InterfaceImplementation OriginalImpl)> GetInflatedInterfaces (this TypeReference typeRef, ITryResolveMetadata resolver) { var typeDef = resolver.TryResolve (typeRef); if (typeDef?.HasInterfaces != true) - return []; - HashSet<(TypeReference, InterfaceImplementation)> inflatedInterfaces = new (); - AddInflatedInterfacesRecursively (typeRef, typeDef, resolver, inflatedInterfaces); - return inflatedInterfaces; + yield break; - static void AddInflatedInterfacesRecursively(TypeReference typeRef, TypeDefinition interfaceProvider, ITryResolveMetadata resolver, HashSet<(TypeReference, InterfaceImplementation)> inflatedInterfaces) - { - if (typeRef is GenericInstanceType genericInstance) { - foreach (var interfaceImpl in interfaceProvider.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. - inflatedInterfaces.Add((InflateGenericType (genericInstance, interfaceImpl.InterfaceType, resolver), interfaceImpl)!); - if (resolver.TryResolve (interfaceImpl.InterfaceType) is { } baseIface) - AddInflatedInterfacesRecursively (typeRef, baseIface, resolver, inflatedInterfaces); - } - } else { - foreach (var interfaceImpl in interfaceProvider.Interfaces) { - inflatedInterfaces.Add ((interfaceImpl.InterfaceType, interfaceImpl)); - if (resolver.TryResolve (interfaceImpl.InterfaceType) is { } baseIface) - AddInflatedInterfacesRecursively (typeRef, baseIface, resolver, inflatedInterfaces); - } + 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); } } From 49b3464695be3a5f97111d5bf3511a49c1b7c4a3 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Fri, 16 Feb 2024 10:19:11 -0800 Subject: [PATCH 14/34] Comment out all failing assertions --- .../DimProvidedByUnreferencedIfaceInHierarchy.cs | 12 ++++++------ ...aticDimProvidedByUnreferencedIfaceInHierarchy.cs | 13 ++++++------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByUnreferencedIfaceInHierarchy.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByUnreferencedIfaceInHierarchy.cs index cfc9e4a367d23..7b749d20d46c2 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByUnreferencedIfaceInHierarchy.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByUnreferencedIfaceInHierarchy.cs @@ -12,14 +12,14 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods [SkipILVerify] #if IL_ASSEMBLY_AVAILABLE - [KeptMemberInAssembly ("library.dll", typeof(Program.IBar), "Program.IFoo.Method()")] [KeptMemberInAssembly ("library.dll", typeof(Program.IFoo), "Method()")] - [KeptTypeInAssembly ("library.dll", typeof(Program.IBar))] - [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IBar), "library.dll", typeof (Program.IFoo))] // https://github.com/dotnet/runtime/issues/98536 - //[KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.MyFoo), "library.dll", typeof (Program.IBaz))] - //[KeptTypeInAssembly ("library.dll", typeof(Program.IBaz))] - //[KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IBaz), "library.dll", typeof (Program.IBar))] + // [KeptTypeInAssembly ("library.dll", typeof(Program.IBar))] + // [KeptMemberInAssembly ("library.dll", typeof(Program.IBar), "Program.IFoo.Method()")] + // [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IBar), "library.dll", typeof (Program.IFoo))] + // [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.MyFoo), "library.dll", typeof (Program.IBaz))] + // [KeptTypeInAssembly ("library.dll", typeof(Program.IBaz))] + // [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IBaz), "library.dll", typeof (Program.IBar))] [KeptMemberInAssembly ("library.dll", typeof(Program), "CallMethod(Program/IFoo)")] #endif class DimProvidedByUnreferencedIfaceInHierarchy diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs index a65ad7d2f754c..1cb85f6c0e0c4 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs @@ -15,15 +15,14 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods [KeptMemberInAssembly ("library.dll", typeof(Program), "CallMethod<#1>()")] [KeptTypeInAssembly ("library.dll", typeof(Program.IBase))] [KeptMemberInAssembly ("library.dll", typeof(Program.IBase), "Method()")] - [KeptTypeInAssembly ("library.dll", typeof(Program.I2))] - [KeptMemberInAssembly ("library.dll", typeof(Program.I2), "Program.IBase.Method()")] - [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I2), "library.dll", typeof (Program.IBase))] - // https://github.com/dotnet/runtime/issues/98536 - //[KeptTypeInAssembly ("library.dll", typeof(Program.I3))] - //[KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I3), "library.dll", typeof (Program.I2))] [KeptTypeInAssembly ("library.dll", typeof(Program.I4))] // https://github.com/dotnet/runtime/issues/98536 - //[KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I4), "library.dll", typeof (Program.I3))] + // [KeptTypeInAssembly ("library.dll", typeof(Program.I2))] + // [KeptMemberInAssembly ("library.dll", typeof(Program.I2), "Program.IBase.Method()")] + // [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I2), "library.dll", typeof (Program.IBase))] + // [KeptTypeInAssembly ("library.dll", typeof(Program.I3))] + // [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I3), "library.dll", typeof (Program.I2))] + // [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I4), "library.dll", typeof (Program.I3))] #endif class StaticDimProvidedByUnreferencedIfaceInHierarchy { From 14814ed4eb8569ef7c42d515c83ec774e4a93fbe Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 21 Feb 2024 15:18:24 -0800 Subject: [PATCH 15/34] PR Feedback --- .../src/linker/Linker/InterfaceImplementor.cs | 13 ++++++- .../src/linker/Linker/OverrideInformation.cs | 2 + .../illink/src/linker/Linker/TypeMapInfo.cs | 37 +++++++++---------- 3 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs index 854860adba55d..6f0a55aba9b13 100644 --- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs @@ -8,11 +8,20 @@ namespace Mono.Linker { public class InterfaceImplementor { + /// + /// The type that implements . + /// public TypeDefinition Implementor { get; } + /// + /// The .interfaceimpl on that points to + /// public InterfaceImplementation InterfaceImplementation { get; } - public TypeDefinition? InterfaceType { get; } + /// + /// The type of the interface that is implemented by + /// + public TypeDefinition InterfaceType { get; } - public InterfaceImplementor (TypeDefinition implementor, InterfaceImplementation interfaceImplementation, TypeDefinition? interfaceType) + public InterfaceImplementor (TypeDefinition implementor, InterfaceImplementation interfaceImplementation, TypeDefinition interfaceType) { Implementor = implementor; InterfaceImplementation = interfaceImplementation; diff --git a/src/tools/illink/src/linker/Linker/OverrideInformation.cs b/src/tools/illink/src/linker/Linker/OverrideInformation.cs index 04d42bb888bb5..0727d5d25c19a 100644 --- a/src/tools/illink/src/linker/Linker/OverrideInformation.cs +++ b/src/tools/illink/src/linker/Linker/OverrideInformation.cs @@ -24,6 +24,8 @@ internal OverrideInformation (MethodDefinition @base, MethodDefinition @override // 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); + // Ensure the interfaceImplementor is for the interface we expect + Debug.Assert (@base.DeclaringType.IsInterface ? interfaceImplementor!.InterfaceType == @base.DeclaringType : true); } public InterfaceImplementation? MatchingInterfaceImplementation diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 929e1e2d54120..a2915ab1a8c8a 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -32,7 +32,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Linq; using Mono.Cecil; namespace Mono.Linker @@ -169,20 +168,20 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) // Try to find an implementation with a name/sig match on the current type MethodDefinition? exactMatchOnType = TryMatchMethod (type, interfaceMethod); if (exactMatchOnType != null) { - AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType, new (type, interfaceImpl.OriginalImpl, context.Resolve(interfaceImpl.OriginalImpl.InterfaceType))); + AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType, new (type, interfaceImpl.OriginalImpl, context.Resolve(interfaceImpl.OriginalImpl.InterfaceType)!)); 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, context.Resolve(interfaceImpl.OriginalImpl.InterfaceType))); + AnnotateMethods (resolvedInterfaceMethod, @base, new (type, interfaceImpl.OriginalImpl, context.Resolve(interfaceImpl.OriginalImpl.InterfaceType)!)); continue; } } // Look for a default implementation last. - FindAndAddDefaultInterfaceImplementations (type, type, resolvedInterfaceMethod); + FindAndAddDefaultInterfaceImplementations (type, resolvedInterfaceMethod); } } } @@ -219,25 +218,25 @@ void MapVirtualMethod (MethodDefinition method) void MapOverrides (MethodDefinition method) { - foreach (MethodReference override_ref in method.Overrides) { - MethodDefinition? @override = context.TryResolve (override_ref); - if (@override == null) + foreach (MethodReference baseMethodRef in method.Overrides) { + MethodDefinition? baseMethod = context.TryResolve (baseMethodRef); + if (baseMethod == null) continue; - if (@override.DeclaringType.IsInterface) + if (baseMethod.DeclaringType.IsInterface) { // Find the matching interface implementation if the base is an interface method - foreach (var @interface in method.DeclaringType.Interfaces) + foreach (var iface in method.DeclaringType.Interfaces) { - if (context.TryResolve (@interface.InterfaceType) == @override.DeclaringType) + if (context.TryResolve (iface.InterfaceType) == baseMethod.DeclaringType) { - AnnotateMethods(@override, method, new InterfaceImplementor(method.DeclaringType, @interface, context.Resolve(@interface.InterfaceType))); + AnnotateMethods (baseMethod, method, new InterfaceImplementor (method.DeclaringType, iface, context.Resolve (baseMethod.DeclaringType)!)); break; } } } else { - AnnotateMethods (@override, method); + AnnotateMethods (baseMethod, method); } } } @@ -300,12 +299,12 @@ void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, Interf // Note that this returns a list to potentially cover the diamond case (more than one // most specific implementation of the given interface methods). ILLink needs to preserve // all the implementations so that the proper exception can be thrown at runtime. - void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplementsInterface, TypeDefinition typeThatMayHaveDimProvidingInterface, MethodDefinition interfaceMethod) + void FindAndAddDefaultInterfaceImplementations (TypeDefinition type, MethodDefinition interfaceMethod) { // Go over all interfaces, trying to find a method that is an explicit MethodImpl of the // interface method in question. - foreach (var interfaceImpl in typeThatMayHaveDimProvidingInterface.Interfaces) { + foreach (var interfaceImpl in type.Interfaces) { var potentialImplInterface = context.TryResolve (interfaceImpl.InterfaceType); if (potentialImplInterface == null) continue; @@ -315,7 +314,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement foreach (var potentialImplMethod in potentialImplInterface.Methods) { if (potentialImplMethod == interfaceMethod && !potentialImplMethod.IsAbstract) { - AddDefaultInterfaceImplementation (interfaceMethod, new (typeThatImplementsInterface, interfaceImpl, context.Resolve(interfaceImpl.InterfaceType)), potentialImplMethod); + AddDefaultInterfaceImplementation (interfaceMethod, new (type, interfaceImpl, context.Resolve(interfaceMethod.DeclaringType)!), potentialImplMethod); foundImpl = true; break; } @@ -324,9 +323,9 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement continue; // 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, new (typeThatImplementsInterface, interfaceImpl, context.Resolve(interfaceImpl.InterfaceType)), @potentialImplMethod); + foreach (var baseMethod in potentialImplMethod.Overrides) { + if (context.TryResolve (baseMethod) == interfaceMethod) { + AddDefaultInterfaceImplementation (interfaceMethod, new (type, interfaceImpl, context.Resolve(interfaceMethod.DeclaringType)!), @potentialImplMethod); foundImpl = true; break; } @@ -340,7 +339,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement // 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, interfaceMethod); + FindAndAddDefaultInterfaceImplementations (potentialImplInterface, interfaceMethod); } } } From 56f23e4f2b123bd991ebf4de0b9abd3993c1be12 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 21 Feb 2024 15:38:47 -0800 Subject: [PATCH 16/34] Don't need to resolve TypeDefinitions --- src/tools/illink/src/linker/Linker/TypeMapInfo.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index a2915ab1a8c8a..ecefaf901d965 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -227,9 +227,9 @@ void MapOverrides (MethodDefinition method) // Find the matching interface implementation if the base is an interface method foreach (var iface in method.DeclaringType.Interfaces) { - if (context.TryResolve (iface.InterfaceType) == baseMethod.DeclaringType) + if (context.TryResolve (iface.InterfaceType) == baseMethod.DeclaringType) { - AnnotateMethods (baseMethod, method, new InterfaceImplementor (method.DeclaringType, iface, context.Resolve (baseMethod.DeclaringType)!)); + AnnotateMethods (baseMethod, method, new InterfaceImplementor (method.DeclaringType, iface, baseMethod.DeclaringType)); break; } } @@ -314,7 +314,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition type, MethodDefin foreach (var potentialImplMethod in potentialImplInterface.Methods) { if (potentialImplMethod == interfaceMethod && !potentialImplMethod.IsAbstract) { - AddDefaultInterfaceImplementation (interfaceMethod, new (type, interfaceImpl, context.Resolve(interfaceMethod.DeclaringType)!), potentialImplMethod); + AddDefaultInterfaceImplementation (interfaceMethod, new (type, interfaceImpl, interfaceMethod.DeclaringType), potentialImplMethod); foundImpl = true; break; } @@ -325,7 +325,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 baseMethod in potentialImplMethod.Overrides) { if (context.TryResolve (baseMethod) == interfaceMethod) { - AddDefaultInterfaceImplementation (interfaceMethod, new (type, interfaceImpl, context.Resolve(interfaceMethod.DeclaringType)!), @potentialImplMethod); + AddDefaultInterfaceImplementation (interfaceMethod, new (type, interfaceImpl, interfaceMethod.DeclaringType), @potentialImplMethod); foundImpl = true; break; } From 1d1e88305bd40fe90cf877f50da154d4e44b2779 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 22 Feb 2024 10:04:43 -0800 Subject: [PATCH 17/34] Use correct InterfaceImpl --- src/tools/illink/illink.sln | 1 + src/tools/illink/src/linker/Linker/TypeMapInfo.cs | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/tools/illink/illink.sln b/src/tools/illink/illink.sln index 87e99d208c7fb..9b8d904942baa 100644 --- a/src/tools/illink/illink.sln +++ b/src/tools/illink/illink.sln @@ -223,6 +223,7 @@ Global SolutionGuid = {E43A3901-42B0-48CA-BB36-5CD40A99A6EE} EndGlobalSection GlobalSection(SharedMSBuildProjectFiles) = preSolution + test\Trimming.Tests.Shared\Trimming.Tests.Shared.projitems*{400a1561-b6b6-482d-9e4c-3ddaede5bd07}*SharedItemsImports = 5 src\ILLink.Shared\ILLink.Shared.projitems*{dd28e2b1-057b-4b4d-a04d-b2ebd9e76e46}*SharedItemsImports = 5 src\ILLink.Shared\ILLink.Shared.projitems*{f1a44a78-34ee-408b-8285-9a26f0e7d4f2}*SharedItemsImports = 5 src\ILLink.Shared\ILLink.Shared.projitems*{ff598e93-8e9e-4091-9f50-61a7572663ae}*SharedItemsImports = 13 diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index f0466b670821b..0f6b206633ee9 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -168,14 +168,14 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) // Try to find an implementation with a name/sig match on the current type MethodDefinition? exactMatchOnType = TryMatchMethod (type, interfaceMethod); if (exactMatchOnType != null) { - AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType, new (type, interfaceImpl.OriginalImpl, context.Resolve(interfaceImpl.OriginalImpl.InterfaceType)!)); + AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType, new (type, interfaceImpl.OriginalImpl, resolvedInterfaceMethod.DeclaringType)); 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, context.Resolve(interfaceImpl.OriginalImpl.InterfaceType)!)); + AnnotateMethods (resolvedInterfaceMethod, @base, new (type, interfaceImpl.OriginalImpl, resolvedInterfaceMethod.DeclaringType)); continue; } } @@ -306,7 +306,7 @@ void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, Interf /// /// The InterfaceImplementation on that points to the DeclaringType of . /// - void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplementsInterface, TypeDefinition typeThatMayHaveDIM, MethodDefinition interfaceMethodToBeImplemented) + void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplementsInterface, TypeDefinition typeThatMayHaveDIM, MethodDefinition interfaceMethodToBeImplemented, InterfaceImplementation originalInterfaceImpl) { // Go over all interfaces, trying to find a method that is an explicit MethodImpl of the // interface method in question. @@ -321,7 +321,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement foreach (var potentialImplMethod in potentialImplInterface.Methods) { if (potentialImplMethod == interfaceMethodToBeImplemented && !potentialImplMethod.IsAbstract) { - AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, interfaceImpl, interfaceMethodToBeImplemented.DeclaringType), potentialImplMethod); + AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl, interfaceMethodToBeImplemented.DeclaringType), potentialImplMethod); foundImpl = true; break; } @@ -332,7 +332,7 @@ 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, interfaceImpl, interfaceMethodToBeImplemented.DeclaringType), @potentialImplMethod); + AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl, interfaceMethodToBeImplemented.DeclaringType), @potentialImplMethod); foundImpl = true; break; } @@ -346,7 +346,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement // 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); + FindAndAddDefaultInterfaceImplementations (typeThatImplementsInterface, potentialImplInterface, interfaceMethodToBeImplemented, originalInterfaceImpl); } } } From 7b218b65b724d751fbb0be421a4e47ceb6ae1f69 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 22 Feb 2024 14:38:26 -0800 Subject: [PATCH 18/34] Add argument to first call --- src/tools/illink/src/linker/Linker/TypeMapInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 0f6b206633ee9..e7b3ee3abb63f 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -181,7 +181,7 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) } // Look for a default implementation last. - FindAndAddDefaultInterfaceImplementations (type, type, resolvedInterfaceMethod); + FindAndAddDefaultInterfaceImplementations (type, type, resolvedInterfaceMethod, interfaceImpl.OriginalImpl); } } } From 81ebc93c265a7914b580e4fb62c0c77b078c9adb Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Fri, 23 Feb 2024 15:54:20 -0800 Subject: [PATCH 19/34] Look on base types and interfaces for interfaceImpl --- .../src/linker/Linker/InterfaceImplementor.cs | 37 +++++++++++++++++++ .../illink/src/linker/Linker/TypeMapInfo.cs | 16 +++----- 2 files changed, 42 insertions(+), 11 deletions(-) diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs index 6f0a55aba9b13..3dd346504e6fd 100644 --- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs @@ -1,6 +1,9 @@ // 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; @@ -27,5 +30,39 @@ public InterfaceImplementor (TypeDefinition implementor, InterfaceImplementation InterfaceImplementation = 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); + } + } + var baseTypeRef = implementor.BaseType; + while (baseTypeRef is not null) { + var baseType = resolver.Resolve (baseTypeRef); + foreach(InterfaceImplementation iface in baseType.Interfaces) { + if (resolver.Resolve(iface.InterfaceType) == interfaceType) { + return new InterfaceImplementor(implementor, iface, interfaceType); + } + } + baseTypeRef = baseType.BaseType; + } + + Queue ifacesToCheck = new (); + ifacesToCheck.Enqueue(implementor); + while (ifacesToCheck.Count > 0) { + var myFace = ifacesToCheck.Dequeue (); + + foreach(InterfaceImplementation ifaceImpl in myFace.Interfaces) { + var iface = resolver.Resolve (ifaceImpl.InterfaceType); + if (iface == interfaceType) { + return new InterfaceImplementor(implementor, ifaceImpl, interfaceType); + } + ifacesToCheck.Enqueue (iface); + } + } + throw new InvalidOperationException ($"Type '{implementor.FullName}' does not implement interface '{interfaceType.FullName}' directly or through any base types or interfaces"); + } } } diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index e7b3ee3abb63f..5e71a2756a6a1 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -29,9 +29,11 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // +using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Linq; using Mono.Cecil; namespace Mono.Linker @@ -222,21 +224,13 @@ void MapOverrides (MethodDefinition method) MethodDefinition? baseMethod = context.TryResolve (baseMethodRef); if (baseMethod == null) continue; - if (baseMethod.DeclaringType.IsInterface) + if (!baseMethod.DeclaringType.IsInterface) { - // Find the matching interface implementation if the base is an interface method - foreach (var iface in method.DeclaringType.Interfaces) - { - if (context.TryResolve (iface.InterfaceType) == baseMethod.DeclaringType) - { - AnnotateMethods (baseMethod, method, new InterfaceImplementor (method.DeclaringType, iface, baseMethod.DeclaringType)); - break; - } - } + AnnotateMethods (baseMethod, method); } else { - AnnotateMethods (baseMethod, method); + AnnotateMethods (baseMethod, method, InterfaceImplementor.Create (method.DeclaringType, baseMethod.DeclaringType, context)); } } } From 6b2f99d9146e4fd83bb55efd0361655f816cd3fb Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 26 Feb 2024 21:54:52 -0800 Subject: [PATCH 20/34] wip --- .../src/linker/Linker.Steps/MarkStep.cs | 32 +++- .../src/linker/Linker/InterfaceImplementor.cs | 22 +-- .../src/linker/Linker/OverrideInformation.cs | 2 +- .../linker/Linker/TypeDefinitionExtensions.cs | 1 + .../illink/src/linker/Linker/TypeMapInfo.cs | 164 +++++++++++++++++- ...terfaces.DefaultInterfaceMethodsTests.g.cs | 6 + .../Inheritance.InterfacesTests.g.cs | 6 + ...mProvidedByUnreferencedIfaceInHierarchy.cs | 13 +- ...mProvidedByUnreferencedIfaceInHierarchy.cs | 12 +- 9 files changed, 220 insertions(+), 38 deletions(-) diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index 32fd98cbe039c..59e0e12a3a801 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -825,7 +825,9 @@ void ProcessDefaultImplementation (OverrideInformation ov) || ov.Override.IsStatic && !Annotations.IsRelevantToVariantCasting (ov.InterfaceImplementor.Implementor)) return; - MarkInterfaceImplementation (ov.InterfaceImplementor.InterfaceImplementation); + foreach(var ifaceImpl in ov.InterfaceImplementor.InterfaceImplementations) { + MarkInterfaceImplementation (ifaceImpl); + } } void MarkMarshalSpec (IMarshalInfoProvider spec, in DependencyInfo reason) @@ -2453,11 +2455,27 @@ void MarkInterfaceImplementations (TypeDefinition type) if (!type.HasInterfaces) return; - foreach (var iface in type.Interfaces) { - // Only mark interface implementations of interface types that have been marked. - // This enables stripping of interfaces that are never used - if (ShouldMarkInterfaceImplementation (type, iface)) - MarkInterfaceImplementation (iface, new MessageOrigin (type)); + // Should look at all recursive interfaces + Queue ifacesToVisit = new(); + foreach(var iface in type.Interfaces) ifacesToVisit.Enqueue ([iface]); + HashSet visitedTypes = new(); + while (ifacesToVisit.Count > 0) { + var ifaces = ifacesToVisit.Dequeue(); + var topInterfaceType = Context.Resolve(ifaces[0].InterfaceType); + if (topInterfaceType is not TypeDefinition resolvedInterfaceType) + continue; + + if (visitedTypes.Add (resolvedInterfaceType)) { + if (ShouldMarkInterfaceImplementation(type, ifaces[0])) + { + foreach (var iface in ifaces) + MarkInterfaceImplementation (iface, new MessageOrigin (type)); + } + if (resolvedInterfaceType.HasInterfaces) + { + foreach (var iface in resolvedInterfaceType.Interfaces) ifacesToVisit.Enqueue ([iface, ..ifaces]); + } + } } } @@ -2562,7 +2580,7 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat // 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; + InterfaceImplementation? iface = overrideInformation.MatchingInterfaceImplementation; if (!((iface is not null && Annotations.IsMarked (iface)) || IsInterfaceImplementationMarkedRecursively (method.DeclaringType, @base.DeclaringType))) return false; diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs index 3dd346504e6fd..6c14d6c173cbb 100644 --- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs @@ -5,6 +5,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using Mono.Cecil; namespace Mono.Linker @@ -18,16 +19,16 @@ public class InterfaceImplementor /// /// The .interfaceimpl on that points to /// - public InterfaceImplementation InterfaceImplementation { get; } + public InterfaceImplementation[] InterfaceImplementations { get; } /// /// The type of the interface that is implemented by /// public TypeDefinition InterfaceType { get; } - public InterfaceImplementor (TypeDefinition implementor, InterfaceImplementation interfaceImplementation, TypeDefinition interfaceType) + public InterfaceImplementor (TypeDefinition implementor, InterfaceImplementation[] interfaceImplementations, TypeDefinition interfaceType) { Implementor = implementor; - InterfaceImplementation = interfaceImplementation; + InterfaceImplementations = interfaceImplementations; InterfaceType = interfaceType; } @@ -35,7 +36,7 @@ public static InterfaceImplementor Create(TypeDefinition implementor, TypeDefini { foreach(InterfaceImplementation iface in implementor.Interfaces) { if (resolver.Resolve(iface.InterfaceType) == interfaceType) { - return new InterfaceImplementor(implementor, iface, interfaceType); + return new InterfaceImplementor(implementor, [iface], interfaceType); } } var baseTypeRef = implementor.BaseType; @@ -43,23 +44,24 @@ public static InterfaceImplementor Create(TypeDefinition implementor, TypeDefini var baseType = resolver.Resolve (baseTypeRef); foreach(InterfaceImplementation iface in baseType.Interfaces) { if (resolver.Resolve(iface.InterfaceType) == interfaceType) { - return new InterfaceImplementor(implementor, iface, interfaceType); + return new InterfaceImplementor(implementor, [iface], interfaceType); } } baseTypeRef = baseType.BaseType; } - Queue ifacesToCheck = new (); - ifacesToCheck.Enqueue(implementor); + Queue<(TypeDefinition, IEnumerable)> ifacesToCheck = new (); + ifacesToCheck.Enqueue((implementor, [])); while (ifacesToCheck.Count > 0) { - var myFace = ifacesToCheck.Dequeue (); + var (myFace, interfaceImpls) = ifacesToCheck.Dequeue (); foreach(InterfaceImplementation ifaceImpl in myFace.Interfaces) { var iface = resolver.Resolve (ifaceImpl.InterfaceType); if (iface == interfaceType) { - return new InterfaceImplementor(implementor, ifaceImpl, interfaceType); + + return new InterfaceImplementor(implementor, interfaceImpls.Append(ifaceImpl).ToArray(), interfaceType); } - ifacesToCheck.Enqueue (iface); + ifacesToCheck.Enqueue ((iface, interfaceImpls.Append(ifaceImpl))); } } throw new InvalidOperationException ($"Type '{implementor.FullName}' does not implement interface '{interfaceType.FullName}' directly or through any base types or interfaces"); diff --git a/src/tools/illink/src/linker/Linker/OverrideInformation.cs b/src/tools/illink/src/linker/Linker/OverrideInformation.cs index 0727d5d25c19a..46275615e9423 100644 --- a/src/tools/illink/src/linker/Linker/OverrideInformation.cs +++ b/src/tools/illink/src/linker/Linker/OverrideInformation.cs @@ -29,7 +29,7 @@ internal OverrideInformation (MethodDefinition @base, MethodDefinition @override } public InterfaceImplementation? MatchingInterfaceImplementation - => InterfaceImplementor?.InterfaceImplementation; + => InterfaceImplementor?.InterfaceImplementations[^1]; public TypeDefinition? InterfaceType => InterfaceImplementor?.InterfaceType; diff --git a/src/tools/illink/src/linker/Linker/TypeDefinitionExtensions.cs b/src/tools/illink/src/linker/Linker/TypeDefinitionExtensions.cs index 939c8f5266854..ead37f1bb9b57 100644 --- a/src/tools/illink/src/linker/Linker/TypeDefinitionExtensions.cs +++ b/src/tools/illink/src/linker/Linker/TypeDefinitionExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Collections.Generic; using Mono.Cecil; namespace Mono.Linker diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 5e71a2756a6a1..e2c327dd9695c 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -46,6 +46,7 @@ public class TypeMapInfo protected readonly Dictionary> base_methods = new Dictionary> (); protected readonly Dictionary> override_methods = new Dictionary> (); protected readonly Dictionary> default_interface_implementations = new Dictionary> (); + protected readonly Dictionary> interfaces = new (); public TypeMapInfo (LinkContext context) { @@ -133,6 +134,7 @@ public void AddDefaultInterfaceImplementation (MethodDefinition @base, Interface protected virtual void MapType (TypeDefinition type) { + MapInterfacesOnType (type); MapVirtualMethods (type); MapInterfaceMethodsInTypeHierarchy (type); @@ -143,6 +145,102 @@ protected virtual void MapType (TypeDefinition type) MapType (nested); } + record IFaceImplr(TypeDefinition ImplementingType, TypeDefinition InterfaceImplProvider, TypeDefinition InterfaceType, ImplNode InterfaceImplChain); + readonly Dictionary> _ifaces = new (); + record ImplNode (InterfaceImplementation Value, ImplNode? Next) + { + public InterfaceImplementation GetLast () + { + var curr = this; + while (curr.Next is not null) { + curr = curr.Next; + } + return curr.Value; + } + public InterfaceImplementation[] ToArray() + { + List builder = new(); + var curr = this; + while (curr is not null) { + builder.Add (curr.Value); + curr = curr.Next; + } + return builder.ToArray (); + } + } + protected void MapInterfacesOnType (TypeDefinition type) + { + if (_ifaces.ContainsKey (type)) + return; + + List implrs = []; + HashSet interfacesSeen = []; + foreach (var iface in type.Interfaces) { + var ifaceType = context.Resolve (iface.InterfaceType); + Debug.Assert (ifaceType is not null); + implrs.Add (new (type, type, ifaceType, new ImplNode (iface, null))); + interfacesSeen.Add (ifaceType); + } + if (type.BaseType is { } baseType && context.Resolve (baseType) is { } baseDef) { + + MapInterfacesOnType (baseDef); + var baseInterfaces = _ifaces[baseDef]; + foreach(var item in baseInterfaces) { + if (interfacesSeen.Add(item.InterfaceType)) { + implrs.Add (item with { ImplementingType = type }); + interfacesSeen.Add (item.InterfaceType); + } + } + } + // Look into all interfaces for other interfaces this type might implement + Queue typeDefinitions = new(); + foreach(var iface in implrs) { + MapInterfacesOnType (iface.InterfaceType); + var baseInterfaces = _ifaces[iface.InterfaceType]; + foreach(var item in baseInterfaces) { + if (interfacesSeen.Add(item.InterfaceType)) { + // Don't add these to the seen interfaces list - we need to keep all of these to be able to find the most specific DIMs + implrs.Add (item with { + ImplementingType = type, + InterfaceImplChain = new ImplNode (iface.InterfaceImplChain.Value, item.InterfaceImplChain) + }); + } + } + } + _ifaces.Add (type, implrs); + } + + IEqualityComparer? _inflatedInterfaceComparer; + IEqualityComparer InflatedInterfaceComparer => _inflatedInterfaceComparer ??= EqualityComparer.Create ((a, b) => { + Debug.Assert (a is not null && b is not null); + // Skip comparing generic parameters since it's difficult to compare them correctly + if (a is TypeSpecification || b is TypeSpecification) + return false; + var retval = context.Resolve (a) is { } aDef && context.Resolve (b) is { } bDef && aDef == bDef; + if (retval) + _ = 0; + return retval; + }, obj => context.Resolve(obj)?.GetHashCode() ?? 0); + + public IEnumerable<(TypeReference InflatedInterface, ImplNode OriginalImpl)> GetInflatedInterfaces (TypeReference typeRef) + { + var typeDef = context.TryResolve (typeRef); + + if (typeDef?.HasInterfaces != true) + yield break; + + if (typeRef is GenericInstanceType genericInstance) { + foreach (var interfaceImpl in _ifaces[typeDef]) { + // 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 (TypeReferenceExtensions.InflateGenericType (genericInstance, interfaceImpl.InterfaceType, context), interfaceImpl.InterfaceImplChain)!; + } + } else { + foreach (var interfaceImpl in _ifaces[typeDef]) + yield return (interfaceImpl.InterfaceType, interfaceImpl.InterfaceImplChain); + } + } + void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) { if (!type.HasInterfaces) @@ -150,7 +248,7 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) // Foreach interface and for each newslot virtual method on the interface, try // to find the method implementation and record it. - foreach (var interfaceImpl in type.GetInflatedInterfaces (context)) { + foreach (var interfaceImpl in GetInflatedInterfaces (type)) { foreach (MethodReference interfaceMethod in interfaceImpl.InflatedInterface.GetMethods (context)) { MethodDefinition? resolvedInterfaceMethod = context.TryResolve (interfaceMethod); if (resolvedInterfaceMethod == null) @@ -170,14 +268,14 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) // Try to find an implementation with a name/sig match on the current type MethodDefinition? exactMatchOnType = TryMatchMethod (type, interfaceMethod); if (exactMatchOnType != null) { - AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType, new (type, interfaceImpl.OriginalImpl, resolvedInterfaceMethod.DeclaringType)); + AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType, new (type, interfaceImpl.OriginalImpl.ToArray(), resolvedInterfaceMethod.DeclaringType)); 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)); + AnnotateMethods (resolvedInterfaceMethod, @base, new (type, interfaceImpl.OriginalImpl.ToArray(), resolvedInterfaceMethod.DeclaringType)); continue; } } @@ -186,6 +284,56 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) FindAndAddDefaultInterfaceImplementations (type, type, resolvedInterfaceMethod, interfaceImpl.OriginalImpl); } } + Queue<(TypeReference InflatedInterface, InterfaceImplementation OriginalImpl)> ifacesToVisit = new (type.GetInflatedInterfaces(context)); + + HashSet visitedIfaces = new([], InflatedInterfaceComparer); + + List interfaceImplementations = new (16); + while (ifacesToVisit.Count > 0) { + var interfaceImpl = ifacesToVisit.Dequeue (); + foreach(var iface in interfaceImpl.InflatedInterface.GetInflatedInterfaces (context)) { + ifacesToVisit.Enqueue (iface); + } + if (!visitedIfaces.Add (interfaceImpl.InflatedInterface)) + continue; + + foreach (MethodReference interfaceMethod in interfaceImpl.InflatedInterface.GetMethods (context)) { + MethodDefinition? resolvedInterfaceMethod = context.TryResolve (interfaceMethod); + if (resolvedInterfaceMethod == null) + continue; + + // 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. + + if (!resolvedInterfaceMethod.IsVirtual + || resolvedInterfaceMethod.IsFinal) + continue; + + // 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)); + 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)); + continue; + } + } + + // Look for a default implementation last. + interfaceImplementations.Clear(); + interfaceImplementations.Add (interfaceImpl.OriginalImpl); + FindAndAddDefaultInterfaceImplementations (type, type, resolvedInterfaceMethod, [interfaceImpl.OriginalImpl]); + } + } } void MapVirtualMethods (TypeDefinition type) @@ -300,11 +448,10 @@ void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, Interf /// /// The InterfaceImplementation on that points to the DeclaringType of . /// - void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplementsInterface, TypeDefinition typeThatMayHaveDIM, MethodDefinition interfaceMethodToBeImplemented, InterfaceImplementation originalInterfaceImpl) + void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplementsInterface, TypeDefinition typeThatMayHaveDIM, MethodDefinition interfaceMethodToBeImplemented, List originalInterfaceImpl) { // Go over all interfaces, trying to find a method that is an explicit MethodImpl of the // interface method in question. - foreach (var interfaceImpl in typeThatMayHaveDIM.Interfaces) { var potentialImplInterface = context.TryResolve (interfaceImpl.InterfaceType); if (potentialImplInterface == null) @@ -315,7 +462,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement foreach (var potentialImplMethod in potentialImplInterface.Methods) { if (potentialImplMethod == interfaceMethodToBeImplemented && !potentialImplMethod.IsAbstract) { - AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl, interfaceMethodToBeImplemented.DeclaringType), potentialImplMethod); + AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl.ToArray(), interfaceMethodToBeImplemented.DeclaringType), potentialImplMethod); foundImpl = true; break; } @@ -326,7 +473,7 @@ 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), @potentialImplMethod); + AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl.ToArray(), interfaceMethodToBeImplemented.DeclaringType), @potentialImplMethod); foundImpl = true; break; } @@ -340,7 +487,10 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement // We haven't found a MethodImpl on the current interface, but one of the interfaces // this interface requires could still provide it. if (!foundImpl) { + var oldLength = originalInterfaceImpl.Count; + originalInterfaceImpl.Add (interfaceImpl); FindAndAddDefaultInterfaceImplementations (typeThatImplementsInterface, potentialImplInterface, interfaceMethodToBeImplemented, originalInterfaceImpl); + originalInterfaceImpl.RemoveRange(oldLength, originalInterfaceImpl.Count - oldLength); } } } 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 f89602a195055..bdb96c8cfd9df 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 @@ -57,6 +57,12 @@ public Task StaticDefaultInterfaceMethodOnStruct () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task StaticDimProvidedByUnreferencedIfaceInHierarchy () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task UnusedDefaultInterfaceImplementation () { diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs index 2e1a2bbcb3454..6d21b3563420a 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs @@ -15,6 +15,12 @@ public Task CanDisableUnusedInterfaces () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task InterfaceImplementedRecursively () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task InterfaceOnUninstantiatedTypeRemoved () { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByUnreferencedIfaceInHierarchy.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByUnreferencedIfaceInHierarchy.cs index 7b749d20d46c2..3d7c7e0fa1df1 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByUnreferencedIfaceInHierarchy.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByUnreferencedIfaceInHierarchy.cs @@ -13,13 +13,12 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods #if IL_ASSEMBLY_AVAILABLE [KeptMemberInAssembly ("library.dll", typeof(Program.IFoo), "Method()")] - // https://github.com/dotnet/runtime/issues/98536 - // [KeptTypeInAssembly ("library.dll", typeof(Program.IBar))] - // [KeptMemberInAssembly ("library.dll", typeof(Program.IBar), "Program.IFoo.Method()")] - // [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IBar), "library.dll", typeof (Program.IFoo))] - // [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.MyFoo), "library.dll", typeof (Program.IBaz))] - // [KeptTypeInAssembly ("library.dll", typeof(Program.IBaz))] - // [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IBaz), "library.dll", typeof (Program.IBar))] + [KeptTypeInAssembly ("library.dll", typeof(Program.IBar))] + [KeptMemberInAssembly ("library.dll", typeof(Program.IBar), "Program.IFoo.Method()")] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IBar), "library.dll", typeof (Program.IFoo))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.MyFoo), "library.dll", typeof (Program.IBaz))] + [KeptTypeInAssembly ("library.dll", typeof(Program.IBaz))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IBaz), "library.dll", typeof (Program.IBar))] [KeptMemberInAssembly ("library.dll", typeof(Program), "CallMethod(Program/IFoo)")] #endif class DimProvidedByUnreferencedIfaceInHierarchy diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs index 1cb85f6c0e0c4..afa6f3898a97b 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs @@ -17,12 +17,12 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods [KeptMemberInAssembly ("library.dll", typeof(Program.IBase), "Method()")] [KeptTypeInAssembly ("library.dll", typeof(Program.I4))] // https://github.com/dotnet/runtime/issues/98536 - // [KeptTypeInAssembly ("library.dll", typeof(Program.I2))] - // [KeptMemberInAssembly ("library.dll", typeof(Program.I2), "Program.IBase.Method()")] - // [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I2), "library.dll", typeof (Program.IBase))] - // [KeptTypeInAssembly ("library.dll", typeof(Program.I3))] - // [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I3), "library.dll", typeof (Program.I2))] - // [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I4), "library.dll", typeof (Program.I3))] + [KeptTypeInAssembly ("library.dll", typeof(Program.I2))] + [KeptMemberInAssembly ("library.dll", typeof(Program.I2), "Program.IBase.Method()")] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I2), "library.dll", typeof (Program.IBase))] + [KeptTypeInAssembly ("library.dll", typeof(Program.I3))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I3), "library.dll", typeof (Program.I2))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I4), "library.dll", typeof (Program.I3))] #endif class StaticDimProvidedByUnreferencedIfaceInHierarchy { From 99164ebe58df6e113b8272875cabf55649b25a4e Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 28 Feb 2024 13:14:47 -0800 Subject: [PATCH 21/34] wip --- .../src/linker/Linker.Steps/MarkStep.cs | 3 + .../illink/src/linker/Linker/Annotations.cs | 2 + .../src/linker/Linker/InterfaceImplementor.cs | 148 +++++++++++++-- .../illink/src/linker/Linker/TypeMapInfo.cs | 176 +++++++++--------- ...enericInterfaceWithMethodManyVariations.cs | 3 +- 5 files changed, 226 insertions(+), 106 deletions(-) diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index 59e0e12a3a801..7c7d24d8df1bc 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -2456,6 +2456,9 @@ void MarkInterfaceImplementations (TypeDefinition type) return; // Should look at all recursive interfaces + foreach(var iface in Annotations.GetRecusiveInterfaces(type)) { + + } Queue ifacesToVisit = new(); foreach(var iface in type.Interfaces) ifacesToVisit.Enqueue ([iface]); HashSet visitedTypes = new(); diff --git a/src/tools/illink/src/linker/Linker/Annotations.cs b/src/tools/illink/src/linker/Linker/Annotations.cs index a7b3198265e81..f2c485f393132 100644 --- a/src/tools/illink/src/linker/Linker/Annotations.cs +++ b/src/tools/illink/src/linker/Linker/Annotations.cs @@ -717,5 +717,7 @@ public void EnqueueVirtualMethod (MethodDefinition method) if (FlowAnnotations.RequiresVirtualMethodDataFlowAnalysis (method) || HasLinkerAttribute (method)) VirtualMethodsWithAnnotationsToValidate.Add (method); } + + internal IEnumerable GetRecusiveInterfaces (TypeDefinition type) => TypeMapInfo.GetRecursiveInterfaces (type); } } diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs index 6c14d6c173cbb..78b3b79d9c0a1 100644 --- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs @@ -16,55 +16,173 @@ public class InterfaceImplementor /// The type that implements . /// public TypeDefinition Implementor { get; } + /// /// The .interfaceimpl on that points to /// - public InterfaceImplementation[] InterfaceImplementations { get; } + public InterfaceImplementation[] InterfaceImplementations => InterfaceImplChain.ToArray (); + + public ImplNode InterfaceImplChain { get; } + /// /// The type of the interface that is implemented by /// - public TypeDefinition InterfaceType { get; } + public TypeDefinition? InterfaceType { get; } - public InterfaceImplementor (TypeDefinition implementor, InterfaceImplementation[] interfaceImplementations, TypeDefinition interfaceType) + public TypeReference InterfaceTypeReference => InterfaceImplChain.GetLast ().InterfaceType; + public TypeReference InflatedInterface { get; } + + public InterfaceImplementor (TypeDefinition implementor, TypeDefinition? interfaceType, TypeReference inflatedInterface, ImplNode implNode) { Implementor = implementor; - InterfaceImplementations = interfaceImplementations; InterfaceType = interfaceType; + InterfaceImplChain = implNode; + InflatedInterface = inflatedInterface; + } + + public InterfaceImplementor WithImplementor (TypeDefinition implementor) + { + return new InterfaceImplementor (implementor, InterfaceType, InterfaceImplChain); } - public static InterfaceImplementor Create(TypeDefinition implementor, TypeDefinition interfaceType, IMetadataResolver resolver) + 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); + foreach (InterfaceImplementation iface in implementor.Interfaces) { + if (resolver.Resolve (iface.InterfaceType) == interfaceType) { + return new InterfaceImplementor (implementor, [iface], interfaceType); } } var baseTypeRef = implementor.BaseType; while (baseTypeRef is not null) { var baseType = resolver.Resolve (baseTypeRef); - foreach(InterfaceImplementation iface in baseType.Interfaces) { - if (resolver.Resolve(iface.InterfaceType) == interfaceType) { - return new InterfaceImplementor(implementor, [iface], interfaceType); + foreach (InterfaceImplementation iface in baseType.Interfaces) { + if (resolver.Resolve (iface.InterfaceType) == interfaceType) { + return new InterfaceImplementor (implementor, [iface], interfaceType); } } baseTypeRef = baseType.BaseType; } Queue<(TypeDefinition, IEnumerable)> ifacesToCheck = new (); - ifacesToCheck.Enqueue((implementor, [])); + ifacesToCheck.Enqueue ((implementor, [])); while (ifacesToCheck.Count > 0) { var (myFace, interfaceImpls) = ifacesToCheck.Dequeue (); - foreach(InterfaceImplementation ifaceImpl in myFace.Interfaces) { + foreach (InterfaceImplementation ifaceImpl in myFace.Interfaces) { var iface = resolver.Resolve (ifaceImpl.InterfaceType); if (iface == interfaceType) { - return new InterfaceImplementor(implementor, interfaceImpls.Append(ifaceImpl).ToArray(), interfaceType); + return new InterfaceImplementor (implementor, interfaceImpls.Append (ifaceImpl).ToArray (), interfaceType); } - ifacesToCheck.Enqueue ((iface, interfaceImpls.Append(ifaceImpl))); + ifacesToCheck.Enqueue ((iface, interfaceImpls.Append (ifaceImpl))); } } throw new InvalidOperationException ($"Type '{implementor.FullName}' does not implement interface '{interfaceType.FullName}' directly or through any base types or interfaces"); } + + Dictionary _ifacesRecursively; + void FindMostDerivedImplsForEachInterface (TypeDefinition type) + { + + Dictionary> MostDerivedImpls = new (); + Dictionary> IsMostDerivedImplOf = new (); + // All InterfaceImplementations on this type and base type + foreach (var iface in _ifacesRecursively[type]) { + var ifaceType = iface; + foreach (var impledIface in _ifacesRecursively[ifaceType]) { + if (MostDerivedImpls.TryGetValue (impledIface, out var mostDerived)) { + // If this interface derives from all the currently most derived implementors, this interface (or the most derived implementor of it) is the new most derived + if (mostDerived.All (d => _ifacesRecursively[ifaceType].Contains (d))) { + SetAsMoreDerivedThan (impledIface, ifaceType); + } + else { + bool addToList = true; + for (int i = 0; i < MostDerivedImpls[impledIface].Count; i++) { + if (_ifacesRecursively[ifaceType].Contains(MostDerivedImpls[impledIface][i])) { + MostDerivedImpls[impledIface][i] = ifaceType; + addToList = false; + } + if (MostDerivedImpls[ifaceType].Contains (MostDerivedImpls[impledIface][i])) { + addToList = false; + break; + } + } + if (addToList) + MostDerivedImpls[impledIface].Add (ifaceType); + } + } else { + MostDerivedImpls.Add (impledIface, [ifaceType]); + } + } + } + + void SetAsMoreDerivedThan (TypeDefinition type, TypeDefinition moreDerivedType) + { + MostDerivedImpls[type] = MostDerivedImpls[moreDerivedType!]; + + if (IsMostDerivedImplOf.TryGetValue(type, out var mostDerivedImpls)) { + foreach(var lessDerivedThanType in mostDerivedImpls) { + SetAsMoreDerivedThan (lessDerivedThanType, moreDerivedType); + } + } + + List? marr; + if (!IsMostDerivedImplOf.TryGetValue (moreDerivedType!, out marr)) { + marr = []; + IsMostDerivedImplOf[moreDerivedType!] = marr; + } + marr.Add (type); + } + } + } + public sealed record ImplNode (InterfaceImplementation Value, TypeDefinition InterfaceImplementationProvider, ImplNode? Next) : IEnumerable + { + sealed class Enumerator : IEnumerator + { + public Enumerator (ImplNode original) + { + _current = original; + _original = original; + + } + ImplNode _current; + ImplNode _original; + public InterfaceImplementation Current => _current.Value; + + object IEnumerator.Current => _current.Value; + + public void Dispose () { } + public bool MoveNext () + { + if (_current.Next == null) + return false; + _current = _current.Next!; + return true; + } + + public void Reset () => _current = _original; + } + public IEnumerator GetEnumerator () => new Enumerator (this); + + public InterfaceImplementation GetLast () + { + var curr = this; + while (curr.Next is not null) { + curr = curr.Next; + } + return curr.Value; + } + public InterfaceImplementation[] ToArray () + { + List builder = new (); + var curr = this; + while (curr is not null) { + builder.Add (curr.Value); + curr = curr.Next; + } + return builder.ToArray (); + } + + IEnumerator IEnumerable.GetEnumerator () => new Enumerator (this); } } diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index e2c327dd9695c..cbf4a4f93bc53 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -30,10 +30,12 @@ // using System; +using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Runtime.CompilerServices; using Mono.Cecil; namespace Mono.Linker @@ -123,7 +125,7 @@ public void AddOverride (MethodDefinition @base, MethodDefinition @override, Int public void AddDefaultInterfaceImplementation (MethodDefinition @base, InterfaceImplementor interfaceImplementor, MethodDefinition defaultImplementationMethod) { - Debug.Assert(@base.DeclaringType.IsInterface); + Debug.Assert (@base.DeclaringType.IsInterface); if (!default_interface_implementations.TryGetValue (@base, out var implementations)) { implementations = new List (); default_interface_implementations.Add (@base, implementations); @@ -145,84 +147,55 @@ protected virtual void MapType (TypeDefinition type) MapType (nested); } - record IFaceImplr(TypeDefinition ImplementingType, TypeDefinition InterfaceImplProvider, TypeDefinition InterfaceType, ImplNode InterfaceImplChain); - readonly Dictionary> _ifaces = new (); - record ImplNode (InterfaceImplementation Value, ImplNode? Next) - { - public InterfaceImplementation GetLast () - { - var curr = this; - while (curr.Next is not null) { - curr = curr.Next; - } - return curr.Value; - } - public InterfaceImplementation[] ToArray() - { - List builder = new(); - var curr = this; - while (curr is not null) { - builder.Add (curr.Value); - curr = curr.Next; - } - return builder.ToArray (); - } - } + public sealed record IFaceImplr(TypeDefinition ImplementingType, TypeDefinition InterfaceImplProvider, TypeDefinition? InterfaceType, ImplNode InterfaceImplChain); + readonly Dictionary> _ifaces = new (); protected void MapInterfacesOnType (TypeDefinition type) { if (_ifaces.ContainsKey (type)) return; - List implrs = []; + List implrs = []; HashSet interfacesSeen = []; - foreach (var iface in type.Interfaces) { - var ifaceType = context.Resolve (iface.InterfaceType); - Debug.Assert (ifaceType is not null); - implrs.Add (new (type, type, ifaceType, new ImplNode (iface, null))); - interfacesSeen.Add (ifaceType); + + // Use GetInflatedInterfaces + foreach (var pair in GetInflatedInterfaces(type)) { + var iface = pair.OriginalImpl; + var inflatedIface = pair.InflatedInterface; + var ifaceType = context.Resolve (iface.Value.InterfaceType); + implrs.Add (new (type, ifaceType, inflatedIface, new ImplNode (type, iface, null))); + if (ifaceType is not null) + interfacesSeen.Add (ifaceType); } if (type.BaseType is { } baseType && context.Resolve (baseType) is { } baseDef) { - MapInterfacesOnType (baseDef); var baseInterfaces = _ifaces[baseDef]; - foreach(var item in baseInterfaces) { - if (interfacesSeen.Add(item.InterfaceType)) { - implrs.Add (item with { ImplementingType = type }); - interfacesSeen.Add (item.InterfaceType); + foreach (var item in baseInterfaces) { + if (item.InterfaceType is null || interfacesSeen.Add (item.InterfaceType)) { + implrs.Add (new (type, item.InterfaceType, item.InflatedInterface, item.InterfaceImplChain)); + if (item.InterfaceType is not null) + interfacesSeen.Add (item.InterfaceType); } } } // Look into all interfaces for other interfaces this type might implement - Queue typeDefinitions = new(); - foreach(var iface in implrs) { + var directImplrs = implrs.ToArray (); + foreach (var iface in directImplrs) { + if (iface.InterfaceType is null) + continue; MapInterfacesOnType (iface.InterfaceType); var baseInterfaces = _ifaces[iface.InterfaceType]; - foreach(var item in baseInterfaces) { - if (interfacesSeen.Add(item.InterfaceType)) { - // Don't add these to the seen interfaces list - we need to keep all of these to be able to find the most specific DIMs - implrs.Add (item with { - ImplementingType = type, - InterfaceImplChain = new ImplNode (iface.InterfaceImplChain.Value, item.InterfaceImplChain) - }); + foreach (var item in baseInterfaces) { + if (item.InterfaceType is null) + continue; + if (interfacesSeen.Add (item.InterfaceType)) { + implrs.Add (new InterfaceImplementor (type, item.InterfaceType, item.InflatedInterface, new ImplNode (iface.InterfaceImplChain.Value, iface.InterfaceType, item.InterfaceImplChain))); } } } _ifaces.Add (type, implrs); } - IEqualityComparer? _inflatedInterfaceComparer; - IEqualityComparer InflatedInterfaceComparer => _inflatedInterfaceComparer ??= EqualityComparer.Create ((a, b) => { - Debug.Assert (a is not null && b is not null); - // Skip comparing generic parameters since it's difficult to compare them correctly - if (a is TypeSpecification || b is TypeSpecification) - return false; - var retval = context.Resolve (a) is { } aDef && context.Resolve (b) is { } bDef && aDef == bDef; - if (retval) - _ = 0; - return retval; - }, obj => context.Resolve(obj)?.GetHashCode() ?? 0); - - public IEnumerable<(TypeReference InflatedInterface, ImplNode OriginalImpl)> GetInflatedInterfaces (TypeReference typeRef) + IEnumerable<(TypeReference InflatedInterface, ImplNode OriginalImpl)> GetInflatedInterfaces (TypeReference typeRef) { var typeDef = context.TryResolve (typeRef); @@ -233,11 +206,11 @@ protected void MapInterfacesOnType (TypeDefinition type) foreach (var interfaceImpl in _ifaces[typeDef]) { // 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 (TypeReferenceExtensions.InflateGenericType (genericInstance, interfaceImpl.InterfaceType, context), interfaceImpl.InterfaceImplChain)!; + yield return (TypeReferenceExtensions.InflateGenericType (genericInstance, interfaceImpl.InterfaceImplChain.GetLast().InterfaceType, context), interfaceImpl.InterfaceImplChain)!; } } else { foreach (var interfaceImpl in _ifaces[typeDef]) - yield return (interfaceImpl.InterfaceType, interfaceImpl.InterfaceImplChain); + yield return (interfaceImpl.InterfaceImplChain.GetLast().InterfaceType, interfaceImpl.InterfaceImplChain); } } @@ -248,8 +221,9 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) // Foreach interface and for each newslot virtual method on the interface, try // to find the method implementation and record it. - foreach (var interfaceImpl in GetInflatedInterfaces (type)) { - foreach (MethodReference interfaceMethod in interfaceImpl.InflatedInterface.GetMethods (context)) { + var allInflatedInterface = _ifaces[type]; + foreach(var interfaceImplementor in allInflatedInterface) { + foreach (MethodReference interfaceMethod in interfaceImplementor.InflatedInterface.GetMethods (context)) { MethodDefinition? resolvedInterfaceMethod = context.TryResolve (interfaceMethod); if (resolvedInterfaceMethod == null) continue; @@ -268,35 +242,22 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) // Try to find an implementation with a name/sig match on the current type MethodDefinition? exactMatchOnType = TryMatchMethod (type, interfaceMethod); if (exactMatchOnType != null) { - AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType, new (type, interfaceImpl.OriginalImpl.ToArray(), resolvedInterfaceMethod.DeclaringType)); + AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType, interfaceImplementor); continue; } // Next try to find an implementation with a name/sig match in the base hierarchy var @base = GetBaseMethodInTypeHierarchy (type, interfaceMethod); if (@base != null) { - AnnotateMethods (resolvedInterfaceMethod, @base, new (type, interfaceImpl.OriginalImpl.ToArray(), resolvedInterfaceMethod.DeclaringType)); + AnnotateMethods (resolvedInterfaceMethod, @base, interfaceImplementor); continue; } } - - // Look for a default implementation last. - FindAndAddDefaultInterfaceImplementations (type, type, resolvedInterfaceMethod, interfaceImpl.OriginalImpl); + // Look along the chain of interfaceImpls to find the closest implementing } } - Queue<(TypeReference InflatedInterface, InterfaceImplementation OriginalImpl)> ifacesToVisit = new (type.GetInflatedInterfaces(context)); - - HashSet visitedIfaces = new([], InflatedInterfaceComparer); - - List interfaceImplementations = new (16); - while (ifacesToVisit.Count > 0) { - var interfaceImpl = ifacesToVisit.Dequeue (); - foreach(var iface in interfaceImpl.InflatedInterface.GetInflatedInterfaces (context)) { - ifacesToVisit.Enqueue (iface); - } - if (!visitedIfaces.Add (interfaceImpl.InflatedInterface)) - continue; - + // Use _ifaces map to get all interfaces on the type, base types, and base interfaces + foreach (var interfaceImpl in GetInflatedInterfaces (type)) { foreach (MethodReference interfaceMethod in interfaceImpl.InflatedInterface.GetMethods (context)) { MethodDefinition? resolvedInterfaceMethod = context.TryResolve (interfaceMethod); if (resolvedInterfaceMethod == null) @@ -316,22 +277,20 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) // Try to find an implementation with a name/sig match on the current type MethodDefinition? exactMatchOnType = TryMatchMethod (type, interfaceMethod); if (exactMatchOnType != null) { - AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType, new (type, [interfaceImpl.OriginalImpl], resolvedInterfaceMethod.DeclaringType)); + AnnotateMethods (resolvedInterfaceMethod, exactMatchOnType, new (type, context.Resolve(interfaceImpl.), resolvedInterfaceMethod.DeclaringType)); 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)); + AnnotateMethods (resolvedInterfaceMethod, @base, new (type, interfaceImpl.OriginalImpl.ToArray (), resolvedInterfaceMethod.DeclaringType)); continue; } } // Look for a default implementation last. - interfaceImplementations.Clear(); - interfaceImplementations.Add (interfaceImpl.OriginalImpl); - FindAndAddDefaultInterfaceImplementations (type, type, resolvedInterfaceMethod, [interfaceImpl.OriginalImpl]); + FindAndAddDefaultInterfaceImplementations (type, resolvedInterfaceMethod, interfaceImpl.OriginalImpl); } } } @@ -361,7 +320,7 @@ void MapVirtualMethod (MethodDefinition method) if (@base == null) return; - Debug.Assert(!@base.DeclaringType.IsInterface); + Debug.Assert (!@base.DeclaringType.IsInterface); AnnotateMethods (@base, method); } @@ -448,7 +407,42 @@ void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, Interf /// /// The InterfaceImplementation on that points to the DeclaringType of . /// - void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplementsInterface, TypeDefinition typeThatMayHaveDIM, MethodDefinition interfaceMethodToBeImplemented, List originalInterfaceImpl) + void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplementsInterface, MethodDefinition interfaceMethodToBeImplemented, ImplNode originalInterfaceImpl) + { + // Can I maybe only look at types in the implNode chain? I'd need all the chains that lead to the interfaceType, but it could be possible. + bool foundImpl = false; + foreach (var iface in _ifaces[typeThatImplementsInterface]) { + if (iface.InterfaceType is null) + continue; + if (_ifaces[iface.InterfaceType].Any (implr => implr.InterfaceType == interfaceMethodToBeImplemented.DeclaringType)) { + foreach (var potentialImplMethod in iface.InterfaceType.Methods) { + if (potentialImplMethod == interfaceMethodToBeImplemented && + !potentialImplMethod.IsAbstract) { + AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl.ToArray (), interfaceMethodToBeImplemented.DeclaringType), potentialImplMethod); + foundImpl = true; + break; + } + + if (!potentialImplMethod.HasOverrides) + continue; + + // 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.ToArray (), interfaceMethodToBeImplemented.DeclaringType), @potentialImplMethod); + foundImpl = true; + break; + } + } + if (foundImpl) { + break; + } + } + } + } + } + + void Nothing (TypeDefinition typeThatImplementsInterface, TypeDefinition typeThatMayHaveDIM, MethodDefinition interfaceMethodToBeImplemented, List originalInterfaceImpl) { // Go over all interfaces, trying to find a method that is an explicit MethodImpl of the // interface method in question. @@ -462,7 +456,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement foreach (var potentialImplMethod in potentialImplInterface.Methods) { if (potentialImplMethod == interfaceMethodToBeImplemented && !potentialImplMethod.IsAbstract) { - AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl.ToArray(), interfaceMethodToBeImplemented.DeclaringType), potentialImplMethod); + AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl.ToArray (), interfaceMethodToBeImplemented.DeclaringType), potentialImplMethod); foundImpl = true; break; } @@ -473,7 +467,7 @@ 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.ToArray(), interfaceMethodToBeImplemented.DeclaringType), @potentialImplMethod); + AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl.ToArray (), interfaceMethodToBeImplemented.DeclaringType), @potentialImplMethod); foundImpl = true; break; } @@ -489,8 +483,8 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement if (!foundImpl) { var oldLength = originalInterfaceImpl.Count; originalInterfaceImpl.Add (interfaceImpl); - FindAndAddDefaultInterfaceImplementations (typeThatImplementsInterface, potentialImplInterface, interfaceMethodToBeImplemented, originalInterfaceImpl); - originalInterfaceImpl.RemoveRange(oldLength, originalInterfaceImpl.Count - oldLength); + //FindAndAddDefaultInterfaceImplementations (typeThatImplementsInterface, potentialImplInterface, interfaceMethodToBeImplemented, originalInterfaceImpl); + originalInterfaceImpl.RemoveRange (oldLength, originalInterfaceImpl.Count - oldLength); } } } @@ -653,5 +647,7 @@ static bool TypeMatch (TypeReference a, TypeReference b) return a.FullName == b.FullName; } + + internal List GetRecursiveInterfaces (TypeDefinition type) => _ifaces.TryGetValue(type, out var value) ? value : []; } } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyVariations.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyVariations.cs index 078f4b94c60ba..90faee318c52d 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyVariations.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyVariations.cs @@ -2,6 +2,7 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.BaseProvidesInterfaceMember { + [SkipILVerify] public class GenericInterfaceWithMethodManyVariations { public static void Main () @@ -68,4 +69,4 @@ class Bar { } } -} \ No newline at end of file +} From 295c099bb4757d9fe75dbe1019cc6554e2482adf Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Thu, 29 Feb 2024 07:54:58 -0800 Subject: [PATCH 22/34] wip --- .../src/linker/Linker.Steps/MarkStep.cs | 39 ++--- .../illink/src/linker/Linker/Annotations.cs | 2 +- .../src/linker/Linker/InterfaceImplementor.cs | 49 +----- .../src/linker/Linker/OverrideInformation.cs | 10 ++ .../illink/src/linker/Linker/TypeMapInfo.cs | 160 +++++------------- .../InterfaceImplementedRecursively.il | 67 ++++++++ .../InterfaceImplementedRecursively.cs | 49 ++++++ 7 files changed, 185 insertions(+), 191 deletions(-) create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedRecursively.il create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedRecursively.cs diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index 7c7d24d8df1bc..92cce4c820d95 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -2457,51 +2457,36 @@ void MarkInterfaceImplementations (TypeDefinition type) // Should look at all recursive interfaces foreach(var iface in Annotations.GetRecusiveInterfaces(type)) { - - } - Queue ifacesToVisit = new(); - foreach(var iface in type.Interfaces) ifacesToVisit.Enqueue ([iface]); - HashSet visitedTypes = new(); - while (ifacesToVisit.Count > 0) { - var ifaces = ifacesToVisit.Dequeue(); - var topInterfaceType = Context.Resolve(ifaces[0].InterfaceType); - if (topInterfaceType is not TypeDefinition resolvedInterfaceType) - continue; - - if (visitedTypes.Add (resolvedInterfaceType)) { - if (ShouldMarkInterfaceImplementation(type, ifaces[0])) + if (ShouldMarkInterfaceImplementation(iface)) + { + foreach(InterfaceImplementation interfaceImpl in iface.InterfaceImplChain) { - foreach (var iface in ifaces) - MarkInterfaceImplementation (iface, new MessageOrigin (type)); - } - if (resolvedInterfaceType.HasInterfaces) - { - foreach (var iface in resolvedInterfaceType.Interfaces) ifacesToVisit.Enqueue ([iface, ..ifaces]); + MarkInterfaceImplementation (interfaceImpl, new MessageOrigin (type)); } } } } - protected virtual bool ShouldMarkInterfaceImplementation (TypeDefinition type, InterfaceImplementation iface) + protected virtual bool ShouldMarkInterfaceImplementation (InterfaceImplementor interfaceImplementor) { - if (Annotations.IsMarked (iface)) - return false; + // if (Annotations.IsMarked (iface)) + // return false; - if (!Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, type)) + if (!Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, interfaceImplementor.Implementor)) return true; - if (Context.Resolve (iface.InterfaceType) is not TypeDefinition resolvedInterfaceType) + if (interfaceImplementor.InterfaceType is null) return false; - if (Annotations.IsMarked (resolvedInterfaceType)) + if (Annotations.IsMarked (interfaceImplementor.InterfaceType)) return true; // It's hard to know if a com or windows runtime interface will be needed from managed code alone, // so as a precaution we will mark these interfaces once the type is instantiated - if (resolvedInterfaceType.IsImport || resolvedInterfaceType.IsWindowsRuntime) + if (interfaceImplementor.InterfaceType.IsImport || interfaceImplementor.InterfaceType.IsWindowsRuntime) return true; - return IsFullyPreserved (type); + return IsFullyPreserved (interfaceImplementor.Implementor); } void MarkGenericParameterProvider (IGenericParameterProvider provider) diff --git a/src/tools/illink/src/linker/Linker/Annotations.cs b/src/tools/illink/src/linker/Linker/Annotations.cs index f2c485f393132..2b9f1c6fec81f 100644 --- a/src/tools/illink/src/linker/Linker/Annotations.cs +++ b/src/tools/illink/src/linker/Linker/Annotations.cs @@ -718,6 +718,6 @@ public void EnqueueVirtualMethod (MethodDefinition method) VirtualMethodsWithAnnotationsToValidate.Add (method); } - internal IEnumerable GetRecusiveInterfaces (TypeDefinition type) => TypeMapInfo.GetRecursiveInterfaces (type); + internal List GetRecusiveInterfaces (TypeDefinition type) => TypeMapInfo.GetRecursiveInterfaces (type); } } diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs index 78b3b79d9c0a1..527051754cdf8 100644 --- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs @@ -32,57 +32,19 @@ public class InterfaceImplementor public TypeReference InterfaceTypeReference => InterfaceImplChain.GetLast ().InterfaceType; public TypeReference InflatedInterface { get; } - public InterfaceImplementor (TypeDefinition implementor, TypeDefinition? interfaceType, TypeReference inflatedInterface, ImplNode implNode) + public InterfaceImplementor (TypeDefinition implementor, TypeDefinition? interfaceType, TypeReference inflatedInterface, ImplNode implNode, LinkContext context) { Implementor = implementor; InterfaceType = interfaceType; InterfaceImplChain = implNode; InflatedInterface = inflatedInterface; + Debug.Assert (interfaceType == context.Resolve (implNode.GetLast ().InterfaceType)); } - public InterfaceImplementor WithImplementor (TypeDefinition implementor) - { - return new InterfaceImplementor (implementor, InterfaceType, InterfaceImplChain); - } - - 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); - } - } - var baseTypeRef = implementor.BaseType; - while (baseTypeRef is not null) { - var baseType = resolver.Resolve (baseTypeRef); - foreach (InterfaceImplementation iface in baseType.Interfaces) { - if (resolver.Resolve (iface.InterfaceType) == interfaceType) { - return new InterfaceImplementor (implementor, [iface], interfaceType); - } - } - baseTypeRef = baseType.BaseType; - } - - Queue<(TypeDefinition, IEnumerable)> ifacesToCheck = new (); - ifacesToCheck.Enqueue ((implementor, [])); - while (ifacesToCheck.Count > 0) { - var (myFace, interfaceImpls) = ifacesToCheck.Dequeue (); - - foreach (InterfaceImplementation ifaceImpl in myFace.Interfaces) { - var iface = resolver.Resolve (ifaceImpl.InterfaceType); - if (iface == interfaceType) { - - return new InterfaceImplementor (implementor, interfaceImpls.Append (ifaceImpl).ToArray (), interfaceType); - } - ifacesToCheck.Enqueue ((iface, interfaceImpls.Append (ifaceImpl))); - } - } - throw new InvalidOperationException ($"Type '{implementor.FullName}' does not implement interface '{interfaceType.FullName}' directly or through any base types or interfaces"); - } - - Dictionary _ifacesRecursively; - void FindMostDerivedImplsForEachInterface (TypeDefinition type) + public void FindMostDerivedImplsForEachInterface (TypeDefinition type) { + // Should be a field with all interfaces + Dictionary _ifacesRecursively = new(); Dictionary> MostDerivedImpls = new (); Dictionary> IsMostDerivedImplOf = new (); @@ -135,6 +97,7 @@ void SetAsMoreDerivedThan (TypeDefinition type, TypeDefinition moreDerivedType) } } } + public sealed record ImplNode (InterfaceImplementation Value, TypeDefinition InterfaceImplementationProvider, ImplNode? Next) : IEnumerable { sealed class Enumerator : IEnumerator diff --git a/src/tools/illink/src/linker/Linker/OverrideInformation.cs b/src/tools/illink/src/linker/Linker/OverrideInformation.cs index 46275615e9423..9f9e1cdf083ea 100644 --- a/src/tools/illink/src/linker/Linker/OverrideInformation.cs +++ b/src/tools/illink/src/linker/Linker/OverrideInformation.cs @@ -10,10 +10,20 @@ namespace Mono.Linker [DebuggerDisplay ("{Override}")] public class OverrideInformation { + /// + /// The method that is being overridden or implemented + /// public MethodDefinition Base { get; } + /// + /// The method that overrides . For interface methods, this may be a method on a base type, or a default interface method. + /// public MethodDefinition Override { get; } + /// + /// For overrides of interface methods, includes info about the interface / implementor pair that correspong to the base / override pair + /// The may be a method on , one of it's base types, or a default interface method on one of the interfaces implemented by it. + /// internal InterfaceImplementor? InterfaceImplementor { get; } internal OverrideInformation (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null) diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index cbf4a4f93bc53..f99acfa5152b2 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -32,6 +32,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -90,6 +91,19 @@ public void EnsureProcessed (AssemblyDefinition assembly) return bases; } + public InterfaceImplementor? GetInterfaceImplementor(TypeDefinition implementor, TypeDefinition interfaceType) + { + if (!_ifaces.TryGetValue (implementor, out var implrs)) + return null; + + foreach(var iface in implrs) + { + if (iface.InterfaceType == interfaceType) + return iface; + } + return null; + } + /// /// Returns a list of all default interface methods that implement for a type. /// ImplementingType is the type that implements the interface, @@ -155,65 +169,49 @@ protected void MapInterfacesOnType (TypeDefinition type) return; List implrs = []; + // There may be many different chains of interface implementations that lead to the same interface. We should only keep one for each. HashSet interfacesSeen = []; - // Use GetInflatedInterfaces - foreach (var pair in GetInflatedInterfaces(type)) { - var iface = pair.OriginalImpl; - var inflatedIface = pair.InflatedInterface; - var ifaceType = context.Resolve (iface.Value.InterfaceType); - implrs.Add (new (type, ifaceType, inflatedIface, new ImplNode (type, iface, null))); + // Get all interfaces directly implemented by this type + foreach (var iface in type.Interfaces) { + var inflatedIface = iface.InterfaceType; + var ifaceType = context.Resolve (iface.InterfaceType); + implrs.Add (new (type, ifaceType, inflatedIface, new ImplNode (iface, type, null), context)); if (ifaceType is not null) interfacesSeen.Add (ifaceType); } + + // Add interfaces on base type with the same implementation chain if (type.BaseType is { } baseType && context.Resolve (baseType) is { } baseDef) { MapInterfacesOnType (baseDef); var baseInterfaces = _ifaces[baseDef]; foreach (var item in baseInterfaces) { if (item.InterfaceType is null || interfacesSeen.Add (item.InterfaceType)) { - implrs.Add (new (type, item.InterfaceType, item.InflatedInterface, item.InterfaceImplChain)); + implrs.Add (new (type, item.InterfaceType, item.InflatedInterface, item.InterfaceImplChain, context)); if (item.InterfaceType is not null) interfacesSeen.Add (item.InterfaceType); } } } - // Look into all interfaces for other interfaces this type might implement + // Add interfaces on base interfaces with their interfaces var directImplrs = implrs.ToArray (); foreach (var iface in directImplrs) { if (iface.InterfaceType is null) continue; MapInterfacesOnType (iface.InterfaceType); + // This would be nice if we could do a breadth-first search instead of a depth-first search. That way we could make sure we keep the shortest interfaceImpl chain to each interface. var baseInterfaces = _ifaces[iface.InterfaceType]; foreach (var item in baseInterfaces) { if (item.InterfaceType is null) continue; if (interfacesSeen.Add (item.InterfaceType)) { - implrs.Add (new InterfaceImplementor (type, item.InterfaceType, item.InflatedInterface, new ImplNode (iface.InterfaceImplChain.Value, iface.InterfaceType, item.InterfaceImplChain))); + implrs.Add (new InterfaceImplementor (type, item.InterfaceType, item.InflatedInterface, new ImplNode (iface.InterfaceImplChain.Value, iface.InterfaceType, item.InterfaceImplChain), context)); } } } _ifaces.Add (type, implrs); } - IEnumerable<(TypeReference InflatedInterface, ImplNode OriginalImpl)> GetInflatedInterfaces (TypeReference typeRef) - { - var typeDef = context.TryResolve (typeRef); - - if (typeDef?.HasInterfaces != true) - yield break; - - if (typeRef is GenericInstanceType genericInstance) { - foreach (var interfaceImpl in _ifaces[typeDef]) { - // 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 (TypeReferenceExtensions.InflateGenericType (genericInstance, interfaceImpl.InterfaceImplChain.GetLast().InterfaceType, context), interfaceImpl.InterfaceImplChain)!; - } - } else { - foreach (var interfaceImpl in _ifaces[typeDef]) - yield return (interfaceImpl.InterfaceImplChain.GetLast().InterfaceType, interfaceImpl.InterfaceImplChain); - } - } - void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) { if (!type.HasInterfaces) @@ -254,43 +252,7 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) } } // Look along the chain of interfaceImpls to find the closest implementing - } - } - // Use _ifaces map to get all interfaces on the type, base types, and base interfaces - foreach (var interfaceImpl in GetInflatedInterfaces (type)) { - foreach (MethodReference interfaceMethod in interfaceImpl.InflatedInterface.GetMethods (context)) { - MethodDefinition? resolvedInterfaceMethod = context.TryResolve (interfaceMethod); - if (resolvedInterfaceMethod == null) - continue; - - // 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. - - if (!resolvedInterfaceMethod.IsVirtual - || resolvedInterfaceMethod.IsFinal) - continue; - - // 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, context.Resolve(interfaceImpl.), resolvedInterfaceMethod.DeclaringType)); - 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.ToArray (), resolvedInterfaceMethod.DeclaringType)); - continue; - } - } - - // Look for a default implementation last. - FindAndAddDefaultInterfaceImplementations (type, resolvedInterfaceMethod, interfaceImpl.OriginalImpl); + FindAndAddDefaultInterfaceImplementations(type, resolvedInterfaceMethod, interfaceImplementor); } } } @@ -337,7 +299,9 @@ void MapOverrides (MethodDefinition method) } else { - AnnotateMethods (baseMethod, method, InterfaceImplementor.Create (method.DeclaringType, baseMethod.DeclaringType, context)); + var implr = GetInterfaceImplementor(method.DeclaringType, baseMethod.DeclaringType); + Debug.Assert(implr != null); + AnnotateMethods (baseMethod, method, implr); } } } @@ -407,56 +371,21 @@ void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, Interf /// /// The InterfaceImplementation on that points to the DeclaringType of . /// - void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplementsInterface, MethodDefinition interfaceMethodToBeImplemented, ImplNode originalInterfaceImpl) + void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplementsInterface, MethodDefinition interfaceMethodToBeImplemented, InterfaceImplementor implr) { // Can I maybe only look at types in the implNode chain? I'd need all the chains that lead to the interfaceType, but it could be possible. bool foundImpl = false; - foreach (var iface in _ifaces[typeThatImplementsInterface]) { - if (iface.InterfaceType is null) + foreach (var potentialDimProviderImplementation in _ifaces[typeThatImplementsInterface]) { + if (potentialDimProviderImplementation.InterfaceType is null) continue; - if (_ifaces[iface.InterfaceType].Any (implr => implr.InterfaceType == interfaceMethodToBeImplemented.DeclaringType)) { - foreach (var potentialImplMethod in iface.InterfaceType.Methods) { - if (potentialImplMethod == interfaceMethodToBeImplemented && - !potentialImplMethod.IsAbstract) { - AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl.ToArray (), interfaceMethodToBeImplemented.DeclaringType), potentialImplMethod); - foundImpl = true; - break; - } - - if (!potentialImplMethod.HasOverrides) - continue; - - // 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.ToArray (), interfaceMethodToBeImplemented.DeclaringType), @potentialImplMethod); - foundImpl = true; - break; - } - } - if (foundImpl) { - break; - } - } - } - } - } - - void Nothing (TypeDefinition typeThatImplementsInterface, TypeDefinition typeThatMayHaveDIM, MethodDefinition interfaceMethodToBeImplemented, List originalInterfaceImpl) - { - // Go over all interfaces, trying to find a method that is an explicit MethodImpl of the - // interface method in question. - foreach (var interfaceImpl in typeThatMayHaveDIM.Interfaces) { - var potentialImplInterface = context.TryResolve (interfaceImpl.InterfaceType); - if (potentialImplInterface == null) + // If this interface doesn't implement the interface with the method we're looking for, it can't provide a default implementation. + if (GetInterfaceImplementor(potentialDimProviderImplementation.InterfaceType, interfaceMethodToBeImplemented.DeclaringType) is null) continue; - bool foundImpl = false; - - foreach (var potentialImplMethod in potentialImplInterface.Methods) { + foreach (var potentialImplMethod in potentialDimProviderImplementation.InterfaceType.Methods) { if (potentialImplMethod == interfaceMethodToBeImplemented && !potentialImplMethod.IsAbstract) { - AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, new (typeThatImplementsInterface, originalInterfaceImpl.ToArray (), interfaceMethodToBeImplemented.DeclaringType), potentialImplMethod); + AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, implr, potentialImplMethod); foundImpl = true; break; } @@ -467,26 +396,17 @@ void Nothing (TypeDefinition typeThatImplementsInterface, TypeDefinition typeTha // 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.ToArray (), interfaceMethodToBeImplemented.DeclaringType), @potentialImplMethod); + AddDefaultInterfaceImplementation (interfaceMethodToBeImplemented, implr, @potentialImplMethod); foundImpl = true; break; } } - if (foundImpl) { break; } } - - // We haven't found a MethodImpl on the current interface, but one of the interfaces - // this interface requires could still provide it. - if (!foundImpl) { - var oldLength = originalInterfaceImpl.Count; - originalInterfaceImpl.Add (interfaceImpl); - //FindAndAddDefaultInterfaceImplementations (typeThatImplementsInterface, potentialImplInterface, interfaceMethodToBeImplemented, originalInterfaceImpl); - originalInterfaceImpl.RemoveRange (oldLength, originalInterfaceImpl.Count - oldLength); - } } + // Debug.Assert(foundImpl); } MethodDefinition? TryMatchMethod (TypeReference type, MethodReference method) @@ -648,6 +568,6 @@ static bool TypeMatch (TypeReference a, TypeReference b) return a.FullName == b.FullName; } - internal List GetRecursiveInterfaces (TypeDefinition type) => _ifaces.TryGetValue(type, out var value) ? value : []; + internal List GetRecursiveInterfaces (TypeDefinition type) => _ifaces.TryGetValue(type, out var value) ? value : []; } } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedRecursively.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedRecursively.il new file mode 100644 index 0000000000000..c1d2943997da6 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedRecursively.il @@ -0,0 +1,67 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class public auto ansi abstract sealed beforefieldinit Program + extends [System.Runtime]System.Object +{ + // Nested Types + .class interface nested public auto ansi abstract beforefieldinit IBase + { + } // end of class IBase + + .class interface nested public auto ansi abstract beforefieldinit IMiddle + implements Program/IBase + { + } // end of class IMiddle + + .class interface nested public auto ansi abstract beforefieldinit IDerived + implements Program/IMiddle + { + } // end of class IDerived + + .class nested public auto ansi beforefieldinit C + extends [System.Runtime]System.Object + implements Program/IDerived + { + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2066 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [System.Runtime]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method C::.ctor + + } // end of class C + + + // Methods + .method public hidebysig static + void Main () cil managed + { + // Method begins at RVA 0x2050 + // Code size 10 (0xa) + .maxstack 1 + .locals init ( + [0] class Program/IBase b, + [1] class Program/C c + ) + + IL_0000: nop + IL_0001: ldnull + IL_0002: stloc.0 + IL_0003: newobj instance void Program/C::.ctor() + IL_0008: stloc.1 + IL_0009: ret + } // end of method Program::Main + +} // end of class Program diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedRecursively.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedRecursively.cs new file mode 100644 index 0000000000000..8c1eb6255a81e --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedRecursively.cs @@ -0,0 +1,49 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces +{ + [SetupLinkerArgument ("--skip-unresolved", "true")] + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/InterfaceImplementedRecursively.il" })] + [SkipILVerify] +#if IL_ASSEMBLY_AVAILABLE + [KeptTypeInAssembly ("library.dll", typeof(Program.IBase))] + [KeptTypeInAssembly ("library.dll", typeof(Program.IMiddle))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IMiddle), "library.dll", typeof (Program.IBase))] + [KeptTypeInAssembly ("library.dll", typeof(Program.IDerived))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IDerived), "library.dll", typeof (Program.IMiddle))] + [KeptTypeInAssembly ("library.dll", typeof(Program.C))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.C), "library.dll", typeof (Program.IDerived))] +#endif + /// + /// This test case is to verify that the linker will keep all the metadata necessary for C to implement IBase when an interfaceImpl isn't directly on C. + /// + class InterfaceImplementedRecursively + { + public static void Main() + { + +#if IL_ASSEMBLY_AVAILABLE + Program.IBase b = null; + object c = new Program.C(); + +#endif + } + } + // interface IBase {} + // interface IMiddle : IBase {} + // interface IDerived : IMiddle {} + // class C : IDerived + // { + // } +} From a3a16dfbe28d73638678af450b881666c08a7155 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 4 Mar 2024 14:26:27 -0800 Subject: [PATCH 23/34] Passing tests --- .../src/linker/Linker.Steps/MarkStep.cs | 8 +++---- .../illink/src/linker/Linker/Annotations.cs | 2 +- .../src/linker/Linker/InterfaceImplementor.cs | 5 ++--- .../illink/src/linker/Linker/TypeMapInfo.cs | 21 +++++++++---------- .../Inheritance.InterfacesTests.g.cs | 6 ++++++ 5 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index 753e62e0c2b2c..5c5cf3d160cde 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -2452,11 +2452,11 @@ void MarkNamedProperty (TypeDefinition type, string property_name, in Dependency void MarkInterfaceImplementations (TypeDefinition type) { - if (!type.HasInterfaces) - return; + //if (!type.HasInterfaces) + //return; // Should look at all recursive interfaces - foreach(var iface in Annotations.GetRecusiveInterfaces(type)) { + foreach(var iface in Annotations.GetRecursiveInterfaces(type)) { if (ShouldMarkInterfaceImplementation(iface)) { foreach(InterfaceImplementation interfaceImpl in iface.InterfaceImplChain) @@ -2568,7 +2568,7 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat // 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; + InterfaceImplementation? iface = overrideInformation.MatchingInterfaceImplementation; if (!((iface is not null && Annotations.IsMarked (iface)) || IsInterfaceImplementationMarkedRecursively (method.DeclaringType, @base.DeclaringType))) return false; diff --git a/src/tools/illink/src/linker/Linker/Annotations.cs b/src/tools/illink/src/linker/Linker/Annotations.cs index 2b9f1c6fec81f..cc3c834142899 100644 --- a/src/tools/illink/src/linker/Linker/Annotations.cs +++ b/src/tools/illink/src/linker/Linker/Annotations.cs @@ -718,6 +718,6 @@ public void EnqueueVirtualMethod (MethodDefinition method) VirtualMethodsWithAnnotationsToValidate.Add (method); } - internal List GetRecusiveInterfaces (TypeDefinition type) => TypeMapInfo.GetRecursiveInterfaces (type); + internal List GetRecursiveInterfaces (TypeDefinition type) => TypeMapInfo.GetRecursiveInterfaces (type); } } diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs index 527051754cdf8..173a63a667830 100644 --- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs @@ -104,9 +104,8 @@ sealed class Enumerator : IEnumerator { public Enumerator (ImplNode original) { - _current = original; - _original = original; - + _current = new ImplNode (null!, null!, original); + _original = _current; } ImplNode _current; ImplNode _original; diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index edbcc72546eb1..2156cf99e183b 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -49,7 +49,7 @@ public class TypeMapInfo protected readonly Dictionary> base_methods = new Dictionary> (); protected readonly Dictionary> override_methods = new Dictionary> (); protected readonly Dictionary> default_interface_implementations = new Dictionary> (); - protected readonly Dictionary> interfaces = new (); + readonly Dictionary> _interfaces = new (); public TypeMapInfo (LinkContext context) { @@ -93,7 +93,7 @@ public void EnsureProcessed (AssemblyDefinition assembly) public InterfaceImplementor? GetInterfaceImplementor(TypeDefinition implementor, TypeDefinition interfaceType) { - if (!_ifaces.TryGetValue (implementor, out var implrs)) + if (!_interfaces.TryGetValue (implementor, out var implrs)) return null; foreach(var iface in implrs) @@ -161,15 +161,14 @@ protected virtual void MapType (TypeDefinition type) MapType (nested); } - public sealed record IFaceImplr(TypeDefinition ImplementingType, TypeDefinition InterfaceImplProvider, TypeDefinition? InterfaceType, ImplNode InterfaceImplChain); - readonly Dictionary> _ifaces = new (); protected void MapInterfacesOnType (TypeDefinition type) { - if (_ifaces.ContainsKey (type)) + if (_interfaces.ContainsKey (type)) return; List implrs = []; // There may be many different chains of interface implementations that lead to the same interface. We should only keep one for each. + // This should use TypeReferences since the interfaceImpl could point to different generic instantiations of the interface HashSet interfacesSeen = []; // Get all interfaces directly implemented by this type @@ -184,7 +183,7 @@ protected void MapInterfacesOnType (TypeDefinition type) // Add interfaces on base type with the same implementation chain if (type.BaseType is { } baseType && context.Resolve (baseType) is { } baseDef) { MapInterfacesOnType (baseDef); - var baseInterfaces = _ifaces[baseDef]; + var baseInterfaces = _interfaces[baseDef]; foreach (var item in baseInterfaces) { if (item.InterfaceType is null || interfacesSeen.Add (item.InterfaceType)) { implrs.Add (new (type, item.InterfaceType, item.InflatedInterface, item.InterfaceImplChain, context)); @@ -200,7 +199,7 @@ protected void MapInterfacesOnType (TypeDefinition type) continue; MapInterfacesOnType (iface.InterfaceType); // This would be nice if we could do a breadth-first search instead of a depth-first search. That way we could make sure we keep the shortest interfaceImpl chain to each interface. - var baseInterfaces = _ifaces[iface.InterfaceType]; + var baseInterfaces = _interfaces[iface.InterfaceType]; foreach (var item in baseInterfaces) { if (item.InterfaceType is null) continue; @@ -209,7 +208,7 @@ protected void MapInterfacesOnType (TypeDefinition type) } } } - _ifaces.Add (type, implrs); + _interfaces.Add (type, implrs); } void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) @@ -219,7 +218,7 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) // Foreach interface and for each newslot virtual method on the interface, try // to find the method implementation and record it. - var allInflatedInterface = _ifaces[type]; + var allInflatedInterface = _interfaces[type]; foreach(var interfaceImplementor in allInflatedInterface) { foreach (MethodReference interfaceMethod in interfaceImplementor.InflatedInterface.GetMethods (context)) { MethodDefinition? resolvedInterfaceMethod = context.TryResolve (interfaceMethod); @@ -375,7 +374,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement { // Can I maybe only look at types in the implNode chain? I'd need all the chains that lead to the interfaceType, but it could be possible. bool foundImpl = false; - foreach (var potentialDimProviderImplementation in _ifaces[typeThatImplementsInterface]) { + foreach (var potentialDimProviderImplementation in _interfaces[typeThatImplementsInterface]) { if (potentialDimProviderImplementation.InterfaceType is null) continue; // If this interface doesn't implement the interface with the method we're looking for, it can't provide a default implementation. @@ -574,6 +573,6 @@ static bool TypeMatch (TypeReference a, TypeReference b) return a.FullName == b.FullName; } - internal List GetRecursiveInterfaces (TypeDefinition type) => _ifaces.TryGetValue(type, out var value) ? value : []; + internal List GetRecursiveInterfaces (TypeDefinition type) => _interfaces.TryGetValue(type, out var value) ? value : []; } } diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs index 6d21b3563420a..e384bc419d06f 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs @@ -21,6 +21,12 @@ public Task InterfaceImplementedRecursively () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task InterfaceImplementedThroughBaseInterface () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task InterfaceOnUninstantiatedTypeRemoved () { From 01d6f9ccc4bfe1da1e133587c283f65449365e4b Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Tue, 5 Mar 2024 11:48:30 -0800 Subject: [PATCH 24/34] wip --- .../src/linker/Linker.Steps/MarkStep.cs | 56 +++++---- .../src/linker/Linker/InterfaceImplementor.cs | 110 +++++------------- .../src/linker/Linker/OverrideInformation.cs | 3 +- .../illink/src/linker/Linker/TypeMapInfo.cs | 38 ++++-- 4 files changed, 88 insertions(+), 119 deletions(-) diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index 5c5cf3d160cde..1164990bc945f 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -804,14 +804,20 @@ bool RequiresInterfaceRecursively (TypeDefinition typeToExamine, TypeDefinition if (typeToExamine == interfaceType) return true; - if (typeToExamine.HasInterfaces) { - foreach (var iface in typeToExamine.Interfaces) { - var resolved = Context.TryResolve (iface.InterfaceType); - if (resolved == null) - continue; - - if (RequiresInterfaceRecursively (resolved, interfaceType)) - return true; + //if (typeToExamine.HasInterfaces) { + //foreach (var iface in typeToExamine.Interfaces) { + //var resolved = Context.TryResolve (iface.InterfaceType); + //if (resolved == null) + //continue; + + //if (RequiresInterfaceRecursively (resolved, interfaceType)) + //return true; + //} + //} + + foreach (var iface in Annotations.GetRecursiveInterfaces (interfaceType)) { + if (iface.InterfaceType == interfaceType) { + return true; } } @@ -825,7 +831,7 @@ void ProcessDefaultImplementation (OverrideInformation ov) || ov.Override.IsStatic && !Annotations.IsRelevantToVariantCasting (ov.InterfaceImplementor.Implementor)) return; - foreach(var ifaceImpl in ov.InterfaceImplementor.InterfaceImplementations) { + foreach (var ifaceImpl in ov.InterfaceImplementor.InterfaceImplementationNode) { MarkInterfaceImplementation (ifaceImpl); } } @@ -2452,15 +2458,9 @@ void MarkNamedProperty (TypeDefinition type, string property_name, in Dependency void MarkInterfaceImplementations (TypeDefinition type) { - //if (!type.HasInterfaces) - //return; - - // Should look at all recursive interfaces - foreach(var iface in Annotations.GetRecursiveInterfaces(type)) { - if (ShouldMarkInterfaceImplementation(iface)) - { - foreach(InterfaceImplementation interfaceImpl in iface.InterfaceImplChain) - { + foreach (var iface in Annotations.GetRecursiveInterfaces (type)) { + if (ShouldMarkInterfaceImplementation (iface)) { + foreach (InterfaceImplementation interfaceImpl in iface.InterfaceImplementationNode) { MarkInterfaceImplementation (interfaceImpl, new MessageOrigin (type)); } } @@ -2469,8 +2469,15 @@ void MarkInterfaceImplementations (TypeDefinition type) protected virtual bool ShouldMarkInterfaceImplementation (InterfaceImplementor interfaceImplementor) { - // if (Annotations.IsMarked (iface)) - // return false; + //bool allMarked = true; + //foreach (var iface in interfaceImplementor.InterfaceImplementationNode) { + //if (!Annotations.IsMarked (iface)) { + //allMarked = false; + //break; + //} + //} + //if (allMarked) + //return false; if (!Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, interfaceImplementor.Implementor)) return true; @@ -2570,8 +2577,12 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat // A type that doesn't implement the interface isn't required to have methods that implement the interface. InterfaceImplementation? iface = overrideInformation.MatchingInterfaceImplementation; if (!((iface is not null && Annotations.IsMarked (iface)) - || IsInterfaceImplementationMarkedRecursively (method.DeclaringType, @base.DeclaringType))) + || IsInterfaceImplementationMarkedRecursively (method.DeclaringType, @base.DeclaringType))) return false; + //foreach (var iface in overrideInformation.InterfaceImplementor.InterfaceImplementationNode) { + //if (!Annotations.IsMarked (iface)) + //return false; + //} // If the interface method is not marked and the interface doesn't come from a preserved scope, do not mark the implementation method // Unmarked interface methods from link assemblies will be removed so the implementing method does not need to be kept. @@ -3770,8 +3781,7 @@ protected virtual void MarkInstruction (Instruction instruction, MethodDefinitio ScopeStack.UpdateCurrentScopeInstructionOffset (instruction.Offset); if (markForReflectionAccess) { MarkMethodVisibleToReflection (methodReference, new DependencyInfo (dependencyKind, method), ScopeStack.CurrentScope.Origin); - } - else { + } else { MarkMethod (methodReference, new DependencyInfo (dependencyKind, method), ScopeStack.CurrentScope.Origin); } break; diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs index 173a63a667830..6ab8cebe8012a 100644 --- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using Mono.Cecil; @@ -17,114 +18,65 @@ public class InterfaceImplementor /// public TypeDefinition Implementor { get; } - /// - /// The .interfaceimpl on that points to - /// - public InterfaceImplementation[] InterfaceImplementations => InterfaceImplChain.ToArray (); - - public ImplNode InterfaceImplChain { get; } - /// /// The type of the interface that is implemented by /// public TypeDefinition? InterfaceType { get; } - public TypeReference InterfaceTypeReference => InterfaceImplChain.GetLast ().InterfaceType; + public ImplNode InterfaceImplementationNode { get; } + public TypeReference InflatedInterface { get; } public InterfaceImplementor (TypeDefinition implementor, TypeDefinition? interfaceType, TypeReference inflatedInterface, ImplNode implNode, LinkContext context) { Implementor = implementor; InterfaceType = interfaceType; - InterfaceImplChain = implNode; + InterfaceImplementationNode = implNode; InflatedInterface = inflatedInterface; Debug.Assert (interfaceType == context.Resolve (implNode.GetLast ().InterfaceType)); } - - public void FindMostDerivedImplsForEachInterface (TypeDefinition type) - { - // Should be a field with all interfaces - Dictionary _ifacesRecursively = new(); - - Dictionary> MostDerivedImpls = new (); - Dictionary> IsMostDerivedImplOf = new (); - // All InterfaceImplementations on this type and base type - foreach (var iface in _ifacesRecursively[type]) { - var ifaceType = iface; - foreach (var impledIface in _ifacesRecursively[ifaceType]) { - if (MostDerivedImpls.TryGetValue (impledIface, out var mostDerived)) { - // If this interface derives from all the currently most derived implementors, this interface (or the most derived implementor of it) is the new most derived - if (mostDerived.All (d => _ifacesRecursively[ifaceType].Contains (d))) { - SetAsMoreDerivedThan (impledIface, ifaceType); - } - else { - bool addToList = true; - for (int i = 0; i < MostDerivedImpls[impledIface].Count; i++) { - if (_ifacesRecursively[ifaceType].Contains(MostDerivedImpls[impledIface][i])) { - MostDerivedImpls[impledIface][i] = ifaceType; - addToList = false; - } - if (MostDerivedImpls[ifaceType].Contains (MostDerivedImpls[impledIface][i])) { - addToList = false; - break; - } - } - if (addToList) - MostDerivedImpls[impledIface].Add (ifaceType); - } - } else { - MostDerivedImpls.Add (impledIface, [ifaceType]); - } - } - } - - void SetAsMoreDerivedThan (TypeDefinition type, TypeDefinition moreDerivedType) - { - MostDerivedImpls[type] = MostDerivedImpls[moreDerivedType!]; - - if (IsMostDerivedImplOf.TryGetValue(type, out var mostDerivedImpls)) { - foreach(var lessDerivedThanType in mostDerivedImpls) { - SetAsMoreDerivedThan (lessDerivedThanType, moreDerivedType); - } - } - - List? marr; - if (!IsMostDerivedImplOf.TryGetValue (moreDerivedType!, out marr)) { - marr = []; - IsMostDerivedImplOf[moreDerivedType!] = marr; - } - marr.Add (type); - } - } } - public sealed record ImplNode (InterfaceImplementation Value, TypeDefinition InterfaceImplementationProvider, ImplNode? Next) : IEnumerable + public sealed record ImplNode (InterfaceImplementation InterfaceImplementation, TypeDefinition InterfaceImplementationProvider, ImplNode? Next) : IEnumerable { - sealed class Enumerator : IEnumerator + public struct Enumerator : IEnumerator { + ImplNode _current; + ImplNode _original; + bool _hasBeenMovedOnce; + public Enumerator (ImplNode original) { - _current = new ImplNode (null!, null!, original); - _original = _current; + _current = original; + _original = original; + _hasBeenMovedOnce = false; } - ImplNode _current; - ImplNode _original; + public InterfaceImplementation Current => _current.Value; object IEnumerator.Current => _current.Value; public void Dispose () { } + public bool MoveNext () { - if (_current.Next == null) + if (!_hasBeenMovedOnce) { + _hasBeenMovedOnce = true; + return true; + } + if (_current is null) + throw new InvalidOperationException (); + if (_current.Next is null) return false; - _current = _current.Next!; + _current = _current.Next; return true; } public void Reset () => _current = _original; } - public IEnumerator GetEnumerator () => new Enumerator (this); + + public Enumerator GetEnumerator () => new Enumerator (this); + IEnumerator IEnumerable.GetEnumerator () => new Enumerator (this); public InterfaceImplementation GetLast () { @@ -134,16 +86,6 @@ public InterfaceImplementation GetLast () } return curr.Value; } - public InterfaceImplementation[] ToArray () - { - List builder = new (); - var curr = this; - while (curr is not null) { - builder.Add (curr.Value); - curr = curr.Next; - } - return builder.ToArray (); - } IEnumerator IEnumerable.GetEnumerator () => new Enumerator (this); } diff --git a/src/tools/illink/src/linker/Linker/OverrideInformation.cs b/src/tools/illink/src/linker/Linker/OverrideInformation.cs index 9f9e1cdf083ea..04d800a44dfb7 100644 --- a/src/tools/illink/src/linker/Linker/OverrideInformation.cs +++ b/src/tools/illink/src/linker/Linker/OverrideInformation.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using Mono.Cecil; using System.Diagnostics.CodeAnalysis; +using System.Linq; namespace Mono.Linker { @@ -39,7 +40,7 @@ internal OverrideInformation (MethodDefinition @base, MethodDefinition @override } public InterfaceImplementation? MatchingInterfaceImplementation - => InterfaceImplementor?.InterfaceImplementations[^1]; + => InterfaceImplementor?.InterfaceImplementationNode.GetLast(); public TypeDefinition? InterfaceType => InterfaceImplementor?.InterfaceType; diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 2156cf99e183b..7437217b3fa4e 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -41,6 +41,17 @@ namespace Mono.Linker { + public static class DictOfListE + { + public static void AddToList(this Dictionary> me, TKey key, TValueElement value) where TKey : notnull + { + if (!me.TryGetValue (key, out List? methods)) { + methods = new List (); + me[key] = methods; + } + methods.Add (value); + } + } public class TypeMapInfo { @@ -166,8 +177,9 @@ protected void MapInterfacesOnType (TypeDefinition type) if (_interfaces.ContainsKey (type)) return; + Dictionary> waysToImplementIface = new(); + //List<(InterfaceImplementor, string)> unresolveableInterfaceImplrs = new(); List implrs = []; - // There may be many different chains of interface implementations that lead to the same interface. We should only keep one for each. // This should use TypeReferences since the interfaceImpl could point to different generic instantiations of the interface HashSet interfacesSeen = []; @@ -175,9 +187,13 @@ protected void MapInterfacesOnType (TypeDefinition type) foreach (var iface in type.Interfaces) { var inflatedIface = iface.InterfaceType; var ifaceType = context.Resolve (iface.InterfaceType); - implrs.Add (new (type, ifaceType, inflatedIface, new ImplNode (iface, type, null), context)); - if (ifaceType is not null) + ImplNode implNode = new ImplNode (iface, type, []); + //InterfaceImplementor impl = new (type, ifaceType, inflatedIface, implNode, context); + //implrs.Add (impl); + if (ifaceType is not null) { + waysToImplementIface.AddToList (ifaceType, implNode); interfacesSeen.Add (ifaceType); + } } // Add interfaces on base type with the same implementation chain @@ -185,11 +201,11 @@ protected void MapInterfacesOnType (TypeDefinition type) MapInterfacesOnType (baseDef); var baseInterfaces = _interfaces[baseDef]; foreach (var item in baseInterfaces) { - if (item.InterfaceType is null || interfacesSeen.Add (item.InterfaceType)) { - implrs.Add (new (type, item.InterfaceType, item.InflatedInterface, item.InterfaceImplChain, context)); + //if (item.InterfaceType is null || interfacesSeen.Add (item.InterfaceType)) { + implrs.Add (new (type, item.InterfaceType, item.InflatedInterface, item.InterfaceImplementationNode, context)); if (item.InterfaceType is not null) interfacesSeen.Add (item.InterfaceType); - } + //} } } // Add interfaces on base interfaces with their interfaces @@ -203,9 +219,9 @@ protected void MapInterfacesOnType (TypeDefinition type) foreach (var item in baseInterfaces) { if (item.InterfaceType is null) continue; - if (interfacesSeen.Add (item.InterfaceType)) { - implrs.Add (new InterfaceImplementor (type, item.InterfaceType, item.InflatedInterface, new ImplNode (iface.InterfaceImplChain.Value, iface.InterfaceType, item.InterfaceImplChain), context)); - } + //if (interfacesSeen.Add (item.InterfaceType)) { + implrs.Add (new InterfaceImplementor (type, item.InterfaceType, item.InflatedInterface, new ImplNode (iface.InterfaceImplementationNode, iface.InterfaceType, item.InterfaceImplementationNode), context)); + //} } } _interfaces.Add (type, implrs); @@ -218,7 +234,7 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) // Foreach interface and for each newslot virtual method on the interface, try // to find the method implementation and record it. - var allInflatedInterface = _interfaces[type]; + var allInflatedInterface = _interfaces[type].DistinctBy(static i => i.InterfaceType); foreach(var interfaceImplementor in allInflatedInterface) { foreach (MethodReference interfaceMethod in interfaceImplementor.InflatedInterface.GetMethods (context)) { MethodDefinition? resolvedInterfaceMethod = context.TryResolve (interfaceMethod); @@ -374,7 +390,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement { // Can I maybe only look at types in the implNode chain? I'd need all the chains that lead to the interfaceType, but it could be possible. bool foundImpl = false; - foreach (var potentialDimProviderImplementation in _interfaces[typeThatImplementsInterface]) { + foreach (var potentialDimProviderImplementation in _interfaces[typeThatImplementsInterface].DistinctBy(static i => i.InterfaceType)) { if (potentialDimProviderImplementation.InterfaceType is null) continue; // If this interface doesn't implement the interface with the method we're looking for, it can't provide a default implementation. From 8996d547af35eec4e5cedeec9cb710f774017f74 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 6 Mar 2024 11:43:24 -0800 Subject: [PATCH 25/34] Passing tests --- .../src/linker/Linker.Steps/MarkStep.cs | 60 +++---- .../src/linker/Linker/InterfaceImplementor.cs | 109 ++++++++----- .../src/linker/Linker/OverrideInformation.cs | 2 +- .../illink/src/linker/Linker/TypeMapInfo.cs | 151 +++++++++++------- .../linker/Linker/TypeReferenceExtensions.cs | 1 + .../DataFlow/FeatureCheckDataFlow.cs | 2 +- .../CompilerGeneratedCodeSubstitutions.cs | 2 +- 7 files changed, 199 insertions(+), 128 deletions(-) diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index 1164990bc945f..46b022bd413da 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -36,7 +36,6 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection.Runtime.TypeParsing; -using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using ILLink.Shared; using ILLink.Shared.TrimAnalysis; @@ -804,20 +803,25 @@ bool RequiresInterfaceRecursively (TypeDefinition typeToExamine, TypeDefinition if (typeToExamine == interfaceType) return true; - //if (typeToExamine.HasInterfaces) { - //foreach (var iface in typeToExamine.Interfaces) { - //var resolved = Context.TryResolve (iface.InterfaceType); - //if (resolved == null) - //continue; - - //if (RequiresInterfaceRecursively (resolved, interfaceType)) - //return true; - //} - //} + if (typeToExamine.HasInterfaces) { + foreach (var iface in typeToExamine.Interfaces) { + var resolved = Context.TryResolve (iface.InterfaceType); + if (resolved == null) + continue; - foreach (var iface in Annotations.GetRecursiveInterfaces (interfaceType)) { - if (iface.InterfaceType == interfaceType) { - return true; + if (RequiresInterfaceRecursively (resolved, interfaceType)) { + var check = () => { + if (resolved == interfaceType) return true; + foreach (var iface in Annotations.GetRecursiveInterfaces (resolved)) { + if (iface.InterfaceType == interfaceType) { + return true; + } + } + return false; + }; + Debug.Assert (check ()); + return true; + } } } @@ -831,7 +835,7 @@ void ProcessDefaultImplementation (OverrideInformation ov) || ov.Override.IsStatic && !Annotations.IsRelevantToVariantCasting (ov.InterfaceImplementor.Implementor)) return; - foreach (var ifaceImpl in ov.InterfaceImplementor.InterfaceImplementationNode) { + foreach (var ifaceImpl in ov.InterfaceImplementor.ShortestInterfaceImplementationChain ()) { MarkInterfaceImplementation (ifaceImpl); } } @@ -2460,7 +2464,7 @@ void MarkInterfaceImplementations (TypeDefinition type) { foreach (var iface in Annotations.GetRecursiveInterfaces (type)) { if (ShouldMarkInterfaceImplementation (iface)) { - foreach (InterfaceImplementation interfaceImpl in iface.InterfaceImplementationNode) { + foreach (InterfaceImplementation interfaceImpl in iface.ShortestInterfaceImplementationChain ()) { MarkInterfaceImplementation (interfaceImpl, new MessageOrigin (type)); } } @@ -2469,14 +2473,7 @@ void MarkInterfaceImplementations (TypeDefinition type) protected virtual bool ShouldMarkInterfaceImplementation (InterfaceImplementor interfaceImplementor) { - //bool allMarked = true; - //foreach (var iface in interfaceImplementor.InterfaceImplementationNode) { - //if (!Annotations.IsMarked (iface)) { - //allMarked = false; - //break; - //} - //} - //if (allMarked) + //if (interfaceImplementor.IsMarked (Annotations)) //return false; if (!Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, interfaceImplementor.Implementor)) @@ -2575,14 +2572,17 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat // 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; - if (!((iface is not null && Annotations.IsMarked (iface)) - || IsInterfaceImplementationMarkedRecursively (method.DeclaringType, @base.DeclaringType))) - return false; - //foreach (var iface in overrideInformation.InterfaceImplementor.InterfaceImplementationNode) { - //if (!Annotations.IsMarked (iface)) + //InterfaceImplementation? iface = overrideInformation.MatchingInterfaceImplementation; + //if (!((iface is not null && Annotations.IsMarked (iface)) + //|| IsInterfaceImplementationMarkedRecursively (method.DeclaringType, @base.DeclaringType))) { + //Debug.Assert (!overrideInformation.InterfaceImplementor.IsMarked (Annotations)); //return false; //} + if (!overrideInformation.InterfaceImplementor.IsMarked(Annotations)) { + //Debug.Assert (!IsInterfaceImplementationMarkedRecursively (method.DeclaringType, @base.DeclaringType)); + return false; + } + //Debug.Assert (overrideInformation.InterfaceImplementor.IsMarked (Annotations)); // If the interface method is not marked and the interface doesn't come from a preserved scope, do not mark the implementation method // Unmarked interface methods from link assemblies will be removed so the implementing method does not need to be kept. diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs index 6ab8cebe8012a..2d297f6f35d26 100644 --- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs @@ -7,6 +7,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Transactions; using Mono.Cecil; namespace Mono.Linker @@ -23,70 +24,100 @@ public class InterfaceImplementor /// public TypeDefinition? InterfaceType { get; } - public ImplNode InterfaceImplementationNode { get; } + public ImplNode[] InterfaceImplementationNode { get; } public TypeReference InflatedInterface { get; } - public InterfaceImplementor (TypeDefinition implementor, TypeDefinition? interfaceType, TypeReference inflatedInterface, ImplNode implNode, LinkContext context) + public InterfaceImplementor (TypeDefinition implementor, TypeDefinition? interfaceType, TypeReference inflatedInterface, ImplNode[] implNode, LinkContext context) { Implementor = implementor; InterfaceType = interfaceType; InterfaceImplementationNode = implNode; InflatedInterface = inflatedInterface; - Debug.Assert (interfaceType == context.Resolve (implNode.GetLast ().InterfaceType)); + Debug.Assert (context.Resolve (inflatedInterface) == interfaceType); + Debug.Assert (implNode.Length != 0); + Debug.Assert (implNode.All (i => interfaceType == context.Resolve (i.GetLast ().InterfaceType))); + // Ensure the ImplNode is sorted by Length + Debug.Assert ( + implNode.Aggregate( + (true, 0), + (acc, next) => { + if (!acc.Item1) return acc; + if (acc.Item2 <= next.Length) + return (true, next.Length); + return (false, next.Length); + }) + .Item1); } - } - public sealed record ImplNode (InterfaceImplementation InterfaceImplementation, TypeDefinition InterfaceImplementationProvider, ImplNode? Next) : IEnumerable - { - public struct Enumerator : IEnumerator + public IEnumerable ShortestInterfaceImplementationChain() + { + var curr = InterfaceImplementationNode[0]; + yield return curr.InterfaceImplementation; + while (curr.Next.Length != 0) { + curr = curr.Next[0]; + yield return curr.InterfaceImplementation; + } + } + public void MarkShortestImplementation(AnnotationStore annotations, in DependencyInfo reason, in MessageOrigin origin) { - ImplNode _current; - ImplNode _original; - bool _hasBeenMovedOnce; + InterfaceImplementationNode[0].MarkShortestImplementation (annotations, reason, origin); + } + public bool IsMarked(AnnotationStore annotations) + { + foreach(var i in InterfaceImplementationNode) { + if (i.IsMarked(annotations)) + return true; + } + return false; + } + } - public Enumerator (ImplNode original) - { - _current = original; - _original = original; - _hasBeenMovedOnce = false; + public sealed record ImplNode (InterfaceImplementation InterfaceImplementation, TypeDefinition InterfaceImplementationProvider, ImmutableArray Next) + { + int _length = -1; + public int Length { + get { + if (_length != -1) + return _length; + if (Next.Length == 0) + return _length = 0; + return _length = Next[0].Length + 1; } + } - public InterfaceImplementation Current => _current.Value; + public void MarkShortestImplementation (AnnotationStore annotations, in DependencyInfo reason, in MessageOrigin origin) + { + ImplNode curr = this; + annotations.Mark (curr.InterfaceImplementation, reason, origin); + while (curr.Next.Length > 0) { + curr = curr.Next[0]; + annotations.Mark (curr.InterfaceImplementation, reason, origin); + } + } - object IEnumerator.Current => _current.Value; + public bool IsMarked (AnnotationStore annotations) + { + if (!annotations.IsMarked (InterfaceImplementation)) + return false; - public void Dispose () { } + if (Next.Length == 0) + return true; - public bool MoveNext () - { - if (!_hasBeenMovedOnce) { - _hasBeenMovedOnce = true; + foreach(var impl in Next) { + if (impl.IsMarked (annotations)) return true; - } - if (_current is null) - throw new InvalidOperationException (); - if (_current.Next is null) - return false; - _current = _current.Next; - return true; } - - public void Reset () => _current = _original; + return false; } - public Enumerator GetEnumerator () => new Enumerator (this); - IEnumerator IEnumerable.GetEnumerator () => new Enumerator (this); - public InterfaceImplementation GetLast () { var curr = this; - while (curr.Next is not null) { - curr = curr.Next; + while (curr.Next.Length > 0) { + curr = curr.Next[0]; } - return curr.Value; + return curr.InterfaceImplementation; } - - IEnumerator IEnumerable.GetEnumerator () => new Enumerator (this); } } diff --git a/src/tools/illink/src/linker/Linker/OverrideInformation.cs b/src/tools/illink/src/linker/Linker/OverrideInformation.cs index 04d800a44dfb7..8e0b421ef33c0 100644 --- a/src/tools/illink/src/linker/Linker/OverrideInformation.cs +++ b/src/tools/illink/src/linker/Linker/OverrideInformation.cs @@ -40,7 +40,7 @@ internal OverrideInformation (MethodDefinition @base, MethodDefinition @override } public InterfaceImplementation? MatchingInterfaceImplementation - => InterfaceImplementor?.InterfaceImplementationNode.GetLast(); + => InterfaceImplementor?.ShortestInterfaceImplementationChain().Last(); public TypeDefinition? InterfaceType => InterfaceImplementor?.InterfaceType; diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 7437217b3fa4e..3a5d093e15edc 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -43,7 +43,7 @@ namespace Mono.Linker { public static class DictOfListE { - public static void AddToList(this Dictionary> me, TKey key, TValueElement value) where TKey : notnull + public static void AddToList (this Dictionary> me, TKey key, TValueElement value) where TKey : notnull { if (!me.TryGetValue (key, out List? methods)) { methods = new List (); @@ -102,13 +102,12 @@ public void EnsureProcessed (AssemblyDefinition assembly) return bases; } - public InterfaceImplementor? GetInterfaceImplementor(TypeDefinition implementor, TypeDefinition interfaceType) + public InterfaceImplementor? GetInterfaceImplementor (TypeDefinition implementor, TypeDefinition interfaceType) { if (!_interfaces.TryGetValue (implementor, out var implrs)) return null; - foreach(var iface in implrs) - { + foreach (var iface in implrs) { if (iface.InterfaceType == interfaceType) return iface; } @@ -172,27 +171,90 @@ protected virtual void MapType (TypeDefinition type) MapType (nested); } + public static bool InterfaceTypeEquals (TypeReference? type, TypeReference? other, ITryResolveMetadata resolver) + { + GenericInstanceType? genericInstanceType = other as GenericInstanceType; + _ = genericInstanceType; + //var asdf = TypeReferenceExtensions.InflateGenericType (genericInstanceType, type, resolver); + + Debug.Assert (type is not null && other is not null); + if (type == other) + return true; + + if (resolver.TryResolve (type) != resolver.TryResolve (other)) + return false; + + if (type is GenericInstanceType genericInstance1) { + if (other is not GenericInstanceType genericInstance2) + return false; + if (genericInstance1.HasGenericParameters != genericInstance2.HasGenericParameters) + return false; + if (genericInstance1.GenericParameters.Count != genericInstance2.GenericParameters.Count + || genericInstance2.GenericArguments.Count != genericInstance2.GenericArguments.Count) + return false; + for (var i = 0; i < genericInstance1.GenericArguments.Count; ++i) { + if (!InterfaceTypeEquals (genericInstance1.GenericArguments[i], genericInstance2.GenericArguments[i], resolver)) + return false; + } + return true; + } + + if (type is TypeSpecification typeSpec1) { + if (other is not TypeSpecification typeSpec2) + return false; + return InterfaceTypeEquals (typeSpec1.ElementType, typeSpec2.ElementType, resolver); + } + + return true; + } + TypeReference? TryInflateType(TypeReference type, TypeReference genericProvider) + { + if (genericProvider is GenericInstanceType git) + return TypeReferenceExtensions.InflateGenericType (git, type, context); + return type; + } protected void MapInterfacesOnType (TypeDefinition type) { if (_interfaces.ContainsKey (type)) return; - Dictionary> waysToImplementIface = new(); - //List<(InterfaceImplementor, string)> unresolveableInterfaceImplrs = new(); - List implrs = []; - // This should use TypeReferences since the interfaceImpl could point to different generic instantiations of the interface - HashSet interfacesSeen = []; + Dictionary> waysToImplementIface = new (EqualityComparer.Create ((t, other) => InterfaceTypeEquals (t, other, context), t => context.Resolve (t)?.GetHashCode () ?? 0)); + Dictionary> unresolvedImpls = new (); // Get all interfaces directly implemented by this type foreach (var iface in type.Interfaces) { - var inflatedIface = iface.InterfaceType; - var ifaceType = context.Resolve (iface.InterfaceType); + var ifaceDirectlyOnType = context.Resolve (iface.InterfaceType); ImplNode implNode = new ImplNode (iface, type, []); - //InterfaceImplementor impl = new (type, ifaceType, inflatedIface, implNode, context); - //implrs.Add (impl); - if (ifaceType is not null) { - waysToImplementIface.AddToList (ifaceType, implNode); - interfacesSeen.Add (ifaceType); + if (ifaceDirectlyOnType is null) { + unresolvedImpls.AddToList (iface.InterfaceType.FullName, implNode); + continue; + } + waysToImplementIface.AddToList (iface.InterfaceType, implNode); + + MapInterfacesOnType (ifaceDirectlyOnType); + var recursiveInterfaces = _interfaces[ifaceDirectlyOnType]; + foreach (var recursiveInterface in recursiveInterfaces) { + if (recursiveInterface.InterfaceType is null) { + unresolvedImpls.AddToList (recursiveInterface.InflatedInterface.FullName, implNode); + continue; + } + var ifaceImplr = new ImplNode (iface, type, recursiveInterface.InterfaceImplementationNode.ToImmutableArray ()); + // inflate interface type reference here with the type + + TypeReference inflatedIface = TryInflateType (iface.InterfaceType, type)!; + if (recursiveInterface.InterfaceImplementationNode.Length > 0) { + // foreach(var node in Nodes) inflatedIface = Inflate(node.InterfaceType, inflatedIface); + var currNode = recursiveInterface.InterfaceImplementationNode[0]; + while(currNode.Next.Length > 0) { + inflatedIface = TryInflateType (currNode.InterfaceImplementation.InterfaceType, inflatedIface)!; + currNode = currNode.Next[0]; + } + // One last inflation + inflatedIface = TryInflateType (currNode.InterfaceImplementation.InterfaceType, inflatedIface)!; + } + // After inflating the first + //var inflatedIface2 = TryInflateType (recursiveInterface.InflatedInterface, iface.InterfaceType); + waysToImplementIface.AddToList (inflatedIface, ifaceImplr); } } @@ -201,29 +263,16 @@ protected void MapInterfacesOnType (TypeDefinition type) MapInterfacesOnType (baseDef); var baseInterfaces = _interfaces[baseDef]; foreach (var item in baseInterfaces) { - //if (item.InterfaceType is null || interfacesSeen.Add (item.InterfaceType)) { - implrs.Add (new (type, item.InterfaceType, item.InflatedInterface, item.InterfaceImplementationNode, context)); + foreach (var node in item.InterfaceImplementationNode) { if (item.InterfaceType is not null) - interfacesSeen.Add (item.InterfaceType); - //} - } - } - // Add interfaces on base interfaces with their interfaces - var directImplrs = implrs.ToArray (); - foreach (var iface in directImplrs) { - if (iface.InterfaceType is null) - continue; - MapInterfacesOnType (iface.InterfaceType); - // This would be nice if we could do a breadth-first search instead of a depth-first search. That way we could make sure we keep the shortest interfaceImpl chain to each interface. - var baseInterfaces = _interfaces[iface.InterfaceType]; - foreach (var item in baseInterfaces) { - if (item.InterfaceType is null) - continue; - //if (interfacesSeen.Add (item.InterfaceType)) { - implrs.Add (new InterfaceImplementor (type, item.InterfaceType, item.InflatedInterface, new ImplNode (iface.InterfaceImplementationNode, iface.InterfaceType, item.InterfaceImplementationNode), context)); - //} + waysToImplementIface.AddToList (item.InflatedInterface, node); + else + unresolvedImpls.AddToList (item.InflatedInterface.FullName, node); + } } } + List implrs = waysToImplementIface.Select (kvp => new InterfaceImplementor (type, context.Resolve(kvp.Key), kvp.Key, kvp.Value.OrderBy (i => i.Length).ToArray (), context)) + .Concat (unresolvedImpls.Select (kvp => new InterfaceImplementor (type, null, kvp.Value[0].GetLast ().InterfaceType, kvp.Value.OrderBy (i => i.Length).ToArray (), context))).ToList (); _interfaces.Add (type, implrs); } @@ -234,8 +283,8 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) // Foreach interface and for each newslot virtual method on the interface, try // to find the method implementation and record it. - var allInflatedInterface = _interfaces[type].DistinctBy(static i => i.InterfaceType); - foreach(var interfaceImplementor in allInflatedInterface) { + var allInflatedInterface = _interfaces[type]; + foreach (var interfaceImplementor in allInflatedInterface) { foreach (MethodReference interfaceMethod in interfaceImplementor.InflatedInterface.GetMethods (context)) { MethodDefinition? resolvedInterfaceMethod = context.TryResolve (interfaceMethod); if (resolvedInterfaceMethod == null) @@ -267,7 +316,7 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) } } // Look along the chain of interfaceImpls to find the closest implementing - FindAndAddDefaultInterfaceImplementations(type, resolvedInterfaceMethod, interfaceImplementor); + FindAndAddDefaultInterfaceImplementations (type, resolvedInterfaceMethod, interfaceImplementor); } } } @@ -308,14 +357,11 @@ void MapOverrides (MethodDefinition method) MethodDefinition? baseMethod = context.TryResolve (baseMethodRef); if (baseMethod == null) continue; - if (baseMethod.DeclaringType.IsInterface) - { - var implr = GetInterfaceImplementor(method.DeclaringType, baseMethod.DeclaringType); - Debug.Assert(implr != null); + if (baseMethod.DeclaringType.IsInterface) { + var implr = GetInterfaceImplementor (method.DeclaringType, baseMethod.DeclaringType); + Debug.Assert (implr != null); AnnotateMethods (baseMethod, method, implr); - } - else - { + } else { AnnotateMethods (baseMethod, method); } } @@ -390,11 +436,11 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement { // Can I maybe only look at types in the implNode chain? I'd need all the chains that lead to the interfaceType, but it could be possible. bool foundImpl = false; - foreach (var potentialDimProviderImplementation in _interfaces[typeThatImplementsInterface].DistinctBy(static i => i.InterfaceType)) { + foreach (var potentialDimProviderImplementation in _interfaces[typeThatImplementsInterface]) { if (potentialDimProviderImplementation.InterfaceType is null) continue; // If this interface doesn't implement the interface with the method we're looking for, it can't provide a default implementation. - if (GetInterfaceImplementor(potentialDimProviderImplementation.InterfaceType, interfaceMethodToBeImplemented.DeclaringType) is null) + if (GetInterfaceImplementor (potentialDimProviderImplementation.InterfaceType, interfaceMethodToBeImplemented.DeclaringType) is null) continue; foreach (var potentialImplMethod in potentialDimProviderImplementation.InterfaceType.Methods) { @@ -420,14 +466,7 @@ void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplement 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); - // } } - // Debug.Assert(foundImpl); } MethodDefinition? TryMatchMethod (TypeReference type, MethodReference method) @@ -589,6 +628,6 @@ static bool TypeMatch (TypeReference a, TypeReference b) return a.FullName == b.FullName; } - internal List GetRecursiveInterfaces (TypeDefinition type) => _interfaces.TryGetValue(type, out var value) ? value : []; + internal List GetRecursiveInterfaces (TypeDefinition type) => _interfaces.TryGetValue (type, out var value) ? value : []; } } diff --git a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs index 5092fe1158e34..5b821db9c33d3 100644 --- a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs +++ b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs @@ -116,6 +116,7 @@ void parseArrayDimensions (ArrayType at) } } + public static TypeReference? GetInflatedDeclaringType (this TypeReference type, ITryResolveMetadata resolver) { if (type.IsGenericParameter || type.IsByReference || type.IsPointer) diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs index d0d236997445e..b71cd280bdaeb 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs @@ -1155,7 +1155,7 @@ static void GuardedLocalFunction () public static void Test () { - GuardInIterator (); + foreach (var _ in GuardInIterator ()) ; StateFlowsAcrossYield (); GuardInAsync (); StateFlowsAcrossAwait (); diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/CompilerGeneratedCodeSubstitutions.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/CompilerGeneratedCodeSubstitutions.cs index 5b1edc37671ac..39f652833ca2e 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/CompilerGeneratedCodeSubstitutions.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/CompilerGeneratedCodeSubstitutions.cs @@ -174,7 +174,7 @@ static IEnumerable TestBranchWithYieldBefore () public static void Test () { - TestBranchWithNormalCall (); + foreach (var _ in TestBranchWithNormalCall ()) ; TestBranchWithYieldAfter (); TestBranchWithYieldBefore (); } From 6c51750ea9b12eb3df7cbb1b158c307898394869 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 6 Mar 2024 15:24:27 -0800 Subject: [PATCH 26/34] Make InterfaceImpl iterator a struct and unify resolved/unresolved ifaceImpls --- .../src/linker/Linker.Steps/MarkStep.cs | 4 +- .../src/linker/Linker/InterfaceImplementor.cs | 34 ++++++++--- .../src/linker/Linker/OverrideInformation.cs | 16 +++++- .../illink/src/linker/Linker/TypeMapInfo.cs | 57 +++++++++---------- .../TestCasesRunner/AssemblyChecker.cs | 2 +- 5 files changed, 70 insertions(+), 43 deletions(-) diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index 46b022bd413da..959c44495257c 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -835,7 +835,7 @@ void ProcessDefaultImplementation (OverrideInformation ov) || ov.Override.IsStatic && !Annotations.IsRelevantToVariantCasting (ov.InterfaceImplementor.Implementor)) return; - foreach (var ifaceImpl in ov.InterfaceImplementor.ShortestInterfaceImplementationChain ()) { + foreach (var ifaceImpl in ov.InterfaceImplementor.ShortestInterfaceImplementationChain) { MarkInterfaceImplementation (ifaceImpl); } } @@ -2464,7 +2464,7 @@ void MarkInterfaceImplementations (TypeDefinition type) { foreach (var iface in Annotations.GetRecursiveInterfaces (type)) { if (ShouldMarkInterfaceImplementation (iface)) { - foreach (InterfaceImplementation interfaceImpl in iface.ShortestInterfaceImplementationChain ()) { + foreach (InterfaceImplementation interfaceImpl in iface.ShortestInterfaceImplementationChain) { MarkInterfaceImplementation (interfaceImpl, new MessageOrigin (type)); } } diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs index 2d297f6f35d26..318e590d901bc 100644 --- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs @@ -7,6 +7,7 @@ using System.Collections.Immutable; using System.Diagnostics; using System.Linq; +using System.Runtime.InteropServices; using System.Transactions; using Mono.Cecil; @@ -39,7 +40,7 @@ public InterfaceImplementor (TypeDefinition implementor, TypeDefinition? interfa Debug.Assert (implNode.All (i => interfaceType == context.Resolve (i.GetLast ().InterfaceType))); // Ensure the ImplNode is sorted by Length Debug.Assert ( - implNode.Aggregate( + implNode.Aggregate ( (true, 0), (acc, next) => { if (!acc.Item1) return acc; @@ -50,19 +51,38 @@ public InterfaceImplementor (TypeDefinition implementor, TypeDefinition? interfa .Item1); } - public IEnumerable ShortestInterfaceImplementationChain() + public ShortestInterfaceImplementationChainEnumerator ShortestInterfaceImplementationChain => new (this); + + public struct ShortestInterfaceImplementationChainEnumerator { - var curr = InterfaceImplementationNode[0]; - yield return curr.InterfaceImplementation; - while (curr.Next.Length != 0) { - curr = curr.Next[0]; - yield return curr.InterfaceImplementation; + ImplNode _current; + bool _hasMoved; + public ShortestInterfaceImplementationChainEnumerator (InterfaceImplementor implementor) + { + _current = implementor.InterfaceImplementationNode[0]; + _hasMoved = false; + } + public ShortestInterfaceImplementationChainEnumerator GetEnumerator() => this; + public InterfaceImplementation Current => _current.InterfaceImplementation; + + public bool MoveNext() + { + if (!_hasMoved) { + _hasMoved = true; + return true; + } + if (_current.Next.Length == 0) + return false; + _current = _current.Next[0]; + return true; } } + public void MarkShortestImplementation(AnnotationStore annotations, in DependencyInfo reason, in MessageOrigin origin) { InterfaceImplementationNode[0].MarkShortestImplementation (annotations, reason, origin); } + public bool IsMarked(AnnotationStore annotations) { foreach(var i in InterfaceImplementationNode) { diff --git a/src/tools/illink/src/linker/Linker/OverrideInformation.cs b/src/tools/illink/src/linker/Linker/OverrideInformation.cs index 8e0b421ef33c0..a7696a6247274 100644 --- a/src/tools/illink/src/linker/Linker/OverrideInformation.cs +++ b/src/tools/illink/src/linker/Linker/OverrideInformation.cs @@ -5,6 +5,7 @@ using Mono.Cecil; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Transactions; namespace Mono.Linker { @@ -33,14 +34,23 @@ internal OverrideInformation (MethodDefinition @base, MethodDefinition @override Override = @override; InterfaceImplementor = interfaceImplementor; // Ensure we have an interface implementation if the base method is from an interface and the override method is on a class - Debug.Assert(@base.DeclaringType.IsInterface && interfaceImplementor != null + Debug.Assert (@base.DeclaringType.IsInterface && interfaceImplementor != null || !@base.DeclaringType.IsInterface && interfaceImplementor == null); // Ensure the interfaceImplementor is for the interface we expect Debug.Assert (@base.DeclaringType.IsInterface ? interfaceImplementor!.InterfaceType == @base.DeclaringType : true); } - public InterfaceImplementation? MatchingInterfaceImplementation - => InterfaceImplementor?.ShortestInterfaceImplementationChain().Last(); + public InterfaceImplementation? MatchingInterfaceImplementation { + get { + if (InterfaceImplementor is null) + return null; + InterfaceImplementation? last = null; + foreach (InterfaceImplementation curr in InterfaceImplementor!.ShortestInterfaceImplementationChain) { + last = curr; + } + return last; + } + } public TypeDefinition? InterfaceType => InterfaceImplementor?.InterfaceType; diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 3a5d093e15edc..52ab73c5b14e4 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -43,10 +43,10 @@ namespace Mono.Linker { public static class DictOfListE { - public static void AddToList (this Dictionary> me, TKey key, TValueElement value) where TKey : notnull + public static void AddToList (this Dictionary me, TKey key, TValueElement value) where TKey : notnull where TList : IList, new() { - if (!me.TryGetValue (key, out List? methods)) { - methods = new List (); + if (!me.TryGetValue (key, out TList? methods)) { + methods = new TList (); me[key] = methods; } methods.Add (value); @@ -171,17 +171,13 @@ protected virtual void MapType (TypeDefinition type) MapType (nested); } - public static bool InterfaceTypeEquals (TypeReference? type, TypeReference? other, ITryResolveMetadata resolver) + public bool InterfaceTypeEquals (TypeReference? type, TypeReference? other) { - GenericInstanceType? genericInstanceType = other as GenericInstanceType; - _ = genericInstanceType; - //var asdf = TypeReferenceExtensions.InflateGenericType (genericInstanceType, type, resolver); - Debug.Assert (type is not null && other is not null); if (type == other) return true; - if (resolver.TryResolve (type) != resolver.TryResolve (other)) + if (context.TryResolve (type) != context.TryResolve (other)) return false; if (type is GenericInstanceType genericInstance1) { @@ -193,7 +189,7 @@ public static bool InterfaceTypeEquals (TypeReference? type, TypeReference? othe || genericInstance2.GenericArguments.Count != genericInstance2.GenericArguments.Count) return false; for (var i = 0; i < genericInstance1.GenericArguments.Count; ++i) { - if (!InterfaceTypeEquals (genericInstance1.GenericArguments[i], genericInstance2.GenericArguments[i], resolver)) + if (!InterfaceTypeEquals (genericInstance1.GenericArguments[i], genericInstance2.GenericArguments[i])) return false; } return true; @@ -202,48 +198,50 @@ public static bool InterfaceTypeEquals (TypeReference? type, TypeReference? othe if (type is TypeSpecification typeSpec1) { if (other is not TypeSpecification typeSpec2) return false; - return InterfaceTypeEquals (typeSpec1.ElementType, typeSpec2.ElementType, resolver); + return InterfaceTypeEquals (typeSpec1.ElementType, typeSpec2.ElementType); } - - return true; + return type.FullName == other.FullName; } + TypeReference? TryInflateType(TypeReference type, TypeReference genericProvider) { if (genericProvider is GenericInstanceType git) return TypeReferenceExtensions.InflateGenericType (git, type, context); return type; } + + EqualityComparer? _inflatedInterfaceComparer; + EqualityComparer InflatedInterfaceComparer => _inflatedInterfaceComparer ??= EqualityComparer.Create (InterfaceTypeEquals, t => context.Resolve (t)?.GetHashCode () ?? 0); + protected void MapInterfacesOnType (TypeDefinition type) { if (_interfaces.ContainsKey (type)) return; - Dictionary> waysToImplementIface = new (EqualityComparer.Create ((t, other) => InterfaceTypeEquals (t, other, context), t => context.Resolve (t)?.GetHashCode () ?? 0)); - Dictionary> unresolvedImpls = new (); + Dictionary> waysToImplementIface = new (InflatedInterfaceComparer); + //Dictionary> unresolvedImpls = new (); // Get all interfaces directly implemented by this type foreach (var iface in type.Interfaces) { - var ifaceDirectlyOnType = context.Resolve (iface.InterfaceType); ImplNode implNode = new ImplNode (iface, type, []); + waysToImplementIface.AddToList (iface.InterfaceType, implNode); + var ifaceDirectlyOnType = context.Resolve (iface.InterfaceType); if (ifaceDirectlyOnType is null) { - unresolvedImpls.AddToList (iface.InterfaceType.FullName, implNode); continue; } - waysToImplementIface.AddToList (iface.InterfaceType, implNode); MapInterfacesOnType (ifaceDirectlyOnType); var recursiveInterfaces = _interfaces[ifaceDirectlyOnType]; foreach (var recursiveInterface in recursiveInterfaces) { - if (recursiveInterface.InterfaceType is null) { - unresolvedImpls.AddToList (recursiveInterface.InflatedInterface.FullName, implNode); - continue; - } + //if (recursiveInterface.InterfaceType is null) { + //unresolvedImpls.AddToList (recursiveInterface.InflatedInterface.FullName, implNode); + //continue; + //} var ifaceImplr = new ImplNode (iface, type, recursiveInterface.InterfaceImplementationNode.ToImmutableArray ()); - // inflate interface type reference here with the type + // inflate interface type reference here with the type TypeReference inflatedIface = TryInflateType (iface.InterfaceType, type)!; if (recursiveInterface.InterfaceImplementationNode.Length > 0) { - // foreach(var node in Nodes) inflatedIface = Inflate(node.InterfaceType, inflatedIface); var currNode = recursiveInterface.InterfaceImplementationNode[0]; while(currNode.Next.Length > 0) { inflatedIface = TryInflateType (currNode.InterfaceImplementation.InterfaceType, inflatedIface)!; @@ -253,7 +251,6 @@ protected void MapInterfacesOnType (TypeDefinition type) inflatedIface = TryInflateType (currNode.InterfaceImplementation.InterfaceType, inflatedIface)!; } // After inflating the first - //var inflatedIface2 = TryInflateType (recursiveInterface.InflatedInterface, iface.InterfaceType); waysToImplementIface.AddToList (inflatedIface, ifaceImplr); } } @@ -264,15 +261,15 @@ protected void MapInterfacesOnType (TypeDefinition type) var baseInterfaces = _interfaces[baseDef]; foreach (var item in baseInterfaces) { foreach (var node in item.InterfaceImplementationNode) { - if (item.InterfaceType is not null) + //if (item.InterfaceType is not null) waysToImplementIface.AddToList (item.InflatedInterface, node); - else - unresolvedImpls.AddToList (item.InflatedInterface.FullName, node); + //else + //unresolvedImpls.AddToList (item.InflatedInterface.FullName, node); } } } - List implrs = waysToImplementIface.Select (kvp => new InterfaceImplementor (type, context.Resolve(kvp.Key), kvp.Key, kvp.Value.OrderBy (i => i.Length).ToArray (), context)) - .Concat (unresolvedImpls.Select (kvp => new InterfaceImplementor (type, null, kvp.Value[0].GetLast ().InterfaceType, kvp.Value.OrderBy (i => i.Length).ToArray (), context))).ToList (); + List implrs = waysToImplementIface.Select (kvp => new InterfaceImplementor (type, context.Resolve (kvp.Key), kvp.Key, kvp.Value.OrderBy (i => i.Length).ToArray (), context)).ToList (); + //.Concat (unresolvedImpls.Select (kvp => new InterfaceImplementor (type, null, kvp.Value[0].GetLast ().InterfaceType, kvp.Value.OrderBy (i => i.Length).ToArray (), context))).ToList (); _interfaces.Add (type, implrs); } diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs index 8ed15724cc114..467c2272c1bed 100644 --- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs +++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs @@ -345,7 +345,7 @@ IEnumerable VerifyInterfaces (TypeDefinition src, TypeDefinition linked) } if (expectedInterfaces.Any ()) { - yield return $"Expected interfaces were not found on {src}"; + yield return $"Expected interfaces were not found on {src}. Expected to find: \n{string.Join(Environment.NewLine, expectedInterfaces)}\n"; } } } From d4f4ae8c2b66585e0af240cd402b6210ebf114fb Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 11 Mar 2024 10:31:43 -0700 Subject: [PATCH 27/34] Rename tests, reorder code, add DictionaryExtensions --- .../src/linker/Linker.Steps/MarkStep.cs | 73 +----- .../illink/src/linker/Linker/Annotations.cs | 7 +- .../src/linker/Linker/DictionaryExtensions.cs | 19 ++ .../src/linker/Linker/InterfaceImplementor.cs | 35 +-- .../src/linker/Linker/OverrideInformation.cs | 2 +- .../illink/src/linker/Linker/TypeMapInfo.cs | 236 ++++++++---------- .../linker/Linker/TypeReferenceExtensions.cs | 7 + ...e.Interfaces.RecursiveInterfacesTests.g.cs | 19 ++ .../Inheritance.InterfacesTests.g.cs | 10 +- ...mProvidedByUnreferencedIfaceInHierarchy.il | 1 - ...mProvidedByUnreferencedIfaceInHierarchy.cs | 1 - ...nterfaceImplementedThroughBaseInterface.il | 48 ---- ...nterfaceImplementedThroughBaseInterface.cs | 34 --- .../GenericInterfaceImplementedRecursively.il | 45 ++++ .../InterfaceImplementedRecursively.il | 0 ...ecursiveInterfaceTwoImplementationPaths.il | 62 +++++ .../GenericInterfaceImplementedRecursively.cs | 42 ++++ .../InterfaceImplementedRecursively.cs | 0 .../RecursiveInterfaceKept.cs | 38 +++ 19 files changed, 373 insertions(+), 306 deletions(-) create mode 100644 src/tools/illink/src/linker/Linker/DictionaryExtensions.cs create mode 100644 src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.RecursiveInterfacesTests.g.cs delete mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedThroughBaseInterface.il delete mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedThroughBaseInterface.cs create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/GenericInterfaceImplementedRecursively.il rename src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/{ => RecursiveInterfaces}/Dependencies/InterfaceImplementedRecursively.il (100%) create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveInterfaceTwoImplementationPaths.il create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/GenericInterfaceImplementedRecursively.cs rename src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/{ => RecursiveInterfaces}/InterfaceImplementedRecursively.cs (100%) create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveInterfaceKept.cs diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index 959c44495257c..173c0966aa60d 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -778,56 +778,6 @@ void MarkMethodIfNeededByBaseMethod (MethodDefinition method) } } - /// - /// Returns true if implements and the interface implementation is marked, - /// or if any marked interface implementations on are interfaces that implement and that interface implementation is marked - /// - bool IsInterfaceImplementationMarkedRecursively (TypeDefinition type, TypeDefinition interfaceType) - { - if (type.HasInterfaces) { - foreach (var intf in type.Interfaces) { - TypeDefinition? resolvedInterface = Context.Resolve (intf.InterfaceType); - if (resolvedInterface == null) - continue; - - if (Annotations.IsMarked (intf) && RequiresInterfaceRecursively (resolvedInterface, interfaceType)) - return true; - } - } - - return false; - } - - bool RequiresInterfaceRecursively (TypeDefinition typeToExamine, TypeDefinition interfaceType) - { - if (typeToExamine == interfaceType) - return true; - - if (typeToExamine.HasInterfaces) { - foreach (var iface in typeToExamine.Interfaces) { - var resolved = Context.TryResolve (iface.InterfaceType); - if (resolved == null) - continue; - - if (RequiresInterfaceRecursively (resolved, interfaceType)) { - var check = () => { - if (resolved == interfaceType) return true; - foreach (var iface in Annotations.GetRecursiveInterfaces (resolved)) { - if (iface.InterfaceType == interfaceType) { - return true; - } - } - return false; - }; - Debug.Assert (check ()); - return true; - } - } - } - - return false; - } - void ProcessDefaultImplementation (OverrideInformation ov) { Debug.Assert (ov.IsOverrideOfInterfaceMember); @@ -835,7 +785,7 @@ void ProcessDefaultImplementation (OverrideInformation ov) || ov.Override.IsStatic && !Annotations.IsRelevantToVariantCasting (ov.InterfaceImplementor.Implementor)) return; - foreach (var ifaceImpl in ov.InterfaceImplementor.ShortestInterfaceImplementationChain) { + foreach (var ifaceImpl in ov.InterfaceImplementor.MostDirectInterfaceImplementationPath) { MarkInterfaceImplementation (ifaceImpl); } } @@ -2462,9 +2412,9 @@ void MarkNamedProperty (TypeDefinition type, string property_name, in Dependency void MarkInterfaceImplementations (TypeDefinition type) { - foreach (var iface in Annotations.GetRecursiveInterfaces (type)) { - if (ShouldMarkInterfaceImplementation (iface)) { - foreach (InterfaceImplementation interfaceImpl in iface.ShortestInterfaceImplementationChain) { + foreach (var interfaceImplementor in Annotations.GetRecursiveInterfaces (type)) { + if (ShouldMarkInterfaceImplementation (interfaceImplementor)) { + foreach (InterfaceImplementation interfaceImpl in interfaceImplementor.MostDirectInterfaceImplementationPath) { MarkInterfaceImplementation (interfaceImpl, new MessageOrigin (type)); } } @@ -2473,8 +2423,8 @@ void MarkInterfaceImplementations (TypeDefinition type) protected virtual bool ShouldMarkInterfaceImplementation (InterfaceImplementor interfaceImplementor) { - //if (interfaceImplementor.IsMarked (Annotations)) - //return false; + if (interfaceImplementor.IsMostDirectImplementationMarked (Annotations)) + return false; if (!Context.IsOptimizationEnabled (CodeOptimizations.UnusedInterfaces, interfaceImplementor.Implementor)) return true; @@ -2570,19 +2520,10 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat if (Annotations.IsMarked (method)) return false; - // If the interface implementation is not marked, do not mark the implementation method - // A type that doesn't implement the interface isn't required to have methods that implement the interface. - //InterfaceImplementation? iface = overrideInformation.MatchingInterfaceImplementation; - //if (!((iface is not null && Annotations.IsMarked (iface)) - //|| IsInterfaceImplementationMarkedRecursively (method.DeclaringType, @base.DeclaringType))) { - //Debug.Assert (!overrideInformation.InterfaceImplementor.IsMarked (Annotations)); - //return false; - //} + // If there is no chain of interface implementations that make the type implement the interface, the method isn't required if (!overrideInformation.InterfaceImplementor.IsMarked(Annotations)) { - //Debug.Assert (!IsInterfaceImplementationMarkedRecursively (method.DeclaringType, @base.DeclaringType)); return false; } - //Debug.Assert (overrideInformation.InterfaceImplementor.IsMarked (Annotations)); // If the interface method is not marked and the interface doesn't come from a preserved scope, do not mark the implementation method // Unmarked interface methods from link assemblies will be removed so the implementing method does not need to be kept. diff --git a/src/tools/illink/src/linker/Linker/Annotations.cs b/src/tools/illink/src/linker/Linker/Annotations.cs index cc3c834142899..5d5f3a5936e4d 100644 --- a/src/tools/illink/src/linker/Linker/Annotations.cs +++ b/src/tools/illink/src/linker/Linker/Annotations.cs @@ -31,6 +31,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; @@ -93,7 +94,7 @@ protected Tracer Tracer { internal HashSet VirtualMethodsWithAnnotationsToValidate { get; } - public TypeMapInfo TypeMapInfo { get; } + internal TypeMapInfo TypeMapInfo { get; } public MemberActionStore MemberActions { get; } @@ -462,7 +463,7 @@ public bool IsPublic (IMetadataTokenProvider provider) /// DefaultInterfaceMethod is the method that implements . /// /// The interface method to find default implementations for - public IEnumerable? GetDefaultInterfaceImplementations (MethodDefinition method) + public List? GetDefaultInterfaceImplementations (MethodDefinition method) { return TypeMapInfo.GetDefaultInterfaceImplementations (method); } @@ -718,6 +719,6 @@ public void EnqueueVirtualMethod (MethodDefinition method) VirtualMethodsWithAnnotationsToValidate.Add (method); } - internal List GetRecursiveInterfaces (TypeDefinition type) => TypeMapInfo.GetRecursiveInterfaces (type); + internal ImmutableArray GetRecursiveInterfaces (TypeDefinition type) => TypeMapInfo.GetRecursiveInterfaces (type); } } diff --git a/src/tools/illink/src/linker/Linker/DictionaryExtensions.cs b/src/tools/illink/src/linker/Linker/DictionaryExtensions.cs new file mode 100644 index 0000000000000..c718901f943d9 --- /dev/null +++ b/src/tools/illink/src/linker/Linker/DictionaryExtensions.cs @@ -0,0 +1,19 @@ +// 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.Collections.Generic; + +namespace Mono.Linker +{ + public static class DictionaryExtensions + { + public static void AddToList (this Dictionary me, TKey key, TValueElement value) where TKey : notnull where TList : ICollection, new() + { + if (!me.TryGetValue (key, out TList? methods)) { + methods = new TList (); + me[key] = methods; + } + methods.Add (value); + } + } +} diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs index 318e590d901bc..a5836f21cd587 100644 --- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs @@ -25,11 +25,11 @@ public class InterfaceImplementor /// public TypeDefinition? InterfaceType { get; } - public ImplNode[] InterfaceImplementationNode { get; } + public ImmutableArray InterfaceImplementationNode { get; } public TypeReference InflatedInterface { get; } - public InterfaceImplementor (TypeDefinition implementor, TypeDefinition? interfaceType, TypeReference inflatedInterface, ImplNode[] implNode, LinkContext context) + public InterfaceImplementor (TypeDefinition implementor, TypeDefinition? interfaceType, TypeReference inflatedInterface, ImmutableArray implNode, LinkContext context) { Implementor = implementor; InterfaceType = interfaceType; @@ -38,20 +38,9 @@ public InterfaceImplementor (TypeDefinition implementor, TypeDefinition? interfa Debug.Assert (context.Resolve (inflatedInterface) == interfaceType); Debug.Assert (implNode.Length != 0); Debug.Assert (implNode.All (i => interfaceType == context.Resolve (i.GetLast ().InterfaceType))); - // Ensure the ImplNode is sorted by Length - Debug.Assert ( - implNode.Aggregate ( - (true, 0), - (acc, next) => { - if (!acc.Item1) return acc; - if (acc.Item2 <= next.Length) - return (true, next.Length); - return (false, next.Length); - }) - .Item1); } - public ShortestInterfaceImplementationChainEnumerator ShortestInterfaceImplementationChain => new (this); + public ShortestInterfaceImplementationChainEnumerator MostDirectInterfaceImplementationPath => new (this); public struct ShortestInterfaceImplementationChainEnumerator { @@ -83,6 +72,11 @@ public void MarkShortestImplementation(AnnotationStore annotations, in Dependenc InterfaceImplementationNode[0].MarkShortestImplementation (annotations, reason, origin); } + public bool IsMostDirectImplementationMarked(AnnotationStore annotations) + { + return InterfaceImplementationNode[0].IsMostDirectImplementationMarked (annotations); + } + public bool IsMarked(AnnotationStore annotations) { foreach(var i in InterfaceImplementationNode) { @@ -93,7 +87,7 @@ public bool IsMarked(AnnotationStore annotations) } } - public sealed record ImplNode (InterfaceImplementation InterfaceImplementation, TypeDefinition InterfaceImplementationProvider, ImmutableArray Next) + public sealed record ImplNode (InterfaceImplementation InterfaceImplementation, TypeDefinition InterfaceImplementationProvider, ImmutableArray Next) : IComparable { int _length = -1; public int Length { @@ -116,6 +110,15 @@ public void MarkShortestImplementation (AnnotationStore annotations, in Dependen } } + public bool IsMostDirectImplementationMarked(AnnotationStore annotations) + { + if (!annotations.IsMarked (InterfaceImplementation)) + return false; + if (Next.Length == 0) + return true; + return Next[0].IsMostDirectImplementationMarked (annotations); + } + public bool IsMarked (AnnotationStore annotations) { if (!annotations.IsMarked (InterfaceImplementation)) @@ -139,5 +142,7 @@ public InterfaceImplementation GetLast () } return curr.InterfaceImplementation; } + + public int CompareTo (ImplNode? other) => this.Length - other!.Length; } } diff --git a/src/tools/illink/src/linker/Linker/OverrideInformation.cs b/src/tools/illink/src/linker/Linker/OverrideInformation.cs index a7696a6247274..1f856d260bfef 100644 --- a/src/tools/illink/src/linker/Linker/OverrideInformation.cs +++ b/src/tools/illink/src/linker/Linker/OverrideInformation.cs @@ -45,7 +45,7 @@ public InterfaceImplementation? MatchingInterfaceImplementation { if (InterfaceImplementor is null) return null; InterfaceImplementation? last = null; - foreach (InterfaceImplementation curr in InterfaceImplementor!.ShortestInterfaceImplementationChain) { + foreach (InterfaceImplementation curr in InterfaceImplementor!.MostDirectInterfaceImplementationPath) { last = curr; } return last; diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 52ab73c5b14e4..cdc48066421a6 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -29,38 +29,23 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // -using System; -using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Runtime.CompilerServices; using Mono.Cecil; namespace Mono.Linker { - public static class DictOfListE - { - public static void AddToList (this Dictionary me, TKey key, TValueElement value) where TKey : notnull where TList : IList, new() - { - if (!me.TryGetValue (key, out TList? methods)) { - methods = new TList (); - me[key] = methods; - } - methods.Add (value); - } - } - - public class TypeMapInfo + internal sealed class TypeMapInfo { readonly HashSet assemblies = new HashSet (); 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> (); - readonly Dictionary> _interfaces = new (); + readonly Dictionary> base_methods = new Dictionary> (); + readonly Dictionary> override_methods = new Dictionary> (); + readonly Dictionary> default_interface_implementations = new Dictionary> (); + readonly Dictionary> _interfaces = new (); public TypeMapInfo (LinkContext context) { @@ -102,7 +87,7 @@ public void EnsureProcessed (AssemblyDefinition assembly) return bases; } - public InterfaceImplementor? GetInterfaceImplementor (TypeDefinition implementor, TypeDefinition interfaceType) + InterfaceImplementor? GetInterfaceImplementor (TypeDefinition implementor, TypeDefinition interfaceType) { if (!_interfaces.TryGetValue (implementor, out var implrs)) return null; @@ -121,44 +106,38 @@ public void EnsureProcessed (AssemblyDefinition assembly) /// DefaultInterfaceMethod is the method that implements . /// /// The interface method to find default implementations for - public IEnumerable? GetDefaultInterfaceImplementations (MethodDefinition baseMethod) + public List? GetDefaultInterfaceImplementations (MethodDefinition baseMethod) { + EnsureProcessed (baseMethod.Module.Assembly); default_interface_implementations.TryGetValue (baseMethod, out var ret); return ret; } - public void AddBaseMethod (MethodDefinition method, MethodDefinition @base, InterfaceImplementor? interfaceImplementor) + internal ImmutableArray GetRecursiveInterfaces (TypeDefinition type) { - if (!base_methods.TryGetValue (method, out List? methods)) { - methods = new List (); - base_methods[method] = methods; - } - - methods.Add (new OverrideInformation (@base, method, interfaceImplementor)); + EnsureProcessed (type.Module.Assembly); + return _interfaces.TryGetValue (type, out var value) ? value : []; } - public void AddOverride (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null) + void AddBaseMethod (MethodDefinition method, OverrideInformation overrideInformation) { - if (!override_methods.TryGetValue (@base, out List? methods)) { - methods = new List (); - override_methods.Add (@base, methods); - } + Debug.Assert (overrideInformation.Override == method); + base_methods.AddToList (method, overrideInformation); + } - methods.Add (new OverrideInformation (@base, @override, interfaceImplementor)); + void AddOverrideOfMethod (MethodDefinition @base, OverrideInformation overrideInformation) + { + Debug.Assert (overrideInformation.Base == @base); + override_methods.AddToList (@base, overrideInformation); } - public void AddDefaultInterfaceImplementation (MethodDefinition @base, InterfaceImplementor interfaceImplementor, MethodDefinition defaultImplementationMethod) + void AddDefaultInterfaceImplementation (MethodDefinition @base, InterfaceImplementor interfaceImplementor, MethodDefinition defaultImplementationMethod) { Debug.Assert (@base.DeclaringType.IsInterface); - if (!default_interface_implementations.TryGetValue (@base, out var implementations)) { - implementations = new List (); - default_interface_implementations.Add (@base, implementations); - } - - implementations.Add (new (@base, defaultImplementationMethod, interfaceImplementor)); + default_interface_implementations.AddToList (@base, new OverrideInformation (@base, defaultImplementationMethod, interfaceImplementor)); } - protected virtual void MapType (TypeDefinition type) + void MapType (TypeDefinition type) { MapInterfacesOnType (type); MapVirtualMethods (type); @@ -171,88 +150,19 @@ protected virtual void MapType (TypeDefinition type) MapType (nested); } - public bool InterfaceTypeEquals (TypeReference? type, TypeReference? other) - { - Debug.Assert (type is not null && other is not null); - if (type == other) - return true; - - if (context.TryResolve (type) != context.TryResolve (other)) - return false; - - if (type is GenericInstanceType genericInstance1) { - if (other is not GenericInstanceType genericInstance2) - return false; - if (genericInstance1.HasGenericParameters != genericInstance2.HasGenericParameters) - return false; - if (genericInstance1.GenericParameters.Count != genericInstance2.GenericParameters.Count - || genericInstance2.GenericArguments.Count != genericInstance2.GenericArguments.Count) - return false; - for (var i = 0; i < genericInstance1.GenericArguments.Count; ++i) { - if (!InterfaceTypeEquals (genericInstance1.GenericArguments[i], genericInstance2.GenericArguments[i])) - return false; - } - return true; - } - - if (type is TypeSpecification typeSpec1) { - if (other is not TypeSpecification typeSpec2) - return false; - return InterfaceTypeEquals (typeSpec1.ElementType, typeSpec2.ElementType); - } - return type.FullName == other.FullName; - } - - TypeReference? TryInflateType(TypeReference type, TypeReference genericProvider) - { - if (genericProvider is GenericInstanceType git) - return TypeReferenceExtensions.InflateGenericType (git, type, context); - return type; - } - - EqualityComparer? _inflatedInterfaceComparer; - EqualityComparer InflatedInterfaceComparer => _inflatedInterfaceComparer ??= EqualityComparer.Create (InterfaceTypeEquals, t => context.Resolve (t)?.GetHashCode () ?? 0); - - protected void MapInterfacesOnType (TypeDefinition type) + void MapInterfacesOnType (TypeDefinition type) { if (_interfaces.ContainsKey (type)) return; - Dictionary> waysToImplementIface = new (InflatedInterfaceComparer); - //Dictionary> unresolvedImpls = new (); + // Map from inflated interface type => list of every way to recursively implement that interface + Dictionary> waysToImplementIface = new (InflatedInterfaceComparer); // Get all interfaces directly implemented by this type - foreach (var iface in type.Interfaces) { - ImplNode implNode = new ImplNode (iface, type, []); - waysToImplementIface.AddToList (iface.InterfaceType, implNode); - var ifaceDirectlyOnType = context.Resolve (iface.InterfaceType); - if (ifaceDirectlyOnType is null) { - continue; - } - - MapInterfacesOnType (ifaceDirectlyOnType); - var recursiveInterfaces = _interfaces[ifaceDirectlyOnType]; - foreach (var recursiveInterface in recursiveInterfaces) { - //if (recursiveInterface.InterfaceType is null) { - //unresolvedImpls.AddToList (recursiveInterface.InflatedInterface.FullName, implNode); - //continue; - //} - var ifaceImplr = new ImplNode (iface, type, recursiveInterface.InterfaceImplementationNode.ToImmutableArray ()); - - // inflate interface type reference here with the type - TypeReference inflatedIface = TryInflateType (iface.InterfaceType, type)!; - if (recursiveInterface.InterfaceImplementationNode.Length > 0) { - var currNode = recursiveInterface.InterfaceImplementationNode[0]; - while(currNode.Next.Length > 0) { - inflatedIface = TryInflateType (currNode.InterfaceImplementation.InterfaceType, inflatedIface)!; - currNode = currNode.Next[0]; - } - // One last inflation - inflatedIface = TryInflateType (currNode.InterfaceImplementation.InterfaceType, inflatedIface)!; - } - // After inflating the first - waysToImplementIface.AddToList (inflatedIface, ifaceImplr); - } + foreach (var directIface in type.Interfaces) { + ImplNode directlyImplementedNode = new ImplNode (directIface, type, []); + TypeReference inflatedDirectIface = directIface.InterfaceType.TryInflateFrom (type, context)!; + waysToImplementIface.AddToList (inflatedDirectIface, directlyImplementedNode); } // Add interfaces on base type with the same implementation chain @@ -261,16 +171,35 @@ protected void MapInterfacesOnType (TypeDefinition type) var baseInterfaces = _interfaces[baseDef]; foreach (var item in baseInterfaces) { foreach (var node in item.InterfaceImplementationNode) { - //if (item.InterfaceType is not null) - waysToImplementIface.AddToList (item.InflatedInterface, node); - //else - //unresolvedImpls.AddToList (item.InflatedInterface.FullName, node); + waysToImplementIface.AddToList (item.InflatedInterface, node); + } + } + } + // Recursive interfaces next to preserve Inherit/Implement tree order + foreach (var directIface in type.Interfaces) { + // If we can't resolve the interface type we can't find recursive interfaces + var ifaceDirectlyOnType = context.Resolve (directIface.InterfaceType); + if (ifaceDirectlyOnType is null) { + continue; + } + MapInterfacesOnType (ifaceDirectlyOnType); + TypeReference inflatedDirectIface = directIface.InterfaceType.TryInflateFrom (type, context)!; + var recursiveInterfaces = _interfaces[ifaceDirectlyOnType]; + foreach (var recursiveInterface in recursiveInterfaces) { + var implToRecursiveIfaceChain = new ImplNode (directIface, type, recursiveInterface.InterfaceImplementationNode); + // Inflate the generic arguments up to the terminal interfaceImpl to get the inflated interface type implemented by this type + TypeReference inflatedRecursiveInterface = inflatedDirectIface; + foreach (var interfaceImpl in recursiveInterface.MostDirectInterfaceImplementationPath) { + inflatedRecursiveInterface = interfaceImpl.InterfaceType.TryInflateFrom (inflatedRecursiveInterface, context)!; } + waysToImplementIface.AddToList (inflatedRecursiveInterface, implToRecursiveIfaceChain); } } - List implrs = waysToImplementIface.Select (kvp => new InterfaceImplementor (type, context.Resolve (kvp.Key), kvp.Key, kvp.Value.OrderBy (i => i.Length).ToArray (), context)).ToList (); - //.Concat (unresolvedImpls.Select (kvp => new InterfaceImplementor (type, null, kvp.Value[0].GetLast ().InterfaceType, kvp.Value.OrderBy (i => i.Length).ToArray (), context))).ToList (); - _interfaces.Add (type, implrs); + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder (waysToImplementIface.Count); + foreach (var kvp in waysToImplementIface) { + builder.Add (new InterfaceImplementor (type, context.Resolve (kvp.Key), kvp.Key, kvp.Value.ToImmutableArray (), context)); + } + _interfaces.Add (type, builder.MoveToImmutable ()); } void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) @@ -312,7 +241,7 @@ void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) continue; } } - // Look along the chain of interfaceImpls to find the closest implementing + // Look for a default implementation last. FindAndAddDefaultInterfaceImplementations (type, resolvedInterfaceMethod, interfaceImplementor); } } @@ -366,8 +295,9 @@ void MapOverrides (MethodDefinition method) void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null) { - AddBaseMethod (@override, @base, interfaceImplementor); - AddOverride (@base, @override, interfaceImplementor); + OverrideInformation ov = new OverrideInformation (@base, @override, interfaceImplementor); + AddBaseMethod (@override, ov); + AddOverrideOfMethod (@base, ov); } MethodDefinition? GetBaseMethodInTypeHierarchy (MethodDefinition method) @@ -418,6 +348,51 @@ void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, Interf return context.TryResolve (type)?.BaseType; } + public bool InterfaceTypeEquals (TypeReference? type, TypeReference? other) + { + Debug.Assert (type is not null && other is not null); + Debug.Assert (context.TryResolve (type)?.IsInterface is null or true); + Debug.Assert (context.TryResolve (other)?.IsInterface is null or true); + return TypeEquals (type, other); + + bool TypeEquals (TypeReference type1, TypeReference type2) + { + if (type1 == type2) + return true; + + if (context.TryResolve (type1) != context.TryResolve (type2)) + return false; + + if (type1 is GenericInstanceType genericInstance1) { + if (type2 is not GenericInstanceType genericInstance2) + return false; + if (genericInstance1.HasGenericParameters != genericInstance2.HasGenericParameters) + return false; + if (genericInstance1.GenericParameters.Count != genericInstance2.GenericParameters.Count + || genericInstance2.GenericArguments.Count != genericInstance2.GenericArguments.Count) + return false; + for (var i = 0; i < genericInstance1.GenericArguments.Count; ++i) { + if (!TypeEquals (genericInstance1.GenericArguments[i], genericInstance2.GenericArguments[i])) + return false; + } + return true; + } + + if (type1 is TypeSpecification typeSpec1) { + if (type2 is not TypeSpecification typeSpec2) + return false; + return InterfaceTypeEquals (typeSpec1.ElementType, typeSpec2.ElementType); + } + return type1.FullName == type2.FullName; + } + } + + /// + /// Used only to compare inflated interfaces in MapInterfacesOnType + /// + public EqualityComparer InflatedInterfaceComparer => _inflatedInterfaceComparer ??= EqualityComparer.Create (InterfaceTypeEquals, t => context.Resolve (t)?.GetHashCode () ?? 0); + public EqualityComparer? _inflatedInterfaceComparer; + /// /// Returns a list of default implementations of the given interface method on this type. /// Note that this returns a list to potentially cover the diamond case (more than one @@ -431,7 +406,6 @@ void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, Interf /// void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplementsInterface, MethodDefinition interfaceMethodToBeImplemented, InterfaceImplementor implr) { - // Can I maybe only look at types in the implNode chain? I'd need all the chains that lead to the interfaceType, but it could be possible. bool foundImpl = false; foreach (var potentialDimProviderImplementation in _interfaces[typeThatImplementsInterface]) { if (potentialDimProviderImplementation.InterfaceType is null) @@ -624,7 +598,5 @@ static bool TypeMatch (TypeReference a, TypeReference b) return a.FullName == b.FullName; } - - internal List GetRecursiveInterfaces (TypeDefinition type) => _interfaces.TryGetValue (type, out var value) ? value : []; } } diff --git a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs index 5b821db9c33d3..a5f25c5bc59d0 100644 --- a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs +++ b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs @@ -171,6 +171,13 @@ void parseArrayDimensions (ArrayType at) } } + public static TypeReference? TryInflateFrom (this TypeReference type, TypeReference maybeGenericInstanceProvider, ITryResolveMetadata resolver) + { + if (maybeGenericInstanceProvider is GenericInstanceType git) + return InflateGenericType (git, type, resolver); + return type; + } + public static TypeReference? InflateGenericType (GenericInstanceType genericInstanceProvider, TypeReference typeToInflate, ITryResolveMetadata resolver) { if (typeToInflate is ArrayType arrayType) { diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.RecursiveInterfacesTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.RecursiveInterfacesTests.g.cs new file mode 100644 index 0000000000000..a4a66c302fe2a --- /dev/null +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.RecursiveInterfacesTests.g.cs @@ -0,0 +1,19 @@ +using System; +using System.Threading.Tasks; +using Xunit; + +namespace ILLink.RoslynAnalyzer.Tests.Inheritance.Interfaces +{ + public sealed partial class RecursiveInterfacesTests : LinkerTestBase + { + + protected override string TestSuiteName => "Inheritance.Interfaces.RecursiveInterfaces"; + + [Fact] + public Task RecursiveInterfaceKept () + { + return RunTest (allowMissingWarnings: true); + } + + } +} diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs index e384bc419d06f..c05bc05913103 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs @@ -16,31 +16,31 @@ public Task CanDisableUnusedInterfaces () } [Fact] - public Task InterfaceImplementedRecursively () + public Task InterfaceOnUninstantiatedTypeRemoved () { return RunTest (allowMissingWarnings: true); } [Fact] - public Task InterfaceImplementedThroughBaseInterface () + public Task InterfaceVariants () { return RunTest (allowMissingWarnings: true); } [Fact] - public Task InterfaceOnUninstantiatedTypeRemoved () + public Task InterfaceWithoutNewSlot () { return RunTest (allowMissingWarnings: true); } [Fact] - public Task InterfaceVariants () + public Task GenericInterfaceImplementedRecursively () { return RunTest (allowMissingWarnings: true); } [Fact] - public Task InterfaceWithoutNewSlot () + public Task InterfaceImplementedRecursively () { return RunTest (allowMissingWarnings: true); } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByUnreferencedIfaceInHierarchy.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByUnreferencedIfaceInHierarchy.il index 416f1202c682c..c85892208989b 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByUnreferencedIfaceInHierarchy.il +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByUnreferencedIfaceInHierarchy.il @@ -1,4 +1,3 @@ - // 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. diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs index afa6f3898a97b..2d10e78d146f3 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/StaticDimProvidedByUnreferencedIfaceInHierarchy.cs @@ -16,7 +16,6 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods [KeptTypeInAssembly ("library.dll", typeof(Program.IBase))] [KeptMemberInAssembly ("library.dll", typeof(Program.IBase), "Method()")] [KeptTypeInAssembly ("library.dll", typeof(Program.I4))] - // https://github.com/dotnet/runtime/issues/98536 [KeptTypeInAssembly ("library.dll", typeof(Program.I2))] [KeptMemberInAssembly ("library.dll", typeof(Program.I2), "Program.IBase.Method()")] [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I2), "library.dll", typeof (Program.IBase))] diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedThroughBaseInterface.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedThroughBaseInterface.il deleted file mode 100644 index 61080f8b7d066..0000000000000 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedThroughBaseInterface.il +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -.assembly extern mscorlib { } - -.assembly 'library' { } - -.class interface public auto ansi abstract beforefieldinit IBase -{ - // Methods - .method public hidebysig newslot abstract virtual - instance void M () cil managed - { - } // end of method IBase::M - -} // end of class IBase - -.class interface public auto ansi abstract beforefieldinit IDerived - implements IBase -{ -} // end of class IDerived - -.class public auto ansi beforefieldinit C - extends [System.Runtime]System.Object - implements IDerived -{ - // Methods - .method private final hidebysig newslot virtual - instance void IBase.M () cil managed - { - .override method instance void IBase::M() - // Method begins at RVA 0x2050 - // Code size 2 (0x2) - .maxstack 8 - - IL_0001: ret - } // end of method C::IBase.M - - .method public hidebysig specialname rtspecialname - instance void .ctor () cil managed - { - // Method begins at RVA 0x2053 - // Code size 8 (0x8) - .maxstack 8 - - IL_0007: ret - } // end of method C::.ctor -} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedThroughBaseInterface.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedThroughBaseInterface.cs deleted file mode 100644 index e701fb9c28ba6..0000000000000 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedThroughBaseInterface.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Mono.Linker.Tests.Cases.Expectations.Assertions; -using Mono.Linker.Tests.Cases.Expectations.Metadata; - -namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces -{ - [SetupLinkerArgument ("--skip-unresolved", "true")] - [SetupLinkerArgument ("-a", "test.exe", "library")] - [SetupLinkerArgument ("-a", "library.dll", "library")] - [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] - [Define ("IL_ASSEMBLY_AVAILABLE")] - [SetupCompileBefore ("library.dll", new[] { "Dependencies/InterfaceImplementedThroughBaseInterface.il" })] - [SkipILVerify] - -#if IL_ASSEMBLY_AVAILABLE - [KeptMemberInAssembly ("library.dll", typeof(C), "IBase.M()")] -#endif - [KeptMember(".ctor()")] - public class InterfaceImplementedThroughBaseInterface - { - public static void Main () - { - } - } -} - - diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/GenericInterfaceImplementedRecursively.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/GenericInterfaceImplementedRecursively.il new file mode 100644 index 0000000000000..7ff59e54ad065 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/GenericInterfaceImplementedRecursively.il @@ -0,0 +1,45 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class public auto ansi abstract sealed beforefieldinit Program + extends [mscorlib]System.Object +{ + // Nested Types + .class interface nested public auto ansi abstract beforefieldinit IBase`1 + { + } // end of class IBase + + .class interface nested public auto ansi abstract beforefieldinit IMiddle`1 + implements class Program/IBase`1 + { + } // end of class IMiddle + + .class interface nested public auto ansi abstract beforefieldinit IDerived`1 + implements class Program/IMiddle`1 + { + } // end of class IDerived + + .class nested public auto ansi beforefieldinit C + extends [mscorlib]System.Object + implements class Program/IDerived`1 + { + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2066 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method C::.ctor + + } // end of class C +} // end of class Program diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedRecursively.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/InterfaceImplementedRecursively.il similarity index 100% rename from src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/Dependencies/InterfaceImplementedRecursively.il rename to src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/InterfaceImplementedRecursively.il diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveInterfaceTwoImplementationPaths.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveInterfaceTwoImplementationPaths.il new file mode 100644 index 0000000000000..7ed9906af924c --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveInterfaceTwoImplementationPaths.il @@ -0,0 +1,62 @@ + +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class public auto ansi abstract sealed beforefieldinit Library + extends [mscorlib]System.Object +{ + .class interface nested public auto ansi abstract beforefieldinit I0 + { + } // end of class I0 + + .class interface nested public auto ansi abstract beforefieldinit I00 + implements Library/I0 + { + } // end of class I00 + + .class interface nested public auto ansi abstract beforefieldinit I01 + implements Library/I0 + { + } // end of class I01 + + .class interface nested public auto ansi abstract beforefieldinit I000 + implements Library/I00 + { + } // end of class I000 + + .class interface nested public auto ansi abstract beforefieldinit I010 + implements Library/I00 + { + } // end of class I010 + + .class interface nested public auto ansi abstract beforefieldinit I0100 + implements Library/I010 + { + } // end of class I010 + + .class nested public auto ansi beforefieldinit MyClass + extends [mscorlib]System.Object + implements Library/I0100, + Library/I000 + { + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2076 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method MyFoo::.ctor + + } // end of class MyFoo + +} // end of class Library diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/GenericInterfaceImplementedRecursively.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/GenericInterfaceImplementedRecursively.cs new file mode 100644 index 0000000000000..86cdfcf9728d7 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/GenericInterfaceImplementedRecursively.cs @@ -0,0 +1,42 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces +{ + [SetupLinkerArgument ("--skip-unresolved", "true")] + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/GenericInterfaceImplementedRecursively.il" })] + [SkipILVerify] +#if IL_ASSEMBLY_AVAILABLE + [KeptTypeInAssembly ("library.dll", typeof(Program.IBase<>))] + [KeptTypeInAssembly ("library.dll", typeof(Program.IMiddle<>))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IMiddle<>), "library.dll", typeof (Program.IBase<>))] + [KeptTypeInAssembly ("library.dll", typeof(Program.IDerived<>))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IDerived<>), "library.dll", typeof (Program.IMiddle<>))] + [KeptTypeInAssembly ("library.dll", typeof(Program.C))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.C), "library.dll", typeof (Program.IDerived))] +#endif + /// + /// This test case is to verify that the linker will keep all the metadata necessary for C to implement IBase when an interfaceImpl isn't directly on C. + /// + class GenericInterfaceImplementedRecursively + { + public static void Main() + { + +#if IL_ASSEMBLY_AVAILABLE + Program.IBase _ = null; + _ = new Program.C(); +#endif + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedRecursively.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/InterfaceImplementedRecursively.cs similarity index 100% rename from src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceImplementedRecursively.cs rename to src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/InterfaceImplementedRecursively.cs diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveInterfaceKept.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveInterfaceKept.cs new file mode 100644 index 0000000000000..2e08e5081c4cd --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveInterfaceKept.cs @@ -0,0 +1,38 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces +{ + /// + /// This tests that when a type implements an interface recursively (via implementations on implemented interfaces), + /// the shortest chain of interface implementations required to keep the implementation is marked + /// MyFoo => I000 => I00 => I0 (3 interfaceImpl long chain) + /// MyFoo => I0100 => I010 => I01 => I0 (4 interfaceImpl long chain) + /// + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/RecursiveInterfaceTwoImplementationPaths.il" })] + [SkipILVerify] +#if IL_ASSEMBLY_AVAILABLE + [KeptTypeInAssembly ("library.dll", typeof(Library.MyClass))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.MyClass), "library.dll", typeof (Library.I000))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.I000), "library.dll", typeof (Library.I00))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.I00), "library.dll", typeof (Library.I0))] + [RemovedTypeInAssembly("library.dll", typeof(Library.I01))] + [RemovedTypeInAssembly("library.dll", typeof(Library.I010))] + [RemovedTypeInAssembly("library.dll", typeof(Library.I0100))] + [RemovedInterfaceOnTypeInAssembly("library.dll", typeof (Library.MyClass), "library.dll", typeof (Library.I0100))] +#endif + public class RecursiveInterfaceKept + { + public static void Main() + { +#if IL_ASSEMBLY_AVAILABLE + Library.I0 _ = new Library.MyClass(); +#endif + } + } +} From fafdda8a314a6c07c53cf27095190cbc8c016857 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 11 Mar 2024 14:13:31 -0700 Subject: [PATCH 28/34] Clean up and add comments --- .../src/linker/Linker.Steps/MarkStep.cs | 3 +- .../src/linker/Linker/InterfaceImplementor.cs | 83 +++++++++++++------ .../src/linker/Linker/OverrideInformation.cs | 2 - .../linker/Linker/TypeDefinitionExtensions.cs | 1 - .../illink/src/linker/Linker/TypeMapInfo.cs | 22 +++-- .../linker/Linker/TypeReferenceExtensions.cs | 1 - .../Inheritance.InterfacesTests.g.cs | 6 ++ ...enericInterfaceWithMethodManyVariations.cs | 1 - ...ecursiveInterfaceTwoImplementationPaths.il | 2 +- .../GenericInterfaceImplementedRecursively.cs | 2 +- .../InterfaceImplementedRecursively.cs | 2 +- .../RecursiveInterfaceKept.cs | 18 ++-- 12 files changed, 89 insertions(+), 54 deletions(-) diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index 173c0966aa60d..713e1d2dcace5 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -3722,7 +3722,8 @@ protected virtual void MarkInstruction (Instruction instruction, MethodDefinitio ScopeStack.UpdateCurrentScopeInstructionOffset (instruction.Offset); if (markForReflectionAccess) { MarkMethodVisibleToReflection (methodReference, new DependencyInfo (dependencyKind, method), ScopeStack.CurrentScope.Origin); - } else { + } + else { MarkMethod (methodReference, new DependencyInfo (dependencyKind, method), ScopeStack.CurrentScope.Origin); } break; diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs index a5836f21cd587..f3e0b81275d9c 100644 --- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs @@ -2,13 +2,9 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; -using System.Collections; -using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; using System.Linq; -using System.Runtime.InteropServices; -using System.Transactions; using Mono.Cecil; namespace Mono.Linker @@ -25,30 +21,44 @@ public class InterfaceImplementor /// public TypeDefinition? InterfaceType { get; } - public ImmutableArray InterfaceImplementationNode { get; } - + /// + /// A to the with the generic parameters substituted. + /// public TypeReference InflatedInterface { get; } - public InterfaceImplementor (TypeDefinition implementor, TypeDefinition? interfaceType, TypeReference inflatedInterface, ImmutableArray implNode, LinkContext context) + /// + /// The graphs of s that make implement the . + /// There can be many ways a type implements an interface if an explicit interface implementation is given for an interface that is also implemented recursively due to another interface implementation. + /// It will be in the following order: + /// 1. Explicit interface implementation on + /// 2. Explicit interface implementation on a base type of + /// 3. Recursive interface implementations on an explicitly implemented interface on or it's base types + /// + public readonly ImmutableArray InterfaceImplementationNodes; + + public InterfaceImplementor (TypeDefinition implementor, TypeDefinition? interfaceType, TypeReference inflatedInterface, ImmutableArray implNode, LinkContext context) { Implementor = implementor; InterfaceType = interfaceType; - InterfaceImplementationNode = implNode; + InterfaceImplementationNodes = implNode; InflatedInterface = inflatedInterface; Debug.Assert (context.Resolve (inflatedInterface) == interfaceType); Debug.Assert (implNode.Length != 0); Debug.Assert (implNode.All (i => interfaceType == context.Resolve (i.GetLast ().InterfaceType))); } + /// + /// An Enumerable over the most direct chain to the + /// public ShortestInterfaceImplementationChainEnumerator MostDirectInterfaceImplementationPath => new (this); public struct ShortestInterfaceImplementationChainEnumerator { - ImplNode _current; + InterfaceImplementationNode _current; bool _hasMoved; public ShortestInterfaceImplementationChainEnumerator (InterfaceImplementor implementor) { - _current = implementor.InterfaceImplementationNode[0]; + _current = implementor.InterfaceImplementationNodes[0]; _hasMoved = false; } public ShortestInterfaceImplementationChainEnumerator GetEnumerator() => this; @@ -67,19 +77,20 @@ public bool MoveNext() } } - public void MarkShortestImplementation(AnnotationStore annotations, in DependencyInfo reason, in MessageOrigin origin) - { - InterfaceImplementationNode[0].MarkShortestImplementation (annotations, reason, origin); - } - + /// + /// Returns true if the most direct implementation of is marked. may still have a different recursive implementation marked. + /// public bool IsMostDirectImplementationMarked(AnnotationStore annotations) { - return InterfaceImplementationNode[0].IsMostDirectImplementationMarked (annotations); + return InterfaceImplementationNodes[0].IsMostDirectImplementationMarked (annotations); } + /// + /// Returns true if implements via any of the possible interface implementation chains. + /// public bool IsMarked(AnnotationStore annotations) { - foreach(var i in InterfaceImplementationNode) { + foreach(var i in InterfaceImplementationNodes) { if (i.IsMarked(annotations)) return true; } @@ -87,9 +98,29 @@ public bool IsMarked(AnnotationStore annotations) } } - public sealed record ImplNode (InterfaceImplementation InterfaceImplementation, TypeDefinition InterfaceImplementationProvider, ImmutableArray Next) : IComparable + /// + /// Represents a node in the graph of a type implementing an interface. + /// + public sealed class InterfaceImplementationNode : IComparable { - int _length = -1; + /// + /// The that is on that is part of the chain of interface implementations. + /// + public InterfaceImplementation InterfaceImplementation { get; } + + /// + /// The type that has in its . + /// + public TypeDefinition InterfaceImplementationProvider { get; } + + /// + /// The s that are on the type pointed to by that lead to the interface type. + /// + public ImmutableArray Next { get; } + + /// + /// The number of interface implementations on the most direct way the interface is implemented from + /// public int Length { get { if (_length != -1) @@ -99,15 +130,13 @@ public int Length { return _length = Next[0].Length + 1; } } + int _length = -1; - public void MarkShortestImplementation (AnnotationStore annotations, in DependencyInfo reason, in MessageOrigin origin) + public InterfaceImplementationNode(InterfaceImplementation interfaceImplementation, TypeDefinition interfaceImplementationProvider, ImmutableArray next) { - ImplNode curr = this; - annotations.Mark (curr.InterfaceImplementation, reason, origin); - while (curr.Next.Length > 0) { - curr = curr.Next[0]; - annotations.Mark (curr.InterfaceImplementation, reason, origin); - } + InterfaceImplementation = interfaceImplementation; + InterfaceImplementationProvider = interfaceImplementationProvider; + Next = next; } public bool IsMostDirectImplementationMarked(AnnotationStore annotations) @@ -143,6 +172,6 @@ public InterfaceImplementation GetLast () return curr.InterfaceImplementation; } - public int CompareTo (ImplNode? other) => this.Length - other!.Length; + int IComparable.CompareTo (InterfaceImplementationNode? other) => this.Length - other!.Length; } } diff --git a/src/tools/illink/src/linker/Linker/OverrideInformation.cs b/src/tools/illink/src/linker/Linker/OverrideInformation.cs index 1f856d260bfef..c306a3c111eb1 100644 --- a/src/tools/illink/src/linker/Linker/OverrideInformation.cs +++ b/src/tools/illink/src/linker/Linker/OverrideInformation.cs @@ -4,8 +4,6 @@ using System.Diagnostics; using Mono.Cecil; using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Transactions; namespace Mono.Linker { diff --git a/src/tools/illink/src/linker/Linker/TypeDefinitionExtensions.cs b/src/tools/illink/src/linker/Linker/TypeDefinitionExtensions.cs index ead37f1bb9b57..939c8f5266854 100644 --- a/src/tools/illink/src/linker/Linker/TypeDefinitionExtensions.cs +++ b/src/tools/illink/src/linker/Linker/TypeDefinitionExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; -using System.Collections.Generic; using Mono.Cecil; namespace Mono.Linker diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index cdc48066421a6..1fc6855dbc169 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -155,26 +155,29 @@ void MapInterfacesOnType (TypeDefinition type) if (_interfaces.ContainsKey (type)) return; - // Map from inflated interface type => list of every way to recursively implement that interface - Dictionary> waysToImplementIface = new (InflatedInterfaceComparer); + // Map from inflated interface type => list of every way to recursively implement that interface in 'type declaration order' according to ECMA 335 12.2 + Dictionary> waysToImplementIface = new (InflatedInterfaceComparer); - // Get all interfaces directly implemented by this type + // Get all explicit interfaces of this type foreach (var directIface in type.Interfaces) { - ImplNode directlyImplementedNode = new ImplNode (directIface, type, []); + InterfaceImplementationNode directlyImplementedNode = new InterfaceImplementationNode (directIface, type, []); TypeReference inflatedDirectIface = directIface.InterfaceType.TryInflateFrom (type, context)!; waysToImplementIface.AddToList (inflatedDirectIface, directlyImplementedNode); } - // Add interfaces on base type with the same implementation chain + // Add interfaces on base type if (type.BaseType is { } baseType && context.Resolve (baseType) is { } baseDef) { MapInterfacesOnType (baseDef); var baseInterfaces = _interfaces[baseDef]; foreach (var item in baseInterfaces) { - foreach (var node in item.InterfaceImplementationNode) { - waysToImplementIface.AddToList (item.InflatedInterface, node); + var inflatedInterface = item.InflatedInterface.TryInflateFrom (type.BaseType, context); + Debug.Assert (inflatedInterface is not null); + foreach (var node in item.InterfaceImplementationNodes) { + waysToImplementIface.AddToList (inflatedInterface, node); } } } + // Recursive interfaces next to preserve Inherit/Implement tree order foreach (var directIface in type.Interfaces) { // If we can't resolve the interface type we can't find recursive interfaces @@ -186,7 +189,7 @@ void MapInterfacesOnType (TypeDefinition type) TypeReference inflatedDirectIface = directIface.InterfaceType.TryInflateFrom (type, context)!; var recursiveInterfaces = _interfaces[ifaceDirectlyOnType]; foreach (var recursiveInterface in recursiveInterfaces) { - var implToRecursiveIfaceChain = new ImplNode (directIface, type, recursiveInterface.InterfaceImplementationNode); + var implToRecursiveIfaceChain = new InterfaceImplementationNode (directIface, type, recursiveInterface.InterfaceImplementationNodes); // Inflate the generic arguments up to the terminal interfaceImpl to get the inflated interface type implemented by this type TypeReference inflatedRecursiveInterface = inflatedDirectIface; foreach (var interfaceImpl in recursiveInterface.MostDirectInterfaceImplementationPath) { @@ -348,6 +351,9 @@ void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, Interf return context.TryResolve (type)?.BaseType; } + /// + /// Compares two TypeReferences to interface types and determines if they are equivalent references, taking into account generic arguments and element types. + /// public bool InterfaceTypeEquals (TypeReference? type, TypeReference? other) { Debug.Assert (type is not null && other is not null); diff --git a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs index a5f25c5bc59d0..b886ad8aaa4d8 100644 --- a/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs +++ b/src/tools/illink/src/linker/Linker/TypeReferenceExtensions.cs @@ -116,7 +116,6 @@ void parseArrayDimensions (ArrayType at) } } - public static TypeReference? GetInflatedDeclaringType (this TypeReference type, ITryResolveMetadata resolver) { if (type.IsGenericParameter || type.IsByReference || type.IsPointer) diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs index c05bc05913103..e973af7045c68 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs @@ -15,6 +15,12 @@ public Task CanDisableUnusedInterfaces () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task GenericInterfaceImplementedMultipleTimes () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task InterfaceOnUninstantiatedTypeRemoved () { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyVariations.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyVariations.cs index 90faee318c52d..79ad8eaaa58c2 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyVariations.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/OnReferenceType/BaseProvidesInterfaceMember/GenericInterfaceWithMethodManyVariations.cs @@ -2,7 +2,6 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.OnReferenceType.BaseProvidesInterfaceMember { - [SkipILVerify] public class GenericInterfaceWithMethodManyVariations { public static void Main () diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveInterfaceTwoImplementationPaths.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveInterfaceTwoImplementationPaths.il index 7ed9906af924c..854b0cd4a45b2 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveInterfaceTwoImplementationPaths.il +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/RecursiveInterfaceTwoImplementationPaths.il @@ -29,7 +29,7 @@ } // end of class I000 .class interface nested public auto ansi abstract beforefieldinit I010 - implements Library/I00 + implements Library/I01 { } // end of class I010 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 86cdfcf9728d7..bb0d318cac1a1 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 @@ -9,7 +9,7 @@ using Mono.Linker.Tests.Cases.Expectations.Assertions; using Mono.Linker.Tests.Cases.Expectations.Metadata; -namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces { [SetupLinkerArgument ("--skip-unresolved", "true")] [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/InterfaceImplementedRecursively.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/InterfaceImplementedRecursively.cs index 8c1eb6255a81e..89f59777c5fcd 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/InterfaceImplementedRecursively.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/InterfaceImplementedRecursively.cs @@ -9,7 +9,7 @@ using Mono.Linker.Tests.Cases.Expectations.Assertions; using Mono.Linker.Tests.Cases.Expectations.Metadata; -namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces { [SetupLinkerArgument ("--skip-unresolved", "true")] [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveInterfaceKept.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveInterfaceKept.cs index 2e08e5081c4cd..56e08d67be74f 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveInterfaceKept.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/RecursiveInterfaceKept.cs @@ -8,9 +8,7 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces { /// /// This tests that when a type implements an interface recursively (via implementations on implemented interfaces), - /// the shortest chain of interface implementations required to keep the implementation is marked - /// MyFoo => I000 => I00 => I0 (3 interfaceImpl long chain) - /// MyFoo => I0100 => I010 => I01 => I0 (4 interfaceImpl long chain) + /// the interface implementations kept are in type declaration order according to ECMA-335 12.2 /// [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] [Define ("IL_ASSEMBLY_AVAILABLE")] @@ -18,13 +16,13 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces [SkipILVerify] #if IL_ASSEMBLY_AVAILABLE [KeptTypeInAssembly ("library.dll", typeof(Library.MyClass))] - [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.MyClass), "library.dll", typeof (Library.I000))] - [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.I000), "library.dll", typeof (Library.I00))] - [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.I00), "library.dll", typeof (Library.I0))] - [RemovedTypeInAssembly("library.dll", typeof(Library.I01))] - [RemovedTypeInAssembly("library.dll", typeof(Library.I010))] - [RemovedTypeInAssembly("library.dll", typeof(Library.I0100))] - [RemovedInterfaceOnTypeInAssembly("library.dll", typeof (Library.MyClass), "library.dll", typeof (Library.I0100))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.MyClass), "library.dll", typeof (Library.I0100))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.I0100), "library.dll", typeof (Library.I010))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.I010), "library.dll", typeof (Library.I01))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Library.I01), "library.dll", typeof (Library.I0))] + [RemovedTypeInAssembly("library.dll", typeof(Library.I00))] + [RemovedTypeInAssembly("library.dll", typeof(Library.I000))] + [RemovedInterfaceOnTypeInAssembly("library.dll", typeof (Library.MyClass), "library.dll", typeof (Library.I000))] #endif public class RecursiveInterfaceKept { From dd1feeeda20bc003f58bb8e85a05ca5da0304b8c Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:15:56 -0700 Subject: [PATCH 29/34] Rename tests, format code, add a few comments --- .../illink/src/linker/Linker/Annotations.cs | 2 +- .../src/linker/Linker/InterfaceImplementor.cs | 137 +++++++++--------- .../illink/src/linker/Linker/TypeMapInfo.cs | 7 +- ...terfaces.DefaultInterfaceMethodsTests.g.cs | 8 +- ...e.Interfaces.RecursiveInterfacesTests.g.cs | 12 ++ .../Inheritance.InterfacesTests.g.cs | 18 --- .../DataFlow/FeatureCheckDataFlow.cs | 2 + ....il => DimProvidedByRecursiveInterface.il} | 0 ...ultipleDimsProvidedByRecursiveInterface.il | 86 +++++++++++ ....cs => DimProvidedByRecursiveInterface.cs} | 4 +- .../GenericDefaultInterfaceMethod.cs | 37 +++++ ...ultipleDimsProvidedByRecursiveInterface.cs | 86 +++++++++++ .../CompilerGeneratedCodeSubstitutions.cs | 2 + 13 files changed, 308 insertions(+), 93 deletions(-) rename src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/{DimProvidedByUnreferencedIfaceInHierarchy.il => DimProvidedByRecursiveInterface.il} (100%) create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/MultipleDimsProvidedByRecursiveInterface.il rename src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/{DimProvidedByUnreferencedIfaceInHierarchy.cs => DimProvidedByRecursiveInterface.cs} (95%) create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/GenericDefaultInterfaceMethod.cs create mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MultipleDimsProvidedByRecursiveInterface.cs diff --git a/src/tools/illink/src/linker/Linker/Annotations.cs b/src/tools/illink/src/linker/Linker/Annotations.cs index 5d5f3a5936e4d..ba3d298141b4d 100644 --- a/src/tools/illink/src/linker/Linker/Annotations.cs +++ b/src/tools/illink/src/linker/Linker/Annotations.cs @@ -463,7 +463,7 @@ public bool IsPublic (IMetadataTokenProvider provider) /// DefaultInterfaceMethod is the method that implements . /// /// The interface method to find default implementations for - public List? GetDefaultInterfaceImplementations (MethodDefinition method) + public IEnumerable? GetDefaultInterfaceImplementations (MethodDefinition method) { return TypeMapInfo.GetDefaultInterfaceImplementations (method); } diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs index f3e0b81275d9c..31423a2540bb8 100644 --- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs @@ -18,6 +18,7 @@ public class InterfaceImplementor /// /// The type of the interface that is implemented by + /// Null if the type could not be resolved /// public TypeDefinition? InterfaceType { get; } @@ -61,10 +62,10 @@ public ShortestInterfaceImplementationChainEnumerator (InterfaceImplementor impl _current = implementor.InterfaceImplementationNodes[0]; _hasMoved = false; } - public ShortestInterfaceImplementationChainEnumerator GetEnumerator() => this; + public ShortestInterfaceImplementationChainEnumerator GetEnumerator () => this; public InterfaceImplementation Current => _current.InterfaceImplementation; - public bool MoveNext() + public bool MoveNext () { if (!_hasMoved) { _hasMoved = true; @@ -80,7 +81,7 @@ public bool MoveNext() /// /// Returns true if the most direct implementation of is marked. may still have a different recursive implementation marked. /// - public bool IsMostDirectImplementationMarked(AnnotationStore annotations) + public bool IsMostDirectImplementationMarked (AnnotationStore annotations) { return InterfaceImplementationNodes[0].IsMostDirectImplementationMarked (annotations); } @@ -88,90 +89,90 @@ public bool IsMostDirectImplementationMarked(AnnotationStore annotations) /// /// Returns true if implements via any of the possible interface implementation chains. /// - public bool IsMarked(AnnotationStore annotations) + public bool IsMarked (AnnotationStore annotations) { - foreach(var i in InterfaceImplementationNodes) { - if (i.IsMarked(annotations)) + foreach (var i in InterfaceImplementationNodes) { + if (i.IsMarked (annotations)) return true; } return false; } - } - - /// - /// Represents a node in the graph of a type implementing an interface. - /// - public sealed class InterfaceImplementationNode : IComparable - { - /// - /// The that is on that is part of the chain of interface implementations. - /// - public InterfaceImplementation InterfaceImplementation { get; } /// - /// The type that has in its . + /// Represents a node in the graph of a type implementing an interface. /// - public TypeDefinition InterfaceImplementationProvider { get; } + public sealed class InterfaceImplementationNode : IComparable + { + /// + /// The that is on that is part of the chain of interface implementations. + /// + public InterfaceImplementation InterfaceImplementation { get; } + + /// + /// The type that has in its . + /// + public TypeDefinition InterfaceImplementationProvider { get; } + + /// + /// The s that are on the type pointed to by that lead to the interface type. + /// + public ImmutableArray Next { get; } + + /// + /// The number of interface implementations on the most direct way the interface is implemented from + /// + public int Length { + get { + if (_length != -1) + return _length; + if (Next.Length == 0) + return _length = 0; + return _length = Next[0].Length + 1; + } + } + int _length = -1; - /// - /// The s that are on the type pointed to by that lead to the interface type. - /// - public ImmutableArray Next { get; } + public InterfaceImplementationNode (InterfaceImplementation interfaceImplementation, TypeDefinition interfaceImplementationProvider, ImmutableArray next) + { + InterfaceImplementation = interfaceImplementation; + InterfaceImplementationProvider = interfaceImplementationProvider; + Next = next; + } - /// - /// The number of interface implementations on the most direct way the interface is implemented from - /// - public int Length { - get { - if (_length != -1) - return _length; + public bool IsMostDirectImplementationMarked (AnnotationStore annotations) + { + if (!annotations.IsMarked (InterfaceImplementation)) + return false; if (Next.Length == 0) - return _length = 0; - return _length = Next[0].Length + 1; + return true; + return Next[0].IsMostDirectImplementationMarked (annotations); } - } - int _length = -1; - public InterfaceImplementationNode(InterfaceImplementation interfaceImplementation, TypeDefinition interfaceImplementationProvider, ImmutableArray next) - { - InterfaceImplementation = interfaceImplementation; - InterfaceImplementationProvider = interfaceImplementationProvider; - Next = next; - } + public bool IsMarked (AnnotationStore annotations) + { + if (!annotations.IsMarked (InterfaceImplementation)) + return false; - public bool IsMostDirectImplementationMarked(AnnotationStore annotations) - { - if (!annotations.IsMarked (InterfaceImplementation)) - return false; - if (Next.Length == 0) - return true; - return Next[0].IsMostDirectImplementationMarked (annotations); - } + if (Next.Length == 0) + return true; - public bool IsMarked (AnnotationStore annotations) - { - if (!annotations.IsMarked (InterfaceImplementation)) + foreach (var impl in Next) { + if (impl.IsMarked (annotations)) + return true; + } return false; - - if (Next.Length == 0) - return true; - - foreach(var impl in Next) { - if (impl.IsMarked (annotations)) - return true; } - return false; - } - public InterfaceImplementation GetLast () - { - var curr = this; - while (curr.Next.Length > 0) { - curr = curr.Next[0]; + public InterfaceImplementation GetLast () + { + var curr = this; + while (curr.Next.Length > 0) { + curr = curr.Next[0]; + } + return curr.InterfaceImplementation; } - return curr.InterfaceImplementation; - } - int IComparable.CompareTo (InterfaceImplementationNode? other) => this.Length - other!.Length; + int IComparable.CompareTo (InterfaceImplementationNode? other) => this.Length - other!.Length; + } } } diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 1fc6855dbc169..0a9e5bb1b72b7 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -35,6 +35,7 @@ using System.Diagnostics.CodeAnalysis; using System.Linq; using Mono.Cecil; +using static Mono.Linker.InterfaceImplementor; namespace Mono.Linker { @@ -106,7 +107,7 @@ public void EnsureProcessed (AssemblyDefinition assembly) /// DefaultInterfaceMethod is the method that implements . /// /// The interface method to find default implementations for - public List? GetDefaultInterfaceImplementations (MethodDefinition baseMethod) + public IEnumerable? GetDefaultInterfaceImplementations (MethodDefinition baseMethod) { EnsureProcessed (baseMethod.Module.Assembly); default_interface_implementations.TryGetValue (baseMethod, out var ret); @@ -387,7 +388,7 @@ bool TypeEquals (TypeReference type1, TypeReference type2) if (type1 is TypeSpecification typeSpec1) { if (type2 is not TypeSpecification typeSpec2) return false; - return InterfaceTypeEquals (typeSpec1.ElementType, typeSpec2.ElementType); + return TypeEquals (typeSpec1.ElementType, typeSpec2.ElementType); } return type1.FullName == type2.FullName; } @@ -412,8 +413,8 @@ bool TypeEquals (TypeReference type1, TypeReference type2) /// void FindAndAddDefaultInterfaceImplementations (TypeDefinition typeThatImplementsInterface, MethodDefinition interfaceMethodToBeImplemented, InterfaceImplementor implr) { - bool foundImpl = false; foreach (var potentialDimProviderImplementation in _interfaces[typeThatImplementsInterface]) { + bool foundImpl = false; if (potentialDimProviderImplementation.InterfaceType is null) continue; // If this interface doesn't implement the interface with the method we're looking for, it can't provide a default implementation. 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 bdb96c8cfd9df..19189bfcd0170 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs @@ -16,7 +16,7 @@ public Task DefaultInterfaceMethodCallIntoClass () } [Fact] - public Task DimProvidedByUnreferencedIfaceInHierarchy () + public Task DimProvidedByRecursiveInterface () { return RunTest (allowMissingWarnings: true); } @@ -45,6 +45,12 @@ public Task MostSpecificDefaultImplementationKeptStatic () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task MultipleDimsProvidedByRecursiveInterface () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task SimpleDefaultInterfaceMethod () { 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 a4a66c302fe2a..d436348e800bc 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,18 @@ public sealed partial class RecursiveInterfacesTests : LinkerTestBase protected override string TestSuiteName => "Inheritance.Interfaces.RecursiveInterfaces"; + [Fact] + public Task GenericInterfaceImplementedRecursively () + { + return RunTest (allowMissingWarnings: true); + } + + [Fact] + public Task InterfaceImplementedRecursively () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task RecursiveInterfaceKept () { diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs index e973af7045c68..2e1a2bbcb3454 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.InterfacesTests.g.cs @@ -15,12 +15,6 @@ public Task CanDisableUnusedInterfaces () return RunTest (allowMissingWarnings: true); } - [Fact] - public Task GenericInterfaceImplementedMultipleTimes () - { - return RunTest (allowMissingWarnings: true); - } - [Fact] public Task InterfaceOnUninstantiatedTypeRemoved () { @@ -39,17 +33,5 @@ public Task InterfaceWithoutNewSlot () return RunTest (allowMissingWarnings: true); } - [Fact] - public Task GenericInterfaceImplementedRecursively () - { - return RunTest (allowMissingWarnings: true); - } - - [Fact] - public Task InterfaceImplementedRecursively () - { - return RunTest (allowMissingWarnings: true); - } - } } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs index b71cd280bdaeb..9fc2b77d7a487 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Runtime.CompilerServices; using System.Threading.Tasks; using ILLink.RoslynAnalyzer; @@ -1155,6 +1156,7 @@ static void GuardedLocalFunction () public static void Test () { + // Use the IEnumerable to mark the IEnumerable methods foreach (var _ in GuardInIterator ()) ; StateFlowsAcrossYield (); GuardInAsync (); diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByUnreferencedIfaceInHierarchy.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByRecursiveInterface.il similarity index 100% rename from src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByUnreferencedIfaceInHierarchy.il rename to src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/DimProvidedByRecursiveInterface.il diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/MultipleDimsProvidedByRecursiveInterface.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/MultipleDimsProvidedByRecursiveInterface.il new file mode 100644 index 0000000000000..4937513f28531 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/Dependencies/MultipleDimsProvidedByRecursiveInterface.il @@ -0,0 +1,86 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +.assembly extern mscorlib { } + +.assembly 'library' { } + +.class public auto ansi abstract sealed beforefieldinit Program + extends [mscorlib]System.Object +{ + // Nested Types + .class interface nested public auto ansi abstract beforefieldinit I0 + { + // Methods + .method public hidebysig newslot abstract virtual + instance void Method () cil managed + { + } // end of method I0::Method + + } // end of class I0 + + .class interface nested public auto ansi abstract beforefieldinit I00 + implements Program/I0 + { + // Methods + .method public final hidebysig virtual + instance void Program.I0.Method () cil managed + { + .override method instance void Program/I0::Method() + // Method begins at RVA 0x2068 + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: nop + IL_0001: ret + } // end of method I00::Program.I0.Method + } // end of class I00 + + .class interface nested public auto ansi abstract beforefieldinit I01 + implements Program/I0 + { + // Methods + .method public final hidebysig virtual + instance void Program.I0.Method () cil managed + { + .override method instance void Program/I0::Method() + // Method begins at RVA 0x2068 + // Code size 2 (0x2) + .maxstack 8 + + IL_0000: nop + IL_0001: ret + } // end of method I00::Program.I0.Method + } // end of class I00 + + .class interface nested public auto ansi abstract beforefieldinit I000 + implements Program/I00 + { + } // end of class I000 + + .class interface nested public auto ansi abstract beforefieldinit I010 + implements Program/I01 + { + } // end of class I000 + + .class nested public auto ansi beforefieldinit MyFoo + extends [mscorlib]System.Object + implements Program/I000, Program/I010 + { + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2076 + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method MyFoo::.ctor + + } // end of class MyFoo + +} // end of class Program diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByUnreferencedIfaceInHierarchy.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByRecursiveInterface.cs similarity index 95% rename from src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByUnreferencedIfaceInHierarchy.cs rename to src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByRecursiveInterface.cs index 3d7c7e0fa1df1..743df7227a80c 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByUnreferencedIfaceInHierarchy.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/DimProvidedByRecursiveInterface.cs @@ -8,7 +8,7 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods [SetupLinkerArgument ("--skip-unresolved", "true")] [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] [Define ("IL_ASSEMBLY_AVAILABLE")] - [SetupCompileBefore ("library.dll", new[] { "Dependencies/DimProvidedByUnreferencedIfaceInHierarchy.il" })] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/DimProvidedByRecursiveInterface.il" })] [SkipILVerify] #if IL_ASSEMBLY_AVAILABLE @@ -21,7 +21,7 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IBaz), "library.dll", typeof (Program.IBar))] [KeptMemberInAssembly ("library.dll", typeof(Program), "CallMethod(Program/IFoo)")] #endif - class DimProvidedByUnreferencedIfaceInHierarchy + class DimProvidedByRecursiveInterface { static void Main () { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/GenericDefaultInterfaceMethod.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/GenericDefaultInterfaceMethod.cs new file mode 100644 index 0000000000000..4c7bb896aed8d --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/GenericDefaultInterfaceMethod.cs @@ -0,0 +1,37 @@ +// 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 GenericDefaultInterfaceMethod + { + public static void Main() + { + new C (); + ((I00) null!).M (0); + } + [Kept] + public interface I0 + { + [Kept] + void M (T value); + } + [Kept] + public interface I00 : I0 + { + [Kept] + void I0.M (int value) { } + } + [Kept] + public class C : I00, I0 + { + } + } +} diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MultipleDimsProvidedByRecursiveInterface.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MultipleDimsProvidedByRecursiveInterface.cs new file mode 100644 index 0000000000000..7d027825ec409 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/MultipleDimsProvidedByRecursiveInterface.cs @@ -0,0 +1,86 @@ + + +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods +{ + [SetupLinkerArgument ("--skip-unresolved", "true")] + [TestCaseRequirements (TestRunCharacteristics.SupportsDefaultInterfaceMethods, "Requires support for default interface methods")] + [Define ("IL_ASSEMBLY_AVAILABLE")] + [SetupCompileBefore ("library.dll", new[] { "Dependencies/MultipleDimsProvidedByRecursiveInterface.il" })] + [SkipILVerify] + +#if IL_ASSEMBLY_AVAILABLE + // Both DIMs on I01 and I00 should be kept because one is not more specific than another. + [KeptMemberInAssembly ("library.dll", typeof(Program.I0), "Method()")] + [KeptTypeInAssembly ("library.dll", typeof(Program.I00))] + [KeptMemberInAssembly ("library.dll", typeof(Program.I00), "Program.I0.Method()")] + [KeptMemberInAssembly ("library.dll", typeof(Program.I01), "Program.I0.Method()")] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I00), "library.dll", typeof (Program.I0))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.MyFoo), "library.dll", typeof (Program.I000))] + [KeptTypeInAssembly ("library.dll", typeof(Program.I000))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I000), "library.dll", typeof (Program.I00))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.MyFoo), "library.dll", typeof (Program.I010))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I010), "library.dll", typeof (Program.I01))] + [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.I01), "library.dll", typeof (Program.I0))] +#endif + class MultipleDimsProvidedByRecursiveInterface + { + static void Main () + { +#if IL_ASSEMBLY_AVAILABLE + Program.I0 foo = new Program.MyFoo (); + CallMethod(foo); +#endif + } +#if IL_ASSEMBLY_AVAILABLE + [Kept] + static void CallMethod(Program.I0 foo) + { + foo.Method(); + } +#endif + } +} + + + +// public static class Program +// { +// [Kept] +// interface I0 +// { +// void Method(); +// } + +// [Kept] +// interface I00 : I0 +// { +// [Kept] +// void I0.Method() { } +// } + +// [Kept] +// interface I000: I00 /* not I0 */ +// { +// } + +// [Kept] +// interface I01 : I0 +// { +// [Kept] +// void I0.Method() { } +// } + +// [Kept] +// interface I010: I01 /* not I0 */ +// { +// } + +// [Kept] +// [KeptInterface(typeof(I000))] +// class MyFoo : I000, I010 +// { } + +// } diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/CompilerGeneratedCodeSubstitutions.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/CompilerGeneratedCodeSubstitutions.cs index 39f652833ca2e..3c59c3e446aad 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/CompilerGeneratedCodeSubstitutions.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/UnreachableBlock/CompilerGeneratedCodeSubstitutions.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Threading.Tasks; using Mono.Linker.Tests.Cases.Expectations.Assertions; using Mono.Linker.Tests.Cases.Expectations.Metadata; @@ -174,6 +175,7 @@ static IEnumerable TestBranchWithYieldBefore () public static void Test () { + // Use the IEnumerable to mark the IEnumerable methods foreach (var _ in TestBranchWithNormalCall ()) ; TestBranchWithYieldAfter (); TestBranchWithYieldBefore (); From cbab8b5000f51606ddfdae30ff7708eb83d442d8 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:43:55 -0700 Subject: [PATCH 30/34] Remove some unnecessary changes, add 'HasExplicitImplementation' prop --- src/tools/illink/src/linker/Linker/Annotations.cs | 2 +- .../illink/src/linker/Linker/InterfaceImplementor.cs | 6 +++--- src/tools/illink/src/linker/Linker/TypeMapInfo.cs | 8 ++++---- ...heritance.Interfaces.DefaultInterfaceMethodsTests.g.cs | 6 ++++++ 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/tools/illink/src/linker/Linker/Annotations.cs b/src/tools/illink/src/linker/Linker/Annotations.cs index ba3d298141b4d..6a21a921e76f9 100644 --- a/src/tools/illink/src/linker/Linker/Annotations.cs +++ b/src/tools/illink/src/linker/Linker/Annotations.cs @@ -94,7 +94,7 @@ protected Tracer Tracer { internal HashSet VirtualMethodsWithAnnotationsToValidate { get; } - internal TypeMapInfo TypeMapInfo { get; } + public TypeMapInfo TypeMapInfo { get; } public MemberActionStore MemberActions { get; } diff --git a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs index 31423a2540bb8..321584cb74e27 100644 --- a/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs +++ b/src/tools/illink/src/linker/Linker/InterfaceImplementor.cs @@ -37,6 +37,8 @@ public class InterfaceImplementor /// public readonly ImmutableArray InterfaceImplementationNodes; + public bool HasExplicitImplementation => InterfaceImplementationNodes[0].Length == 0; + public InterfaceImplementor (TypeDefinition implementor, TypeDefinition? interfaceType, TypeReference inflatedInterface, ImmutableArray implNode, LinkContext context) { Implementor = implementor; @@ -101,7 +103,7 @@ public bool IsMarked (AnnotationStore annotations) /// /// Represents a node in the graph of a type implementing an interface. /// - public sealed class InterfaceImplementationNode : IComparable + public sealed class InterfaceImplementationNode { /// /// The that is on that is part of the chain of interface implementations. @@ -171,8 +173,6 @@ public InterfaceImplementation GetLast () } return curr.InterfaceImplementation; } - - int IComparable.CompareTo (InterfaceImplementationNode? other) => this.Length - other!.Length; } } } diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 0a9e5bb1b72b7..357e5f2ceb5b1 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -39,13 +39,13 @@ namespace Mono.Linker { - internal sealed class TypeMapInfo + public class TypeMapInfo { readonly HashSet assemblies = new HashSet (); readonly LinkContext context; - readonly Dictionary> base_methods = new Dictionary> (); - readonly Dictionary> override_methods = new Dictionary> (); - readonly Dictionary> default_interface_implementations = new Dictionary> (); + protected readonly Dictionary> base_methods = new Dictionary> (); + protected readonly Dictionary> override_methods = new Dictionary> (); + protected readonly Dictionary> default_interface_implementations = new Dictionary> (); readonly Dictionary> _interfaces = new (); public TypeMapInfo (LinkContext context) 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 19189bfcd0170..2ed8346211e4b 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 @@ -21,6 +21,12 @@ public Task DimProvidedByRecursiveInterface () return RunTest (allowMissingWarnings: true); } + [Fact] + public Task GenericDefaultInterfaceMethod () + { + return RunTest (allowMissingWarnings: true); + } + [Fact] public Task GenericDefaultInterfaceMethods () { From 8b209180cb3b10ee212ce345e0f0327cef74df8a Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 11 Mar 2024 16:49:01 -0700 Subject: [PATCH 31/34] Move property definition closer to use and make private --- .../illink/src/linker/Linker/TypeMapInfo.cs | 96 +++++++++---------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs index 357e5f2ceb5b1..a5304569192be 100644 --- a/src/tools/illink/src/linker/Linker/TypeMapInfo.cs +++ b/src/tools/illink/src/linker/Linker/TypeMapInfo.cs @@ -206,6 +206,54 @@ void MapInterfacesOnType (TypeDefinition type) _interfaces.Add (type, builder.MoveToImmutable ()); } + /// + /// Used only to compare inflated interfaces in MapInterfacesOnType + /// + EqualityComparer InflatedInterfaceComparer => _inflatedInterfaceComparer ??= EqualityComparer.Create (InterfaceTypeEquals, t => context.Resolve (t)?.GetHashCode () ?? 0); + EqualityComparer? _inflatedInterfaceComparer; + + /// + /// Compares two TypeReferences to interface types and determines if they are equivalent references, taking into account generic arguments and element types. + /// + bool InterfaceTypeEquals (TypeReference? type, TypeReference? other) + { + Debug.Assert (type is not null && other is not null); + Debug.Assert (context.TryResolve (type)?.IsInterface is null or true); + Debug.Assert (context.TryResolve (other)?.IsInterface is null or true); + return TypeEquals (type, other); + + bool TypeEquals (TypeReference type1, TypeReference type2) + { + if (type1 == type2) + return true; + + if (context.TryResolve (type1) != context.TryResolve (type2)) + return false; + + if (type1 is GenericInstanceType genericInstance1) { + if (type2 is not GenericInstanceType genericInstance2) + return false; + if (genericInstance1.HasGenericParameters != genericInstance2.HasGenericParameters) + return false; + if (genericInstance1.GenericParameters.Count != genericInstance2.GenericParameters.Count + || genericInstance2.GenericArguments.Count != genericInstance2.GenericArguments.Count) + return false; + for (var i = 0; i < genericInstance1.GenericArguments.Count; ++i) { + if (!TypeEquals (genericInstance1.GenericArguments[i], genericInstance2.GenericArguments[i])) + return false; + } + return true; + } + + if (type1 is TypeSpecification typeSpec1) { + if (type2 is not TypeSpecification typeSpec2) + return false; + return TypeEquals (typeSpec1.ElementType, typeSpec2.ElementType); + } + return type1.FullName == type2.FullName; + } + } + void MapInterfaceMethodsInTypeHierarchy (TypeDefinition type) { if (!type.HasInterfaces) @@ -352,54 +400,6 @@ void AnnotateMethods (MethodDefinition @base, MethodDefinition @override, Interf return context.TryResolve (type)?.BaseType; } - /// - /// Compares two TypeReferences to interface types and determines if they are equivalent references, taking into account generic arguments and element types. - /// - public bool InterfaceTypeEquals (TypeReference? type, TypeReference? other) - { - Debug.Assert (type is not null && other is not null); - Debug.Assert (context.TryResolve (type)?.IsInterface is null or true); - Debug.Assert (context.TryResolve (other)?.IsInterface is null or true); - return TypeEquals (type, other); - - bool TypeEquals (TypeReference type1, TypeReference type2) - { - if (type1 == type2) - return true; - - if (context.TryResolve (type1) != context.TryResolve (type2)) - return false; - - if (type1 is GenericInstanceType genericInstance1) { - if (type2 is not GenericInstanceType genericInstance2) - return false; - if (genericInstance1.HasGenericParameters != genericInstance2.HasGenericParameters) - return false; - if (genericInstance1.GenericParameters.Count != genericInstance2.GenericParameters.Count - || genericInstance2.GenericArguments.Count != genericInstance2.GenericArguments.Count) - return false; - for (var i = 0; i < genericInstance1.GenericArguments.Count; ++i) { - if (!TypeEquals (genericInstance1.GenericArguments[i], genericInstance2.GenericArguments[i])) - return false; - } - return true; - } - - if (type1 is TypeSpecification typeSpec1) { - if (type2 is not TypeSpecification typeSpec2) - return false; - return TypeEquals (typeSpec1.ElementType, typeSpec2.ElementType); - } - return type1.FullName == type2.FullName; - } - } - - /// - /// Used only to compare inflated interfaces in MapInterfacesOnType - /// - public EqualityComparer InflatedInterfaceComparer => _inflatedInterfaceComparer ??= EqualityComparer.Create (InterfaceTypeEquals, t => context.Resolve (t)?.GetHashCode () ?? 0); - public EqualityComparer? _inflatedInterfaceComparer; - /// /// Returns a list of default implementations of the given interface method on this type. /// Note that this returns a list to potentially cover the diamond case (more than one From 999dc7d727bee4bfb5b0fb5d962c40297cf5103e Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Mon, 18 Mar 2024 11:57:12 -0700 Subject: [PATCH 32/34] Remove redundant test --- ...terfaces.DefaultInterfaceMethodsTests.g.cs | 6 --- .../GenericDefaultInterfaceMethod.cs | 37 ------------------- 2 files changed, 43 deletions(-) delete mode 100644 src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/GenericDefaultInterfaceMethod.cs 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 2ed8346211e4b..19189bfcd0170 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/generated/ILLink.RoslynAnalyzer.Tests.Generator/ILLink.RoslynAnalyzer.Tests.TestCaseGenerator/Inheritance.Interfaces.DefaultInterfaceMethodsTests.g.cs @@ -21,12 +21,6 @@ public Task DimProvidedByRecursiveInterface () return RunTest (allowMissingWarnings: true); } - [Fact] - public Task GenericDefaultInterfaceMethod () - { - return RunTest (allowMissingWarnings: true); - } - [Fact] public Task GenericDefaultInterfaceMethods () { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/GenericDefaultInterfaceMethod.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/GenericDefaultInterfaceMethod.cs deleted file mode 100644 index 4c7bb896aed8d..0000000000000 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/DefaultInterfaceMethods/GenericDefaultInterfaceMethod.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) .NET Foundation and contributors. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Mono.Linker.Tests.Cases.Expectations.Assertions; - -namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.DefaultInterfaceMethods -{ - class GenericDefaultInterfaceMethod - { - public static void Main() - { - new C (); - ((I00) null!).M (0); - } - [Kept] - public interface I0 - { - [Kept] - void M (T value); - } - [Kept] - public interface I00 : I0 - { - [Kept] - void I0.M (int value) { } - } - [Kept] - public class C : I00, I0 - { - } - } -} From 9aa7554d4646f356e49319c341cd90c5c02dc323 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Tue, 19 Mar 2024 14:04:09 -0700 Subject: [PATCH 33/34] Add annotations for IReflection to ProjectingType and its hierarchy --- .../src/System/Reflection/Context/Custom/CustomType.cs | 2 ++ .../Reflection/Context/Delegation/DelegatingType.cs | 10 ++++++++++ .../Reflection/Context/Projection/ProjectingType.cs | 10 ++++++++++ 3 files changed, 22 insertions(+) diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs index 95ee34ab4504b..56ab55515ce19 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Custom/CustomType.cs @@ -44,6 +44,7 @@ public override bool IsInstanceOfType([NotNullWhen(true)] object? o) return IsAssignableFrom(objectType); } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) { // list of properties on this type according to the underlying ReflectionContext @@ -139,6 +140,7 @@ public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) return binder.SelectProperty(bindingAttr, matchingProperties.ToArray(), returnType, types, modifiers); } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] public override MethodInfo[] GetMethods(BindingFlags bindingAttr) { // list of methods on this type according to the underlying ReflectionContext diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingType.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingType.cs index bd7ef3ff90eae..db8e8a7ece923 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingType.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingType.cs @@ -325,11 +325,13 @@ public override EventInfo[] GetEvents(BindingFlags bindingAttr) return _typeInfo.GetEvents(bindingAttr); } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] public override FieldInfo? GetField(string name, BindingFlags bindingAttr) { return _typeInfo.GetField(name, bindingAttr); } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] public override FieldInfo[] GetFields(BindingFlags bindingAttr) { return _typeInfo.GetFields(bindingAttr); @@ -345,6 +347,12 @@ public override Type[] GetInterfaces() return _typeInfo.GetInterfaces(); } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | + DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | + DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | + DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | + DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] public override MemberInfo[] GetMembers(BindingFlags bindingAttr) { return _typeInfo.GetMembers(bindingAttr); @@ -358,6 +366,7 @@ public override MemberInfo[] GetMembers(BindingFlags bindingAttr) _typeInfo.GetMethod(name, bindingAttr, binder, callConvention, types, modifiers); } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] public override MethodInfo[] GetMethods(BindingFlags bindingAttr) { return _typeInfo.GetMethods(bindingAttr); @@ -373,6 +382,7 @@ public override Type[] GetNestedTypes(BindingFlags bindingAttr) return _typeInfo.GetNestedTypes(bindingAttr); } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) { return _typeInfo.GetProperties(bindingAttr); diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingType.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingType.cs index 0ac9bb0de89f3..f61ea789bd738 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingType.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Projection/ProjectingType.cs @@ -206,11 +206,13 @@ public override EventInfo[] GetEvents(BindingFlags bindingAttr) return _projector.Project(base.GetEvents(bindingAttr), _projector.ProjectEvent); } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] public override FieldInfo? GetField(string name, BindingFlags bindingAttr) { return _projector.ProjectField(base.GetField(name, bindingAttr)); } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] public override FieldInfo[] GetFields(BindingFlags bindingAttr) { return _projector.Project(base.GetFields(bindingAttr), _projector.ProjectField); @@ -226,6 +228,12 @@ public override Type[] GetInterfaces() return _projector.Project(base.GetInterfaces(), _projector.ProjectType); } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | + DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | + DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | + DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | + DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | + DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] public override MemberInfo[] GetMembers(BindingFlags bindingAttr) { MethodInfo[] methods = GetMethods(bindingAttr); @@ -264,6 +272,7 @@ public override MemberInfo[] GetMembers(BindingFlags bindingAttr) return _projector.ProjectMethod(base.GetMethodImpl(name, bindingAttr, binder, callConvention, types, modifiers)); } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] public override MethodInfo[] GetMethods(BindingFlags bindingAttr) { return _projector.Project(base.GetMethods(bindingAttr), _projector.ProjectMethod); @@ -279,6 +288,7 @@ public override Type[] GetNestedTypes(BindingFlags bindingAttr) return _projector.Project(base.GetNestedTypes(bindingAttr), _projector.ProjectType); } + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) { return _projector.Project(base.GetProperties(bindingAttr), _projector.ProjectProperty); From cfc1189b4d67669ee53cdf587023641c46fe9464 Mon Sep 17 00:00:00 2001 From: Jackson Schuster <36744439+jtschuster@users.noreply.github.com> Date: Wed, 20 Mar 2024 17:28:21 -0700 Subject: [PATCH 34/34] ifdef out DynamicallyAccessedMembers --- .../Reflection/Context/Delegation/DelegatingType.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingType.cs b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingType.cs index db8e8a7ece923..5bad1592b9ba4 100644 --- a/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingType.cs +++ b/src/libraries/System.Reflection.Context/src/System/Reflection/Context/Delegation/DelegatingType.cs @@ -325,13 +325,17 @@ public override EventInfo[] GetEvents(BindingFlags bindingAttr) return _typeInfo.GetEvents(bindingAttr); } +#if NET8_0_OR_GREATER [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] +#endif public override FieldInfo? GetField(string name, BindingFlags bindingAttr) { return _typeInfo.GetField(name, bindingAttr); } +#if NET8_0_OR_GREATER [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] +#endif public override FieldInfo[] GetFields(BindingFlags bindingAttr) { return _typeInfo.GetFields(bindingAttr); @@ -347,12 +351,14 @@ public override Type[] GetInterfaces() return _typeInfo.GetInterfaces(); } +#if NET8_0_OR_GREATER [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] +#endif public override MemberInfo[] GetMembers(BindingFlags bindingAttr) { return _typeInfo.GetMembers(bindingAttr); @@ -366,7 +372,9 @@ public override MemberInfo[] GetMembers(BindingFlags bindingAttr) _typeInfo.GetMethod(name, bindingAttr, binder, callConvention, types, modifiers); } +#if NET8_0_OR_GREATER [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] +#endif public override MethodInfo[] GetMethods(BindingFlags bindingAttr) { return _typeInfo.GetMethods(bindingAttr); @@ -382,7 +390,9 @@ public override Type[] GetNestedTypes(BindingFlags bindingAttr) return _typeInfo.GetNestedTypes(bindingAttr); } +#if NET8_0_OR_GREATER [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] +#endif public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) { return _typeInfo.GetProperties(bindingAttr);