-
Notifications
You must be signed in to change notification settings - Fork 128
Trim static interfaces #2741
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
Trim static interfaces #2741
Changes from 22 commits
0388e48
39ffb9f
e5b3788
61115f6
c4ebd45
d7cc881
3a8b76d
1c04ad2
be37029
7ad5afc
07143dd
a7c7e13
64692a0
4ea2991
578c686
798e87f
3fe5a52
9908282
3b03b77
4ae7888
7df4117
0b51131
e61826c
31a2447
6e1aead
52c4bb4
854d08b
14f71b9
8edb5ac
0c79f06
ca0e253
c32a329
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -592,6 +592,7 @@ void ProcessMarkedTypesWithInterfaces () | |
|
|
||
| using (ScopeStack.PushScope (scope)) { | ||
| MarkInterfaceImplementations (type); | ||
| RemoveStaticInterfaceOverrideAnnotations (type); | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -2265,6 +2266,24 @@ void MarkInterfaceImplementations (TypeDefinition type) | |
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Removes the 'override' annotation for implementation of static interface methods when the interface method is removed. | ||
| /// </summary> | ||
| void RemoveStaticInterfaceOverrideAnnotations (TypeDefinition type) | ||
| { | ||
| foreach (var method in type.Methods) { | ||
| // Modify overrides in place | ||
| for (int i = 0; i < method.Overrides.Count;) { | ||
| if (Context.Resolve(method.Overrides[i].DeclaringType)?.IsInterface == true | ||
| && Context.Resolve (method.Overrides[i])?.IsStatic == true | ||
| && !Annotations.IsMarked (method.Overrides[i])) | ||
| method.Overrides.RemoveAt (i); | ||
| else | ||
| i++; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void MarkGenericParameterProvider (IGenericParameterProvider provider) | ||
| { | ||
| if (!provider.HasGenericParameters) | ||
|
|
@@ -3049,8 +3068,15 @@ protected virtual void ProcessMethod (MethodDefinition method, in DependencyInfo | |
| } | ||
| } | ||
|
|
||
| // Mark overrides except for static interface methods | ||
|
||
| if (method.HasOverrides) { | ||
| foreach (MethodReference ov in method.Overrides) { | ||
| // Don't mark overrides for static interface methods. | ||
| // Since they can only be called on a concrete type and not the interface, these methods can safely be removed in some cases | ||
|
||
| if (Context.Resolve (ov)?.IsStatic == true | ||
| && Context.Resolve (ov.DeclaringType)?.IsInterface == true) { | ||
| continue; | ||
| } | ||
| MarkMethod (ov, new DependencyInfo (DependencyKind.MethodImplOverride, method)); | ||
| MarkExplicitInterfaceImplementation (method, ov); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,29 +20,61 @@ public static void Main () | |
| t = typeof (UninstantiatedPublicClassWithImplicitlyImplementedInterface); | ||
| t = typeof (UninstantiatedPublicClassWithPrivateInterface); | ||
|
|
||
| UninstantiatedPublicClassWithInterface.InternalStaticInterfaceMethodUsed (); | ||
| InstantiatedClassWithInterfaces.InternalStaticInterfaceMethodUsed (); | ||
|
|
||
| UninstantiatedPublicClassWithInterface.InternalStaticInterfaceMethodUsedThroughImplementation (); | ||
|
||
| InstantiatedClassWithInterfaces.InternalStaticInterfaceMethodUsedThroughImplementation (); | ||
| GenericMethodThatCallsInternalStaticInterfaceMethod | ||
| <ImplementsInternalStaticInterfaceWithInterfaceDefinitionUsedThroughGeneric> (); | ||
| typeof (InterfaceVariants).GetMethod ("GenericMethodThatCallsInternalStaticInterfaceMethod") | ||
| .MakeGenericMethod (typeof (ImplementsInternalStaticInterfaceWithInterfaceDefinitionNotUsedThroughGeneric)) | ||
| .Invoke (null, null); | ||
| // Use all public interfaces - they're marked as public only to denote them as "used" | ||
| typeof (IPublicInterface).RequiresPublicMethods (); | ||
| typeof (IPublicStaticInterface).RequiresPublicMethods (); | ||
|
|
||
| var a = new InstantiatedClassWithInterfaces (); | ||
| } | ||
|
|
||
| [Kept] | ||
| internal static void GenericMethodThatCallsInternalStaticInterfaceMethod<T> () where T : IInternalStaticInterfaceWithInterfaceDefinitionUsed | ||
|
||
| { | ||
| T.InternalStaticInterfaceUsedThroughInterface (); | ||
| } | ||
|
|
||
| [Kept] | ||
| [KeptInterface (typeof (IInternalStaticInterfaceWithInterfaceDefinitionUsed))] | ||
| internal class ImplementsInternalStaticInterfaceWithInterfaceDefinitionUsedThroughGeneric : IInternalStaticInterfaceWithInterfaceDefinitionUsed | ||
| { | ||
| [Kept] | ||
| public static void InternalStaticInterfaceUsedThroughInterface () | ||
| { | ||
| } | ||
| public static void UnusedMethod () { } | ||
| } | ||
|
|
||
| [Kept] | ||
| [KeptInterface (typeof (IInternalStaticInterfaceWithInterfaceDefinitionUsed))] | ||
| internal class ImplementsInternalStaticInterfaceWithInterfaceDefinitionNotUsedThroughGeneric : IInternalStaticInterfaceWithInterfaceDefinitionUsed | ||
|
||
| { | ||
| [Kept] | ||
| public static void InternalStaticInterfaceUsedThroughInterface () | ||
| { | ||
| } | ||
|
|
||
| public static void UnusedMethod () { } | ||
| } | ||
|
|
||
| [Kept] | ||
| [KeptInterface (typeof (IEnumerator))] | ||
| [KeptInterface (typeof (IPublicInterface))] | ||
| [KeptInterface (typeof (IPublicStaticInterface))] | ||
| [KeptInterface (typeof (IInternalStaticInterfaceWithUsedMethod))] // https://github.com/dotnet/linker/issues/2733 | ||
| [KeptInterface (typeof (ICopyLibraryInterface))] | ||
| [KeptInterface (typeof (ICopyLibraryStaticInterface))] | ||
| public class UninstantiatedPublicClassWithInterface : | ||
| IPublicInterface, | ||
| IPublicStaticInterface, | ||
| IInternalInterface, | ||
| IInternalStaticInterface, | ||
| IInternalStaticInterfaceWithUsedMethod, | ||
| IInternalStaticInterfaceWithUsedMethodImplementation, | ||
| IEnumerator, | ||
| ICopyLibraryInterface, | ||
| ICopyLibraryStaticInterface | ||
|
|
@@ -70,7 +102,7 @@ public static void InternalStaticInterfaceMethod () { } | |
| static void IInternalStaticInterface.ExplicitImplementationInternalStaticInterfaceMethod () { } | ||
|
|
||
| [Kept] | ||
| public static void InternalStaticInterfaceMethodUsed () { } | ||
| public static void InternalStaticInterfaceMethodUsedThroughImplementation () { } | ||
|
|
||
| [Kept] | ||
| [ExpectBodyModified] | ||
|
|
@@ -122,15 +154,14 @@ public string ToString (string format, IFormatProvider formatProvider) | |
| [KeptInterface (typeof (IEnumerator))] | ||
| [KeptInterface (typeof (IPublicInterface))] | ||
| [KeptInterface (typeof (IPublicStaticInterface))] | ||
| [KeptInterface (typeof (IInternalStaticInterfaceWithUsedMethod))] // https://github.com/dotnet/linker/issues/2733 | ||
| [KeptInterface (typeof (ICopyLibraryInterface))] | ||
| [KeptInterface (typeof (ICopyLibraryStaticInterface))] | ||
| public class InstantiatedClassWithInterfaces : | ||
| IPublicInterface, | ||
| IPublicStaticInterface, | ||
| IInternalInterface, | ||
| IInternalStaticInterface, | ||
| IInternalStaticInterfaceWithUsedMethod, | ||
| IInternalStaticInterfaceWithUsedMethodImplementation, | ||
| IEnumerator, | ||
| ICopyLibraryInterface, | ||
| ICopyLibraryStaticInterface | ||
|
|
@@ -159,7 +190,7 @@ public static void InternalStaticInterfaceMethod () { } | |
| static void IInternalStaticInterface.ExplicitImplementationInternalStaticInterfaceMethod () { } | ||
|
|
||
| [Kept] | ||
| public static void InternalStaticInterfaceMethodUsed () { } | ||
| public static void InternalStaticInterfaceMethodUsedThroughImplementation () { } | ||
|
|
||
| [Kept] | ||
| bool IEnumerator.MoveNext () { throw new PlatformNotSupportedException (); } | ||
|
|
@@ -225,13 +256,20 @@ internal interface IInternalStaticInterface | |
| static abstract void ExplicitImplementationInternalStaticInterfaceMethod (); | ||
| } | ||
|
|
||
| // The interface methods themselves are not used, but the implentation of these methods is | ||
| // https://github.com/dotnet/linker/issues/2733 | ||
| // The interface methods themselves are not used, but the implementation of these methods is | ||
| internal interface IInternalStaticInterfaceWithUsedMethodImplementation | ||
| { | ||
| static abstract void InternalStaticInterfaceMethodUsedThroughImplementation (); | ||
| } | ||
|
|
||
| // The interface methods themselves are used through the interface | ||
| [Kept] | ||
| internal interface IInternalStaticInterfaceWithUsedMethod | ||
| internal interface IInternalStaticInterfaceWithInterfaceDefinitionUsed | ||
| { | ||
| [Kept] // https://github.com/dotnet/linker/issues/2733 | ||
| static abstract void InternalStaticInterfaceMethodUsed (); | ||
| [Kept] | ||
| static abstract void InternalStaticInterfaceUsedThroughInterface (); | ||
|
|
||
| static abstract void UnusedMethod (); | ||
| } | ||
|
|
||
| private interface IPrivateInterface | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to write a test for this?
I don't know if the test infra actually verifies the "overrides" (maybe it should at least as an opt-in)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't look like there's anything in the test infra yet, but I'll look into it more and add a
KeptOverride/NoOverrideattribute to check for thatThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added a couple attribute to check for the presence/removal of the override annotations. They only perform a check if there is a
KeptOverrideorRemovedOverride, so it doesn't affect any other tests.