File: Emitter\Model\PEModuleBuilder.cs
Web Access
Project: src\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.PortableExecutable;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
using ReferenceEqualityComparer = Roslyn.Utilities.ReferenceEqualityComparer;
 
namespace Microsoft.CodeAnalysis.CSharp.Emit
{
    internal abstract class PEModuleBuilder : PEModuleBuilder<CSharpCompilation, SourceModuleSymbol, AssemblySymbol, TypeSymbol, NamedTypeSymbol, MethodSymbol, SyntaxNode, NoPia.EmbeddedTypesManager, ModuleCompilationState>
    {
        // TODO: Need to estimate amount of elements for this map and pass that value to the constructor.
        protected readonly ConcurrentDictionary<Symbol, Cci.IModuleReference> AssemblyOrModuleSymbolToModuleRefMap = new ConcurrentDictionary<Symbol, Cci.IModuleReference>();
        private readonly ConcurrentDictionary<Symbol, object> _genericInstanceMap = new ConcurrentDictionary<Symbol, object>(Symbols.SymbolEqualityComparer.ConsiderEverything);
        private readonly ConcurrentDictionary<ImportChain, ImmutableArray<Cci.UsedNamespaceOrType>> _translatedImportsMap =
                                                            new ConcurrentDictionary<ImportChain, ImmutableArray<Cci.UsedNamespaceOrType>>(ReferenceEqualityComparer.Instance);
        private readonly ConcurrentSet<TypeSymbol> _reportedErrorTypesMap = new ConcurrentSet<TypeSymbol>();
 
        private readonly NoPia.EmbeddedTypesManager _embeddedTypesManagerOpt;
        public override NoPia.EmbeddedTypesManager EmbeddedTypesManagerOpt
            => _embeddedTypesManagerOpt;
 
        // Gives the name of this module (may not reflect the name of the underlying symbol).
        // See Assembly.MetadataName.
        private readonly string _metadataName;
 
        private ImmutableArray<Cci.ExportedType> _lazyExportedTypes;
 
        /// <summary>
        /// The compiler-generated implementation type for each fixed-size buffer.
        /// </summary>
        private Dictionary<FieldSymbol, NamedTypeSymbol> _fixedImplementationTypes;
 
        private SynthesizedPrivateImplementationDetailsType _lazyPrivateImplementationDetailsClass;
 
        private int _needsGeneratedAttributes;
        private bool _needsGeneratedAttributes_IsFrozen;
 
        /// <summary>
        /// Returns a value indicating which embedded attributes should be generated during emit phase.
        /// The value is set during binding the symbols that need those attributes, and is frozen on first trial to get it.
        /// Freezing is needed to make sure that nothing tries to modify the value after the value is read.
        /// </summary>
        internal EmbeddableAttributes GetNeedsGeneratedAttributes()
        {
            _needsGeneratedAttributes_IsFrozen = true;
            return GetNeedsGeneratedAttributesInternal();
        }
 
        private EmbeddableAttributes GetNeedsGeneratedAttributesInternal()
        {
            return (EmbeddableAttributes)_needsGeneratedAttributes | Compilation.GetNeedsGeneratedAttributes();
        }
 
        private void SetNeedsGeneratedAttributes(EmbeddableAttributes attributes)
        {
            Debug.Assert(!_needsGeneratedAttributes_IsFrozen);
            ThreadSafeFlagOperations.Set(ref _needsGeneratedAttributes, (int)attributes);
        }
 
        internal PEModuleBuilder(
            SourceModuleSymbol sourceModule,
            EmitOptions emitOptions,
            OutputKind outputKind,
            Cci.ModulePropertiesForSerialization serializationProperties,
            IEnumerable<ResourceDescription> manifestResources)
            : base(sourceModule.ContainingSourceAssembly.DeclaringCompilation,
                   sourceModule,
                   serializationProperties,
                   manifestResources,
                   outputKind,
                   emitOptions,
                   new ModuleCompilationState())
        {
            var specifiedName = sourceModule.MetadataName;
 
            _metadataName = specifiedName != Microsoft.CodeAnalysis.Compilation.UnspecifiedModuleAssemblyName ?
                            specifiedName :
                            emitOptions.OutputNameOverride ?? specifiedName;
 
            AssemblyOrModuleSymbolToModuleRefMap.Add(sourceModule, this);
 
            if (sourceModule.AnyReferencedAssembliesAreLinked)
            {
                _embeddedTypesManagerOpt = new NoPia.EmbeddedTypesManager(this);
            }
        }
 
        public override string Name
        {
            get { return _metadataName; }
        }
 
        internal sealed override string ModuleName
        {
            get { return _metadataName; }
        }
 
        internal sealed override Cci.ICustomAttribute SynthesizeAttribute(WellKnownMember attributeConstructor)
        {
            return Compilation.TrySynthesizeAttribute(attributeConstructor);
        }
 
        public sealed override IEnumerable<Cci.ICustomAttribute> GetSourceAssemblyAttributes(bool isRefAssembly)
        {
            return SourceModule.ContainingSourceAssembly
                .GetCustomAttributesToEmit(this, isRefAssembly, emittingAssemblyAttributesInNetModule: OutputKind.IsNetModule());
        }
 
        public sealed override IEnumerable<Cci.SecurityAttribute> GetSourceAssemblySecurityAttributes()
        {
            return SourceModule.ContainingSourceAssembly.GetSecurityAttributes();
        }
 
        public sealed override IEnumerable<Cci.ICustomAttribute> GetSourceModuleAttributes()
        {
            return SourceModule.GetCustomAttributesToEmit(this);
        }
 
        internal sealed override AssemblySymbol CorLibrary
        {
            get { return SourceModule.ContainingSourceAssembly.CorLibrary; }
        }
 
        public sealed override bool GenerateVisualBasicStylePdb => false;
 
        // C# doesn't emit linked assembly names into PDBs.
        public sealed override IEnumerable<string> LinkedAssembliesDebugInfo => SpecializedCollections.EmptyEnumerable<string>();
 
        // C# currently doesn't emit compilation level imports (TODO: scripting).
        public sealed override ImmutableArray<Cci.UsedNamespaceOrType> GetImports() => ImmutableArray<Cci.UsedNamespaceOrType>.Empty;
 
        // C# doesn't allow to define default namespace for compilation.
        public sealed override string DefaultNamespace => null;
 
        protected sealed override IEnumerable<Cci.IAssemblyReference> GetAssemblyReferencesFromAddedModules(DiagnosticBag diagnostics)
        {
            ImmutableArray<ModuleSymbol> modules = SourceModule.ContainingAssembly.Modules;
 
            for (int i = 1; i < modules.Length; i++)
            {
                foreach (AssemblySymbol aRef in modules[i].GetReferencedAssemblySymbols())
                {
                    yield return Translate(aRef, diagnostics);
                }
            }
        }
 
        private void ValidateReferencedAssembly(AssemblySymbol assembly, AssemblyReference asmRef, DiagnosticBag diagnostics)
        {
            AssemblyIdentity asmIdentity = SourceModule.ContainingAssembly.Identity;
            AssemblyIdentity refIdentity = asmRef.Identity;
 
            if (asmIdentity.IsStrongName && !refIdentity.IsStrongName &&
                asmRef.Identity.ContentType != AssemblyContentType.WindowsRuntime)
            {
                // Dev12 reported error, we have changed it to a warning to allow referencing libraries
                // built for platforms that don't support strong names.
                diagnostics.Add(new CSDiagnosticInfo(ErrorCode.WRN_ReferencedAssemblyDoesNotHaveStrongName, assembly), NoLocation.Singleton);
            }
 
            if (OutputKind != OutputKind.NetModule &&
               !string.IsNullOrEmpty(refIdentity.CultureName) &&
               !string.Equals(refIdentity.CultureName, asmIdentity.CultureName, StringComparison.OrdinalIgnoreCase))
            {
                diagnostics.Add(new CSDiagnosticInfo(ErrorCode.WRN_RefCultureMismatch, assembly, refIdentity.CultureName), NoLocation.Singleton);
            }
 
            var refMachine = assembly.Machine;
            // If other assembly is agnostic this is always safe
            // Also, if no mscorlib was specified for back compat we add a reference to mscorlib
            // that resolves to the current framework directory. If the compiler is 64-bit
            // this is a 64-bit mscorlib, which will produce a warning if /platform:x86 is
            // specified. A reference to the default mscorlib should always succeed without
            // warning so we ignore it here.
            if ((object)assembly != (object)assembly.CorLibrary &&
                !(refMachine == Machine.I386 && !assembly.Bit32Required))
            {
                var machine = SourceModule.Machine;
 
                if (!(machine == Machine.I386 && !SourceModule.Bit32Required) &&
                    machine != refMachine)
                {
                    // Different machine types, and neither is agnostic
                    diagnostics.Add(new CSDiagnosticInfo(ErrorCode.WRN_ConflictingMachineAssembly, assembly), NoLocation.Singleton);
                }
            }
 
            if (_embeddedTypesManagerOpt != null && _embeddedTypesManagerOpt.IsFrozen)
            {
                _embeddedTypesManagerOpt.ReportIndirectReferencesToLinkedAssemblies(assembly, diagnostics);
            }
        }
 
        internal sealed override IEnumerable<Cci.INestedTypeDefinition> GetSynthesizedNestedTypes(NamedTypeSymbol container)
        {
            return null;
        }
 
        public sealed override IEnumerable<(Cci.ITypeDefinition, ImmutableArray<Cci.DebugSourceDocument>)> GetTypeToDebugDocumentMap(EmitContext context)
        {
            var typesToProcess = ArrayBuilder<Cci.ITypeDefinition>.GetInstance();
            var debugDocuments = ArrayBuilder<Cci.DebugSourceDocument>.GetInstance();
            var methodDocumentList = PooledHashSet<Cci.DebugSourceDocument>.GetInstance();
 
            var namespacesAndTopLevelTypesToProcess = ArrayBuilder<NamespaceOrTypeSymbol>.GetInstance();
            namespacesAndTopLevelTypesToProcess.Push(SourceModule.GlobalNamespace);
            while (namespacesAndTopLevelTypesToProcess.Count > 0)
            {
                var symbol = namespacesAndTopLevelTypesToProcess.Pop();
 
                switch (symbol.Kind)
                {
                    case SymbolKind.Namespace:
                        var location = GetSmallestSourceLocationOrNull(symbol);
 
                        // filtering out synthesized symbols not having real source
                        // locations such as anonymous types, etc...
                        if (location != null)
                        {
                            foreach (var member in symbol.GetMembers())
                            {
                                switch (member.Kind)
                                {
                                    case SymbolKind.Namespace:
                                    case SymbolKind.NamedType:
                                        namespacesAndTopLevelTypesToProcess.Push((NamespaceOrTypeSymbol)member);
                                        break;
                                    default:
                                        throw ExceptionUtilities.UnexpectedValue(member.Kind);
                                }
                            }
                        }
                        break;
                    case SymbolKind.NamedType:
                        Debug.Assert(debugDocuments.Count == 0);
                        Debug.Assert(methodDocumentList.Count == 0);
                        Debug.Assert(typesToProcess.Count == 0);
 
                        var typeDefinition = (Cci.ITypeDefinition)symbol.GetCciAdapter();
                        typesToProcess.Push(typeDefinition);
                        GetDocumentsForMethodsAndNestedTypes(methodDocumentList, typesToProcess, context);
 
                        foreach (var loc in symbol.Locations)
                        {
                            if (!loc.IsInSource)
                            {
                                continue;
                            }
 
                            var span = loc.GetLineSpan();
                            var debugDocument = DebugDocumentsBuilder.TryGetDebugDocument(span.Path, basePath: null);
 
                            // If we have a debug document that is already referenced by method debug info in this type, or a nested type,
                            // then we don't need to include it. Since its impossible to declare a nested type without also including
                            // a declaration for its containing type, we don't need to consider nested types in this method itself.
                            if (debugDocument is not null && !methodDocumentList.Contains(debugDocument))
                            {
                                debugDocuments.Add(debugDocument);
                            }
                        }
 
                        if (debugDocuments.Count > 0)
                        {
                            yield return (typeDefinition, debugDocuments.ToImmutable());
                        }
 
                        debugDocuments.Clear();
                        methodDocumentList.Clear();
                        break;
                    default:
                        throw ExceptionUtilities.UnexpectedValue(symbol.Kind);
                }
            }
 
            namespacesAndTopLevelTypesToProcess.Free();
            debugDocuments.Free();
            methodDocumentList.Free();
            typesToProcess.Free();
        }
 
        /// <summary>
        /// Gets a list of documents from the method definitions in the types in <paramref name="typesToProcess"/> or any
        /// nested types of those types.
        /// </summary>
        private static void GetDocumentsForMethodsAndNestedTypes(PooledHashSet<Cci.DebugSourceDocument> documentList, ArrayBuilder<Cci.ITypeDefinition> typesToProcess, EmitContext context)
        {
            // Temporarily disable assert to unblock getting net8.0 teststing re-nabled on Unix. Will 
            // remove this shortly.
            // https://github.com/dotnet/roslyn/issues/71571
            // Debug.Assert(!context.MetadataOnly);
 
            while (typesToProcess.Count > 0)
            {
                var definition = typesToProcess.Pop();
 
                var typeMethods = definition.GetMethods(context);
                foreach (var method in typeMethods)
                {
                    var body = method.GetBody(context);
                    if (body is null)
                    {
                        continue;
                    }
 
                    foreach (var point in body.SequencePoints)
                    {
                        documentList.Add(point.Document);
                    }
                }
 
                var nestedTypes = definition.GetNestedTypes(context);
                foreach (var nestedTypeDefinition in nestedTypes)
                {
                    typesToProcess.Push(nestedTypeDefinition);
                }
            }
        }
 
        public sealed override MultiDictionary<Cci.DebugSourceDocument, Cci.DefinitionWithLocation> GetSymbolToLocationMap()
        {
            var result = new MultiDictionary<Cci.DebugSourceDocument, Cci.DefinitionWithLocation>();
 
            var namespacesAndTypesToProcess = new Stack<NamespaceOrTypeSymbol>();
            namespacesAndTypesToProcess.Push(SourceModule.GlobalNamespace);
 
            Location location = null;
 
            while (namespacesAndTypesToProcess.Count > 0)
            {
                NamespaceOrTypeSymbol symbol = namespacesAndTypesToProcess.Pop();
                switch (symbol.Kind)
                {
                    case SymbolKind.Namespace:
                        location = GetSmallestSourceLocationOrNull(symbol);
 
                        // filtering out synthesized symbols not having real source
                        // locations such as anonymous types, etc...
                        if (location != null)
                        {
                            foreach (var member in symbol.GetMembers())
                            {
                                switch (member.Kind)
                                {
                                    case SymbolKind.Namespace:
                                    case SymbolKind.NamedType:
                                        namespacesAndTypesToProcess.Push((NamespaceOrTypeSymbol)member);
                                        break;
 
                                    default:
                                        throw ExceptionUtilities.UnexpectedValue(member.Kind);
                                }
                            }
                        }
                        break;
 
                    case SymbolKind.NamedType:
                        location = GetSmallestSourceLocationOrNull(symbol);
                        if (location != null)
                        {
                            //  add this named type location
                            AddSymbolLocation(result, location, (Cci.IDefinition)symbol.GetCciAdapter());
 
                            foreach (var member in symbol.GetMembers())
                            {
                                switch (member.Kind)
                                {
                                    case SymbolKind.NamedType:
                                        namespacesAndTypesToProcess.Push((NamespaceOrTypeSymbol)member);
                                        break;
 
                                    case SymbolKind.Method:
                                        // NOTE: Dev11 does not add synthesized static constructors to this map,
                                        //       but adds synthesized instance constructors, Roslyn adds both
                                        var method = (MethodSymbol)member;
                                        if (!method.ShouldEmit())
                                        {
                                            break;
                                        }
 
                                        AddSymbolLocation(result, member);
                                        break;
 
                                    case SymbolKind.Property:
                                        AddSymbolLocation(result, member);
                                        break;
                                    case SymbolKind.Field:
                                        if (member is TupleErrorFieldSymbol)
                                        {
                                            break;
                                        }
 
                                        // NOTE: Dev11 does not add synthesized backing fields for properties,
                                        //       but adds backing fields for events, Roslyn adds both
                                        AddSymbolLocation(result, member);
                                        break;
 
                                    case SymbolKind.Event:
                                        AddSymbolLocation(result, member);
                                        //  event backing fields do not show up in GetMembers
                                        {
                                            FieldSymbol field = ((EventSymbol)member).AssociatedField;
                                            if ((object)field != null)
                                            {
                                                AddSymbolLocation(result, field);
                                            }
                                        }
                                        break;
 
                                    default:
                                        throw ExceptionUtilities.UnexpectedValue(member.Kind);
                                }
                            }
                        }
                        break;
 
                    default:
                        throw ExceptionUtilities.UnexpectedValue(symbol.Kind);
                }
            }
 
            return result;
        }
 
        private void AddSymbolLocation(MultiDictionary<Cci.DebugSourceDocument, Cci.DefinitionWithLocation> result, Symbol symbol)
        {
            var location = GetSmallestSourceLocationOrNull(symbol);
            if (location != null)
            {
                AddSymbolLocation(result, location, (Cci.IDefinition)symbol.GetCciAdapter());
            }
        }
 
        private void AddSymbolLocation(MultiDictionary<Cci.DebugSourceDocument, Cci.DefinitionWithLocation> result, Location location, Cci.IDefinition definition)
        {
            FileLinePositionSpan span = location.GetLineSpan();
 
            Cci.DebugSourceDocument doc = DebugDocumentsBuilder.TryGetDebugDocument(span.Path, basePath: location.SourceTree.FilePath);
 
            if (doc != null)
            {
                result.Add(doc,
                           new Cci.DefinitionWithLocation(
                               definition,
                               span.StartLinePosition.Line,
                               span.StartLinePosition.Character,
                               span.EndLinePosition.Line,
                               span.EndLinePosition.Character));
            }
        }
 
        private Location GetSmallestSourceLocationOrNull(Symbol symbol)
        {
            CSharpCompilation compilation = symbol.DeclaringCompilation;
            Debug.Assert(Compilation == compilation, "How did we get symbol from different compilation?");
 
            Location result = null;
            foreach (var loc in symbol.Locations)
            {
                if (loc.IsInSource && (result == null || compilation.CompareSourceLocations(result, loc) > 0))
                {
                    result = loc;
                }
            }
 
            return result;
        }
 
        /// <summary>
        /// Ignore accessibility when resolving well-known type
        /// members, in particular for generic type arguments
        /// (e.g.: binding to internal types in the EE).
        /// </summary>
        internal virtual bool IgnoreAccessibility => false;
 
        /// <summary>
        /// Override the dynamic operation context type for all dynamic calls in the module.
        /// </summary>
        internal virtual NamedTypeSymbol GetDynamicOperationContextType(NamedTypeSymbol contextType)
        {
            return contextType;
        }
 
        internal virtual VariableSlotAllocator TryCreateVariableSlotAllocator(MethodSymbol method, MethodSymbol topLevelMethod, DiagnosticBag diagnostics)
        {
            return null;
        }
 
        internal virtual MethodInstrumentation GetMethodBodyInstrumentations(MethodSymbol method)
            => new MethodInstrumentation { Kinds = EmitOptions.InstrumentationKinds };
 
        internal virtual ImmutableArray<AnonymousTypeKey> GetPreviousAnonymousTypes()
        {
            return ImmutableArray<AnonymousTypeKey>.Empty;
        }
 
        internal virtual ImmutableArray<SynthesizedDelegateKey> GetPreviousAnonymousDelegates()
        {
            return ImmutableArray<SynthesizedDelegateKey>.Empty;
        }
 
        internal virtual int GetNextAnonymousTypeIndex()
        {
            return 0;
        }
 
        internal virtual int GetNextAnonymousDelegateIndex()
        {
            return 0;
        }
 
        internal virtual bool TryGetPreviousAnonymousTypeValue(AnonymousTypeManager.AnonymousTypeOrDelegateTemplateSymbol template, out AnonymousTypeValue typeValue)
        {
            Debug.Assert(Compilation == template.DeclaringCompilation);
 
            typeValue = default;
            return false;
        }
 
        internal virtual bool TryGetAnonymousDelegateValue(AnonymousTypeManager.AnonymousDelegateTemplateSymbol template, out SynthesizedDelegateValue delegateValue)
        {
            Debug.Assert(Compilation == template.DeclaringCompilation);
 
            delegateValue = default;
            return false;
        }
 
        public sealed override IEnumerable<Cci.INamespaceTypeDefinition> GetAnonymousTypeDefinitions(EmitContext context)
        {
            if (context.MetadataOnly)
            {
                return SpecializedCollections.EmptyEnumerable<Cci.INamespaceTypeDefinition>();
            }
 
            return Compilation.AnonymousTypeManager.GetAllCreatedTemplates()
#if DEBUG
                   .Select(type => type.GetCciAdapter())
 
#endif
                   ;
        }
 
        public override IEnumerable<Cci.INamespaceTypeDefinition> GetTopLevelSourceTypeDefinitions(EmitContext context)
        {
            var namespacesToProcess = new Stack<NamespaceSymbol>();
            namespacesToProcess.Push(SourceModule.GlobalNamespace);
 
            while (namespacesToProcess.Count > 0)
            {
                var ns = namespacesToProcess.Pop();
                foreach (var member in ns.GetMembers())
                {
                    if (member.Kind == SymbolKind.Namespace)
                    {
                        namespacesToProcess.Push((NamespaceSymbol)member);
                    }
                    else
                    {
                        yield return ((NamedTypeSymbol)member).GetCciAdapter();
                    }
                }
            }
        }
 
        private static void GetExportedTypes(NamespaceOrTypeSymbol symbol, int parentIndex, ArrayBuilder<Cci.ExportedType> builder)
        {
            int index;
            if (symbol.Kind == SymbolKind.NamedType)
            {
                if (symbol.DeclaredAccessibility != Accessibility.Public)
                {
                    return;
                }
 
                Debug.Assert(symbol.IsDefinition);
                index = builder.Count;
                builder.Add(new Cci.ExportedType((Cci.ITypeReference)symbol.GetCciAdapter(), parentIndex, isForwarder: false));
            }
            else
            {
                index = -1;
            }
 
            foreach (var member in symbol.GetMembers())
            {
                var namespaceOrType = member as NamespaceOrTypeSymbol;
                if ((object)namespaceOrType != null)
                {
                    GetExportedTypes(namespaceOrType, index, builder);
                }
            }
        }
 
        public sealed override ImmutableArray<Cci.ExportedType> GetExportedTypes(DiagnosticBag diagnostics)
        {
            Debug.Assert(HaveDeterminedTopLevelTypes);
 
            if (_lazyExportedTypes.IsDefault)
            {
                _lazyExportedTypes = CalculateExportedTypes();
 
                if (_lazyExportedTypes.Length > 0)
                {
                    ReportExportedTypeNameCollisions(_lazyExportedTypes, diagnostics);
                }
            }
 
            return _lazyExportedTypes;
        }
 
        /// <summary>
        /// Builds an array of public type symbols defined in netmodules included in the compilation
        /// and type forwarders defined in this compilation or any included netmodule (in this order).
        /// </summary>
        private ImmutableArray<Cci.ExportedType> CalculateExportedTypes()
        {
            SourceAssemblySymbol sourceAssembly = SourceModule.ContainingSourceAssembly;
            var builder = ArrayBuilder<Cci.ExportedType>.GetInstance();
 
            if (!OutputKind.IsNetModule())
            {
                var modules = sourceAssembly.Modules;
                for (int i = 1; i < modules.Length; i++) //NOTE: skipping modules[0]
                {
                    GetExportedTypes(modules[i].GlobalNamespace, -1, builder);
                }
            }
 
            Debug.Assert(OutputKind.IsNetModule() == sourceAssembly.DeclaringCompilation.Options.OutputKind.IsNetModule());
            GetForwardedTypes(sourceAssembly, builder);
 
            return builder.ToImmutableAndFree();
        }
 
#nullable enable
        /// <summary>
        /// Returns a set of top-level forwarded types
        /// </summary>
        internal static HashSet<NamedTypeSymbol> GetForwardedTypes(SourceAssemblySymbol sourceAssembly, ArrayBuilder<Cci.ExportedType>? builder)
        {
            var seenTopLevelForwardedTypes = new HashSet<NamedTypeSymbol>();
            GetForwardedTypes(seenTopLevelForwardedTypes, sourceAssembly.GetSourceDecodedWellKnownAttributeData(), builder);
 
            if (!sourceAssembly.DeclaringCompilation.Options.OutputKind.IsNetModule())
            {
                GetForwardedTypes(seenTopLevelForwardedTypes, sourceAssembly.GetNetModuleDecodedWellKnownAttributeData(), builder);
            }
 
            return seenTopLevelForwardedTypes;
        }
#nullable disable
 
        private void ReportExportedTypeNameCollisions(ImmutableArray<Cci.ExportedType> exportedTypes, DiagnosticBag diagnostics)
        {
            var sourceAssembly = SourceModule.ContainingSourceAssembly;
            var exportedNamesMap = new Dictionary<string, NamedTypeSymbol>(StringOrdinalComparer.Instance);
 
            foreach (var exportedType in exportedTypes)
            {
                var type = (NamedTypeSymbol)exportedType.Type.GetInternalSymbol();
 
                Debug.Assert(type.IsDefinition);
 
                if (!type.IsTopLevelType())
                {
                    continue;
                }
 
                // exported types are not emitted in EnC deltas (hence generation 0):
                string fullEmittedName = MetadataHelpers.BuildQualifiedName(
                    ((Cci.INamespaceTypeReference)type.GetCciAdapter()).NamespaceName,
                    Cci.MetadataWriter.GetMetadataName(type.GetCciAdapter(), generation: 0));
 
                // First check against types declared in the primary module
                if (ContainsTopLevelType(fullEmittedName))
                {
                    if ((object)type.ContainingAssembly == sourceAssembly)
                    {
                        diagnostics.Add(ErrorCode.ERR_ExportedTypeConflictsWithDeclaration, NoLocation.Singleton, type, type.ContainingModule);
                    }
                    else
                    {
                        diagnostics.Add(ErrorCode.ERR_ForwardedTypeConflictsWithDeclaration, NoLocation.Singleton, type);
                    }
 
                    continue;
                }
 
                NamedTypeSymbol contender;
 
                // Now check against other exported types
                if (exportedNamesMap.TryGetValue(fullEmittedName, out contender))
                {
                    if ((object)type.ContainingAssembly == sourceAssembly)
                    {
                        // all exported types precede forwarded types, therefore contender cannot be a forwarded type.
                        Debug.Assert(contender.ContainingAssembly == sourceAssembly);
 
                        diagnostics.Add(ErrorCode.ERR_ExportedTypesConflict, NoLocation.Singleton, type, type.ContainingModule, contender, contender.ContainingModule);
                    }
                    else if ((object)contender.ContainingAssembly == sourceAssembly)
                    {
                        // Forwarded type conflicts with exported type
                        diagnostics.Add(ErrorCode.ERR_ForwardedTypeConflictsWithExportedType, NoLocation.Singleton, type, type.ContainingAssembly, contender, contender.ContainingModule);
                    }
                    else
                    {
                        // Forwarded type conflicts with another forwarded type
                        diagnostics.Add(ErrorCode.ERR_ForwardedTypesConflict, NoLocation.Singleton, type, type.ContainingAssembly, contender, contender.ContainingAssembly);
                    }
 
                    continue;
                }
 
                exportedNamesMap.Add(fullEmittedName, type);
            }
        }
 
#nullable enable
        private static void GetForwardedTypes(
            HashSet<NamedTypeSymbol> seenTopLevelTypes,
            CommonAssemblyWellKnownAttributeData<NamedTypeSymbol> wellKnownAttributeData,
            ArrayBuilder<Cci.ExportedType>? builder)
        {
            if (wellKnownAttributeData?.ForwardedTypes?.Count > 0)
            {
                // (type, index of the parent exported type in builder, or -1 if the type is a top-level type)
                var stack = ArrayBuilder<(NamedTypeSymbol type, int parentIndex)>.GetInstance();
 
                // Hashset enumeration is not guaranteed to be deterministic. Emitting in the order of fully qualified names.
                IEnumerable<NamedTypeSymbol> orderedForwardedTypes = wellKnownAttributeData.ForwardedTypes;
 
                if (builder is object)
                {
                    orderedForwardedTypes = orderedForwardedTypes.OrderBy(t => t.OriginalDefinition.ToDisplayString(SymbolDisplayFormat.QualifiedNameArityFormat));
                }
 
                foreach (NamedTypeSymbol forwardedType in orderedForwardedTypes)
                {
                    NamedTypeSymbol originalDefinition = forwardedType.OriginalDefinition;
                    Debug.Assert((object)originalDefinition.ContainingType == null, "How did a nested type get forwarded?");
 
                    // Since we need to allow multiple constructions of the same generic type at the source
                    // level, we need to de-dup the original definitions before emitting.
                    if (!seenTopLevelTypes.Add(originalDefinition)) continue;
 
                    if (builder is object)
                    {
                        // Return all nested types.
                        // Note the order: depth first, children in reverse order (to match dev10, not a requirement).
                        Debug.Assert(stack.Count == 0);
                        stack.Push((originalDefinition, -1));
 
                        while (stack.Count > 0)
                        {
                            var (type, parentIndex) = stack.Pop();
 
                            // In general, we don't want private types to appear in the ExportedTypes table.
                            // BREAK: dev11 emits these types.  The problem was discovered in dev10, but failed
                            // to meet the bar Bug: Dev10/258038 and was left as-is.
                            if (type.DeclaredAccessibility == Accessibility.Private)
                            {
                                // NOTE: this will also exclude nested types of type
                                continue;
                            }
 
                            // NOTE: not bothering to put nested types in seenTypes - the top-level type is adequate protection.
 
                            int index = builder.Count;
                            builder.Add(new Cci.ExportedType(type.GetCciAdapter(), parentIndex, isForwarder: true));
 
                            // Iterate backwards so they get popped in forward order.
                            ImmutableArray<NamedTypeSymbol> nested = type.GetTypeMembers(); // Ordered.
                            for (int i = nested.Length - 1; i >= 0; i--)
                            {
                                stack.Push((nested[i], index));
                            }
                        }
                    }
                }
 
                stack.Free();
            }
        }
#nullable disable
 
        internal IEnumerable<AssemblySymbol> GetReferencedAssembliesUsedSoFar()
        {
            foreach (AssemblySymbol a in SourceModule.GetReferencedAssemblySymbols())
            {
                if (!a.IsLinked && !a.IsMissing && AssemblyOrModuleSymbolToModuleRefMap.ContainsKey(a))
                {
                    yield return a;
                }
            }
        }
 
        private NamedTypeSymbol GetUntranslatedSpecialType(SpecialType specialType, SyntaxNode syntaxNodeOpt, DiagnosticBag diagnostics)
        {
            Debug.Assert(diagnostics != null);
 
            var typeSymbol = SourceModule.ContainingAssembly.GetSpecialType(specialType);
 
            DiagnosticInfo info = typeSymbol.GetUseSiteInfo().DiagnosticInfo;
            if (info != null)
            {
                Symbol.ReportUseSiteDiagnostic(info,
                                               diagnostics,
                                               syntaxNodeOpt != null ? syntaxNodeOpt.Location : NoLocation.Singleton);
            }
 
            return typeSymbol;
        }
 
        internal sealed override Cci.INamedTypeReference GetSpecialType(SpecialType specialType, SyntaxNode syntaxNodeOpt, DiagnosticBag diagnostics)
        {
            return Translate(GetUntranslatedSpecialType(specialType, syntaxNodeOpt, diagnostics),
                             diagnostics: diagnostics,
                             syntaxNodeOpt: syntaxNodeOpt,
                             needDeclaration: true);
        }
 
        public sealed override Cci.IMethodReference GetInitArrayHelper()
        {
            return ((MethodSymbol)Compilation.GetWellKnownTypeMember(WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__InitializeArrayArrayRuntimeFieldHandle))?.GetCciAdapter();
        }
 
        public sealed override bool IsPlatformType(Cci.ITypeReference typeRef, Cci.PlatformType platformType)
        {
            var namedType = typeRef.GetInternalSymbol() as NamedTypeSymbol;
            if ((object)namedType != null)
            {
                if (platformType == Cci.PlatformType.SystemType)
                {
                    return (object)namedType == (object)Compilation.GetWellKnownType(WellKnownType.System_Type);
                }
 
                return namedType.SpecialType == (SpecialType)platformType;
            }
 
            return false;
        }
 
        protected sealed override Cci.IAssemblyReference GetCorLibraryReferenceToEmit(CodeAnalysis.Emit.EmitContext context)
        {
            AssemblySymbol corLibrary = CorLibrary;
 
            if (!corLibrary.IsMissing &&
                !corLibrary.IsLinked &&
                !ReferenceEquals(corLibrary, SourceModule.ContainingAssembly))
            {
                return Translate(corLibrary, context.Diagnostics);
            }
 
            return null;
        }
 
        internal sealed override Cci.IAssemblyReference Translate(AssemblySymbol assembly, DiagnosticBag diagnostics)
        {
            if (ReferenceEquals(SourceModule.ContainingAssembly, assembly))
            {
                return (Cci.IAssemblyReference)this;
            }
 
            Cci.IModuleReference reference;
 
            if (AssemblyOrModuleSymbolToModuleRefMap.TryGetValue(assembly, out reference))
            {
                return (Cci.IAssemblyReference)reference;
            }
 
            AssemblyReference asmRef = new AssemblyReference(assembly);
 
            AssemblyReference cachedAsmRef = (AssemblyReference)AssemblyOrModuleSymbolToModuleRefMap.GetOrAdd(assembly, asmRef);
 
            if (cachedAsmRef == asmRef)
            {
                ValidateReferencedAssembly(assembly, cachedAsmRef, diagnostics);
            }
 
            // TryAdd because whatever is associated with assembly should be associated with Modules[0]
            AssemblyOrModuleSymbolToModuleRefMap.TryAdd(assembly.Modules[0], cachedAsmRef);
 
            return cachedAsmRef;
        }
 
        internal Cci.IModuleReference Translate(ModuleSymbol module, DiagnosticBag diagnostics)
        {
            if (ReferenceEquals(SourceModule, module))
            {
                return this;
            }
 
            if ((object)module == null)
            {
                return null;
            }
 
            Cci.IModuleReference moduleRef;
 
            if (AssemblyOrModuleSymbolToModuleRefMap.TryGetValue(module, out moduleRef))
            {
                return moduleRef;
            }
 
            moduleRef = TranslateModule(module, diagnostics);
            moduleRef = AssemblyOrModuleSymbolToModuleRefMap.GetOrAdd(module, moduleRef);
 
            return moduleRef;
        }
 
        protected virtual Cci.IModuleReference TranslateModule(ModuleSymbol module, DiagnosticBag diagnostics)
        {
            AssemblySymbol container = module.ContainingAssembly;
 
            if ((object)container != null && ReferenceEquals(container.Modules[0], module))
            {
                Cci.IModuleReference moduleRef = new AssemblyReference(container);
                Cci.IModuleReference cachedModuleRef = AssemblyOrModuleSymbolToModuleRefMap.GetOrAdd(container, moduleRef);
 
                if (cachedModuleRef == moduleRef)
                {
                    ValidateReferencedAssembly(container, (AssemblyReference)moduleRef, diagnostics);
                }
                else
                {
                    moduleRef = cachedModuleRef;
                }
 
                return moduleRef;
            }
            else
            {
                return new ModuleReference(this, module);
            }
        }
 
        internal Cci.INamedTypeReference Translate(
            NamedTypeSymbol namedTypeSymbol,
            SyntaxNode syntaxNodeOpt,
            DiagnosticBag diagnostics,
            bool fromImplements = false,
            bool needDeclaration = false)
        {
            Debug.Assert(namedTypeSymbol.IsDefinitionOrDistinct());
            Debug.Assert(diagnostics != null);
 
            // Anonymous type being translated
            if (namedTypeSymbol.IsAnonymousType)
            {
                Debug.Assert(!needDeclaration);
                namedTypeSymbol = AnonymousTypeManager.TranslateAnonymousTypeSymbol(namedTypeSymbol);
            }
            else if (namedTypeSymbol.IsTupleType)
            {
                CheckTupleUnderlyingType(namedTypeSymbol, syntaxNodeOpt, diagnostics);
            }
 
            // Substitute error types with a special singleton object.
            // Unreported bad types can come through NoPia embedding, for example.
            if (namedTypeSymbol.OriginalDefinition.Kind == SymbolKind.ErrorType)
            {
                ErrorTypeSymbol errorType = (ErrorTypeSymbol)namedTypeSymbol.OriginalDefinition;
                DiagnosticInfo diagInfo = errorType.GetUseSiteInfo().DiagnosticInfo ?? errorType.ErrorInfo;
 
                if (diagInfo == null && namedTypeSymbol.Kind == SymbolKind.ErrorType)
                {
                    errorType = (ErrorTypeSymbol)namedTypeSymbol;
                    diagInfo = errorType.GetUseSiteInfo().DiagnosticInfo ?? errorType.ErrorInfo;
                }
 
                // Try to decrease noise by not complaining about the same type over and over again.
                if (_reportedErrorTypesMap.Add(errorType))
                {
                    diagnostics.Add(new CSDiagnostic(diagInfo ?? new CSDiagnosticInfo(ErrorCode.ERR_BogusType, string.Empty), syntaxNodeOpt == null ? NoLocation.Singleton : syntaxNodeOpt.Location));
                }
 
                return CodeAnalysis.Emit.ErrorType.Singleton;
            }
 
            if (!namedTypeSymbol.IsDefinition)
            {
                // generic instantiation for sure
                Debug.Assert(!needDeclaration);
 
                if (namedTypeSymbol.IsUnboundGenericType)
                {
                    namedTypeSymbol = namedTypeSymbol.OriginalDefinition;
                }
                else
                {
                    return (Cci.INamedTypeReference)GetCciAdapter(namedTypeSymbol);
                }
            }
            else if (!needDeclaration)
            {
                object reference;
                Cci.INamedTypeReference typeRef;
 
                NamedTypeSymbol container = namedTypeSymbol.ContainingType;
 
                if (namedTypeSymbol.Arity > 0)
                {
                    if (_genericInstanceMap.TryGetValue(namedTypeSymbol, out reference))
                    {
                        return (Cci.INamedTypeReference)reference;
                    }
 
                    if ((object)container != null)
                    {
                        if (IsGenericType(container))
                        {
                            // Container is a generic instance too.
                            typeRef = new SpecializedGenericNestedTypeInstanceReference(namedTypeSymbol);
                        }
                        else
                        {
                            typeRef = new GenericNestedTypeInstanceReference(namedTypeSymbol);
                        }
                    }
                    else
                    {
                        typeRef = new GenericNamespaceTypeInstanceReference(namedTypeSymbol);
                    }
 
                    typeRef = (Cci.INamedTypeReference)_genericInstanceMap.GetOrAdd(namedTypeSymbol, typeRef);
 
                    return typeRef;
                }
                else if (IsGenericType(container))
                {
                    Debug.Assert((object)container != null);
 
                    if (_genericInstanceMap.TryGetValue(namedTypeSymbol, out reference))
                    {
                        return (Cci.INamedTypeReference)reference;
                    }
 
                    typeRef = new SpecializedNestedTypeReference(namedTypeSymbol);
                    typeRef = (Cci.INamedTypeReference)_genericInstanceMap.GetOrAdd(namedTypeSymbol, typeRef);
 
                    return typeRef;
                }
                else if (namedTypeSymbol.NativeIntegerUnderlyingType is NamedTypeSymbol underlyingType)
                {
                    namedTypeSymbol = underlyingType;
                }
            }
 
            // NoPia: See if this is a type, which definition we should copy into our assembly.
            Debug.Assert(namedTypeSymbol.IsDefinition);
 
            return _embeddedTypesManagerOpt?.EmbedTypeIfNeedTo(namedTypeSymbol, fromImplements, syntaxNodeOpt, diagnostics) ?? namedTypeSymbol.GetCciAdapter();
        }
 
        private object GetCciAdapter(Symbol symbol)
        {
            return _genericInstanceMap.GetOrAdd(symbol, s => s.GetCciAdapter());
        }
 
        private void CheckTupleUnderlyingType(NamedTypeSymbol namedTypeSymbol, SyntaxNode syntaxNodeOpt, DiagnosticBag diagnostics)
        {
            // check that underlying type of a ValueTuple is indeed a value type (or error)
            // this should never happen, in theory,
            // but if it does happen we should make it a failure.
            // NOTE: declaredBase could be null for interfaces
            var declaredBase = namedTypeSymbol.BaseTypeNoUseSiteDiagnostics;
            if ((object)declaredBase != null && declaredBase.SpecialType == SpecialType.System_ValueType)
            {
                return;
            }
 
            // Try to decrease noise by not complaining about the same type over and over again.
            if (!_reportedErrorTypesMap.Add(namedTypeSymbol))
            {
                return;
            }
 
            var location = syntaxNodeOpt == null ? NoLocation.Singleton : syntaxNodeOpt.Location;
            if ((object)declaredBase != null)
            {
                var diagnosticInfo = declaredBase.GetUseSiteInfo().DiagnosticInfo;
                if (diagnosticInfo != null && diagnosticInfo.Severity == DiagnosticSeverity.Error)
                {
                    diagnostics.Add(diagnosticInfo, location);
                    return;
                }
            }
 
            diagnostics.Add(
                new CSDiagnostic(
                    new CSDiagnosticInfo(ErrorCode.ERR_PredefinedValueTupleTypeMustBeStruct, namedTypeSymbol.MetadataName),
                    location));
        }
 
        public static bool IsGenericType(NamedTypeSymbol toCheck)
        {
            while ((object)toCheck != null)
            {
                if (toCheck.Arity > 0)
                {
                    return true;
                }
 
                toCheck = toCheck.ContainingType;
            }
 
            return false;
        }
 
        internal static Cci.IGenericParameterReference Translate(TypeParameterSymbol param)
        {
            if (!param.IsDefinition)
                throw new InvalidOperationException(string.Format(CSharpResources.GenericParameterDefinition, param.Name));
 
            return param.GetCciAdapter();
        }
 
        internal sealed override Cci.ITypeReference Translate(
            TypeSymbol typeSymbol,
            SyntaxNode syntaxNodeOpt,
            DiagnosticBag diagnostics)
        {
            Debug.Assert(diagnostics != null);
 
            switch (typeSymbol.Kind)
            {
                case SymbolKind.DynamicType:
                    return Translate(syntaxNodeOpt, diagnostics);
 
                case SymbolKind.ArrayType:
                    return Translate((ArrayTypeSymbol)typeSymbol);
 
                case SymbolKind.ErrorType:
                case SymbolKind.NamedType:
                    return Translate((NamedTypeSymbol)typeSymbol, syntaxNodeOpt, diagnostics);
 
                case SymbolKind.PointerType:
                    return Translate((PointerTypeSymbol)typeSymbol);
 
                case SymbolKind.TypeParameter:
                    return Translate((TypeParameterSymbol)typeSymbol);
 
                case SymbolKind.FunctionPointerType:
                    return Translate((FunctionPointerTypeSymbol)typeSymbol);
            }
 
            throw ExceptionUtilities.UnexpectedValue(typeSymbol.Kind);
        }
 
        internal Cci.IFieldReference Translate(
            FieldSymbol fieldSymbol,
            SyntaxNode syntaxNodeOpt,
            DiagnosticBag diagnostics,
            bool needDeclaration = false)
        {
            Debug.Assert(fieldSymbol.IsDefinitionOrDistinct());
            Debug.Assert(!fieldSymbol.IsVirtualTupleField &&
                (object)(fieldSymbol.TupleUnderlyingField ?? fieldSymbol) == fieldSymbol &&
                fieldSymbol is not TupleErrorFieldSymbol, "tuple fields should be rewritten to underlying by now");
 
            if (!fieldSymbol.IsDefinition)
            {
                Debug.Assert(!needDeclaration);
 
                return (Cci.IFieldReference)GetCciAdapter(fieldSymbol);
            }
            else if (!needDeclaration && IsGenericType(fieldSymbol.ContainingType))
            {
                object reference;
                Cci.IFieldReference fieldRef;
 
                if (_genericInstanceMap.TryGetValue(fieldSymbol, out reference))
                {
                    return (Cci.IFieldReference)reference;
                }
 
                fieldRef = new SpecializedFieldReference(fieldSymbol);
                fieldRef = (Cci.IFieldReference)_genericInstanceMap.GetOrAdd(fieldSymbol, fieldRef);
 
                return fieldRef;
            }
 
            return _embeddedTypesManagerOpt?.EmbedFieldIfNeedTo(fieldSymbol.GetCciAdapter(), syntaxNodeOpt, diagnostics) ?? fieldSymbol.GetCciAdapter();
        }
 
        internal sealed override Cci.IMethodReference Translate(MethodSymbol symbol, DiagnosticBag diagnostics, bool needDeclaration)
        {
            return Translate(symbol, null, diagnostics, null, needDeclaration);
        }
 
        internal Cci.IMethodReference Translate(
            MethodSymbol methodSymbol,
            SyntaxNode syntaxNodeOpt,
            DiagnosticBag diagnostics,
            BoundArgListOperator optArgList = null,
            bool needDeclaration = false)
        {
            Debug.Assert(!methodSymbol.IsDefaultValueTypeConstructor());
            Debug.Assert(optArgList == null || (methodSymbol.IsVararg && !needDeclaration));
 
            Cci.IMethodReference unexpandedMethodRef = Translate(methodSymbol, syntaxNodeOpt, diagnostics, needDeclaration);
 
            if (optArgList != null && optArgList.Arguments.Length > 0)
            {
                Cci.IParameterTypeInformation[] @params = new Cci.IParameterTypeInformation[optArgList.Arguments.Length];
                int ordinal = methodSymbol.ParameterCount;
 
                for (int i = 0; i < @params.Length; i++)
                {
                    @params[i] = new ArgListParameterTypeInformation(ordinal,
                                                                    !optArgList.ArgumentRefKindsOpt.IsDefaultOrEmpty && optArgList.ArgumentRefKindsOpt[i] != RefKind.None,
                                                                    Translate(optArgList.Arguments[i].Type, syntaxNodeOpt, diagnostics));
                    ordinal++;
                }
 
                return new ExpandedVarargsMethodReference(unexpandedMethodRef, @params.AsImmutableOrNull());
            }
            else
            {
                return unexpandedMethodRef;
            }
        }
 
#nullable enable
        private Cci.IMethodReference Translate(
            MethodSymbol methodSymbol,
            SyntaxNode syntaxNodeOpt,
            DiagnosticBag diagnostics,
            bool needDeclaration)
        {
            object? reference;
            Cci.IMethodReference methodRef;
            NamedTypeSymbol container = methodSymbol.ContainingType;
 
            // Method of anonymous type being translated
            if (container?.IsAnonymousType == true)
            {
                Debug.Assert(!needDeclaration);
                methodSymbol = AnonymousTypeManager.TranslateAnonymousTypeMethodSymbol(methodSymbol);
            }
 
            Debug.Assert(methodSymbol.IsDefinitionOrDistinct());
 
            if (!methodSymbol.IsDefinition)
            {
                Debug.Assert(!needDeclaration);
                Debug.Assert(!(methodSymbol.OriginalDefinition is NativeIntegerMethodSymbol));
                Debug.Assert(!(methodSymbol.ConstructedFrom is NativeIntegerMethodSymbol));
 
                return (Cci.IMethodReference)GetCciAdapter(methodSymbol);
            }
            else if (!needDeclaration)
            {
                bool methodIsGeneric = methodSymbol.IsGenericMethod;
                bool typeIsGeneric = IsGenericType(container);
 
                if (methodIsGeneric || typeIsGeneric)
                {
                    if (_genericInstanceMap.TryGetValue(methodSymbol, out reference))
                    {
                        return (Cci.IMethodReference)reference;
                    }
 
                    if (methodIsGeneric)
                    {
                        if (typeIsGeneric)
                        {
                            // Specialized and generic instance at the same time.
                            methodRef = new SpecializedGenericMethodInstanceReference(methodSymbol);
                        }
                        else
                        {
                            methodRef = new GenericMethodInstanceReference(methodSymbol);
                        }
                    }
                    else
                    {
                        Debug.Assert(typeIsGeneric);
                        methodRef = new SpecializedMethodReference(methodSymbol);
                    }
 
                    methodRef = (Cci.IMethodReference)_genericInstanceMap.GetOrAdd(methodSymbol, methodRef);
 
                    return methodRef;
                }
                else if (methodSymbol is NativeIntegerMethodSymbol { UnderlyingMethod: MethodSymbol underlyingMethod })
                {
                    methodSymbol = underlyingMethod;
                }
            }
 
            if (_embeddedTypesManagerOpt != null)
            {
                return _embeddedTypesManagerOpt.EmbedMethodIfNeedTo(methodSymbol.GetCciAdapter(), syntaxNodeOpt, diagnostics);
            }
 
            return methodSymbol.GetCciAdapter();
        }
#nullable disable
 
        internal Cci.IMethodReference TranslateOverriddenMethodReference(
            MethodSymbol methodSymbol,
            CSharpSyntaxNode syntaxNodeOpt,
            DiagnosticBag diagnostics)
        {
            Cci.IMethodReference methodRef;
            NamedTypeSymbol container = methodSymbol.ContainingType;
 
            if (IsGenericType(container))
            {
                if (methodSymbol.IsDefinition)
                {
                    object reference;
 
                    if (_genericInstanceMap.TryGetValue(methodSymbol, out reference))
                    {
                        methodRef = (Cci.IMethodReference)reference;
                    }
                    else
                    {
                        methodRef = new SpecializedMethodReference(methodSymbol);
                        methodRef = (Cci.IMethodReference)_genericInstanceMap.GetOrAdd(methodSymbol, methodRef);
                    }
                }
                else
                {
                    methodRef = new SpecializedMethodReference(methodSymbol);
                }
            }
            else
            {
                Debug.Assert(methodSymbol.IsDefinition);
 
                if (_embeddedTypesManagerOpt != null)
                {
                    methodRef = _embeddedTypesManagerOpt.EmbedMethodIfNeedTo(methodSymbol.GetCciAdapter(), syntaxNodeOpt, diagnostics);
                }
                else
                {
                    methodRef = methodSymbol.GetCciAdapter();
                }
            }
 
            return methodRef;
        }
 
        internal ImmutableArray<Cci.IParameterTypeInformation> Translate(ImmutableArray<ParameterSymbol> @params)
        {
            Debug.Assert(@params.All(p => p.IsDefinitionOrDistinct()));
 
            bool mustBeTranslated = @params.Any() && MustBeWrapped(@params.First());
            Debug.Assert(@params.All(p => mustBeTranslated == MustBeWrapped(p)), "either all or no parameters need translating");
 
            if (!mustBeTranslated)
            {
#if DEBUG
                return @params.SelectAsArray<ParameterSymbol, Cci.IParameterTypeInformation>(p => p.GetCciAdapter());
#else
                return StaticCast<Cci.IParameterTypeInformation>.From(@params);
#endif
            }
 
            return TranslateAll(@params);
        }
 
        private static bool MustBeWrapped(ParameterSymbol param)
        {
            // we represent parameters of generic methods as definitions
            // CCI wants them represented as IParameterTypeInformation
            // so we need to create a wrapper of parameters iff
            // 1) parameters are definitions and
            // 2) container is generic
            // NOTE: all parameters must always agree on whether they need wrapping
            if (param.IsDefinition)
            {
                var container = param.ContainingSymbol;
                if (ContainerIsGeneric(container))
                {
                    return true;
                }
            }
 
            return false;
        }
 
        private ImmutableArray<Cci.IParameterTypeInformation> TranslateAll(ImmutableArray<ParameterSymbol> @params)
        {
            var builder = ArrayBuilder<Cci.IParameterTypeInformation>.GetInstance();
            foreach (var param in @params)
            {
                builder.Add(CreateParameterTypeInformationWrapper(param));
            }
            return builder.ToImmutableAndFree();
        }
 
        private Cci.IParameterTypeInformation CreateParameterTypeInformationWrapper(ParameterSymbol param)
        {
            object reference;
            Cci.IParameterTypeInformation paramRef;
 
            if (_genericInstanceMap.TryGetValue(param, out reference))
            {
                return (Cci.IParameterTypeInformation)reference;
            }
 
            paramRef = new ParameterTypeInformation(param);
            paramRef = (Cci.IParameterTypeInformation)_genericInstanceMap.GetOrAdd(param, paramRef);
 
            return paramRef;
        }
 
        private static bool ContainerIsGeneric(Symbol container)
        {
            return container.Kind == SymbolKind.Method && ((MethodSymbol)container).IsGenericMethod ||
                IsGenericType(container.ContainingType);
        }
 
        internal Cci.ITypeReference Translate(
            SyntaxNode syntaxNodeOpt,
            DiagnosticBag diagnostics)
        {
            // Translate the dynamic type to System.Object special type to avoid duplicate entries in TypeRef table.
            // We don't need to recursively replace the dynamic type with Object since the DynamicTypeSymbol adapter
            // masquerades the TypeRef as System.Object when used to encode signatures.
            return GetSpecialType(SpecialType.System_Object, syntaxNodeOpt, diagnostics);
        }
 
        internal Cci.IArrayTypeReference Translate(ArrayTypeSymbol symbol)
        {
            return (Cci.IArrayTypeReference)GetCciAdapter(symbol);
        }
 
        internal Cci.IPointerTypeReference Translate(PointerTypeSymbol symbol)
        {
            return (Cci.IPointerTypeReference)GetCciAdapter(symbol);
        }
 
        internal Cci.IFunctionPointerTypeReference Translate(FunctionPointerTypeSymbol symbol)
        {
            return (Cci.IFunctionPointerTypeReference)GetCciAdapter(symbol);
        }
 
        /// <summary>
        /// Set the underlying implementation type for a given fixed-size buffer field.
        /// </summary>
        public NamedTypeSymbol SetFixedImplementationType(SourceMemberFieldSymbol field)
        {
            if (_fixedImplementationTypes == null)
            {
                Interlocked.CompareExchange(ref _fixedImplementationTypes, new Dictionary<FieldSymbol, NamedTypeSymbol>(), null);
            }
 
            lock (_fixedImplementationTypes)
            {
                NamedTypeSymbol result;
                if (_fixedImplementationTypes.TryGetValue(field, out result))
                {
                    return result;
                }
 
                result = new FixedFieldImplementationType(field);
                _fixedImplementationTypes.Add(field, result);
                AddSynthesizedDefinition(result.ContainingType, result.GetCciAdapter());
                return result;
            }
        }
 
        protected override Cci.IMethodDefinition CreatePrivateImplementationDetailsStaticConstructor(SyntaxNode syntaxOpt, DiagnosticBag diagnostics)
        {
            return new SynthesizedPrivateImplementationDetailsStaticConstructor(GetPrivateImplClass(syntaxOpt, diagnostics), GetUntranslatedSpecialType(SpecialType.System_Void, syntaxOpt, diagnostics)).GetCciAdapter();
        }
 
        internal abstract SynthesizedAttributeData SynthesizeEmbeddedAttribute();
 
        internal SynthesizedAttributeData SynthesizeIsReadOnlyAttribute(Symbol symbol)
        {
            if ((object)Compilation.SourceModule != symbol.ContainingModule)
            {
                // For symbols that are not defined in the same compilation (like NoPia), don't synthesize this attribute.
                return null;
            }
 
            return TrySynthesizeIsReadOnlyAttribute();
        }
 
        internal SynthesizedAttributeData SynthesizeRequiresLocationAttribute(ParameterSymbol symbol)
        {
            if ((object)Compilation.SourceModule != symbol.ContainingModule)
            {
                // For symbols that are not defined in the same compilation (like NoPia), don't synthesize this attribute.
                return null;
            }
 
            return TrySynthesizeRequiresLocationAttribute();
        }
 
        internal SynthesizedAttributeData SynthesizeParamCollectionAttribute(ParameterSymbol symbol)
        {
            if ((object)Compilation.SourceModule != symbol.ContainingModule)
            {
                // For symbols that are not defined in the same compilation (like NoPia), don't synthesize this attribute.
                return null;
            }
 
            return TrySynthesizeParamCollectionAttribute();
        }
 
        internal SynthesizedAttributeData SynthesizeIsUnmanagedAttribute(Symbol symbol)
        {
            if ((object)Compilation.SourceModule != symbol.ContainingModule)
            {
                // For symbols that are not defined in the same compilation (like NoPia), don't synthesize this attribute.
                return null;
            }
 
            return TrySynthesizeIsUnmanagedAttribute();
        }
 
        internal SynthesizedAttributeData SynthesizeIsByRefLikeAttribute(Symbol symbol)
        {
            if ((object)Compilation.SourceModule != symbol.ContainingModule)
            {
                // For symbols that are not defined in the same compilation (like NoPia), don't synthesize this attribute.
                return null;
            }
 
            return TrySynthesizeIsByRefLikeAttribute();
        }
 
        /// <summary>
        /// Given a type <paramref name="type"/>, which is either a nullable reference type OR
        /// is a constructed type with a nullable reference type present in its type argument tree,
        /// returns a synthesized NullableAttribute with encoded nullable transforms array.
        /// </summary>
        internal SynthesizedAttributeData SynthesizeNullableAttributeIfNecessary(Symbol symbol, byte? nullableContextValue, TypeWithAnnotations type)
        {
            if ((object)Compilation.SourceModule != symbol.ContainingModule)
            {
                // For symbols that are not defined in the same compilation (like NoPia), don't synthesize this attribute.
                return null;
            }
 
            var flagsBuilder = ArrayBuilder<byte>.GetInstance();
            type.AddNullableTransforms(flagsBuilder);
 
            SynthesizedAttributeData attribute;
            if (!flagsBuilder.Any())
            {
                attribute = null;
            }
            else
            {
                Debug.Assert(flagsBuilder.All(f => f <= 2));
                byte? commonValue = MostCommonNullableValueBuilder.GetCommonValue(flagsBuilder);
                if (commonValue != null)
                {
                    attribute = SynthesizeNullableAttributeIfNecessary(nullableContextValue, commonValue.GetValueOrDefault());
                }
                else
                {
                    NamedTypeSymbol byteType = Compilation.GetSpecialType(SpecialType.System_Byte);
                    var byteArrayType = ArrayTypeSymbol.CreateSZArray(byteType.ContainingAssembly, TypeWithAnnotations.Create(byteType));
                    var value = flagsBuilder.SelectAsArray((flag, byteType) => new TypedConstant(byteType, TypedConstantKind.Primitive, flag), byteType);
                    attribute = SynthesizeNullableAttribute(
                        WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorTransformFlags,
                        ImmutableArray.Create(new TypedConstant(byteArrayType, value)));
                }
            }
 
            flagsBuilder.Free();
            return attribute;
        }
 
        internal SynthesizedAttributeData SynthesizeNullableAttributeIfNecessary(byte? nullableContextValue, byte nullableValue)
        {
            if (nullableValue == nullableContextValue ||
                (nullableContextValue == null && nullableValue == 0))
            {
                return null;
            }
 
            NamedTypeSymbol byteType = Compilation.GetSpecialType(SpecialType.System_Byte);
            return SynthesizeNullableAttribute(
                WellKnownMember.System_Runtime_CompilerServices_NullableAttribute__ctorByte,
                ImmutableArray.Create(new TypedConstant(byteType, TypedConstantKind.Primitive, nullableValue)));
        }
 
        internal virtual SynthesizedAttributeData SynthesizeNullableAttribute(WellKnownMember member, ImmutableArray<TypedConstant> arguments)
        {
            // For modules, this attribute should be present. Only assemblies generate and embed this type.
            // https://github.com/dotnet/roslyn/issues/30062 Should not be optional.
            return Compilation.TrySynthesizeAttribute(member, arguments, isOptionalUse: true);
        }
 
        internal SynthesizedAttributeData SynthesizeNullableContextAttribute(Symbol symbol, byte value)
        {
            var module = Compilation.SourceModule;
            if ((object)module != symbol && (object)module != symbol.ContainingModule)
            {
                // For symbols that are not defined in the same compilation (like NoPia), don't synthesize this attribute.
                return null;
            }
 
            return SynthesizeNullableContextAttribute(
                ImmutableArray.Create(new TypedConstant(Compilation.GetSpecialType(SpecialType.System_Byte), TypedConstantKind.Primitive, value)));
        }
 
        internal virtual SynthesizedAttributeData SynthesizeNullableContextAttribute(ImmutableArray<TypedConstant> arguments)
        {
            // For modules, this attribute should be present. Only assemblies generate and embed this type.
            // https://github.com/dotnet/roslyn/issues/30062 Should not be optional.
            return Compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_NullableContextAttribute__ctor, arguments, isOptionalUse: true);
        }
 
        internal SynthesizedAttributeData SynthesizePreserveBaseOverridesAttribute()
        {
            return Compilation.TrySynthesizeAttribute(SpecialMember.System_Runtime_CompilerServices_PreserveBaseOverridesAttribute__ctor, isOptionalUse: true);
        }
 
        internal SynthesizedAttributeData SynthesizeNativeIntegerAttribute(Symbol symbol, TypeSymbol type)
        {
            Debug.Assert((object)type != null);
            Debug.Assert(type.ContainsNativeIntegerWrapperType());
            Debug.Assert(Compilation.ShouldEmitNativeIntegerAttributes());
 
            if ((object)Compilation.SourceModule != symbol.ContainingModule)
            {
                // For symbols that are not defined in the same compilation (like NoPia), don't synthesize this attribute.
                return null;
            }
 
            var builder = ArrayBuilder<bool>.GetInstance();
            CSharpCompilation.NativeIntegerTransformsEncoder.Encode(builder, type);
 
            Debug.Assert(builder.Any());
            Debug.Assert(builder.Contains(true));
 
            SynthesizedAttributeData attribute;
            if (builder.Count == 1 && builder[0])
            {
                attribute = SynthesizeNativeIntegerAttribute(WellKnownMember.System_Runtime_CompilerServices_NativeIntegerAttribute__ctor, ImmutableArray<TypedConstant>.Empty);
            }
            else
            {
                NamedTypeSymbol booleanType = Compilation.GetSpecialType(SpecialType.System_Boolean);
                Debug.Assert((object)booleanType != null);
                var transformFlags = builder.SelectAsArray((flag, constantType) => new TypedConstant(constantType, TypedConstantKind.Primitive, flag), booleanType);
                var boolArray = ArrayTypeSymbol.CreateSZArray(booleanType.ContainingAssembly, TypeWithAnnotations.Create(booleanType));
                var arguments = ImmutableArray.Create(new TypedConstant(boolArray, transformFlags));
                attribute = SynthesizeNativeIntegerAttribute(WellKnownMember.System_Runtime_CompilerServices_NativeIntegerAttribute__ctorTransformFlags, arguments);
            }
 
            builder.Free();
            return attribute;
        }
 
        internal virtual SynthesizedAttributeData SynthesizeNativeIntegerAttribute(WellKnownMember member, ImmutableArray<TypedConstant> arguments)
        {
            Debug.Assert(Compilation.ShouldEmitNativeIntegerAttributes());
 
            // For modules, this attribute should be present. Only assemblies generate and embed this type.
            // https://github.com/dotnet/roslyn/issues/30062 Should not be optional.
            return Compilation.TrySynthesizeAttribute(member, arguments, isOptionalUse: true);
        }
 
        internal SynthesizedAttributeData SynthesizeScopedRefAttribute(ParameterSymbol symbol, ScopedKind scope)
        {
            Debug.Assert(scope != ScopedKind.None);
            Debug.Assert(!ParameterHelpers.IsRefScopedByDefault(symbol) || scope == ScopedKind.ScopedValue);
            Debug.Assert(!symbol.IsThis);
 
            if ((object)Compilation.SourceModule != symbol.ContainingModule)
            {
                // For symbols that are not defined in the same compilation (like NoPia), don't synthesize this attribute.
                return null;
            }
 
            return SynthesizeScopedRefAttribute(WellKnownMember.System_Runtime_CompilerServices_ScopedRefAttribute__ctor);
        }
 
        internal virtual SynthesizedAttributeData SynthesizeScopedRefAttribute(WellKnownMember member)
        {
            // For modules, this attribute should be present. Only assemblies generate and embed this type.
            // https://github.com/dotnet/roslyn/issues/30062 Should not be optional.
            return Compilation.TrySynthesizeAttribute(member, isOptionalUse: true);
        }
 
        internal virtual SynthesizedAttributeData SynthesizeRefSafetyRulesAttribute(ImmutableArray<TypedConstant> arguments)
        {
            // For modules, this attribute should be present. Only assemblies generate and embed this type.
            // https://github.com/dotnet/roslyn/issues/30062 Should not be optional.
            return Compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_RefSafetyRulesAttribute__ctor, arguments, isOptionalUse: true);
        }
 
        internal bool ShouldEmitNullablePublicOnlyAttribute()
        {
            // No need to look at this.GetNeedsGeneratedAttributes() since those bits are
            // only set for members generated by the rewriter which are not public.
            return Compilation.GetUsesNullableAttributes() && Compilation.EmitNullablePublicOnly;
        }
 
        internal virtual SynthesizedAttributeData SynthesizeNullablePublicOnlyAttribute(ImmutableArray<TypedConstant> arguments)
        {
            // For modules, this attribute should be present. Only assemblies generate and embed this type.
            return Compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_NullablePublicOnlyAttribute__ctor, arguments);
        }
 
        protected virtual SynthesizedAttributeData TrySynthesizeIsReadOnlyAttribute()
        {
            // For modules, this attribute should be present. Only assemblies generate and embed this type.
            return Compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_IsReadOnlyAttribute__ctor);
        }
 
        protected virtual SynthesizedAttributeData TrySynthesizeRequiresLocationAttribute()
        {
            // For modules, this attribute should be present. Only assemblies generate and embed this type.
            return Compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_RequiresLocationAttribute__ctor);
        }
 
        protected virtual SynthesizedAttributeData TrySynthesizeParamCollectionAttribute()
        {
            // For modules, this attribute should be present. Only assemblies generate and embed this type.
            return Compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_ParamCollectionAttribute__ctor);
        }
 
        protected virtual SynthesizedAttributeData TrySynthesizeIsUnmanagedAttribute()
        {
            // For modules, this attribute should be present. Only assemblies generate and embed this type.
            return Compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_IsUnmanagedAttribute__ctor);
        }
 
        protected virtual SynthesizedAttributeData TrySynthesizeIsByRefLikeAttribute()
        {
            // For modules, this attribute should be present. Only assemblies generate and embed this type.
            return Compilation.TrySynthesizeAttribute(WellKnownMember.System_Runtime_CompilerServices_IsByRefLikeAttribute__ctor);
        }
 
        private void EnsureEmbeddableAttributeExists(
            EmbeddableAttributes attribute,
            BindingDiagnosticBag diagnosticsOpt = null,
            Location locationOpt = null)
        {
            Debug.Assert(!_needsGeneratedAttributes_IsFrozen);
            Debug.Assert(diagnosticsOpt is null || attribute == EmbeddableAttributes.ParamCollectionAttribute, "Don't report any errors. They should be reported during binding.");
 
            if ((GetNeedsGeneratedAttributesInternal() & attribute) != 0)
            {
                return;
            }
 
            if (Compilation.CheckIfAttributeShouldBeEmbedded(attribute, diagnosticsOpt, locationOpt))
            {
                SetNeedsGeneratedAttributes(attribute);
            }
        }
 
        internal void EnsureIsReadOnlyAttributeExists()
        {
            EnsureEmbeddableAttributeExists(EmbeddableAttributes.IsReadOnlyAttribute);
        }
 
        internal void EnsureRequiresLocationAttributeExists()
        {
            EnsureEmbeddableAttributeExists(EmbeddableAttributes.RequiresLocationAttribute);
        }
 
        internal void EnsureParamCollectionAttributeExists(BindingDiagnosticBag diagnostics, Location location)
        {
            EnsureEmbeddableAttributeExists(EmbeddableAttributes.ParamCollectionAttribute, diagnostics, location);
        }
 
        internal void EnsureIsUnmanagedAttributeExists()
        {
            EnsureEmbeddableAttributeExists(EmbeddableAttributes.IsUnmanagedAttribute);
        }
 
        internal void EnsureNullableAttributeExists()
        {
            EnsureEmbeddableAttributeExists(EmbeddableAttributes.NullableAttribute);
        }
 
        internal void EnsureNullableContextAttributeExists()
        {
            EnsureEmbeddableAttributeExists(EmbeddableAttributes.NullableContextAttribute);
        }
 
        internal void EnsureNativeIntegerAttributeExists()
        {
            Debug.Assert(Compilation.ShouldEmitNativeIntegerAttributes());
            EnsureEmbeddableAttributeExists(EmbeddableAttributes.NativeIntegerAttribute);
        }
 
        internal void EnsureScopedRefAttributeExists()
        {
            EnsureEmbeddableAttributeExists(EmbeddableAttributes.ScopedRefAttribute);
        }
 
#nullable enable
 
        /// <summary>
        /// Creates the ThrowSwitchExpressionException helper if needed.
        /// </summary>
        internal MethodSymbol EnsureThrowSwitchExpressionExceptionExists(SyntaxNode syntaxNode, SyntheticBoundNodeFactory factory, DiagnosticBag diagnostics)
        {
            return EnsurePrivateImplClassMethodExists(syntaxNode, PrivateImplementationDetails.SynthesizedThrowSwitchExpressionExceptionFunctionName,
                                                      static (privateImplClass, factory) =>
                                                      {
                                                          TypeSymbol returnType = factory.SpecialType(SpecialType.System_Void);
                                                          TypeSymbol unmatchedValueType = factory.SpecialType(SpecialType.System_Object);
                                                          return new SynthesizedThrowSwitchExpressionExceptionMethod(privateImplClass, returnType, unmatchedValueType);
                                                      },
                                                      factory,
                                                      diagnostics);
        }
 
        private MethodSymbol EnsurePrivateImplClassMethodExists<TArg>(SyntaxNode syntaxNode, string methodName, Func<SynthesizedPrivateImplementationDetailsType, TArg, MethodSymbol> createMethodSymbol, TArg arg, DiagnosticBag diagnostics)
        {
            var privateImplClass = GetPrivateImplClass(syntaxNode, diagnostics);
            var methodAdapter = privateImplClass.PrivateImplementationDetails.GetMethod(methodName);
            if (methodAdapter is not null)
            {
                Debug.Assert(methodAdapter.Name == methodName);
                return (MethodSymbol)methodAdapter.GetInternalSymbol()!;
            }
 
            MethodSymbol methodSymbol = createMethodSymbol(privateImplClass, arg);
            Debug.Assert(methodSymbol.Name == methodName);
 
            // use add-then-get pattern to ensure the symbol exists, and then ensure we use the single "canonical" instance added by whichever thread won the race.
            privateImplClass.PrivateImplementationDetails.TryAddSynthesizedMethod(methodSymbol.GetCciAdapter());
            return (MethodSymbol)privateImplClass.PrivateImplementationDetails.GetMethod(methodName)!.GetInternalSymbol()!;
        }
 
        internal new SynthesizedPrivateImplementationDetailsType GetPrivateImplClass(SyntaxNode syntaxNodeOpt, DiagnosticBag diagnostics)
        {
            if (_lazyPrivateImplementationDetailsClass is null)
            {
                Interlocked.CompareExchange(
                    ref _lazyPrivateImplementationDetailsClass,
                    new SynthesizedPrivateImplementationDetailsType(base.GetPrivateImplClass(syntaxNodeOpt, diagnostics), SourceModule.GlobalNamespace, Compilation.ObjectType),
                    null);
            }
 
            return _lazyPrivateImplementationDetailsClass;
        }
 
        /// <summary>
        /// Creates the ThrowSwitchExpressionExceptionParameterless helper if needed.
        /// </summary>
        internal MethodSymbol EnsureThrowSwitchExpressionExceptionParameterlessExists(SyntaxNode syntaxNode, SyntheticBoundNodeFactory factory, DiagnosticBag diagnostics)
        {
            return EnsurePrivateImplClassMethodExists(syntaxNode, PrivateImplementationDetails.SynthesizedThrowSwitchExpressionExceptionParameterlessFunctionName,
                                                      static (privateImplClass, factory) =>
                                                      {
                                                          TypeSymbol returnType = factory.SpecialType(SpecialType.System_Void);
 
                                                          return new SynthesizedParameterlessThrowMethod(
                                                              privateImplClass,
                                                              returnType,
                                                              PrivateImplementationDetails.SynthesizedThrowSwitchExpressionExceptionParameterlessFunctionName,
                                                              factory.WellKnownMethod(WellKnownMember.System_Runtime_CompilerServices_SwitchExpressionException__ctor));
                                                      },
                                                      factory,
                                                      diagnostics);
        }
 
        /// <summary>
        /// Creates the ThrowInvalidOperationException helper if needed.
        /// </summary>
        internal MethodSymbol EnsureThrowInvalidOperationExceptionExists(SyntaxNode syntaxNode, SyntheticBoundNodeFactory factory, DiagnosticBag diagnostics)
        {
            return EnsurePrivateImplClassMethodExists(syntaxNode, PrivateImplementationDetails.SynthesizedThrowInvalidOperationExceptionFunctionName,
                                                      static (privateImplClass, factory) =>
                                                      {
                                                          TypeSymbol returnType = factory.SpecialType(SpecialType.System_Void);
 
                                                          return new SynthesizedParameterlessThrowMethod(
                                                              privateImplClass,
                                                              returnType,
                                                              PrivateImplementationDetails.SynthesizedThrowInvalidOperationExceptionFunctionName,
                                                              factory.WellKnownMethod(WellKnownMember.System_InvalidOperationException__ctor));
                                                      },
                                                      factory,
                                                      diagnostics);
        }
 
        internal MethodSymbol EnsureInlineArrayAsSpanExists(SyntaxNode syntaxNode, NamedTypeSymbol spanType, NamedTypeSymbol intType, DiagnosticBag diagnostics)
        {
            Debug.Assert(intType.SpecialType == SpecialType.System_Int32);
 
            return EnsurePrivateImplClassMethodExists(syntaxNode, PrivateImplementationDetails.SynthesizedInlineArrayAsSpanName,
                                                      static (privateImplClass, arg) =>
                                                      {
                                                          return new SynthesizedInlineArrayAsSpanMethod(
                                                              privateImplClass,
                                                              PrivateImplementationDetails.SynthesizedInlineArrayAsSpanName,
                                                              arg.spanType,
                                                              arg.intType);
                                                      },
                                                      (spanType, intType),
                                                      diagnostics);
        }
 
        internal NamedTypeSymbol EnsureInlineArrayTypeExists(SyntaxNode syntaxNode, SyntheticBoundNodeFactory factory, int arrayLength, DiagnosticBag diagnostics)
        {
            Debug.Assert(Compilation.Assembly.RuntimeSupportsInlineArrayTypes);
            Debug.Assert(arrayLength > 0);
 
            string typeName = GeneratedNames.MakeSynthesizedInlineArrayName(arrayLength, CurrentGenerationOrdinal);
            var privateImplClass = GetPrivateImplClass(syntaxNode, diagnostics).PrivateImplementationDetails;
            var typeAdapter = privateImplClass.GetSynthesizedType(typeName);
 
            if (typeAdapter is null)
            {
                var attributeConstructor = (MethodSymbol)factory.SpecialMember(SpecialMember.System_Runtime_CompilerServices_InlineArrayAttribute__ctor);
                Debug.Assert(attributeConstructor is { });
 
                var typeSymbol = new SynthesizedInlineArrayTypeSymbol(SourceModule, typeName, arrayLength, attributeConstructor);
                privateImplClass.TryAddSynthesizedType(typeSymbol.GetCciAdapter());
                typeAdapter = privateImplClass.GetSynthesizedType(typeName)!;
            }
 
            Debug.Assert(typeAdapter.Name == typeName);
            return (NamedTypeSymbol)typeAdapter.GetInternalSymbol()!;
        }
 
        internal NamedTypeSymbol EnsureReadOnlyListTypeExists(SyntaxNode syntaxNode, SynthesizedReadOnlyListKind kind, DiagnosticBag diagnostics)
        {
            Debug.Assert(syntaxNode is { });
            Debug.Assert(diagnostics is { });
 
            string typeName = GeneratedNames.MakeSynthesizedReadOnlyListName(kind, CurrentGenerationOrdinal);
            var privateImplClass = GetPrivateImplClass(syntaxNode, diagnostics).PrivateImplementationDetails;
            var typeAdapter = privateImplClass.GetSynthesizedType(typeName);
            NamedTypeSymbol typeSymbol;
 
            if (typeAdapter is null)
            {
                typeSymbol = SynthesizedReadOnlyListTypeSymbol.Create(SourceModule, typeName, kind);
                privateImplClass.TryAddSynthesizedType(typeSymbol.GetCciAdapter());
                typeAdapter = privateImplClass.GetSynthesizedType(typeName)!;
            }
 
            Debug.Assert(typeAdapter.Name == typeName);
            typeSymbol = (NamedTypeSymbol)typeAdapter.GetInternalSymbol()!;
 
            var info = typeSymbol.GetUseSiteInfo().DiagnosticInfo;
            if (info is { })
            {
                Symbol.ReportUseSiteDiagnostic(info, diagnostics, syntaxNode.Location);
            }
 
            return typeSymbol;
        }
 
        internal MethodSymbol EnsureInlineArrayAsReadOnlySpanExists(SyntaxNode syntaxNode, NamedTypeSymbol spanType, NamedTypeSymbol intType, DiagnosticBag diagnostics)
        {
            Debug.Assert(intType.SpecialType == SpecialType.System_Int32);
 
            return EnsurePrivateImplClassMethodExists(syntaxNode, PrivateImplementationDetails.SynthesizedInlineArrayAsReadOnlySpanName,
                                                      static (privateImplClass, arg) =>
                                                      {
                                                          return new SynthesizedInlineArrayAsReadOnlySpanMethod(
                                                              privateImplClass,
                                                              PrivateImplementationDetails.SynthesizedInlineArrayAsReadOnlySpanName,
                                                              arg.spanType,
                                                              arg.intType);
                                                      },
                                                      (spanType, intType),
                                                      diagnostics);
        }
 
        internal MethodSymbol EnsureInlineArrayElementRefExists(SyntaxNode syntaxNode, NamedTypeSymbol intType, DiagnosticBag diagnostics)
        {
            Debug.Assert(intType.SpecialType == SpecialType.System_Int32);
 
            return EnsurePrivateImplClassMethodExists(syntaxNode, PrivateImplementationDetails.SynthesizedInlineArrayElementRefName,
                                                      static (privateImplClass, intType) =>
                                                      {
                                                          return new SynthesizedInlineArrayElementRefMethod(
                                                              privateImplClass,
                                                              PrivateImplementationDetails.SynthesizedInlineArrayElementRefName,
                                                              intType);
                                                      },
                                                      intType,
                                                      diagnostics);
        }
 
        internal MethodSymbol EnsureInlineArrayElementRefReadOnlyExists(SyntaxNode syntaxNode, NamedTypeSymbol intType, DiagnosticBag diagnostics)
        {
            Debug.Assert(intType.SpecialType == SpecialType.System_Int32);
 
            return EnsurePrivateImplClassMethodExists(syntaxNode, PrivateImplementationDetails.SynthesizedInlineArrayElementRefReadOnlyName,
                                                      static (privateImplClass, intType) =>
                                                      {
                                                          return new SynthesizedInlineArrayElementRefReadOnlyMethod(
                                                              privateImplClass,
                                                              PrivateImplementationDetails.SynthesizedInlineArrayElementRefReadOnlyName,
                                                              intType);
                                                      },
                                                      intType,
                                                      diagnostics);
        }
 
        internal MethodSymbol EnsureInlineArrayFirstElementRefExists(SyntaxNode syntaxNode, DiagnosticBag diagnostics)
        {
            return EnsurePrivateImplClassMethodExists(syntaxNode, PrivateImplementationDetails.SynthesizedInlineArrayFirstElementRefName,
                                                      static (privateImplClass, _) =>
                                                      {
                                                          return new SynthesizedInlineArrayFirstElementRefMethod(
                                                              privateImplClass,
                                                              PrivateImplementationDetails.SynthesizedInlineArrayFirstElementRefName);
                                                      },
                                                      (object?)null,
                                                      diagnostics);
        }
 
        internal MethodSymbol EnsureInlineArrayFirstElementRefReadOnlyExists(SyntaxNode syntaxNode, DiagnosticBag diagnostics)
        {
            return EnsurePrivateImplClassMethodExists(syntaxNode, PrivateImplementationDetails.SynthesizedInlineArrayFirstElementRefReadOnlyName,
                                                      static (privateImplClass, _) =>
                                                      {
                                                          return new SynthesizedInlineArrayFirstElementRefReadOnlyMethod(
                                                              privateImplClass,
                                                              PrivateImplementationDetails.SynthesizedInlineArrayFirstElementRefReadOnlyName);
                                                      },
                                                      (object?)null,
                                                      diagnostics);
        }
 
#nullable disable
 
        public override IEnumerable<Cci.INamespaceTypeDefinition> GetAdditionalTopLevelTypeDefinitions(EmitContext context)
        {
            return GetAdditionalTopLevelTypes()
#if DEBUG
                   .Select(type => type.GetCciAdapter())
#endif
                   ;
        }
 
        public override IEnumerable<Cci.INamespaceTypeDefinition> GetEmbeddedTypeDefinitions(EmitContext context)
        {
            return GetEmbeddedTypes(context.Diagnostics)
#if DEBUG
                   .Select(type => type.GetCciAdapter())
#endif
                   ;
        }
 
        public sealed override ImmutableArray<NamedTypeSymbol> GetEmbeddedTypes(DiagnosticBag diagnostics)
        {
            var bindingDiagnostics = BindingDiagnosticBag.GetInstance(withDiagnostics: true, withDependencies: false);
 
            var result = GetEmbeddedTypes(bindingDiagnostics);
 
            diagnostics.AddRange(bindingDiagnostics.DiagnosticBag);
            bindingDiagnostics.Free();
            return result;
        }
 
        internal virtual ImmutableArray<NamedTypeSymbol> GetEmbeddedTypes(BindingDiagnosticBag diagnostics)
        {
            return base.GetEmbeddedTypes(diagnostics.DiagnosticBag);
        }
 
        internal bool TryGetTranslatedImports(ImportChain chain, out ImmutableArray<Cci.UsedNamespaceOrType> imports)
        {
            return _translatedImportsMap.TryGetValue(chain, out imports);
        }
 
        internal ImmutableArray<Cci.UsedNamespaceOrType> GetOrAddTranslatedImports(ImportChain chain, ImmutableArray<Cci.UsedNamespaceOrType> imports)
        {
            return _translatedImportsMap.GetOrAdd(chain, imports);
        }
    }
}