File: Symbols\Source\SourceMemberContainerSymbol.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 System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using ReferenceEqualityComparer = Roslyn.Utilities.ReferenceEqualityComparer;
 
namespace Microsoft.CodeAnalysis.CSharp.Symbols
{
    /// <summary>
    /// Represents a named type symbol whose members are declared in source.
    /// </summary>
    internal abstract partial class SourceMemberContainerTypeSymbol : NamedTypeSymbol
    {
        // The flags type is used to compact many different bits of information efficiently.
        private struct Flags
        {
            // We current pack everything into one 32-bit int; layout is given below.
            //
            // |            p|ss|vvv|zzzz|f|d|yy|wwwwww|
            //
            // w = special type.  6 bits.
            // y = IsManagedType.  2 bits.
            // d = FieldDefinitionsNoted. 1 bit.
            // f = FlattenedMembersIsSorted.  1 bit.
            // z = TypeKind. 4 bits.
            // v = NullableContext. 3 bits.
            // s = DeclaredRequiredMembers. 2 bits
            // p = HasPrimaryConstructor. 1 bit.
            private int _flags;
 
            private const int SpecialTypeOffset = 0;
            private const int SpecialTypeSize = 6;
 
            private const int ManagedKindOffset = SpecialTypeOffset + SpecialTypeSize;
            private const int ManagedKindSize = 2;
 
            private const int FieldDefinitionsNotedOffset = ManagedKindOffset + ManagedKindSize;
            private const int FieldDefinitionsNotedSize = 1;
 
            private const int FlattenedMembersIsSortedOffset = FieldDefinitionsNotedOffset + FieldDefinitionsNotedSize;
            private const int FlattenedMembersIsSortedSize = 1;
 
            private const int TypeKindOffset = FlattenedMembersIsSortedOffset + FlattenedMembersIsSortedSize;
            private const int TypeKindSize = 4;
 
            private const int NullableContextOffset = TypeKindOffset + TypeKindSize;
            private const int NullableContextSize = 3;
 
            private const int HasDeclaredRequiredMembersOffset = NullableContextOffset + NullableContextSize;
            private const int HasDeclaredRequiredMembersSize = 2;
 
            private const int HasPrimaryConstructorOffset = HasDeclaredRequiredMembersOffset + HasDeclaredRequiredMembersSize;
            // private const int HasPrimaryConstructorSize = 1;
 
            private const int SpecialTypeMask = (1 << SpecialTypeSize) - 1;
            private const int ManagedKindMask = (1 << ManagedKindSize) - 1;
            private const int TypeKindMask = (1 << TypeKindSize) - 1;
            private const int NullableContextMask = (1 << NullableContextSize) - 1;
 
            private const int FieldDefinitionsNotedBit = 1 << FieldDefinitionsNotedOffset;
            private const int FlattenedMembersIsSortedBit = 1 << FlattenedMembersIsSortedOffset;
 
            private const int HasDeclaredMembersBit = (1 << HasDeclaredRequiredMembersOffset);
            private const int HasDeclaredMembersBitSet = (1 << (HasDeclaredRequiredMembersOffset + 1));
 
            private const int HasPrimaryConstructorBit = 1 << HasPrimaryConstructorOffset;
 
            public ExtendedSpecialType ExtendedSpecialType
            {
                get { return (ExtendedSpecialType)((_flags >> SpecialTypeOffset) & SpecialTypeMask); }
            }
 
            public ManagedKind ManagedKind
            {
                get { return (ManagedKind)((_flags >> ManagedKindOffset) & ManagedKindMask); }
            }
 
            public bool FieldDefinitionsNoted
            {
                get { return (_flags & FieldDefinitionsNotedBit) != 0; }
            }
 
            // True if "lazyMembersFlattened" is sorted.
            public bool FlattenedMembersIsSorted
            {
                get { return (_flags & FlattenedMembersIsSortedBit) != 0; }
            }
 
            public TypeKind TypeKind
            {
                get { return (TypeKind)((_flags >> TypeKindOffset) & TypeKindMask); }
            }
 
#if DEBUG
            static Flags()
            {
                // Verify masks are sufficient for values.
                _ = new int[SpecialTypeMask - (int)InternalSpecialType.NextAvailable + 1];
                Debug.Assert(EnumUtilities.ContainsAllValues<SpecialType>(SpecialTypeMask));
                Debug.Assert(EnumUtilities.ContainsAllValues<InternalSpecialType>(SpecialTypeMask)); //This assert might false fail in the future, we don't really need to be able to represent NextAvailable
                Debug.Assert(EnumUtilities.ContainsAllValues<NullableContextKind>(NullableContextMask));
            }
#endif
 
            public Flags(ExtendedSpecialType specialType, TypeKind typeKind, bool hasPrimaryConstructor)
            {
                int specialTypeInt = ((int)specialType & SpecialTypeMask) << SpecialTypeOffset;
                int typeKindInt = ((int)typeKind & TypeKindMask) << TypeKindOffset;
                int hasPrimaryConstructorInt = hasPrimaryConstructor ? HasPrimaryConstructorBit : 0;
 
                _flags = specialTypeInt | typeKindInt | hasPrimaryConstructorInt;
            }
 
            public void SetFieldDefinitionsNoted()
            {
                ThreadSafeFlagOperations.Set(ref _flags, FieldDefinitionsNotedBit);
            }
 
            public void SetFlattenedMembersIsSorted()
            {
                ThreadSafeFlagOperations.Set(ref _flags, (FlattenedMembersIsSortedBit));
            }
 
            private static bool BitsAreUnsetOrSame(int bits, int mask)
            {
                return (bits & mask) == 0 || (bits & mask) == mask;
            }
 
            public void SetManagedKind(ManagedKind managedKind)
            {
                int bitsToSet = ((int)managedKind & ManagedKindMask) << ManagedKindOffset;
                Debug.Assert(BitsAreUnsetOrSame(_flags, bitsToSet));
                ThreadSafeFlagOperations.Set(ref _flags, bitsToSet);
            }
 
            public bool TryGetNullableContext(out byte? value)
            {
                return ((NullableContextKind)((_flags >> NullableContextOffset) & NullableContextMask)).TryGetByte(out value);
            }
 
            public bool SetNullableContext(byte? value)
            {
                return ThreadSafeFlagOperations.Set(ref _flags, (((int)value.ToNullableContextFlags() & NullableContextMask) << NullableContextOffset));
            }
 
            public bool TryGetHasDeclaredRequiredMembers(out bool value)
            {
                if ((_flags & (HasDeclaredMembersBitSet)) != 0)
                {
                    value = (_flags & HasDeclaredMembersBit) != 0;
                    return true;
                }
                else
                {
                    value = false;
                    return false;
                }
            }
 
            public bool SetHasDeclaredRequiredMembers(bool value)
            {
                return ThreadSafeFlagOperations.Set(ref _flags, HasDeclaredMembersBitSet | (value ? HasDeclaredMembersBit : 0));
            }
 
            public readonly bool HasPrimaryConstructor => (_flags & HasPrimaryConstructorBit) != 0;
        }
 
        private static readonly ObjectPool<PooledDictionary<Symbol, Symbol>> s_duplicateRecordMemberSignatureDictionary =
            PooledDictionary<Symbol, Symbol>.CreatePool(MemberSignatureComparer.RecordAPISignatureComparer);
 
        protected SymbolCompletionState state;
 
        private Flags _flags;
        private ImmutableArray<DiagnosticInfo> _managedKindUseSiteDiagnostics;
        private ImmutableArray<AssemblySymbol> _managedKindUseSiteDependencies;
 
        private readonly DeclarationModifiers _declModifiers;
        private readonly NamespaceOrTypeSymbol _containingSymbol;
        protected readonly MergedTypeDeclaration declaration;
 
        // The entry point symbol (resulting from top-level statements) is needed to construct non-type members because
        // it contributes to their binders, so we have to compute it first.
        // The value changes from "default" to "real value". The transition from "default" can only happen once.
        private ImmutableArray<SynthesizedSimpleProgramEntryPointSymbol> _lazySimpleProgramEntryPoints;
 
        // To compute explicitly declared members, binding must be limited (to avoid race conditions where binder cache captures symbols that aren't part of the final set)
        // The value changes from "uninitialized" to "real value" to null. The transition from "uninitialized" can only happen once.
        private DeclaredMembersAndInitializers? _lazyDeclaredMembersAndInitializers = DeclaredMembersAndInitializers.UninitializedSentinel;
 
        private MembersAndInitializers? _lazyMembersAndInitializers;
        private Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>>? _lazyMembersDictionary;
        private Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>>? _lazyEarlyAttributeDecodingMembersDictionary;
 
        private static readonly Dictionary<ReadOnlyMemory<char>, ImmutableArray<NamedTypeSymbol>> s_emptyTypeMembers =
            new Dictionary<ReadOnlyMemory<char>, ImmutableArray<NamedTypeSymbol>>(EmptyReadOnlyMemoryOfCharComparer.Instance);
 
        private Dictionary<ReadOnlyMemory<char>, ImmutableArray<NamedTypeSymbol>>? _lazyTypeMembers;
        private ImmutableArray<Symbol> _lazyMembersFlattened;
        private SynthesizedExplicitImplementations? _lazySynthesizedExplicitImplementations;
        private int _lazyKnownCircularStruct;
        private LexicalSortKey _lazyLexicalSortKey = LexicalSortKey.NotInitialized;
 
        private ThreeState _lazyContainsExtensionMethods;
        private ThreeState _lazyAnyMemberHasAttributes;
 
        #region Construction
 
        internal SourceMemberContainerTypeSymbol(
            NamespaceOrTypeSymbol containingSymbol,
            MergedTypeDeclaration declaration,
            BindingDiagnosticBag diagnostics,
            TupleExtraData? tupleData = null)
            : base(tupleData)
        {
            // If we're dealing with a simple program, then we must be in the global namespace
            Debug.Assert(containingSymbol is NamespaceSymbol { IsGlobalNamespace: true } || !declaration.Declarations.Any(static d => d.IsSimpleProgram));
 
            _containingSymbol = containingSymbol;
            this.declaration = declaration;
 
            TypeKind typeKind = declaration.Kind.ToTypeKind();
            var modifiers = MakeModifiers(typeKind, diagnostics);
 
            foreach (var singleDeclaration in declaration.Declarations)
            {
                diagnostics.AddRange(singleDeclaration.Diagnostics);
            }
 
            int access = (int)(modifiers & DeclarationModifiers.AccessibilityMask);
            if ((access & (access - 1)) != 0)
            {   // more than one access modifier
                if ((modifiers & DeclarationModifiers.Partial) != 0)
                    diagnostics.Add(ErrorCode.ERR_PartialModifierConflict, GetFirstLocation(), this);
                access = access & ~(access - 1); // narrow down to one access modifier
                modifiers &= ~DeclarationModifiers.AccessibilityMask; // remove them all
                modifiers |= (DeclarationModifiers)access; // except the one
            }
            _declModifiers = modifiers;
 
            var specialType = access == (int)DeclarationModifiers.Public
                ? MakeExtendedSpecialType()
                : default;
 
            _flags = new Flags(specialType, typeKind, declaration.HasPrimaryConstructor);
            Debug.Assert(typeKind is TypeKind.Struct or TypeKind.Class || !HasPrimaryConstructor);
 
            var containingType = this.ContainingType;
            if (containingType?.IsSealed == true && this.DeclaredAccessibility.HasProtected())
            {
                diagnostics.Add(AccessCheck.GetProtectedMemberInSealedTypeError(ContainingType), GetFirstLocation(), this);
            }
 
            state.NotePartComplete(CompletionPart.TypeArguments); // type arguments need not be computed separately
        }
 
        private ExtendedSpecialType MakeExtendedSpecialType()
        {
            // check if this is one of the COR library types
            if (ContainingSymbol.Kind == SymbolKind.Namespace &&
                ContainingSymbol.ContainingAssembly.KeepLookingForDeclaredSpecialTypes)
            {
                //for a namespace, the emitted name is a dot-separated list of containing namespaces
                var emittedName = ContainingSymbol.ToDisplayString(SymbolDisplayFormat.QualifiedNameOnlyFormat);
                emittedName = MetadataHelpers.BuildQualifiedName(emittedName, MetadataName);
 
                return SpecialTypes.GetTypeFromMetadataName(emittedName);
            }
            else
            {
                return default;
            }
        }
 
        private DeclarationModifiers MakeModifiers(TypeKind typeKind, BindingDiagnosticBag diagnostics)
        {
            Symbol containingSymbol = this.ContainingSymbol;
            DeclarationModifiers defaultAccess;
 
            // note: we give a specific diagnostic when a file-local type is nested
            var allowedModifiers = DeclarationModifiers.AccessibilityMask | DeclarationModifiers.File;
 
            if (containingSymbol.Kind == SymbolKind.Namespace)
            {
                defaultAccess = DeclarationModifiers.Internal;
            }
            else
            {
                allowedModifiers |= DeclarationModifiers.New;
 
                if (((NamedTypeSymbol)containingSymbol).IsInterface)
                {
                    defaultAccess = DeclarationModifiers.Public;
                }
                else
                {
                    defaultAccess = DeclarationModifiers.Private;
                }
            }
 
            switch (typeKind)
            {
                case TypeKind.Class:
                case TypeKind.Submission:
                    allowedModifiers |= DeclarationModifiers.Partial | DeclarationModifiers.Sealed | DeclarationModifiers.Abstract
                        | DeclarationModifiers.Unsafe;
 
                    if (!this.IsRecord)
                    {
                        allowedModifiers |= DeclarationModifiers.Static;
                    }
 
                    break;
                case TypeKind.Struct:
                    allowedModifiers |= DeclarationModifiers.Partial | DeclarationModifiers.ReadOnly | DeclarationModifiers.Unsafe;
 
                    if (!this.IsRecordStruct)
                    {
                        allowedModifiers |= DeclarationModifiers.Ref;
                    }
 
                    break;
                case TypeKind.Interface:
                    allowedModifiers |= DeclarationModifiers.Partial | DeclarationModifiers.Unsafe;
                    break;
                case TypeKind.Delegate:
                    allowedModifiers |= DeclarationModifiers.Unsafe;
                    break;
            }
 
            bool modifierErrors;
            var mods = MakeAndCheckTypeModifiers(
                defaultAccess,
                allowedModifiers,
                diagnostics,
                out modifierErrors);
 
            this.CheckUnsafeModifier(mods, diagnostics);
 
            if (!modifierErrors &&
                (mods & DeclarationModifiers.Abstract) != 0 &&
                (mods & (DeclarationModifiers.Sealed | DeclarationModifiers.Static)) != 0)
            {
                diagnostics.Add(ErrorCode.ERR_AbstractSealedStatic, GetFirstLocation(), this);
            }
 
            if (!modifierErrors &&
                (mods & (DeclarationModifiers.Sealed | DeclarationModifiers.Static)) == (DeclarationModifiers.Sealed | DeclarationModifiers.Static))
            {
                diagnostics.Add(ErrorCode.ERR_SealedStaticClass, GetFirstLocation(), this);
            }
 
            switch (typeKind)
            {
                case TypeKind.Interface:
                    mods |= DeclarationModifiers.Abstract;
                    break;
                case TypeKind.Struct:
                case TypeKind.Enum:
                    mods |= DeclarationModifiers.Sealed;
                    break;
                case TypeKind.Delegate:
                    mods |= DeclarationModifiers.Sealed;
                    break;
            }
 
            return mods;
        }
 
        private DeclarationModifiers MakeAndCheckTypeModifiers(
            DeclarationModifiers defaultAccess,
            DeclarationModifiers allowedModifiers,
            BindingDiagnosticBag diagnostics,
            out bool modifierErrors)
        {
            modifierErrors = false;
 
            var result = DeclarationModifiers.Unset;
            var partCount = declaration.Declarations.Length;
            var missingPartial = false;
 
            for (var i = 0; i < partCount; i++)
            {
                var decl = declaration.Declarations[i];
                var mods = decl.Modifiers;
 
                if (partCount > 1 && (mods & DeclarationModifiers.Partial) == 0)
                {
                    missingPartial = true;
                }
 
                if (!modifierErrors)
                {
                    mods = ModifierUtils.CheckModifiers(
                        isForTypeDeclaration: true, isForInterfaceMember: false,
                        mods, allowedModifiers, declaration.Declarations[i].NameLocation, diagnostics,
                        modifierTokens: null, modifierErrors: out modifierErrors);
 
                    // It is an error for the same modifier to appear multiple times.
                    if (!modifierErrors)
                    {
                        modifierErrors = ModifierUtils.CheckAccessibility(mods, this, isExplicitInterfaceImplementation: false, diagnostics, this.GetFirstLocation());
                    }
                }
 
                if (result == DeclarationModifiers.Unset)
                {
                    result = mods;
                }
                else
                {
                    result |= mods;
                }
 
            }
 
            if ((result & DeclarationModifiers.AccessibilityMask) == 0)
            {
                result |= defaultAccess;
            }
            else if ((result & DeclarationModifiers.File) != 0)
            {
                diagnostics.Add(ErrorCode.ERR_FileTypeNoExplicitAccessibility, GetFirstLocation(), this);
            }
 
            if (missingPartial)
            {
                if ((result & DeclarationModifiers.Partial) == 0)
                {
                    // duplicate definitions
                    switch (this.ContainingSymbol.Kind)
                    {
                        case SymbolKind.Namespace:
                            for (var i = 1; i < partCount; i++)
                            {
                                // note: a declaration with the 'file' modifier will only be grouped with declarations in the same file.
                                diagnostics.Add((result & DeclarationModifiers.File) != 0
                                    ? ErrorCode.ERR_FileLocalDuplicateNameInNS
                                    : ErrorCode.ERR_DuplicateNameInNS, declaration.Declarations[i].NameLocation, this.Name, this.ContainingSymbol);
                                modifierErrors = true;
                            }
                            break;
 
                        case SymbolKind.NamedType:
                            for (var i = 1; i < partCount; i++)
                            {
                                if (ContainingType!.Locations.Length == 1 || ContainingType.IsPartial())
                                    diagnostics.Add(ErrorCode.ERR_DuplicateNameInClass, declaration.Declarations[i].NameLocation, this.ContainingSymbol, this.Name);
                                modifierErrors = true;
                            }
                            break;
                    }
                }
                else
                {
                    for (var i = 0; i < partCount; i++)
                    {
                        var singleDeclaration = declaration.Declarations[i];
                        var mods = singleDeclaration.Modifiers;
                        if ((mods & DeclarationModifiers.Partial) == 0)
                        {
                            diagnostics.Add(ErrorCode.ERR_MissingPartial, singleDeclaration.NameLocation, this.Name);
                            modifierErrors = true;
                        }
                    }
                }
            }
 
            return result;
        }
 
        internal static bool IsReservedTypeName(string? name)
        {
            return name is { Length: > 0 } && name.All(c => c >= 'a' && c <= 'z');
        }
 
        internal static void ReportReservedTypeName(string? name, CSharpCompilation compilation, DiagnosticBag? diagnostics, Location location)
        {
            if (diagnostics is null)
            {
                return;
            }
 
            if (reportIfContextual(SyntaxKind.RecordKeyword, MessageID.IDS_FeatureRecords, ErrorCode.WRN_RecordNamedDisallowed)
                || reportIfContextual(SyntaxKind.RequiredKeyword, MessageID.IDS_FeatureRequiredMembers, ErrorCode.ERR_RequiredNameDisallowed)
                || reportIfContextual(SyntaxKind.FileKeyword, MessageID.IDS_FeatureFileTypes, ErrorCode.ERR_FileTypeNameDisallowed)
                || reportIfContextual(SyntaxKind.ScopedKeyword, MessageID.IDS_FeatureRefFields, ErrorCode.ERR_ScopedTypeNameDisallowed))
            {
                return;
            }
            else if (IsReservedTypeName(name))
            {
                diagnostics.Add(ErrorCode.WRN_LowerCaseTypeName, location, name);
            }
 
            bool reportIfContextual(SyntaxKind contextualKind, MessageID featureId, ErrorCode error)
            {
                if (name == SyntaxFacts.GetText(contextualKind) && compilation.LanguageVersion >= featureId.RequiredVersion())
                {
                    diagnostics.Add(error, location);
                    return true;
                }
 
                return false;
            }
        }
        #endregion
 
        #region Completion
 
        internal sealed override bool RequiresCompletion
        {
            get { return true; }
        }
 
        internal sealed override bool HasComplete(CompletionPart part)
        {
            return state.HasComplete(part);
        }
 
        protected abstract void CheckBase(BindingDiagnosticBag diagnostics);
        protected abstract void CheckInterfaces(BindingDiagnosticBag diagnostics);
 
        internal override void ForceComplete(SourceLocation? locationOpt, Predicate<Symbol>? filter, CancellationToken cancellationToken)
        {
            if (filter?.Invoke(this) == false)
            {
                return;
            }
 
            while (true)
            {
                // NOTE: cases that depend on GetMembers[ByName] should call RequireCompletionPartMembers.
                cancellationToken.ThrowIfCancellationRequested();
                var incompletePart = state.NextIncompletePart;
                switch (incompletePart)
                {
                    case CompletionPart.Attributes:
                        GetAttributes();
                        break;
 
                    case CompletionPart.StartBaseType:
                    case CompletionPart.FinishBaseType:
                        if (state.NotePartComplete(CompletionPart.StartBaseType))
                        {
                            var diagnostics = BindingDiagnosticBag.GetInstance();
                            CheckBase(diagnostics);
                            AddDeclarationDiagnostics(diagnostics);
                            state.NotePartComplete(CompletionPart.FinishBaseType);
                            diagnostics.Free();
                        }
                        break;
 
                    case CompletionPart.StartInterfaces:
                    case CompletionPart.FinishInterfaces:
                        if (state.NotePartComplete(CompletionPart.StartInterfaces))
                        {
                            var diagnostics = BindingDiagnosticBag.GetInstance();
                            CheckInterfaces(diagnostics);
                            AddDeclarationDiagnostics(diagnostics);
                            state.NotePartComplete(CompletionPart.FinishInterfaces);
                            diagnostics.Free();
                        }
                        break;
 
                    case CompletionPart.EnumUnderlyingType:
                        var discarded = this.EnumUnderlyingType;
                        break;
 
                    case CompletionPart.TypeArguments:
                        {
                            var tmp = this.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics; // force type arguments
                        }
                        break;
 
                    case CompletionPart.TypeParameters:
                        // force type parameters
                        foreach (var typeParameter in this.TypeParameters)
                        {
                            // We can't filter out type parameters: if this container was requested, then all its type parameters need to be compiled
                            typeParameter.ForceComplete(locationOpt, filter: null, cancellationToken);
                        }
 
                        state.NotePartComplete(CompletionPart.TypeParameters);
                        break;
 
                    case CompletionPart.Members:
                        this.GetMembersByName();
                        break;
 
                    case CompletionPart.TypeMembers:
                        this.GetTypeMembersUnordered();
                        break;
 
                    case CompletionPart.SynthesizedExplicitImplementations:
                        this.GetSynthesizedExplicitImplementations(cancellationToken); //force interface and base class errors to be checked
                        break;
 
                    case CompletionPart.StartMemberChecks:
                    case CompletionPart.FinishMemberChecks:
                        if (state.NotePartComplete(CompletionPart.StartMemberChecks))
                        {
                            var diagnostics = BindingDiagnosticBag.GetInstance();
                            AfterMembersChecks(diagnostics);
                            AddDeclarationDiagnostics(diagnostics);
 
                            // We may produce a SymbolDeclaredEvent for the enclosing type before events for its contained members
                            DeclaringCompilation.SymbolDeclaredEvent(this);
                            var thisThreadCompleted = state.NotePartComplete(CompletionPart.FinishMemberChecks);
                            Debug.Assert(thisThreadCompleted);
                            diagnostics.Free();
                        }
                        break;
 
                    case CompletionPart.MembersCompletedChecksStarted:
                    case CompletionPart.MembersCompleted:
                        {
                            ImmutableArray<Symbol> members = this.GetMembersUnordered();
 
                            bool allCompleted = true;
 
                            if (locationOpt == null && filter == null)
                            {
                                foreach (var member in members)
                                {
                                    cancellationToken.ThrowIfCancellationRequested();
                                    member.ForceComplete(locationOpt, filter: null, cancellationToken);
                                }
                            }
                            else
                            {
                                foreach (var member in members)
                                {
                                    ForceCompleteMemberConditionally(locationOpt, filter, member, cancellationToken);
                                    allCompleted = allCompleted && member.HasComplete(CompletionPart.All);
                                }
                            }
 
                            if (!allCompleted)
                            {
                                // We did not complete all members so we won't have enough information for
                                // the PointedAtManagedTypeChecks, so just kick out now.
                                var allParts = CompletionPart.NamedTypeSymbolWithLocationAll;
                                state.SpinWaitComplete(allParts, cancellationToken);
                                return;
                            }
 
                            EnsureFieldDefinitionsNoted();
                            cancellationToken.ThrowIfCancellationRequested();
 
                            if (state.NotePartComplete(CompletionPart.MembersCompletedChecksStarted))
                            {
                                var diagnostics = BindingDiagnosticBag.GetInstance();
                                AfterMembersCompletedChecks(diagnostics);
                                AddDeclarationDiagnostics(diagnostics);
 
                                // We've completed all members, so we're ready for the PointedAtManagedTypeChecks;
                                // proceed to the next iteration.
                                var thisThreadCompleted = state.NotePartComplete(CompletionPart.MembersCompleted);
                                Debug.Assert(thisThreadCompleted);
                                diagnostics.Free();
                            }
                        }
                        break;
 
                    case CompletionPart.None:
                        return;
 
                    default:
                        // This assert will trigger if we forgot to handle any of the completion parts
                        Debug.Assert((incompletePart & CompletionPart.NamedTypeSymbolAll) == 0);
                        // any other values are completion parts intended for other kinds of symbols
                        state.NotePartComplete(CompletionPart.All & ~CompletionPart.NamedTypeSymbolAll);
                        break;
                }
 
                state.SpinWaitComplete(incompletePart, cancellationToken);
            }
 
            throw ExceptionUtilities.Unreachable();
        }
 
        internal void EnsureFieldDefinitionsNoted()
        {
            if (_flags.FieldDefinitionsNoted)
            {
                return;
            }
 
            NoteFieldDefinitions();
        }
 
        private void NoteFieldDefinitions()
        {
            // we must note all fields once therefore we need to lock
            var membersAndInitializers = this.GetMembersAndInitializers();
            lock (membersAndInitializers)
            {
                if (!_flags.FieldDefinitionsNoted)
                {
                    var assembly = (SourceAssemblySymbol)ContainingAssembly;
 
                    Accessibility containerEffectiveAccessibility = EffectiveAccessibility();
 
                    foreach (var member in membersAndInitializers.NonTypeMembers)
                    {
                        FieldSymbol field;
                        if (!member.IsFieldOrFieldLikeEvent(out field) || field.IsConst || field.IsFixedSizeBuffer)
                        {
                            continue;
                        }
 
                        Accessibility fieldDeclaredAccessibility = field.DeclaredAccessibility;
                        if (fieldDeclaredAccessibility == Accessibility.Private)
                        {
                            // mark private fields as tentatively unassigned and unread unless we discover otherwise.
                            assembly.NoteFieldDefinition(field, isInternal: false, isUnread: true);
                        }
                        else if (containerEffectiveAccessibility == Accessibility.Private)
                        {
                            // mark effectively private fields as tentatively unassigned unless we discover otherwise.
                            assembly.NoteFieldDefinition(field, isInternal: false, isUnread: false);
                        }
                        else if (fieldDeclaredAccessibility == Accessibility.Internal || containerEffectiveAccessibility == Accessibility.Internal)
                        {
                            // mark effectively internal fields as tentatively unassigned unless we discover otherwise.
                            // NOTE: These fields will be reported as unassigned only if internals are not visible from this assembly.
                            // See property SourceAssemblySymbol.UnusedFieldWarnings.
                            assembly.NoteFieldDefinition(field, isInternal: true, isUnread: false);
                        }
                    }
                    _flags.SetFieldDefinitionsNoted();
                }
            }
        }
 
        #endregion
 
        #region Containers
 
        public sealed override NamedTypeSymbol? ContainingType
        {
            get
            {
                return _containingSymbol as NamedTypeSymbol;
            }
        }
 
        public sealed override Symbol ContainingSymbol
        {
            get
            {
                return _containingSymbol;
            }
        }
 
        #endregion
 
        #region Flags Encoded Properties
 
        public override ExtendedSpecialType ExtendedSpecialType
        {
            get
            {
                return _flags.ExtendedSpecialType;
            }
        }
 
        public override TypeKind TypeKind
        {
            get
            {
                return _flags.TypeKind;
            }
        }
 
        internal MergedTypeDeclaration MergedDeclaration
        {
            get
            {
                return this.declaration;
            }
        }
 
        internal sealed override bool IsInterface
        {
            get
            {
                // TypeKind is computed eagerly, so this is cheap.
                return this.TypeKind == TypeKind.Interface;
            }
        }
 
        internal override ManagedKind GetManagedKind(ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
        {
            var managedKind = _flags.ManagedKind;
            if (managedKind == ManagedKind.Unknown)
            {
                var managedKindUseSiteInfo = new CompoundUseSiteInfo<AssemblySymbol>(ContainingAssembly);
                managedKind = base.GetManagedKind(ref managedKindUseSiteInfo);
                ImmutableInterlocked.InterlockedInitialize(ref _managedKindUseSiteDiagnostics, managedKindUseSiteInfo.Diagnostics?.ToImmutableArray() ?? ImmutableArray<DiagnosticInfo>.Empty);
                ImmutableInterlocked.InterlockedInitialize(ref _managedKindUseSiteDependencies, managedKindUseSiteInfo.Dependencies?.ToImmutableArray() ?? ImmutableArray<AssemblySymbol>.Empty);
                _flags.SetManagedKind(managedKind);
            }
 
            if (useSiteInfo.AccumulatesDiagnostics)
            {
                ImmutableArray<DiagnosticInfo> useSiteDiagnostics = _managedKindUseSiteDiagnostics;
                // Ensure we have the latest value from the field
                useSiteDiagnostics = ImmutableInterlocked.InterlockedCompareExchange(ref _managedKindUseSiteDiagnostics, useSiteDiagnostics, useSiteDiagnostics);
                Debug.Assert(!useSiteDiagnostics.IsDefault);
                useSiteInfo.AddDiagnostics(useSiteDiagnostics);
            }
 
            if (useSiteInfo.AccumulatesDependencies)
            {
                ImmutableArray<AssemblySymbol> useSiteDependencies = _managedKindUseSiteDependencies;
                // Ensure we have the latest value from the field
                useSiteDependencies = ImmutableInterlocked.InterlockedCompareExchange(ref _managedKindUseSiteDependencies, useSiteDependencies, useSiteDependencies);
                Debug.Assert(!useSiteDependencies.IsDefault);
                useSiteInfo.AddDependencies(useSiteDependencies);
            }
 
            return managedKind;
        }
 
        public override bool IsStatic => HasFlag(DeclarationModifiers.Static);
 
        public sealed override bool IsRefLikeType => HasFlag(DeclarationModifiers.Ref);
 
        public override bool IsReadOnly => HasFlag(DeclarationModifiers.ReadOnly);
 
        public override bool IsSealed => HasFlag(DeclarationModifiers.Sealed);
 
        public override bool IsAbstract => HasFlag(DeclarationModifiers.Abstract);
 
        internal bool IsPartial => HasFlag(DeclarationModifiers.Partial);
 
        internal bool IsNew => HasFlag(DeclarationModifiers.New);
 
        internal sealed override bool IsFileLocal => HasFlag(DeclarationModifiers.File);
 
        internal bool IsUnsafe => HasFlag(DeclarationModifiers.Unsafe);
 
        /// <summary>
        /// If this type is file-local, the syntax tree in which the type is declared. Otherwise, null.
        /// </summary>
        private SyntaxTree? AssociatedSyntaxTree => IsFileLocal ? declaration.Declarations[0].Location.SourceTree : null;
 
        internal sealed override FileIdentifier? AssociatedFileIdentifier
        {
            get
            {
                if (AssociatedSyntaxTree is not SyntaxTree syntaxTree)
                {
                    return null;
                }
 
                return FileIdentifier.Create(syntaxTree, DeclaringCompilation?.Options?.SourceReferenceResolver);
            }
        }
 
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private bool HasFlag(DeclarationModifiers flag) => (_declModifiers & flag) != 0;
 
        public override Accessibility DeclaredAccessibility
        {
            get
            {
                return ModifierUtils.EffectiveAccessibility(_declModifiers);
            }
        }
 
        /// <summary>
        /// Compute the "effective accessibility" of the current class for the purpose of warnings about unused fields.
        /// </summary>
        private Accessibility EffectiveAccessibility()
        {
            var result = DeclaredAccessibility;
            if (result == Accessibility.Private) return Accessibility.Private;
            for (Symbol? container = this.ContainingType; !(container is null); container = container.ContainingType)
            {
                switch (container.DeclaredAccessibility)
                {
                    case Accessibility.Private:
                        return Accessibility.Private;
                    case Accessibility.Internal:
                        result = Accessibility.Internal;
                        continue;
                }
            }
 
            return result;
        }
 
        #endregion
 
        #region Syntax
 
        public override bool IsScriptClass
        {
            get
            {
                var kind = this.declaration.Declarations[0].Kind;
                return kind == DeclarationKind.Script || kind == DeclarationKind.Submission;
            }
        }
 
        public override bool IsImplicitClass
        {
            get
            {
                return this.declaration.Declarations[0].Kind == DeclarationKind.ImplicitClass;
            }
        }
 
        internal override bool IsRecord
        {
            get
            {
                return this.declaration.Declarations[0].Kind == DeclarationKind.Record;
            }
        }
 
        internal override bool IsRecordStruct
        {
            get
            {
                return this.declaration.Declarations[0].Kind == DeclarationKind.RecordStruct;
            }
        }
 
        public override bool IsImplicitlyDeclared
        {
            get
            {
                return IsImplicitClass || IsScriptClass;
            }
        }
 
        public override int Arity
        {
            get
            {
                return declaration.Arity;
            }
        }
 
        public override string Name
        {
            get
            {
                return declaration.Name;
            }
        }
 
        internal override bool MangleName
        {
            get
            {
                return Arity > 0;
            }
        }
 
        internal override LexicalSortKey GetLexicalSortKey()
        {
            if (!_lazyLexicalSortKey.IsInitialized)
            {
                _lazyLexicalSortKey.SetFrom(declaration.GetLexicalSortKey(this.DeclaringCompilation));
            }
            return _lazyLexicalSortKey;
        }
 
        public sealed override ImmutableArray<Location> Locations
            => ImmutableArray<Location>.CastUp(declaration.NameLocations.ToImmutable());
 
        public override Location TryGetFirstLocation()
            => declaration.Declarations[0].NameLocation;
 
        public ImmutableArray<SyntaxReference> SyntaxReferences
        {
            get
            {
                return this.declaration.SyntaxReferences;
            }
        }
 
        public override ImmutableArray<SyntaxReference> DeclaringSyntaxReferences
        {
            get
            {
                return SyntaxReferences;
            }
        }
 
        // This method behaves the same was as the base class, but avoids allocations associated with DeclaringSyntaxReferences
        public override bool IsDefinedInSourceTree(SyntaxTree tree, TextSpan? definedWithinSpan, CancellationToken cancellationToken)
        {
            var declarations = declaration.Declarations;
            if (IsImplicitlyDeclared && declarations.IsEmpty)
            {
                return ContainingSymbol.IsDefinedInSourceTree(tree, definedWithinSpan, cancellationToken);
            }
 
            foreach (var declaration in declarations)
            {
                cancellationToken.ThrowIfCancellationRequested();
 
                var syntaxRef = declaration.SyntaxReference;
                if (syntaxRef.SyntaxTree == tree &&
                    (!definedWithinSpan.HasValue || syntaxRef.Span.IntersectsWith(definedWithinSpan.Value)))
                {
                    return true;
                }
            }
 
            return false;
        }
 
        #endregion
 
        #region Members
 
        /// <summary>
        /// Encapsulates information about the non-type members of a (i.e. this) type.
        ///   1) For non-initializers, symbols are created and stored in a list.
        ///   2) For fields and properties/indexers, the symbols are stored in (1) and their initializers are
        ///      stored with other initialized fields and properties from the same syntax tree with
        ///      the same static-ness.
        /// </summary>
        protected sealed class MembersAndInitializers
        {
            internal readonly SynthesizedPrimaryConstructor? PrimaryConstructor;
            internal readonly ImmutableArray<Symbol> NonTypeMembers;
            internal readonly ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> StaticInitializers;
            internal readonly ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> InstanceInitializers;
            internal readonly bool HaveIndexers;
            internal readonly bool IsNullableEnabledForInstanceConstructorsAndFields;
            internal readonly bool IsNullableEnabledForStaticConstructorsAndFields;
 
            public MembersAndInitializers(
                SynthesizedPrimaryConstructor? primaryConstructor,
                ImmutableArray<Symbol> nonTypeMembers,
                ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> staticInitializers,
                ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> instanceInitializers,
                bool haveIndexers,
                bool isNullableEnabledForInstanceConstructorsAndFields,
                bool isNullableEnabledForStaticConstructorsAndFields)
            {
                Debug.Assert(!nonTypeMembers.IsDefault);
                Debug.Assert(!staticInitializers.IsDefault);
                Debug.Assert(staticInitializers.All(g => !g.IsDefault));
                Debug.Assert(!instanceInitializers.IsDefault);
                Debug.Assert(instanceInitializers.All(g => !g.IsDefault));
 
                Debug.Assert(!nonTypeMembers.Any(static s => s is TypeSymbol));
                Debug.Assert(haveIndexers == nonTypeMembers.Any(static s => s.IsIndexer()));
 
                this.PrimaryConstructor = primaryConstructor;
                this.NonTypeMembers = nonTypeMembers;
                this.StaticInitializers = staticInitializers;
                this.InstanceInitializers = instanceInitializers;
                this.HaveIndexers = haveIndexers;
                this.IsNullableEnabledForInstanceConstructorsAndFields = isNullableEnabledForInstanceConstructorsAndFields;
                this.IsNullableEnabledForStaticConstructorsAndFields = isNullableEnabledForStaticConstructorsAndFields;
            }
        }
 
        internal ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> StaticInitializers
        {
            get { return GetMembersAndInitializers().StaticInitializers; }
        }
 
        internal ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> InstanceInitializers
        {
            get { return GetMembersAndInitializers().InstanceInitializers; }
        }
 
        internal int CalculateSyntaxOffsetInSynthesizedConstructor(int position, SyntaxTree tree, bool isStatic)
        {
            if (IsScriptClass && !isStatic)
            {
                int aggregateLength = 0;
 
                foreach (var declaration in this.declaration.Declarations)
                {
                    var syntaxRef = declaration.SyntaxReference;
                    if (tree == syntaxRef.SyntaxTree)
                    {
                        return aggregateLength + position;
                    }
 
                    aggregateLength += syntaxRef.Span.Length;
                }
 
                throw ExceptionUtilities.Unreachable();
            }
 
            int syntaxOffset;
            if (TryCalculateSyntaxOffsetOfPositionInInitializer(position, tree, isStatic, ctorInitializerLength: 0, syntaxOffset: out syntaxOffset))
            {
                return syntaxOffset;
            }
 
            if (declaration.Declarations.Length >= 1 && position == declaration.Declarations[0].Location.SourceSpan.Start)
            {
                // With dynamic analysis instrumentation, the introducing declaration of a type can provide
                // the syntax associated with both the analysis payload local of a synthesized constructor
                // and with the constructor itself. If the synthesized constructor includes an initializer with a lambda,
                // that lambda needs a closure that captures the analysis payload of the constructor,
                // and the offset of the syntax for the local within the constructor is by definition zero.
                return 0;
            }
 
            // an implicit constructor has no body and no initializer, so the variable has to be declared in a member initializer
            throw ExceptionUtilities.Unreachable();
        }
 
        /// <summary>
        /// Calculates a syntax offset of a syntax position that is contained in a property or field initializer (if it is in fact contained in one).
        /// </summary>
        internal bool TryCalculateSyntaxOffsetOfPositionInInitializer(int position, SyntaxTree tree, bool isStatic, int ctorInitializerLength, out int syntaxOffset)
        {
            Debug.Assert(ctorInitializerLength >= 0);
 
            var membersAndInitializers = GetMembersAndInitializers();
            var allInitializers = isStatic ? membersAndInitializers.StaticInitializers : membersAndInitializers.InstanceInitializers;
 
            if (!findInitializer(allInitializers, position, tree, out FieldOrPropertyInitializer initializer, out int precedingLength))
            {
                syntaxOffset = 0;
                return false;
            }
 
            //                                 |<-----------distanceFromCtorBody----------->|
            // [      initializer 0    ][ initializer 1 ][ initializer 2 ][ctor initializer][ctor body]
            // |<--preceding init len-->|      ^
            //                             position
 
            int initializersLength = getInitializersLength(allInitializers);
            int distanceFromInitializerStart = position - initializer.Syntax.Span.Start;
 
            int distanceFromCtorBody =
                initializersLength + ctorInitializerLength -
                (precedingLength + distanceFromInitializerStart);
 
            Debug.Assert(distanceFromCtorBody > 0);
 
            // syntax offset 0 is at the start of the ctor body:
            syntaxOffset = -distanceFromCtorBody;
            return true;
 
            static bool findInitializer(ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> initializers, int position, SyntaxTree tree,
                out FieldOrPropertyInitializer found, out int precedingLength)
            {
                precedingLength = 0;
                foreach (var group in initializers)
                {
                    if (!group.IsEmpty &&
                        group[0].Syntax.SyntaxTree == tree &&
                        position < group.Last().Syntax.Span.End)
                    {
                        // Found group of interest
                        var initializerIndex = IndexOfInitializerContainingPosition(group, position);
                        if (initializerIndex < 0)
                        {
                            break;
                        }
 
                        precedingLength += getPrecedingInitializersLength(group, initializerIndex);
                        found = group[initializerIndex];
                        return true;
                    }
 
                    precedingLength += getGroupLength(group);
                }
 
                found = default;
                return false;
            }
 
            static int getGroupLength(ImmutableArray<FieldOrPropertyInitializer> initializers)
            {
                int length = 0;
                foreach (var initializer in initializers)
                {
                    length += getInitializerLength(initializer);
                }
 
                return length;
            }
 
            static int getPrecedingInitializersLength(ImmutableArray<FieldOrPropertyInitializer> initializers, int index)
            {
                int length = 0;
                for (var i = 0; i < index; i++)
                {
                    length += getInitializerLength(initializers[i]);
                }
 
                return length;
            }
 
            static int getInitializersLength(ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> initializers)
            {
                int length = 0;
                foreach (var group in initializers)
                {
                    length += getGroupLength(group);
                }
 
                return length;
            }
 
            static int getInitializerLength(FieldOrPropertyInitializer initializer)
            {
                // A constant field of type decimal needs a field initializer, so
                // check if it is a metadata constant, not just a constant to exclude
                // decimals. Other constants do not need field initializers.
                if (initializer.FieldOpt == null || !initializer.FieldOpt.IsMetadataConstant)
                {
                    // ignore leading and trailing trivia of the node:
                    return initializer.Syntax.Span.Length;
                }
 
                return 0;
            }
        }
 
        private static int IndexOfInitializerContainingPosition(ImmutableArray<FieldOrPropertyInitializer> initializers, int position)
        {
            // Search for the start of the span (the spans are non-overlapping and sorted)
            int index = initializers.BinarySearch(position, (initializer, pos) => initializer.Syntax.Span.Start.CompareTo(pos));
 
            // Binary search returns non-negative result if the position is exactly the start of some span.
            if (index >= 0)
            {
                return index;
            }
 
            // Otherwise, ~index is the closest span whose start is greater than the position.
            // => Check if the preceding initializer span contains the position.
            int precedingInitializerIndex = ~index - 1;
            if (precedingInitializerIndex >= 0 && initializers[precedingInitializerIndex].Syntax.Span.Contains(position))
            {
                return precedingInitializerIndex;
            }
 
            return -1;
        }
 
        public override IEnumerable<string> MemberNames
        {
            get
            {
                return (IsTupleType || IsRecord || IsRecordStruct) ? GetMembers().Select(m => m.Name) : this.declaration.MemberNames;
            }
        }
 
        internal override ImmutableArray<NamedTypeSymbol> GetTypeMembersUnordered()
        {
            return GetTypeMembersDictionary().Flatten();
        }
 
        public override ImmutableArray<NamedTypeSymbol> GetTypeMembers()
        {
            return GetTypeMembersDictionary().Flatten(LexicalOrderSymbolComparer.Instance);
        }
 
        public override ImmutableArray<NamedTypeSymbol> GetTypeMembers(ReadOnlyMemory<char> name)
        {
            ImmutableArray<NamedTypeSymbol> members;
            if (GetTypeMembersDictionary().TryGetValue(name, out members))
            {
                return members;
            }
 
            return ImmutableArray<NamedTypeSymbol>.Empty;
        }
 
        public override ImmutableArray<NamedTypeSymbol> GetTypeMembers(ReadOnlyMemory<char> name, int arity)
        {
            return GetTypeMembers(name).WhereAsArray((t, arity) => t.Arity == arity, arity);
        }
 
        private Dictionary<ReadOnlyMemory<char>, ImmutableArray<NamedTypeSymbol>> GetTypeMembersDictionary()
        {
            if (_lazyTypeMembers == null)
            {
                var diagnostics = BindingDiagnosticBag.GetInstance();
                if (Interlocked.CompareExchange(ref _lazyTypeMembers, MakeTypeMembers(diagnostics), null) == null)
                {
                    AddDeclarationDiagnostics(diagnostics);
 
                    state.NotePartComplete(CompletionPart.TypeMembers);
                }
 
                diagnostics.Free();
            }
 
            return _lazyTypeMembers;
        }
 
        private Dictionary<ReadOnlyMemory<char>, ImmutableArray<NamedTypeSymbol>> MakeTypeMembers(BindingDiagnosticBag diagnostics)
        {
            var symbols = ArrayBuilder<NamedTypeSymbol>.GetInstance();
            var conflictDict = new Dictionary<(string name, int arity, SyntaxTree? syntaxTree), SourceNamedTypeSymbol>();
            try
            {
                // Declarations which can be merged into a single type symbol have already been merged at this phase.
                // Merging behaves the same in either presence or absence of 'partial' modifiers.
                // However, type declarations which can never be partial won't merge, e.g. 'enum',
                // and type declarations with different kinds, e.g. 'class' and 'struct' will never merge.
                // Now we want to figure out if declarations which didn't merge have name conflicts.
                foreach (var childDeclaration in declaration.Children)
                {
                    var t = new SourceNamedTypeSymbol(this, childDeclaration, diagnostics);
                    this.CheckMemberNameDistinctFromType(t, diagnostics);
 
                    var key = (t.Name, t.Arity, t.AssociatedSyntaxTree);
                    SourceNamedTypeSymbol? other;
                    if (conflictDict.TryGetValue(key, out other))
                    {
                        if (Locations.Length == 1 || IsPartial)
                        {
                            if (t.IsPartial && other.IsPartial)
                            {
                                diagnostics.Add(ErrorCode.ERR_PartialTypeKindConflict, t.GetFirstLocation(), t);
                            }
                            else
                            {
                                diagnostics.Add(ErrorCode.ERR_DuplicateNameInClass, t.GetFirstLocation(), this, t.Name);
                            }
                        }
                    }
                    else
                    {
                        conflictDict.Add(key, t);
                    }
 
                    symbols.Add(t);
                }
 
                if (IsInterface)
                {
                    foreach (var t in symbols)
                    {
                        Binder.CheckFeatureAvailability(t.DeclaringSyntaxReferences[0].GetSyntax(), MessageID.IDS_DefaultInterfaceImplementation, diagnostics, t.GetFirstLocation());
                    }
                }
 
                Debug.Assert(s_emptyTypeMembers.Count == 0);
                return symbols.Count > 0 ?
                    symbols.ToDictionary(s => s.Name.AsMemory(), ReadOnlyMemoryOfCharComparer.Instance) :
                    s_emptyTypeMembers;
            }
            finally
            {
                symbols.Free();
            }
        }
 
        private void CheckMemberNameDistinctFromType(Symbol member, BindingDiagnosticBag diagnostics)
        {
            switch (this.TypeKind)
            {
                case TypeKind.Class:
                case TypeKind.Struct:
                    if (member.Name == this.Name)
                    {
                        diagnostics.Add(ErrorCode.ERR_MemberNameSameAsType, member.GetFirstLocation(), this.Name);
                    }
                    break;
                case TypeKind.Interface:
                    if (member.IsStatic)
                    {
                        goto case TypeKind.Class;
                    }
                    break;
            }
        }
 
        internal override bool HasDeclaredRequiredMembers
        {
            get
            {
                if (_flags.TryGetHasDeclaredRequiredMembers(out bool hasDeclaredMembers))
                {
                    return hasDeclaredMembers;
                }
 
                hasDeclaredMembers = declaration.Declarations.Any(static decl => decl.HasRequiredMembers);
                _flags.SetHasDeclaredRequiredMembers(hasDeclaredMembers);
                return hasDeclaredMembers;
            }
        }
 
        internal override bool HasAsyncMethodBuilderAttribute(out TypeSymbol? builderArgument)
        {
            return HasAsyncMethodBuilderAttribute(this, out builderArgument);
        }
 
        /// <summary>
        /// Returns true if the method has a [AsyncMethodBuilder(typeof(B))] attribute. If so it returns type B.
        /// Validation of builder type B is left for elsewhere. This method returns B without validation of any kind.
        /// </summary>
        internal static bool HasAsyncMethodBuilderAttribute(Symbol symbol, [NotNullWhen(true)] out TypeSymbol? builderArgument)
        {
            Debug.Assert(symbol is not null);
 
            // Find the AsyncMethodBuilder attribute.
            foreach (var attr in symbol.GetAttributes())
            {
                Debug.Assert(attr is SourceAttributeData);
 
                if (attr.IsTargetAttribute(AttributeDescription.AsyncMethodBuilderAttribute)
                    && attr.CommonConstructorArguments.Length == 1
                    && attr.CommonConstructorArguments[0].Kind == TypedConstantKind.Type)
                {
                    builderArgument = (TypeSymbol)attr.CommonConstructorArguments[0].ValueInternal!;
                    return true;
                }
            }
 
            builderArgument = null;
            return false;
        }
 
        internal override ImmutableArray<Symbol> GetMembersUnordered()
        {
            var result = _lazyMembersFlattened;
 
            if (result.IsDefault)
            {
                result = GetMembersByName().Flatten(null);  // do not sort.
                ImmutableInterlocked.InterlockedInitialize(ref _lazyMembersFlattened, result);
                result = _lazyMembersFlattened;
            }
 
            return result.ConditionallyDeOrder();
        }
 
        public override ImmutableArray<Symbol> GetMembers()
        {
            if (_flags.FlattenedMembersIsSorted)
            {
                return _lazyMembersFlattened;
            }
            else
            {
                var allMembers = this.GetMembersUnordered();
 
                if (allMembers.Length > 1)
                {
                    // The array isn't sorted. Sort it and remember that we sorted it.
                    allMembers = allMembers.Sort(LexicalOrderSymbolComparer.Instance);
                    ImmutableInterlocked.InterlockedExchange(ref _lazyMembersFlattened, allMembers);
                }
 
                _flags.SetFlattenedMembersIsSorted();
                return allMembers;
            }
        }
 
        public sealed override ImmutableArray<Symbol> GetMembers(string name)
        {
            ImmutableArray<Symbol> members;
            if (GetMembersByName().TryGetValue(name.AsMemory(), out members))
            {
                return members;
            }
 
            return ImmutableArray<Symbol>.Empty;
        }
 
        /// <remarks>
        /// For source symbols, there can only be a valid clone method if this is a record, which is a
        /// simple syntax check. This will need to change when we generalize cloning, but it's a good
        /// heuristic for now.
        /// </remarks>
        internal override bool HasPossibleWellKnownCloneMethod()
            => IsRecord;
 
        internal override ImmutableArray<Symbol> GetSimpleNonTypeMembers(string name)
        {
            if (_lazyMembersDictionary != null || declaration.MemberNames.Contains(name) || declaration.Kind is DeclarationKind.Record or DeclarationKind.RecordStruct)
            {
                return GetMembers(name);
            }
 
            return ImmutableArray<Symbol>.Empty;
        }
 
        internal override IEnumerable<FieldSymbol> GetFieldsToEmit()
        {
            if (this.TypeKind == TypeKind.Enum)
            {
                // For consistency with Dev10, emit value__ field first.
                var valueField = ((SourceNamedTypeSymbol)this).EnumValueField;
                RoslynDebug.Assert((object)valueField != null);
                yield return valueField;
            }
 
            foreach (var m in this.GetMembers())
            {
                switch (m.Kind)
                {
                    case SymbolKind.Field:
                        if (m is TupleErrorFieldSymbol)
                        {
                            break;
                        }
 
                        yield return (FieldSymbol)m;
                        break;
                    case SymbolKind.Event:
                        FieldSymbol? associatedField = ((EventSymbol)m).AssociatedField;
                        if ((object?)associatedField != null)
                        {
                            yield return associatedField;
                        }
                        break;
                }
            }
        }
 
        /// <summary>
        /// During early attribute decoding, we consider a safe subset of all members that will not
        /// cause cyclic dependencies.  Get all such members for this symbol.
        ///
        /// In particular, this method will return nested types and fields (other than auto-property
        /// backing fields).
        /// </summary>
        internal override ImmutableArray<Symbol> GetEarlyAttributeDecodingMembers()
        {
            return GetEarlyAttributeDecodingMembersDictionary().Flatten();
        }
 
        /// <summary>
        /// During early attribute decoding, we consider a safe subset of all members that will not
        /// cause cyclic dependencies.  Get all such members for this symbol that have a particular name.
        ///
        /// In particular, this method will return nested types and fields (other than auto-property
        /// backing fields).
        /// </summary>
        internal override ImmutableArray<Symbol> GetEarlyAttributeDecodingMembers(string name)
        {
            ImmutableArray<Symbol> result;
            return GetEarlyAttributeDecodingMembersDictionary().TryGetValue(name.AsMemory(), out result) ? result : ImmutableArray<Symbol>.Empty;
        }
 
        private Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>> GetEarlyAttributeDecodingMembersDictionary()
        {
            if (_lazyEarlyAttributeDecodingMembersDictionary == null)
            {
                if (Volatile.Read(ref _lazyMembersDictionary) is Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>> result)
                {
                    return result;
                }
 
                var membersAndInitializers = GetMembersAndInitializers(); //NOTE: separately cached
 
                // NOTE: members were added in a single pass over the syntax, so they're already
                // in lexical order.
 
                Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>> membersByName;
 
                if (!membersAndInitializers.HaveIndexers)
                {
                    membersByName = ToNameKeyedDictionary(membersAndInitializers.NonTypeMembers);
                }
                else
                {
                    // We can't include indexer symbol yet, because we don't know
                    // what name it will have after attribute binding (because of
                    // IndexerNameAttribute).
                    membersByName = ToNameKeyedDictionary(membersAndInitializers.NonTypeMembers.
                        WhereAsArray(s => !s.IsIndexer() && (!s.IsAccessor() || ((MethodSymbol)s).AssociatedSymbol?.IsIndexer() != true)));
                }
 
                AddNestedTypesToDictionary(membersByName, GetTypeMembersDictionary());
 
                Interlocked.CompareExchange(ref _lazyEarlyAttributeDecodingMembersDictionary, membersByName, null);
            }
 
            return _lazyEarlyAttributeDecodingMembersDictionary;
        }
 
        private static Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>> ToNameKeyedDictionary(ImmutableArray<Symbol> symbols)
        {
            if (symbols is [var symbol])
            {
                return new Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>>(1, ReadOnlyMemoryOfCharComparer.Instance)
                {
                    {  symbol.Name.AsMemory(), ImmutableArray.Create(symbol) },
                };
            }
 
            if (symbols.Length == 0)
            {
                return new Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>>(ReadOnlyMemoryOfCharComparer.Instance);
            }
 
            // bucketize
            // prevent reallocation. it may not have 'count' entries, but it won't have more. 
            //
            // We store a mapping from keys to either a single item (very common in practice as this is used from
            // callers that maps names to symbols with that name, and most names are unique), or an array builder of items.
 
            var accumulator = s_nameToObjectPool.Allocate();
            foreach (var item in symbols)
                ImmutableArrayExtensions.AddToMultiValueDictionaryBuilder(accumulator, item.Name.AsMemory(), item);
 
            var dictionary = new Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>>(accumulator.Count, ReadOnlyMemoryOfCharComparer.Instance);
 
            // freeze
            foreach (var pair in accumulator)
            {
                dictionary.Add(pair.Key, pair.Value is ArrayBuilder<Symbol> arrayBuilder
                    ? arrayBuilder.ToImmutableAndFree()
                    : ImmutableArray.Create((Symbol)pair.Value));
            }
 
            accumulator.Free();
 
            return dictionary;
        }
 
        // NOTE: this method should do as little work as possible
        //       we often need to get members just to do a lookup.
        //       All additional checks and diagnostics may be not
        //       needed yet or at all.
        protected MembersAndInitializers GetMembersAndInitializers()
        {
            var membersAndInitializers = _lazyMembersAndInitializers;
            if (membersAndInitializers != null)
            {
                return membersAndInitializers;
            }
 
            var diagnostics = BindingDiagnosticBag.GetInstance();
            membersAndInitializers = BuildMembersAndInitializers(diagnostics);
 
            var alreadyKnown = Interlocked.CompareExchange(ref _lazyMembersAndInitializers, membersAndInitializers, null);
            if (alreadyKnown != null)
            {
                diagnostics.Free();
                return alreadyKnown;
            }
 
            AddDeclarationDiagnostics(diagnostics);
            diagnostics.Free();
            _lazyDeclaredMembersAndInitializers = null;
 
            return membersAndInitializers!;
        }
 
        /// <summary>
        /// The purpose of this function is to assert that the <paramref name="member"/> symbol
        /// is actually among the symbols cached by this type symbol in a way that ensures
        /// that any consumer of standard APIs to get to type's members is going to get the same
        /// symbol (same instance) for the member rather than an equivalent, but different instance.
        /// </summary>
        [Conditional("DEBUG")]
        internal void AssertMemberExposure(Symbol member, bool forDiagnostics = false)
        {
            if (member is NamedTypeSymbol type)
            {
                RoslynDebug.AssertOrFailFast(forDiagnostics);
                RoslynDebug.AssertOrFailFast(Volatile.Read(ref _lazyTypeMembers)?.Values.Any(types => types.Contains(t => t == (object)type)) == true);
                return;
            }
            else if (member is TypeParameterSymbol || member is SynthesizedMethodBaseSymbol)
            {
                RoslynDebug.AssertOrFailFast(forDiagnostics);
                return;
            }
            else if (member is FieldSymbol field && field.AssociatedSymbol is EventSymbol e)
            {
                RoslynDebug.AssertOrFailFast(forDiagnostics);
                // Backing fields for field-like events are not added to the members list.
                member = e;
            }
 
            var membersAndInitializers = Volatile.Read(ref _lazyMembersAndInitializers);
 
            if (isMemberInCompleteMemberList(membersAndInitializers, member))
            {
                return;
            }
 
            if (membersAndInitializers is null)
            {
                if (member is SynthesizedSimpleProgramEntryPointSymbol)
                {
                    RoslynDebug.AssertOrFailFast(GetSimpleProgramEntryPoints().Contains(m => m == (object)member));
                    return;
                }
 
                var declared = Volatile.Read(ref _lazyDeclaredMembersAndInitializers);
                RoslynDebug.AssertOrFailFast(declared != DeclaredMembersAndInitializers.UninitializedSentinel);
 
                if (declared is object)
                {
                    if (declared.NonTypeMembers.Contains(m => m == (object)member) || declared.PrimaryConstructor == (object)member)
                    {
                        return;
                    }
                }
                else
                {
                    // It looks like there was a race and we need to check _lazyMembersAndInitializers again
                    membersAndInitializers = Volatile.Read(ref _lazyMembersAndInitializers);
                    RoslynDebug.AssertOrFailFast(membersAndInitializers is object);
 
                    if (isMemberInCompleteMemberList(membersAndInitializers, member))
                    {
                        return;
                    }
                }
            }
 
            RoslynDebug.AssertOrFailFast(false, "Premature symbol exposure.");
 
            static bool isMemberInCompleteMemberList(MembersAndInitializers? membersAndInitializers, Symbol member)
            {
                return membersAndInitializers?.NonTypeMembers.Contains(m => m == (object)member) == true;
            }
        }
 
        protected Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>> GetMembersByName()
        {
            if (this.state.HasComplete(CompletionPart.Members))
            {
                return _lazyMembersDictionary!;
            }
 
            return GetMembersByNameSlow();
        }
 
        private Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>> GetMembersByNameSlow()
        {
            if (_lazyMembersDictionary == null)
            {
                var diagnostics = BindingDiagnosticBag.GetInstance();
                var membersDictionary = MakeAllMembers(diagnostics);
 
                if (Interlocked.CompareExchange(ref _lazyMembersDictionary, membersDictionary, null) == null)
                {
                    AddDeclarationDiagnostics(diagnostics);
                    state.NotePartComplete(CompletionPart.Members);
                }
 
                diagnostics.Free();
            }
 
            state.SpinWaitComplete(CompletionPart.Members, default(CancellationToken));
            return _lazyMembersDictionary;
        }
 
        internal bool AreMembersComplete => state.HasComplete(CompletionPart.Members);
 
        internal override IEnumerable<Symbol> GetInstanceFieldsAndEvents()
        {
            var membersAndInitializers = this.GetMembersAndInitializers();
 
            IEnumerable<Symbol> result = membersAndInitializers.NonTypeMembers.Where(IsInstanceFieldOrEvent);
 
            return result;
        }
 
        protected void AfterMembersChecks(BindingDiagnosticBag diagnostics)
        {
            if (IsInterface)
            {
                CheckInterfaceMembers(this.GetMembersAndInitializers().NonTypeMembers, diagnostics);
            }
 
            CheckMemberNamesDistinctFromType(diagnostics);
            CheckMemberNameConflicts(diagnostics);
            CheckRecordMemberNames(diagnostics);
            CheckSpecialMemberErrors(diagnostics);
            CheckTypeParameterNameConflicts(diagnostics);
            CheckAccessorNameConflicts(diagnostics);
 
            bool unused = KnownCircularStruct;
 
            CheckSequentialOnPartialType(diagnostics);
            CheckForProtectedInStaticClass(diagnostics);
            CheckForUnmatchedOperators(diagnostics);
            CheckForRequiredMemberAttribute(diagnostics);
 
            if (IsScriptClass || IsSubmissionClass)
            {
                ReportRequiredMembers(diagnostics);
            }
 
            var location = GetFirstLocation();
            var compilation = DeclaringCompilation;
 
            if (this.IsRefLikeType)
            {
                compilation.EnsureIsByRefLikeAttributeExists(diagnostics, location, modifyCompilation: true);
            }
 
            if (this.IsReadOnly)
            {
                compilation.EnsureIsReadOnlyAttributeExists(diagnostics, location, modifyCompilation: true);
            }
 
            var baseType = BaseTypeNoUseSiteDiagnostics;
            var interfaces = GetInterfacesToEmit();
 
            if (compilation.ShouldEmitNativeIntegerAttributes())
            {
                // https://github.com/dotnet/roslyn/issues/30080: Report diagnostics for base type and interfaces at more specific locations.
                if (hasBaseTypeOrInterface(static t => t.ContainsNativeIntegerWrapperType()))
                {
                    compilation.EnsureNativeIntegerAttributeExists(diagnostics, location, modifyCompilation: true);
                }
            }
 
            if (compilation.ShouldEmitNullableAttributes(this))
            {
                if (ShouldEmitNullableContextValue(out _))
                {
                    compilation.EnsureNullableContextAttributeExists(diagnostics, location, modifyCompilation: true);
                }
 
                if (hasBaseTypeOrInterface(static t => t.NeedsNullableAttribute()))
                {
                    compilation.EnsureNullableAttributeExists(diagnostics, location, modifyCompilation: true);
                }
            }
 
            if (interfaces.Any(needsTupleElementNamesAttribute))
            {
                // Note: we don't need to check base type or directly implemented interfaces (which will be reported during binding)
                // so the checking of all interfaces here involves some redundancy.
                Binder.ReportMissingTupleElementNamesAttributesIfNeeded(compilation, location, diagnostics);
            }
 
            if (IsReservedTypeName(Name))
            {
                foreach (var syntaxRef in SyntaxReferences)
                {
                    SyntaxToken? identifier = syntaxRef.GetSyntax() switch
                    {
                        BaseTypeDeclarationSyntax typeDecl => typeDecl.Identifier,
                        DelegateDeclarationSyntax delegateDecl => delegateDecl.Identifier,
                        _ => null
                    };
 
                    ReportReservedTypeName(identifier?.Text, this.DeclaringCompilation, diagnostics.DiagnosticBag, identifier?.GetLocation() ?? Location.None);
                }
            }
 
            if (AssociatedFileIdentifier is { } fileIdentifier)
            {
                Debug.Assert(IsFileLocal);
 
                // A well-behaved file-local type only has declarations in one syntax tree.
                // There may be multiple syntax trees across declarations in error scenarios,
                // but we're not interested in handling that for the purposes of producing this error.
                var tree = declaration.Declarations[0].SyntaxReference.SyntaxTree;
                if (fileIdentifier.EncoderFallbackErrorMessage is { } errorMessage)
                {
                    Debug.Assert(fileIdentifier.FilePathChecksumOpt.IsDefault);
                    diagnostics.Add(ErrorCode.ERR_FilePathCannotBeConvertedToUtf8, location, this, errorMessage);
                }
 
                if ((object?)ContainingType != null)
                {
                    diagnostics.Add(ErrorCode.ERR_FileTypeNested, location, this);
                }
            }
 
            return;
 
            bool hasBaseTypeOrInterface(Func<NamedTypeSymbol, bool> predicate)
            {
                return ((object)baseType != null && predicate(baseType)) ||
                    interfaces.Any(predicate);
            }
 
            static bool needsTupleElementNamesAttribute(TypeSymbol type)
            {
                if (type is null)
                {
                    return false;
                }
 
                var resultType = type.VisitType(
                    predicate: (t, a, b) => !t.TupleElementNames.IsDefaultOrEmpty && !t.IsErrorType(),
                    arg: (object?)null);
                return resultType is object;
            }
        }
 
        protected virtual void AfterMembersCompletedChecks(BindingDiagnosticBag diagnostics)
        {
        }
 
        private void CheckMemberNamesDistinctFromType(BindingDiagnosticBag diagnostics)
        {
            foreach (var member in GetMembersAndInitializers().NonTypeMembers)
            {
                CheckMemberNameDistinctFromType(member, diagnostics);
            }
        }
 
        private void CheckRecordMemberNames(BindingDiagnosticBag diagnostics)
        {
            if (declaration.Kind != DeclarationKind.Record &&
                declaration.Kind != DeclarationKind.RecordStruct)
            {
                return;
            }
 
            foreach (var member in GetMembers("Clone"))
            {
                diagnostics.Add(ErrorCode.ERR_CloneDisallowedInRecord, member.GetFirstLocation());
            }
        }
 
        private void CheckMemberNameConflicts(BindingDiagnosticBag diagnostics)
        {
            Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>> membersByName = GetMembersByName();
 
            // Collisions involving indexers are handled specially.
            CheckIndexerNameConflicts(diagnostics, membersByName);
 
            // key and value will be the same object in these dictionaries.
            var methodsBySignature = new Dictionary<SourceMemberMethodSymbol, SourceMemberMethodSymbol>(MemberSignatureComparer.DuplicateSourceComparer);
            var conversionsAsMethods = new Dictionary<SourceMemberMethodSymbol, SourceMemberMethodSymbol>(MemberSignatureComparer.DuplicateSourceComparer);
            var conversionsAsConversions = new HashSet<SourceUserDefinedConversionSymbol>(ConversionSignatureComparer.Comparer);
 
            // SPEC: The signature of an operator must differ from the signatures of all other
            // SPEC: operators declared in the same class.
 
            // DELIBERATE SPEC VIOLATION:
            // The specification does not state that a user-defined conversion reserves the names
            // op_Implicit or op_Explicit, but nevertheless the native compiler does so; an attempt
            // to define a field or a conflicting method with the metadata name of a user-defined
            // conversion is an error.  We preserve this reasonable behavior.
            //
            // Similarly, we treat "public static C operator +(C, C)" as colliding with
            // "public static C op_Addition(C, C)". Fortunately, this behavior simply
            // falls out of treating user-defined operators as ordinary methods; we do
            // not need any special handling in this method.
            //
            // However, we must have special handling for conversions because conversions
            // use a completely different rule for detecting collisions between two
            // conversions: conversion signatures consist only of the source and target
            // types of the conversions, and not the kind of the conversion (implicit or explicit),
            // the name of the method, and so on.
            //
            // Therefore we must detect the following kinds of member name conflicts:
            //
            // 1. a method, conversion or field has the same name as a (different) field (* see note below)
            // 2. a method has the same method signature as another method or conversion
            // 3. a conversion has the same conversion signature as another conversion.
            //
            // However, we must *not* detect "a conversion has the same *method* signature
            // as another conversion" because conversions are allowed to overload on
            // return type but methods are not.
            //
            // (*) NOTE: Throughout the rest of this method I will use "field" as a shorthand for
            // "non-method, non-conversion, non-type member", rather than spelling out
            // "field, property or event...")
 
            foreach (var pair in membersByName)
            {
                var name = pair.Key;
                Symbol? lastSym = GetTypeMembers(name).FirstOrDefault();
                methodsBySignature.Clear();
                // Conversion collisions do not consider the name of the conversion,
                // so do not clear that dictionary.
                foreach (var symbol in pair.Value)
                {
                    if (symbol.Kind == SymbolKind.NamedType ||
                        symbol.IsAccessor() ||
                        symbol.IsIndexer())
                    {
                        continue;
                    }
 
                    // We detect the first category of conflict by running down the list of members
                    // of the same name, and producing an error when we discover any of the following
                    // "bad transitions".
                    //
                    // * a method or conversion that comes after any field (not necessarily directly)
                    // * a field directly following a field
                    // * a field directly following a method or conversion
                    //
                    // Furthermore: we do not wish to detect collisions between nested types in
                    // this code; that is tested elsewhere. However, we do wish to detect a collision
                    // between a nested type and a field, method or conversion. Therefore we
                    // initialize our "bad transition" detector with a type of the given name,
                    // if there is one. That way we also detect the transitions of "method following
                    // type", and so on.
                    //
                    // The "lastSym" local below is used to detect these transitions. Its value is
                    // one of the following:
                    //
                    // * a nested type of the given name, or
                    // * the first method of the given name, or
                    // * the most recently processed field of the given name.
                    //
                    // If either the current symbol or the "last symbol" are not methods then
                    // there must be a collision:
                    //
                    // * if the current symbol is not a method and the last symbol is, then
                    //   there is a field directly following a method of the same name
                    // * if the current symbol is a method and the last symbol is not, then
                    //   there is a method directly or indirectly following a field of the same name,
                    //   or a method of the same name as a nested type.
                    // * if neither are methods then either we have a field directly
                    //   following a field of the same name, or a field and a nested type of the same name.
                    //
 
                    if (lastSym is object)
                    {
                        if (symbol.Kind != SymbolKind.Method || lastSym.Kind != SymbolKind.Method)
                        {
                            if (symbol.Kind != SymbolKind.Field || !symbol.IsImplicitlyDeclared)
                            {
                                // The type '{0}' already contains a definition for '{1}'
                                if (Locations.Length == 1 || IsPartial)
                                {
                                    diagnostics.Add(ErrorCode.ERR_DuplicateNameInClass, symbol.GetFirstLocation(), this, symbol.Name);
                                }
                            }
 
                            if (lastSym.Kind == SymbolKind.Method)
                            {
                                lastSym = symbol;
                            }
                        }
                    }
                    else
                    {
                        lastSym = symbol;
                    }
 
                    // That takes care of the first category of conflict; we detect the
                    // second and third categories as follows:
 
                    var conversion = symbol as SourceUserDefinedConversionSymbol;
                    var method = symbol as SourceMemberMethodSymbol;
 
                    // We don't want to consider explicit interface implementations
                    if (conversion is { MethodKind: MethodKind.Conversion })
                    {
                        // Does this conversion collide *as a conversion* with any previously-seen
                        // conversion?
 
                        if (!conversionsAsConversions.Add(conversion))
                        {
                            // CS0557: Duplicate user-defined conversion in type 'C'
                            diagnostics.Add(ErrorCode.ERR_DuplicateConversionInClass, conversion.GetFirstLocation(), this);
                        }
                        else
                        {
                            // The other set might already contain a conversion which would collide
                            // *as a method* with the current conversion.
                            if (!conversionsAsMethods.ContainsKey(conversion))
                            {
                                conversionsAsMethods.Add(conversion, conversion);
                            }
                        }
 
                        // Does this conversion collide *as a method* with any previously-seen
                        // non-conversion method?
 
                        if (methodsBySignature.TryGetValue(conversion, out var previousMethod))
                        {
                            ReportMethodSignatureCollision(diagnostics, conversion, previousMethod);
                        }
                        // Do not add the conversion to the set of previously-seen methods; that set
                        // is only non-conversion methods.
                    }
                    else if (!(method is null))
                    {
                        // Does this method collide *as a method* with any previously-seen
                        // conversion?
 
                        if (conversionsAsMethods.TryGetValue(method, out var previousConversion))
                        {
                            ReportMethodSignatureCollision(diagnostics, method, previousConversion);
                        }
                        // Do not add the method to the set of previously-seen conversions.
 
                        // Does this method collide *as a method* with any previously-seen
                        // non-conversion method?
 
                        if (methodsBySignature.TryGetValue(method, out var previousMethod))
                        {
                            ReportMethodSignatureCollision(diagnostics, method, previousMethod);
                        }
                        else
                        {
                            // We haven't seen this method before. Make a note of it in case
                            // we see a colliding method later.
                            methodsBySignature.Add(method, method);
                        }
                    }
                }
            }
        }
 
        // Report a name conflict; the error is reported on the location of method1.
        // UNDONE: Consider adding a secondary location pointing to the second method.
        private void ReportMethodSignatureCollision(BindingDiagnosticBag diagnostics, SourceMemberMethodSymbol method1, SourceMemberMethodSymbol method2)
        {
            switch (method1, method2)
            {
                case (SourceOrdinaryMethodSymbol { IsPartialDefinition: true }, SourceOrdinaryMethodSymbol { IsPartialImplementation: true }):
                case (SourceOrdinaryMethodSymbol { IsPartialImplementation: true }, SourceOrdinaryMethodSymbol { IsPartialDefinition: true }):
                    // these could be 2 parts of the same partial method.
                    // Partial methods are allowed to collide by signature.
                    return;
                case (SynthesizedSimpleProgramEntryPointSymbol { }, SynthesizedSimpleProgramEntryPointSymbol { }):
                    return;
            }
 
            // If method1 is a constructor only because its return type is missing, then
            // we've already produced a diagnostic for the missing return type and we suppress the
            // diagnostic about duplicate signature.
            if (method1.MethodKind == MethodKind.Constructor &&
                ((ConstructorDeclarationSyntax)method1.SyntaxRef.GetSyntax()).Identifier.ValueText != this.Name)
            {
                return;
            }
 
            Debug.Assert(method1.ParameterCount == method2.ParameterCount);
 
            for (int i = 0; i < method1.ParameterCount; i++)
            {
                var refKind1 = method1.Parameters[i].RefKind;
                var refKind2 = method2.Parameters[i].RefKind;
 
                if (refKind1 != refKind2)
                {
                    // '{0}' cannot define an overloaded {1} that differs only on parameter modifiers '{2}' and '{3}'
                    var methodKind = method1.MethodKind == MethodKind.Constructor ? MessageID.IDS_SK_CONSTRUCTOR : MessageID.IDS_SK_METHOD;
                    diagnostics.Add(ErrorCode.ERR_OverloadRefKind, method1.GetFirstLocation(), this, methodKind.Localize(), refKind1.ToParameterDisplayString(), refKind2.ToParameterDisplayString());
 
                    return;
                }
            }
 
            // Special case: if there are two destructors, use the destructor syntax instead of "Finalize"
            var methodName = (method1.MethodKind == MethodKind.Destructor && method2.MethodKind == MethodKind.Destructor) ?
                "~" + this.Name :
                (method1.IsConstructor() ? this.Name : method1.Name);
 
            // Type '{1}' already defines a member called '{0}' with the same parameter types
            diagnostics.Add(ErrorCode.ERR_MemberAlreadyExists, method1.GetFirstLocation(), methodName, this);
        }
 
        private void CheckIndexerNameConflicts(BindingDiagnosticBag diagnostics, Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>> membersByName)
        {
            PooledHashSet<string>? typeParameterNames = null;
            if (this.Arity > 0)
            {
                typeParameterNames = PooledHashSet<string>.GetInstance();
                foreach (TypeParameterSymbol typeParameter in this.TypeParameters)
                {
                    typeParameterNames.Add(typeParameter.Name);
                }
            }
 
            var indexersBySignature = new Dictionary<PropertySymbol, PropertySymbol>(MemberSignatureComparer.DuplicateSourceComparer);
 
            // Note: Can't assume that all indexers are called WellKnownMemberNames.Indexer because
            // they may be explicit interface implementations.
            foreach (var members in membersByName.Values)
            {
                string? lastIndexerName = null;
                indexersBySignature.Clear();
                foreach (var symbol in members)
                {
                    if (symbol.IsIndexer())
                    {
                        PropertySymbol indexer = (PropertySymbol)symbol;
                        CheckIndexerSignatureCollisions(
                            indexer,
                            diagnostics,
                            membersByName,
                            indexersBySignature,
                            ref lastIndexerName);
 
                        // Also check for collisions with type parameters, which aren't in the member map.
                        // NOTE: Accessors have normal names and are handled in CheckTypeParameterNameConflicts.
                        if (typeParameterNames != null)
                        {
                            string indexerName = indexer.MetadataName;
                            if (typeParameterNames.Contains(indexerName))
                            {
                                diagnostics.Add(ErrorCode.ERR_DuplicateNameInClass, indexer.GetFirstLocation(), this, indexerName);
                                continue;
                            }
                        }
                    }
                }
            }
 
            typeParameterNames?.Free();
        }
 
        private void CheckIndexerSignatureCollisions(
            PropertySymbol indexer,
            BindingDiagnosticBag diagnostics,
            Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>> membersByName,
            Dictionary<PropertySymbol, PropertySymbol> indexersBySignature,
            ref string? lastIndexerName)
        {
            if (!indexer.IsExplicitInterfaceImplementation) //explicit implementation names are not checked
            {
                string indexerName = indexer.MetadataName;
 
                if (lastIndexerName != null && lastIndexerName != indexerName)
                {
                    // NOTE: dev10 checks indexer names by comparing each to the previous.
                    // For example, if indexers are declared with names A, B, A, B, then there
                    // will be three errors - one for each time the name is different from the
                    // previous one.  If, on the other hand, the names are A, A, B, B, then
                    // there will only be one error because only one indexer has a different
                    // name from the previous one.
 
                    diagnostics.Add(ErrorCode.ERR_InconsistentIndexerNames, indexer.GetFirstLocation());
                }
 
                lastIndexerName = indexerName;
 
                if (Locations.Length == 1 || IsPartial)
                {
#pragma warning disable CA1854 //Prefer a 'TryGetValue' call over a Dictionary indexer access guarded by a 'ContainsKey' check to avoid double lookup
                    if (membersByName.ContainsKey(indexerName.AsMemory()))
#pragma warning restore CA1854
                    {
                        // The name of the indexer is reserved - it can only be used by other indexers.
                        Debug.Assert(!membersByName[indexerName.AsMemory()].Any(SymbolExtensions.IsIndexer));
                        diagnostics.Add(ErrorCode.ERR_DuplicateNameInClass, indexer.GetFirstLocation(), this, indexerName);
                    }
                }
            }
 
            if (indexersBySignature.TryGetValue(indexer, out var prevIndexerBySignature))
            {
                // Type '{1}' already defines a member called '{0}' with the same parameter types
                // NOTE: Dev10 prints "this" as the name of the indexer.
                diagnostics.Add(ErrorCode.ERR_MemberAlreadyExists, indexer.GetFirstLocation(), SyntaxFacts.GetText(SyntaxKind.ThisKeyword), this);
            }
            else
            {
                indexersBySignature[indexer] = indexer;
            }
        }
 
        private void CheckSpecialMemberErrors(BindingDiagnosticBag diagnostics)
        {
            var conversions = this.ContainingAssembly.CorLibrary.TypeConversions;
            foreach (var member in this.GetMembersUnordered())
            {
                member.AfterAddingTypeMembersChecks(conversions, diagnostics);
            }
        }
 
        private void CheckTypeParameterNameConflicts(BindingDiagnosticBag diagnostics)
        {
            if (this.TypeKind == TypeKind.Delegate)
            {
                // Delegates do not have conflicts between their type parameter
                // names and their methods; it is legal (though odd) to say
                // delegate void D<Invoke>(Invoke x);
 
                return;
            }
 
            if (Locations.Length == 1 || IsPartial)
            {
                foreach (var tp in TypeParameters)
                {
                    foreach (var dup in GetMembers(tp.Name))
                    {
                        diagnostics.Add(ErrorCode.ERR_DuplicateNameInClass, dup.GetFirstLocation(), this, tp.Name);
                    }
                }
            }
        }
 
        private void CheckAccessorNameConflicts(BindingDiagnosticBag diagnostics)
        {
            // Report errors where property and event accessors
            // conflict with other members of the same name.
            foreach (Symbol symbol in this.GetMembersUnordered())
            {
                if (symbol.IsExplicitInterfaceImplementation())
                {
                    // If there's a name conflict it will show up as a more specific
                    // interface implementation error.
                    continue;
                }
                switch (symbol.Kind)
                {
                    case SymbolKind.Property:
                        {
                            var propertySymbol = (PropertySymbol)symbol;
                            this.CheckForMemberConflictWithPropertyAccessor(propertySymbol, getNotSet: true, diagnostics: diagnostics);
                            this.CheckForMemberConflictWithPropertyAccessor(propertySymbol, getNotSet: false, diagnostics: diagnostics);
                            break;
                        }
                    case SymbolKind.Event:
                        {
                            var eventSymbol = (EventSymbol)symbol;
                            this.CheckForMemberConflictWithEventAccessor(eventSymbol, isAdder: true, diagnostics: diagnostics);
                            this.CheckForMemberConflictWithEventAccessor(eventSymbol, isAdder: false, diagnostics: diagnostics);
                            break;
                        }
                }
            }
        }
 
        internal override bool KnownCircularStruct
        {
            get
            {
                if (_lazyKnownCircularStruct == (int)ThreeState.Unknown)
                {
                    if (TypeKind != TypeKind.Struct)
                    {
                        Interlocked.CompareExchange(ref _lazyKnownCircularStruct, (int)ThreeState.False, (int)ThreeState.Unknown);
                    }
                    else
                    {
                        var diagnostics = BindingDiagnosticBag.GetInstance();
                        var value = (int)CheckStructCircularity(diagnostics).ToThreeState();
 
                        if (Interlocked.CompareExchange(ref _lazyKnownCircularStruct, value, (int)ThreeState.Unknown) == (int)ThreeState.Unknown)
                        {
                            AddDeclarationDiagnostics(diagnostics);
                        }
 
                        Debug.Assert(value == _lazyKnownCircularStruct);
                        diagnostics.Free();
                    }
                }
 
                return _lazyKnownCircularStruct == (int)ThreeState.True;
            }
        }
 
        private bool CheckStructCircularity(BindingDiagnosticBag diagnostics)
        {
            Debug.Assert(TypeKind == TypeKind.Struct);
 
            CheckFiniteFlatteningGraph(diagnostics);
            return HasStructCircularity(diagnostics);
        }
 
        private bool HasStructCircularity(BindingDiagnosticBag diagnostics)
        {
            foreach (var valuesByName in GetMembersByName().Values)
            {
                foreach (var member in valuesByName)
                {
                    FieldSymbol? field;
 
                    // Only instance fields (including field-like events) affect the outcome.
                    switch (member.Kind)
                    {
                        case SymbolKind.Field:
                            field = (FieldSymbol)member;
                            Debug.Assert(field.AssociatedSymbol is not EventSymbol, "Didn't expect to find a field-like event backing field in the member list.");
                            break;
                        case SymbolKind.Event:
                            field = ((EventSymbol)member).AssociatedField;
                            break;
                        default:
                            continue;
                    }
 
                    if (field is null || field.IsStatic)
                    {
                        continue;
                    }
                    var type = field.NonPointerType();
                    if (((object)type != null) &&
                        (type.TypeKind == TypeKind.Struct) &&
                        BaseTypeAnalysis.StructDependsOn((NamedTypeSymbol)type, this) &&
                        !type.IsPrimitiveRecursiveStruct()) // allow System.Int32 to contain a field of its own type
                    {
                        if (field is SynthesizedPrimaryConstructorParameterBackingFieldSymbol { ParameterSymbol: var parameterSymbol })
                        {
                            diagnostics.Add(ErrorCode.ERR_StructLayoutCyclePrimaryConstructorParameter, parameterSymbol.GetFirstLocation(), parameterSymbol, type);
                        }
                        else
                        {
                            // If this is a backing field, report the error on the associated property.
                            var symbol = field.AssociatedSymbol ?? field;
 
                            // Struct member '{0}' of type '{1}' causes a cycle in the struct layout
                            diagnostics.Add(ErrorCode.ERR_StructLayoutCycle, symbol.GetFirstLocation(), symbol, type);
                        }
                        return true;
                    }
                }
            }
            return false;
        }
 
        private void CheckForProtectedInStaticClass(BindingDiagnosticBag diagnostics)
        {
            if (!IsStatic)
            {
                return;
            }
 
            // no protected members allowed
            foreach (var valuesByName in GetMembersByName().Values)
            {
                foreach (var member in valuesByName)
                {
                    if (member is TypeSymbol)
                    {
                        // Duplicate Dev10's failure to diagnose this error.
                        continue;
                    }
 
                    if (member.DeclaredAccessibility.HasProtected())
                    {
                        if (member.Kind != SymbolKind.Method || ((MethodSymbol)member).MethodKind != MethodKind.Destructor)
                        {
                            diagnostics.Add(ErrorCode.ERR_ProtectedInStatic, member.GetFirstLocation(), member);
                        }
                    }
                }
            }
        }
 
        private void CheckForUnmatchedOperators(BindingDiagnosticBag diagnostics)
        {
            // SPEC: The true and false unary operators require pairwise declaration.
            // SPEC: A compile-time error occurs if a class or struct declares one
            // SPEC: of these operators without also declaring the other.
            //
            // SPEC DEFICIENCY: The line of the specification quoted above should say
            // the same thing as the lines below: that the formal parameters of the
            // paired true/false operators must match exactly. You can't do
            // op true(S) and op false(S?) for example.
 
            // SPEC: Certain binary operators require pairwise declaration. For every
            // SPEC: declaration of either operator of a pair, there must be a matching
            // SPEC: declaration of the other operator of the pair. Two operator
            // SPEC: declarations match when they have the same return type and the same
            // SPEC: type for each parameter. The following operators require pairwise
            // SPEC: declaration: == and !=, > and <, >= and <=.
 
            CheckForUnmatchedOperator(diagnostics, WellKnownMemberNames.TrueOperatorName, WellKnownMemberNames.FalseOperatorName);
            CheckForUnmatchedOperator(diagnostics, WellKnownMemberNames.EqualityOperatorName, WellKnownMemberNames.InequalityOperatorName);
            CheckForUnmatchedOperator(diagnostics, WellKnownMemberNames.LessThanOperatorName, WellKnownMemberNames.GreaterThanOperatorName);
            CheckForUnmatchedOperator(diagnostics, WellKnownMemberNames.LessThanOrEqualOperatorName, WellKnownMemberNames.GreaterThanOrEqualOperatorName);
 
            CheckForUnmatchedOperator(diagnostics, WellKnownMemberNames.CheckedDecrementOperatorName, WellKnownMemberNames.DecrementOperatorName, symmetricCheck: false);
            CheckForUnmatchedOperator(diagnostics, WellKnownMemberNames.CheckedIncrementOperatorName, WellKnownMemberNames.IncrementOperatorName, symmetricCheck: false);
            CheckForUnmatchedOperator(diagnostics, WellKnownMemberNames.CheckedUnaryNegationOperatorName, WellKnownMemberNames.UnaryNegationOperatorName, symmetricCheck: false);
            CheckForUnmatchedOperator(diagnostics, WellKnownMemberNames.CheckedAdditionOperatorName, WellKnownMemberNames.AdditionOperatorName, symmetricCheck: false);
            CheckForUnmatchedOperator(diagnostics, WellKnownMemberNames.CheckedDivisionOperatorName, WellKnownMemberNames.DivisionOperatorName, symmetricCheck: false);
            CheckForUnmatchedOperator(diagnostics, WellKnownMemberNames.CheckedMultiplyOperatorName, WellKnownMemberNames.MultiplyOperatorName, symmetricCheck: false);
            CheckForUnmatchedOperator(diagnostics, WellKnownMemberNames.CheckedSubtractionOperatorName, WellKnownMemberNames.SubtractionOperatorName, symmetricCheck: false);
            CheckForUnmatchedOperator(diagnostics, WellKnownMemberNames.CheckedExplicitConversionName, WellKnownMemberNames.ExplicitConversionName, symmetricCheck: false);
 
            // We also produce a warning if == / != is overridden without also overriding
            // Equals and GetHashCode, or if Equals is overridden without GetHashCode.
 
            CheckForEqualityAndGetHashCode(diagnostics);
        }
 
        private void CheckForUnmatchedOperator(BindingDiagnosticBag diagnostics, string operatorName1, string operatorName2, bool symmetricCheck = true)
        {
            ImmutableArray<MethodSymbol> ops1 = this.GetOperators(operatorName1);
 
            if (symmetricCheck)
            {
                var ops2 = this.GetOperators(operatorName2);
                CheckForUnmatchedOperator(diagnostics, ops1, ops2, operatorName2, reportOperatorNeedsMatch);
                CheckForUnmatchedOperator(diagnostics, ops2, ops1, operatorName1, reportOperatorNeedsMatch);
            }
            else if (!ops1.IsEmpty)
            {
                var ops2 = this.GetOperators(operatorName2);
                CheckForUnmatchedOperator(diagnostics, ops1, ops2, operatorName2, reportCheckedOperatorNeedsMatch);
            }
 
            static void reportOperatorNeedsMatch(BindingDiagnosticBag diagnostics, string operatorName2, MethodSymbol op1)
            {
                // CS0216: The operator 'C.operator true(C)' requires a matching operator 'false' to also be defined
                diagnostics.Add(ErrorCode.ERR_OperatorNeedsMatch, op1.GetFirstLocation(), op1,
                    SyntaxFacts.GetText(SyntaxFacts.GetOperatorKind(operatorName2)));
            }
 
            static void reportCheckedOperatorNeedsMatch(BindingDiagnosticBag diagnostics, string operatorName2, MethodSymbol op1)
            {
                diagnostics.Add(ErrorCode.ERR_CheckedOperatorNeedsMatch, op1.GetFirstLocation(), op1);
            }
        }
 
        private static void CheckForUnmatchedOperator(
            BindingDiagnosticBag diagnostics,
            ImmutableArray<MethodSymbol> ops1,
            ImmutableArray<MethodSymbol> ops2,
            string operatorName2,
            Action<BindingDiagnosticBag, string, MethodSymbol> reportMatchNotFoundError)
        {
            foreach (var op1 in ops1)
            {
                bool foundMatch = false;
                foreach (var op2 in ops2)
                {
                    foundMatch = DoOperatorsPair(op1, op2);
                    if (foundMatch)
                    {
                        break;
                    }
                }
 
                if (!foundMatch)
                {
                    reportMatchNotFoundError(diagnostics, operatorName2, op1);
                }
            }
        }
 
        internal static bool DoOperatorsPair(MethodSymbol op1, MethodSymbol op2)
        {
            if (op1.ParameterCount != op2.ParameterCount)
            {
                return false;
            }
 
            for (int p = 0; p < op1.ParameterCount; ++p)
            {
                if (!op1.ParameterTypesWithAnnotations[p].Equals(op2.ParameterTypesWithAnnotations[p], TypeCompareKind.AllIgnoreOptions))
                {
                    return false;
                }
            }
 
            if (!op1.ReturnType.Equals(op2.ReturnType, TypeCompareKind.AllIgnoreOptions))
            {
                return false;
            }
 
            return true;
        }
 
        private void CheckForEqualityAndGetHashCode(BindingDiagnosticBag diagnostics)
        {
            if (this.IsInterfaceType())
            {
                // Interfaces are allowed to define Equals without GetHashCode if they want.
                return;
            }
 
            if (IsRecord || IsRecordStruct)
            {
                // For records the warnings reported below are simply going to echo record specific errors,
                // producing more noise.
                return;
            }
 
            bool hasOp = this.GetOperators(WellKnownMemberNames.EqualityOperatorName).Any() ||
                this.GetOperators(WellKnownMemberNames.InequalityOperatorName).Any();
            bool overridesEquals = this.TypeOverridesObjectMethod("Equals");
 
            if (hasOp || overridesEquals)
            {
                bool overridesGHC = this.TypeOverridesObjectMethod("GetHashCode");
                if (overridesEquals && !overridesGHC)
                {
                    // CS0659: 'C' overrides Object.Equals(object o) but does not override Object.GetHashCode()
                    diagnostics.Add(ErrorCode.WRN_EqualsWithoutGetHashCode, this.GetFirstLocation(), this);
                }
 
                if (hasOp && !overridesEquals)
                {
                    // CS0660: 'C' defines operator == or operator != but does not override Object.Equals(object o)
                    diagnostics.Add(ErrorCode.WRN_EqualityOpWithoutEquals, this.GetFirstLocation(), this);
                }
 
                if (hasOp && !overridesGHC)
                {
                    // CS0661: 'C' defines operator == or operator != but does not override Object.GetHashCode()
                    diagnostics.Add(ErrorCode.WRN_EqualityOpWithoutGetHashCode, this.GetFirstLocation(), this);
                }
            }
        }
 
        private void CheckForRequiredMemberAttribute(BindingDiagnosticBag diagnostics)
        {
            if (HasDeclaredRequiredMembers)
            {
                // Ensure that an error is reported if the required constructor isn't present.
                _ = Binder.GetWellKnownTypeMember(DeclaringCompilation, WellKnownMember.System_Runtime_CompilerServices_RequiredMemberAttribute__ctor, diagnostics, GetFirstLocation());
            }
 
            if (HasAnyRequiredMembers)
            {
                _ = Binder.GetWellKnownTypeMember(DeclaringCompilation, WellKnownMember.System_Runtime_CompilerServices_CompilerFeatureRequiredAttribute__ctor, diagnostics, GetFirstLocation());
 
                if (this.IsRecord)
                {
                    // Copy constructors need to emit SetsRequiredMembers on the ctor
                    _ = Binder.GetWellKnownTypeMember(DeclaringCompilation, WellKnownMember.System_Diagnostics_CodeAnalysis_SetsRequiredMembersAttribute__ctor, diagnostics, GetFirstLocation());
                }
            }
 
            if (BaseTypeNoUseSiteDiagnostics is (not SourceMemberContainerTypeSymbol) and { HasRequiredMembersError: true })
            {
                foreach (var member in GetMembersUnordered())
                {
                    if (member is not MethodSymbol method || !method.ShouldCheckRequiredMembers())
                    {
                        continue;
                    }
 
                    // The required members list for the base type '{0}' is malformed and cannot be interpreted. To use this constructor, apply the 'SetsRequiredMembers' attribute.
                    diagnostics.Add(ErrorCode.ERR_RequiredMembersBaseTypeInvalid, method.GetFirstLocation(), BaseTypeNoUseSiteDiagnostics);
                }
            }
        }
 
        private void ReportRequiredMembers(BindingDiagnosticBag diagnostics)
        {
            Debug.Assert(IsSubmissionClass || IsScriptClass);
 
            foreach (var member in GetMembersUnordered())
            {
                if (member.IsRequired())
                {
                    // Required members are not allowed on the top level of a script or submission.
                    diagnostics.Add(ErrorCode.ERR_ScriptsAndSubmissionsCannotHaveRequiredMembers, member.GetFirstLocation());
                }
            }
        }
 
        private bool TypeOverridesObjectMethod(string name)
        {
            foreach (var method in this.GetMembers(name).OfType<MethodSymbol>())
            {
                if (method.IsOverride && method.GetConstructedLeastOverriddenMethod(this, requireSameReturnType: false).ContainingType.SpecialType == Microsoft.CodeAnalysis.SpecialType.System_Object)
                {
                    return true;
                }
            }
            return false;
        }
 
        private void CheckFiniteFlatteningGraph(BindingDiagnosticBag diagnostics)
        {
            Debug.Assert(ReferenceEquals(this, this.OriginalDefinition));
            if (AllTypeArgumentCount() == 0) return;
            var instanceMap = new Dictionary<NamedTypeSymbol, NamedTypeSymbol>(ReferenceEqualityComparer.Instance);
            instanceMap.Add(this, this);
            foreach (var m in this.GetMembersUnordered())
            {
                FieldSymbol? f;
 
                // Only instance fields (including field-like events) affect the outcome.
                switch (m.Kind)
                {
                    case SymbolKind.Field:
                        f = (FieldSymbol)m;
                        Debug.Assert(f.AssociatedSymbol is not EventSymbol, "Didn't expect to find a field-like event backing field in the member list.");
                        break;
                    case SymbolKind.Event:
                        f = ((EventSymbol)m).AssociatedField;
                        break;
                    default:
                        continue;
                }
 
                if (f is null || !f.IsStatic || f.Type.TypeKind != TypeKind.Struct) continue;
                var type = (NamedTypeSymbol)f.Type;
                if (InfiniteFlatteningGraph(this, type, instanceMap))
                {
                    // Struct member '{0}' of type '{1}' causes a cycle in the struct layout
                    diagnostics.Add(ErrorCode.ERR_StructLayoutCycle, f.GetFirstLocation(), f, type);
                    //this.KnownCircularStruct = true;
                    return;
                }
            }
        }
 
        private static bool InfiniteFlatteningGraph(SourceMemberContainerTypeSymbol top, NamedTypeSymbol t, Dictionary<NamedTypeSymbol, NamedTypeSymbol> instanceMap)
        {
            if (!t.ContainsTypeParameter()) return false;
            var tOriginal = t.OriginalDefinition;
            if (instanceMap.TryGetValue(tOriginal, out var oldInstance))
            {
                // short circuit when we find a cycle, but only return true when the cycle contains the top struct
                return (!TypeSymbol.Equals(oldInstance, t, TypeCompareKind.AllNullableIgnoreOptions)) && ReferenceEquals(tOriginal, top);
            }
            else
            {
                instanceMap.Add(tOriginal, t);
                try
                {
                    foreach (var m in t.GetMembersUnordered())
                    {
                        var f = m as FieldSymbol;
                        if (f is null || !f.IsStatic || f.Type.TypeKind != TypeKind.Struct) continue;
                        var type = (NamedTypeSymbol)f.Type;
                        if (InfiniteFlatteningGraph(top, type, instanceMap)) return true;
                    }
                    return false;
                }
                finally
                {
                    instanceMap.Remove(tOriginal);
                }
            }
        }
 
        private void CheckSequentialOnPartialType(BindingDiagnosticBag diagnostics)
        {
            if (!IsPartial || this.Layout.Kind != LayoutKind.Sequential)
            {
                return;
            }
 
            SyntaxReference? whereFoundField = null;
            if (this.SyntaxReferences.Length <= 1)
            {
                return;
            }
 
            foreach (var syntaxRef in this.SyntaxReferences)
            {
                var syntax = syntaxRef.GetSyntax() as TypeDeclarationSyntax;
                if (syntax == null)
                {
                    continue;
                }
 
                foreach (var m in syntax.Members)
                {
                    if (hasInstanceData(m))
                    {
                        if (whereFoundField != null && whereFoundField != syntaxRef)
                        {
                            diagnostics.Add(ErrorCode.WRN_SequentialOnPartialClass, GetFirstLocation(), this);
                            return;
                        }
 
                        whereFoundField = syntaxRef;
                    }
                }
            }
 
            if (whereFoundField != null &&
                PrimaryConstructor is { } primaryConstructor && primaryConstructor.GetCapturedParameters().Any() &&
                (primaryConstructor.SyntaxRef.SyntaxTree != whereFoundField.SyntaxTree || primaryConstructor.SyntaxRef.Span != whereFoundField.Span))
            {
                diagnostics.Add(ErrorCode.WRN_SequentialOnPartialClass, GetFirstLocation(), this);
                return;
            }
 
            static bool hasInstanceData(MemberDeclarationSyntax m)
            {
                switch (m.Kind())
                {
                    case SyntaxKind.FieldDeclaration:
                        var fieldDecl = (FieldDeclarationSyntax)m;
                        return
                            !ContainsModifier(fieldDecl.Modifiers, SyntaxKind.StaticKeyword) &&
                            !ContainsModifier(fieldDecl.Modifiers, SyntaxKind.ConstKeyword);
                    case SyntaxKind.PropertyDeclaration:
                        // auto-property
                        var propertyDecl = (PropertyDeclarationSyntax)m;
                        return
                            !ContainsModifier(propertyDecl.Modifiers, SyntaxKind.StaticKeyword) &&
                            !ContainsModifier(propertyDecl.Modifiers, SyntaxKind.AbstractKeyword) &&
                            !ContainsModifier(propertyDecl.Modifiers, SyntaxKind.ExternKeyword) &&
                            !ContainsModifier(propertyDecl.Modifiers, SyntaxKind.PartialKeyword) &&
                            propertyDecl.AccessorList != null &&
                            All(propertyDecl.AccessorList.Accessors, a => a.Body == null && a.ExpressionBody == null);
                    case SyntaxKind.EventFieldDeclaration:
                        // field-like event declaration
                        var eventFieldDecl = (EventFieldDeclarationSyntax)m;
                        return
                            !ContainsModifier(eventFieldDecl.Modifiers, SyntaxKind.StaticKeyword) &&
                            !ContainsModifier(eventFieldDecl.Modifiers, SyntaxKind.AbstractKeyword) &&
                            !ContainsModifier(eventFieldDecl.Modifiers, SyntaxKind.ExternKeyword);
                    default:
                        return false;
                }
            }
        }
 
        private static bool All<T>(SyntaxList<T> list, Func<T, bool> predicate) where T : CSharpSyntaxNode
        {
            foreach (var t in list) { if (predicate(t)) return true; };
            return false;
        }
 
        private static bool ContainsModifier(SyntaxTokenList modifiers, SyntaxKind modifier)
        {
            foreach (var m in modifiers) { if (m.IsKind(modifier)) return true; };
            return false;
        }
 
        private Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>> MakeAllMembers(BindingDiagnosticBag diagnostics)
        {
            Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>> membersByName;
            var membersAndInitializers = GetMembersAndInitializers();
 
            // Most types don't have indexers.  If this is one of those types,
            // just reuse the dictionary we build for early attribute decoding.
            // For tuples, we also need to take the slow path.
            if (!membersAndInitializers.HaveIndexers && !this.IsTupleType && _lazyEarlyAttributeDecodingMembersDictionary is object)
            {
                membersByName = _lazyEarlyAttributeDecodingMembersDictionary;
            }
            else
            {
                membersByName = ToNameKeyedDictionary(membersAndInitializers.NonTypeMembers);
 
                // Merge types into the member dictionary
                AddNestedTypesToDictionary(membersByName, GetTypeMembersDictionary());
            }
 
            MergePartialMembers(ref membersByName, diagnostics);
 
            return membersByName;
        }
 
        private static void AddNestedTypesToDictionary(
            Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>> membersByName,
            Dictionary<ReadOnlyMemory<char>, ImmutableArray<NamedTypeSymbol>> typesByName)
        {
            foreach ((ReadOnlyMemory<char> name, ImmutableArray<NamedTypeSymbol> types) in typesByName)
            {
                ImmutableArray<Symbol> typesAsSymbols = StaticCast<Symbol>.From(types);
 
                ImmutableArray<Symbol> membersForName;
                if (membersByName.TryGetValue(name, out membersForName))
                {
                    membersByName[name] = membersForName.Concat(typesAsSymbols);
                }
                else
                {
                    membersByName.Add(name, typesAsSymbols);
                }
            }
        }
 
        private sealed class DeclaredMembersAndInitializersBuilder
        {
            public ArrayBuilder<Symbol> NonTypeMembers = ArrayBuilder<Symbol>.GetInstance();
            public readonly ArrayBuilder<ArrayBuilder<FieldOrPropertyInitializer>> StaticInitializers = ArrayBuilder<ArrayBuilder<FieldOrPropertyInitializer>>.GetInstance();
            public readonly ArrayBuilder<ArrayBuilder<FieldOrPropertyInitializer>> InstanceInitializers = ArrayBuilder<ArrayBuilder<FieldOrPropertyInitializer>>.GetInstance();
            public bool HaveIndexers;
            public TypeDeclarationSyntax? DeclarationWithParameters;
 
            public SynthesizedPrimaryConstructor? PrimaryConstructor;
            public bool IsNullableEnabledForInstanceConstructorsAndFields;
            public bool IsNullableEnabledForStaticConstructorsAndFields;
 
            public DeclaredMembersAndInitializers ToReadOnlyAndFree(CSharpCompilation compilation)
            {
                return new DeclaredMembersAndInitializers(
                    NonTypeMembers.ToImmutableAndFree(),
                    MembersAndInitializersBuilder.ToReadOnlyAndFree(StaticInitializers),
                    MembersAndInitializersBuilder.ToReadOnlyAndFree(InstanceInitializers),
                    HaveIndexers,
                    DeclarationWithParameters,
                    PrimaryConstructor,
                    isNullableEnabledForInstanceConstructorsAndFields: IsNullableEnabledForInstanceConstructorsAndFields,
                    isNullableEnabledForStaticConstructorsAndFields: IsNullableEnabledForStaticConstructorsAndFields,
                    compilation);
            }
 
            public void UpdateIsNullableEnabledForConstructorsAndFields(bool useStatic, CSharpCompilation compilation, CSharpSyntaxNode syntax)
            {
                ref bool isNullableEnabled = ref GetIsNullableEnabledForConstructorsAndFields(useStatic);
                isNullableEnabled = isNullableEnabled || compilation.IsNullableAnalysisEnabledIn(syntax);
            }
 
            public void UpdateIsNullableEnabledForConstructorsAndFields(bool useStatic, bool value)
            {
                ref bool isNullableEnabled = ref GetIsNullableEnabledForConstructorsAndFields(useStatic);
                isNullableEnabled = isNullableEnabled || value;
            }
 
            private ref bool GetIsNullableEnabledForConstructorsAndFields(bool useStatic)
            {
                return ref useStatic ? ref IsNullableEnabledForStaticConstructorsAndFields : ref IsNullableEnabledForInstanceConstructorsAndFields;
            }
 
            public void Free()
            {
                NonTypeMembers.Free();
 
                foreach (var group in StaticInitializers)
                {
                    group.Free();
                }
                StaticInitializers.Free();
 
                foreach (var group in InstanceInitializers)
                {
                    group.Free();
                }
                InstanceInitializers.Free();
            }
        }
 
        protected sealed class DeclaredMembersAndInitializers
        {
            public readonly ImmutableArray<Symbol> NonTypeMembers;
            public readonly ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> StaticInitializers;
            public readonly ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> InstanceInitializers;
            public readonly bool HaveIndexers;
            public readonly TypeDeclarationSyntax? DeclarationWithParameters;
            public readonly SynthesizedPrimaryConstructor? PrimaryConstructor;
            public readonly bool IsNullableEnabledForInstanceConstructorsAndFields;
            public readonly bool IsNullableEnabledForStaticConstructorsAndFields;
 
            public static readonly DeclaredMembersAndInitializers UninitializedSentinel = new DeclaredMembersAndInitializers();
 
            private DeclaredMembersAndInitializers()
            {
            }
 
            public DeclaredMembersAndInitializers(
                ImmutableArray<Symbol> nonTypeMembers,
                ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> staticInitializers,
                ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> instanceInitializers,
                bool haveIndexers,
                TypeDeclarationSyntax? declarationWithParameters,
                SynthesizedPrimaryConstructor? primaryConstructor,
                bool isNullableEnabledForInstanceConstructorsAndFields,
                bool isNullableEnabledForStaticConstructorsAndFields,
                CSharpCompilation compilation)
            {
                Debug.Assert(!nonTypeMembers.IsDefault);
                AssertInitializers(staticInitializers, compilation);
                AssertInitializers(instanceInitializers, compilation);
 
                Debug.Assert(!nonTypeMembers.Any(static s => s is TypeSymbol));
                Debug.Assert(declarationWithParameters is object == primaryConstructor is object);
 
                this.NonTypeMembers = nonTypeMembers;
                this.StaticInitializers = staticInitializers;
                this.InstanceInitializers = instanceInitializers;
                this.HaveIndexers = haveIndexers;
                this.DeclarationWithParameters = declarationWithParameters;
                this.PrimaryConstructor = primaryConstructor;
                this.IsNullableEnabledForInstanceConstructorsAndFields = isNullableEnabledForInstanceConstructorsAndFields;
                this.IsNullableEnabledForStaticConstructorsAndFields = isNullableEnabledForStaticConstructorsAndFields;
            }
 
            [Conditional("DEBUG")]
            public static void AssertInitializers(ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> initializers, CSharpCompilation compilation)
            {
                Debug.Assert(!initializers.IsDefault);
                if (initializers.IsEmpty)
                {
                    return;
                }
 
                foreach (ImmutableArray<FieldOrPropertyInitializer> group in initializers)
                {
                    Debug.Assert(!group.IsDefaultOrEmpty);
                }
 
                for (int i = 0; i < initializers.Length; i++)
                {
                    if (i > 0)
                    {
                        Debug.Assert(LexicalSortKey.Compare(new LexicalSortKey(initializers[i - 1].First().Syntax, compilation), new LexicalSortKey(initializers[i].Last().Syntax, compilation)) < 0);
                    }
 
                    if (i + 1 < initializers.Length)
                    {
                        Debug.Assert(LexicalSortKey.Compare(new LexicalSortKey(initializers[i].First().Syntax, compilation), new LexicalSortKey(initializers[i + 1].Last().Syntax, compilation)) < 0);
                    }
 
                    if (initializers[i].Length != 1)
                    {
                        Debug.Assert(LexicalSortKey.Compare(new LexicalSortKey(initializers[i].First().Syntax, compilation), new LexicalSortKey(initializers[i].Last().Syntax, compilation)) < 0);
                    }
                }
            }
        }
 
        private sealed class MembersAndInitializersBuilder
        {
            private ArrayBuilder<Symbol>? NonTypeMembers;
            private ArrayBuilder<FieldOrPropertyInitializer>? InstanceInitializersForPositionalMembers;
            private bool IsNullableEnabledForInstanceConstructorsAndFields;
            private bool IsNullableEnabledForStaticConstructorsAndFields;
 
            public MembersAndInitializersBuilder(DeclaredMembersAndInitializers declaredMembersAndInitializers)
            {
                Debug.Assert(declaredMembersAndInitializers != DeclaredMembersAndInitializers.UninitializedSentinel);
 
                this.IsNullableEnabledForInstanceConstructorsAndFields = declaredMembersAndInitializers.IsNullableEnabledForInstanceConstructorsAndFields;
                this.IsNullableEnabledForStaticConstructorsAndFields = declaredMembersAndInitializers.IsNullableEnabledForStaticConstructorsAndFields;
            }
 
            public MembersAndInitializers ToReadOnlyAndFree(DeclaredMembersAndInitializers declaredMembers)
            {
                var nonTypeMembers = NonTypeMembers?.ToImmutableAndFree() ?? declaredMembers.NonTypeMembers;
 
                var instanceInitializers = InstanceInitializersForPositionalMembers is null
                    ? declaredMembers.InstanceInitializers
                    : mergeInitializers();
 
                return new MembersAndInitializers(
                    declaredMembers.PrimaryConstructor,
                    nonTypeMembers,
                    declaredMembers.StaticInitializers,
                    instanceInitializers,
                    declaredMembers.HaveIndexers,
                    isNullableEnabledForInstanceConstructorsAndFields: IsNullableEnabledForInstanceConstructorsAndFields,
                    isNullableEnabledForStaticConstructorsAndFields: IsNullableEnabledForStaticConstructorsAndFields);
 
                ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> mergeInitializers()
                {
                    Debug.Assert(InstanceInitializersForPositionalMembers.Count != 0);
                    Debug.Assert(declaredMembers.PrimaryConstructor is object);
                    Debug.Assert(declaredMembers.DeclarationWithParameters is object);
                    Debug.Assert(declaredMembers.DeclarationWithParameters.SyntaxTree == InstanceInitializersForPositionalMembers[0].Syntax.SyntaxTree);
                    Debug.Assert(declaredMembers.DeclarationWithParameters.Span.Contains(InstanceInitializersForPositionalMembers[0].Syntax.Span.Start));
 
                    var groupCount = declaredMembers.InstanceInitializers.Length;
 
                    if (groupCount == 0)
                    {
                        return ImmutableArray.Create(InstanceInitializersForPositionalMembers.ToImmutableAndFree());
                    }
 
                    var compilation = declaredMembers.PrimaryConstructor.DeclaringCompilation;
                    var sortKey = new LexicalSortKey(InstanceInitializersForPositionalMembers.First().Syntax, compilation);
 
                    int insertAt;
 
                    for (insertAt = 0; insertAt < groupCount; insertAt++)
                    {
                        if (LexicalSortKey.Compare(sortKey, new LexicalSortKey(declaredMembers.InstanceInitializers[insertAt][0].Syntax, compilation)) < 0)
                        {
                            break;
                        }
                    }
 
                    ArrayBuilder<ImmutableArray<FieldOrPropertyInitializer>> groupsBuilder;
 
                    if (insertAt != groupCount &&
                        declaredMembers.DeclarationWithParameters.SyntaxTree == declaredMembers.InstanceInitializers[insertAt][0].Syntax.SyntaxTree &&
                        declaredMembers.DeclarationWithParameters.Span.Contains(declaredMembers.InstanceInitializers[insertAt][0].Syntax.Span.Start))
                    {
                        // Need to merge into the previous group
                        var declaredInitializers = declaredMembers.InstanceInitializers[insertAt];
                        var insertedInitializers = InstanceInitializersForPositionalMembers;
#if DEBUG
                        // initializers should be added in syntax order:
                        Debug.Assert(insertedInitializers[insertedInitializers.Count - 1].Syntax.SyntaxTree == declaredInitializers[0].Syntax.SyntaxTree);
                        Debug.Assert(insertedInitializers[insertedInitializers.Count - 1].Syntax.Span.Start < declaredInitializers[0].Syntax.Span.Start);
#endif
 
                        insertedInitializers.AddRange(declaredInitializers);
 
                        groupsBuilder = ArrayBuilder<ImmutableArray<FieldOrPropertyInitializer>>.GetInstance(groupCount);
                        groupsBuilder.AddRange(declaredMembers.InstanceInitializers, insertAt);
                        groupsBuilder.Add(insertedInitializers.ToImmutableAndFree());
                        groupsBuilder.AddRange(declaredMembers.InstanceInitializers, insertAt + 1, groupCount - (insertAt + 1));
                        Debug.Assert(groupsBuilder.Count == groupCount);
                    }
                    else
                    {
                        Debug.Assert(!declaredMembers.InstanceInitializers.Any(g => declaredMembers.DeclarationWithParameters.SyntaxTree == g[0].Syntax.SyntaxTree &&
                                                                                    declaredMembers.DeclarationWithParameters.Span.Contains(g[0].Syntax.Span.Start)));
                        groupsBuilder = ArrayBuilder<ImmutableArray<FieldOrPropertyInitializer>>.GetInstance(groupCount + 1);
                        groupsBuilder.AddRange(declaredMembers.InstanceInitializers, insertAt);
                        groupsBuilder.Add(InstanceInitializersForPositionalMembers.ToImmutableAndFree());
                        groupsBuilder.AddRange(declaredMembers.InstanceInitializers, insertAt, groupCount - insertAt);
                        Debug.Assert(groupsBuilder.Count == groupCount + 1);
                    }
 
                    var result = groupsBuilder.ToImmutableAndFree();
 
                    DeclaredMembersAndInitializers.AssertInitializers(result, compilation);
                    return result;
                }
            }
 
            public void AddInstanceInitializerForPositionalMembers(FieldOrPropertyInitializer initializer)
            {
                if (InstanceInitializersForPositionalMembers is null)
                {
                    InstanceInitializersForPositionalMembers = ArrayBuilder<FieldOrPropertyInitializer>.GetInstance();
                }
 
                InstanceInitializersForPositionalMembers.Add(initializer);
            }
 
            public IReadOnlyCollection<Symbol> GetNonTypeMembers(DeclaredMembersAndInitializers declaredMembers)
            {
                return NonTypeMembers ?? (IReadOnlyCollection<Symbol>)declaredMembers.NonTypeMembers;
            }
 
            public void AddNonTypeMember(Symbol member, DeclaredMembersAndInitializers declaredMembers)
            {
                if (NonTypeMembers is null)
                {
                    NonTypeMembers = ArrayBuilder<Symbol>.GetInstance(declaredMembers.NonTypeMembers.Length + 1);
                    NonTypeMembers.AddRange(declaredMembers.NonTypeMembers);
                }
 
                NonTypeMembers.Add(member);
            }
 
            public void SetNonTypeMembers(ArrayBuilder<Symbol> members)
            {
                NonTypeMembers?.Free();
                NonTypeMembers = members;
            }
 
            public void UpdateIsNullableEnabledForConstructorsAndFields(bool useStatic, CSharpCompilation compilation, CSharpSyntaxNode syntax)
            {
                ref bool isNullableEnabled = ref GetIsNullableEnabledForConstructorsAndFields(useStatic);
                isNullableEnabled = isNullableEnabled || compilation.IsNullableAnalysisEnabledIn(syntax);
            }
 
            private ref bool GetIsNullableEnabledForConstructorsAndFields(bool useStatic)
            {
                return ref useStatic ? ref IsNullableEnabledForStaticConstructorsAndFields : ref IsNullableEnabledForInstanceConstructorsAndFields;
            }
 
            internal static ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> ToReadOnlyAndFree(ArrayBuilder<ArrayBuilder<FieldOrPropertyInitializer>> initializers)
            {
                if (initializers.Count == 0)
                {
                    initializers.Free();
                    return ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>>.Empty;
                }
 
                var builder = ArrayBuilder<ImmutableArray<FieldOrPropertyInitializer>>.GetInstance(initializers.Count);
                foreach (ArrayBuilder<FieldOrPropertyInitializer> group in initializers)
                {
                    builder.Add(group.ToImmutableAndFree());
                }
 
                initializers.Free();
                return builder.ToImmutableAndFree();
            }
 
            public void Free()
            {
                NonTypeMembers?.Free();
                InstanceInitializersForPositionalMembers?.Free();
            }
        }
 
        private MembersAndInitializers? BuildMembersAndInitializers(BindingDiagnosticBag diagnostics)
        {
            var declaredMembersAndInitializers = getDeclaredMembersAndInitializers();
            if (declaredMembersAndInitializers is null)
            {
                // Another thread completed the work before this one
                return null;
            }
 
            var membersAndInitializersBuilder = new MembersAndInitializersBuilder(declaredMembersAndInitializers);
            AddSynthesizedMembers(membersAndInitializersBuilder, declaredMembersAndInitializers, diagnostics);
 
            if (Volatile.Read(ref _lazyMembersAndInitializers) != null)
            {
                // Another thread completed the work before this one
                membersAndInitializersBuilder.Free();
                return null;
            }
 
            return membersAndInitializersBuilder.ToReadOnlyAndFree(declaredMembersAndInitializers);
 
            DeclaredMembersAndInitializers? getDeclaredMembersAndInitializers()
            {
                var declaredMembersAndInitializers = _lazyDeclaredMembersAndInitializers;
                if (declaredMembersAndInitializers != DeclaredMembersAndInitializers.UninitializedSentinel)
                {
                    return declaredMembersAndInitializers;
                }
 
                if (Volatile.Read(ref _lazyMembersAndInitializers) is not null)
                {
                    // We're previously computed declared members and already cleared them out
                    // No need to compute them again
                    return null;
                }
 
                var diagnostics = BindingDiagnosticBag.GetInstance();
                declaredMembersAndInitializers = buildDeclaredMembersAndInitializers(diagnostics);
 
                var alreadyKnown = Interlocked.CompareExchange(ref _lazyDeclaredMembersAndInitializers, declaredMembersAndInitializers, DeclaredMembersAndInitializers.UninitializedSentinel);
                if (alreadyKnown != DeclaredMembersAndInitializers.UninitializedSentinel)
                {
                    diagnostics.Free();
                    return alreadyKnown;
                }
 
                AddDeclarationDiagnostics(diagnostics);
                diagnostics.Free();
 
                return declaredMembersAndInitializers!;
            }
 
            // Builds explicitly declared members (as opposed to synthesized members).
            // This should not attempt to bind any method parameters as that would cause
            // the members being built to be captured in the binder cache before the final
            // list of members is determined.
            DeclaredMembersAndInitializers? buildDeclaredMembersAndInitializers(BindingDiagnosticBag diagnostics)
            {
                var builder = new DeclaredMembersAndInitializersBuilder();
                AddDeclaredNontypeMembers(builder, diagnostics);
 
                switch (TypeKind)
                {
                    case TypeKind.Struct:
                        CheckForStructBadInitializers(builder, diagnostics);
                        CheckForStructDefaultConstructors(builder.NonTypeMembers, isEnum: false, diagnostics: diagnostics);
                        break;
 
                    case TypeKind.Enum:
                        CheckForStructDefaultConstructors(builder.NonTypeMembers, isEnum: true, diagnostics: diagnostics);
                        break;
 
                    case TypeKind.Class:
                    case TypeKind.Interface:
                    case TypeKind.Submission:
                        // No additional checking required.
                        break;
 
                    default:
                        break;
                }
 
                if (Volatile.Read(ref _lazyDeclaredMembersAndInitializers) != DeclaredMembersAndInitializers.UninitializedSentinel)
                {
                    // _lazyDeclaredMembersAndInitializers is already computed. no point to continue.
                    builder.Free();
                    return null;
                }
 
                return builder.ToReadOnlyAndFree(DeclaringCompilation);
            }
        }
 
        internal ImmutableArray<SynthesizedSimpleProgramEntryPointSymbol> GetSimpleProgramEntryPoints()
        {
 
            if (_lazySimpleProgramEntryPoints.IsDefault)
            {
                var diagnostics = BindingDiagnosticBag.GetInstance();
                var simpleProgramEntryPoints = buildSimpleProgramEntryPoint(diagnostics);
 
                if (ImmutableInterlocked.InterlockedInitialize(ref _lazySimpleProgramEntryPoints, simpleProgramEntryPoints))
                {
                    AddDeclarationDiagnostics(diagnostics);
                }
 
                diagnostics.Free();
            }
 
            Debug.Assert(!_lazySimpleProgramEntryPoints.IsDefault);
            return _lazySimpleProgramEntryPoints;
 
            ImmutableArray<SynthesizedSimpleProgramEntryPointSymbol> buildSimpleProgramEntryPoint(BindingDiagnosticBag diagnostics)
            {
                if (this.ContainingSymbol is not NamespaceSymbol { IsGlobalNamespace: true }
                    || this.Name != WellKnownMemberNames.TopLevelStatementsEntryPointTypeName)
                {
                    return ImmutableArray<SynthesizedSimpleProgramEntryPointSymbol>.Empty;
                }
 
                ArrayBuilder<SynthesizedSimpleProgramEntryPointSymbol>? builder = null;
 
                foreach (var singleDecl in declaration.Declarations)
                {
                    if (singleDecl.IsSimpleProgram)
                    {
                        if (builder is null)
                        {
                            builder = ArrayBuilder<SynthesizedSimpleProgramEntryPointSymbol>.GetInstance();
                        }
                        else
                        {
                            Binder.Error(diagnostics, ErrorCode.ERR_SimpleProgramMultipleUnitsWithTopLevelStatements, singleDecl.NameLocation);
                        }
 
                        builder.Add(new SynthesizedSimpleProgramEntryPointSymbol(this, singleDecl, diagnostics));
                    }
                }
 
                if (builder is null)
                {
                    return ImmutableArray<SynthesizedSimpleProgramEntryPointSymbol>.Empty;
                }
 
                return builder.ToImmutableAndFree();
            }
        }
 
        internal bool HasPrimaryConstructor => this._flags.HasPrimaryConstructor;
 
        internal SynthesizedPrimaryConstructor? PrimaryConstructor
        {
            get
            {
                if (!HasPrimaryConstructor)
                    return null;
 
                var declared = Volatile.Read(ref _lazyDeclaredMembersAndInitializers);
                SynthesizedPrimaryConstructor? result;
                if (declared is not null && declared != DeclaredMembersAndInitializers.UninitializedSentinel)
                {
                    result = declared.PrimaryConstructor;
                }
                else
                {
                    result = GetMembersAndInitializers().PrimaryConstructor;
                }
 
                Debug.Assert(result is object);
                return result;
            }
        }
 
        internal IEnumerable<SourceMemberMethodSymbol> GetMethodsPossiblyCapturingPrimaryConstructorParameters()
        {
            ImmutableArray<Symbol> nonTypeMembersToCheck;
            SynthesizedPrimaryConstructor? primaryConstructor;
 
            var declared = Volatile.Read(ref _lazyDeclaredMembersAndInitializers);
            if (declared is not null && declared != DeclaredMembersAndInitializers.UninitializedSentinel)
            {
                nonTypeMembersToCheck = declared.NonTypeMembers;
                primaryConstructor = declared.PrimaryConstructor;
            }
            else
            {
                var membersAndInituializers = GetMembersAndInitializers();
                nonTypeMembersToCheck = membersAndInituializers.NonTypeMembers;
                primaryConstructor = membersAndInituializers.PrimaryConstructor;
            }
 
            Debug.Assert(primaryConstructor is not null);
            Debug.Assert(!this.IsDelegateType());
 
            foreach (var member in nonTypeMembersToCheck)
            {
                if ((object)member == primaryConstructor)
                {
                    continue;
                }
 
                if (member.IsStatic ||
                    !(member is MethodSymbol method && MethodCompiler.GetMethodToCompile(method) is SourceMemberMethodSymbol sourceMethod))
                {
                    continue;
                }
 
                if (sourceMethod.IsExtern)
                {
                    continue;
                }
 
                if (sourceMethod.IsAbstract || sourceMethod.SynthesizesLoweredBoundBody)
                {
                    continue;
                }
 
                yield return sourceMethod;
            }
        }
 
        internal ImmutableArray<Symbol> GetMembersToMatchAgainstDeclarationSpan()
        {
            var declared = Volatile.Read(ref _lazyDeclaredMembersAndInitializers);
            if (declared is not null && declared != DeclaredMembersAndInitializers.UninitializedSentinel)
            {
                Debug.Assert(declared.PrimaryConstructor is not null);
                return declared.NonTypeMembers;
            }
            else
            {
                var membersAndInituializers = GetMembersAndInitializers();
                Debug.Assert(membersAndInituializers.PrimaryConstructor is not null);
                return membersAndInituializers.NonTypeMembers;
            }
        }
 
        internal ImmutableArray<Symbol> GetCandidateMembersForLookup(string name)
        {
            if (this is { IsRecord: true } or { IsRecordStruct: true } ||
                this.state.HasComplete(CompletionPart.Members))
            {
                return GetMembers(name);
            }
 
            ImmutableArray<Symbol> nonTypeMembersToCheck;
            SynthesizedPrimaryConstructor? primaryConstructor;
 
            var declared = Volatile.Read(ref _lazyDeclaredMembersAndInitializers);
            if (declared is not null && declared != DeclaredMembersAndInitializers.UninitializedSentinel)
            {
                nonTypeMembersToCheck = declared.NonTypeMembers;
                primaryConstructor = declared.PrimaryConstructor;
            }
            else
            {
                var membersAndInituializers = GetMembersAndInitializers();
                nonTypeMembersToCheck = membersAndInituializers.NonTypeMembers;
                primaryConstructor = membersAndInituializers.PrimaryConstructor;
            }
 
            Debug.Assert(primaryConstructor is not null);
 
            if (primaryConstructor.ParameterCount == 0)
            {
                return GetMembers(name);
            }
 
            ImmutableArray<Symbol> types = GetTypeMembers(name).Cast<NamedTypeSymbol, Symbol>();
            ArrayBuilder<Symbol>? memberBuilder = null;
 
            foreach (var member in nonTypeMembersToCheck)
            {
                if (member.IsAccessor())
                {
                    continue;
                }
 
                if (member.Name == name)
                {
                    memberBuilder ??= ArrayBuilder<Symbol>.GetInstance(types.Length + 1);
                    memberBuilder.Add(member);
                }
            }
 
            if (memberBuilder is null)
            {
                return types;
            }
 
            memberBuilder.AddRange(types);
            return memberBuilder.ToImmutableAndFree();
        }
 
        private void AddSynthesizedMembers(MembersAndInitializersBuilder builder, DeclaredMembersAndInitializers declaredMembersAndInitializers, BindingDiagnosticBag diagnostics)
        {
            if (TypeKind is TypeKind.Class)
            {
                AddSynthesizedSimpleProgramEntryPointIfNecessary(builder, declaredMembersAndInitializers);
            }
 
            switch (TypeKind)
            {
                case TypeKind.Struct:
                case TypeKind.Enum:
                case TypeKind.Class:
                case TypeKind.Interface:
                case TypeKind.Submission:
                    AddSynthesizedTypeMembersIfNecessary(builder, declaredMembersAndInitializers, diagnostics);
                    AddSynthesizedConstructorsIfNecessary(builder, declaredMembersAndInitializers, diagnostics);
                    break;
 
                default:
                    break;
            }
 
            AddSynthesizedTupleMembersIfNecessary(builder, declaredMembersAndInitializers);
        }
 
        private void AddDeclaredNontypeMembers(DeclaredMembersAndInitializersBuilder builder, BindingDiagnosticBag diagnostics)
        {
            foreach (var decl in this.declaration.Declarations)
            {
                if (!decl.HasAnyNontypeMembers)
                {
                    continue;
                }
 
                if (_lazyMembersAndInitializers != null)
                {
                    // membersAndInitializers is already computed. no point to continue.
                    return;
                }
 
                var syntax = decl.SyntaxReference.GetSyntax();
 
                switch (syntax.Kind())
                {
                    case SyntaxKind.EnumDeclaration:
                        AddEnumMembers(builder, (EnumDeclarationSyntax)syntax, diagnostics);
                        break;
 
                    case SyntaxKind.DelegateDeclaration:
                        SourceDelegateMethodSymbol.AddDelegateMembers(this, builder.NonTypeMembers, (DelegateDeclarationSyntax)syntax, diagnostics);
                        break;
 
                    case SyntaxKind.NamespaceDeclaration:
                    case SyntaxKind.FileScopedNamespaceDeclaration:
                        // The members of a global anonymous type is in a syntax tree of a namespace declaration or a compilation unit.
                        AddNonTypeMembers(builder, ((BaseNamespaceDeclarationSyntax)syntax).Members, diagnostics);
                        break;
 
                    case SyntaxKind.CompilationUnit:
                        AddNonTypeMembers(builder, ((CompilationUnitSyntax)syntax).Members, diagnostics);
                        break;
 
                    case SyntaxKind.InterfaceDeclaration:
                        AddNonTypeMembers(builder, ((InterfaceDeclarationSyntax)syntax).Members, diagnostics);
                        break;
 
                    case SyntaxKind.ClassDeclaration:
                    case SyntaxKind.StructDeclaration:
                    case SyntaxKind.RecordDeclaration:
                    case SyntaxKind.RecordStructDeclaration:
                        var typeDecl = (TypeDeclarationSyntax)syntax;
                        noteTypeParameters(typeDecl, builder, diagnostics);
                        AddNonTypeMembers(builder, typeDecl.Members, diagnostics);
                        break;
 
                    default:
                        throw ExceptionUtilities.UnexpectedValue(syntax.Kind());
                }
            }
 
            void noteTypeParameters(TypeDeclarationSyntax syntax, DeclaredMembersAndInitializersBuilder builder, BindingDiagnosticBag diagnostics)
            {
                var parameterList = syntax.ParameterList;
 
                if (parameterList is null)
                {
                    return;
                }
 
                if (builder.DeclarationWithParameters is null)
                {
                    builder.DeclarationWithParameters = syntax;
                    var ctor = new SynthesizedPrimaryConstructor(this, syntax);
 
                    if (this.IsStatic)
                    {
                        diagnostics.Add(ErrorCode.ERR_ConstructorInStaticClass, syntax.Identifier.GetLocation());
                    }
 
                    builder.PrimaryConstructor = ctor;
 
                    var compilation = DeclaringCompilation;
                    builder.UpdateIsNullableEnabledForConstructorsAndFields(ctor.IsStatic, compilation, parameterList);
                    if (syntax is { PrimaryConstructorBaseTypeIfClass: { ArgumentList: { } baseParamList } })
                    {
                        builder.UpdateIsNullableEnabledForConstructorsAndFields(ctor.IsStatic, compilation, baseParamList);
                    }
                }
                else
                {
                    diagnostics.Add(ErrorCode.ERR_MultipleRecordParameterLists, parameterList.Location);
                }
            }
        }
 
        internal Binder GetBinder(CSharpSyntaxNode syntaxNode)
        {
            return this.DeclaringCompilation.GetBinder(syntaxNode);
        }
 
        private void MergePartialMembers(
            ref Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>> membersByName,
            BindingDiagnosticBag diagnostics)
        {
            var memberNames = ArrayBuilder<ReadOnlyMemory<char>>.GetInstance(membersByName.Count);
            memberNames.AddRange(membersByName.Keys);
 
            //key and value will be the same object
            var membersBySignature = new Dictionary<Symbol, Symbol>(MemberSignatureComparer.PartialMethodsComparer);
 
            foreach (var name in memberNames)
            {
                membersBySignature.Clear();
                foreach (var symbol in membersByName[name])
                {
                    if (!symbol.IsPartialMember())
                    {
                        continue;
                    }
 
                    if (!membersBySignature.TryGetValue(symbol, out var prev))
                    {
                        membersBySignature.Add(symbol, symbol);
                        continue;
                    }
 
                    switch (symbol, prev)
                    {
                        case (SourceOrdinaryMethodSymbol currentMethod, SourceOrdinaryMethodSymbol prevMethod):
                            mergePartialMethods(ref membersByName, name, currentMethod, prevMethod, diagnostics);
                            break;
 
                        case (SourcePropertySymbol currentProperty, SourcePropertySymbol prevProperty):
                            mergePartialProperties(ref membersByName, name, currentProperty, prevProperty, diagnostics);
                            break;
 
                        case (SourcePropertyAccessorSymbol, SourcePropertyAccessorSymbol):
                            break; // accessor symbols and their diagnostics are handled by processing the associated property
 
                        default:
                            // This is an error scenario. We simply don't merge the symbols in this case and a duplicate name diagnostic is reported separately.
                            // One way this case can be reached is if type contains both `public partial int P { get; }` and `public partial int P_get();`.
                            Debug.Assert(symbol is SourceOrdinaryMethodSymbol or SourcePropertySymbol or SourcePropertyAccessorSymbol);
                            Debug.Assert(prev is SourceOrdinaryMethodSymbol or SourcePropertySymbol or SourcePropertyAccessorSymbol);
                            break;
                    }
                }
 
                foreach (var symbol in membersBySignature.Values)
                {
                    switch (symbol)
                    {
                        case SourceOrdinaryMethodSymbol method:
                            // partial implementations not paired with a definition
                            if (method.IsPartialImplementation && method.OtherPartOfPartial is null)
                            {
                                diagnostics.Add(ErrorCode.ERR_PartialMethodMustHaveLatent, method.GetFirstLocation(), method);
                            }
                            else if (method is { IsPartialDefinition: true, OtherPartOfPartial: null, HasExplicitAccessModifier: true })
                            {
                                diagnostics.Add(ErrorCode.ERR_PartialMethodWithAccessibilityModsMustHaveImplementation, method.GetFirstLocation(), method);
                            }
                            break;
 
                        case SourcePropertySymbol property:
                            if (property.OtherPartOfPartial is null)
                            {
                                diagnostics.Add(
                                    property.IsPartialDefinition ? ErrorCode.ERR_PartialPropertyMissingImplementation : ErrorCode.ERR_PartialPropertyMissingDefinition,
                                    property.GetFirstLocation(),
                                    property);
                            }
                            break;
 
                        case SourcePropertyAccessorSymbol:
                            break; // diagnostics for missing partial accessors are handled in 'mergePartialProperties'.
 
                        default:
                            throw ExceptionUtilities.UnexpectedValue(symbol);
                    }
                }
            }
 
            memberNames.Free();
 
            void mergePartialMethods(ref Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>> membersByName, ReadOnlyMemory<char> name, SourceOrdinaryMethodSymbol currentMethod, SourceOrdinaryMethodSymbol prevMethod, BindingDiagnosticBag diagnostics)
            {
                if (currentMethod.IsPartialImplementation &&
                    (prevMethod.IsPartialImplementation || (prevMethod.OtherPartOfPartial is MethodSymbol otherImplementation && (object)otherImplementation != currentMethod)))
                {
                    // A partial method may not have multiple implementing declarations
                    diagnostics.Add(ErrorCode.ERR_PartialMethodOnlyOneActual, currentMethod.GetFirstLocation());
                }
                else if (currentMethod.IsPartialDefinition &&
                    (prevMethod.IsPartialDefinition || (prevMethod.OtherPartOfPartial is MethodSymbol otherDefinition && (object)otherDefinition != currentMethod)))
                {
                    // A partial method may not have multiple defining declarations
                    diagnostics.Add(ErrorCode.ERR_PartialMethodOnlyOneLatent, currentMethod.GetFirstLocation());
                }
                else
                {
                    DuplicateMembersByNameIfCached(ref membersByName);
                    membersByName[name] = FixPartialMethod(membersByName[name], prevMethod, currentMethod);
                }
            }
 
            void mergePartialProperties(ref Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>> membersByName, ReadOnlyMemory<char> name, SourcePropertySymbol currentProperty, SourcePropertySymbol prevProperty, BindingDiagnosticBag diagnostics)
            {
                if (currentProperty.IsPartialImplementation &&
                    (prevProperty.IsPartialImplementation || (prevProperty.OtherPartOfPartial is SourcePropertySymbol otherImplementation && (object)otherImplementation != currentProperty)))
                {
                    diagnostics.Add(ErrorCode.ERR_PartialPropertyDuplicateImplementation, currentProperty.GetFirstLocation());
                }
                else if (currentProperty.IsPartialDefinition &&
                    (prevProperty.IsPartialDefinition || (prevProperty.OtherPartOfPartial is SourcePropertySymbol otherDefinition && (object)otherDefinition != currentProperty)))
                {
                    diagnostics.Add(ErrorCode.ERR_PartialPropertyDuplicateDefinition, currentProperty.GetFirstLocation());
                }
                else
                {
                    if (hasInitializer(prevProperty) && hasInitializer(currentProperty))
                    {
                        diagnostics.Add(ErrorCode.ERR_PartialPropertyDuplicateInitializer, currentProperty.GetFirstLocation());
                    }
 
                    DuplicateMembersByNameIfCached(ref membersByName);
                    mergeAccessors(ref membersByName, (SourcePropertyAccessorSymbol?)currentProperty.GetMethod, (SourcePropertyAccessorSymbol?)prevProperty.GetMethod);
                    mergeAccessors(ref membersByName, (SourcePropertyAccessorSymbol?)currentProperty.SetMethod, (SourcePropertyAccessorSymbol?)prevProperty.SetMethod);
                    FixPartialProperty(ref membersByName, name, prevProperty, currentProperty);
                }
 
                void mergeAccessors(ref Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>> membersByName, SourcePropertyAccessorSymbol? currentAccessor, SourcePropertyAccessorSymbol? prevAccessor)
                {
                    if (currentAccessor is { } && prevAccessor is { })
                    {
                        var name = currentAccessor.Name.AsMemory();
                        var implementationAccessor = currentProperty.IsPartialDefinition ? prevAccessor : currentAccessor;
                        membersByName[name] = Remove(membersByName[name], implementationAccessor);
                    }
                    else if (currentAccessor is { } || prevAccessor is { })
                    {
                        var (foundAccessor, containingProperty, otherProperty) = prevAccessor is { } ? (prevAccessor, prevProperty, currentProperty) : (currentAccessor!, currentProperty, prevProperty);
                        // When an accessor is present on definition but not on implementation, the accessor is said to be missing on the implementation.
                        // When an accessor is present on implementation but not on definition, the accessor is said to be unexpected on the implementation.
                        var (errorCode, propertyToBlame) = foundAccessor.IsPartialDefinition
                            ? (ErrorCode.ERR_PartialPropertyMissingAccessor, otherProperty)
                            : (ErrorCode.ERR_PartialPropertyUnexpectedAccessor, containingProperty);
                        diagnostics.Add(errorCode, propertyToBlame.GetFirstLocation(), foundAccessor);
                    }
                }
 
                static bool hasInitializer(SourcePropertySymbol property)
                {
                    return property.DeclaredBackingField?.HasInitializer == true;
                }
            }
        }
 
        private void DuplicateMembersByNameIfCached(ref Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>> membersByName)
        {
            if ((object)membersByName == _lazyEarlyAttributeDecodingMembersDictionary)
            {
                // Avoid mutating the cached dictionary and especially avoid doing this possibly on multiple threads in parallel.
                membersByName = new Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>>(membersByName, ReadOnlyMemoryOfCharComparer.Instance);
            }
        }
 
        /// <summary>Links together the definition and implementation parts of a partial method. Returns a member list which has the implementation part removed.</summary>
        private static ImmutableArray<Symbol> FixPartialMethod(ImmutableArray<Symbol> symbols, SourceOrdinaryMethodSymbol part1, SourceOrdinaryMethodSymbol part2)
        {
            SourceOrdinaryMethodSymbol definition;
            SourceOrdinaryMethodSymbol implementation;
            if (part1.IsPartialDefinition)
            {
                definition = part1;
                implementation = part2;
            }
            else
            {
                definition = part2;
                implementation = part1;
            }
 
            SourceOrdinaryMethodSymbol.InitializePartialMethodParts(definition, implementation);
 
            // a partial method is represented in the member list by its definition part:
            return Remove(symbols, implementation);
        }
 
        /// <summary>Links together the definition and implementation parts of a partial property. Returns a member list which has the implementation part removed.</summary>
        private static void FixPartialProperty(ref Dictionary<ReadOnlyMemory<char>, ImmutableArray<Symbol>> membersByName, ReadOnlyMemory<char> name, SourcePropertySymbol part1, SourcePropertySymbol part2)
        {
            SourcePropertySymbol definition;
            SourcePropertySymbol implementation;
            if (part1.IsPartialDefinition)
            {
                definition = part1;
                implementation = part2;
            }
            else
            {
                definition = part2;
                implementation = part1;
            }
 
            if (implementation.DeclaredBackingField is { } implementationField &&
                definition.DeclaredBackingField is { })
            {
                var fieldName = implementationField.Name.AsMemory();
                membersByName[fieldName] = Remove(membersByName[fieldName], implementationField);
            }
 
            SourcePropertySymbol.InitializePartialPropertyParts(definition, implementation);
 
            // a partial property is represented in the member list by its definition part:
            membersByName[name] = Remove(membersByName[name], implementation);
        }
 
        private static ImmutableArray<Symbol> Remove(ImmutableArray<Symbol> symbols, Symbol symbol)
        {
            var builder = ArrayBuilder<Symbol>.GetInstance();
            foreach (var s in symbols)
            {
                if (!ReferenceEquals(s, symbol))
                {
                    builder.Add(s);
                }
            }
            return builder.ToImmutableAndFree();
        }
 
        /// <summary>
        /// Report an error if a member (other than a method) exists with the same name
        /// as the property accessor, or if a method exists with the same name and signature.
        /// </summary>
        private void CheckForMemberConflictWithPropertyAccessor(
            PropertySymbol propertySymbol,
            bool getNotSet,
            BindingDiagnosticBag diagnostics)
        {
            Debug.Assert(!propertySymbol.IsExplicitInterfaceImplementation); // checked by caller
 
            MethodSymbol accessor = getNotSet ? propertySymbol.GetMethod : propertySymbol.SetMethod;
            string accessorName;
            if ((object)accessor != null)
            {
                accessorName = accessor.Name;
            }
            else
            {
                string propertyName = propertySymbol.IsIndexer ? propertySymbol.MetadataName : propertySymbol.Name;
                accessorName = SourcePropertyAccessorSymbol.GetAccessorName(propertyName,
                    getNotSet,
                    propertySymbol.IsCompilationOutputWinMdObj());
            }
 
            foreach (var symbol in GetMembers(accessorName))
            {
                if (symbol.Kind != SymbolKind.Method)
                {
                    // The type '{0}' already contains a definition for '{1}'
                    if (Locations.Length == 1 || IsPartial)
                        diagnostics.Add(ErrorCode.ERR_DuplicateNameInClass, GetAccessorOrPropertyLocation(propertySymbol, getNotSet), this, accessorName);
                    return;
                }
                else
                {
                    var methodSymbol = (MethodSymbol)symbol;
                    if ((methodSymbol.MethodKind == MethodKind.Ordinary) &&
                        ParametersMatchPropertyAccessor(propertySymbol, getNotSet, methodSymbol.Parameters))
                    {
                        // Type '{1}' already reserves a member called '{0}' with the same parameter types
                        diagnostics.Add(ErrorCode.ERR_MemberReserved, GetAccessorOrPropertyLocation(propertySymbol, getNotSet), accessorName, this);
                        return;
                    }
                }
            }
        }
 
        /// <summary>
        /// Report an error if a member (other than a method) exists with the same name
        /// as the event accessor, or if a method exists with the same name and signature.
        /// </summary>
        private void CheckForMemberConflictWithEventAccessor(
            EventSymbol eventSymbol,
            bool isAdder,
            BindingDiagnosticBag diagnostics)
        {
            Debug.Assert(!eventSymbol.IsExplicitInterfaceImplementation); // checked by caller
 
            string accessorName = SourceEventSymbol.GetAccessorName(eventSymbol.Name, isAdder);
 
            foreach (var symbol in GetMembers(accessorName))
            {
                if (symbol.Kind != SymbolKind.Method)
                {
                    // The type '{0}' already contains a definition for '{1}'
                    if (Locations.Length == 1 || IsPartial)
                        diagnostics.Add(ErrorCode.ERR_DuplicateNameInClass, GetAccessorOrEventLocation(eventSymbol, isAdder), this, accessorName);
                    return;
                }
                else
                {
                    var methodSymbol = (MethodSymbol)symbol;
                    if ((methodSymbol.MethodKind == MethodKind.Ordinary) &&
                        ParametersMatchEventAccessor(eventSymbol, methodSymbol.Parameters))
                    {
                        // Type '{1}' already reserves a member called '{0}' with the same parameter types
                        diagnostics.Add(ErrorCode.ERR_MemberReserved, GetAccessorOrEventLocation(eventSymbol, isAdder), accessorName, this);
                        return;
                    }
                }
            }
        }
 
        /// <summary>
        /// Return the location of the accessor, or if no accessor, the location of the property.
        /// </summary>
        private static Location GetAccessorOrPropertyLocation(PropertySymbol propertySymbol, bool getNotSet)
        {
            var locationFrom = (Symbol)(getNotSet ? propertySymbol.GetMethod : propertySymbol.SetMethod) ?? propertySymbol;
            return locationFrom.GetFirstLocation();
        }
 
        /// <summary>
        /// Return the location of the accessor, or if no accessor, the location of the event.
        /// </summary>
        private static Location GetAccessorOrEventLocation(EventSymbol propertySymbol, bool isAdder)
        {
            var locationFrom = (Symbol?)(isAdder ? propertySymbol.AddMethod : propertySymbol.RemoveMethod) ?? propertySymbol;
            return locationFrom.GetFirstLocation();
        }
 
        /// <summary>
        /// Return true if the method parameters match the parameters of the
        /// property accessor, including the value parameter for the setter.
        /// </summary>
        private static bool ParametersMatchPropertyAccessor(PropertySymbol propertySymbol, bool getNotSet, ImmutableArray<ParameterSymbol> methodParams)
        {
            var propertyParams = propertySymbol.Parameters;
            var numParams = propertyParams.Length + (getNotSet ? 0 : 1);
            if (numParams != methodParams.Length)
            {
                return false;
            }
 
            for (int i = 0; i < numParams; i++)
            {
                var methodParam = methodParams[i];
                if (methodParam.RefKind != RefKind.None)
                {
                    return false;
                }
 
                var propertyParamType = (((i == numParams - 1) && !getNotSet) ? propertySymbol.TypeWithAnnotations : propertyParams[i].TypeWithAnnotations).Type;
                if (!propertyParamType.Equals(methodParam.Type, TypeCompareKind.AllIgnoreOptions))
                {
                    return false;
                }
            }
 
            return true;
        }
 
        /// <summary>
        /// Return true if the method parameters match the parameters of the
        /// event accessor, including the value parameter.
        /// </summary>
        private static bool ParametersMatchEventAccessor(EventSymbol eventSymbol, ImmutableArray<ParameterSymbol> methodParams)
        {
            return
                methodParams.Length == 1 &&
                methodParams[0].RefKind == RefKind.None &&
                eventSymbol.Type.Equals(methodParams[0].Type, TypeCompareKind.AllIgnoreOptions);
        }
 
        private void AddEnumMembers(DeclaredMembersAndInitializersBuilder result, EnumDeclarationSyntax syntax, BindingDiagnosticBag diagnostics)
        {
            // The previous enum constant used to calculate subsequent
            // implicit enum constants. (This is the most recent explicit
            // enum constant or the first implicit constant if no explicit values.)
            SourceEnumConstantSymbol? otherSymbol = null;
 
            // Offset from "otherSymbol".
            int otherSymbolOffset = 0;
 
            foreach (var member in syntax.Members)
            {
                SourceEnumConstantSymbol symbol;
                var valueOpt = member.EqualsValue;
 
                if (valueOpt != null)
                {
                    symbol = SourceEnumConstantSymbol.CreateExplicitValuedConstant(this, member, diagnostics);
                }
                else
                {
                    symbol = SourceEnumConstantSymbol.CreateImplicitValuedConstant(this, member, otherSymbol, otherSymbolOffset, diagnostics);
                }
 
                result.NonTypeMembers.Add(symbol);
 
                if (valueOpt != null || otherSymbol is null)
                {
                    otherSymbol = symbol;
                    otherSymbolOffset = 1;
                }
                else
                {
                    otherSymbolOffset++;
                }
            }
        }
 
        private static void AddInitializer(ref ArrayBuilder<FieldOrPropertyInitializer>? initializers, FieldSymbol? fieldOpt, CSharpSyntaxNode node)
        {
            if (initializers == null)
            {
                initializers = ArrayBuilder<FieldOrPropertyInitializer>.GetInstance();
            }
            else if (initializers.Count != 0)
            {
                // initializers should be added in syntax order:
                Debug.Assert(node.SyntaxTree == initializers.Last().Syntax.SyntaxTree);
                Debug.Assert(node.SpanStart > initializers.Last().Syntax.Span.Start);
            }
 
            initializers.Add(new FieldOrPropertyInitializer(fieldOpt, node));
        }
 
        private static void AddInitializers(
            ArrayBuilder<ArrayBuilder<FieldOrPropertyInitializer>> allInitializers,
            ArrayBuilder<FieldOrPropertyInitializer>? siblingsOpt)
        {
            if (siblingsOpt != null)
            {
                allInitializers.Add(siblingsOpt);
            }
        }
 
        private static void CheckInterfaceMembers(ImmutableArray<Symbol> nonTypeMembers, BindingDiagnosticBag diagnostics)
        {
            foreach (var member in nonTypeMembers)
            {
                CheckInterfaceMember(member, diagnostics);
            }
        }
 
        private static void CheckInterfaceMember(Symbol member, BindingDiagnosticBag diagnostics)
        {
            switch (member.Kind)
            {
                case SymbolKind.Field:
                    break;
 
                case SymbolKind.Method:
                    var meth = (MethodSymbol)member;
                    switch (meth.MethodKind)
                    {
                        case MethodKind.Constructor:
                            diagnostics.Add(ErrorCode.ERR_InterfacesCantContainConstructors, member.GetFirstLocation());
                            break;
                        case MethodKind.Conversion:
                            break;
                        case MethodKind.UserDefinedOperator:
                            break;
                        case MethodKind.Destructor:
                            diagnostics.Add(ErrorCode.ERR_OnlyClassesCanContainDestructors, member.GetFirstLocation());
                            break;
                        case MethodKind.ExplicitInterfaceImplementation:
                        //CS0541 is handled in SourcePropertySymbol
                        case MethodKind.Ordinary:
                        case MethodKind.LocalFunction:
                        case MethodKind.PropertyGet:
                        case MethodKind.PropertySet:
                        case MethodKind.EventAdd:
                        case MethodKind.EventRemove:
                        case MethodKind.StaticConstructor:
                            break;
                        default:
                            throw ExceptionUtilities.UnexpectedValue(meth.MethodKind);
                    }
                    break;
 
                case SymbolKind.Property:
                    break;
 
                case SymbolKind.Event:
                    break;
 
                default:
                    throw ExceptionUtilities.UnexpectedValue(member.Kind);
            }
        }
 
        private static void CheckForStructDefaultConstructors(
            ArrayBuilder<Symbol> members,
            bool isEnum,
            BindingDiagnosticBag diagnostics)
        {
            foreach (var s in members)
            {
                var m = s as MethodSymbol;
                if (!(m is null))
                {
                    if (m.MethodKind == MethodKind.Constructor && m.ParameterCount == 0)
                    {
                        var location = m.GetFirstLocation();
                        if (isEnum)
                        {
                            diagnostics.Add(ErrorCode.ERR_EnumsCantContainDefaultConstructor, location);
                        }
                        else
                        {
                            MessageID.IDS_FeatureParameterlessStructConstructors.CheckFeatureAvailability(diagnostics, m.DeclaringCompilation, location);
                            if (m.DeclaredAccessibility != Accessibility.Public)
                            {
                                diagnostics.Add(ErrorCode.ERR_NonPublicParameterlessStructConstructor, location);
                            }
                        }
                    }
                }
            }
        }
 
        private void CheckForStructBadInitializers(DeclaredMembersAndInitializersBuilder builder, BindingDiagnosticBag diagnostics)
        {
            Debug.Assert(TypeKind == TypeKind.Struct);
 
            if (builder.DeclarationWithParameters is not null)
            {
                Debug.Assert(builder.DeclarationWithParameters is TypeDeclarationSyntax { ParameterList: not null } type
                    && type.Kind() is (SyntaxKind.RecordStructDeclaration or SyntaxKind.StructDeclaration));
                return;
            }
 
            bool hasInitializers = false;
            foreach (var initializers in builder.InstanceInitializers)
            {
                foreach (FieldOrPropertyInitializer initializer in initializers)
                {
                    hasInitializers = true;
                    var symbol = initializer.FieldOpt.AssociatedSymbol ?? initializer.FieldOpt;
                    MessageID.IDS_FeatureStructFieldInitializers.CheckFeatureAvailability(diagnostics, symbol.DeclaringCompilation, symbol.GetFirstLocation());
                }
            }
 
            if (hasInitializers && !builder.NonTypeMembers.Any(member => member is MethodSymbol { MethodKind: MethodKind.Constructor }))
            {
                diagnostics.Add(ErrorCode.ERR_StructHasInitializersAndNoDeclaredConstructor, GetFirstLocation());
            }
        }
 
        private void AddSynthesizedSimpleProgramEntryPointIfNecessary(MembersAndInitializersBuilder builder, DeclaredMembersAndInitializers declaredMembersAndInitializers)
        {
            var simpleProgramEntryPoints = GetSimpleProgramEntryPoints();
            foreach (var member in simpleProgramEntryPoints)
            {
                builder.AddNonTypeMember(member, declaredMembersAndInitializers);
            }
        }
 
        private void AddSynthesizedTypeMembersIfNecessary(MembersAndInitializersBuilder builder, DeclaredMembersAndInitializers declaredMembersAndInitializers, BindingDiagnosticBag diagnostics)
        {
            if (declaration.Kind is not (DeclarationKind.Record or DeclarationKind.RecordStruct) && declaredMembersAndInitializers.PrimaryConstructor is null)
            {
                return;
            }
 
            var membersSoFar = builder.GetNonTypeMembers(declaredMembersAndInitializers);
            var members = ArrayBuilder<Symbol>.GetInstance(membersSoFar.Count + 1);
 
            if (declaration.Kind is not (DeclarationKind.Record or DeclarationKind.RecordStruct))
            {
                // primary ctor
                var ctor = declaredMembersAndInitializers.PrimaryConstructor;
                Debug.Assert(ctor is object);
 
                members.Add(ctor);
                members.AddRange(ctor.GetBackingFields());
                members.AddRange(membersSoFar);
                builder.SetNonTypeMembers(members);
 
                return;
            }
 
            Debug.Assert(declaredMembersAndInitializers.PrimaryConstructor?.GetBackingFields().Any() != true);
 
            ParameterListSyntax? paramList = declaredMembersAndInitializers.DeclarationWithParameters?.ParameterList;
            var memberSignatures = s_duplicateRecordMemberSignatureDictionary.Allocate();
            var fieldsByName = PooledDictionary<string, Symbol>.GetInstance();
            var memberNames = PooledHashSet<string>.GetInstance();
            foreach (var member in membersSoFar)
            {
                memberNames.Add(member.Name);
 
                switch (member)
                {
                    case EventSymbol:
                    case MethodSymbol { MethodKind: not (MethodKind.Ordinary or MethodKind.Constructor) }:
                        continue;
                    case FieldSymbol { Name: var fieldName }:
                        if (!fieldsByName.ContainsKey(fieldName))
                        {
                            fieldsByName.Add(fieldName, member);
                        }
                        continue;
                }
 
                if (!memberSignatures.ContainsKey(member))
                {
                    memberSignatures.Add(member, member);
                }
            }
 
            CSharpCompilation compilation = this.DeclaringCompilation;
            bool isRecordClass = declaration.Kind == DeclarationKind.Record;
 
            // Positional record
            bool primaryAndCopyCtorAmbiguity = false;
            if (!(paramList is null))
            {
                Debug.Assert(declaredMembersAndInitializers.DeclarationWithParameters is object);
 
                // primary ctor
                var ctor = declaredMembersAndInitializers.PrimaryConstructor;
                Debug.Assert(ctor is object);
                members.Add(ctor);
 
                if (ctor.ParameterCount != 0)
                {
                    // properties and Deconstruct
                    var existingOrAddedMembers = addProperties(ctor.Parameters);
                    addDeconstruct(ctor, existingOrAddedMembers);
                }
 
                if (isRecordClass)
                {
                    primaryAndCopyCtorAmbiguity = ctor.ParameterCount == 1 && ctor.Parameters[0].Type.Equals(this, TypeCompareKind.AllIgnoreOptions);
                }
            }
 
            if (isRecordClass)
            {
                addCopyCtor(primaryAndCopyCtorAmbiguity);
                addCloneMethod();
            }
 
            PropertySymbol? equalityContract = isRecordClass ? addEqualityContract() : null;
 
            var thisEquals = addThisEquals(equalityContract);
 
            if (isRecordClass)
            {
                addBaseEquals();
            }
 
            addObjectEquals(thisEquals);
            var getHashCode = addGetHashCode(equalityContract);
            addEqualityOperators();
 
            if (thisEquals is not SynthesizedRecordEquals && getHashCode is SynthesizedRecordGetHashCode)
            {
                diagnostics.Add(ErrorCode.WRN_RecordEqualsWithoutGetHashCode, thisEquals.GetFirstLocation(), declaration.Name);
            }
 
            var printMembers = addPrintMembersMethod(membersSoFar);
            addToStringMethod(printMembers);
 
            memberSignatures.Free();
            fieldsByName.Free();
            memberNames.Free();
 
            // Synthesizing non-readonly properties in struct would require changing readonly logic for PrintMembers method synthesis
            Debug.Assert(isRecordClass || !members.Any(m => m is PropertySymbol { GetMethod.IsEffectivelyReadOnly: false }));
 
            // We put synthesized record members first so that errors about conflicts show up on user-defined members rather than all
            // going to the record declaration
            members.AddRange(membersSoFar);
            builder.SetNonTypeMembers(members);
 
            return;
 
            void addDeconstruct(SynthesizedPrimaryConstructor ctor, ImmutableArray<Symbol> positionalMembers)
            {
                Debug.Assert(positionalMembers.All(p => p is PropertySymbol or FieldSymbol));
 
                var targetMethod = new SignatureOnlyMethodSymbol(
                    WellKnownMemberNames.DeconstructMethodName,
                    this,
                    MethodKind.Ordinary,
                    Cci.CallingConvention.HasThis,
                    ImmutableArray<TypeParameterSymbol>.Empty,
                    ctor.Parameters.SelectAsArray<ParameterSymbol, ParameterSymbol>(param => new SignatureOnlyParameterSymbol(param.TypeWithAnnotations,
                                                                                                                              ImmutableArray<CustomModifier>.Empty,
                                                                                                                              isParamsArray: false,
                                                                                                                              isParamsCollection: false,
                                                                                                                              RefKind.Out
                                                                                                                              )),
                    RefKind.None,
                    isInitOnly: false,
                    isStatic: false,
                    TypeWithAnnotations.Create(compilation.GetSpecialType(SpecialType.System_Void)),
                    ImmutableArray<CustomModifier>.Empty,
                    ImmutableArray<MethodSymbol>.Empty);
 
                if (!memberSignatures.TryGetValue(targetMethod, out Symbol? existingDeconstructMethod))
                {
                    members.Add(new SynthesizedRecordDeconstruct(this, ctor, positionalMembers, memberOffset: members.Count));
                }
                else
                {
                    var deconstruct = (MethodSymbol)existingDeconstructMethod;
 
                    if (deconstruct.DeclaredAccessibility != Accessibility.Public)
                    {
                        diagnostics.Add(ErrorCode.ERR_NonPublicAPIInRecord, deconstruct.GetFirstLocation(), deconstruct);
                    }
 
                    if (deconstruct.ReturnType.SpecialType != SpecialType.System_Void && !deconstruct.ReturnType.IsErrorType())
                    {
                        diagnostics.Add(ErrorCode.ERR_SignatureMismatchInRecord, deconstruct.GetFirstLocation(), deconstruct, targetMethod.ReturnType);
                    }
 
                    if (deconstruct.IsStatic)
                    {
                        diagnostics.Add(ErrorCode.ERR_StaticAPIInRecord, deconstruct.GetFirstLocation(), deconstruct);
                    }
                }
            }
 
            void addCopyCtor(bool primaryAndCopyCtorAmbiguity)
            {
                Debug.Assert(isRecordClass);
                var targetMethod = new SignatureOnlyMethodSymbol(
                    WellKnownMemberNames.InstanceConstructorName,
                    this,
                    MethodKind.Constructor,
                    Cci.CallingConvention.HasThis,
                    ImmutableArray<TypeParameterSymbol>.Empty,
                    ImmutableArray.Create<ParameterSymbol>(new SignatureOnlyParameterSymbol(
                                                                TypeWithAnnotations.Create(this),
                                                                ImmutableArray<CustomModifier>.Empty,
                                                                isParamsArray: false,
                                                                isParamsCollection: false,
                                                                RefKind.None
                                                                )),
                    RefKind.None,
                    isInitOnly: false,
                    isStatic: false,
                    TypeWithAnnotations.Create(compilation.GetSpecialType(SpecialType.System_Void)),
                    ImmutableArray<CustomModifier>.Empty,
                    ImmutableArray<MethodSymbol>.Empty);
 
                if (!memberSignatures.TryGetValue(targetMethod, out Symbol? existingConstructor))
                {
                    var copyCtor = new SynthesizedRecordCopyCtor(this, memberOffset: members.Count);
                    members.Add(copyCtor);
 
                    if (primaryAndCopyCtorAmbiguity)
                    {
                        diagnostics.Add(ErrorCode.ERR_RecordAmbigCtor, copyCtor.GetFirstLocation());
                    }
                }
                else
                {
                    var constructor = (MethodSymbol)existingConstructor;
 
                    if (!this.IsSealed && (constructor.DeclaredAccessibility != Accessibility.Public && constructor.DeclaredAccessibility != Accessibility.Protected))
                    {
                        diagnostics.Add(ErrorCode.ERR_CopyConstructorWrongAccessibility, constructor.GetFirstLocation(), constructor);
                    }
                }
            }
 
            void addCloneMethod()
            {
                Debug.Assert(isRecordClass);
                members.Add(new SynthesizedRecordClone(this, memberOffset: members.Count));
            }
 
            MethodSymbol addPrintMembersMethod(IEnumerable<Symbol> userDefinedMembers)
            {
                var targetMethod = new SignatureOnlyMethodSymbol(
                    WellKnownMemberNames.PrintMembersMethodName,
                    this,
                    MethodKind.Ordinary,
                    Cci.CallingConvention.HasThis,
                    ImmutableArray<TypeParameterSymbol>.Empty,
                    ImmutableArray.Create<ParameterSymbol>(new SignatureOnlyParameterSymbol(
                        TypeWithAnnotations.Create(compilation.GetWellKnownType(WellKnownType.System_Text_StringBuilder)),
                        ImmutableArray<CustomModifier>.Empty,
                        isParamsArray: false,
                        isParamsCollection: false,
                        RefKind.None)),
                    RefKind.None,
                    isInitOnly: false,
                    isStatic: false,
                    returnType: TypeWithAnnotations.Create(compilation.GetSpecialType(SpecialType.System_Boolean)),
                    refCustomModifiers: ImmutableArray<CustomModifier>.Empty,
                    explicitInterfaceImplementations: ImmutableArray<MethodSymbol>.Empty);
 
                MethodSymbol printMembersMethod;
                if (!memberSignatures.TryGetValue(targetMethod, out Symbol? existingPrintMembersMethod))
                {
                    printMembersMethod = new SynthesizedRecordPrintMembers(this, userDefinedMembers, memberOffset: members.Count);
                    members.Add(printMembersMethod);
                }
                else
                {
                    printMembersMethod = (MethodSymbol)existingPrintMembersMethod;
                    if (!isRecordClass || (this.IsSealed && this.BaseTypeNoUseSiteDiagnostics.IsObjectType()))
                    {
                        if (printMembersMethod.DeclaredAccessibility != Accessibility.Private)
                        {
                            diagnostics.Add(ErrorCode.ERR_NonPrivateAPIInRecord, printMembersMethod.GetFirstLocation(), printMembersMethod);
                        }
                    }
                    else if (printMembersMethod.DeclaredAccessibility != Accessibility.Protected)
                    {
                        diagnostics.Add(ErrorCode.ERR_NonProtectedAPIInRecord, printMembersMethod.GetFirstLocation(), printMembersMethod);
                    }
 
                    if (!printMembersMethod.ReturnType.Equals(targetMethod.ReturnType, TypeCompareKind.AllIgnoreOptions))
                    {
                        if (!printMembersMethod.ReturnType.IsErrorType())
                        {
                            diagnostics.Add(ErrorCode.ERR_SignatureMismatchInRecord, printMembersMethod.GetFirstLocation(), printMembersMethod, targetMethod.ReturnType);
                        }
                    }
                    else if (isRecordClass)
                    {
                        SynthesizedRecordPrintMembers.VerifyOverridesPrintMembersFromBase(printMembersMethod, diagnostics);
                    }
 
                    reportStaticOrNotOverridableAPIInRecord(printMembersMethod, diagnostics);
                }
 
                return printMembersMethod;
            }
 
            void addToStringMethod(MethodSymbol printMethod)
            {
                var targetMethod = new SignatureOnlyMethodSymbol(
                    WellKnownMemberNames.ObjectToString,
                    this,
                    MethodKind.Ordinary,
                    Cci.CallingConvention.HasThis,
                    ImmutableArray<TypeParameterSymbol>.Empty,
                    ImmutableArray<ParameterSymbol>.Empty,
                    RefKind.None,
                    isInitOnly: false,
                    isStatic: false,
                    returnType: TypeWithAnnotations.Create(compilation.GetSpecialType(SpecialType.System_String)),
                    refCustomModifiers: ImmutableArray<CustomModifier>.Empty,
                    explicitInterfaceImplementations: ImmutableArray<MethodSymbol>.Empty);
 
                var baseToStringMethod = getBaseToStringMethod();
 
                if (baseToStringMethod is { IsSealed: true })
                {
                    if (baseToStringMethod.ContainingModule != this.ContainingModule && !this.DeclaringCompilation.IsFeatureEnabled(MessageID.IDS_FeatureSealedToStringInRecord))
                    {
                        var languageVersion = ((CSharpParseOptions)this.GetFirstLocation().SourceTree!.Options).LanguageVersion;
                        var requiredVersion = MessageID.IDS_FeatureSealedToStringInRecord.RequiredVersion();
                        diagnostics.Add(
                            ErrorCode.ERR_InheritingFromRecordWithSealedToString,
                            this.GetFirstLocation(),
                            languageVersion.ToDisplayString(),
                            new CSharpRequiredLanguageVersion(requiredVersion));
                    }
                }
                else
                {
                    if (!memberSignatures.TryGetValue(targetMethod, out Symbol? existingToStringMethod))
                    {
                        var toStringMethod = new SynthesizedRecordToString(
                            this,
                            printMethod,
                            memberOffset: members.Count);
                        members.Add(toStringMethod);
                    }
                    else
                    {
                        var toStringMethod = (MethodSymbol)existingToStringMethod;
                        if (!SynthesizedRecordObjectMethod.VerifyOverridesMethodFromObject(toStringMethod, SpecialMember.System_Object__ToString, diagnostics) && toStringMethod.IsSealed && !IsSealed)
                        {
                            MessageID.IDS_FeatureSealedToStringInRecord.CheckFeatureAvailability(
                                diagnostics,
                                this.DeclaringCompilation,
                                toStringMethod.GetFirstLocation());
                        }
                    }
                }
 
                MethodSymbol? getBaseToStringMethod()
                {
                    var objectToString = this.DeclaringCompilation.GetSpecialTypeMember(SpecialMember.System_Object__ToString);
                    var currentBaseType = this.BaseTypeNoUseSiteDiagnostics;
                    while (currentBaseType is not null)
                    {
                        foreach (var member in currentBaseType.GetSimpleNonTypeMembers(WellKnownMemberNames.ObjectToString))
                        {
                            if (member is not MethodSymbol method)
                                continue;
 
                            if (method.GetLeastOverriddenMethod(null) == objectToString)
                                return method;
                        }
 
                        currentBaseType = currentBaseType.BaseTypeNoUseSiteDiagnostics;
                    }
 
                    return null;
                }
            }
 
            ImmutableArray<Symbol> addProperties(ImmutableArray<ParameterSymbol> recordParameters)
            {
                var existingOrAddedMembers = ArrayBuilder<Symbol>.GetInstance(recordParameters.Length);
                int addedCount = 0;
                foreach (ParameterSymbol param in recordParameters)
                {
                    bool isInherited = false;
                    var syntax = param.GetNonNullSyntaxNode();
 
                    var targetProperty = new SignatureOnlyPropertySymbol(param.Name,
                                                                         this,
                                                                         ImmutableArray<ParameterSymbol>.Empty,
                                                                         RefKind.None,
                                                                         param.TypeWithAnnotations,
                                                                         ImmutableArray<CustomModifier>.Empty,
                                                                         isStatic: false,
                                                                         ImmutableArray<PropertySymbol>.Empty);
                    if (!memberSignatures.TryGetValue(targetProperty, out var existingMember)
                        && !fieldsByName.TryGetValue(param.Name, out existingMember))
                    {
                        existingMember = OverriddenOrHiddenMembersHelpers.FindFirstHiddenMemberIfAny(targetProperty, memberIsFromSomeCompilation: true);
                        isInherited = true;
                    }
 
                    // There should be an error if we picked a member that is hidden
                    // This will be fixed in C# 9 as part of 16.10. Tracked by https://github.com/dotnet/roslyn/issues/52630
 
                    if (existingMember is null)
                    {
                        addProperty(new SynthesizedRecordPropertySymbol(this, syntax, param, isOverride: false, diagnostics));
                    }
                    else if (existingMember is FieldSymbol { IsStatic: false } field
                        && field.TypeWithAnnotations.Equals(param.TypeWithAnnotations, TypeCompareKind.AllIgnoreOptions))
                    {
                        Binder.CheckFeatureAvailability(syntax, MessageID.IDS_FeaturePositionalFieldsInRecords, diagnostics);
                        if (!isInherited || checkMemberNotHidden(field, param))
                        {
                            existingOrAddedMembers.Add(field);
                        }
                    }
                    else if (existingMember is PropertySymbol { IsStatic: false, GetMethod: { } } prop
                        && prop.TypeWithAnnotations.Equals(param.TypeWithAnnotations, TypeCompareKind.AllIgnoreOptions))
                    {
                        // There already exists a member corresponding to the candidate synthesized property.
                        if (isInherited && prop.IsAbstract)
                        {
                            addProperty(new SynthesizedRecordPropertySymbol(this, syntax, param, isOverride: true, diagnostics));
                        }
                        else if (!isInherited || checkMemberNotHidden(prop, param))
                        {
                            // Deconstruct() is specified to simply assign from this property to the corresponding out parameter.
                            existingOrAddedMembers.Add(prop);
                        }
                    }
                    else
                    {
                        diagnostics.Add(ErrorCode.ERR_BadRecordMemberForPositionalParameter,
                            param.GetFirstLocation(),
                            new FormattedSymbol(existingMember, SymbolDisplayFormat.CSharpErrorMessageFormat.WithMemberOptions(SymbolDisplayMemberOptions.IncludeContainingType)),
                            param.TypeWithAnnotations,
                            param.Name);
                    }
 
                    void addProperty(SynthesizedRecordPropertySymbol property)
                    {
                        existingOrAddedMembers.Add(property);
                        members.Add(property);
                        Debug.Assert(property.GetMethod is object);
                        Debug.Assert(property.SetMethod is object);
                        members.Add(property.GetMethod);
                        members.Add(property.SetMethod);
                        var backingField = property.DeclaredBackingField;
                        Debug.Assert(backingField is object);
                        members.Add(backingField);
 
                        builder.AddInstanceInitializerForPositionalMembers(new FieldOrPropertyInitializer(property.BackingField, paramList.Parameters[param.Ordinal]));
                        addedCount++;
                    }
                }
 
                return existingOrAddedMembers.ToImmutableAndFree();
 
                bool checkMemberNotHidden(Symbol symbol, ParameterSymbol param)
                {
                    if (memberNames.Contains(symbol.Name) || this.GetTypeMembersDictionary().ContainsKey(symbol.Name.AsMemory()))
                    {
                        diagnostics.Add(ErrorCode.ERR_HiddenPositionalMember, param.GetFirstLocation(), symbol);
                        return false;
                    }
                    return true;
                }
            }
 
            void addObjectEquals(MethodSymbol thisEquals)
            {
                members.Add(new SynthesizedRecordObjEquals(this, thisEquals, memberOffset: members.Count));
            }
 
            MethodSymbol addGetHashCode(PropertySymbol? equalityContract)
            {
                var targetMethod = new SignatureOnlyMethodSymbol(
                    WellKnownMemberNames.ObjectGetHashCode,
                    this,
                    MethodKind.Ordinary,
                    Cci.CallingConvention.HasThis,
                    ImmutableArray<TypeParameterSymbol>.Empty,
                    ImmutableArray<ParameterSymbol>.Empty,
                    RefKind.None,
                    isInitOnly: false,
                    isStatic: false,
                    TypeWithAnnotations.Create(compilation.GetSpecialType(SpecialType.System_Int32)),
                    ImmutableArray<CustomModifier>.Empty,
                    ImmutableArray<MethodSymbol>.Empty);
 
                MethodSymbol getHashCode;
 
                if (!memberSignatures.TryGetValue(targetMethod, out Symbol? existingHashCodeMethod))
                {
                    getHashCode = new SynthesizedRecordGetHashCode(this, equalityContract, memberOffset: members.Count);
                    members.Add(getHashCode);
                }
                else
                {
                    getHashCode = (MethodSymbol)existingHashCodeMethod;
                    if (!SynthesizedRecordObjectMethod.VerifyOverridesMethodFromObject(getHashCode, SpecialMember.System_Object__GetHashCode, diagnostics) && getHashCode.IsSealed && !IsSealed)
                    {
                        diagnostics.Add(ErrorCode.ERR_SealedAPIInRecord, getHashCode.GetFirstLocation(), getHashCode);
                    }
                }
                return getHashCode;
            }
 
            PropertySymbol addEqualityContract()
            {
                Debug.Assert(isRecordClass);
                var targetProperty = new SignatureOnlyPropertySymbol(SynthesizedRecordEqualityContractProperty.PropertyName,
                                                                     this,
                                                                     ImmutableArray<ParameterSymbol>.Empty,
                                                                     RefKind.None,
                                                                     TypeWithAnnotations.Create(compilation.GetWellKnownType(WellKnownType.System_Type)),
                                                                     ImmutableArray<CustomModifier>.Empty,
                                                                     isStatic: false,
                                                                     ImmutableArray<PropertySymbol>.Empty);
 
                PropertySymbol equalityContract;
 
                if (!memberSignatures.TryGetValue(targetProperty, out Symbol? existingEqualityContractProperty))
                {
                    equalityContract = new SynthesizedRecordEqualityContractProperty(this, diagnostics);
                    members.Add(equalityContract);
                    members.Add(equalityContract.GetMethod);
                }
                else
                {
                    equalityContract = (PropertySymbol)existingEqualityContractProperty;
 
                    if (this.IsSealed && this.BaseTypeNoUseSiteDiagnostics.IsObjectType())
                    {
                        if (equalityContract.DeclaredAccessibility != Accessibility.Private)
                        {
                            diagnostics.Add(ErrorCode.ERR_NonPrivateAPIInRecord, equalityContract.GetFirstLocation(), equalityContract);
                        }
                    }
                    else if (equalityContract.DeclaredAccessibility != Accessibility.Protected)
                    {
                        diagnostics.Add(ErrorCode.ERR_NonProtectedAPIInRecord, equalityContract.GetFirstLocation(), equalityContract);
                    }
 
                    if (!equalityContract.Type.Equals(targetProperty.Type, TypeCompareKind.AllIgnoreOptions))
                    {
                        if (!equalityContract.Type.IsErrorType())
                        {
                            diagnostics.Add(ErrorCode.ERR_SignatureMismatchInRecord, equalityContract.GetFirstLocation(), equalityContract, targetProperty.Type);
                        }
                    }
                    else
                    {
                        SynthesizedRecordEqualityContractProperty.VerifyOverridesEqualityContractFromBase(equalityContract, diagnostics);
                    }
 
                    if (equalityContract.GetMethod is null)
                    {
                        diagnostics.Add(ErrorCode.ERR_EqualityContractRequiresGetter, equalityContract.GetFirstLocation(), equalityContract);
                    }
 
                    reportStaticOrNotOverridableAPIInRecord(equalityContract, diagnostics);
                }
 
                return equalityContract;
            }
 
            MethodSymbol addThisEquals(PropertySymbol? equalityContract)
            {
                var targetMethod = new SignatureOnlyMethodSymbol(
                    WellKnownMemberNames.ObjectEquals,
                    this,
                    MethodKind.Ordinary,
                    Cci.CallingConvention.HasThis,
                    ImmutableArray<TypeParameterSymbol>.Empty,
                    ImmutableArray.Create<ParameterSymbol>(new SignatureOnlyParameterSymbol(
                                                                TypeWithAnnotations.Create(this),
                                                                ImmutableArray<CustomModifier>.Empty,
                                                                isParamsArray: false,
                                                                isParamsCollection: false,
                                                                RefKind.None
                                                                )),
                    RefKind.None,
                    isInitOnly: false,
                    isStatic: false,
                    TypeWithAnnotations.Create(compilation.GetSpecialType(SpecialType.System_Boolean)),
                    ImmutableArray<CustomModifier>.Empty,
                    ImmutableArray<MethodSymbol>.Empty);
 
                MethodSymbol thisEquals;
 
                if (!memberSignatures.TryGetValue(targetMethod, out Symbol? existingEqualsMethod))
                {
                    thisEquals = new SynthesizedRecordEquals(this, equalityContract, memberOffset: members.Count);
                    members.Add(thisEquals);
                }
                else
                {
                    thisEquals = (MethodSymbol)existingEqualsMethod;
 
                    if (thisEquals.DeclaredAccessibility != Accessibility.Public)
                    {
                        diagnostics.Add(ErrorCode.ERR_NonPublicAPIInRecord, thisEquals.GetFirstLocation(), thisEquals);
                    }
 
                    if (thisEquals.ReturnType.SpecialType != SpecialType.System_Boolean && !thisEquals.ReturnType.IsErrorType())
                    {
                        diagnostics.Add(ErrorCode.ERR_SignatureMismatchInRecord, thisEquals.GetFirstLocation(), thisEquals, targetMethod.ReturnType);
                    }
 
                    reportStaticOrNotOverridableAPIInRecord(thisEquals, diagnostics);
                }
 
                return thisEquals;
            }
 
            void reportStaticOrNotOverridableAPIInRecord(Symbol symbol, BindingDiagnosticBag diagnostics)
            {
                if (isRecordClass &&
                    !IsSealed &&
                    ((!symbol.IsAbstract && !symbol.IsVirtual && !symbol.IsOverride) || symbol.IsSealed))
                {
                    diagnostics.Add(ErrorCode.ERR_NotOverridableAPIInRecord, symbol.GetFirstLocation(), symbol);
                }
                else if (symbol.IsStatic)
                {
                    diagnostics.Add(ErrorCode.ERR_StaticAPIInRecord, symbol.GetFirstLocation(), symbol);
                }
            }
 
            void addBaseEquals()
            {
                Debug.Assert(isRecordClass);
                if (!BaseTypeNoUseSiteDiagnostics.IsObjectType())
                {
                    members.Add(new SynthesizedRecordBaseEquals(this, memberOffset: members.Count));
                }
            }
 
            void addEqualityOperators()
            {
                members.Add(new SynthesizedRecordEqualityOperator(this, memberOffset: members.Count, diagnostics));
                members.Add(new SynthesizedRecordInequalityOperator(this, memberOffset: members.Count, diagnostics));
            }
        }
 
        private void AddSynthesizedConstructorsIfNecessary(MembersAndInitializersBuilder builder, DeclaredMembersAndInitializers declaredMembersAndInitializers, BindingDiagnosticBag diagnostics)
        {
            //we're not calling the helpers on NamedTypeSymbol base, because those call
            //GetMembers and we're inside a GetMembers call ourselves (i.e. stack overflow)
            var hasInstanceConstructor = false;
            var hasParameterlessInstanceConstructor = false;
            var hasStaticConstructor = false;
 
            // CONSIDER: if this traversal becomes a bottleneck, the flags could be made outputs of the
            // dictionary construction process.  For now, this is more encapsulated.
            var membersSoFar = builder.GetNonTypeMembers(declaredMembersAndInitializers);
            foreach (var member in membersSoFar)
            {
                if (member.Kind == SymbolKind.Method)
                {
                    var method = (MethodSymbol)member;
                    switch (method.MethodKind)
                    {
                        case MethodKind.Constructor:
                            // Ignore the record copy constructor
                            if (!IsRecord ||
                                !(SynthesizedRecordCopyCtor.HasCopyConstructorSignature(method) && method is not SynthesizedPrimaryConstructor))
                            {
                                hasInstanceConstructor = true;
                                hasParameterlessInstanceConstructor = hasParameterlessInstanceConstructor || method.ParameterCount == 0;
                            }
                            break;
 
                        case MethodKind.StaticConstructor:
                            hasStaticConstructor = true;
                            break;
                    }
                }
 
                //kick out early if we've seen everything we're looking for
                if (hasInstanceConstructor &&
                    hasParameterlessInstanceConstructor &&
                    hasStaticConstructor)
                {
                    break;
                }
            }
 
            // NOTE: Per section 11.3.8 of the spec, "every struct implicitly has a parameterless instance constructor".
            // We won't insert a parameterless constructor for a struct if there already is one.
            // The synthesized constructor will only be emitted if there are field initializers, but it should be in the symbol table.
            if ((!hasParameterlessInstanceConstructor && this.IsStructType()) ||
                (!hasInstanceConstructor && !this.IsStatic && !this.IsInterface))
            {
                builder.AddNonTypeMember((this.TypeKind == TypeKind.Submission) ?
                    new SynthesizedSubmissionConstructor(this, diagnostics) :
                    new SynthesizedInstanceConstructor(this),
                    declaredMembersAndInitializers);
            }
 
            // constants don't count, since they do not exist as fields at runtime
            // NOTE: even for decimal constants (which require field initializers),
            // we do not create .cctor here since a static constructor implicitly created for a decimal
            // should not appear in the list returned by public API like GetMembers().
            if (!hasStaticConstructor && hasNonConstantInitializer(declaredMembersAndInitializers.StaticInitializers))
            {
                // Note: we don't have to put anything in the method - the binder will
                // do that when processing field initializers.
                builder.AddNonTypeMember(new SynthesizedStaticConstructor(this), declaredMembersAndInitializers);
            }
 
            if (this.IsScriptClass)
            {
                var scriptInitializer = new SynthesizedInteractiveInitializerMethod(this, diagnostics);
                builder.AddNonTypeMember(scriptInitializer, declaredMembersAndInitializers);
                var scriptEntryPoint = SynthesizedEntryPointSymbol.Create(scriptInitializer, diagnostics);
                builder.AddNonTypeMember(scriptEntryPoint, declaredMembersAndInitializers);
            }
 
            static bool hasNonConstantInitializer(ImmutableArray<ImmutableArray<FieldOrPropertyInitializer>> initializers)
            {
                return initializers.Any(static siblings => siblings.Any(static initializer => !initializer.FieldOpt.IsConst));
            }
        }
 
        private void AddSynthesizedTupleMembersIfNecessary(MembersAndInitializersBuilder builder, DeclaredMembersAndInitializers declaredMembersAndInitializers)
        {
            if (!this.IsTupleType)
            {
                return;
            }
 
            var synthesizedMembers = this.MakeSynthesizedTupleMembers(declaredMembersAndInitializers.NonTypeMembers);
            if (synthesizedMembers is null)
            {
                return;
            }
 
            foreach (var synthesizedMember in synthesizedMembers)
            {
                builder.AddNonTypeMember(synthesizedMember, declaredMembersAndInitializers);
            }
 
            synthesizedMembers.Free();
        }
 
        private void AddNonTypeMembers(
            DeclaredMembersAndInitializersBuilder builder,
            SyntaxList<MemberDeclarationSyntax> members,
            BindingDiagnosticBag diagnostics)
        {
            if (members.Count == 0)
            {
                return;
            }
 
            var firstMember = members[0];
            var bodyBinder = this.GetBinder(firstMember);
 
            ArrayBuilder<FieldOrPropertyInitializer>? staticInitializers = null;
            ArrayBuilder<FieldOrPropertyInitializer>? instanceInitializers = null;
            var compilation = DeclaringCompilation;
 
            foreach (var m in members)
            {
                if (_lazyMembersAndInitializers != null)
                {
                    // membersAndInitializers is already computed. no point to continue.
                    return;
                }
 
                bool reportMisplacedGlobalCode = !m.HasErrors;
 
                switch (m.Kind())
                {
                    case SyntaxKind.FieldDeclaration:
                        {
                            var fieldSyntax = (FieldDeclarationSyntax)m;
 
                            // Lang version check for ref-fields is done inside SourceMemberFieldSymbol;
                            _ = fieldSyntax.Declaration.Type.SkipScoped(out _).SkipRefInField(out var refKind);
 
                            if (IsImplicitClass && reportMisplacedGlobalCode)
                            {
                                diagnostics.Add(ErrorCode.ERR_NamespaceUnexpected,
                                    new SourceLocation(fieldSyntax.Declaration.Variables.First().Identifier));
                            }
 
                            bool modifierErrors;
                            var modifiers = SourceMemberFieldSymbol.MakeModifiers(this, fieldSyntax.Declaration.Variables[0].Identifier, fieldSyntax.Modifiers, isRefField: refKind != RefKind.None, diagnostics, out modifierErrors);
                            foreach (var variable in fieldSyntax.Declaration.Variables)
                            {
                                var fieldSymbol = (modifiers & DeclarationModifiers.Fixed) == 0
                                    ? new SourceMemberFieldSymbolFromDeclarator(this, variable, modifiers, modifierErrors, diagnostics)
                                    : new SourceFixedFieldSymbol(this, variable, modifiers, modifierErrors, diagnostics);
                                builder.NonTypeMembers.Add(fieldSymbol);
                                // All fields are included in the nullable context for constructors and initializers, even fields without
                                // initializers, to ensure warnings are reported for uninitialized non-nullable fields in NullableWalker.
                                builder.UpdateIsNullableEnabledForConstructorsAndFields(useStatic: fieldSymbol.IsStatic, compilation, variable);
 
                                if (IsScriptClass)
                                {
                                    // also gather expression-declared variables from the bracketed argument lists and the initializers
                                    ExpressionFieldFinder.FindExpressionVariables(builder.NonTypeMembers, variable, this,
                                                            DeclarationModifiers.Private | (modifiers & DeclarationModifiers.Static),
                                                            fieldSymbol);
                                }
 
                                if (variable.Initializer != null)
                                {
                                    if (fieldSymbol.IsStatic)
                                    {
                                        AddInitializer(ref staticInitializers, fieldSymbol, variable.Initializer);
                                    }
                                    else
                                    {
                                        AddInitializer(ref instanceInitializers, fieldSymbol, variable.Initializer);
                                    }
                                }
                            }
                        }
                        break;
 
                    case SyntaxKind.MethodDeclaration:
                        {
                            var methodSyntax = (MethodDeclarationSyntax)m;
                            if (IsImplicitClass && reportMisplacedGlobalCode)
                            {
                                diagnostics.Add(ErrorCode.ERR_NamespaceUnexpected,
                                    new SourceLocation(methodSyntax.Identifier));
                            }
 
                            var method = SourceOrdinaryMethodSymbol.CreateMethodSymbol(this, bodyBinder, methodSyntax, compilation.IsNullableAnalysisEnabledIn(methodSyntax), diagnostics);
                            builder.NonTypeMembers.Add(method);
                        }
                        break;
 
                    case SyntaxKind.ConstructorDeclaration:
                        {
                            var constructorSyntax = (ConstructorDeclarationSyntax)m;
                            if (IsImplicitClass && reportMisplacedGlobalCode)
                            {
                                diagnostics.Add(ErrorCode.ERR_NamespaceUnexpected,
                                    new SourceLocation(constructorSyntax.Identifier));
                            }
 
                            bool isNullableEnabled = compilation.IsNullableAnalysisEnabledIn(constructorSyntax);
                            var constructor = SourceConstructorSymbol.CreateConstructorSymbol(this, constructorSyntax, isNullableEnabled, diagnostics);
                            builder.NonTypeMembers.Add(constructor);
                            if (constructorSyntax.Initializer?.Kind() != SyntaxKind.ThisConstructorInitializer)
                            {
                                builder.UpdateIsNullableEnabledForConstructorsAndFields(useStatic: constructor.IsStatic, isNullableEnabled);
                            }
                        }
                        break;
 
                    case SyntaxKind.DestructorDeclaration:
                        {
                            var destructorSyntax = (DestructorDeclarationSyntax)m;
                            if (IsImplicitClass && reportMisplacedGlobalCode)
                            {
                                diagnostics.Add(ErrorCode.ERR_NamespaceUnexpected,
                                    new SourceLocation(destructorSyntax.Identifier));
                            }
 
                            // CONSIDER: if this doesn't (directly or indirectly) override object.Finalize, the
                            // runtime won't consider it a finalizer and it will not be marked as a destructor
                            // when it is loaded from metadata.  Perhaps we should just treat it as an Ordinary
                            // method in such cases?
                            var destructor = new SourceDestructorSymbol(this, destructorSyntax, compilation.IsNullableAnalysisEnabledIn(destructorSyntax), diagnostics);
                            builder.NonTypeMembers.Add(destructor);
                        }
                        break;
 
                    case SyntaxKind.PropertyDeclaration:
                        {
                            var propertySyntax = (PropertyDeclarationSyntax)m;
                            if (IsImplicitClass && reportMisplacedGlobalCode)
                            {
                                diagnostics.Add(ErrorCode.ERR_NamespaceUnexpected,
                                    new SourceLocation(propertySyntax.Identifier));
                            }
 
                            var property = SourcePropertySymbol.Create(this, bodyBinder, propertySyntax, diagnostics);
                            builder.NonTypeMembers.Add(property);
 
                            AddAccessorIfAvailable(builder.NonTypeMembers, property.GetMethod);
                            AddAccessorIfAvailable(builder.NonTypeMembers, property.SetMethod);
                            FieldSymbol? backingField = property.DeclaredBackingField;
 
                            // TODO: can we leave this out of the member list?
                            // From the 10/12/11 design notes:
                            //   In addition, we will change autoproperties to behavior in
                            //   a similar manner and make the autoproperty fields private.
                            if (backingField is { })
                            {
                                builder.NonTypeMembers.Add(backingField);
                                builder.UpdateIsNullableEnabledForConstructorsAndFields(useStatic: backingField.IsStatic, compilation, propertySyntax);
 
                                var initializer = propertySyntax.Initializer;
                                if (initializer != null)
                                {
                                    if (IsScriptClass)
                                    {
                                        // also gather expression-declared variables from the initializer
                                        ExpressionFieldFinder.FindExpressionVariables(builder.NonTypeMembers,
                                                                                      initializer,
                                                                                      this,
                                                                                      DeclarationModifiers.Private | (property.IsStatic ? DeclarationModifiers.Static : 0),
                                                                                      backingField);
                                    }
 
                                    if (property.IsStatic)
                                    {
                                        AddInitializer(ref staticInitializers, backingField, initializer);
                                    }
                                    else
                                    {
                                        AddInitializer(ref instanceInitializers, backingField, initializer);
                                    }
                                }
                            }
                        }
                        break;
 
                    case SyntaxKind.EventFieldDeclaration:
                        {
                            var eventFieldSyntax = (EventFieldDeclarationSyntax)m;
                            if (IsImplicitClass && reportMisplacedGlobalCode)
                            {
                                diagnostics.Add(
                                    ErrorCode.ERR_NamespaceUnexpected,
                                    new SourceLocation(eventFieldSyntax.Declaration.Variables.First().Identifier));
                            }
 
                            foreach (VariableDeclaratorSyntax declarator in eventFieldSyntax.Declaration.Variables)
                            {
                                SourceFieldLikeEventSymbol @event = new SourceFieldLikeEventSymbol(this, bodyBinder, eventFieldSyntax.Modifiers, declarator, diagnostics);
                                builder.NonTypeMembers.Add(@event);
 
                                FieldSymbol? associatedField = @event.AssociatedField;
 
                                if (IsScriptClass)
                                {
                                    // also gather expression-declared variables from the bracketed argument lists and the initializers
                                    ExpressionFieldFinder.FindExpressionVariables(builder.NonTypeMembers, declarator, this,
                                                            DeclarationModifiers.Private | (@event.IsStatic ? DeclarationModifiers.Static : 0),
                                                            associatedField);
                                }
 
                                if ((object?)associatedField != null)
                                {
                                    // NOTE: specifically don't add the associated field to the members list
                                    // (regard it as an implementation detail).
 
                                    builder.UpdateIsNullableEnabledForConstructorsAndFields(useStatic: associatedField.IsStatic, compilation, declarator);
 
                                    if (declarator.Initializer != null)
                                    {
                                        if (associatedField.IsStatic)
                                        {
                                            AddInitializer(ref staticInitializers, associatedField, declarator.Initializer);
                                        }
                                        else
                                        {
                                            AddInitializer(ref instanceInitializers, associatedField, declarator.Initializer);
                                        }
                                    }
                                }
 
                                Debug.Assert((object)@event.AddMethod != null);
                                Debug.Assert((object)@event.RemoveMethod != null);
 
                                AddAccessorIfAvailable(builder.NonTypeMembers, @event.AddMethod);
                                AddAccessorIfAvailable(builder.NonTypeMembers, @event.RemoveMethod);
                            }
                        }
                        break;
 
                    case SyntaxKind.EventDeclaration:
                        {
                            var eventSyntax = (EventDeclarationSyntax)m;
                            if (IsImplicitClass && reportMisplacedGlobalCode)
                            {
                                diagnostics.Add(ErrorCode.ERR_NamespaceUnexpected,
                                    new SourceLocation(eventSyntax.Identifier));
                            }
 
                            var @event = new SourceCustomEventSymbol(this, bodyBinder, eventSyntax, diagnostics);
 
                            builder.NonTypeMembers.Add(@event);
 
                            AddAccessorIfAvailable(builder.NonTypeMembers, @event.AddMethod);
                            AddAccessorIfAvailable(builder.NonTypeMembers, @event.RemoveMethod);
 
                            Debug.Assert(@event.AssociatedField is null);
                        }
                        break;
 
                    case SyntaxKind.IndexerDeclaration:
                        {
                            var indexerSyntax = (IndexerDeclarationSyntax)m;
                            if (IsImplicitClass && reportMisplacedGlobalCode)
                            {
                                diagnostics.Add(ErrorCode.ERR_NamespaceUnexpected,
                                    new SourceLocation(indexerSyntax.ThisKeyword));
                            }
 
                            var indexer = SourcePropertySymbol.Create(this, bodyBinder, indexerSyntax, diagnostics);
                            builder.HaveIndexers = true;
                            builder.NonTypeMembers.Add(indexer);
                            AddAccessorIfAvailable(builder.NonTypeMembers, indexer.GetMethod);
                            AddAccessorIfAvailable(builder.NonTypeMembers, indexer.SetMethod);
                        }
                        break;
 
                    case SyntaxKind.ConversionOperatorDeclaration:
                        {
                            var conversionOperatorSyntax = (ConversionOperatorDeclarationSyntax)m;
                            if (IsImplicitClass && reportMisplacedGlobalCode)
                            {
                                diagnostics.Add(ErrorCode.ERR_NamespaceUnexpected,
                                    new SourceLocation(conversionOperatorSyntax.OperatorKeyword));
                            }
 
                            var method = SourceUserDefinedConversionSymbol.CreateUserDefinedConversionSymbol(
                                this, bodyBinder, conversionOperatorSyntax, compilation.IsNullableAnalysisEnabledIn(conversionOperatorSyntax), diagnostics);
                            builder.NonTypeMembers.Add(method);
                        }
                        break;
 
                    case SyntaxKind.OperatorDeclaration:
                        {
                            var operatorSyntax = (OperatorDeclarationSyntax)m;
                            if (IsImplicitClass && reportMisplacedGlobalCode)
                            {
                                diagnostics.Add(ErrorCode.ERR_NamespaceUnexpected,
                                    new SourceLocation(operatorSyntax.OperatorKeyword));
                            }
 
                            var method = SourceUserDefinedOperatorSymbol.CreateUserDefinedOperatorSymbol(
                                this, bodyBinder, operatorSyntax, compilation.IsNullableAnalysisEnabledIn(operatorSyntax), diagnostics);
                            builder.NonTypeMembers.Add(method);
                        }
                        break;
 
                    case SyntaxKind.GlobalStatement:
                        {
                            var globalStatement = ((GlobalStatementSyntax)m).Statement;
 
                            if (IsScriptClass)
                            {
                                var innerStatement = globalStatement;
 
                                // drill into any LabeledStatements
                                while (innerStatement.Kind() == SyntaxKind.LabeledStatement)
                                {
                                    innerStatement = ((LabeledStatementSyntax)innerStatement).Statement;
                                }
 
                                switch (innerStatement.Kind())
                                {
                                    case SyntaxKind.LocalDeclarationStatement:
                                        // We shouldn't reach this place, but field declarations preceded with a label end up here.
                                        // This is tracked by https://github.com/dotnet/roslyn/issues/13712. Let's do our best for now.
                                        var decl = (LocalDeclarationStatementSyntax)innerStatement;
                                        foreach (var vdecl in decl.Declaration.Variables)
                                        {
                                            // also gather expression-declared variables from the bracketed argument lists and the initializers
                                            ExpressionFieldFinder.FindExpressionVariables(builder.NonTypeMembers, vdecl, this, DeclarationModifiers.Private,
                                                                                          containingFieldOpt: null);
                                        }
                                        break;
 
                                    case SyntaxKind.ExpressionStatement:
                                    case SyntaxKind.IfStatement:
                                    case SyntaxKind.YieldReturnStatement:
                                    case SyntaxKind.ReturnStatement:
                                    case SyntaxKind.ThrowStatement:
                                    case SyntaxKind.SwitchStatement:
                                    case SyntaxKind.LockStatement:
                                        ExpressionFieldFinder.FindExpressionVariables(builder.NonTypeMembers,
                                                  innerStatement,
                                                  this,
                                                  DeclarationModifiers.Private,
                                                  containingFieldOpt: null);
                                        break;
 
                                    default:
                                        // no other statement introduces variables into the enclosing scope
                                        break;
                                }
 
                                AddInitializer(ref instanceInitializers, null, globalStatement);
                            }
                            else if (reportMisplacedGlobalCode && !SyntaxFacts.IsSimpleProgramTopLevelStatement((GlobalStatementSyntax)m))
                            {
                                diagnostics.Add(ErrorCode.ERR_GlobalStatement, new SourceLocation(globalStatement));
                            }
                        }
                        break;
 
                    default:
                        Debug.Assert(
                            SyntaxFacts.IsTypeDeclaration(m.Kind()) ||
                            m.Kind() is SyntaxKind.NamespaceDeclaration or
                                        SyntaxKind.FileScopedNamespaceDeclaration or
                                        SyntaxKind.IncompleteMember);
                        break;
                }
            }
 
            AddInitializers(builder.InstanceInitializers, instanceInitializers);
            AddInitializers(builder.StaticInitializers, staticInitializers);
        }
 
        private void AddAccessorIfAvailable(ArrayBuilder<Symbol> symbols, MethodSymbol? accessorOpt)
        {
            if (!(accessorOpt is null))
            {
                symbols.Add(accessorOpt);
            }
        }
 
        internal override byte? GetLocalNullableContextValue()
        {
            byte? value;
            if (!_flags.TryGetNullableContext(out value))
            {
                value = ComputeNullableContextValue();
                _flags.SetNullableContext(value);
            }
            return value;
        }
 
        private byte? ComputeNullableContextValue()
        {
            var compilation = DeclaringCompilation;
            if (!compilation.ShouldEmitNullableAttributes(this))
            {
                return null;
            }
 
            var builder = new MostCommonNullableValueBuilder();
            var baseType = BaseTypeNoUseSiteDiagnostics;
            if (baseType is object)
            {
                builder.AddValue(TypeWithAnnotations.Create(baseType));
            }
            foreach (var @interface in GetInterfacesToEmit())
            {
                builder.AddValue(TypeWithAnnotations.Create(@interface));
            }
            foreach (var typeParameter in TypeParameters)
            {
                typeParameter.GetCommonNullableValues(compilation, ref builder);
            }
            foreach (var member in GetMembersUnordered())
            {
                member.GetCommonNullableValues(compilation, ref builder);
            }
            // Not including lambdas or local functions.
            return builder.MostCommonValue;
        }
 
        /// <summary>
        /// Returns true if the overall nullable context is enabled for constructors and initializers.
        /// </summary>
        /// <param name="useStatic">Consider static constructor and fields rather than instance constructors and fields.</param>
        internal bool IsNullableEnabledForConstructorsAndInitializers(bool useStatic)
        {
            var membersAndInitializers = GetMembersAndInitializers();
            return useStatic ?
                membersAndInitializers.IsNullableEnabledForStaticConstructorsAndFields :
                membersAndInitializers.IsNullableEnabledForInstanceConstructorsAndFields;
        }
 
        internal override void AddSynthesizedAttributes(PEModuleBuilder moduleBuilder, ref ArrayBuilder<SynthesizedAttributeData> attributes)
        {
            base.AddSynthesizedAttributes(moduleBuilder, ref attributes);
 
            var compilation = DeclaringCompilation;
            NamedTypeSymbol baseType = this.BaseTypeNoUseSiteDiagnostics;
 
            if (baseType is object)
            {
                if (baseType.ContainsDynamic())
                {
                    AddSynthesizedAttribute(ref attributes, compilation.SynthesizeDynamicAttribute(baseType, customModifiersCount: 0));
                }
 
                if (compilation.ShouldEmitNativeIntegerAttributes(baseType))
                {
                    AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNativeIntegerAttribute(this, baseType));
                }
 
                if (baseType.ContainsTupleNames())
                {
                    AddSynthesizedAttribute(ref attributes, compilation.SynthesizeTupleNamesAttribute(baseType));
                }
            }
 
            if (compilation.ShouldEmitNullableAttributes(this))
            {
                if (ShouldEmitNullableContextValue(out byte nullableContextValue))
                {
                    AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableContextAttribute(this, nullableContextValue));
                }
 
                if (baseType is object)
                {
                    AddSynthesizedAttribute(ref attributes, moduleBuilder.SynthesizeNullableAttributeIfNecessary(this, nullableContextValue, TypeWithAnnotations.Create(baseType)));
                }
            }
        }
 
        #endregion
 
        #region Extension Methods
 
        internal bool ContainsExtensionMethods
        {
            get
            {
                if (!_lazyContainsExtensionMethods.HasValue())
                {
                    bool containsExtensionMethods = ((this.IsStatic && !this.IsGenericType) || this.IsScriptClass) && this.declaration.ContainsExtensionMethods;
                    _lazyContainsExtensionMethods = containsExtensionMethods.ToThreeState();
                }
 
                return _lazyContainsExtensionMethods.Value();
            }
        }
 
        internal bool AnyMemberHasAttributes
        {
            get
            {
                if (!_lazyAnyMemberHasAttributes.HasValue())
                {
                    bool anyMemberHasAttributes = this.declaration.AnyMemberHasAttributes;
                    _lazyAnyMemberHasAttributes = anyMemberHasAttributes.ToThreeState();
                }
 
                return _lazyAnyMemberHasAttributes.Value();
            }
        }
 
        public override bool MightContainExtensionMethods
        {
            get
            {
                return this.ContainsExtensionMethods;
            }
        }
 
        #endregion
 
        internal void DiscoverInterceptors(ArrayBuilder<NamespaceOrTypeSymbol> toSearch)
        {
            foreach (var type in this.GetTypeMembers())
            {
                toSearch.Add(type);
            }
 
            if (!declaration.AnyMemberHasAttributes)
            {
                return;
            }
 
            foreach (var member in this.GetMembersUnordered())
            {
                if (member is MethodSymbol { MethodKind: MethodKind.Ordinary })
                {
                    // force binding attributes and populating compilation-level structures
                    member.GetAttributes();
                }
            }
        }
 
        public sealed override NamedTypeSymbol ConstructedFrom
        {
            get { return this; }
        }
 
        internal class SynthesizedExplicitImplementations
        {
            public static readonly SynthesizedExplicitImplementations Empty = new SynthesizedExplicitImplementations(ImmutableArray<SynthesizedExplicitImplementationForwardingMethod>.Empty,
                                                                                                                     ImmutableArray<(MethodSymbol Body, MethodSymbol Implemented)>.Empty);
 
            public readonly ImmutableArray<SynthesizedExplicitImplementationForwardingMethod> ForwardingMethods;
            public readonly ImmutableArray<(MethodSymbol Body, MethodSymbol Implemented)> MethodImpls;
 
            private SynthesizedExplicitImplementations(
                ImmutableArray<SynthesizedExplicitImplementationForwardingMethod> forwardingMethods,
                ImmutableArray<(MethodSymbol Body, MethodSymbol Implemented)> methodImpls)
            {
                ForwardingMethods = forwardingMethods.NullToEmpty();
                MethodImpls = methodImpls.NullToEmpty();
            }
 
            internal static SynthesizedExplicitImplementations Create(
                ImmutableArray<SynthesizedExplicitImplementationForwardingMethod> forwardingMethods,
                ImmutableArray<(MethodSymbol Body, MethodSymbol Implemented)> methodImpls)
            {
                if (forwardingMethods.IsDefaultOrEmpty && methodImpls.IsDefaultOrEmpty)
                {
                    return Empty;
                }
 
                return new SynthesizedExplicitImplementations(forwardingMethods, methodImpls);
            }
        }
    }
}