Open eerhardt opened 3 years ago
Linker doesn't ensure this.
interface IFoo
{
public static void Hello() => Console.WriteLine("Hello");
public static int Field => 42;
}
interface IBar : IFoo
{
}
class Bar : IBar
{
}
Should DynamicallyAccessedMembers(All)
on Bar
really apply to the static methods and fields on IFoo?
Linker will also remove unused interface methods from the interface itself. So e.g. if an interface method got added to IBar
with a default implementation and Bar
doesn't implement it, it's fair game to remove it.
To make this safe, we would need to make linker keep more stuff than necessary.
Is it actually needed to fully preserve these for the DispatchProxy
scenario? The logic being - if a given interface member is never used anywhere in the app then is it actually necessary to generate a proxy member for it (it should never be used either)?
This same issue came up in System.ComponentModel.TypeConverter
:
I annotated _type
to be [DynamicallyAccessedMembers(All)]
. But ILLink warns in this case. TypeDescriptor.GetAttributes(Type)
is annotated as [DynamicallyAccessedMembers(All)]
on the Type parameter.
So now we have 2 cases where DynamicallyAccessedMembers(All)
should mean "preserve all members of any implemented interfaces on this Type".
I think it should be safe to do that now - we just an intrinsic for this - MarkEntireType does mark all members on implemented interfaces. Back in January I was still hoping we could scale back what .All
means to a saner set, but it basically means "all members, nested types and their members, base types and their members, interfaces and their members, and their attributes, recursively" and we decided that this is what we want for All
to mean.
Linker currently doesn't model arrays of types though so that's a prerequisite (we should probably restrict this to intrinsics instead of generalizing it to allowing void Foo([DynamicallyAccessed(All)] Type[] mytypes)
because that brings complexities).
The case which @eerhardt points to would not need the array handling - it only accessed attributes on the interfaces, so there's no need to propagate the annotation to the interface types for this case.
ould not need the array handling
Maybe I'm missing something:
// `_type` annotated as `.All`
Type[] interfaces = _type.GetInterfaces();
for (int idx = 0; idx < interfaces.Length; idx++)
{
Type iface = interfaces[idx];
// <snip />
// Warns because `iface` doesn't meet `.All`
attributes.AddRange(TypeDescriptor.GetAttributes(iface).Attributes);
}
Iface is the result of a ldelem
on the returned array so in order for the linker to understand that, it needs to propagate annotation from the array to the result of ldelem
.
Well the iface
is only passed to GetAttributes
which probably doesn't have any annotations and thus has no requirements on the passed in value. So technically if iface
is tracked as "unknown" in the linker, it would still "Work".
Ah, I think you missed what Eric wrote above: "TypeDescriptor.GetAttributes(Type)
is annotated as [DynamicallyAccessedMembers(All)]
on the Type parameter"
Right - my bad :-(
But I can suppress the ILLink warning for now, right? Until this bug is addressed in the linker?
Yes - I think it can be suppressed - All
will mark all interfaces and all members on the interfaces.
@vitek-karas - was this fixed with your work on DynamicallyAccessedMembers.Interfaces
? Can we close this issue and remove the suppressions in dotnet/runtime?
I have two PRs which will "fix" this for GetInterface
(singular):
Interfaces
annotation across the GetInterface
call.All
across GetInterface
There's no solution for GetInterfaces
(plural) - and right now I don't plan to have one. Mainly because it would require linker to better understand arrays and be able to handle foreach
over arrays. Definitely doable, but quite a bit more complex.
https://github.com/dotnet/runtime/pull/52461 has been merges, so GetInterface
should propagate the Interfaces
annotation now.
If an interface
Type
is annotated with[DynamicallyAccessedMembers(All)]
, then any Types returned frominterfaceType.GetInterfaces()
should also assume to be annotated with the same member types.Repro
Trimming the following application:
Produces this warning:
Note, for some reason, when the ILLinker runs as part of the shared framework build, I am getting a different warning, I'm not sure if I'm using a different version of the ILLinker or not, but here is the code in DispatchProxy:
and the warning:
cc @vitek-karas @MichalStrehovsky