File: Symbols\Tuples\TupleFieldSymbol.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 Roslyn.Utilities;
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Threading;
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
    /// <summary>
    /// A plain TupleElementFieldSymbol (as opposed to a TupleVirtualElementFieldSymbol) represents
    /// an element field of a tuple type (such as (int, byte).Item1) that is backed by a real field
    /// with the same name within the tuple underlying type.
    ///
    /// Note that original tuple fields (like 'System.ValueTuple`2.Item1') do not get wrapped.
    /// </summary>
    internal class TupleElementFieldSymbol : WrappedFieldSymbol
    {
        /// <summary>
        /// If this field represents a tuple element with index X
        ///  2X      if this field represents Default-named element
        ///  2X + 1  if this field represents Friendly-named element
        /// </summary>
        private readonly int _tupleElementIndex;
 
        protected readonly NamedTypeSymbol _containingTuple;
 
        private readonly ImmutableArray<Location> _locations;
        protected readonly FieldSymbol _correspondingDefaultField;
 
        // default tuple elements like Item1 or Item20 could be provided by the user or
        // otherwise implicitly declared by compiler
        private readonly bool _isImplicitlyDeclared;
 
        public TupleElementFieldSymbol(
            NamedTypeSymbol container,
            FieldSymbol underlyingField,
            int tupleElementIndex,
            ImmutableArray<Location> locations,
            bool isImplicitlyDeclared,
            FieldSymbol? correspondingDefaultFieldOpt = null)
            : base(underlyingField)
        {
            Debug.Assert(tupleElementIndex >= 0);
            Debug.Assert(container.Equals(underlyingField.ContainingType, TypeCompareKind.IgnoreDynamicAndTupleNames) || this is TupleVirtualElementFieldSymbol,
                                            "virtual fields should be represented by " + nameof(TupleVirtualElementFieldSymbol));
            Debug.Assert(!(underlyingField is TupleElementFieldSymbol));
 
            // The fields on definition of ValueTuple<...> types don't need to be wrapped
            Debug.Assert(!container.IsDefinition);
 
            Debug.Assert(container.IsTupleType);
            _containingTuple = container;
            _tupleElementIndex = correspondingDefaultFieldOpt is null ? tupleElementIndex << 1 : (tupleElementIndex << 1) + 1;
            Debug.Assert(!locations.IsDefault);
            _locations = locations;
            _isImplicitlyDeclared = isImplicitlyDeclared;
            _correspondingDefaultField = correspondingDefaultFieldOpt ?? this;
        }
 
        /// <summary>
        /// If this is a field representing a tuple element,
        /// returns the index of the element (zero-based).
        /// Otherwise returns -1
        /// </summary>
        public sealed override int TupleElementIndex
            => _tupleElementIndex >> 1;
 
        public sealed override bool IsDefaultTupleElement
        {
            get
            {
                // even
                return (_tupleElementIndex & 1) == 0;
            }
        }
 
        public sealed override bool IsExplicitlyNamedTupleElement
        {
            get
            {
                return !_isImplicitlyDeclared;
            }
        }
 
        public sealed override FieldSymbol TupleUnderlyingField
        {
            get
            {
                return _underlyingField;
            }
        }
 
        public sealed override Symbol? AssociatedSymbol
        {
            get
            {
                return null;
            }
        }
 
        public override FieldSymbol OriginalDefinition
        {
            get
            {
                NamedTypeSymbol originalContainer = ContainingType.OriginalDefinition;
                if (!originalContainer.IsTupleType)
                {
                    return this;
                }
                return originalContainer.GetTupleMemberSymbolForUnderlyingMember(_underlyingField.OriginalDefinition)!;
            }
        }
 
        public sealed override Symbol ContainingSymbol
        {
            get
            {
                return _containingTuple;
            }
        }
 
        public sealed override RefKind RefKind => _underlyingField.RefKind;
 
        public sealed override ImmutableArray<CustomModifier> RefCustomModifiers => _underlyingField.RefCustomModifiers;
 
        internal override TypeWithAnnotations GetFieldType(ConsList<FieldSymbol> fieldsBeingBound)
        {
            return _underlyingField.GetFieldType(fieldsBeingBound);
        }
 
        public override ImmutableArray<CSharpAttributeData> GetAttributes()
        {
            return _underlyingField.GetAttributes();
        }
 
        internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
        {
            return _underlyingField.GetUseSiteInfo();
        }
 
        internal override bool RequiresCompletion => _underlyingField.RequiresCompletion;
 
        internal override bool HasComplete(CompletionPart part) => _underlyingField.HasComplete(part);
 
        internal override void ForceComplete(SourceLocation? locationOpt, Predicate<Symbol>? filter, CancellationToken cancellationToken)
        {
            _underlyingField.ForceComplete(locationOpt, filter, cancellationToken);
        }
 
        public sealed override int GetHashCode()
        {
            return Hash.Combine(_containingTuple.GetHashCode(), _tupleElementIndex.GetHashCode());
        }
 
        public sealed override bool Equals(Symbol obj, TypeCompareKind compareKind)
        {
            var other = obj as TupleElementFieldSymbol;
 
            if ((object?)other == this)
            {
                return true;
            }
 
            // note we have to compare both index and name because
            // in nameless tuple there could be fields that differ only by index
            // and in named tuples there could be fields that differ only by name
            return (object?)other != null &&
                _tupleElementIndex == other._tupleElementIndex &&
                TypeSymbol.Equals(_containingTuple, other._containingTuple, compareKind);
        }
 
        public sealed override ImmutableArray<Location> Locations
        {
            get
            {
                return _locations;
            }
        }
 
        public sealed override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences
        {
            get
            {
                return _isImplicitlyDeclared ?
                    ImmutableArray<SyntaxReference>.Empty :
                    GetDeclaringSyntaxReferenceHelper<CSharpSyntaxNode>(_locations);
            }
        }
 
        public sealed override bool IsImplicitlyDeclared
        {
            get
            {
                return _isImplicitlyDeclared;
            }
        }
 
        public sealed override FieldSymbol CorrespondingTupleField
        {
            get
            {
                return _correspondingDefaultField;
            }
        }
 
        internal override FieldSymbol AsMember(NamedTypeSymbol newOwner)
        {
            Debug.Assert(newOwner.IsTupleType);
 
            NamedTypeSymbol newUnderlyingOwner = GetNewUnderlyingOwner(newOwner);
            return new TupleElementFieldSymbol(newOwner, _underlyingField.OriginalDefinition.AsMember(newUnderlyingOwner), TupleElementIndex, Locations, IsImplicitlyDeclared);
        }
 
        protected NamedTypeSymbol GetNewUnderlyingOwner(NamedTypeSymbol newOwner)
        {
            int currentIndex = TupleElementIndex;
            NamedTypeSymbol newUnderlyingOwner = newOwner;
            while (currentIndex >= NamedTypeSymbol.ValueTupleRestIndex)
            {
                newUnderlyingOwner = (NamedTypeSymbol)newUnderlyingOwner.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[NamedTypeSymbol.ValueTupleRestIndex].Type;
                currentIndex -= NamedTypeSymbol.ValueTupleRestIndex;
            }
 
            return newUnderlyingOwner;
        }
    }
 
    /// <summary>
    /// Represents an element field of a tuple type that is not backed by a real field
    /// with the same name within the tuple type.
    ///
    /// Examples
    ///     // alias to Item1 with a different name
    ///     (int a, byte b).a
    ///
    ///     // not backed directly by the type
    ///     (int i1, int i2, int i3, int i4, int i5, int i6, int i7, int i8).i8
    ///
    ///     // Item8, which is also not backed directly by the type
    ///     (int, int, int, int, int, int, int, int).Item8
    ///
    /// NOTE: For any virtual element, there is a nonvirtual way to access the same underlying field.
    ///       In scenarios where we need to enumerate actual fields of a struct,
    ///       virtual fields should be ignored.
    /// </summary>
    internal sealed class TupleVirtualElementFieldSymbol : TupleElementFieldSymbol
    {
        private readonly string _name;
        private readonly bool _cannotUse; // With LanguageVersion 7, we will produce named elements that should not be used
 
        public TupleVirtualElementFieldSymbol(
            NamedTypeSymbol container,
            FieldSymbol underlyingField,
            string name,
            int tupleElementIndex,
            ImmutableArray<Location> locations,
            bool cannotUse,
            bool isImplicitlyDeclared,
            FieldSymbol? correspondingDefaultFieldOpt)
            : base(container, underlyingField, tupleElementIndex, locations, isImplicitlyDeclared, correspondingDefaultFieldOpt)
        {
            // The underlying field for 'Hanna' (an 8-th named element) in a long tuple is Item1. The corresponding field is Item8.
 
            Debug.Assert(container.IsTupleType);
            Debug.Assert(underlyingField.ContainingType.IsTupleType);
            Debug.Assert(name != null);
            Debug.Assert(name != underlyingField.Name || !container.Equals(underlyingField.ContainingType, TypeCompareKind.IgnoreDynamicAndTupleNames),
                                "fields that map directly to underlying should not be represented by " + nameof(TupleVirtualElementFieldSymbol));
            Debug.Assert((correspondingDefaultFieldOpt is null) == (NamedTypeSymbol.TupleMemberName(tupleElementIndex + 1) == name));
            Debug.Assert(!(correspondingDefaultFieldOpt is TupleErrorFieldSymbol));
 
            _name = name;
            _cannotUse = cannotUse;
        }
 
        internal override UseSiteInfo<AssemblySymbol> GetUseSiteInfo()
        {
            if (_cannotUse)
            {
                return new UseSiteInfo<AssemblySymbol>(new CSDiagnosticInfo(ErrorCode.ERR_TupleInferredNamesNotAvailable, _name,
                    new CSharpRequiredLanguageVersion(MessageID.IDS_FeatureInferredTupleNames.RequiredVersion())));
            }
 
            return base.GetUseSiteInfo();
        }
 
        public override string Name
        {
            get
            {
                return _name;
            }
        }
 
        internal override int? TypeLayoutOffset
        {
            get
            {
                return null;
            }
        }
 
        public override FieldSymbol OriginalDefinition
        {
            get
            {
                return this;
            }
        }
 
        public override bool IsVirtualTupleField
        {
            get
            {
                return true;
            }
        }
 
        internal override TypeWithAnnotations GetFieldType(ConsList<FieldSymbol> fieldsBeingBound)
            => _underlyingField.GetFieldType(fieldsBeingBound);
 
        public override ImmutableArray<CSharpAttributeData> GetAttributes()
        {
            return _underlyingField.GetAttributes();
        }
 
        internal override FieldSymbol AsMember(NamedTypeSymbol newOwner)
        {
            Debug.Assert(newOwner.IsTupleType);
            Debug.Assert(newOwner.TupleElements.Length == this._containingTuple.TupleElements.Length);
 
            NamedTypeSymbol newUnderlyingOwner = GetNewUnderlyingOwner(newOwner);
 
            FieldSymbol? newCorrespondingDefaultFieldOpt = null;
            if ((object)_correspondingDefaultField != this)
            {
                newCorrespondingDefaultFieldOpt = _correspondingDefaultField.OriginalDefinition.AsMember(newOwner);
            }
 
            return new TupleVirtualElementFieldSymbol(newOwner, _underlyingField.OriginalDefinition.AsMember(newUnderlyingOwner), _name, TupleElementIndex, Locations, _cannotUse, IsImplicitlyDeclared, newCorrespondingDefaultFieldOpt);
        }
    }
}