// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Runtime.InteropServices;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
///
/// Represents a field in a class, struct or enum
///
internal abstract partial class FieldSymbol : Symbol, IFieldSymbol
{
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// 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()
{
}
///
/// 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.
///
public new virtual FieldSymbol OriginalDefinition
{
get
{
return this;
}
}
protected override sealed Symbol OriginalSymbolDefinition
{
get
{
return this.OriginalDefinition;
}
}
///
/// Gets the type of this field.
///
public TypeSymbol Type
{
get
{
return GetFieldType(ConsList.Empty);
}
}
internal abstract TypeSymbol GetFieldType(ConsList fieldsBeingBound);
///
/// Gets the list of custom modifiers, if any, associated with the field.
///
public abstract ImmutableArray CustomModifiers { get; }
///
/// 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.
///
public abstract Symbol AssociatedSymbol { get; }
///
/// Returns true if this field was declared as "readonly".
///
public abstract bool IsReadOnly { get; }
///
/// Returns true if this field was declared as "volatile".
///
public abstract bool IsVolatile { get; }
///
/// 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.
///
public virtual bool IsFixed { get { return false; } }
///
/// If IsFixed is true, the value between brackets in the fixed-size-buffer declaration.
/// If IsFixed 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.
///
public virtual int FixedSize { get { return 0; } }
///
/// If this.IsFixed is true, returns the underlying implementation type for the
/// fixed-size buffer when emitted. Otherwise returns null.
///
internal virtual NamedTypeSymbol FixedImplementationType(PEModuleBuilder emitModule)
{
return null;
}
///
/// Returns true when field is a backing field for a captured frame pointer (typically "this").
///
internal virtual bool IsCapturedFrame { get { return false; } }
///
/// Returns true if this field was declared as "const" (i.e. is a constant declaration).
/// Also returns true for an enum member.
///
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); }
}
///
/// Returns false if the field wasn't declared as "const", or constant value was omitted or erroneous.
/// True otherwise.
///
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
}
}
///
/// If IsConst returns true, then returns the constant value of the field or enum member. If IsConst returns
/// false, then returns null.
///
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);
///
/// Gets the kind of this symbol.
///
public sealed override SymbolKind Kind
{
get
{
return SymbolKind.Field;
}
}
internal override TResult Accept(CSharpSymbolVisitor visitor, TArgument argument)
{
return visitor.VisitField(this, argument);
}
public override void Accept(CSharpSymbolVisitor visitor)
{
visitor.VisitField(this);
}
public override TResult Accept(CSharpSymbolVisitor visitor)
{
return visitor.VisitField(this);
}
///
/// Returns false because field can't be abstract.
///
public sealed override bool IsAbstract
{
get
{
return false;
}
}
///
/// Returns false because field can't be defined externally.
///
public sealed override bool IsExtern
{
get
{
return false;
}
}
///
/// Returns false because field can't be overridden.
///
public sealed override bool IsOverride
{
get
{
return false;
}
}
///
/// Returns false because field can't be sealed.
///
public sealed override bool IsSealed
{
get
{
return false;
}
}
///
/// Returns false because field can't be virtual.
///
public sealed override bool IsVirtual
{
get
{
return false;
}
}
///
/// True if this symbol has a special name (metadata flag SpecialName is set).
///
internal abstract bool HasSpecialName { get; }
///
/// True if this symbol has a runtime-special name (metadata flag RuntimeSpecialName is set).
///
internal abstract bool HasRuntimeSpecialName { get; }
///
/// True if this field is not serialized (metadata flag NotSerialized is set).
///
internal abstract bool IsNotSerialized { get; }
///
/// True if this field has a pointer type.
///
///
/// By default we defer to this.Type.IsPointerType()
/// 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.
///
internal virtual bool HasPointerType
{
get
{
return this.Type.IsPointerType();
}
}
///
/// Describes how the field is marshalled when passed to native code.
/// Null if no specific marshalling information is available for the field.
///
/// PE symbols don't provide this information and always return null.
internal abstract MarshalPseudoCustomAttributeData MarshallingInformation { get; }
///
/// Returns the marshalling type of this field, or 0 if marshalling information isn't available.
///
///
/// By default this information is extracted from 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.
///
internal virtual UnmanagedType MarshallingType
{
get
{
var info = MarshallingInformation;
return info != null ? info.UnmanagedType : 0;
}
}
///
/// Offset assigned to the field when the containing type is laid out by the VM.
/// Null if unspecified.
///
internal abstract int? TypeLayoutOffset { get; }
internal FieldSymbol AsMember(NamedTypeSymbol newOwner)
{
Debug.Assert(this.IsDefinition);
Debug.Assert(ReferenceEquals(newOwner.OriginalDefinition, this.ContainingSymbol.OriginalDefinition));
return (newOwner == this.ContainingSymbol) ? this : new SubstitutedFieldSymbol(newOwner as SubstitutedNamedTypeSymbol, this);
}
#region Use-Site Diagnostics
internal override DiagnosticInfo GetUseSiteDiagnostic()
{
if (this.IsDefinition)
{
return base.GetUseSiteDiagnostic();
}
return this.OriginalDefinition.GetUseSiteDiagnostic();
}
internal bool CalculateUseSiteDiagnostic(ref DiagnosticInfo result)
{
Debug.Assert(IsDefinition);
// Check type, custom modifiers
if (DeriveUseSiteDiagnosticFromType(ref result, this.Type) ||
DeriveUseSiteDiagnosticFromCustomModifiers(ref result, this.CustomModifiers))
{
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 unificationCheckedTypes = null;
if (this.Type.GetUnificationUseSiteDiagnosticRecursive(ref result, this, ref unificationCheckedTypes) ||
GetUnificationUseSiteDiagnosticRecursive(ref result, this.CustomModifiers, this, ref unificationCheckedTypes))
{
return true;
}
}
return false;
}
///
/// Return error code that has highest priority while calculating use site error for this symbol.
///
protected override int HighestPriorityUseSiteError
{
get
{
return (int)ErrorCode.ERR_BindToBogus;
}
}
public sealed override bool HasUnsupportedMetadata
{
get
{
DiagnosticInfo info = GetUseSiteDiagnostic();
return (object)info != null && info.Code == (int)ErrorCode.ERR_BindToBogus;
}
}
#endregion
///
/// Is this a field of a tuple type?
///
public virtual bool IsTupleField
{
get
{
return false;
}
}
///
/// If this is a field of a tuple type, return corresponding underlying field from the
/// tuple underlying type. Otherwise, null. In case of a mulformed underlying type
/// the corresponding underlying field might be missing, return null in this case too.
///
public virtual FieldSymbol TupleUnderlyingField
{
get
{
return null;
}
}
#region IFieldSymbol Members
ISymbol IFieldSymbol.AssociatedSymbol
{
get
{
return this.AssociatedSymbol;
}
}
ITypeSymbol IFieldSymbol.Type
{
get
{
return this.Type;
}
}
ImmutableArray IFieldSymbol.CustomModifiers
{
get { return this.CustomModifiers; }
}
IFieldSymbol IFieldSymbol.OriginalDefinition
{
get { return this.OriginalDefinition; }
}
#endregion
#region ISymbol Members
public override void Accept(SymbolVisitor visitor)
{
visitor.VisitField(this);
}
public override TResult Accept(SymbolVisitor visitor)
{
return visitor.VisitField(this);
}
#endregion
}
}