| File: Symbols\FieldSymbol.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.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(); } } }