-
Notifications
You must be signed in to change notification settings - Fork 4.8k
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
Mark most specific static DIM for types marked RelevantToVariantCasting #97487
Changes from all commits
17c2fd7
642385d
565837a
c3b0c63
e6d3788
bb67ff1
8b8a83d
8851aae
e5b4a19
4beb771
a46a642
601658a
5520d85
64e9f86
57ca7d1
c47c7d3
694443a
1f0cdee
42d8764
386f644
cab167f
bc4470f
51be916
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 |
---|---|---|
|
@@ -65,7 +65,6 @@ protected LinkContext Context { | |
readonly List<AttributeProviderPair> _ivt_attributes; | ||
protected Queue<(AttributeProviderPair, DependencyInfo, MarkScopeStack.Scope)> _lateMarkedAttributes; | ||
protected List<(TypeDefinition, MarkScopeStack.Scope)> _typesWithInterfaces; | ||
protected HashSet<(OverrideInformation, MarkScopeStack.Scope)> _interfaceOverrides; | ||
protected HashSet<AssemblyDefinition> _dynamicInterfaceCastableImplementationTypesDiscovered; | ||
protected List<TypeDefinition> _dynamicInterfaceCastableImplementationTypes; | ||
protected List<(MethodBody, MarkScopeStack.Scope)> _unreachableBodies; | ||
|
@@ -226,7 +225,6 @@ public MarkStep () | |
_ivt_attributes = new List<AttributeProviderPair> (); | ||
_lateMarkedAttributes = new Queue<(AttributeProviderPair, DependencyInfo, MarkScopeStack.Scope)> (); | ||
_typesWithInterfaces = new List<(TypeDefinition, MarkScopeStack.Scope)> (); | ||
_interfaceOverrides = new HashSet<(OverrideInformation, MarkScopeStack.Scope)> (); | ||
_dynamicInterfaceCastableImplementationTypesDiscovered = new HashSet<AssemblyDefinition> (); | ||
_dynamicInterfaceCastableImplementationTypes = new List<TypeDefinition> (); | ||
_unreachableBodies = new List<(MethodBody, MarkScopeStack.Scope)> (); | ||
|
@@ -573,9 +571,10 @@ protected virtual void EnqueueMethod (MethodDefinition method, in DependencyInfo | |
|
||
void ProcessVirtualMethods () | ||
{ | ||
foreach ((MethodDefinition method, MarkScopeStack.Scope scope) in _virtual_methods) { | ||
using (ScopeStack.PushScope (scope)) | ||
foreach ((var method, var scope) in _virtual_methods) { | ||
using (ScopeStack.PushScope (scope)) { | ||
ProcessVirtualMethod (method); | ||
} | ||
} | ||
} | ||
|
||
|
@@ -603,26 +602,19 @@ void ProcessMarkedTypesWithInterfaces () | |
!unusedInterfacesOptimizationEnabled) { | ||
MarkInterfaceImplementations (type); | ||
} | ||
// OverrideInformation for interfaces in PreservedScope aren't added yet | ||
// Interfaces in PreservedScope should have their methods added to _virtual_methods so that they are properly processed | ||
foreach (var method in type.Methods) { | ||
var baseOverrideInformations = Annotations.GetBaseMethods (method); | ||
if (baseOverrideInformations is null) | ||
var baseMethods = Annotations.GetBaseMethods (method); | ||
if (baseMethods is null) | ||
continue; | ||
foreach (var ov in baseOverrideInformations) { | ||
if (ov.Base.DeclaringType is not null && ov.Base.DeclaringType.IsInterface && IgnoreScope (ov.Base.DeclaringType.Scope)) | ||
_interfaceOverrides.Add ((ov, ScopeStack.CurrentScope)); | ||
foreach (var ov in baseMethods) { | ||
if (ov.Base.DeclaringType is not null && ov.Base.DeclaringType.IsInterface && IgnoreScope (ov.Base.DeclaringType.Scope)) { | ||
_virtual_methods.Add ((ov.Base, ScopeStack.CurrentScope)); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
var interfaceOverrides = _interfaceOverrides.ToArray (); | ||
foreach ((var overrideInformation, var scope) in interfaceOverrides) { | ||
using (ScopeStack.PushScope (scope)) { | ||
if (IsInterfaceImplementationMethodNeededByTypeDueToInterface (overrideInformation)) | ||
MarkMethod (overrideInformation.Override, new DependencyInfo (DependencyKind.Override, overrideInformation.Base), scope.Origin); | ||
} | ||
} | ||
} | ||
|
||
void DiscoverDynamicCastableImplementationInterfaces () | ||
|
@@ -705,10 +697,23 @@ void ProcessVirtualMethod (MethodDefinition method) | |
{ | ||
Annotations.EnqueueVirtualMethod (method); | ||
|
||
var defaultImplementations = Annotations.GetDefaultInterfaceImplementations (method); | ||
if (defaultImplementations != null) { | ||
foreach (var defaultImplementationInfo in defaultImplementations) { | ||
ProcessDefaultImplementation (defaultImplementationInfo.InstanceType, defaultImplementationInfo.ProvidingInterface); | ||
if (method.DeclaringType.IsInterface) { | ||
var defaultImplementations = Annotations.GetDefaultInterfaceImplementations (method); | ||
if (defaultImplementations is not null) { | ||
foreach (var dimInfo in defaultImplementations) { | ||
ProcessDefaultImplementation (dimInfo.ImplementingType, dimInfo.InterfaceImpl, dimInfo.DefaultInterfaceMethod); | ||
|
||
var ov = new OverrideInformation (method, dimInfo.DefaultInterfaceMethod, Context); | ||
if (IsInterfaceImplementationMethodNeededByTypeDueToInterface (ov, dimInfo.ImplementingType)) | ||
MarkMethod (ov.Override, new DependencyInfo (DependencyKind.Override, ov.Base), ScopeStack.CurrentScope.Origin); | ||
} | ||
} | ||
var overridingMethods = Annotations.GetOverrides (method); | ||
if (overridingMethods is not null) { | ||
foreach (var ov in overridingMethods) { | ||
if (IsInterfaceImplementationMethodNeededByTypeDueToInterface (ov, ov.Override.DeclaringType)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't that situation be caught above when we loop over the default implementations of each interface method? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose that's true. It still worries me that the override declaring type isn't the same as the type with interfaceimpl that IsInterfaceImplementationMethodNeededByTypeDueToInterface expects. But I can't think of any problems with it. Maybe add a comment here in case we need to look at this again? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought about this a little more, and it's another one of those "It works all the time, but not because the logic is correct". The DIM logic above handles static interface methods since they cannot be provided by a base type, only a DIM, but If a base method provides an instance method, we'll pass the wrong type in, but if the type implementing the interface is instantiated, the type providing the method for the interface will also be marked as instantiated. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like this fix is going to be more involved. I'll make an issue and make a separate PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think static interface methods can be provided by a base type: https://sharplab.io/#v2:EYLgtghglgdgNAFxAJwK7wCYgNQB8ACATAIwCwAUBQBICmANgA43IB0AIgPYA8bAfABQBKANwUKRAAS1GzCQG8KEpRPwBmFcQBsKgCwTOXACoDBEgO4ALZjQmGJICQEl5i5W8MsAskNHk3AXwpAynJYBGYAMwgAYxtnBT9lfC0JCGAAZwRkGIRdCW8RILFySQBhF0SlNQ1tfD0Circ3ZIBOfgAiWgBPdsLKiWDg8UJ9ewlSuCd5YKA== There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uh oh, I really thought static interface methods couldn't be provided by the base, and I think that assumption might be made in the linker. I'll set up some test cases and see what's broken. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, the compiler injects a static interface method on D that call's C's method, so I think the assumption is valid. If the implementation on D is removed, ilasm can compile the IL, but it fails at runtime with a TypeLoadException:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, good to know. Thanks for checking! |
||
MarkMethod (ov.Override, new DependencyInfo (DependencyKind.Override, ov.Base), ScopeStack.CurrentScope.Origin); | ||
} | ||
} | ||
} | ||
} | ||
|
@@ -724,10 +729,8 @@ bool ShouldMarkOverrideForBase (OverrideInformation overrideInformation) | |
Debug.Assert (Annotations.IsMarked (overrideInformation.Base) || IgnoreScope (overrideInformation.Base.DeclaringType.Scope)); | ||
if (!Annotations.IsMarked (overrideInformation.Override.DeclaringType)) | ||
sbomer marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return false; | ||
if (overrideInformation.IsOverrideOfInterfaceMember) { | ||
_interfaceOverrides.Add ((overrideInformation, ScopeStack.CurrentScope)); | ||
if (overrideInformation.IsOverrideOfInterfaceMember) | ||
return false; | ||
} | ||
|
||
if (!Context.IsOptimizationEnabled (CodeOptimizations.OverrideRemoval, overrideInformation.Override)) | ||
return true; | ||
|
@@ -816,9 +819,10 @@ bool RequiresInterfaceRecursively (TypeDefinition typeToExamine, TypeDefinition | |
return false; | ||
} | ||
|
||
void ProcessDefaultImplementation (TypeDefinition typeWithDefaultImplementedInterfaceMethod, InterfaceImplementation implementation) | ||
void ProcessDefaultImplementation (TypeDefinition typeWithDefaultImplementedInterfaceMethod, InterfaceImplementation implementation, MethodDefinition implementationMethod) | ||
{ | ||
if (!Annotations.IsInstantiated (typeWithDefaultImplementedInterfaceMethod)) | ||
if ((!implementationMethod.IsStatic && !Annotations.IsInstantiated (typeWithDefaultImplementedInterfaceMethod)) | ||
|| implementationMethod.IsStatic && !Annotations.IsRelevantToVariantCasting (typeWithDefaultImplementedInterfaceMethod)) | ||
return; | ||
|
||
MarkInterfaceImplementation (implementation); | ||
|
@@ -2275,9 +2279,9 @@ void MarkTypeWithDebuggerDisplayAttribute (TypeDefinition type, CustomAttribute | |
// Record a logical dependency on the attribute so that we can blame it for the kept members below. | ||
Tracer.AddDirectDependency (attribute, new DependencyInfo (DependencyKind.CustomAttribute, type), marked: false); | ||
|
||
MarkTypeWithDebuggerDisplayAttributeValue(type, attribute, (string) attribute.ConstructorArguments[0].Value); | ||
MarkTypeWithDebuggerDisplayAttributeValue (type, attribute, (string) attribute.ConstructorArguments[0].Value); | ||
if (attribute.HasProperties) { | ||
foreach (var property in attribute.Properties) { | ||
foreach (var property in attribute.Properties) { | ||
if (property.Name is "Name" or "Type") { | ||
MarkTypeWithDebuggerDisplayAttributeValue (type, attribute, (string) property.Argument.Value); | ||
} | ||
|
@@ -2545,19 +2549,17 @@ bool IsMethodNeededByTypeDueToPreservedScope (MethodDefinition method) | |
/// <summary> | ||
/// 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. | ||
/// </summary> | ||
bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformation overrideInformation) | ||
bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformation overrideInformation, TypeDefinition typeThatImplsInterface) | ||
{ | ||
var @base = overrideInformation.Base; | ||
var method = overrideInformation.Override; | ||
Debug.Assert (@base.DeclaringType.IsInterface); | ||
if (@base is null || method is null || @base.DeclaringType is null) | ||
return false; | ||
|
||
if (Annotations.IsMarked (method)) | ||
return false; | ||
|
||
if ([email protected]) | ||
return false; | ||
|
||
// If the interface implementation is not marked, do not mark the implementation method | ||
// A type that doesn't implement the interface isn't required to have methods that implement the interface. | ||
InterfaceImplementation? iface = overrideInformation.MatchingInterfaceImplementation; | ||
|
@@ -2578,12 +2580,12 @@ bool IsInterfaceImplementationMethodNeededByTypeDueToInterface (OverrideInformat | |
// If the method is static and the implementing type is relevant to variant casting, mark the implementation method. | ||
// A static method may only be called through a constrained call if the type is relevant to variant casting. | ||
if (@base.IsStatic) | ||
return Annotations.IsRelevantToVariantCasting (method.DeclaringType) | ||
return Annotations.IsRelevantToVariantCasting (typeThatImplsInterface) | ||
|| IgnoreScope (@base.DeclaringType.Scope); | ||
|
||
// If the implementing type is marked as instantiated, mark the implementation method. | ||
// If the type is not instantiated, do not mark the implementation method | ||
return Annotations.IsInstantiated (method.DeclaringType); | ||
return Annotations.IsInstantiated (typeThatImplsInterface); | ||
} | ||
|
||
static bool IsSpecialSerializationConstructor (MethodDefinition method) | ||
|
@@ -3231,7 +3233,7 @@ protected virtual void ProcessMethod (MethodDefinition method, in DependencyInfo | |
} else if (method.TryGetProperty (out PropertyDefinition? property)) | ||
MarkProperty (property, new DependencyInfo (PropagateDependencyKindToAccessors (reason.Kind, DependencyKind.PropertyOfPropertyMethod), method)); | ||
else if (method.TryGetEvent (out EventDefinition? @event)) { | ||
MarkEvent (@event, new DependencyInfo (PropagateDependencyKindToAccessors(reason.Kind, DependencyKind.EventOfEventMethod), method)); | ||
MarkEvent (@event, new DependencyInfo (PropagateDependencyKindToAccessors (reason.Kind, DependencyKind.EventOfEventMethod), method)); | ||
} | ||
|
||
if (method.HasMetadataParameters ()) { | ||
|
@@ -3315,7 +3317,7 @@ protected virtual void DoAdditionalMethodProcessing (MethodDefinition method) | |
{ | ||
} | ||
|
||
static DependencyKind PropagateDependencyKindToAccessors(DependencyKind parentDependencyKind, DependencyKind kind) | ||
static DependencyKind PropagateDependencyKindToAccessors (DependencyKind parentDependencyKind, DependencyKind kind) | ||
{ | ||
switch (parentDependencyKind) { | ||
// If the member is marked due to descriptor or similar, propagate the original reason to suppress some warnings correctly | ||
|
@@ -3335,11 +3337,11 @@ void MarkImplicitlyUsedFields (TypeDefinition type) | |
return; | ||
|
||
// keep fields for types with explicit layout, for enums and for InlineArray types | ||
if (!type.IsAutoLayout || type.IsEnum || TypeIsInlineArrayType(type)) | ||
if (!type.IsAutoLayout || type.IsEnum || TypeIsInlineArrayType (type)) | ||
MarkFields (type, includeStatic: type.IsEnum, reason: new DependencyInfo (DependencyKind.MemberOfType, type)); | ||
} | ||
|
||
static bool TypeIsInlineArrayType(TypeDefinition type) | ||
static bool TypeIsInlineArrayType (TypeDefinition type) | ||
{ | ||
if (!type.IsValueType) | ||
return false; | ||
|
@@ -3584,7 +3586,7 @@ protected internal virtual void MarkEvent (EventDefinition evt, in DependencyInf | |
|
||
MarkCustomAttributes (evt, new DependencyInfo (DependencyKind.CustomAttribute, evt)); | ||
|
||
DependencyKind dependencyKind = PropagateDependencyKindToAccessors(reason.Kind, DependencyKind.EventMethod); | ||
DependencyKind dependencyKind = PropagateDependencyKindToAccessors (reason.Kind, DependencyKind.EventMethod); | ||
MarkMethodIfNotNull (evt.AddMethod, new DependencyInfo (dependencyKind, evt), ScopeStack.CurrentScope.Origin); | ||
MarkMethodIfNotNull (evt.InvokeMethod, new DependencyInfo (dependencyKind, evt), ScopeStack.CurrentScope.Origin); | ||
MarkMethodIfNotNull (evt.RemoveMethod, new DependencyInfo (dependencyKind, evt), ScopeStack.CurrentScope.Origin); | ||
|
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.
I wonder if this might be causing the perf regression dotnet/sdk#40060, since it looks like we're newly walking the overrides of a virtual method every time we get here.