File: Symbols\Source\SourceFixedFieldSymbol.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 System.Threading;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
    internal class SourceFixedFieldSymbol : SourceMemberFieldSymbolFromDeclarator
    {
        private const int FixedSizeNotInitialized = -1;
 
        // In a fixed-size field declaration, stores the fixed size of the buffer
        private int _fixedSize = FixedSizeNotInitialized;
 
        internal SourceFixedFieldSymbol(
            SourceMemberContainerTypeSymbol containingType,
            VariableDeclaratorSyntax declarator,
            DeclarationModifiers modifiers,
            bool modifierErrors,
            BindingDiagnosticBag diagnostics)
            : base(containingType, declarator, modifiers, modifierErrors, diagnostics)
        {
            // Checked in parser: a fixed field declaration requires a length in square brackets
 
            Debug.Assert(this.IsFixedSizeBuffer);
        }
 
        internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder<SynthesizedAttributeData> attributes)
        {
            base.AddSynthesizedAttributes(moduleBuilder, ref attributes);
 
            var compilation = this.DeclaringCompilation;
            var systemType = compilation.GetWellKnownType(WellKnownType.System_Type);
            var intType = compilation.GetSpecialType(SpecialType.System_Int32);
            var item1 = new TypedConstant(systemType, TypedConstantKind.Type, ((PointerTypeSymbol)this.Type).PointedAtType);
            var item2 = new TypedConstant(intType, TypedConstantKind.Primitive, this.FixedSize);
            AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(
                WellKnownMember.System_Runtime_CompilerServices_FixedBufferAttribute__ctor,
                ImmutableArray.Create<TypedConstant>(item1, item2)));
        }
 
        public sealed override int FixedSize
        {
            get
            {
                if (_fixedSize == FixedSizeNotInitialized)
                {
                    BindingDiagnosticBag diagnostics = BindingDiagnosticBag.GetInstance();
                    int size = 0;
 
                    VariableDeclaratorSyntax declarator = VariableDeclaratorNode;
                    if (declarator.ArgumentList == null)
                    {
                        // Diagnostic reported by parser.
                    }
                    else
                    {
                        SeparatedSyntaxList<ArgumentSyntax> arguments = declarator.ArgumentList.Arguments;
 
                        if (arguments.Count == 0 || arguments[0].Expression.Kind() == SyntaxKind.OmittedArraySizeExpression)
                        {
                            Debug.Assert(declarator.ArgumentList.ContainsDiagnostics, "The parser should have caught this.");
                        }
                        else
                        {
                            if (arguments.Count > 1)
                            {
                                diagnostics.Add(ErrorCode.ERR_FixedBufferTooManyDimensions, declarator.ArgumentList.Location);
                            }
 
                            ExpressionSyntax sizeExpression = arguments[0].Expression;
 
                            BinderFactory binderFactory = this.DeclaringCompilation.GetBinderFactory(SyntaxTree);
                            Binder binder = binderFactory.GetBinder(sizeExpression);
                            binder = new ExecutableCodeBinder(sizeExpression, binder.ContainingMemberOrLambda, binder).GetBinder(sizeExpression);
 
                            TypeSymbol intType = binder.GetSpecialType(SpecialType.System_Int32, diagnostics, sizeExpression);
                            BoundExpression boundSizeExpression = binder.GenerateConversionForAssignment(
                                intType,
                                binder.BindValue(sizeExpression, diagnostics, Binder.BindValueKind.RValue),
                                diagnostics);
 
                            // GetAndValidateConstantValue doesn't generate a very intuitive-reading diagnostic
                            // for this situation, but this is what the Dev10 compiler produces.
                            ConstantValue sizeConstant = ConstantValueUtils.GetAndValidateConstantValue(boundSizeExpression, this, intType, sizeExpression, diagnostics);
 
                            Debug.Assert(sizeConstant != null);
                            Debug.Assert(sizeConstant.IsIntegral || diagnostics.HasAnyErrors() || sizeExpression.HasErrors);
 
                            if (sizeConstant.IsIntegral)
                            {
                                int int32Value = sizeConstant.Int32Value;
                                if (int32Value > 0)
                                {
                                    size = int32Value;
 
                                    TypeSymbol elementType = ((PointerTypeSymbol)this.Type).PointedAtType;
                                    int elementSize = elementType.FixedBufferElementSizeInBytes();
                                    long totalSize = elementSize * 1L * int32Value;
                                    if (totalSize > int.MaxValue)
                                    {
                                        // Fixed size buffer of length '{0}' and type '{1}' is too big
                                        diagnostics.Add(ErrorCode.ERR_FixedOverflow, sizeExpression.Location, int32Value, elementType);
                                    }
                                }
                                else
                                {
                                    diagnostics.Add(ErrorCode.ERR_InvalidFixedArraySize, sizeExpression.Location);
                                }
                            }
                        }
                    }
 
                    // Winner writes diagnostics.
                    if (Interlocked.CompareExchange(ref _fixedSize, size, FixedSizeNotInitialized) == FixedSizeNotInitialized)
                    {
                        this.AddDeclarationDiagnostics(diagnostics);
                        state.NotePartComplete(CompletionPart.FixedSize);
                    }
 
                    diagnostics.Free();
                }
 
                Debug.Assert(_fixedSize != FixedSizeNotInitialized);
                return _fixedSize;
            }
        }
 
        internal override NamedTypeSymbol FixedImplementationType(PEModuleBuilder emitModule)
        {
            return emitModule.SetFixedImplementationType(this);
        }
    }
 
    internal sealed class FixedFieldImplementationType : SynthesizedContainer
    {
        internal const string FixedElementFieldName = "FixedElementField";
 
        private readonly SourceMemberFieldSymbol _field;
        private readonly MethodSymbol _constructor;
        private readonly FieldSymbol _internalField;
 
        public FixedFieldImplementationType(SourceMemberFieldSymbol field)
            : base(GeneratedNames.MakeFixedFieldImplementationName(field.Name), typeParameters: ImmutableArray<TypeParameterSymbol>.Empty, typeMap: TypeMap.Empty)
        {
            _field = field;
            _constructor = new SynthesizedInstanceConstructor(this);
            _internalField = new SynthesizedFieldSymbol(this, ((PointerTypeSymbol)field.Type).PointedAtType, FixedElementFieldName, isPublic: true);
        }
 
        public override Symbol ContainingSymbol
        {
            get { return _field.ContainingType; }
        }
 
        public override TypeKind TypeKind
        {
            get { return TypeKind.Struct; }
        }
 
        internal override MethodSymbol Constructor
        {
            get { return _constructor; }
        }
 
        internal override TypeLayout Layout
        {
            get
            {
                int nElements = _field.FixedSize;
                var elementType = ((PointerTypeSymbol)_field.Type).PointedAtType;
                int elementSize = elementType.FixedBufferElementSizeInBytes();
                const int alignment = 0;
                int totalSize = nElements * elementSize;
                const LayoutKind layoutKind = LayoutKind.Sequential;
                return new TypeLayout(layoutKind, totalSize, alignment);
            }
        }
 
        internal override CharSet MarshallingCharSet
        {
            get
            {
                // We manually propagate the CharSet field of StructLayout attribute for fabricated structs implementing fixed buffers.
                // See void AttrBind::EmitStructLayoutAttributeCharSet(AttributeNode *attr) in native codebase.
                return _field.ContainingType.MarshallingCharSet;
            }
        }
        internal override FieldSymbol FixedElementField
        {
            get { return _internalField; }
        }
 
        internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder<SynthesizedAttributeData> attributes)
        {
            base.AddSynthesizedAttributes(moduleBuilder, ref attributes);
            var compilation = ContainingSymbol.DeclaringCompilation;
            AddSynthesizedAttribute(ref attributes, compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_UnsafeValueTypeAttribute__ctor));
        }
 
        public override IEnumerable<string> MemberNames
        {
            get { return SpecializedCollections.SingletonEnumerable(FixedElementFieldName); }
        }
 
        public override ImmutableArray<Symbol> GetMembers()
        {
            return ImmutableArray.Create<Symbol>(_constructor, _internalField);
        }
 
        public override ImmutableArray<Symbol> GetMembers(string name)
        {
            return
                (name == _constructor.Name) ? ImmutableArray.Create<Symbol>(_constructor) :
                (name == FixedElementFieldName) ? ImmutableArray.Create<Symbol>(_internalField) :
                ImmutableArray<Symbol>.Empty;
        }
 
        public override Accessibility DeclaredAccessibility
        {
            get { return Accessibility.Public; }
        }
 
        internal override NamedTypeSymbol BaseTypeNoUseSiteDiagnostics
            => ContainingAssembly.GetSpecialType(SpecialType.System_ValueType);
 
        public sealed override bool AreLocalsZeroed
            => throw ExceptionUtilities.Unreachable();
 
        internal override bool IsRecord => false;
        internal override bool IsRecordStruct => false;
        internal override bool HasPossibleWellKnownCloneMethod() => false;
    }
}