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

Make all of the trimmer aware of recursive interfaces #103317

Open
wants to merge 34 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d6564fb
Use recursive-interface-aware types and methods in TypeMapInfo
jtschuster Jun 4, 2024
8e94b0e
Use RuntimeInterfaceImplementations in MethodBodyScanner and ICustomM…
jtschuster Jun 11, 2024
cf304f2
Add remarks regarding checking for marked ifaceImpls with RuntimeInte…
jtschuster Jun 11, 2024
fcbfbb7
Fix ApiCompat failures
jtschuster Jun 11, 2024
e5e0679
Fix visibility and sealed warnings
jtschuster Jun 11, 2024
451b677
Add DAM annotations for recursive interfaces
jtschuster Jun 12, 2024
1e1451a
Add additional test cases and annotate reflection types with ifdefs
jtschuster Jun 13, 2024
ac5a42a
Use suppressions instead of annotating in System.Reflection.Context
jtschuster Jun 13, 2024
4e1f6f9
Add test and fix for failure in System.Text.Json
jtschuster Jun 17, 2024
f8dad6b
Check for implementor to be marked, not overridingMethod.DeclaringType
jtschuster Jun 17, 2024
2aca6c4
PR feedback:
jtschuster Jun 17, 2024
bb308c7
Separate out generic DIM matching fix
jtschuster Jun 18, 2024
4848448
Remove unused parameter, Add test case
jtschuster Jun 26, 2024
dec9745
Mark all implementation chains for interface impls
jtschuster Jun 27, 2024
a7d785f
Merge branch 'main' of https://github.com/dotnet/runtime into Recursi…
jtschuster Jun 28, 2024
b6f101a
Add runtimeInterface assertions to tests
jtschuster Jun 28, 2024
66a3bcf
Add tests for TypeMapInfo
jtschuster Jun 28, 2024
4fa8ec5
Merge branch 'main' of https://github.com/dotnet/runtime into Recursi…
jtschuster Aug 12, 2024
e524b22
Move RuntimeInterfaces algorithm to its own class
jtschuster Aug 12, 2024
a8dfbd3
Inline the runtime interfaces helpers to avoid confusing naming
jtschuster Aug 12, 2024
d6cc947
Merge branch 'main' of https://github.com/dotnet/runtime into Recursi…
jtschuster Aug 12, 2024
01e4518
Undo unrelated formatting
jtschuster Aug 13, 2024
df04484
Revert "Implement type name resolution for ILLink analyzer (#106209)"
jtschuster Aug 13, 2024
ce27822
Don't warn on methods implementing iface methods if the iface is not …
jtschuster Aug 16, 2024
db02cfb
Undo unrelated analyzer changes
jtschuster Aug 16, 2024
d88b219
Undo unrelated formatting
jtschuster Aug 16, 2024
259d734
Revert suppression changes
jtschuster Aug 16, 2024
291e2e6
Merge branch 'main' of https://github.com/dotnet/runtime into Recursi…
jtschuster Aug 16, 2024
1d769b2
Undo unrelated formatting
jtschuster Aug 16, 2024
6a01772
Merge branch 'main' of https://github.com/dotnet/runtime into Recursi…
jtschuster Aug 19, 2024
756d90e
Add 'as' cast to RuntimeInterfacesAlgorithm
jtschuster Aug 20, 2024
626934c
Add generated analyzer tests
jtschuster Aug 20, 2024
41acd7e
Merge branch 'main' into RecursiveGenericInterfaces
jtschuster Aug 23, 2024
f285f9a
Merge branch 'main' into RecursiveGenericInterfaces
jtschuster Aug 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -523,5 +523,53 @@
<property name="Scope">member</property>
<property name="Target">M:System.Reflection.Context.Delegation.DelegatingType.GetInterface(System.String,System.Boolean)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
Copy link
Member Author

Choose a reason for hiding this comment

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

Now that the linker recognizes interfaces on all base types and recursive interfaces, these methods are recognized as overrides of IReflect, which are marked with DAM. There are already suppressions for other missing DAM annotations, so I decided to just suppress the warnings.

Copy link
Member

Choose a reason for hiding this comment

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

These are warning due to interfaces declared on the base type (or recursively required by interfaces declared on the base type), right? For example:

using System;
using System.Diagnostics.CodeAnalysis;
_ = 0;
class Base : I {
    public virtual void M() {} // warning IL2046
}
class Derived : Base {
    public override void M() {} // is this change introducing a warning here?
}
interface I {
    [RequiresUnreferencedCode("RUC")]
    void M();
}

I think this example should only warn on the Base virtual method. If Derived also had an interfaceimpl of I, then I would expect to see a warning on Derived.M as well.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, that makes sense to me. But them should we warn if both have an interfaceimpl of I but Derived doesn't override M()? I lean towards yes in this case.

class Base : I
{
    public virtual void M() {} // warning IL2046
}

class Derived : Base, I // Warning IL2046 pointing to Base.M()?
{ }

interface I
{
    [RequiresUnreferencedCode("RUC")]
    void M();
}

Also, in IL it is valid for an abstract Base to implement I but provide no methods. I assume we should still warn on Derived.M() in this case.

abstract class Base : I
{
}

class Derived : Base
{
    public virtual void M() {} // warning IL2046
}

interface I
{
    [RequiresUnreferencedCode("RUC")]
    void M();
}

Copy link
Member

Choose a reason for hiding this comment

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

But them should we warn if both have an interfaceimpl of I but Derived doesn't override M()? I lean towards yes in this case.

Agreed - in general I think a good model to follow is the compiler error behavior when types don't implement interface methods due to mismatching signatures.

Your second example is interesting, thanks for pointing that out. I agree that one should warn on Derived.M, but it kind of breaks the way I've been thinking about this. I wanted the rule to be roughly:
"When a type requires an interface (via explicit or recursive .interfaceimpl), there should be a warning on any mismatching annotations for the method implementing a method of that interface - pointing to either a method on the type, or the type (if the implementing method isn't on the type itself)."

Now I think we need to add something like:
"When a type requires an interface via an explicit/recursive .interfaceimpl on an abstract (maybe recursive) base class, and none of the base classes provides an implementation of one of the interface methods...".

I think it would be fine to implement the simpler rule in this PR and make a separate change to deal with the abstract case.

Copy link
Member Author

Choose a reason for hiding this comment

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

Implemented the simple warning behavior and filed #106552

<argument>ILLink</argument>
<argument>IL2094</argument>
<property name="Scope">member</property>
<property name="Target">M:System.Reflection.Context.Projection.ProjectingType.GetField(System.String,System.Reflection.BindingFlags)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2094</argument>
<property name="Scope">member</property>
<property name="Target">M:System.Reflection.Context.Projection.ProjectingType.GetFields(System.Reflection.BindingFlags)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2094</argument>
<property name="Scope">member</property>
<property name="Target">M:System.Reflection.Context.Projection.ProjectingType.GetMembers(System.Reflection.BindingFlags)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2094</argument>
<property name="Scope">member</property>
<property name="Target">M:System.Reflection.Context.Projection.ProjectingType.GetMethods(System.Reflection.BindingFlags)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2094</argument>
<property name="Scope">member</property>
<property name="Target">M:System.Reflection.Context.Projection.ProjectingType.GetProperties(System.Reflection.BindingFlags)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2094</argument>
<property name="Scope">member</property>
<property name="Target">M:System.Reflection.Context.Projection.ProjectingType.GetDefaultMembers()</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2094</argument>
<property name="Scope">member</property>
<property name="Target">M:System.Reflection.Context.Custom.CustomType.GetProperties(System.Reflection.BindingFlags)</property>
</attribute>
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
<argument>ILLink</argument>
<argument>IL2094</argument>
<property name="Scope">member</property>
<property name="Target">M:System.Reflection.Context.Custom.CustomType.GetMethods(System.Reflection.BindingFlags)</property>
</attribute>
</assembly>
</linker>
</linker>
Original file line number Diff line number Diff line change
Expand Up @@ -544,8 +544,8 @@ void ValidateMethodGenericParametersHaveNoAnnotations (DynamicallyAccessedMember

void LogValidationWarning (IMetadataTokenProvider provider, IMetadataTokenProvider baseProvider, OverrideInformation ov)
{
IMemberDefinition origin = (ov.IsOverrideOfInterfaceMember && ov.InterfaceImplementor.Implementor != ov.Override.DeclaringType)
? ov.InterfaceImplementor.Implementor
IMemberDefinition origin = (ov.IsOverrideOfInterfaceMember && ov.RuntimeInterfaceImplementation.Implementor != ov.Override.DeclaringType)
? ov.RuntimeInterfaceImplementation.Implementor
: ov.Override;
Debug.Assert (provider.GetType () == baseProvider.GetType ());
Debug.Assert (!(provider is GenericParameter genericParameter) || genericParameter.DeclaringMethod != null);
Expand Down
201 changes: 95 additions & 106 deletions src/tools/illink/src/linker/Linker.Steps/MarkStep.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ void ValidateMethodRequiresUnreferencedCodeAreSame (OverrideInformation ov)
nameof (RequiresUnreferencedCodeAttribute),
method.GetDisplayName (),
baseMethod.GetDisplayName ());
IMemberDefinition origin = (ov.IsOverrideOfInterfaceMember && ov.InterfaceImplementor.Implementor != method.DeclaringType)
? ov.InterfaceImplementor.Implementor
IMemberDefinition origin = (ov.IsOverrideOfInterfaceMember && ov.RuntimeInterfaceImplementation.Implementor != method.DeclaringType)
? ov.RuntimeInterfaceImplementation.Implementor
: method;
Context.LogWarning (origin, DiagnosticId.RequiresUnreferencedCodeAttributeMismatch, message);
Comment on lines +59 to 62
Copy link
Member Author

@jtschuster jtschuster Aug 14, 2024

Choose a reason for hiding this comment

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

It looks like this is the reason for the IL2046 warning not being suppressed.

class Base : IFoo
{
  [Ruc("Message")]
  [UnconditionalSuppressMessage("IL2046")]
  void M() { } // Suppressed IL2046
}

class Derived : Base, IFoo // Another 2046 (unsuppressed)
{
}

interface IFoo
{
  void M();
}

Since BaseType provides the interface implementation method for Derived, we decided to put the warning method on the type that implements the interface. I think that makes sense for if Derived implements IFoo and Base doesn't, but if both do (and both implementations are marked), it might make sense to report the warning for Derived on the implementation method, or not at all (assuming it would warn when analyzing BaseType).

Copy link
Member

Choose a reason for hiding this comment

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

I see, so it sounds like it's due to a combination of this PR and #104753. I think it's fine to produce another warning on the derived type in that case - it's similar to the compiler error behavior.

I think we should just add the same suppressions to the derived types, citing dotnet/linker#1187, no need to block this PR on that issue.

}
Expand Down
8 changes: 7 additions & 1 deletion src/tools/illink/src/linker/Linker/Annotations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
Expand Down Expand Up @@ -71,6 +72,7 @@ public partial class AnnotationStore
protected readonly HashSet<MethodDefinition> indirectly_called = new HashSet<MethodDefinition> ();
protected readonly HashSet<TypeDefinition> types_relevant_to_variant_casting = new HashSet<TypeDefinition> ();
readonly HashSet<IMemberDefinition> reflection_used = new ();
readonly HashSet<RuntimeInterfaceImplementation> _marked_runtime_interface_implementations = new ();

public AnnotationStore (LinkContext context)
{
Expand Down Expand Up @@ -705,9 +707,13 @@ public void EnqueueVirtualMethod (MethodDefinition method)
VirtualMethodsWithAnnotationsToValidate.Add (method);
}

internal List<(TypeReference InterfaceType, List<InterfaceImplementation> ImplementationChain)>? GetRecursiveInterfaces (TypeDefinition type)
internal ImmutableArray<RuntimeInterfaceImplementation>? GetRuntimeInterfaces (TypeDefinition type)
{
return TypeMapInfo.GetRecursiveInterfaces (type);
}

internal bool IsMarked (RuntimeInterfaceImplementation runtimeInterface) => _marked_runtime_interface_implementations.Contains (runtimeInterface);

internal void Mark (RuntimeInterfaceImplementation runtimeInterface) => _marked_runtime_interface_implementations.Add (runtimeInterface);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// 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.Collections.Immutable;
using System.Diagnostics;
using Mono.Cecil;

namespace Mono.Linker
{
internal sealed class InterfaceImplementationChain
{
/// <summary>
/// The type that has the InterfaceImplementation - either the <see cref="Implementor"/> or a base type of it.
/// </summary>
public TypeReference TypeWithInterfaceImplementation { get; }

/// <summary>
/// The path of .interfaceimpl on <see cref="RuntimeInterfaceImplementation.Implementor"/> or a base type that terminates with <see cref="RuntimeInterfaceImplementation.InflatedInterfaceType"/>.
/// </summary>
public ImmutableArray<InterfaceImplementation> InterfaceImplementations { get; }

public InterfaceImplementationChain (TypeReference typeWithInterfaceImplementation, ImmutableArray<InterfaceImplementation> interfaceImplementation)
{
TypeWithInterfaceImplementation = typeWithInterfaceImplementation;
InterfaceImplementations = interfaceImplementation;
}

/// <summary>
/// Returns true if all the .interfaceImpls in the chain and the are marked.
/// </summary>
/// <param name="annotations"></param>
/// <returns></returns>
public bool IsMarked (AnnotationStore annotations, ITryResolveMetadata context)
{
var typeDef = context.TryResolve (TypeWithInterfaceImplementation);
// If we have the .interfaceImpls on this type, it must be resolvable
Debug.Assert (typeDef is not null);
if (!annotations.IsMarked (typeDef))
return false;

foreach (var impl in InterfaceImplementations) {
if (!annotations.IsMarked (impl))
return false;
}

return true;
}
}
}
59 changes: 0 additions & 59 deletions src/tools/illink/src/linker/Linker/InterfaceImplementor.cs

This file was deleted.

2 changes: 1 addition & 1 deletion src/tools/illink/src/linker/Linker/LinkContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ internal TypeNameResolver TypeNameResolver
public SerializationMarker SerializationMarker { get; }

public LinkContext (Pipeline pipeline, ILogger logger, string outputDirectory)
: this(pipeline, logger, outputDirectory, new UnintializedContextFactory ())
: this (pipeline, logger, outputDirectory, new UnintializedContextFactory ())
{
}

Expand Down
31 changes: 8 additions & 23 deletions src/tools/illink/src/linker/Linker/MethodBodyScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public InterfacesOnStackScanner (LinkContext context)
this.context = context;
}

public IEnumerable<(InterfaceImplementation, TypeDefinition)>? GetReferencedInterfaces (MethodIL methodIL)
public IEnumerable<(RuntimeInterfaceImplementation, TypeDefinition)>? GetReferencedInterfaces (MethodIL methodIL)
{
var possibleStackTypes = AllPossibleStackTypes (methodIL);
if (possibleStackTypes.Count == 0)
Expand All @@ -73,7 +73,7 @@ public InterfacesOnStackScanner (LinkContext context)
if (interfaceTypes.Length == 0)
return null;

var interfaceImplementations = new HashSet<(InterfaceImplementation, TypeDefinition)> ();
var interfaceImplementations = new HashSet<(RuntimeInterfaceImplementation, TypeDefinition)> ();

// 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
Expand Down Expand Up @@ -139,31 +139,16 @@ HashSet<TypeDefinition> AllPossibleStackTypes (MethodIL methodIL)
return types;
}

void AddMatchingInterfaces (HashSet<(InterfaceImplementation, TypeDefinition)> results, TypeDefinition type, TypeDefinition[] interfaceTypes)
void AddMatchingInterfaces (HashSet<(RuntimeInterfaceImplementation, TypeDefinition)> results, TypeDefinition type, TypeDefinition[] interfaceTypes)
{
if (!type.HasInterfaces)
var runtimeInterfaces = context.Annotations.GetRuntimeInterfaces (type);
if (runtimeInterfaces is null)
return;

foreach (var interfaceType in interfaceTypes) {
if (HasInterface (type, interfaceType, out InterfaceImplementation? implementation))
results.Add ((implementation, type));
}
}

bool HasInterface (TypeDefinition type, TypeDefinition interfaceType, [NotNullWhen (true)] out InterfaceImplementation? implementation)
{
implementation = null;
if (!type.HasInterfaces)
return false;

foreach (var iface in type.Interfaces) {
if (context.TryResolve (iface.InterfaceType) == interfaceType) {
implementation = iface;
return true;
foreach (var iface in runtimeInterfaces) {
if (interfaceTypes.Contains (iface.InterfaceTypeDefinition)) {
results.Add ((iface, type));
}
}

return false;
}

void AddFromGenericInstance (HashSet<TypeDefinition> set, IGenericInstance instance)
Expand Down
23 changes: 12 additions & 11 deletions src/tools/illink/src/linker/Linker/OverrideInformation.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// 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;
using System.Diagnostics;
using Mono.Cecil;
using System.Diagnostics.CodeAnalysis;
using Mono.Cecil;

namespace Mono.Linker
{
Expand All @@ -14,28 +15,28 @@ public class OverrideInformation

public MethodDefinition Override { get; }

internal InterfaceImplementor? InterfaceImplementor { get; }
internal RuntimeInterfaceImplementation? RuntimeInterfaceImplementation { get; }

internal OverrideInformation (MethodDefinition @base, MethodDefinition @override, InterfaceImplementor? interfaceImplementor = null)
internal OverrideInformation (MethodDefinition @base, MethodDefinition @override, RuntimeInterfaceImplementation? runtimeInterface = null)
{
Base = @base;
Override = @override;
InterfaceImplementor = interfaceImplementor;
RuntimeInterfaceImplementation = runtimeInterface;
// Ensure we have an interface implementation if the base method is from an interface and the override method is on a class
Debug.Assert(@base.DeclaringType.IsInterface && interfaceImplementor != null
|| !@base.DeclaringType.IsInterface && interfaceImplementor == null);
Debug.Assert (@base.DeclaringType.IsInterface && runtimeInterface != null
|| !@base.DeclaringType.IsInterface && runtimeInterface == null);
// Ensure the interfaceImplementor is for the interface we expect
Debug.Assert (@base.DeclaringType.IsInterface ? interfaceImplementor!.InterfaceType == @base.DeclaringType : true);
Debug.Assert (@base.DeclaringType.IsInterface ? runtimeInterface!.InterfaceTypeDefinition == @base.DeclaringType : true);
}

public InterfaceImplementation? MatchingInterfaceImplementation
=> InterfaceImplementor?.InterfaceImplementation;
=> RuntimeInterfaceImplementation?.InterfaceImplementationChains[0].InterfaceImplementations[0];

public TypeDefinition? InterfaceType
=> InterfaceImplementor?.InterfaceType;
=> RuntimeInterfaceImplementation?.InterfaceTypeDefinition;

[MemberNotNullWhen (true, nameof (InterfaceImplementor), nameof (MatchingInterfaceImplementation))]
[MemberNotNullWhen (true, nameof (RuntimeInterfaceImplementation))]
public bool IsOverrideOfInterfaceMember
=> InterfaceImplementor != null;
=> RuntimeInterfaceImplementation != null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// 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;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil;

namespace Mono.Linker
{
/// <summary>
/// Represents an implementation of an interface on a type that may be directly on the type or on a base type or implemented interface.
/// This type is considered to implement the interface at runtime, even though the interface may not be directly on the type.
/// </summary>
/// <remarks>
/// This type should be used for marking, but should NOT be used to check if a runtime interface implementation has been marked.
/// This type represents the most direct way an interface may be implemented, but it may be implemented in a less direct way that is not represented here.
/// You should check all possible implementation 'paths' to determine if an interface is implemented, for example <see cref="MarkStep.IsInterfaceImplementationMarkedRecursively"/>.
/// </remarks>
internal sealed class RuntimeInterfaceImplementation
{
/// <summary>
/// The type that implements <see cref="RuntimeInterfaceImplementation.InflatedInterfaceType"/>.
/// </summary>
public TypeDefinition Implementor { get; }

/// <summary>
/// All the chains of .interfaceImpls that cause <see cref="Implementor"/> to implement <see cref="InflatedInterfaceType"/>
/// </summary>
public ImmutableArray<InterfaceImplementationChain> InterfaceImplementationChains { get; }

/// <summary>
/// The inflated interface type reference that is implemented by <see cref="Implementor"/>.
/// </summary>
public TypeReference InflatedInterfaceType { get; }

/// <summary>
/// The <see cref="TypeDefinition"/> of <see cref="InflatedInterfaceType"/>
/// </summary>
public TypeDefinition? InterfaceTypeDefinition { get; }

public RuntimeInterfaceImplementation (TypeDefinition implementor, TypeReference interfaceType, TypeDefinition? interfaceTypeDefinition, IEnumerable<InterfaceImplementationChain> interfaceImplementations)
{
Implementor = implementor;
InterfaceImplementationChains = interfaceImplementations.ToImmutableArray ();
InflatedInterfaceType = interfaceType;
InterfaceTypeDefinition = interfaceTypeDefinition;
}

public bool IsAnyImplementationMarked (AnnotationStore annotations, ITryResolveMetadata context)
{
if (annotations.IsMarked (this))
return true;
foreach (var chain in InterfaceImplementationChains) {
if (chain.IsMarked (annotations, context)) {
return true;
}
}
return false;
}
}
}
Loading
Loading