File: Symbols\AbstractTypeMap.cs
Web Access
Project: src\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
    /// <summary>
    /// Abstract base class for mutable and immutable type maps.
    /// </summary>
    internal abstract class AbstractTypeMap
    {
        /// <summary>
        /// Substitute for a type declaration.  May use alpha renaming if the container is substituted.
        /// </summary>
        internal virtual NamedTypeSymbol SubstituteTypeDeclaration(NamedTypeSymbol previous)
        {
            Debug.Assert((object)previous.ConstructedFrom == (object)previous);
 
            NamedTypeSymbol newContainingType = SubstituteNamedType(previous.ContainingType);
            if ((object)newContainingType == null)
            {
                return previous;
            }
 
            return previous.OriginalDefinition.AsMember(newContainingType);
        }
 
        /// <summary>
        /// SubstType, but for NamedTypeSymbols only.  This is used for concrete types, so no alpha substitution appears in the result.
        /// </summary>
        internal NamedTypeSymbol SubstituteNamedType(NamedTypeSymbol previous)
        {
            if (ReferenceEquals(previous, null))
                return null;
 
            if (previous.IsUnboundGenericType)
                return previous;
 
            if (previous.IsAnonymousType)
            {
                return ((AnonymousTypeManager.AnonymousTypeOrDelegatePublicSymbol)previous).SubstituteTypes(this);
            }
 
            // TODO: we could construct the result's ConstructedFrom lazily by using a "deep"
            // construct operation here (as VB does), thereby avoiding alpha renaming in most cases.
            // Aleksey has shown that would reduce GC pressure if substitutions of deeply nested generics are common.
            NamedTypeSymbol oldConstructedFrom = previous.ConstructedFrom;
            NamedTypeSymbol newConstructedFrom = SubstituteTypeDeclaration(oldConstructedFrom);
 
            ImmutableArray<TypeWithAnnotations> oldTypeArguments = previous.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics;
            bool changed = !ReferenceEquals(oldConstructedFrom, newConstructedFrom);
            var newTypeArguments = ArrayBuilder<TypeWithAnnotations>.GetInstance(oldTypeArguments.Length);
 
            for (int i = 0; i < oldTypeArguments.Length; i++)
            {
                var oldArgument = oldTypeArguments[i];
                var newArgument = oldArgument.SubstituteType(this);
 
                if (!changed && !oldArgument.IsSameAs(newArgument))
                {
                    changed = true;
                }
 
                newTypeArguments.Add(newArgument);
            }
 
            if (!changed)
            {
                newTypeArguments.Free();
                return previous;
            }
 
            return newConstructedFrom.ConstructIfGeneric(newTypeArguments.ToImmutableAndFree()).WithTupleDataFrom(previous);
        }
 
        /// <summary>
        /// Perform the substitution on the given type.  Each occurrence of the type parameter is
        /// replaced with its corresponding type argument from the map.
        /// </summary>
        /// <param name="previous">The type to be rewritten.</param>
        /// <returns>The type with type parameters replaced with the type arguments.</returns>
        internal TypeWithAnnotations SubstituteType(TypeSymbol previous)
        {
            if (ReferenceEquals(previous, null))
                return default(TypeWithAnnotations);
 
            TypeSymbol result;
 
            switch (previous.Kind)
            {
                case SymbolKind.NamedType:
                    result = SubstituteNamedType((NamedTypeSymbol)previous);
                    break;
                case SymbolKind.TypeParameter:
                    return SubstituteTypeParameter((TypeParameterSymbol)previous);
                case SymbolKind.ArrayType:
                    result = SubstituteArrayType((ArrayTypeSymbol)previous);
                    break;
                case SymbolKind.PointerType:
                    result = SubstitutePointerType((PointerTypeSymbol)previous);
                    break;
                case SymbolKind.FunctionPointerType:
                    result = SubstituteFunctionPointerType((FunctionPointerTypeSymbol)previous);
                    break;
                case SymbolKind.DynamicType:
                    result = SubstituteDynamicType();
                    break;
                case SymbolKind.ErrorType:
                    return ((ErrorTypeSymbol)previous).Substitute(this);
                default:
                    result = previous;
                    break;
            }
 
            return TypeWithAnnotations.Create(result);
        }
 
        internal TypeWithAnnotations SubstituteType(TypeWithAnnotations previous)
        {
            return previous.SubstituteType(this);
        }
 
        internal virtual ImmutableArray<CustomModifier> SubstituteCustomModifiers(ImmutableArray<CustomModifier> customModifiers)
        {
            if (customModifiers.IsDefaultOrEmpty)
            {
                return customModifiers;
            }
 
            for (int i = 0; i < customModifiers.Length; i++)
            {
                NamedTypeSymbol modifier = ((CSharpCustomModifier)customModifiers[i]).ModifierSymbol;
                var substituted = SubstituteNamedType(modifier);
 
                if (!TypeSymbol.Equals(modifier, substituted, TypeCompareKind.ConsiderEverything2))
                {
                    var builder = ArrayBuilder<CustomModifier>.GetInstance(customModifiers.Length);
                    builder.AddRange(customModifiers, i);
 
                    builder.Add(customModifiers[i].IsOptional ? CSharpCustomModifier.CreateOptional(substituted) : CSharpCustomModifier.CreateRequired(substituted));
                    for (i++; i < customModifiers.Length; i++)
                    {
                        modifier = ((CSharpCustomModifier)customModifiers[i]).ModifierSymbol;
                        substituted = SubstituteNamedType(modifier);
 
                        if (!TypeSymbol.Equals(modifier, substituted, TypeCompareKind.ConsiderEverything2))
                        {
                            builder.Add(customModifiers[i].IsOptional ? CSharpCustomModifier.CreateOptional(substituted) : CSharpCustomModifier.CreateRequired(substituted));
                        }
                        else
                        {
                            builder.Add(customModifiers[i]);
                        }
                    }
 
                    Debug.Assert(builder.Count == customModifiers.Length);
                    return builder.ToImmutableAndFree();
                }
            }
 
            return customModifiers;
        }
 
        protected virtual TypeSymbol SubstituteDynamicType()
        {
            return DynamicTypeSymbol.Instance;
        }
 
        protected virtual TypeWithAnnotations SubstituteTypeParameter(TypeParameterSymbol typeParameter)
        {
            return TypeWithAnnotations.Create(typeParameter);
        }
 
        private ArrayTypeSymbol SubstituteArrayType(ArrayTypeSymbol t)
        {
            var oldElement = t.ElementTypeWithAnnotations;
            TypeWithAnnotations element = oldElement.SubstituteType(this);
            if (element.IsSameAs(oldElement))
            {
                return t;
            }
 
            if (t.IsSZArray)
            {
                ImmutableArray<NamedTypeSymbol> interfaces = t.InterfacesNoUseSiteDiagnostics();
                Debug.Assert(0 <= interfaces.Length && interfaces.Length <= 2);
 
                if (interfaces.Length == 1)
                {
                    Debug.Assert(interfaces[0] is NamedTypeSymbol); // IList<T>
                    interfaces = ImmutableArray.Create<NamedTypeSymbol>(SubstituteNamedType(interfaces[0]));
                }
                else if (interfaces.Length == 2)
                {
                    Debug.Assert(interfaces[0] is NamedTypeSymbol); // IList<T>
                    interfaces = ImmutableArray.Create<NamedTypeSymbol>(SubstituteNamedType(interfaces[0]), SubstituteNamedType(interfaces[1]));
                }
                else if (interfaces.Length != 0)
                {
                    throw ExceptionUtilities.Unreachable();
                }
 
                return ArrayTypeSymbol.CreateSZArray(
                    element,
                    t.BaseTypeNoUseSiteDiagnostics,
                    interfaces);
            }
 
            return ArrayTypeSymbol.CreateMDArray(
                element,
                t.Rank,
                t.Sizes,
                t.LowerBounds,
                t.BaseTypeNoUseSiteDiagnostics);
        }
 
        private PointerTypeSymbol SubstitutePointerType(PointerTypeSymbol t)
        {
            var oldPointedAtType = t.PointedAtTypeWithAnnotations;
            var pointedAtType = oldPointedAtType.SubstituteType(this);
            if (pointedAtType.IsSameAs(oldPointedAtType))
            {
                return t;
            }
 
            return new PointerTypeSymbol(pointedAtType);
        }
 
        private FunctionPointerTypeSymbol SubstituteFunctionPointerType(FunctionPointerTypeSymbol f)
        {
            var substitutedReturnType = f.Signature.ReturnTypeWithAnnotations.SubstituteType(this);
            var refCustomModifiers = f.Signature.RefCustomModifiers;
            var substitutedRefCustomModifiers = SubstituteCustomModifiers(refCustomModifiers);
 
            var parameterTypesWithAnnotations = f.Signature.ParameterTypesWithAnnotations;
            ImmutableArray<TypeWithAnnotations> substitutedParamTypes = SubstituteTypes(parameterTypesWithAnnotations);
 
            ImmutableArray<ImmutableArray<CustomModifier>> substitutedParamModifiers = default;
 
            var paramCount = f.Signature.Parameters.Length;
            if (paramCount > 0)
            {
                var builder = ArrayBuilder<ImmutableArray<CustomModifier>>.GetInstance(paramCount);
                bool didSubstitute = false;
                foreach (var param in f.Signature.Parameters)
                {
                    var substituted = SubstituteCustomModifiers(param.RefCustomModifiers);
                    builder.Add(substituted);
                    if (substituted != param.RefCustomModifiers)
                    {
                        didSubstitute = true;
                    }
                }
 
                if (didSubstitute)
                {
                    substitutedParamModifiers = builder.ToImmutableAndFree();
                }
                else
                {
                    builder.Free();
                }
            }
 
            if (substitutedParamTypes != parameterTypesWithAnnotations
                || !substitutedParamModifiers.IsDefault
                || !f.Signature.ReturnTypeWithAnnotations.IsSameAs(substitutedReturnType)
                || substitutedRefCustomModifiers != refCustomModifiers)
            {
                f = f.SubstituteTypeSymbol(substitutedReturnType, substitutedParamTypes, refCustomModifiers, substitutedParamModifiers);
            }
 
            return f;
        }
 
        internal ImmutableArray<TypeSymbol> SubstituteTypesWithoutModifiers(ImmutableArray<TypeSymbol> original)
        {
            if (original.IsDefault)
            {
                return original;
            }
 
            TypeSymbol[] result = null;
 
            for (int i = 0; i < original.Length; i++)
            {
                var t = original[i];
                var substituted = SubstituteType(t).Type;
                if (!Object.ReferenceEquals(substituted, t))
                {
                    if (result == null)
                    {
                        result = new TypeSymbol[original.Length];
                        for (int j = 0; j < i; j++)
                        {
                            result[j] = original[j];
                        }
                    }
                }
 
                if (result != null)
                {
                    result[i] = substituted;
                }
            }
 
            return result != null ? ImmutableCollectionsMarshal.AsImmutableArray(result) : original;
        }
 
        internal ImmutableArray<TypeWithAnnotations> SubstituteTypes(ImmutableArray<TypeWithAnnotations> original)
        {
            if (original.IsDefault)
            {
                return default(ImmutableArray<TypeWithAnnotations>);
            }
 
            var result = ArrayBuilder<TypeWithAnnotations>.GetInstance(original.Length);
 
            foreach (TypeWithAnnotations t in original)
            {
                result.Add(SubstituteType(t));
            }
 
            return result.ToImmutableAndFree();
        }
 
        /// <summary>
        /// Substitute types, and return the results without duplicates, preserving the original order.
        /// Note, all occurrences of 'dynamic' in resulting types will be replaced with 'object'.
        /// </summary>
        internal void SubstituteConstraintTypesDistinctWithoutModifiers(
            TypeParameterSymbol owner,
            ImmutableArray<TypeWithAnnotations> original,
            ArrayBuilder<TypeWithAnnotations> result,
            HashSet<TypeParameterSymbol> ignoreTypesDependentOnTypeParametersOpt)
        {
            DynamicTypeEraser dynamicEraser = null;
 
            if (original.Length == 0)
            {
                return;
            }
            else if (original.Length == 1)
            {
                var type = original[0];
                if (ignoreTypesDependentOnTypeParametersOpt == null ||
                    !type.Type.ContainsTypeParameters(ignoreTypesDependentOnTypeParametersOpt))
                {
                    result.Add(substituteConstraintType(type));
                }
            }
            else
            {
                var map = PooledDictionary<TypeSymbol, int>.GetInstance();
                foreach (var type in original)
                {
                    if (ignoreTypesDependentOnTypeParametersOpt == null ||
                        !type.Type.ContainsTypeParameters(ignoreTypesDependentOnTypeParametersOpt))
                    {
                        var substituted = substituteConstraintType(type);
 
                        if (!map.TryGetValue(substituted.Type, out int mergeWith))
                        {
                            map.Add(substituted.Type, result.Count);
                            result.Add(substituted);
                        }
                        else
                        {
                            result[mergeWith] = ConstraintsHelper.ConstraintWithMostSignificantNullability(result[mergeWith], substituted);
                        }
                    }
                }
 
                map.Free();
            }
 
            TypeWithAnnotations substituteConstraintType(TypeWithAnnotations type)
            {
                if (dynamicEraser == null)
                {
                    dynamicEraser = new DynamicTypeEraser(owner.ContainingAssembly.CorLibrary.GetSpecialType(SpecialType.System_Object));
                }
 
                TypeWithAnnotations substituted = SubstituteType(type);
 
                return substituted.WithTypeAndModifiers(dynamicEraser.EraseDynamic(substituted.Type), substituted.CustomModifiers);
            }
        }
 
        internal ImmutableArray<TypeParameterSymbol> SubstituteTypeParameters(ImmutableArray<TypeParameterSymbol> original)
        {
            return original.SelectAsArray((tp, m) => (TypeParameterSymbol)m.SubstituteTypeParameter(tp).AsTypeSymbolOnly(), this);
        }
 
        /// <summary>
        /// Like SubstTypes, but for NamedTypeSymbols.
        /// </summary>
        internal ImmutableArray<NamedTypeSymbol> SubstituteNamedTypes(ImmutableArray<NamedTypeSymbol> original)
        {
            NamedTypeSymbol[] result = null;
 
            for (int i = 0; i < original.Length; i++)
            {
                var t = original[i];
                var substituted = SubstituteNamedType(t);
                if (!Object.ReferenceEquals(substituted, t))
                {
                    if (result == null)
                    {
                        result = new NamedTypeSymbol[original.Length];
                        for (int j = 0; j < i; j++)
                        {
                            result[j] = original[j];
                        }
                    }
                }
 
                if (result != null)
                {
                    result[i] = substituted;
                }
            }
 
            return result != null ? ImmutableCollectionsMarshal.AsImmutableArray(result) : original;
        }
    }
}