File: parent\parent\Common\TypeSystem\Canon\CastingHelper.Canon.cs
Web Access
Project: ILCompiler.TypeSystem.csproj (ILCompiler.TypeSystem)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Internal.TypeSystem
{
    internal sealed class CanonicalTypeCastingHandler : ICanonicalTypeCastingHandler
    {
        /// <summary>
        /// Check if <paramref name="otherType"/> is a canonical type that <paramref name="thisType"/>
        /// can be cast to. __Canon accepts any reference type; __UniversalCanon accepts any type.
        /// Pointers, byrefs, and function pointers are not valid instantiation arguments.
        /// </summary>
        public static bool IsCanonicalCastTarget(TypeDesc thisType, TypeDesc otherType)
        {
            TypeSystemContext context = thisType.Context;

            if (context.IsCanonicalDefinitionType(otherType, CanonicalFormKind.Universal))
                return true;

            if (context.IsCanonicalDefinitionType(otherType, CanonicalFormKind.Specific))
                return thisType.IsGCPointer;

            return false;
        }

        /// <summary>
        /// Check if two type arguments can be considered matching because one (or both) is canonical.
        /// __Canon matches any reference type; __UniversalCanon matches any type.
        /// </summary>
        public static bool IsCanonicalTypeArgMatch(TypeDesc type, TypeDesc otherType)
        {
            TypeSystemContext context = type.Context;

            if (context.IsCanonicalDefinitionType(otherType, CanonicalFormKind.Universal))
                return true;

            if (context.IsCanonicalDefinitionType(otherType, CanonicalFormKind.Specific))
                return type.IsGCPointer || context.IsCanonicalDefinitionType(type, CanonicalFormKind.Any);

            if (context.IsCanonicalDefinitionType(type, CanonicalFormKind.Universal))
                return true;

            if (context.IsCanonicalDefinitionType(type, CanonicalFormKind.Specific))
                return otherType.IsGCPointer || context.IsCanonicalDefinitionType(otherType, CanonicalFormKind.Any);

            // For non-leaf types (e.g., Arg2<string> vs Arg2<__Canon>), check if they are
            // canon-equivalent: same type definition with canon-compatible type arguments.
            if (IsCanonEquivalent(type, otherType))
                return true;

            // For parameterized types like arrays (string[] vs __Canon[]),
            // recursively match the element/parameter type.
            if (type is ParameterizedType paramType && otherType is ParameterizedType otherParamType
                && type.Category == otherType.Category)
            {
                if (type is ArrayType arrayType && otherType is ArrayType otherArrayType
                    && arrayType.Rank != otherArrayType.Rank)
                    return false;

                return IsCanonicalTypeArgMatch(paramType.ParameterType, otherParamType.ParameterType);
            }

            return false;
        }

        /// <summary>
        /// Check if two types are equivalent considering canonical type matching rules.
        /// Same type definition with all type arguments either equal or canon-compatible.
        /// </summary>
        public static bool IsCanonEquivalent(TypeDesc thisType, TypeDesc otherType)
        {
            if (!thisType.HasSameTypeDefinition(otherType))
                return false;

            Instantiation thisInst = thisType.Instantiation;
            Instantiation otherInst = otherType.Instantiation;

            if (thisInst.Length == 0)
                return false;

            for (int i = 0; i < thisInst.Length; i++)
            {
                if (thisInst[i] == otherInst[i])
                    continue;

                if (!IsCanonicalTypeArgMatch(thisInst[i], otherInst[i]))
                    return false;
            }

            return true;
        }
    }
}