| File: Symbols\AbstractTypeMap.cs | Web Access |
| Project: src\roslyn\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; } } }