Skip to content

Commit 4bed0da

Browse files
authored
Revert "Re-add static interface trimming with more testing (#2791)" (#2841)
This reverts commit ff646e0.
1 parent 226107b commit 4bed0da

File tree

11 files changed

+39
-1837
lines changed

11 files changed

+39
-1837
lines changed

docs/removal-behavior.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public class Program
4242

4343
### Method call on a constrained type parameter
4444

45-
On a call to a static abstract interface method that is accessed through a constrained type parameter, the interface method is kept, as is every implementation method on every kept type.
45+
On a call to a static abstract interface method that is accessed through a constrained type parameter, the interface method is rooted, as well as every implementation method on every type.
4646

4747
Example:
4848

src/linker/Linker.Steps/MarkStep.cs

Lines changed: 10 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ protected LinkContext Context {
6060

6161
protected Queue<(MethodDefinition, DependencyInfo, MessageOrigin)> _methods;
6262
protected List<(MethodDefinition, MarkScopeStack.Scope)> _virtual_methods;
63-
protected List<(MethodDefinition, MarkScopeStack.Scope)> _static_interface_methods;
6463
protected Queue<AttributeProviderPair> _assemblyLevelAttributes;
6564
readonly List<AttributeProviderPair> _ivt_attributes;
6665
protected Queue<(AttributeProviderPair, DependencyInfo, MarkScopeStack.Scope)> _lateMarkedAttributes;
@@ -225,7 +224,6 @@ public MarkStep ()
225224
{
226225
_methods = new Queue<(MethodDefinition, DependencyInfo, MessageOrigin)> ();
227226
_virtual_methods = new List<(MethodDefinition, MarkScopeStack.Scope)> ();
228-
_static_interface_methods = new List<(MethodDefinition, MarkScopeStack.Scope)> ();
229227
_assemblyLevelAttributes = new Queue<AttributeProviderPair> ();
230228
_ivt_attributes = new List<AttributeProviderPair> ();
231229
_lateMarkedAttributes = new Queue<(AttributeProviderPair, DependencyInfo, MarkScopeStack.Scope)> ();
@@ -478,7 +476,6 @@ bool ProcessPrimaryQueue ()
478476
while (!QueueIsEmpty ()) {
479477
ProcessQueue ();
480478
ProcessVirtualMethods ();
481-
ProcessStaticInterfaceMethods ();
482479
ProcessMarkedTypesWithInterfaces ();
483480
ProcessDynamicCastableImplementationInterfaces ();
484481
ProcessPendingBodies ();
@@ -579,30 +576,6 @@ void ProcessVirtualMethods ()
579576
}
580577
}
581578

582-
/// <summary>
583-
/// Handles marking implementations of static interface methods and the interface implementations of types that implement a static interface method.
584-
/// </summary>
585-
void ProcessStaticInterfaceMethods ()
586-
{
587-
foreach ((MethodDefinition method, MarkScopeStack.Scope scope) in _static_interface_methods) {
588-
using (ScopeStack.PushScope (scope)) {
589-
var overrides = Annotations.GetOverrides (method);
590-
if (overrides != null) {
591-
foreach (OverrideInformation @override in overrides) {
592-
ProcessOverride (@override);
593-
// We need to mark the interface implementation for static interface methods
594-
// Explicit interface method implementations already mark the interface implementation in ProcessMethod
595-
MarkExplicitInterfaceImplementation (@override.Override, @override.Base);
596-
}
597-
}
598-
}
599-
}
600-
}
601-
602-
/// <summary>
603-
/// Does extra handling of marked types that have interfaces when it's necessary to know what types are marked or instantiated.
604-
/// Right now it only marks the "implements interface" annotations and removes override annotations for static interface methods.
605-
/// </summary>
606579
void ProcessMarkedTypesWithInterfaces ()
607580
{
608581
// We may mark an interface type later on. Which means we need to reprocess any time with one or more interface implementations that have not been marked
@@ -720,9 +693,6 @@ void ProcessVirtualMethod (MethodDefinition method)
720693
}
721694
}
722695

723-
/// <summary>
724-
/// Handles marking overriding methods if the type with the overriding method is instantiated or if the base method is a static abstract interface method
725-
/// </summary>
726696
void ProcessOverride (OverrideInformation overrideInformation)
727697
{
728698
var method = overrideInformation.Override;
@@ -738,12 +708,12 @@ void ProcessOverride (OverrideInformation overrideInformation)
738708

739709
var isInstantiated = Annotations.IsInstantiated (method.DeclaringType);
740710

741-
// We don't need to mark overrides until it is possible that the type could be instantiated or the method is a static interface method
711+
// We don't need to mark overrides until it is possible that the type could be instantiated
742712
// Note : The base type is interface check should be removed once we have base type sweeping
743713
if (IsInterfaceOverrideThatDoesNotNeedMarked (overrideInformation, isInstantiated))
744714
return;
745715

746-
// Interface static virtual methods will be abstract and will also bypass this check to get marked
716+
// Interface static veitual methods will be abstract and will also by pass this check to get marked
747717
if (!isInstantiated && !@base.IsAbstract && Context.IsOptimizationEnabled (CodeOptimizations.OverrideRemoval, method))
748718
return;
749719

@@ -766,7 +736,8 @@ bool IsInterfaceOverrideThatDoesNotNeedMarked (OverrideInformation overrideInfor
766736
if (!overrideInformation.IsOverrideOfInterfaceMember || isInstantiated)
767737
return false;
768738

769-
if (overrideInformation.IsStaticInterfaceMethodPair)
739+
// This is a static interface method and these checks should all be true
740+
if (overrideInformation.Override.IsStatic && overrideInformation.Base.IsStatic && overrideInformation.Base.IsAbstract && !overrideInformation.Override.IsVirtual)
770741
return false;
771742

772743
if (overrideInformation.MatchingInterfaceImplementation != null)
@@ -3083,28 +3054,17 @@ protected virtual void ProcessMethod (MethodDefinition method, in DependencyInfo
30833054
}
30843055
}
30853056

3086-
// Mark overridden methods and interface implementations except for static interface methods
3087-
// This will not mark implicit interface methods because they do not have a MethodImpl and aren't in the .Overrides
30883057
if (method.HasOverrides) {
3089-
foreach (MethodReference @base in method.Overrides) {
3090-
// Method implementing a static interface method will have an override to it - note nonstatic methods usually don't unless they're explicit.
3091-
// Calling the implementation method directly has no impact on the interface, and as such it should not mark the interface or its method.
3092-
// Only if the interface method is referenced, then all the methods which implemented must be kept, but not the other way round.
3093-
if (Context.Resolve (@base) is MethodDefinition baseDefinition
3094-
&& new OverrideInformation.OverridePair (baseDefinition, method).IsStaticInterfaceMethodPair ())
3095-
continue;
3096-
MarkMethod (@base, new DependencyInfo (DependencyKind.MethodImplOverride, method), ScopeStack.CurrentScope.Origin);
3097-
MarkExplicitInterfaceImplementation (method, @base);
3058+
foreach (MethodReference ov in method.Overrides) {
3059+
MarkMethod (ov, new DependencyInfo (DependencyKind.MethodImplOverride, method), ScopeStack.CurrentScope.Origin);
3060+
MarkExplicitInterfaceImplementation (method, ov);
30983061
}
30993062
}
31003063

31013064
MarkMethodSpecialCustomAttributes (method);
31023065
if (method.IsVirtual)
31033066
_virtual_methods.Add ((method, ScopeStack.CurrentScope));
31043067

3105-
if (method.IsStatic && method.IsAbstract && method.DeclaringType.IsInterface)
3106-
_static_interface_methods.Add ((method, ScopeStack.CurrentScope));
3107-
31083068
MarkNewCodeDependencies (method);
31093069

31103070
MarkBaseMethods (method);
@@ -3187,9 +3147,9 @@ protected virtual IEnumerable<MethodDefinition> GetRequiredMethodsForInstantiate
31873147
}
31883148
}
31893149

3190-
void MarkExplicitInterfaceImplementation (MethodDefinition method, MethodReference overriddenMethod)
3150+
void MarkExplicitInterfaceImplementation (MethodDefinition method, MethodReference ov)
31913151
{
3192-
if (Context.Resolve (overriddenMethod) is not MethodDefinition resolvedOverride)
3152+
if (Context.Resolve (ov) is not MethodDefinition resolvedOverride)
31933153
return;
31943154

31953155
if (resolvedOverride.DeclaringType.IsInterface) {
@@ -3461,7 +3421,7 @@ void MarkInterfacesNeededByBodyStack (MethodBody body)
34613421
{
34623422
// If a type could be on the stack in the body and an interface it implements could be on the stack on the body
34633423
// then we need to mark that interface implementation. When this occurs it is not safe to remove the interface implementation from the type
3464-
// even if the type is never instantiated. (ex. `Type1 x = null; IFoo y = (IFoo)x;`)
3424+
// even if the type is never instantiated
34653425
var implementations = new InterfacesOnStackScanner (Context).GetReferencedInterfaces (body);
34663426
if (implementations == null)
34673427
return;

src/linker/Linker.Steps/SweepStep.cs

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -453,8 +453,6 @@ protected virtual void SweepMethods (Collection<MethodDefinition> methods)
453453

454454
SweepCustomAttributes (method.MethodReturnType);
455455

456-
SweepOverrides (method);
457-
458456
if (!method.HasParameters)
459457
continue;
460458

@@ -469,38 +467,6 @@ protected virtual void SweepMethods (Collection<MethodDefinition> methods)
469467
}
470468
}
471469

472-
void SweepOverrides (MethodDefinition method)
473-
{
474-
for (int i = 0; i < method.Overrides.Count;) {
475-
// We can't rely on the context resolution cache anymore, since it may remember methods which are already removed
476-
// So call the direct Resolve here and avoid the cache.
477-
// We want to remove a method from the list of Overrides if:
478-
// Resolve() is null
479-
// This can happen for a couple of reasons, but it indicates the method isn't in the final assembly.
480-
// Resolve also may return a removed value if method.Overrides[i] is a MethodDefinition. In this case, Resolve short circuits and returns `this`.
481-
// OR
482-
// ov.DeclaringType is null
483-
// ov.DeclaringType may be null if Resolve short circuited and returned a removed method. In this case, we want to remove the override.
484-
// OR
485-
// ov is in a `link` scope and is unmarked
486-
// ShouldRemove returns true if the method is unmarked, but we also We need to make sure the override is in a link scope.
487-
// Only things in a link scope are marked, so ShouldRemove is only valid for items in a `link` scope.
488-
if (method.Overrides[i].Resolve () is not MethodDefinition ov || ov.DeclaringType is null || (IsLinkScope (ov.DeclaringType.Scope) && ShouldRemove (ov)))
489-
method.Overrides.RemoveAt (i);
490-
else
491-
i++;
492-
}
493-
}
494-
495-
/// <summary>
496-
/// Returns true if the assembly of the <paramref name="scope"></paramref> is set to link
497-
/// </summary>
498-
private bool IsLinkScope (IMetadataScope scope)
499-
{
500-
AssemblyDefinition? assembly = Context.Resolve (scope);
501-
return assembly != null && Annotations.GetAction (assembly) == AssemblyAction.Link;
502-
}
503-
504470
void SweepDebugInfo (Collection<MethodDefinition> methods)
505471
{
506472
List<ScopeDebugInformation>? sweptScopes = null;

src/linker/Linker/Annotations.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -436,9 +436,6 @@ public bool IsPublic (IMetadataTokenProvider provider)
436436
return public_api.Contains (provider);
437437
}
438438

439-
/// <summary>
440-
/// Returns an IEnumerable of the methods that override this method. Note this is different than <see cref="MethodDefinition.Overrides"/>, which returns the MethodImpl's
441-
/// </summary>
442439
public IEnumerable<OverrideInformation>? GetOverrides (MethodDefinition method)
443440
{
444441
return TypeMapInfo.GetOverrides (method);

src/linker/Linker/OverrideInformation.cs

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,17 @@ namespace Mono.Linker
1010
public class OverrideInformation
1111
{
1212
readonly ITryResolveMetadata resolver;
13-
readonly OverridePair _pair;
1413

1514
public OverrideInformation (MethodDefinition @base, MethodDefinition @override, ITryResolveMetadata resolver, InterfaceImplementation? matchingInterfaceImplementation = null)
1615
{
17-
_pair = new OverridePair (@base, @override);
16+
Base = @base;
17+
Override = @override;
1818
MatchingInterfaceImplementation = matchingInterfaceImplementation;
1919
this.resolver = resolver;
2020
}
2121

22-
public readonly record struct OverridePair (MethodDefinition Base, MethodDefinition Override)
23-
{
24-
public bool IsStaticInterfaceMethodPair () => Base.DeclaringType.IsInterface && Base.IsStatic && Override.IsStatic;
25-
}
26-
27-
public MethodDefinition Base { get => _pair.Base; }
28-
public MethodDefinition Override { get => _pair.Override; }
22+
public MethodDefinition Base { get; }
23+
public MethodDefinition Override { get; }
2924
public InterfaceImplementation? MatchingInterfaceImplementation { get; }
3025

3126
public bool IsOverrideOfInterfaceMember {
@@ -48,7 +43,5 @@ public TypeDefinition? InterfaceType {
4843
return Base.DeclaringType;
4944
}
5045
}
51-
52-
public bool IsStaticInterfaceMethodPair => _pair.IsStaticInterfaceMethodPair ();
5346
}
5447
}

test/ILLink.RoslynAnalyzer.Tests/Inheritance.Interfaces.StaticInterfaceMethodsTests.cs

Lines changed: 0 additions & 25 deletions
This file was deleted.

test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/InterfaceVariants.cs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -71,51 +71,53 @@ internal class ImplementsUnusedStaticInterface
7171
// The interface methods themselves are not used, but the implementation of these methods is
7272
internal interface IStaticInterfaceMethodUnused
7373
{
74+
// Can be removed with Static Interface trimming optimization
75+
[Kept]
7476
static abstract void InterfaceUsedMethodNot ();
7577
}
7678

79+
// Can be removed with Static Interface Trimming
80+
[Kept]
7781
internal interface IStaticInterfaceUnused
7882
{
83+
// Can be removed with Static Interface Trimming
84+
[Kept]
7985
static abstract void InterfaceAndMethodNoUsed ();
8086
}
8187

8288
[Kept]
89+
[KeptInterface (typeof (IStaticInterfaceUnused))]
90+
[KeptInterface (typeof (IStaticInterfaceMethodUnused))]
8391
internal class InterfaceMethodUsedThroughImplementation : IStaticInterfaceMethodUnused, IStaticInterfaceUnused
8492
{
8593
[Kept]
86-
[RemovedOverride (typeof (IStaticInterfaceMethodUnused))]
8794
public static void InterfaceUsedMethodNot () { }
8895

8996
[Kept]
90-
[RemovedOverride (typeof (IStaticInterfaceUnused))]
9197
public static void InterfaceAndMethodNoUsed () { }
9298
}
9399

94100
[Kept]
101+
[KeptInterface (typeof (IStaticInterfaceMethodUnused))]
102+
[KeptInterface (typeof (IStaticInterfaceUnused))]
95103
internal class InterfaceMethodUnused : IStaticInterfaceMethodUnused, IStaticInterfaceUnused
96104
{
105+
[Kept]
97106
public static void InterfaceUsedMethodNot () { }
98107

108+
[Kept]
99109
public static void InterfaceAndMethodNoUsed () { }
100110
}
101111

102-
[Kept]
103-
// This method keeps InterfaceMethodUnused without making it 'relevant to variant casting' like
104-
// doing a typeof or type argument would do. If the type is relevant to variant casting,
105-
// we will keep all interface implementations for interfaces that are kept
106-
internal static void KeepInterfaceMethodUnused (InterfaceMethodUnused x) { }
107-
108112
[Kept]
109113
public static void Test ()
110114
{
111115
InterfaceMethodUsedThroughImplementation.InterfaceUsedMethodNot ();
112116
InterfaceMethodUsedThroughImplementation.InterfaceAndMethodNoUsed ();
113117

114-
// The interface has to be kept this way, because if both the type and the interface may
115-
// appear on the stack then they would be marked as relevant to variant casting and the
116-
// interface implementation would be kept.
117-
Type t = typeof (IStaticInterfaceMethodUnused);
118-
KeepInterfaceMethodUnused (null);
118+
Type t;
119+
t = typeof (IStaticInterfaceMethodUnused);
120+
t = typeof (InterfaceMethodUnused);
119121
}
120122
}
121123

0 commit comments

Comments
 (0)