File: Symbols\NativeIntegerTypeSymbol.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.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
    /// <summary>
    /// This wrapper is only used on platforms where System.IntPtr isn't considered
    /// a numeric type (as indicated by a RuntimeFeature flag).
    /// </summary>
    internal sealed class NativeIntegerTypeSymbol : WrappedNamedTypeSymbol
#if !DEBUG
        , Cci.IReference
#endif
    {
        private ImmutableArray<NamedTypeSymbol> _lazyInterfaces;
        private ImmutableArray<Symbol> _lazyMembers;
        private NativeIntegerTypeMap? _lazyTypeMap;
 
        internal NativeIntegerTypeSymbol(NamedTypeSymbol underlyingType) : base(underlyingType, tupleData: null)
        {
            Debug.Assert(underlyingType.TupleData is null);
            Debug.Assert(!underlyingType.IsNativeIntegerType);
            Debug.Assert(underlyingType.SpecialType == SpecialType.System_IntPtr || underlyingType.SpecialType == SpecialType.System_UIntPtr);
            Debug.Assert(!underlyingType.ContainingAssembly.RuntimeSupportsNumericIntPtr);
            VerifyEquality(this, underlyingType);
        }
 
        public override ImmutableArray<TypeParameterSymbol> TypeParameters => ImmutableArray<TypeParameterSymbol>.Empty;
 
        public override NamedTypeSymbol ConstructedFrom => this;
 
        public override Symbol ContainingSymbol => _underlyingType.ContainingSymbol;
 
        internal override ImmutableArray<TypeWithAnnotations> TypeArgumentsWithAnnotationsNoUseSiteDiagnostics => ImmutableArray<TypeWithAnnotations>.Empty;
 
        internal override bool IsComImport => _underlyingType.IsComImport;
 
        internal override NamedTypeSymbol BaseTypeNoUseSiteDiagnostics => _underlyingType.BaseTypeNoUseSiteDiagnostics;
 
        public override ExtendedSpecialType ExtendedSpecialType => _underlyingType.ExtendedSpecialType;
 
        public override IEnumerable<string> MemberNames => GetMembers().Select(m => m.Name);
 
        internal override bool HasDeclaredRequiredMembers => false;
 
        /// <summary>
        /// Certain members from the underlying types are not exposed from the native integer types:
        ///   constructors other than the default parameterless constructor are not supported;
        ///   operators are handled explicitly as built-in operators and conversions;
        ///   0 should be used instead of Zero;
        ///   sizeof() should be used instead of Size;
        ///   + and - should be used instead of Add() and Subtract();
        ///   ToInt32(), ToInt64(), ToPointer() should be used from underlying types only.
        /// The remaining members are exposed on the native integer types with appropriate
        /// substitution of underlying types in the signatures.
        /// Specifically, we expose public, non-generic instance and static methods and properties
        /// other than those named above.
        /// </summary>
        public override ImmutableArray<Symbol> GetMembers()
        {
            if (_lazyMembers.IsDefault)
            {
                ImmutableInterlocked.InterlockedInitialize(ref _lazyMembers, makeMembers(_underlyingType.GetMembers()));
            }
            return _lazyMembers;
 
            ImmutableArray<Symbol> makeMembers(ImmutableArray<Symbol> underlyingMembers)
            {
                var builder = ArrayBuilder<Symbol>.GetInstance();
                foreach (var underlyingMember in underlyingMembers)
                {
                    Debug.Assert(_underlyingType.Equals(underlyingMember.ContainingSymbol));
                    if (underlyingMember.DeclaredAccessibility != Accessibility.Public)
                    {
                        continue;
                    }
                    switch (underlyingMember)
                    {
                        case MethodSymbol underlyingMethod:
                            if (underlyingMethod.IsGenericMethod || underlyingMethod.IsAccessor())
                            {
                                break;
                            }
                            switch (underlyingMethod.MethodKind)
                            {
                                case MethodKind.Ordinary:
                                    switch (underlyingMethod.Name)
                                    {
                                        case "Add":
                                        case "Subtract":
                                        case "ToInt32":
                                        case "ToInt64":
                                        case "ToUInt32":
                                        case "ToUInt64":
                                        case "ToPointer":
                                            break;
                                        default:
                                            builder.Add(new NativeIntegerMethodSymbol(this, underlyingMethod, associatedSymbol: null));
                                            break;
                                    }
                                    break;
 
                                case MethodKind.Constructor:
                                    if (underlyingMethod.ParameterCount == 0)
                                    {
                                        builder.Add(new NativeIntegerMethodSymbol(this, underlyingMethod, associatedSymbol: null));
                                    }
                                    break;
                            }
                            break;
                        case PropertySymbol underlyingProperty:
                            if (underlyingProperty.ParameterCount == 0 &&
                                underlyingProperty.Name != "Size")
                            {
                                var property = new NativeIntegerPropertySymbol(
                                    this,
                                    underlyingProperty,
                                    (container, property, underlyingAccessor) => underlyingAccessor is null ? null : new NativeIntegerMethodSymbol(container, underlyingAccessor, property));
                                builder.Add(property);
                                builder.AddIfNotNull(property.GetMethod);
                                builder.AddIfNotNull(property.SetMethod);
                            }
                            break;
                    }
                }
                return builder.ToImmutableAndFree();
            }
        }
 
        public override ImmutableArray<Symbol> GetMembers(string name) => GetMembers().WhereAsArray((member, name) => member.Name == name, name);
 
        public override ImmutableArray<NamedTypeSymbol> GetTypeMembers() => ImmutableArray<NamedTypeSymbol>.Empty;
 
        public override ImmutableArray<NamedTypeSymbol> GetTypeMembers(ReadOnlyMemory<char> name) => ImmutableArray<NamedTypeSymbol>.Empty;
 
        public override ImmutableArray<NamedTypeSymbol> GetTypeMembers(ReadOnlyMemory<char> name, int arity) => ImmutableArray<NamedTypeSymbol>.Empty;
 
        internal override NamedTypeSymbol GetDeclaredBaseType(ConsList<TypeSymbol> basesBeingResolved) => _underlyingType.GetDeclaredBaseType(basesBeingResolved);
 
        internal override ImmutableArray<NamedTypeSymbol> GetDeclaredInterfaces(ConsList<TypeSymbol> basesBeingResolved) => GetInterfaces(basesBeingResolved);
 
        internal override ImmutableArray<Symbol> GetEarlyAttributeDecodingMembers() => throw ExceptionUtilities.Unreachable();
 
        internal override ImmutableArray<Symbol> GetEarlyAttributeDecodingMembers(string name) => throw ExceptionUtilities.Unreachable();
 
        internal override IEnumerable<FieldSymbol> GetFieldsToEmit() => throw ExceptionUtilities.Unreachable();
 
        internal override ImmutableArray<NamedTypeSymbol> GetInterfacesToEmit() => throw ExceptionUtilities.Unreachable();
 
        internal override ImmutableArray<NamedTypeSymbol> InterfacesNoUseSiteDiagnostics(ConsList<TypeSymbol>? basesBeingResolved = null) => GetInterfaces(basesBeingResolved);
 
        protected override NamedTypeSymbol WithTupleDataCore(TupleExtraData newData) => throw ExceptionUtilities.Unreachable();
 
        internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
        {
            var useSiteInfo = _underlyingType.GetUseSiteInfo();
            Debug.Assert(useSiteInfo.DiagnosticInfo is null); // If assert fails, add unit test for use site diagnostic.
            return useSiteInfo;
        }
 
        public override bool AreLocalsZeroed => throw ExceptionUtilities.Unreachable();
 
        internal override bool IsNativeIntegerWrapperType => true;
 
        internal sealed override NamedTypeSymbol AsNativeInteger() => throw ExceptionUtilities.Unreachable();
 
        internal sealed override NamedTypeSymbol NativeIntegerUnderlyingType => _underlyingType;
 
        // note: there is no supported way to create a native integer type whose underlying type is file-local.
        internal sealed override bool IsFileLocal => false;
        internal sealed override FileIdentifier? AssociatedFileIdentifier => null;
 
        internal sealed override bool IsRecord => false;
        internal sealed override bool IsRecordStruct => false;
        internal sealed override bool HasPossibleWellKnownCloneMethod() => false;
 
        internal override bool Equals(TypeSymbol? other, TypeCompareKind comparison)
        {
            if (other is null)
            {
                return false;
            }
            if ((object)this == other)
            {
                return true;
            }
            if (!_underlyingType.Equals(other, comparison))
            {
                return false;
            }
 
            return (comparison & TypeCompareKind.IgnoreNativeIntegers) != 0 ||
                other.IsNativeIntegerWrapperType;
        }
 
        public override int GetHashCode() => _underlyingType.GetHashCode();
 
#if !DEBUG
        void Cci.IReference.Dispatch(Cci.MetadataVisitor visitor)
        {
            // Emit should use underlying symbol only.
            throw ExceptionUtilities.Unreachable();
        }
#endif 
 
        private ImmutableArray<NamedTypeSymbol> GetInterfaces(ConsList<TypeSymbol>? basesBeingResolved)
        {
            if (_lazyInterfaces.IsDefault)
            {
                var interfaces = _underlyingType.InterfacesNoUseSiteDiagnostics(basesBeingResolved).SelectAsArray((type, map) => map.SubstituteNamedType(type), GetTypeMap());
                ImmutableInterlocked.InterlockedInitialize(ref _lazyInterfaces, interfaces);
            }
            return _lazyInterfaces;
        }
 
        private NativeIntegerTypeMap GetTypeMap()
        {
            if (_lazyTypeMap is null)
            {
                Interlocked.CompareExchange(ref _lazyTypeMap, new NativeIntegerTypeMap(this), null);
            }
            return _lazyTypeMap;
        }
 
        /// <summary>
        /// Replaces references to underlying type with references to native integer type.
        /// </summary>
        internal TypeWithAnnotations SubstituteUnderlyingType(TypeWithAnnotations type) => type.SubstituteType(GetTypeMap());
 
        /// <summary>
        /// Replaces references to underlying type with references to native integer type.
        /// </summary>
        internal NamedTypeSymbol SubstituteUnderlyingType(NamedTypeSymbol type) => GetTypeMap().SubstituteNamedType(type);
 
        internal static bool EqualsHelper<TSymbol>(TSymbol symbol, Symbol? other, TypeCompareKind comparison, Func<TSymbol, Symbol> getUnderlyingSymbol)
            where TSymbol : Symbol
        {
            if (other is null)
            {
                return false;
            }
            if ((object)symbol == other)
            {
                return true;
            }
            if (!getUnderlyingSymbol(symbol).Equals(other, comparison))
            {
                return false;
            }
            return (comparison & TypeCompareKind.IgnoreNativeIntegers) != 0 ||
                other is TSymbol;
        }
 
        [Conditional("DEBUG")]
        internal static void VerifyEquality(Symbol symbolA, Symbol symbolB)
        {
            Debug.Assert(!symbolA.Equals(symbolB, TypeCompareKind.ConsiderEverything));
            Debug.Assert(!symbolB.Equals(symbolA, TypeCompareKind.ConsiderEverything));
            Debug.Assert(symbolA.Equals(symbolB, TypeCompareKind.IgnoreNativeIntegers));
            Debug.Assert(symbolB.Equals(symbolA, TypeCompareKind.IgnoreNativeIntegers));
            Debug.Assert(symbolA.GetHashCode() == symbolB.GetHashCode());
        }
 
        internal override IEnumerable<(MethodSymbol Body, MethodSymbol Implemented)> SynthesizedInterfaceMethodImpls()
        {
            return SpecializedCollections.EmptyEnumerable<(MethodSymbol Body, MethodSymbol Implemented)>();
        }
 
        internal override bool HasInlineArrayAttribute(out int length)
        {
            length = 0;
            return false;
        }
 
        internal sealed override bool HasCollectionBuilderAttribute(out TypeSymbol? builderType, out string? methodName)
        {
            builderType = null;
            methodName = null;
            return false;
        }
 
        internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol? builderArgument)
        {
            builderArgument = null;
            return false;
        }
 
        private sealed class NativeIntegerTypeMap : AbstractTypeMap
        {
            private readonly NativeIntegerTypeSymbol _type;
            private readonly SpecialType _specialType;
 
            internal NativeIntegerTypeMap(NativeIntegerTypeSymbol type)
            {
                _type = type;
                _specialType = _type.UnderlyingNamedType.SpecialType;
 
                Debug.Assert(_specialType == SpecialType.System_IntPtr || _specialType == SpecialType.System_UIntPtr);
            }
 
            internal override NamedTypeSymbol SubstituteTypeDeclaration(NamedTypeSymbol previous)
            {
                return previous.SpecialType == _specialType ?
                    _type :
                    base.SubstituteTypeDeclaration(previous);
            }
 
            internal override ImmutableArray<CustomModifier> SubstituteCustomModifiers(ImmutableArray<CustomModifier> customModifiers)
            {
                return customModifiers;
            }
        }
    }
 
    internal sealed class NativeIntegerMethodSymbol : WrappedMethodSymbol
#if !DEBUG
        , Cci.IReference
#endif
    {
        private readonly NativeIntegerTypeSymbol _container;
        private readonly NativeIntegerPropertySymbol? _associatedSymbol;
        private ImmutableArray<ParameterSymbol> _lazyParameters;
 
        internal NativeIntegerMethodSymbol(NativeIntegerTypeSymbol container, MethodSymbol underlyingMethod, NativeIntegerPropertySymbol? associatedSymbol)
        {
            Debug.Assert(!underlyingMethod.IsGenericMethod);
            _container = container;
            _associatedSymbol = associatedSymbol;
            UnderlyingMethod = underlyingMethod;
            NativeIntegerTypeSymbol.VerifyEquality(this, underlyingMethod);
        }
 
        public override Symbol ContainingSymbol => _container;
 
        public override MethodSymbol UnderlyingMethod { get; }
 
        public override TypeWithAnnotations ReturnTypeWithAnnotations => _container.SubstituteUnderlyingType(UnderlyingMethod.ReturnTypeWithAnnotations);
 
        public override ImmutableArray<TypeWithAnnotations> TypeArgumentsWithAnnotations => ImmutableArray<TypeWithAnnotations>.Empty;
 
        public override ImmutableArray<TypeParameterSymbol> TypeParameters => ImmutableArray<TypeParameterSymbol>.Empty;
 
        public override ImmutableArray<ParameterSymbol> Parameters
        {
            get
            {
                if (_lazyParameters.IsDefault)
                {
                    var parameters = UnderlyingMethod.Parameters.SelectAsArray((p, m) => (ParameterSymbol)new NativeIntegerParameterSymbol(m._container, m, p), this);
                    ImmutableInterlocked.InterlockedInitialize(ref _lazyParameters, parameters);
                }
                return _lazyParameters;
            }
        }
 
        public override ImmutableArray<MethodSymbol> ExplicitInterfaceImplementations => ImmutableArray<MethodSymbol>.Empty;
 
        public override ImmutableArray<CustomModifier> RefCustomModifiers => UnderlyingMethod.RefCustomModifiers;
 
        internal override UnmanagedCallersOnlyAttributeData? GetUnmanagedCallersOnlyAttributeData(bool forceComplete) => UnderlyingMethod.GetUnmanagedCallersOnlyAttributeData(forceComplete);
 
        public override Symbol? AssociatedSymbol => _associatedSymbol;
 
        internal override int CalculateLocalSyntaxOffset(int localPosition, SyntaxTree localTree)
        {
            throw ExceptionUtilities.Unreachable();
        }
 
        internal override bool IsNullableAnalysisEnabled() => throw ExceptionUtilities.Unreachable();
 
        public override bool Equals(Symbol? other, TypeCompareKind comparison) => NativeIntegerTypeSymbol.EqualsHelper(this, other, comparison, symbol => symbol.UnderlyingMethod);
 
        public override int GetHashCode() => UnderlyingMethod.GetHashCode();
 
#if !DEBUG
        void Cci.IReference.Dispatch(Cci.MetadataVisitor visitor)
        {
            // Emit should use underlying symbol only.
            throw ExceptionUtilities.Unreachable();
        }
#endif 
 
        internal sealed override bool HasAsyncMethodBuilderAttribute(out TypeSymbol? builderArgument)
        {
            builderArgument = null;
            return false;
        }
    }
 
    internal sealed class NativeIntegerParameterSymbol : WrappedParameterSymbol
#if !DEBUG
        , Cci.IReference
#endif
    {
        private readonly NativeIntegerTypeSymbol _containingType;
        private readonly NativeIntegerMethodSymbol _container;
 
        internal NativeIntegerParameterSymbol(NativeIntegerTypeSymbol containingType, NativeIntegerMethodSymbol container, ParameterSymbol underlyingParameter) : base(underlyingParameter)
        {
            Debug.Assert(container != null);
 
            _containingType = containingType;
            _container = container;
            NativeIntegerTypeSymbol.VerifyEquality(this, underlyingParameter);
        }
 
        public override Symbol ContainingSymbol => _container;
 
        public override TypeWithAnnotations TypeWithAnnotations => _containingType.SubstituteUnderlyingType(_underlyingParameter.TypeWithAnnotations);
 
        public override ImmutableArray<CustomModifier> RefCustomModifiers => _underlyingParameter.RefCustomModifiers;
 
        internal override bool IsCallerLineNumber => _underlyingParameter.IsCallerLineNumber;
 
        internal override bool IsCallerFilePath => _underlyingParameter.IsCallerFilePath;
 
        internal override bool IsCallerMemberName => _underlyingParameter.IsCallerMemberName;
 
        internal override int CallerArgumentExpressionParameterIndex => _underlyingParameter.CallerArgumentExpressionParameterIndex;
 
        internal override ImmutableArray<int> InterpolatedStringHandlerArgumentIndexes => _underlyingParameter.InterpolatedStringHandlerArgumentIndexes;
 
        internal override bool HasInterpolatedStringHandlerArgumentError => _underlyingParameter.HasInterpolatedStringHandlerArgumentError;
 
        public override bool Equals(Symbol? other, TypeCompareKind comparison) => NativeIntegerTypeSymbol.EqualsHelper(this, other, comparison, symbol => symbol._underlyingParameter);
 
        public override int GetHashCode() => _underlyingParameter.GetHashCode();
 
#if !DEBUG
        void Cci.IReference.Dispatch(Cci.MetadataVisitor visitor)
        {
            // Emit should use underlying symbol only.
            throw ExceptionUtilities.Unreachable();
        }
#endif
    }
 
    internal sealed class NativeIntegerPropertySymbol : WrappedPropertySymbol
#if !DEBUG
        , Cci.IReference
#endif
    {
        private readonly NativeIntegerTypeSymbol _container;
 
        internal NativeIntegerPropertySymbol(
            NativeIntegerTypeSymbol container,
            PropertySymbol underlyingProperty,
            Func<NativeIntegerTypeSymbol, NativeIntegerPropertySymbol, MethodSymbol?, NativeIntegerMethodSymbol?> getAccessor) :
            base(underlyingProperty)
        {
            Debug.Assert(underlyingProperty.ParameterCount == 0);
            _container = container;
            GetMethod = getAccessor(container, this, underlyingProperty.GetMethod);
            SetMethod = getAccessor(container, this, underlyingProperty.SetMethod);
            NativeIntegerTypeSymbol.VerifyEquality(this, underlyingProperty);
        }
 
        public override Symbol ContainingSymbol => _container;
 
        public override TypeWithAnnotations TypeWithAnnotations => _container.SubstituteUnderlyingType(_underlyingProperty.TypeWithAnnotations);
 
        public override ImmutableArray<CustomModifier> RefCustomModifiers => UnderlyingProperty.RefCustomModifiers;
 
        public override ImmutableArray<ParameterSymbol> Parameters => ImmutableArray<ParameterSymbol>.Empty;
 
        public override MethodSymbol? GetMethod { get; }
 
        public override MethodSymbol? SetMethod { get; }
 
        public override ImmutableArray<PropertySymbol> ExplicitInterfaceImplementations => ImmutableArray<PropertySymbol>.Empty;
 
        internal override bool MustCallMethodsDirectly => _underlyingProperty.MustCallMethodsDirectly;
 
        public override bool Equals(Symbol? other, TypeCompareKind comparison) => NativeIntegerTypeSymbol.EqualsHelper(this, other, comparison, symbol => symbol._underlyingProperty);
 
        public override int GetHashCode() => _underlyingProperty.GetHashCode();
 
#if !DEBUG
        void Cci.IReference.Dispatch(Cci.MetadataVisitor visitor)
        {
            // Emit should use underlying symbol only.
            throw ExceptionUtilities.Unreachable();
        }
#endif
    }
}