Skip to content

DynamicallyAccessedMembers should transitively apply to GetInterfaces() #1731

@eerhardt

Description

@eerhardt

If an interface Type is annotated with [DynamicallyAccessedMembers(All)], then any Types returned from interfaceType.GetInterfaces() should also assume to be annotated with the same member types.

Repro

Trimming the following application:

using System;
using System.Diagnostics.CodeAnalysis;

namespace ConsoleApp2
{
    interface IMyInterface : IFormattable
    {
        void M1();
    }

    class Program
    {
        static void Main(string[] args)
        {
            GenerateProxyType(typeof(IMyInterface));
        }

        private static void GenerateProxyType(
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type interfaceType)
        {
            foreach (Type t in interfaceType.GetInterfaces())
                AddInterfaceImpl(t);  // <--- Warning happens here

            AddInterfaceImpl(interfaceType);
        }

        static void AddInterfaceImpl([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type iface)
        {
        }
    }
}

Produces this warning:

Program.cs(22,17): Trim analysis warning IL2062: ConsoleApp2.Program.GenerateProxyType(Type): Value passed to parameter 'iface' of method 'ConsoleApp2.Program.AddInterfaceImpl(Type)' can not be statically determined and may not meet 'DynamicallyAccessedMembersAttribute' requirements.

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:

        internal void AddInterfaceImpl([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type iface)
        {
            ...
        }

        private static Type GenerateProxyType(
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type baseType,
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type interfaceType)
        {
            // Parameter validation is deferred until the point we need to create the proxy.
            // This prevents unnecessary overhead revalidating cached proxy types.

            // The interface type must be an interface, not a class
            if (!interfaceType.IsInterface)
            {
                // "T" is the generic parameter seen via the public contract
                throw new ArgumentException(SR.Format(SR.InterfaceType_Must_Be_Interface, interfaceType.FullName), "T");
            }

            // The base type cannot be sealed because the proxy needs to subclass it.
            if (baseType.IsSealed)
            {
                // "TProxy" is the generic parameter seen via the public contract
                throw new ArgumentException(SR.Format(SR.BaseType_Cannot_Be_Sealed, baseType.FullName), "TProxy");
            }

            // The base type cannot be abstract
            if (baseType.IsAbstract)
            {
                throw new ArgumentException(SR.Format(SR.BaseType_Cannot_Be_Abstract, baseType.FullName), "TProxy");
            }

            // The base type must have a public default ctor
            if (baseType.GetConstructor(Type.EmptyTypes) == null)
            {
                throw new ArgumentException(SR.Format(SR.BaseType_Must_Have_Default_Ctor, baseType.FullName), "TProxy");
            }

            // Create a type that derives from 'baseType' provided by caller
            ProxyBuilder pb = s_proxyAssembly.CreateProxy("generatedProxy", baseType);

            foreach (Type t in interfaceType.GetInterfaces())
                pb.AddInterfaceImpl(t);  // <--- Warning happens here

            pb.AddInterfaceImpl(interfaceType);

            Type generatedProxyType = pb.CreateType();
            return generatedProxyType;
        }

and the warning:

DispatchProxyGenerator.cs(139,17): Trim analysis warning IL2072: System.Reflection.DispatchProxyGenerator.GenerateProxyType(Type,Type): 'iface' argument does not satisfy 'All' in call to 'System.Reflection.DispatchProxyGenerator.ProxyBuilder.AddInterfaceImpl(Type)'. The return value of method 'System.Collections.Generic.IEnumerator<T>.Current.get' does not have matching annotations. The source value must declare at least the same requirements as those declared on the target location it is assigned to.

cc @vitek-karas @MichalStrehovsky

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions