-
Notifications
You must be signed in to change notification settings - Fork 128
Re-add static interface trimming with more testing #2791
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
36 commits
Select commit
Hold shift + click to select a range
f704291
Re-add static interface trimming with more testing
jtschuster 68c530d
Add static interface tests for library mode
jtschuster 97cd617
Add sanity check for overrides
jtschuster 8190bf7
Add sanity check for overrides
jtschuster 9a5fe0f
Merge branch 'main' into overrideSanityCheck
jtschuster 3a560d4
Merge branch 'overrideSanityCheck' into staticInterfacesBack
jtschuster 733bf90
Update comment on override removal in sweep step
jtschuster 47104d9
Add attributes
jtschuster c2f4fd1
formatting
jtschuster 3d577ce
formatting
jtschuster 8ac16bb
Merge branch 'overrideSanityCheck' into staticInterfacesBack
jtschuster cccca4c
Modify override removal logic and update comment
jtschuster c380027
Mark override on explicit implementation
jtschuster 4813047
Tweak override removal comment
jtschuster 332e79f
wip
jtschuster 9f64a32
Merge branch 'main' into staticInterfacesBack
jtschuster 9d27c3b
Merge remote-tracking branch 'upstream/main' into staticInterfacesBack
jtschuster 7f1064d
Choose correct merge conflict
jtschuster 15db395
use FullName in comparison
jtschuster 1751697
wip
jtschuster b10cd5f
Update docs/removal-behavior.md
jtschuster e3fbf12
Add explanation for workaround to remove interface implementation
jtschuster 098cb24
Merge branch 'staticInterfacesBack' of https://github.com/jtschuster/…
jtschuster 7467d82
wip
jtschuster 4efa5f2
Mark static interface implmentations in ProcessOverride
jtschuster c3a26ca
Format and update comment
jtschuster 62e3cc8
Use separate list for static interface methods
jtschuster 4359792
Add clarifying comments
jtschuster 060345e
Roll early exit case into function
jtschuster a050fdf
Move check back outside of interface override method
jtschuster 95df541
formatting
jtschuster 2b7a7cc
Use Override pair to store logic for IsStaticInterfaceMethodPair
jtschuster c00e1d1
Formatting
jtschuster 358b010
Move interfaceImpl marking out of ProcessOverride
jtschuster 5672f0f
Merge remote-tracking branch 'upstream/main' into staticInterfacesBack
jtschuster 432375d
PR Feedback
jtschuster File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -60,6 +60,7 @@ protected LinkContext Context { | |
|
||
protected Queue<(MethodDefinition, DependencyInfo, MessageOrigin)> _methods; | ||
protected List<(MethodDefinition, MarkScopeStack.Scope)> _virtual_methods; | ||
protected List<(MethodDefinition, MarkScopeStack.Scope)> _static_interface_methods; | ||
protected Queue<AttributeProviderPair> _assemblyLevelAttributes; | ||
readonly List<AttributeProviderPair> _ivt_attributes; | ||
protected Queue<(AttributeProviderPair, DependencyInfo, MarkScopeStack.Scope)> _lateMarkedAttributes; | ||
|
@@ -224,6 +225,7 @@ public MarkStep () | |
{ | ||
_methods = new Queue<(MethodDefinition, DependencyInfo, MessageOrigin)> (); | ||
_virtual_methods = new List<(MethodDefinition, MarkScopeStack.Scope)> (); | ||
_static_interface_methods = new List<(MethodDefinition, MarkScopeStack.Scope)> (); | ||
_assemblyLevelAttributes = new Queue<AttributeProviderPair> (); | ||
_ivt_attributes = new List<AttributeProviderPair> (); | ||
_lateMarkedAttributes = new Queue<(AttributeProviderPair, DependencyInfo, MarkScopeStack.Scope)> (); | ||
|
@@ -476,6 +478,7 @@ bool ProcessPrimaryQueue () | |
while (!QueueIsEmpty ()) { | ||
ProcessQueue (); | ||
ProcessVirtualMethods (); | ||
ProcessStaticInterfaceMethods (); | ||
ProcessMarkedTypesWithInterfaces (); | ||
ProcessDynamicCastableImplementationInterfaces (); | ||
ProcessPendingBodies (); | ||
|
@@ -576,6 +579,30 @@ void ProcessVirtualMethods () | |
} | ||
} | ||
|
||
/// <summary> | ||
/// Handles marking implementations of static interface methods and the interface implementations of types that implement a static interface method. | ||
/// </summary> | ||
void ProcessStaticInterfaceMethods () | ||
{ | ||
foreach ((MethodDefinition method, MarkScopeStack.Scope scope) in _static_interface_methods) { | ||
using (ScopeStack.PushScope (scope)) { | ||
var overrides = Annotations.GetOverrides (method); | ||
if (overrides != null) { | ||
foreach (OverrideInformation @override in overrides) { | ||
ProcessOverride (@override); | ||
// We need to mark the interface implementation for static interface methods | ||
// Explicit interface method implementations already mark the interface implementation in ProcessMethod | ||
MarkExplicitInterfaceImplementation (@override.Override, @override.Base); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// <summary> | ||
/// Does extra handling of marked types that have interfaces when it's necessary to know what types are marked or instantiated. | ||
/// Right now it only marks the "implements interface" annotations and removes override annotations for static interface methods. | ||
/// </summary> | ||
void ProcessMarkedTypesWithInterfaces () | ||
{ | ||
// We may mark an interface type later on. Which means we need to reprocess any time with one or more interface implementations that have not been marked | ||
|
@@ -693,6 +720,9 @@ void ProcessVirtualMethod (MethodDefinition method) | |
} | ||
} | ||
|
||
/// <summary> | ||
/// Handles marking overriding methods if the type with the overriding method is instantiated or if the base method is a static abstract interface method | ||
/// </summary> | ||
void ProcessOverride (OverrideInformation overrideInformation) | ||
{ | ||
var method = overrideInformation.Override; | ||
|
@@ -708,12 +738,12 @@ void ProcessOverride (OverrideInformation overrideInformation) | |
|
||
var isInstantiated = Annotations.IsInstantiated (method.DeclaringType); | ||
|
||
// We don't need to mark overrides until it is possible that the type could be instantiated | ||
// We don't need to mark overrides until it is possible that the type could be instantiated or the method is a static interface method | ||
// Note : The base type is interface check should be removed once we have base type sweeping | ||
if (IsInterfaceOverrideThatDoesNotNeedMarked (overrideInformation, isInstantiated)) | ||
return; | ||
|
||
// Interface static veitual methods will be abstract and will also by pass this check to get marked | ||
// Interface static virtual methods will be abstract and will also bypass this check to get marked | ||
if (!isInstantiated && [email protected] && Context.IsOptimizationEnabled (CodeOptimizations.OverrideRemoval, method)) | ||
return; | ||
|
||
|
@@ -736,8 +766,7 @@ bool IsInterfaceOverrideThatDoesNotNeedMarked (OverrideInformation overrideInfor | |
if (!overrideInformation.IsOverrideOfInterfaceMember || isInstantiated) | ||
return false; | ||
|
||
// This is a static interface method and these checks should all be true | ||
if (overrideInformation.Override.IsStatic && overrideInformation.Base.IsStatic && overrideInformation.Base.IsAbstract && !overrideInformation.Override.IsVirtual) | ||
if (overrideInformation.IsStaticInterfaceMethodPair) | ||
return false; | ||
|
||
if (overrideInformation.MatchingInterfaceImplementation != null) | ||
|
@@ -3054,17 +3083,28 @@ protected virtual void ProcessMethod (MethodDefinition method, in DependencyInfo | |
} | ||
} | ||
|
||
// Mark overridden methods and interface implementations except for static interface methods | ||
// This will not mark implicit interface methods because they do not have a MethodImpl and aren't in the .Overrides | ||
if (method.HasOverrides) { | ||
foreach (MethodReference ov in method.Overrides) { | ||
MarkMethod (ov, new DependencyInfo (DependencyKind.MethodImplOverride, method), ScopeStack.CurrentScope.Origin); | ||
MarkExplicitInterfaceImplementation (method, ov); | ||
foreach (MethodReference @base in method.Overrides) { | ||
// Method implementing a static interface method will have an override to it - note nonstatic methods usually don't unless they're explicit. | ||
// Calling the implementation method directly has no impact on the interface, and as such it should not mark the interface or its method. | ||
// Only if the interface method is referenced, then all the methods which implemented must be kept, but not the other way round. | ||
if (Context.Resolve (@base) is MethodDefinition baseDefinition | ||
&& new OverrideInformation.OverridePair (baseDefinition, method).IsStaticInterfaceMethodPair ()) | ||
continue; | ||
MarkMethod (@base, new DependencyInfo (DependencyKind.MethodImplOverride, method), ScopeStack.CurrentScope.Origin); | ||
MarkExplicitInterfaceImplementation (method, @base); | ||
} | ||
} | ||
|
||
MarkMethodSpecialCustomAttributes (method); | ||
if (method.IsVirtual) | ||
_virtual_methods.Add ((method, ScopeStack.CurrentScope)); | ||
|
||
if (method.IsStatic && method.IsAbstract && method.DeclaringType.IsInterface) | ||
_static_interface_methods.Add ((method, ScopeStack.CurrentScope)); | ||
|
||
MarkNewCodeDependencies (method); | ||
|
||
MarkBaseMethods (method); | ||
|
@@ -3147,9 +3187,9 @@ protected virtual IEnumerable<MethodDefinition> GetRequiredMethodsForInstantiate | |
} | ||
} | ||
|
||
void MarkExplicitInterfaceImplementation (MethodDefinition method, MethodReference ov) | ||
void MarkExplicitInterfaceImplementation (MethodDefinition method, MethodReference overriddenMethod) | ||
{ | ||
if (Context.Resolve (ov) is not MethodDefinition resolvedOverride) | ||
if (Context.Resolve (overriddenMethod) is not MethodDefinition resolvedOverride) | ||
return; | ||
|
||
if (resolvedOverride.DeclaringType.IsInterface) { | ||
|
@@ -3421,7 +3461,7 @@ void MarkInterfacesNeededByBodyStack (MethodBody body) | |
{ | ||
// If a type could be on the stack in the body and an interface it implements could be on the stack on the body | ||
// then we need to mark that interface implementation. When this occurs it is not safe to remove the interface implementation from the type | ||
// even if the type is never instantiated | ||
// even if the type is never instantiated. (ex. `Type1 x = null; IFoo y = (IFoo)x;`) | ||
var implementations = new InterfacesOnStackScanner (Context).GetReferencedInterfaces (body); | ||
if (implementations == null) | ||
return; | ||
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
test/ILLink.RoslynAnalyzer.Tests/Inheritance.Interfaces.StaticInterfaceMethodsTests.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// Copyright (c) .NET Foundation and contributors. All rights reserved. | ||
// Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
||
using System.Threading.Tasks; | ||
using Xunit; | ||
|
||
namespace ILLink.RoslynAnalyzer.Tests.Inheritance.Interfaces | ||
{ | ||
public sealed partial class StaticInterfaceMethodsTests : LinkerTestBase | ||
{ | ||
protected override string TestSuiteName => "Inheritance.Interfaces.StaticInterfaceMethods"; | ||
|
||
[Fact] | ||
public Task StaticAbstractInterfaceMethods () | ||
{ | ||
return RunTest (nameof (StaticAbstractInterfaceMethods)); | ||
} | ||
|
||
[Fact] | ||
public Task StaticAbstractInterfaceMethodsLibrary () | ||
{ | ||
return RunTest (nameof (StaticAbstractInterfaceMethodsLibrary)); | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.