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