Skip to content

Commit 1afd4bc

Browse files
authored
[ILLink] Add interface implementing type to OverrideInformation (#98274)
Adds the type that implements the interface to the OverrideInformation class to be able to properly handle cases where the base provides the interface method or the type relies on a default interface method. Adds the InterfaceImplementor class that is basically a tuple of (TypeDefinition typeWithInterface, InterfaceImplementation ImplementedInterface) to do this. Also cleans some unnecessary code in OverrideInformation and ensures that OverrideInformation will have an InterfaceImplementor if the base method is an interface method.
1 parent d112020 commit 1afd4bc

File tree

10 files changed

+222
-99
lines changed

10 files changed

+222
-99
lines changed

src/tools/illink/illink.sln

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ Global
223223
SolutionGuid = {E43A3901-42B0-48CA-BB36-5CD40A99A6EE}
224224
EndGlobalSection
225225
GlobalSection(SharedMSBuildProjectFiles) = preSolution
226+
test\Trimming.Tests.Shared\Trimming.Tests.Shared.projitems*{400a1561-b6b6-482d-9e4c-3ddaede5bd07}*SharedItemsImports = 5
226227
src\ILLink.Shared\ILLink.Shared.projitems*{dd28e2b1-057b-4b4d-a04d-b2ebd9e76e46}*SharedItemsImports = 5
227228
src\ILLink.Shared\ILLink.Shared.projitems*{f1a44a78-34ee-408b-8285-9a26f0e7d4f2}*SharedItemsImports = 5
228229
src\ILLink.Shared\ILLink.Shared.projitems*{ff598e93-8e9e-4091-9f50-61a7572663ae}*SharedItemsImports = 13

src/tools/illink/src/linker/CompatibilitySuppressions.xml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,10 @@
253253
<DiagnosticId>CP0001</DiagnosticId>
254254
<Target>T:Mono.Linker.ILogger</Target>
255255
</Suppression>
256+
<Suppression>
257+
<DiagnosticId>CP0001</DiagnosticId>
258+
<Target>T:Mono.Linker.InterfaceImplementor</Target>
259+
</Suppression>
256260
<Suppression>
257261
<DiagnosticId>CP0001</DiagnosticId>
258262
<Target>T:Mono.Linker.InternalErrorException</Target>
@@ -1481,10 +1485,6 @@
14811485
<DiagnosticId>CP0002</DiagnosticId>
14821486
<Target>M:Mono.Linker.OverrideInformation.get_IsOverrideOfInterfaceMember</Target>
14831487
</Suppression>
1484-
<Suppression>
1485-
<DiagnosticId>CP0002</DiagnosticId>
1486-
<Target>M:Mono.Linker.OverrideInformation.get_IsStaticInterfaceMethodPair</Target>
1487-
</Suppression>
14881488
<Suppression>
14891489
<DiagnosticId>CP0002</DiagnosticId>
14901490
<Target>M:Mono.Linker.Steps.BaseStep.get_MarkingHelpers</Target>

src/tools/illink/src/linker/Linker.Steps/MarkStep.cs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -701,17 +701,16 @@ void ProcessVirtualMethod (MethodDefinition method)
701701
var defaultImplementations = Annotations.GetDefaultInterfaceImplementations (method);
702702
if (defaultImplementations is not null) {
703703
foreach (var dimInfo in defaultImplementations) {
704-
ProcessDefaultImplementation (dimInfo.ImplementingType, dimInfo.InterfaceImpl, dimInfo.DefaultInterfaceMethod);
704+
ProcessDefaultImplementation (dimInfo);
705705

706-
var ov = new OverrideInformation (method, dimInfo.DefaultInterfaceMethod, Context, dimInfo.InterfaceImpl);
707-
if (IsInterfaceImplementationMethodNeededByTypeDueToInterface (ov, dimInfo.ImplementingType))
708-
MarkMethod (ov.Override, new DependencyInfo (DependencyKind.Override, ov.Base), ScopeStack.CurrentScope.Origin);
706+
if (IsInterfaceImplementationMethodNeededByTypeDueToInterface (dimInfo))
707+
MarkMethod (dimInfo.Override, new DependencyInfo (DependencyKind.Override, dimInfo.Base), ScopeStack.CurrentScope.Origin);
709708
}
710709
}
711710
var overridingMethods = Annotations.GetOverrides (method);
712711
if (overridingMethods is not null) {
713-
foreach (var ov in overridingMethods) {
714-
if (IsInterfaceImplementationMethodNeededByTypeDueToInterface (ov, ov.Override.DeclaringType))
712+
foreach (OverrideInformation ov in overridingMethods) {
713+
if (IsInterfaceImplementationMethodNeededByTypeDueToInterface (ov))
715714
MarkMethod (ov.Override, new DependencyInfo (DependencyKind.Override, ov.Base), ScopeStack.CurrentScope.Origin);
716715
}
717716
}
@@ -819,13 +818,14 @@ bool RequiresInterfaceRecursively (TypeDefinition typeToExamine, TypeDefinition
819818
return false;
820819
}
821820

822-
void ProcessDefaultImplementation (TypeDefinition typeWithDefaultImplementedInterfaceMethod, InterfaceImplementation implementation, MethodDefinition implementationMethod)
821+
void ProcessDefaultImplementation (OverrideInformation ov)
823822
{
824-
if ((!implementationMethod.IsStatic && !Annotations.IsInstantiated (typeWithDefaultImplementedInterfaceMethod))
825-
|| implementationMethod.IsStatic && !Annotations.IsRelevantToVariantCasting (typeWithDefaultImplementedInterfaceMethod))
823+
Debug.Assert (ov.IsOverrideOfInterfaceMember);
824+
if ((!ov.Override.IsStatic && !Annotations.IsInstantiated (ov.InterfaceImplementor.Implementor))
825+
|| ov.Override.IsStatic && !Annotations.IsRelevantToVariantCasting (ov.InterfaceImplementor.Implementor))
826826
return;
827827

828-
MarkInterfaceImplementation (implementation);
828+
MarkInterfaceImplementation (ov.InterfaceImplementor.InterfaceImplementation);
829829
}
830830

831831
void MarkMarshalSpec (IMarshalInfoProvider spec, in DependencyInfo reason)
@@ -2549,11 +2549,11 @@ bool IsMethodNeededByTypeDueToPreservedScope (MethodDefinition method)
25492549
/// <summary>
25502550
/// Returns true if the override method is required due to the interface that the base method is declared on. See doc at <see href="docs/methods-kept-by-interface.md"/> for explanation of logic.
25512551
/// </summary>
2552-
bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformation overrideInformation, TypeDefinition typeThatImplsInterface)
2552+
bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformation overrideInformation)
25532553
{
25542554
var @base = overrideInformation.Base;
25552555
var method = overrideInformation.Override;
2556-
Debug.Assert (@base.DeclaringType.IsInterface);
2556+
Debug.Assert (overrideInformation.IsOverrideOfInterfaceMember);
25572557
if (@base is null || method is null || @base.DeclaringType is null)
25582558
return false;
25592559

@@ -2562,7 +2562,7 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat
25622562

25632563
// If the interface implementation is not marked, do not mark the implementation method
25642564
// A type that doesn't implement the interface isn't required to have methods that implement the interface.
2565-
InterfaceImplementation? iface = overrideInformation.MatchingInterfaceImplementation;
2565+
InterfaceImplementation? iface = overrideInformation.InterfaceImplementor.InterfaceImplementation;
25662566
if (!((iface is not null && Annotations.IsMarked (iface))
25672567
|| IsInterfaceImplementationMarkedRecursively (method.DeclaringType, @base.DeclaringType)))
25682568
return false;
@@ -2580,12 +2580,12 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat
25802580
// If the method is static and the implementing type is relevant to variant casting, mark the implementation method.
25812581
// A static method may only be called through a constrained call if the type is relevant to variant casting.
25822582
if (@base.IsStatic)
2583-
return Annotations.IsRelevantToVariantCasting (typeThatImplsInterface)
2583+
return Annotations.IsRelevantToVariantCasting (overrideInformation.InterfaceImplementor.Implementor)
25842584
|| IgnoreScope (@base.DeclaringType.Scope);
25852585

25862586
// If the implementing type is marked as instantiated, mark the implementation method.
25872587
// If the type is not instantiated, do not mark the implementation method
2588-
return Annotations.IsInstantiated (typeThatImplsInterface);
2588+
return Annotations.IsInstantiated (overrideInformation.InterfaceImplementor.Implementor);
25892589
}
25902590

25912591
static bool IsSpecialSerializationConstructor (MethodDefinition method)
@@ -3256,7 +3256,7 @@ protected virtual void ProcessMethod (MethodDefinition method, in DependencyInfo
32563256
// Only if the interface method is referenced, then all the methods which implemented must be kept, but not the other way round.
32573257
if (!markAllOverrides &&
32583258
Context.Resolve (@base) is MethodDefinition baseDefinition
3259-
&& new OverrideInformation.OverridePair (baseDefinition, method).IsStaticInterfaceMethodPair ())
3259+
&& baseDefinition.DeclaringType.IsInterface && baseDefinition.IsStatic && method.IsStatic)
32603260
continue;
32613261
MarkMethod (@base, new DependencyInfo (DependencyKind.MethodImplOverride, method), ScopeStack.CurrentScope.Origin);
32623262
MarkExplicitInterfaceImplementation (method, @base);

src/tools/illink/src/linker/Linker/Annotations.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ public bool IsPublic (IMetadataTokenProvider provider)
462462
/// DefaultInterfaceMethod is the method that implements <paramref name="method"/>.
463463
/// </summary>
464464
/// <param name="method">The interface method to find default implementations for</param>
465-
public IEnumerable<(TypeDefinition ImplementingType, InterfaceImplementation InterfaceImpl, MethodDefinition DefaultInterfaceMethod)>? GetDefaultInterfaceImplementations (MethodDefinition method)
465+
public IEnumerable<OverrideInformation>? GetDefaultInterfaceImplementations (MethodDefinition method)
466466
{
467467
return TypeMapInfo.GetDefaultInterfaceImplementations (method);
468468
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright (c) .NET Foundation and contributors. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
using System.Collections;
6+
using System.Collections.Generic;
7+
using System.Diagnostics;
8+
using Mono.Cecil;
9+
10+
namespace Mono.Linker
11+
{
12+
public class InterfaceImplementor
13+
{
14+
/// <summary>
15+
/// The type that implements <see cref="InterfaceImplementor.InterfaceType"/>.
16+
/// </summary>
17+
public TypeDefinition Implementor { get; }
18+
/// <summary>
19+
/// The .interfaceimpl on <see cref="InterfaceImplementor.Implementor"/>that points to <see cref="InterfaceImplementor.InterfaceType"/>
20+
/// </summary>
21+
public InterfaceImplementation InterfaceImplementation { get; }
22+
/// <summary>
23+
/// The type of the interface that is implemented by <see cref="InterfaceImplementor.Implementor"/>
24+
/// </summary>
25+
public TypeDefinition InterfaceType { get; }
26+
27+
public InterfaceImplementor (TypeDefinition implementor, InterfaceImplementation interfaceImplementation, TypeDefinition interfaceType, IMetadataResolver resolver)
28+
{
29+
Implementor = implementor;
30+
InterfaceImplementation = interfaceImplementation;
31+
InterfaceType = interfaceType;
32+
Debug.Assert(resolver.Resolve (interfaceImplementation.InterfaceType) == interfaceType);
33+
}
34+
35+
public static InterfaceImplementor Create(TypeDefinition implementor, TypeDefinition interfaceType, IMetadataResolver resolver)
36+
{
37+
foreach(InterfaceImplementation iface in implementor.Interfaces) {
38+
if (resolver.Resolve(iface.InterfaceType) == interfaceType) {
39+
return new InterfaceImplementor(implementor, iface, interfaceType, resolver);
40+
}
41+
}
42+
43+
Queue<TypeDefinition> ifacesToCheck = new ();
44+
ifacesToCheck.Enqueue(implementor);
45+
while (ifacesToCheck.Count > 0) {
46+
var currentIface = ifacesToCheck.Dequeue ();
47+
48+
foreach(InterfaceImplementation ifaceImpl in currentIface.Interfaces) {
49+
var iface = resolver.Resolve (ifaceImpl.InterfaceType);
50+
if (iface == interfaceType) {
51+
return new InterfaceImplementor(implementor, ifaceImpl, interfaceType, resolver);
52+
}
53+
ifacesToCheck.Enqueue (iface);
54+
}
55+
}
56+
throw new InvalidOperationException ($"Type '{implementor.FullName}' does not implement interface '{interfaceType.FullName}' directly or through any interfaces");
57+
}
58+
}
59+
}

src/tools/illink/src/linker/Linker/OverrideInformation.cs

Lines changed: 21 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,71 +3,39 @@
33

44
using System.Diagnostics;
55
using Mono.Cecil;
6+
using System.Diagnostics.CodeAnalysis;
67

78
namespace Mono.Linker
89
{
910
[DebuggerDisplay ("{Override}")]
1011
public class OverrideInformation
1112
{
12-
readonly ITryResolveMetadata resolver;
13-
readonly OverridePair _pair;
14-
private InterfaceImplementation? _matchingInterfaceImplementation;
13+
public MethodDefinition Base { get; }
1514

16-
public OverrideInformation (MethodDefinition @base, MethodDefinition @override, ITryResolveMetadata resolver, InterfaceImplementation? matchingInterfaceImplementation = null)
17-
{
18-
_pair = new OverridePair (@base, @override);
19-
_matchingInterfaceImplementation = matchingInterfaceImplementation;
20-
this.resolver = resolver;
21-
}
22-
public readonly record struct OverridePair (MethodDefinition Base, MethodDefinition Override)
23-
{
24-
public bool IsStaticInterfaceMethodPair () => Base.DeclaringType.IsInterface && Base.IsStatic && Override.IsStatic;
25-
public InterfaceImplementation? GetMatchingInterfaceImplementation (ITryResolveMetadata resolver)
26-
{
27-
if (!Base.DeclaringType.IsInterface)
28-
return null;
29-
var interfaceType = Base.DeclaringType;
30-
foreach (var @interface in Override.DeclaringType.Interfaces) {
31-
if (resolver.TryResolve (@interface.InterfaceType)?.Equals (interfaceType) == true) {
32-
return @interface;
33-
}
34-
}
35-
return null;
36-
}
37-
}
15+
public MethodDefinition Override { get; }
3816

39-
public MethodDefinition Base { get => _pair.Base; }
40-
public MethodDefinition Override { get => _pair.Override; }
41-
public InterfaceImplementation? MatchingInterfaceImplementation {
42-
get {
43-
if (_matchingInterfaceImplementation is not null)
44-
return _matchingInterfaceImplementation;
45-
_matchingInterfaceImplementation = _pair.GetMatchingInterfaceImplementation (resolver);
46-
return _matchingInterfaceImplementation;
47-
}
48-
}
17+
internal InterfaceImplementor? InterfaceImplementor { get; }
4918

50-
public bool IsOverrideOfInterfaceMember {
51-
get {
52-
if (MatchingInterfaceImplementation != null)
53-
return true;
54-
55-
return Base.DeclaringType.IsInterface;
56-
}
19+
internal OverrideInformation (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null)
20+
{
21+
Base = @base;
22+
Override = @override;
23+
InterfaceImplementor = interfaceImplementor;
24+
// Ensure we have an interface implementation if the base method is from an interface and the override method is on a class
25+
Debug.Assert(@base.DeclaringType.IsInterface && interfaceImplementor != null
26+
|| !@base.DeclaringType.IsInterface && interfaceImplementor == null);
27+
// Ensure the interfaceImplementor is for the interface we expect
28+
Debug.Assert (@base.DeclaringType.IsInterface ? interfaceImplementor!.InterfaceType == @base.DeclaringType : true);
5729
}
5830

59-
public TypeDefinition? InterfaceType {
60-
get {
61-
if (!IsOverrideOfInterfaceMember)
62-
return null;
31+
public InterfaceImplementation? MatchingInterfaceImplementation
32+
=> InterfaceImplementor?.InterfaceImplementation;
6333

64-
if (MatchingInterfaceImplementation != null)
65-
return resolver.TryResolve (MatchingInterfaceImplementation.InterfaceType);
66-
67-
return Base.DeclaringType;
68-
}
69-
}
34+
public TypeDefinition? InterfaceType
35+
=> InterfaceImplementor?.InterfaceType;
7036

71-
public bool IsStaticInterfaceMethodPair => _pair.IsStaticInterfaceMethodPair ();
37+
[MemberNotNullWhen (true, nameof (InterfaceImplementor), nameof (MatchingInterfaceImplementation))]
38+
public bool IsOverrideOfInterfaceMember
39+
=> InterfaceImplementor != null;
7240
}
7341
}

0 commit comments

Comments
 (0)