File: Symbols\FieldSymbol.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.CSharp.Emit;
using Microsoft.CodeAnalysis.RuntimeMembers;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
    /// <summary>
    /// Represents a field in a class, struct or enum
    /// </summary>
    internal abstract partial class FieldSymbol : Symbol, IFieldSymbolInternal
    {
        // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        // Changes to the public interface of this class should remain synchronized with the VB version.
        // Do not make any changes to the public interface without making the corresponding change
        // to the VB version.
        // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 
        internal FieldSymbol()
        {
        }
 
        /// <summary>
        /// The original definition of this symbol. If this symbol is constructed from another
        /// symbol by type substitution then OriginalDefinition gets the original symbol as it was defined in
        /// source or metadata.
        /// </summary>
        public new virtual FieldSymbol OriginalDefinition
        {
            get
            {
                return this;
            }
        }
 
        protected sealed override Symbol OriginalSymbolDefinition
        {
            get
            {
                return this.OriginalDefinition;
            }
        }
 
        /// <summary>
        /// Gets the type of this field along with its annotations.
        /// </summary>
        public TypeWithAnnotations TypeWithAnnotations
        {
            get
            {
                return GetFieldType(ConsList<FieldSymbol>.Empty);
            }
        }
 
        public abstract RefKind RefKind { get; }
 
        public abstract ImmutableArray<CustomModifier> RefCustomModifiers { get; }
 
        public abstract FlowAnalysisAnnotations FlowAnalysisAnnotations { get; }
 
        /// <summary>
        /// Gets the type of this field.
        /// </summary>
        public TypeSymbol Type => TypeWithAnnotations.Type;
 
        internal abstract TypeWithAnnotations GetFieldType(ConsList<FieldSymbol> fieldsBeingBound);
 
        /// <summary>
        /// If this field serves as a backing variable for an automatically generated
        /// property or a field-like event, returns that 
        /// property/event. Otherwise returns null.
        /// Note, the set of possible associated symbols might be expanded in the future to 
        /// reflect changes in the languages.
        /// </summary>
        public abstract Symbol AssociatedSymbol { get; }
 
        /// <summary>
        /// Returns true if this field was declared as "readonly". 
        /// </summary>
        public abstract bool IsReadOnly { get; }
 
        /// <summary>
        /// Returns true if this field was declared as "volatile". 
        /// </summary>
        public abstract bool IsVolatile { get; }
 
        /// <summary>
        /// Returns true if this symbol requires an instance reference as the implicit receiver. This is false if the symbol is static.
        /// </summary>
        public virtual bool RequiresInstanceReceiver => !IsStatic;
 
        /// <summary>
        /// Returns true if this field was declared as "fixed".
        /// Note that for a fixed-size buffer declaration, this.Type will be a pointer type, of which
        /// the pointed-to type will be the declared element type of the fixed-size buffer.
        /// </summary>
        public virtual bool IsFixedSizeBuffer { get { return false; } }
 
        /// <summary>
        /// If IsFixedSizeBuffer is true, the value between brackets in the fixed-size-buffer declaration.
        /// If IsFixedSizeBuffer is false FixedSize is 0.
        /// Note that for fixed-a size buffer declaration, this.Type will be a pointer type, of which
        /// the pointed-to type will be the declared element type of the fixed-size buffer.
        /// </summary>
        public virtual int FixedSize { get { return 0; } }
 
        /// <summary>
        /// If this.IsFixedSizeBuffer is true, returns the underlying implementation type for the
        /// fixed-size buffer when emitted.  Otherwise returns null.
        /// </summary>
        internal virtual NamedTypeSymbol FixedImplementationType(PEModuleBuilder emitModule)
        {
            return null;
        }
 
        /// <summary>
        /// Returns true when field is a backing field for a captured frame pointer (typically "this").
        /// </summary>
        internal virtual bool IsCapturedFrame { get { return false; } }
 
        /// <summary>
        /// Returns true if this field was declared as "const" (i.e. is a constant declaration).
        /// Also returns true for an enum member.
        /// </summary>
        public abstract bool IsConst { get; }
 
        // Gets a value indicating whether this instance is metadata constant. A constant field is considered to be 
        // metadata constant unless they are of type decimal, because decimals are not regarded as constant by the CLR.
        public bool IsMetadataConstant
        {
            get { return this.IsConst && (this.Type.SpecialType != SpecialType.System_Decimal); }
        }
 
        /// <summary>
        /// Returns false if the field wasn't declared as "const", or constant value was omitted or erroneous.
        /// True otherwise.
        /// </summary>
        public virtual bool HasConstantValue
        {
            get
            {
                if (!IsConst)
                {
                    return false;
                }
 
                ConstantValue constantValue = GetConstantValue(ConstantFieldsInProgress.Empty, earlyDecodingWellKnownAttributes: false);
                return constantValue != null && !constantValue.IsBad; //can be null in error scenarios
            }
        }
 
        /// <summary>
        /// If IsConst returns true, then returns the constant value of the field or enum member. If IsConst returns
        /// false, then returns null.
        /// </summary>
        public virtual object ConstantValue
        {
            get
            {
                if (!IsConst)
                {
                    return null;
                }
 
                ConstantValue constantValue = GetConstantValue(ConstantFieldsInProgress.Empty, earlyDecodingWellKnownAttributes: false);
                return constantValue == null ? null : constantValue.Value; //can be null in error scenarios
            }
        }
 
        internal abstract ConstantValue GetConstantValue(ConstantFieldsInProgress inProgress, bool earlyDecodingWellKnownAttributes);
 
        /// <summary>
        /// Gets the kind of this symbol.
        /// </summary>
        public sealed override SymbolKind Kind
        {
            get
            {
                return SymbolKind.Field;
            }
        }
 
        internal override TResult Accept<TArgument, TResult>(CSharpSymbolVisitor<TArgument, TResult> visitor, TArgument argument)
        {
            return visitor.VisitField(this, argument);
        }
 
        public override void Accept(CSharpSymbolVisitor visitor)
        {
            visitor.VisitField(this);
        }
 
        public override TResult Accept<TResult>(CSharpSymbolVisitor<TResult> visitor)
        {
            return visitor.VisitField(this);
        }
 
        /// <summary>
        /// Returns false because field can't be abstract.
        /// </summary>
        public sealed override bool IsAbstract
        {
            get
            {
                return false;
            }
        }
 
        /// <summary>
        /// Returns false because field can't be defined externally.
        /// </summary>
        public sealed override bool IsExtern
        {
            get
            {
                return false;
            }
        }
 
        /// <summary>
        /// Returns false because field can't be overridden.
        /// </summary>
        public sealed override bool IsOverride
        {
            get
            {
                return false;
            }
        }
 
        /// <summary>
        /// Returns false because field can't be sealed.
        /// </summary>
        public sealed override bool IsSealed
        {
            get
            {
                return false;
            }
        }
 
        /// <summary>
        /// Returns false because field can't be virtual.
        /// </summary>
        public sealed override bool IsVirtual
        {
            get
            {
                return false;
            }
        }
 
        /// <summary>
        /// True if this symbol has a special name (metadata flag SpecialName is set).
        /// </summary>
        internal abstract bool HasSpecialName { get; }
 
        /// <summary>
        /// True if this symbol has a runtime-special name (metadata flag RuntimeSpecialName is set).
        /// </summary>
        internal abstract bool HasRuntimeSpecialName { get; }
 
        /// <summary>
        /// True if this field is not serialized (metadata flag NotSerialized is set).
        /// </summary>
        internal abstract bool IsNotSerialized { get; }
 
        /// <summary>
        /// True if this field has a pointer type.
        /// </summary>
        /// <remarks>
        /// By default we defer to this.Type.IsPointerOrFunctionPointer() 
        /// However in some cases this may cause circular dependency via binding a
        /// pointer that points to the type that contains the current field.
        /// Fortunately in those cases we do not need to force binding of the field's type 
        /// and can just check the declaration syntax if the field type is not yet known.
        /// </remarks>
        internal virtual bool HasPointerType
        {
            get
            {
                return this.Type.IsPointerOrFunctionPointer();
            }
        }
 
        /// <summary>
        /// Describes how the field is marshalled when passed to native code.
        /// Null if no specific marshalling information is available for the field.
        /// </summary>
        /// <remarks>PE symbols don't provide this information and always return null.</remarks>
        internal abstract MarshalPseudoCustomAttributeData MarshallingInformation { get; }
 
        /// <summary>
        /// Returns the marshalling type of this field, or 0 if marshalling information isn't available.
        /// </summary>
        /// <remarks>
        /// By default this information is extracted from <see cref="MarshallingInformation"/> if available. 
        /// Since the compiler does only need to know the marshalling type of symbols that aren't emitted 
        /// PE symbols just decode the type from metadata and don't provide full marshalling information.
        /// </remarks>
        internal virtual UnmanagedType MarshallingType
        {
            get
            {
                var info = MarshallingInformation;
                return info != null ? info.UnmanagedType : 0;
            }
        }
 
        /// <summary>
        /// Offset assigned to the field when the containing type is laid out by the VM.
        /// Null if unspecified.
        /// </summary>
        internal abstract int? TypeLayoutOffset { get; }
 
        internal virtual FieldSymbol AsMember(NamedTypeSymbol newOwner)
        {
            Debug.Assert(this.IsDefinition);
            Debug.Assert(ReferenceEquals(newOwner.OriginalDefinition, this.ContainingSymbol.OriginalDefinition));
            return newOwner.IsDefinition ? this : new SubstitutedFieldSymbol(newOwner as SubstitutedNamedTypeSymbol, this);
        }
 
        /// <summary>
        /// Returns true if this field is required to be set in an object initializer on object creation.
        /// </summary>
        internal abstract bool IsRequired { get; }
 
        #region Use-Site Diagnostics
 
        internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
        {
            if (this.IsDefinition)
            {
                return new UseSiteInfo<AssemblySymbol>(PrimaryDependency);
            }
 
            return this.OriginalDefinition.GetUseSiteInfo();
        }
 
        internal bool CalculateUseSiteDiagnostic(ref UseSiteInfo<AssemblySymbol> result)
        {
            Debug.Assert(IsDefinition);
 
            // Check type, custom modifiers
            if (DeriveUseSiteInfoFromType(ref result, this.TypeWithAnnotations, RefKind == RefKind.None ? AllowedRequiredModifierType.System_Runtime_CompilerServices_Volatile : AllowedRequiredModifierType.None) ||
                DeriveUseSiteInfoFromCustomModifiers(ref result, this.RefCustomModifiers, AllowedRequiredModifierType.None))
            {
                return true;
            }
 
            // If the member is in an assembly with unified references, 
            // we check if its definition depends on a type from a unified reference.
            if (this.ContainingModule.HasUnifiedReferences)
            {
                HashSet<TypeSymbol> unificationCheckedTypes = null;
                DiagnosticInfo diagnosticInfo = result.DiagnosticInfo;
                if (this.TypeWithAnnotations.GetUnificationUseSiteDiagnosticRecursive(ref diagnosticInfo, this, ref unificationCheckedTypes))
                {
                    result = result.AdjustDiagnosticInfo(diagnosticInfo);
                    return true;
                }
 
                result = result.AdjustDiagnosticInfo(diagnosticInfo);
            }
 
            return false;
        }
 
        /// <summary>
        /// Returns true if the error code is highest priority while calculating use site error for this symbol. 
        /// </summary>
        protected sealed override bool IsHighestPriorityUseSiteErrorCode(int code) => code is (int)ErrorCode.ERR_UnsupportedCompilerFeature or (int)ErrorCode.ERR_BindToBogus;
 
        public sealed override bool HasUnsupportedMetadata
        {
            get
            {
                DiagnosticInfo info = GetUseSiteInfo().DiagnosticInfo;
                return (object)info != null && info.Code is (int)ErrorCode.ERR_BindToBogus or (int)ErrorCode.ERR_UnsupportedCompilerFeature;
            }
        }
 
        #endregion
 
        /// <summary>
        /// Returns True when field symbol is not mapped directly to a field in the underlying tuple struct.
        /// </summary>
        public virtual bool IsVirtualTupleField
        {
            get
            {
                return false;
            }
        }
 
        /// <summary>
        /// Returns true if this is a field representing a Default element like Item1, Item2...
        /// </summary>
        public virtual bool IsDefaultTupleElement
        {
            get
            {
                Debug.Assert(!(this is TupleElementFieldSymbol or TupleErrorFieldSymbol));
                return TupleElementIndex >= 0;
            }
        }
 
        public virtual bool IsExplicitlyNamedTupleElement
        {
            get
            {
                Debug.Assert(!(this is TupleElementFieldSymbol or TupleErrorFieldSymbol));
                return false;
            }
        }
 
        /// <summary>
        /// If this is a field of a tuple type, return corresponding underlying field from the
        /// tuple underlying type. Otherwise, null. In case of a malformed underlying type
        /// the corresponding underlying field might be missing, return null in this case too.
        /// </summary>
        public virtual FieldSymbol TupleUnderlyingField
        {
            get
            {
                Debug.Assert(!(this is TupleElementFieldSymbol));
                return ContainingType.IsTupleType ? this : null;
            }
        }
 
        /// <summary>
        /// If this field represents a tuple element, returns a corresponding default element field.
        /// Otherwise returns null.
        /// </summary>
        public virtual FieldSymbol CorrespondingTupleField
        {
            get
            {
                Debug.Assert(!(this is TupleElementFieldSymbol));
                return TupleElementIndex >= 0 ? this : null;
            }
        }
 
        /// <summary>
        /// Returns true if a given field is a tuple element
        /// </summary>
        internal bool IsTupleElement()
        {
            return this.CorrespondingTupleField is object;
        }
 
        /// <summary>
        /// If this is a field representing a tuple element,
        /// returns the index of the element (zero-based).
        /// Otherwise returns -1
        /// </summary>
        public virtual int TupleElementIndex
        {
            get
            {
                // wrapped tuple fields already have this information and override this property
                Debug.Assert(!(this is TupleElementFieldSymbol or TupleErrorFieldSymbol or Retargeting.RetargetingFieldSymbol));
                if (!ContainingType.IsTupleType)
                {
                    return -1;
                }
 
                if (!ContainingType.IsDefinition)
                {
                    return this.OriginalDefinition.TupleElementIndex;
                }
 
                var tupleElementPosition = NamedTypeSymbol.MatchesCanonicalTupleElementName(Name);
                int arity = ContainingType.Arity;
                if (tupleElementPosition <= 0 || tupleElementPosition > arity)
                {
                    // ex: no "Item2" in 'ValueTuple<T1>'
                    return -1;
                }
                Debug.Assert(tupleElementPosition < NamedTypeSymbol.ValueTupleRestPosition);
 
                WellKnownMember wellKnownMember = NamedTypeSymbol.GetTupleTypeMember(arity, tupleElementPosition);
                MemberDescriptor descriptor = WellKnownMembers.GetDescriptor(wellKnownMember);
                Symbol found = CSharpCompilation.GetRuntimeMember(ImmutableArray.Create<Symbol>(this), descriptor, CSharpCompilation.SpecialMembersSignatureComparer.Instance,
                    accessWithinOpt: null); // force lookup of public members only
 
                return found is not null
                    ? tupleElementPosition - 1
                    : -1;
            }
        }
 
        ISymbolInternal IFieldSymbolInternal.AssociatedSymbol => AssociatedSymbol;
        bool IFieldSymbolInternal.IsVolatile => IsVolatile;
        ITypeSymbolInternal IFieldSymbolInternal.Type => Type;
 
        protected override ISymbol CreateISymbol()
        {
            return new PublicModel.FieldSymbol(this);
        }
 
        public override bool Equals(Symbol other, TypeCompareKind compareKind)
        {
            if (other is SubstitutedFieldSymbol sfs)
            {
                return sfs.Equals(this, compareKind);
            }
 
            return base.Equals(other, compareKind);
        }
 
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }
    }
}