Skip to content
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

Re-add static interface trimming with more testing #2791

Merged
merged 36 commits into from
Jun 14, 2022

Conversation

jtschuster
Copy link
Member

@jtschuster jtschuster commented May 11, 2022

Revert the commit that reverted static interface trimming and add more comprehensive tests.

I also found that sometimes method.Overrides[i].Resolve() would sometimes return null if it had been removed earlier, in which case we want to remove the override. The code has been updated to account for this case.

This is missing library tests, but I wanted to make sure that I don't miss any cases before I move to that. Please let me know if there are any other cases that needs to be tested.

@jtschuster jtschuster marked this pull request as ready for review May 17, 2022 15:37
@jtschuster jtschuster requested a review from marek-safar as a code owner May 17, 2022 15:37
@jtschuster jtschuster requested a review from vitek-karas May 25, 2022 17:10
if (Context.Resolve (ov)?.IsStatic == true
&& Context.Resolve (ov.DeclaringType)?.IsInterface == true
// Public methods can be called directly on the type. If it non-public it is an explicit implementation and if the method is marked we need to mark the override.
&& method.IsPublic) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What goes wrong if we don't mark the interface method for non-public explicit implementations here? I understand that the non-public implementation will only be reached through the interface method, but I would have roughly expected that logic elsewhere, in ProcessMarkedTypesWithInterfaces, would take care of that. This looks like it's doing the right thing but I would like to understand if it's necessary.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also prefer we remove the IsPublic check. What if it's internal and there InternalsVisibleTo applied - then it is effectively public. Visibility should have basically no effect here. The explicit implementation methods should be never accessed directly as C# doesn't let you do that - so the only way is via reflection, and in that case I think it's OK to overmark, definitely not something I would optimize for.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically speaking we should be able to avoid marking overrides for interface methods completely (not just for static interfaces) - but that's a bigger/unrelated change. (And it is unlikely to actually do much since non-static interfaces do not use overrides in C#, so the cases where this would happen are very limitted in real world).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I can tell, a method implementing an interface method must be public to implicitly implement the method or private for an explicit implementation, so I don't think we'd run into the internal with InternalsVisibleTo issue.

I think I could avoid of complication by looking from base -> override instead of override -> base like I do here. I didn't see the AnnotationStore.GetOverrides which does the inverse of Cecil's .Overrides. If I check if a method is a static interface method and kept, I can mark the methods in GetOverrides as well as the interface implementation.

I would have roughly expected that logic elsewhere, in ProcessMarkedTypesWithInterfaces
I agree, that would make sense to me, but at the moment that method returns early if the method isn't instantiated (or relevant to variant casting). If we move the logic for static interface methods, we'd have to iterate through each method to check if any override a static interface method before we can say we don't need an interface implementation. I think it could be worth it to keep the related logic in the same place though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I though that the reverse direction (from base to overrides) is already there in the ProcessMarkedTypesWithInterfaces (or similar). The code here is the opposite direction which is from implementation (override) to base.

Copy link
Member Author

@jtschuster jtschuster Jun 2, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think here we should keep marking interface implementations for explicitly implemented non-static interface methods. I moved the logic to mark static interface method overrides from base -> implementation to ProcessOverrides.

If we move marking the interface implementations for explicit interface method implementations to ProcessMArkedTypesWithInterfaces we would be duplicating work with ProcessMethod because we'd have to look at each method in the type to determine if we should keep the interface impl.

I think the way it is now makes the most sense to me, but I can rework if we think another way would be better.

Copy link
Member

@agocke agocke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM aside from a few small questions

@agocke
Copy link
Member

agocke commented May 27, 2022

Also, great test coverage here!

@jtschuster
Copy link
Member Author

I've updated the marking of static interface methods to be like the virtual method marking, where we store each static interface method that is marked, then mark the overriding methods later. The interface implementations are then marked in ProcessOverride.

@jtschuster jtschuster requested review from sbomer and agocke June 7, 2022 17:23
Copy link
Member

@sbomer sbomer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, great work! I'd suggest running this against runtime locally before we merge, if you haven't already.

@jtschuster jtschuster merged commit ff646e0 into dotnet:main Jun 14, 2022
jtschuster added a commit to jtschuster/linker that referenced this pull request Jun 15, 2022
sbomer pushed a commit that referenced this pull request Jun 16, 2022
jtschuster added a commit to jtschuster/linker that referenced this pull request Jun 22, 2022
sbomer added a commit to sbomer/linker that referenced this pull request Jun 24, 2022
jtschuster added a commit to jtschuster/linker that referenced this pull request Jun 24, 2022
agocke pushed a commit to dotnet/runtime that referenced this pull request Nov 16, 2022
Enables more precise removal of static abstract interface methods and adds more tests than previously.

Co-authored-by: Sven Boemer <sbomer@gmail.com>

Commit migrated from dotnet/linker@ff646e0
agocke pushed a commit to dotnet/runtime that referenced this pull request Nov 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants