File: Compiler\Dataflow\HandleCallAction.cs
Web Access
Project: src\src\runtime\src\coreclr\tools\aot\ILCompiler.Compiler\ILCompiler.Compiler.csproj (ILCompiler.Compiler)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using ILCompiler;
using ILCompiler.Dataflow;
using ILCompiler.DependencyAnalysis;
using ILCompiler.DependencyAnalysisFramework;
using ILLink.Shared.TypeSystemProxy;
using Internal.TypeSystem;
using Internal.IL;

using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.DependencyList;
using MultiValue = ILLink.Shared.DataFlow.ValueSet<ILLink.Shared.DataFlow.SingleValue>;
using WellKnownType = ILLink.Shared.TypeSystemProxy.WellKnownType;

#nullable enable

namespace ILLink.Shared.TrimAnalysis
{
    internal partial struct HandleCallAction
    {
#pragma warning disable CA1822 // Mark members as static - the other partial implementations might need to be instance methods

        private readonly ReflectionMarker _reflectionMarker;
        private ILOpcode _operation;
        private readonly MethodDesc _callingMethod;
        private readonly MethodDesc _reason;

        public HandleCallAction(
            FlowAnnotations annotations,
            ILOpcode operation,
            ReflectionMarker reflectionMarker,
            in DiagnosticContext diagnosticContext,
            MethodDesc callingMethod,
            MethodDesc reason)
        {
            _reflectionMarker = reflectionMarker;
            _operation = operation;
            _isNewObj = operation == ILOpcode.newobj;
            _diagnosticContext = diagnosticContext;
            _callingMethod = callingMethod;
            _annotations = annotations;
            _reason = reason;
            _requireDynamicallyAccessedMembersAction = new(reflectionMarker, diagnosticContext, reason);
        }

        private partial bool TryHandleIntrinsic(
            MethodProxy calledMethod,
            MultiValue instanceValue,
            IReadOnlyList<MultiValue> argumentValues,
            IntrinsicId intrinsicId,
            out MultiValue? methodReturnValue)
        {
            MultiValue? maybeMethodReturnValue = methodReturnValue = null;

            switch (intrinsicId)
            {
                case IntrinsicId.Type_MakeGenericType:
                    {
                        bool triggersWarning = false;

                        if (!instanceValue.IsEmpty() && !argumentValues[0].IsEmpty())
                        {
                            foreach (var value in instanceValue.AsEnumerable())
                            {
                                if (value is SystemTypeValue typeValue)
                                {
                                    TypeDesc typeInstantiated = typeValue.RepresentedType.Type;
                                    if (!typeInstantiated.IsGenericDefinition)
                                    {
                                        // Nothing to do, will fail at runtime
                                    }
                                    else if (TryGetMakeGenericInstantiation(_callingMethod, argumentValues[0], out Instantiation inst, out bool isExact))
                                    {
                                        if (inst.Length == typeInstantiated.Instantiation.Length)
                                        {
                                            typeInstantiated = ((MetadataType)typeInstantiated).MakeInstantiatedType(inst);

                                            if (isExact)
                                            {
                                                if (typeInstantiated.CheckConstraints())
                                                    _reflectionMarker.MarkType(_diagnosticContext.Origin, typeInstantiated, "MakeGenericType");
                                            }
                                            else
                                            {
                                                _reflectionMarker.RuntimeDeterminedDependencies.Add((_callingMethod, new MakeGenericTypeSite(typeInstantiated)));
                                            }
                                        }
                                    }
                                    else
                                    {
                                        if (typeInstantiated.Instantiation.IsConstrainedToBeReferenceTypes())
                                        {
                                            // This will always succeed thanks to the runtime type loader
                                        }
                                        else
                                        {
                                            triggersWarning = true;
                                        }

                                        // This should technically be in the IsConstrainedToBeReferenceTypes branch above
                                        // but we have trim warning suppressions in dotnet/runtime and elsewhere that rely on the implementation
                                        // detail that reference type instantiations will work, even if the generic is not
                                        // constrained to be a reference type.
                                        // MarkType will try to come up with a reference type type loader template.
                                        _reflectionMarker.MarkType(_diagnosticContext.Origin, typeInstantiated, "MakeGenericType");
                                    }
                                }
                                else if (value == NullValue.Instance)
                                {
                                    // Nothing to do
                                }
                                else
                                {
                                    // We don't know what type the `MakeGenericType` was called on
                                    triggersWarning = true;
                                }
                            }
                        }

                        if (triggersWarning)
                        {
                            ReflectionMethodBodyScanner.CheckAndReportRequires(_diagnosticContext, calledMethod.Method, DiagnosticUtilities.RequiresDynamicCodeAttribute);
                        }

                        // This intrinsic is relevant to both trimming and AOT - call into trimming logic as well.
                        return TryHandleSharedIntrinsic(calledMethod, instanceValue, argumentValues, intrinsicId, out methodReturnValue);
                    }

                case IntrinsicId.MethodInfo_MakeGenericMethod:
                    {
                        bool triggersWarning = false;

                        if (!instanceValue.IsEmpty())
                        {
                            foreach (var methodValue in instanceValue.AsEnumerable())
                            {
                                if (methodValue is SystemReflectionMethodBaseValue methodBaseValue)
                                {
                                    MethodDesc methodInstantiated = methodBaseValue.RepresentedMethod.Method;
                                    if (!methodInstantiated.IsGenericMethodDefinition)
                                    {
                                        // Nothing to do, will fail at runtime
                                    }
                                    else if (!methodInstantiated.OwningType.IsGenericDefinition
                                        && TryGetMakeGenericInstantiation(_callingMethod, argumentValues[0], out Instantiation inst, out bool isExact))
                                    {
                                        if (inst.Length == methodInstantiated.Instantiation.Length)
                                        {
                                            methodInstantiated = methodInstantiated.MakeInstantiatedMethod(inst);

                                            if (isExact)
                                            {
                                                if (methodInstantiated.CheckConstraints())
                                                    _reflectionMarker.MarkMethod(_diagnosticContext.Origin, methodInstantiated, "MakeGenericMethod");
                                            }
                                            else
                                            {
                                                _reflectionMarker.RuntimeDeterminedDependencies.Add((_callingMethod, new MakeGenericMethodSite(methodInstantiated)));
                                            }
                                        }
                                    }
                                    else if (methodInstantiated.Instantiation.IsConstrainedToBeReferenceTypes())
                                    {
                                        // This will always succeed thanks to the runtime type loader
                                    }
                                    else
                                    {
                                        // If the owning type is a generic definition, we can't help much.
                                        triggersWarning = true;
                                    }
                                }
                                else if (methodValue == NullValue.Instance)
                                {
                                    // Nothing to do
                                }
                                else
                                {
                                    // We don't know what method the `MakeGenericMethod` was called on
                                    triggersWarning = true;
                                }
                            }
                        }

                        if (triggersWarning)
                        {
                            ReflectionMethodBodyScanner.CheckAndReportRequires(_diagnosticContext, calledMethod.Method, DiagnosticUtilities.RequiresDynamicCodeAttribute);
                        }

                        // This intrinsic is relevant to both trimming and AOT - call into trimming logic as well.
                        return TryHandleSharedIntrinsic(calledMethod, instanceValue, argumentValues, intrinsicId, out methodReturnValue);
                    }

                case IntrinsicId.None:
                    {
                        if (ReflectionMethodBodyScanner.IsPInvokeDangerous(calledMethod.Method, out bool comDangerousMethod, out bool aotUnsafeDelegate))
                        {
                            if (aotUnsafeDelegate)
                            {
                                _diagnosticContext.AddDiagnostic(DiagnosticId.CorrectnessOfAbstractDelegatesCannotBeGuaranteed, calledMethod.GetDisplayName());
                            }

                            if (comDangerousMethod)
                            {
                                _diagnosticContext.AddDiagnostic(DiagnosticId.CorrectnessOfCOMCannotBeGuaranteed, calledMethod.GetDisplayName());
                            }
                        }

                        ReflectionMethodBodyScanner.CheckAndReportAllRequires(_diagnosticContext, calledMethod.Method);

                        return TryHandleSharedIntrinsic(calledMethod, instanceValue, argumentValues, intrinsicId, out methodReturnValue);
                    }

                case IntrinsicId.TypeDelegator_Ctor:
                    {
                        // This is an identity function for analysis purposes
                        if (_operation == ILOpcode.newobj)
                            AddReturnValue(argumentValues[0]);
                    }
                    break;

                case IntrinsicId.Array_Empty:
                    {
                        AddReturnValue(ArrayValue.Create(0, calledMethod.Method.Instantiation[0]));
                    }
                    break;

                //
                // System.Array
                //
                // CreateInstance(Type, Int32)
                //
                case IntrinsicId.Array_CreateInstance:
                    {
#if !ILTRIM
                        // We could try to analyze if the type is known, but for now making sure this works for canonical arrays is enough.
                        TypeDesc canonArrayType = _reflectionMarker.Factory.TypeSystemContext.CanonType.MakeArrayType();
                        _reflectionMarker.MarkType(_diagnosticContext.Origin, canonArrayType, "Array.CreateInstance was called");
#endif
                    }
                    break;

                //
                // System.Enum
                //
                // static GetValues(Type)
                //
                case IntrinsicId.Enum_GetValues:
                    {
                        // Enum.GetValues returns System.Array, but it's the array of the enum type under the hood
                        // and people depend on this undocumented detail (could have returned enum of the underlying
                        // type instead).
                        //
                        // At least until we have shared enum code, this needs extra handling to get it right.
                        foreach (var value in argumentValues[0].AsEnumerable())
                        {
                            if (value is SystemTypeValue systemTypeValue
                                && !systemTypeValue.RepresentedType.Type.IsGenericDefinition
                                && !systemTypeValue.RepresentedType.Type.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true))
                            {
                                if (systemTypeValue.RepresentedType.Type.IsEnum)
                                {
                                    _reflectionMarker.Dependencies.Add(_reflectionMarker.Factory.ReflectedType(systemTypeValue.RepresentedType.Type.MakeArrayType()), "Enum.GetValues");
                                }
                            }
                            else
                                ReflectionMethodBodyScanner.CheckAndReportRequires(_diagnosticContext, calledMethod.Method, DiagnosticUtilities.RequiresDynamicCodeAttribute);
                        }
                    }
                    break;

                //
                // System.Runtime.InteropServices.Marshal
                //
                // static SizeOf(Type)
                // static PtrToStructure(IntPtr, Type)
                // static DestroyStructure(IntPtr, Type)
                // static OffsetOf(Type, string)
                //
                case IntrinsicId.Marshal_SizeOf:
                case IntrinsicId.Marshal_PtrToStructure:
                case IntrinsicId.Marshal_DestroyStructure:
                case IntrinsicId.Marshal_OffsetOf:
#if !ILTRIM
                    {
                        int paramIndex = intrinsicId == IntrinsicId.Marshal_SizeOf
                            || intrinsicId == IntrinsicId.Marshal_OffsetOf
                            ? 0 : 1;

                        // We need the data to do struct marshalling.
                        foreach (var value in argumentValues[paramIndex].AsEnumerable())
                        {
                            if (value is SystemTypeValue systemTypeValue
                                && !systemTypeValue.RepresentedType.Type.IsGenericDefinition
                                && !systemTypeValue.RepresentedType.Type.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true))
                            {
                                if (systemTypeValue.RepresentedType.Type.IsDefType)
                                {
                                    _reflectionMarker.Dependencies.Add(_reflectionMarker.Factory.StructMarshallingData((DefType)systemTypeValue.RepresentedType.Type), "Marshal API");
                                    if (intrinsicId == IntrinsicId.Marshal_PtrToStructure
                                        && systemTypeValue.RepresentedType.Type.GetParameterlessConstructor() is MethodDesc ctorMethod
                                        && !_reflectionMarker.Factory.MetadataManager.IsReflectionBlocked(ctorMethod))
                                    {
                                        _reflectionMarker.Dependencies.Add(_reflectionMarker.Factory.ReflectedMethod(ctorMethod.GetCanonMethodTarget(CanonicalFormKind.Specific)), "Marshal API");
                                    }
                                }
                            }
                            else
                                ReflectionMethodBodyScanner.CheckAndReportRequires(_diagnosticContext, calledMethod.Method, DiagnosticUtilities.RequiresDynamicCodeAttribute);
                        }
                    }
#endif
                    break;

                //
                // System.Runtime.InteropServices.Marshal
                //
                // static GetDelegateForFunctionPointer(IntPtr, Type)
                //
                case IntrinsicId.Marshal_GetDelegateForFunctionPointer:
#if !ILTRIM
                    {
                        // We need the data to do delegate marshalling.
                        foreach (var value in argumentValues[1].AsEnumerable())
                        {
                            if (value is SystemTypeValue systemTypeValue
                                && !systemTypeValue.RepresentedType.Type.IsGenericDefinition
                                && !systemTypeValue.RepresentedType.Type.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true))
                            {
                                if (systemTypeValue.RepresentedType.Type.IsDelegate)
                                {
                                    _reflectionMarker.Dependencies.Add(_reflectionMarker.Factory.DelegateMarshallingData((DefType)systemTypeValue.RepresentedType.Type), "Marshal API");
                                }
                            }
                            else
                                ReflectionMethodBodyScanner.CheckAndReportRequires(_diagnosticContext, calledMethod.Method, DiagnosticUtilities.RequiresDynamicCodeAttribute);
                        }
                    }
#endif
                    break;

                //
                // System.Delegate
                //
                // get_Method()
                //
                // System.Reflection.RuntimeReflectionExtensions
                //
                // GetMethodInfo(System.Delegate)
                //
                case IntrinsicId.RuntimeReflectionExtensions_GetMethodInfo:
                case IntrinsicId.Delegate_get_Method:
#if !ILTRIM
                    {
                        // Find the parameter: first is an instance method, second is an extension method.
                        MultiValue param = intrinsicId == IntrinsicId.RuntimeReflectionExtensions_GetMethodInfo
                            ? argumentValues[0] : instanceValue;

                        // If this is Delegate.Method accessed from RuntimeReflectionExtensions.GetMethodInfo, ignore
                        // because we handle the callsites to that one here as well.
                        if (Intrinsics.GetIntrinsicIdForMethod(_callingMethod) == IntrinsicId.RuntimeReflectionExtensions_GetMethodInfo)
                            break;

                        foreach (var valueNode in param.AsEnumerable())
                        {
                            TypeDesc? staticType = (valueNode as IValueWithStaticType)?.StaticType?.Type;
                            if (staticType is null || !staticType.IsDelegate)
                            {
                                // The static type is unknown or something useless like Delegate or MulticastDelegate.
                                _reflectionMarker.Dependencies.Add(_reflectionMarker.Factory.ReflectedDelegate(null), "Delegate.Method access on unknown delegate type");
                            }
                            else
                            {
                                if (staticType.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true))
                                    _reflectionMarker.Dependencies.Add(_reflectionMarker.Factory.ReflectedDelegate(staticType.GetTypeDefinition()), "Delegate.Method access (on inexact type)");
                                else
                                    _reflectionMarker.Dependencies.Add(_reflectionMarker.Factory.ReflectedDelegate(staticType.ConvertToCanonForm(CanonicalFormKind.Specific)), "Delegate.Method access");
                            }
                        }
                    }
#endif
                    break;

                //
                // System.Object
                //
                // GetType()
                //
                case IntrinsicId.Object_GetType:
                    {
                        if (instanceValue.IsEmpty()) {
                            AddReturnValue(MultiValueLattice.Top);
                            break;
                        }

                        foreach (var valueNode in instanceValue.AsEnumerable())
                        {
                            // Note that valueNode can be statically typed in IL as some generic argument type.
                            // For example:
                            //   void Method<T>(T instance) { instance.GetType().... }
                            // It could be that T is annotated with for example PublicMethods:
                            //   void Method<[DAM(PublicMethods)] T>(T instance) { instance.GetType().GetMethod("Test"); }
                            // In this case it's in theory possible to handle it, by treating the T basically as a base class
                            // for the actual type of "instance". But the analysis for this would be pretty complicated (as the marking
                            // has to happen on the callsite, which doesn't know that GetType() will be used...).
                            // For now we're intentionally ignoring this case - it will produce a warning.
                            // The counter example is:
                            //   Method<Base>(new Derived);
                            // In this case to get correct results, trimmer would have to mark all public methods on Derived. Which
                            // currently it won't do.

                            TypeDesc? staticType = (valueNode as IValueWithStaticType)?.StaticType?.Type;
                            if (staticType?.IsByRef == true)
                            {
                                staticType = ((ByRefType)staticType).ParameterType;
                            }
                            if (staticType is null || (!staticType.IsDefType && !staticType.IsArray))
                            {
                                DynamicallyAccessedMemberTypes annotation = default;
                                if (staticType is GenericParameterDesc genericParam)
                                {
                                    foreach (TypeDesc constraint in genericParam.TypeConstraints)
                                    {
                                        if (constraint.IsWellKnownType(Internal.TypeSystem.WellKnownType.Enum))
                                        {
                                            annotation = DynamicallyAccessedMemberTypes.PublicFields;
                                            break;
                                        }
                                    }
                                }

                                if (annotation != default)
                                {
                                    AddReturnValue(_reflectionMarker.Annotations.GetMethodReturnValue(calledMethod, _isNewObj, annotation));
                                }
                                else
                                {
                                    // We don't know anything about the type GetType was called on. Track this as a usual "result of a method call without any annotations"
                                    AddReturnValue(_reflectionMarker.Annotations.GetMethodReturnValue(calledMethod, _isNewObj));
                                }
                            }
                            else if (staticType.IsSealed() || staticType.IsTypeOf("System", "Delegate"))
                            {
                                // We can treat this one the same as if it was a typeof() expression

                                // We can allow Object.GetType to be modeled as System.Delegate because we keep all methods
                                // on delegates anyway so reflection on something this approximation would miss is actually safe.

                                // We ignore the fact that the type can be annotated (see below for handling of annotated types)
                                // This means the annotations (if any) won't be applied - instead we rely on the exact knowledge
                                // of the type. So for example even if the type is annotated with PublicMethods
                                // but the code calls GetProperties on it - it will work - mark properties, don't mark methods
                                // since we ignored the fact that it's annotated.
                                // This can be seen a little bit as a violation of the annotation, but we already have similar cases
                                // where a parameter is annotated and if something in the method sets a specific known type to it
                                // we will also make it just work, even if the annotation doesn't match the usage.
                                AddReturnValue(new SystemTypeValue(staticType));
                            }
                            else if (staticType.IsTypeOf("System", "Enum"))
                            {
                                AddReturnValue(_reflectionMarker.Annotations.GetMethodReturnValue(calledMethod, _isNewObj, DynamicallyAccessedMemberTypes.PublicFields));
                            }
                            else
                            {
                                Debug.Assert(staticType is MetadataType || staticType.IsArray);
                                MetadataType closestMetadataType = staticType is MetadataType mdType ?
                                    mdType : (MetadataType)_reflectionMarker.Factory.TypeSystemContext.GetWellKnownType(Internal.TypeSystem.WellKnownType.Array);

                                var annotation = _reflectionMarker.Annotations.GetTypeAnnotation(staticType);

                                if (annotation != default)
                                {
                                    _reflectionMarker.Dependencies.Add(_reflectionMarker.Factory.ObjectGetTypeCalled(closestMetadataType), "GetType called on this type");
                                }

                                // Return a value which is "unknown type" with annotation. For now we'll use the return value node
                                // for the method, which means we're loosing the information about which staticType this
                                // started with. For now we don't need it, but we can add it later on.
                                AddReturnValue(_reflectionMarker.Annotations.GetMethodReturnValue(calledMethod, _isNewObj, annotation));
                            }
                        }
                    }
                    break;

                //
                // string System.Reflection.Assembly.Location getter
                // string System.Reflection.AssemblyName.CodeBase getter
                // string System.Reflection.AssemblyName.EscapedCodeBase getter
                //
                case IntrinsicId.Assembly_get_Location:
                case IntrinsicId.AssemblyName_get_CodeBase:
                case IntrinsicId.AssemblyName_get_EscapedCodeBase:
                    _diagnosticContext.AddDiagnostic(DiagnosticId.AvoidAssemblyLocationInSingleFile, calledMethod.GetDisplayName());
                    break;

                //
                // string System.Reflection.Assembly.GetFile(string)
                // string System.Reflection.Assembly.GetFiles()
                // string System.Reflection.Assembly.GetFiles(bool)
                //
                case IntrinsicId.Assembly_GetFile:
                case IntrinsicId.Assembly_GetFiles:
                    _diagnosticContext.AddDiagnostic(DiagnosticId.AvoidAssemblyGetFilesInSingleFile, calledMethod.GetDisplayName());
                    break;

                case IntrinsicId.TypeMapping_GetOrCreateExternalTypeMapping:
                {
                    // TODO-ILTRIM: type maps
#if !ILTRIM
                    if (calledMethod.Method.Instantiation[0].ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true))
                    {
                        // We only support GetOrCreateExternalTypeMapping for a fully specified type.
                        _diagnosticContext.AddDiagnostic(DiagnosticId.TypeMapGroupTypeCannotBeStaticallyDetermined,
                            calledMethod.Method.Instantiation[0].GetDisplayName());
                    }
                    else
                    {
                        TypeDesc typeMapGroup = calledMethod.Method.Instantiation[0];
                        _reflectionMarker.Dependencies.Add(_reflectionMarker.Factory.ExternalTypeMapRequest(typeMapGroup), "TypeMapping.GetOrCreateExternalTypeMapping called on type");
                    }
#endif
                    break;
                }
                case IntrinsicId.TypeMapping_GetOrCreateProxyTypeMapping:
                {
                    // TODO-ILTRIM: type maps
#if !ILTRIM
                    if (calledMethod.Method.Instantiation[0].ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true))
                    {
                        // We only support GetOrCreateProxyTypeMapping for a fully specified type.
                        _diagnosticContext.AddDiagnostic(DiagnosticId.TypeMapGroupTypeCannotBeStaticallyDetermined,
                            calledMethod.Method.Instantiation[0].GetDisplayName());
                    }
                    else
                    {
                        TypeDesc typeMapGroup = calledMethod.Method.Instantiation[0];
                        _reflectionMarker.Dependencies.Add(_reflectionMarker.Factory.ProxyTypeMapRequest(typeMapGroup), "TypeMapping.GetOrCreateProxyTypeMapping called on type");
                    }
#endif
                    break;
                }
                default:
                    return false;
            }

            methodReturnValue = maybeMethodReturnValue;
            return true;

            void AddReturnValue(MultiValue value)
            {
                maybeMethodReturnValue = (maybeMethodReturnValue is null) ? value : MultiValueLattice.Meet((MultiValue)maybeMethodReturnValue, value);
            }
        }

        private static bool TryGetMakeGenericInstantiation(
            MethodDesc contextMethod,
            in MultiValue genericParametersArray,
            out Instantiation inst,
            out bool isExact)
        {
            // We support calling MakeGeneric APIs with a very concrete instantiation array.
            // Only the form of `new Type[] { typeof(Foo), typeof(T), typeof(Foo<T>) }` is supported.

            inst = default;
            isExact = true;
            Debug.Assert(contextMethod.GetTypicalMethodDefinition() == contextMethod);

            var typesValue = genericParametersArray.AsSingleValue();
            if (typesValue is NullValue)
            {
                // This will fail at runtime but no warning needed
                inst = Instantiation.Empty;
                return true;
            }

            // Is this an array we model?
            if (typesValue is not ArrayValue array)
            {
                return false;
            }

            int? size = array.Size.AsConstInt();
            if (size == null)
            {
                return false;
            }

            TypeDesc[]? sigInst = null;
            TypeDesc[]? defInst = null;

            ArrayBuilder<TypeDesc> result = default;
            for (int i = 0; i < size.Value; i++)
            {
                // Go over each element of the array. If the value is unknown, bail.
                if (!array.TryGetValueByIndex(i, out MultiValue value))
                {
                    return false;
                }

                var singleValue = value.AsSingleValue();

                TypeDesc? type = singleValue switch
                {
                    SystemTypeValue systemType => systemType.RepresentedType.Type,
                    GenericParameterValue genericParamType => genericParamType.GenericParameter.GenericParameter,
                    NullableSystemTypeValue nullableSystemType => nullableSystemType.NullableType.Type,
                    _ => null
                };

                if (type is null)
                {
                    return false;
                }

                // type is now some type.
                // Because dataflow analysis oddly operates on method bodies instantiated over
                // generic parameters (as opposed to instantiated over signature variables)
                // We need to swap generic parameters (T, U,...) for signature variables (!0, !!1,...).
                // We need to do this for both generic parameters of the owning type, and generic
                // parameters of the owning method.
                if (type.ContainsSignatureVariables(treatGenericParameterLikeSignatureVariable: true))
                {
                    if (sigInst == null)
                    {
                        TypeDesc contextType = contextMethod.OwningType;
                        sigInst = new TypeDesc[contextType.Instantiation.Length + contextMethod.Instantiation.Length];
                        defInst = new TypeDesc[contextType.Instantiation.Length + contextMethod.Instantiation.Length];
                        TypeSystemContext context = type.Context;
                        for (int j = 0; j < contextType.Instantiation.Length; j++)
                        {
                            sigInst[j] = context.GetSignatureVariable(j, method: false);
                            defInst[j] = contextType.Instantiation[j];
                        }
                        for (int j = 0; j < contextMethod.Instantiation.Length; j++)
                        {
                            sigInst[j + contextType.Instantiation.Length] = context.GetSignatureVariable(j, method: true);
                            defInst[j + contextType.Instantiation.Length] = contextMethod.Instantiation[j];
                        }
                    }

                    isExact = false;

                    // defInst is [T, U, V], sigInst is `[!0, !!0, !!1]`.
                    type = type.ReplaceTypesInConstructionOfType(defInst, sigInst);
                }

                result.Add(type);
            }

            inst = new Instantiation(result.ToArray());
            return true;
        }

        private partial bool MethodIsTypeConstructor(MethodProxy method)
        {
            if (!method.Method.IsConstructor)
                return false;
            TypeDesc? type = method.Method.OwningType;
            while (type is not null)
            {
                if (type.IsTypeOf(WellKnownType.System_Type))
                    return true;
                type = type.BaseType;
            }
            return false;
        }

        private partial IEnumerable<SystemReflectionMethodBaseValue> GetMethodsOnTypeHierarchy(TypeProxy type, string name, BindingFlags? bindingFlags)
        {
            foreach (var method in type.Type.GetMethodsOnTypeHierarchy(m => m.Name.StringEquals(name), bindingFlags))
                yield return new SystemReflectionMethodBaseValue(new MethodProxy(method));
        }

        private partial IEnumerable<SystemTypeValue> GetNestedTypesOnType(TypeProxy type, string name, BindingFlags? bindingFlags)
        {
            foreach (var nestedType in type.Type.GetNestedTypesOnType(t => t.Name.StringEquals(name), bindingFlags))
                yield return new SystemTypeValue(new TypeProxy(nestedType));
        }

        private partial bool TryGetBaseType(TypeProxy type, out TypeProxy? baseType)
        {
            if (type.Type.BaseType != null)
            {
                baseType = new TypeProxy(type.Type.BaseType);
                return true;
            }

            baseType = null;
            return false;
        }

        private partial bool TryResolveTypeNameForCreateInstanceAndMark(in MethodProxy calledMethod, string assemblyName, string typeName, out TypeProxy resolvedType)
        {
            if (!System.Reflection.Metadata.AssemblyNameInfo.TryParse(assemblyName, out var an)
                || _callingMethod.Context.ResolveAssembly(an) is not ModuleDesc resolvedAssembly)
            {
                _diagnosticContext.AddDiagnostic(DiagnosticId.UnresolvedAssemblyInCreateInstance,
                    assemblyName,
                    calledMethod.GetDisplayName());
                resolvedType = default;
                return false;
            }

            if (!_reflectionMarker.TryResolveTypeNameAndMark(resolvedAssembly, typeName, _diagnosticContext, "Reflection", fallbackToCoreLib: true, out TypeDesc? foundType))
            {
                // It's not wrong to have a reference to non-existing type - the code may well expect to get an exception in this case
                // Note that we did find the assembly, so it's not a ILLink config problem, it's either intentional, or wrong versions of assemblies
                // but ILLink can't know that. In case a user tries to create an array using System.Activator we should simply ignore it, the user
                // might expect an exception to be thrown.
                resolvedType = default;
                return false;
            }

            resolvedType = new TypeProxy(foundType);
            return true;
        }

        private partial string? GetAssemblyName(TypeProxy type)
            // Only named types are supported. Reject array/pointer/byref (ParameterizedType),
            // function pointer, signature variables, and System.Array itself. Rejecting System.Array
            // covers the case where Cecil's IL scanner lowers typeof(SomeType[]) to System.Array,
            // which would otherwise produce wrong analysis (System.Array.Assembly is CoreLib at
            // runtime, but typeof(SomeType[]).Assembly is SomeType's assembly).
            => type.Type is MetadataType metadataType && !metadataType.IsWellKnownType(Internal.TypeSystem.WellKnownType.Array)
                ? metadataType.Module.Assembly.GetName().Name
                : null;

        private partial bool TryResolveTypeNameInAssemblyAndMark(string assemblyName, string typeName, out TypeProxy resolvedType)
        {
            if (!System.Reflection.Metadata.AssemblyNameInfo.TryParse(assemblyName, out var an)
                || _callingMethod.Context.ResolveAssembly(an) is not ModuleDesc resolvedAssembly)
            {
                resolvedType = default;
                return false;
            }

            if (!_reflectionMarker.TryResolveTypeNameAndMark(resolvedAssembly, typeName, _diagnosticContext, "Reflection", fallbackToCoreLib: false, out TypeDesc? foundType))
            {
                resolvedType = default;
                return false;
            }

            resolvedType = new TypeProxy(foundType);
            return true;
        }

        private partial void MarkStaticConstructor(TypeProxy type)
            => _reflectionMarker.MarkStaticConstructor(_diagnosticContext.Origin, type.Type, _reason);

        private partial void ReportRequiresUnreferencedCode(MethodProxy calledMethod)
            => ReflectionMethodBodyScanner.CheckAndReportRequires(_diagnosticContext, calledMethod.Method, DiagnosticUtilities.RequiresUnreferencedCodeAttribute);

        private partial void MarkEventsOnTypeHierarchy(TypeProxy type, string name, BindingFlags? bindingFlags)
            => _reflectionMarker.MarkEventsOnTypeHierarchy(_diagnosticContext.Origin, type.Type, e => e.Name == name, _reason, bindingFlags);

        private partial void MarkFieldsOnTypeHierarchy(TypeProxy type, string name, BindingFlags? bindingFlags)
            => _reflectionMarker.MarkFieldsOnTypeHierarchy(_diagnosticContext.Origin, type.Type, f => f.Name.StringEquals(name), _reason, bindingFlags);

        private partial void MarkPropertiesOnTypeHierarchy(TypeProxy type, string name, BindingFlags? bindingFlags)
            => _reflectionMarker.MarkPropertiesOnTypeHierarchy(_diagnosticContext.Origin, type.Type, p => p.Name == name, _reason, bindingFlags);

        private partial void MarkPublicParameterlessConstructorOnType(TypeProxy type)
            => _reflectionMarker.MarkConstructorsOnType(_diagnosticContext.Origin, type.Type, m => m.IsPublic() && !m.HasMetadataParameters(), _reason);

        private partial void MarkConstructorsOnType(TypeProxy type, BindingFlags? bindingFlags, int? parameterCount)
            => _reflectionMarker.MarkConstructorsOnType(_diagnosticContext.Origin, type.Type, parameterCount == null ? null : m => m.GetMetadataParametersCount() == parameterCount, _reason, bindingFlags);

        private partial void MarkMethod(MethodProxy method)
            => _reflectionMarker.MarkMethod(_diagnosticContext.Origin, method.Method, _reason);

        private partial void MarkType(TypeProxy type)
            => _reflectionMarker.MarkType(_diagnosticContext.Origin, type.Type, _reason);

        private partial bool MarkAssociatedProperty(MethodProxy method)
        {
            var propertyDefinition = method.Method.GetPropertyForAccessor();
            if (propertyDefinition is null)
            {
                return false;
            }

            _reflectionMarker.MarkProperty(_diagnosticContext.Origin, propertyDefinition, _reason);
            return true;
        }

        private partial string GetContainingSymbolDisplayName() => _callingMethod.GetDisplayName();

        private sealed class MakeGenericMethodSite : INodeWithRuntimeDeterminedDependencies
        {
            private readonly MethodDesc _method;

            public MakeGenericMethodSite(MethodDesc method) => _method = method;

            public IEnumerable<DependencyNodeCore<NodeFactory>.DependencyListEntry> InstantiateDependencies(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, bool isConcreteInstantiation)
            {
                var list = new DependencyList();
                MethodDesc instantiatedMethod = _method.InstantiateSignature(typeInstantiation, methodInstantiation);
                if (instantiatedMethod.CheckConstraints(new InstantiationContext(typeInstantiation, methodInstantiation)))
                    RootingHelpers.TryGetDependenciesForReflectedMethod(ref list, factory, instantiatedMethod, "MakeGenericMethod");
                return list;
            }
        }

        private sealed class MakeGenericTypeSite : INodeWithRuntimeDeterminedDependencies
        {
            private readonly TypeDesc _type;

            public MakeGenericTypeSite(TypeDesc type) => _type = type;

            public IEnumerable<DependencyNodeCore<NodeFactory>.DependencyListEntry> InstantiateDependencies(NodeFactory factory, Instantiation typeInstantiation, Instantiation methodInstantiation, bool isConcreteInstantiation)
            {
                var list = new DependencyList();
                TypeDesc instantiatedType = _type.InstantiateSignature(typeInstantiation, methodInstantiation);
                if (instantiatedType.CheckConstraints(new InstantiationContext(typeInstantiation, methodInstantiation)))
                    RootingHelpers.TryGetDependenciesForReflectedType(ref list, factory, instantiatedType, "MakeGenericType");
                return list;
            }
        }

    }

    file static class Extensions
    {
        public static bool IsConstrainedToBeReferenceTypes(this Instantiation inst)
        {
            foreach (GenericParameterDesc param in inst)
                if (!param.HasReferenceTypeConstraint)
                    return false;

            return true;
        }
    }
}