File: Operations\CSharpOperationFactory.cs
Web Access
Project: src\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Operations
{
    internal sealed partial class CSharpOperationFactory
    {
        private readonly SemanticModel _semanticModel;
 
        public CSharpOperationFactory(SemanticModel semanticModel)
        {
            _semanticModel = semanticModel;
        }
 
        [return: NotNullIfNotNull(nameof(boundNode))]
        public IOperation? Create(BoundNode? boundNode)
        {
            if (boundNode == null)
            {
                return null;
            }
 
            switch (boundNode.Kind)
            {
                case BoundKind.DeconstructValuePlaceholder:
                    return CreateBoundDeconstructValuePlaceholderOperation((BoundDeconstructValuePlaceholder)boundNode);
                case BoundKind.DeconstructionAssignmentOperator:
                    return CreateBoundDeconstructionAssignmentOperator((BoundDeconstructionAssignmentOperator)boundNode);
                case BoundKind.Call:
                    return CreateBoundCallOperation((BoundCall)boundNode);
                case BoundKind.Local:
                    return CreateBoundLocalOperation((BoundLocal)boundNode);
                case BoundKind.FieldAccess:
                    return CreateBoundFieldAccessOperation((BoundFieldAccess)boundNode);
                case BoundKind.PropertyAccess:
                    return CreateBoundPropertyAccessOperation((BoundPropertyAccess)boundNode);
                case BoundKind.IndexerAccess:
                    return CreateBoundIndexerAccessOperation((BoundIndexerAccess)boundNode);
                case BoundKind.EventAccess:
                    return CreateBoundEventAccessOperation((BoundEventAccess)boundNode);
                case BoundKind.EventAssignmentOperator:
                    return CreateBoundEventAssignmentOperatorOperation((BoundEventAssignmentOperator)boundNode);
                case BoundKind.Parameter:
                    return CreateBoundParameterOperation((BoundParameter)boundNode);
                case BoundKind.Literal:
                    return CreateBoundLiteralOperation((BoundLiteral)boundNode);
                case BoundKind.Utf8String:
                    return CreateBoundUtf8StringOperation((BoundUtf8String)boundNode);
                case BoundKind.DynamicInvocation:
                    return CreateBoundDynamicInvocationExpressionOperation((BoundDynamicInvocation)boundNode);
                case BoundKind.DynamicIndexerAccess:
                    return CreateBoundDynamicIndexerAccessExpressionOperation((BoundDynamicIndexerAccess)boundNode);
                case BoundKind.ObjectCreationExpression:
                    return CreateBoundObjectCreationExpressionOperation((BoundObjectCreationExpression)boundNode);
                case BoundKind.WithExpression:
                    return CreateBoundWithExpressionOperation((BoundWithExpression)boundNode);
                case BoundKind.DynamicObjectCreationExpression:
                    return CreateBoundDynamicObjectCreationExpressionOperation((BoundDynamicObjectCreationExpression)boundNode);
                case BoundKind.ObjectInitializerExpression:
                    return CreateBoundObjectInitializerExpressionOperation((BoundObjectInitializerExpression)boundNode);
                case BoundKind.CollectionInitializerExpression:
                    return CreateBoundCollectionInitializerExpressionOperation((BoundCollectionInitializerExpression)boundNode);
                case BoundKind.ObjectInitializerMember:
                    return CreateBoundObjectInitializerMemberOperation((BoundObjectInitializerMember)boundNode);
                case BoundKind.CollectionElementInitializer:
                    return CreateBoundCollectionElementInitializerOperation((BoundCollectionElementInitializer)boundNode);
                case BoundKind.DynamicObjectInitializerMember:
                    return CreateBoundDynamicObjectInitializerMemberOperation((BoundDynamicObjectInitializerMember)boundNode);
                case BoundKind.DynamicMemberAccess:
                    return CreateBoundDynamicMemberAccessOperation((BoundDynamicMemberAccess)boundNode);
                case BoundKind.DynamicCollectionElementInitializer:
                    return CreateBoundDynamicCollectionElementInitializerOperation((BoundDynamicCollectionElementInitializer)boundNode);
                case BoundKind.UnboundLambda:
                    return CreateUnboundLambdaOperation((UnboundLambda)boundNode);
                case BoundKind.Lambda:
                    return CreateBoundLambdaOperation((BoundLambda)boundNode);
                case BoundKind.Conversion:
                    return CreateBoundConversionOperation((BoundConversion)boundNode);
                case BoundKind.AsOperator:
                    return CreateBoundAsOperatorOperation((BoundAsOperator)boundNode);
                case BoundKind.IsOperator:
                    return CreateBoundIsOperatorOperation((BoundIsOperator)boundNode);
                case BoundKind.SizeOfOperator:
                    return CreateBoundSizeOfOperatorOperation((BoundSizeOfOperator)boundNode);
                case BoundKind.TypeOfOperator:
                    return CreateBoundTypeOfOperatorOperation((BoundTypeOfOperator)boundNode);
                case BoundKind.ArrayCreation:
                    return CreateBoundArrayCreationOperation((BoundArrayCreation)boundNode);
                case BoundKind.ArrayInitialization:
                    return CreateBoundArrayInitializationOperation((BoundArrayInitialization)boundNode);
                case BoundKind.CollectionExpression:
                    return CreateBoundCollectionExpression((BoundCollectionExpression)boundNode);
                case BoundKind.DefaultLiteral:
                    return CreateBoundDefaultLiteralOperation((BoundDefaultLiteral)boundNode);
                case BoundKind.DefaultExpression:
                    return CreateBoundDefaultExpressionOperation((BoundDefaultExpression)boundNode);
                case BoundKind.BaseReference:
                    return CreateBoundBaseReferenceOperation((BoundBaseReference)boundNode);
                case BoundKind.ThisReference:
                    return CreateBoundThisReferenceOperation((BoundThisReference)boundNode);
                case BoundKind.AssignmentOperator:
                    return CreateBoundAssignmentOperatorOrMemberInitializerOperation((BoundAssignmentOperator)boundNode);
                case BoundKind.CompoundAssignmentOperator:
                    return CreateBoundCompoundAssignmentOperatorOperation((BoundCompoundAssignmentOperator)boundNode);
                case BoundKind.IncrementOperator:
                    return CreateBoundIncrementOperatorOperation((BoundIncrementOperator)boundNode);
                case BoundKind.BadExpression:
                    return CreateBoundBadExpressionOperation((BoundBadExpression)boundNode);
                case BoundKind.NewT:
                    return CreateBoundNewTOperation((BoundNewT)boundNode);
                case BoundKind.NoPiaObjectCreationExpression:
                    return CreateNoPiaObjectCreationExpressionOperation((BoundNoPiaObjectCreationExpression)boundNode);
                case BoundKind.UnaryOperator:
                    return CreateBoundUnaryOperatorOperation((BoundUnaryOperator)boundNode);
                case BoundKind.BinaryOperator:
                case BoundKind.UserDefinedConditionalLogicalOperator:
                    return CreateBoundBinaryOperatorBase((BoundBinaryOperatorBase)boundNode);
                case BoundKind.TupleBinaryOperator:
                    return CreateBoundTupleBinaryOperatorOperation((BoundTupleBinaryOperator)boundNode);
                case BoundKind.ConditionalOperator:
                    return CreateBoundConditionalOperatorOperation((BoundConditionalOperator)boundNode);
                case BoundKind.NullCoalescingOperator:
                    return CreateBoundNullCoalescingOperatorOperation((BoundNullCoalescingOperator)boundNode);
                case BoundKind.AwaitExpression:
                    return CreateBoundAwaitExpressionOperation((BoundAwaitExpression)boundNode);
                case BoundKind.ArrayAccess:
                    return CreateBoundArrayAccessOperation((BoundArrayAccess)boundNode);
                case BoundKind.ImplicitIndexerAccess:
                    return CreateBoundImplicitIndexerAccessOperation((BoundImplicitIndexerAccess)boundNode);
                case BoundKind.InlineArrayAccess:
                    return CreateBoundInlineArrayAccessOperation((BoundInlineArrayAccess)boundNode);
                case BoundKind.NameOfOperator:
                    return CreateBoundNameOfOperatorOperation((BoundNameOfOperator)boundNode);
                case BoundKind.ThrowExpression:
                    return CreateBoundThrowExpressionOperation((BoundThrowExpression)boundNode);
                case BoundKind.AddressOfOperator:
                    return CreateBoundAddressOfOperatorOperation((BoundAddressOfOperator)boundNode);
                case BoundKind.ImplicitReceiver:
                    return CreateBoundImplicitReceiverOperation((BoundImplicitReceiver)boundNode);
                case BoundKind.ConditionalAccess:
                    return CreateBoundConditionalAccessOperation((BoundConditionalAccess)boundNode);
                case BoundKind.ConditionalReceiver:
                    return CreateBoundConditionalReceiverOperation((BoundConditionalReceiver)boundNode);
                case BoundKind.FieldEqualsValue:
                    return CreateBoundFieldEqualsValueOperation((BoundFieldEqualsValue)boundNode);
                case BoundKind.PropertyEqualsValue:
                    return CreateBoundPropertyEqualsValueOperation((BoundPropertyEqualsValue)boundNode);
                case BoundKind.ParameterEqualsValue:
                    return CreateBoundParameterEqualsValueOperation((BoundParameterEqualsValue)boundNode);
                case BoundKind.Block:
                    return CreateBoundBlockOperation((BoundBlock)boundNode);
                case BoundKind.ContinueStatement:
                    return CreateBoundContinueStatementOperation((BoundContinueStatement)boundNode);
                case BoundKind.BreakStatement:
                    return CreateBoundBreakStatementOperation((BoundBreakStatement)boundNode);
                case BoundKind.YieldBreakStatement:
                    return CreateBoundYieldBreakStatementOperation((BoundYieldBreakStatement)boundNode);
                case BoundKind.GotoStatement:
                    return CreateBoundGotoStatementOperation((BoundGotoStatement)boundNode);
                case BoundKind.NoOpStatement:
                    return CreateBoundNoOpStatementOperation((BoundNoOpStatement)boundNode);
                case BoundKind.IfStatement:
                    return CreateBoundIfStatementOperation((BoundIfStatement)boundNode);
                case BoundKind.WhileStatement:
                    return CreateBoundWhileStatementOperation((BoundWhileStatement)boundNode);
                case BoundKind.DoStatement:
                    return CreateBoundDoStatementOperation((BoundDoStatement)boundNode);
                case BoundKind.ForStatement:
                    return CreateBoundForStatementOperation((BoundForStatement)boundNode);
                case BoundKind.ForEachStatement:
                    return CreateBoundForEachStatementOperation((BoundForEachStatement)boundNode);
                case BoundKind.TryStatement:
                    return CreateBoundTryStatementOperation((BoundTryStatement)boundNode);
                case BoundKind.CatchBlock:
                    return CreateBoundCatchBlockOperation((BoundCatchBlock)boundNode);
                case BoundKind.FixedStatement:
                    return CreateBoundFixedStatementOperation((BoundFixedStatement)boundNode);
                case BoundKind.UsingStatement:
                    return CreateBoundUsingStatementOperation((BoundUsingStatement)boundNode);
                case BoundKind.ThrowStatement:
                    return CreateBoundThrowStatementOperation((BoundThrowStatement)boundNode);
                case BoundKind.ReturnStatement:
                    return CreateBoundReturnStatementOperation((BoundReturnStatement)boundNode);
                case BoundKind.YieldReturnStatement:
                    return CreateBoundYieldReturnStatementOperation((BoundYieldReturnStatement)boundNode);
                case BoundKind.LockStatement:
                    return CreateBoundLockStatementOperation((BoundLockStatement)boundNode);
                case BoundKind.BadStatement:
                    return CreateBoundBadStatementOperation((BoundBadStatement)boundNode);
                case BoundKind.LocalDeclaration:
                    return CreateBoundLocalDeclarationOperation((BoundLocalDeclaration)boundNode);
                case BoundKind.MultipleLocalDeclarations:
                case BoundKind.UsingLocalDeclarations:
                    return CreateBoundMultipleLocalDeclarationsBaseOperation((BoundMultipleLocalDeclarationsBase)boundNode);
                case BoundKind.LabelStatement:
                    return CreateBoundLabelStatementOperation((BoundLabelStatement)boundNode);
                case BoundKind.LabeledStatement:
                    return CreateBoundLabeledStatementOperation((BoundLabeledStatement)boundNode);
                case BoundKind.ExpressionStatement:
                    return CreateBoundExpressionStatementOperation((BoundExpressionStatement)boundNode);
                case BoundKind.TupleLiteral:
                case BoundKind.ConvertedTupleLiteral:
                    return CreateBoundTupleOperation((BoundTupleExpression)boundNode);
                case BoundKind.InterpolatedString:
                    return CreateBoundInterpolatedStringExpressionOperation((BoundInterpolatedString)boundNode);
                case BoundKind.StringInsert:
                    return CreateBoundInterpolationOperation((BoundStringInsert)boundNode);
                case BoundKind.LocalFunctionStatement:
                    return CreateBoundLocalFunctionStatementOperation((BoundLocalFunctionStatement)boundNode);
                case BoundKind.AnonymousObjectCreationExpression:
                    return CreateBoundAnonymousObjectCreationExpressionOperation((BoundAnonymousObjectCreationExpression)boundNode);
                case BoundKind.ConstantPattern:
                    return CreateBoundConstantPatternOperation((BoundConstantPattern)boundNode);
                case BoundKind.DeclarationPattern:
                    return CreateBoundDeclarationPatternOperation((BoundDeclarationPattern)boundNode);
                case BoundKind.RecursivePattern:
                    return CreateBoundRecursivePatternOperation((BoundRecursivePattern)boundNode);
                case BoundKind.ITuplePattern:
                    return CreateBoundRecursivePatternOperation((BoundITuplePattern)boundNode);
                case BoundKind.DiscardPattern:
                    return CreateBoundDiscardPatternOperation((BoundDiscardPattern)boundNode);
                case BoundKind.BinaryPattern:
                    return CreateBoundBinaryPatternOperation((BoundBinaryPattern)boundNode);
                case BoundKind.NegatedPattern:
                    return CreateBoundNegatedPatternOperation((BoundNegatedPattern)boundNode);
                case BoundKind.RelationalPattern:
                    return CreateBoundRelationalPatternOperation((BoundRelationalPattern)boundNode);
                case BoundKind.TypePattern:
                    return CreateBoundTypePatternOperation((BoundTypePattern)boundNode);
                case BoundKind.SlicePattern:
                    return CreateBoundSlicePatternOperation((BoundSlicePattern)boundNode);
                case BoundKind.ListPattern:
                    return CreateBoundListPatternOperation((BoundListPattern)boundNode);
                case BoundKind.SwitchStatement:
                    return CreateBoundSwitchStatementOperation((BoundSwitchStatement)boundNode);
                case BoundKind.SwitchLabel:
                    return CreateBoundSwitchLabelOperation((BoundSwitchLabel)boundNode);
                case BoundKind.IsPatternExpression:
                    return CreateBoundIsPatternExpressionOperation((BoundIsPatternExpression)boundNode);
                case BoundKind.QueryClause:
                    return CreateBoundQueryClauseOperation((BoundQueryClause)boundNode);
                case BoundKind.DelegateCreationExpression:
                    return CreateBoundDelegateCreationExpressionOperation((BoundDelegateCreationExpression)boundNode);
                case BoundKind.RangeVariable:
                    return CreateBoundRangeVariableOperation((BoundRangeVariable)boundNode);
                case BoundKind.ConstructorMethodBody:
                    return CreateConstructorBodyOperation((BoundConstructorMethodBody)boundNode);
                case BoundKind.NonConstructorMethodBody:
                    return CreateMethodBodyOperation((BoundNonConstructorMethodBody)boundNode);
                case BoundKind.DiscardExpression:
                    return CreateBoundDiscardExpressionOperation((BoundDiscardExpression)boundNode);
                case BoundKind.NullCoalescingAssignmentOperator:
                    return CreateBoundNullCoalescingAssignmentOperatorOperation((BoundNullCoalescingAssignmentOperator)boundNode);
                case BoundKind.FromEndIndexExpression:
                    return CreateFromEndIndexExpressionOperation((BoundFromEndIndexExpression)boundNode);
                case BoundKind.RangeExpression:
                    return CreateRangeExpressionOperation((BoundRangeExpression)boundNode);
                case BoundKind.SwitchSection:
                    return CreateBoundSwitchSectionOperation((BoundSwitchSection)boundNode);
                case BoundKind.ConvertedSwitchExpression:
                    return CreateBoundSwitchExpressionOperation((BoundConvertedSwitchExpression)boundNode);
                case BoundKind.SwitchExpressionArm:
                    return CreateBoundSwitchExpressionArmOperation((BoundSwitchExpressionArm)boundNode);
                case BoundKind.ObjectOrCollectionValuePlaceholder:
                    return CreateCollectionValuePlaceholderOperation((BoundObjectOrCollectionValuePlaceholder)boundNode);
                case BoundKind.FunctionPointerInvocation:
                    return CreateBoundFunctionPointerInvocationOperation((BoundFunctionPointerInvocation)boundNode);
                case BoundKind.UnconvertedAddressOfOperator:
                    return CreateBoundUnconvertedAddressOfOperatorOperation((BoundUnconvertedAddressOfOperator)boundNode);
                case BoundKind.InterpolatedStringArgumentPlaceholder:
                    return CreateBoundInterpolatedStringArgumentPlaceholder((BoundInterpolatedStringArgumentPlaceholder)boundNode);
                case BoundKind.InterpolatedStringHandlerPlaceholder:
                    return CreateBoundInterpolatedStringHandlerPlaceholder((BoundInterpolatedStringHandlerPlaceholder)boundNode);
                case BoundKind.Attribute:
                    return CreateBoundAttributeOperation((BoundAttribute)boundNode);
 
                case BoundKind.ArgList:
                case BoundKind.ArgListOperator:
                case BoundKind.ConvertedStackAllocExpression:
                case BoundKind.FixedLocalCollectionInitializer:
                case BoundKind.GlobalStatementInitializer:
                case BoundKind.HostObjectMemberReference:
                case BoundKind.MakeRefOperator:
                case BoundKind.MethodGroup:
                case BoundKind.NamespaceExpression:
                case BoundKind.PointerElementAccess:
                case BoundKind.PointerIndirectionOperator:
                case BoundKind.PreviousSubmissionReference:
                case BoundKind.RefTypeOperator:
                case BoundKind.RefValueOperator:
                case BoundKind.Sequence:
                case BoundKind.StackAllocArrayCreation:
                case BoundKind.TypeExpression:
                case BoundKind.TypeOrValueExpression:
                    ConstantValue? constantValue = (boundNode as BoundExpression)?.ConstantValueOpt;
                    bool isImplicit = boundNode.WasCompilerGenerated;
 
                    if (!isImplicit)
                    {
                        switch (boundNode.Kind)
                        {
                            case BoundKind.FixedLocalCollectionInitializer:
                                isImplicit = true;
                                break;
                        }
                    }
                    ImmutableArray<IOperation> children = GetIOperationChildren(boundNode);
                    ITypeSymbol? type = boundNode switch
                    {
                        BoundExpression boundExpr => boundExpr.GetPublicTypeSymbol(),
                        _ => null
                    };
                    return new NoneOperation(children, _semanticModel, boundNode.Syntax, type: type, constantValue, isImplicit: isImplicit);
                case BoundKind.UnconvertedInterpolatedString:
                case BoundKind.UnconvertedConditionalOperator:
                case BoundKind.UnconvertedSwitchExpression:
                case BoundKind.AnonymousPropertyDeclaration:
                default:
                    // If you're hitting this because the IOperation test hook has failed, see
                    // <roslyn-root>/docs/Compilers/IOperation Test Hook.md for instructions on how to fix.
                    throw ExceptionUtilities.UnexpectedValue(boundNode.Kind);
            }
        }
 
        public ImmutableArray<TOperation> CreateFromArray<TBoundNode, TOperation>(ImmutableArray<TBoundNode> boundNodes) where TBoundNode : BoundNode where TOperation : class, IOperation
        {
            if (boundNodes.IsDefault)
            {
                return ImmutableArray<TOperation>.Empty;
            }
            var builder = ArrayBuilder<TOperation>.GetInstance(boundNodes.Length);
            foreach (var node in boundNodes)
            {
                builder.AddIfNotNull((TOperation)Create(node));
            }
            return builder.ToImmutableAndFree();
        }
 
        private IMethodBodyOperation CreateMethodBodyOperation(BoundNonConstructorMethodBody boundNode)
        {
            return new MethodBodyOperation(
                (IBlockOperation?)Create(boundNode.BlockBody),
                (IBlockOperation?)Create(boundNode.ExpressionBody),
                 _semanticModel,
                 boundNode.Syntax,
                 isImplicit: boundNode.WasCompilerGenerated);
        }
 
        private IConstructorBodyOperation CreateConstructorBodyOperation(BoundConstructorMethodBody boundNode)
        {
            return new ConstructorBodyOperation(
                boundNode.Locals.GetPublicSymbols(),
                Create(boundNode.Initializer),
                (IBlockOperation?)Create(boundNode.BlockBody),
                (IBlockOperation?)Create(boundNode.ExpressionBody),
                _semanticModel,
                boundNode.Syntax,
                isImplicit: boundNode.WasCompilerGenerated);
        }
 
        internal ImmutableArray<IOperation> GetIOperationChildren(IBoundNodeWithIOperationChildren boundNodeWithChildren)
        {
            var children = boundNodeWithChildren.Children;
            if (children.IsDefaultOrEmpty)
            {
                return ImmutableArray<IOperation>.Empty;
            }
 
            var builder = ArrayBuilder<IOperation>.GetInstance(children.Length);
            foreach (BoundNode? childNode in children)
            {
                if (childNode == null)
                {
                    continue;
                }
 
                IOperation operation = Create(childNode);
                builder.Add(operation);
            }
 
            return builder.ToImmutableAndFree();
        }
 
        internal ImmutableArray<IVariableDeclaratorOperation> CreateVariableDeclarator(BoundNode declaration, SyntaxNode declarationSyntax)
        {
            switch (declaration.Kind)
            {
                case BoundKind.LocalDeclaration:
                    {
                        return ImmutableArray.Create(CreateVariableDeclaratorInternal((BoundLocalDeclaration)declaration, (declarationSyntax as VariableDeclarationSyntax)?.Variables[0] ?? declarationSyntax));
                    }
                case BoundKind.MultipleLocalDeclarations:
                case BoundKind.UsingLocalDeclarations:
                    {
                        var multipleDeclaration = (BoundMultipleLocalDeclarationsBase)declaration;
                        var builder = ArrayBuilder<IVariableDeclaratorOperation>.GetInstance(multipleDeclaration.LocalDeclarations.Length);
                        foreach (var decl in multipleDeclaration.LocalDeclarations)
                        {
                            builder.Add((IVariableDeclaratorOperation)CreateVariableDeclaratorInternal(decl, decl.Syntax));
                        }
                        return builder.ToImmutableAndFree();
                    }
                default:
                    throw ExceptionUtilities.UnexpectedValue(declaration.Kind);
            }
        }
 
        private IPlaceholderOperation CreateBoundDeconstructValuePlaceholderOperation(BoundDeconstructValuePlaceholder boundDeconstructValuePlaceholder)
        {
            SyntaxNode syntax = boundDeconstructValuePlaceholder.Syntax;
            ITypeSymbol? type = boundDeconstructValuePlaceholder.GetPublicTypeSymbol();
            bool isImplicit = boundDeconstructValuePlaceholder.WasCompilerGenerated;
            return new PlaceholderOperation(PlaceholderKind.Unspecified, _semanticModel, syntax, type, isImplicit);
        }
 
        private IDeconstructionAssignmentOperation CreateBoundDeconstructionAssignmentOperator(BoundDeconstructionAssignmentOperator boundDeconstructionAssignmentOperator)
        {
            IOperation target = Create(boundDeconstructionAssignmentOperator.Left);
            // Skip the synthetic deconstruction conversion wrapping the right operand. This is a compiler-generated conversion that we don't want to reflect
            // in the public API because it's an implementation detail.
            IOperation value = Create(boundDeconstructionAssignmentOperator.Right.Operand);
            SyntaxNode syntax = boundDeconstructionAssignmentOperator.Syntax;
            ITypeSymbol? type = boundDeconstructionAssignmentOperator.GetPublicTypeSymbol();
            bool isImplicit = boundDeconstructionAssignmentOperator.WasCompilerGenerated;
            return new DeconstructionAssignmentOperation(target, value, _semanticModel, syntax, type, isImplicit);
        }
 
        private IOperation CreateBoundCallOperation(BoundCall boundCall)
        {
            MethodSymbol targetMethod = boundCall.Method;
            SyntaxNode syntax = boundCall.Syntax;
            ITypeSymbol? type = boundCall.GetPublicTypeSymbol();
            ConstantValue? constantValue = boundCall.ConstantValueOpt;
            bool isImplicit = boundCall.WasCompilerGenerated;
 
            if (!boundCall.OriginalMethodsOpt.IsDefault || IsMethodInvalid(boundCall.ResultKind, targetMethod))
            {
                ImmutableArray<IOperation> children = CreateFromArray<BoundNode, IOperation>(((IBoundInvalidNode)boundCall).InvalidNodeChildren);
                return new InvalidOperation(children, _semanticModel, syntax, type, constantValue, isImplicit);
            }
 
            TypeParameterSymbol? constrainedToType = GetConstrainedToType(targetMethod, boundCall.ReceiverOpt);
            bool isVirtual = constrainedToType is not null || IsCallVirtual(targetMethod, boundCall.ReceiverOpt);
            IOperation? receiver = CreateReceiverOperation(boundCall.ReceiverOpt, targetMethod);
            ImmutableArray<IArgumentOperation> arguments = DeriveArguments(boundCall);
            return new InvocationOperation(targetMethod.GetPublicSymbol(), constrainedToType.GetPublicSymbol(), receiver, isVirtual, arguments, _semanticModel, syntax, type, isImplicit);
        }
 
        private static TypeParameterSymbol? GetConstrainedToType(Symbol targetMember, BoundExpression? receiverOpt)
        {
            if (targetMember.IsStatic && (targetMember.IsAbstract || targetMember.IsVirtual) &&
                receiverOpt is BoundTypeExpression { Type: TypeParameterSymbol typeParameter })
            {
                return typeParameter;
            }
 
            return null;
        }
 
        private IOperation CreateBoundFunctionPointerInvocationOperation(BoundFunctionPointerInvocation boundFunctionPointerInvocation)
        {
            ITypeSymbol? type = boundFunctionPointerInvocation.GetPublicTypeSymbol();
            SyntaxNode syntax = boundFunctionPointerInvocation.Syntax;
            bool isImplicit = boundFunctionPointerInvocation.WasCompilerGenerated;
 
            if (boundFunctionPointerInvocation.ResultKind != LookupResultKind.Viable)
            {
                ImmutableArray<IOperation> children = CreateFromArray<BoundNode, IOperation>(((IBoundInvalidNode)boundFunctionPointerInvocation).InvalidNodeChildren);
                return new InvalidOperation(children, _semanticModel, syntax, type, constantValue: null, isImplicit);
            }
 
            var pointer = Create(boundFunctionPointerInvocation.InvokedExpression);
            var arguments = DeriveArguments(boundFunctionPointerInvocation);
            return new FunctionPointerInvocationOperation(pointer, arguments, _semanticModel, syntax, type, isImplicit);
        }
 
        private IOperation CreateBoundUnconvertedAddressOfOperatorOperation(BoundUnconvertedAddressOfOperator boundUnconvertedAddressOf)
        {
            return new AddressOfOperation(
                Create(boundUnconvertedAddressOf.Operand),
                _semanticModel,
                boundUnconvertedAddressOf.Syntax,
                boundUnconvertedAddressOf.GetPublicTypeSymbol(),
                boundUnconvertedAddressOf.WasCompilerGenerated);
        }
 
        private IOperation CreateBoundAttributeOperation(BoundAttribute boundAttribute)
        {
            var isAttributeImplicit = boundAttribute.WasCompilerGenerated;
            if (boundAttribute.Constructor is null)
            {
                var invalidOperation = OperationFactory.CreateInvalidOperation(_semanticModel, boundAttribute.Syntax, GetIOperationChildren(boundAttribute), isImplicit: true);
                return new AttributeOperation(invalidOperation, _semanticModel, boundAttribute.Syntax, isAttributeImplicit);
            }
 
            ObjectOrCollectionInitializerOperation? initializer = null;
            if (!boundAttribute.NamedArguments.IsEmpty)
            {
                var namedArguments = CreateFromArray<BoundAssignmentOperator, IOperation>(boundAttribute.NamedArguments);
                initializer = new ObjectOrCollectionInitializerOperation(namedArguments, _semanticModel, boundAttribute.Syntax, boundAttribute.GetPublicTypeSymbol(), isImplicit: true);
                Debug.Assert(initializer.Initializers.All(i => i is ISimpleAssignmentOperation));
            }
 
            var objectCreationOperation = new ObjectCreationOperation(boundAttribute.Constructor.GetPublicSymbol(), initializer, DeriveArguments(boundAttribute), _semanticModel, boundAttribute.Syntax, boundAttribute.GetPublicTypeSymbol(), boundAttribute.ConstantValueOpt, isImplicit: true);
            return new AttributeOperation(objectCreationOperation, _semanticModel, boundAttribute.Syntax, isAttributeImplicit);
        }
 
        internal ImmutableArray<IOperation> CreateIgnoredDimensions(BoundNode declaration)
        {
            switch (declaration.Kind)
            {
                case BoundKind.LocalDeclaration:
                    {
                        BoundTypeExpression? declaredTypeOpt = ((BoundLocalDeclaration)declaration).DeclaredTypeOpt;
                        Debug.Assert(declaredTypeOpt != null);
                        return CreateFromArray<BoundExpression, IOperation>(declaredTypeOpt.BoundDimensionsOpt);
                    }
                case BoundKind.MultipleLocalDeclarations:
                case BoundKind.UsingLocalDeclarations:
                    {
                        var declarations = ((BoundMultipleLocalDeclarationsBase)declaration).LocalDeclarations;
                        ImmutableArray<BoundExpression> dimensions;
                        if (declarations.Length > 0)
                        {
                            BoundTypeExpression? declaredTypeOpt = declarations[0].DeclaredTypeOpt;
                            Debug.Assert(declaredTypeOpt != null);
                            dimensions = declaredTypeOpt.BoundDimensionsOpt;
                        }
                        else
                        {
                            dimensions = ImmutableArray<BoundExpression>.Empty;
                        }
                        return CreateFromArray<BoundExpression, IOperation>(dimensions);
                    }
                default:
                    throw ExceptionUtilities.UnexpectedValue(declaration.Kind);
            }
        }
 
        internal IOperation CreateBoundLocalOperation(BoundLocal boundLocal, bool createDeclaration = true)
        {
            ILocalSymbol local = boundLocal.LocalSymbol.GetPublicSymbol();
            bool isDeclaration = boundLocal.DeclarationKind != BoundLocalDeclarationKind.None;
            SyntaxNode syntax = boundLocal.Syntax;
            ITypeSymbol? type = boundLocal.GetPublicTypeSymbol();
            ConstantValue? constantValue = boundLocal.ConstantValueOpt;
            bool isImplicit = boundLocal.WasCompilerGenerated;
            if (isDeclaration && syntax is DeclarationExpressionSyntax declarationExpressionSyntax)
            {
                syntax = declarationExpressionSyntax.Designation;
                if (createDeclaration)
                {
                    IOperation localReference = CreateBoundLocalOperation(boundLocal, createDeclaration: false);
                    return new DeclarationExpressionOperation(localReference, _semanticModel, declarationExpressionSyntax, type, isImplicit: false);
                }
            }
            return new LocalReferenceOperation(local, isDeclaration, _semanticModel, syntax, type, constantValue, isImplicit);
        }
 
        internal IOperation CreateBoundFieldAccessOperation(BoundFieldAccess boundFieldAccess, bool createDeclaration = true)
        {
            IFieldSymbol field = boundFieldAccess.FieldSymbol.GetPublicSymbol();
            bool isDeclaration = boundFieldAccess.IsDeclaration;
            SyntaxNode syntax = boundFieldAccess.Syntax;
            ITypeSymbol? type = boundFieldAccess.GetPublicTypeSymbol();
            ConstantValue? constantValue = boundFieldAccess.ConstantValueOpt;
            bool isImplicit = boundFieldAccess.WasCompilerGenerated;
            if (isDeclaration && syntax is DeclarationExpressionSyntax declarationExpressionSyntax)
            {
                syntax = declarationExpressionSyntax.Designation;
 
                if (createDeclaration)
                {
                    IOperation fieldAccess = CreateBoundFieldAccessOperation(boundFieldAccess, createDeclaration: false);
                    return new DeclarationExpressionOperation(fieldAccess, _semanticModel, declarationExpressionSyntax, type, isImplicit: false);
                }
            }
 
            IOperation? instance = CreateReceiverOperation(boundFieldAccess.ReceiverOpt, boundFieldAccess.FieldSymbol);
            return new FieldReferenceOperation(field, isDeclaration, instance, _semanticModel, syntax, type, constantValue, isImplicit);
        }
 
        internal IOperation? CreateBoundPropertyReferenceInstance(BoundNode boundNode)
        {
            switch (boundNode)
            {
                case BoundPropertyAccess boundPropertyAccess:
                    return CreateReceiverOperation(boundPropertyAccess.ReceiverOpt, boundPropertyAccess.PropertySymbol);
                case BoundObjectInitializerMember boundObjectInitializerMember:
                    return boundObjectInitializerMember.MemberSymbol?.IsStatic == true ?
                        null :
                        CreateImplicitReceiver(boundObjectInitializerMember.Syntax, boundObjectInitializerMember.ReceiverType);
                case BoundIndexerAccess boundIndexerAccess:
                    return CreateReceiverOperation(boundIndexerAccess.ReceiverOpt, boundIndexerAccess.ExpressionSymbol);
                default:
                    throw ExceptionUtilities.UnexpectedValue(boundNode.Kind);
            }
        }
 
        private IPropertyReferenceOperation CreateBoundPropertyAccessOperation(BoundPropertyAccess boundPropertyAccess)
        {
            IOperation? instance = CreateReceiverOperation(boundPropertyAccess.ReceiverOpt, boundPropertyAccess.PropertySymbol);
            var arguments = ImmutableArray<IArgumentOperation>.Empty;
            IPropertySymbol property = boundPropertyAccess.PropertySymbol.GetPublicSymbol();
            SyntaxNode syntax = boundPropertyAccess.Syntax;
            ITypeSymbol? type = boundPropertyAccess.GetPublicTypeSymbol();
            bool isImplicit = boundPropertyAccess.WasCompilerGenerated;
            TypeParameterSymbol? constrainedToType = GetConstrainedToType(boundPropertyAccess.PropertySymbol, boundPropertyAccess.ReceiverOpt);
            return new PropertyReferenceOperation(property, constrainedToType.GetPublicSymbol(), arguments, instance, _semanticModel, syntax, type, isImplicit);
        }
 
        private IOperation CreateBoundIndexerAccessOperation(BoundIndexerAccess boundIndexerAccess)
        {
            PropertySymbol property = boundIndexerAccess.Indexer;
            SyntaxNode syntax = boundIndexerAccess.Syntax;
            ITypeSymbol? type = boundIndexerAccess.GetPublicTypeSymbol();
            bool isImplicit = boundIndexerAccess.WasCompilerGenerated;
 
            if (!boundIndexerAccess.OriginalIndexersOpt.IsDefault || boundIndexerAccess.ResultKind == LookupResultKind.OverloadResolutionFailure)
            {
                var children = CreateFromArray<BoundNode, IOperation>(((IBoundInvalidNode)boundIndexerAccess).InvalidNodeChildren);
                return new InvalidOperation(children, _semanticModel, syntax, type, constantValue: null, isImplicit);
            }
 
            ImmutableArray<IArgumentOperation> arguments = DeriveArguments(boundIndexerAccess);
            IOperation? instance = CreateReceiverOperation(boundIndexerAccess.ReceiverOpt, boundIndexerAccess.ExpressionSymbol);
            TypeParameterSymbol? constrainedToType = GetConstrainedToType(property, boundIndexerAccess.ReceiverOpt);
            return new PropertyReferenceOperation(property.GetPublicSymbol(), constrainedToType.GetPublicSymbol(), arguments, instance, _semanticModel, syntax, type, isImplicit);
        }
 
        private IEventReferenceOperation CreateBoundEventAccessOperation(BoundEventAccess boundEventAccess)
        {
            IEventSymbol @event = boundEventAccess.EventSymbol.GetPublicSymbol();
            IOperation? instance = CreateReceiverOperation(boundEventAccess.ReceiverOpt, boundEventAccess.EventSymbol);
            SyntaxNode syntax = boundEventAccess.Syntax;
            ITypeSymbol? type = boundEventAccess.GetPublicTypeSymbol();
            bool isImplicit = boundEventAccess.WasCompilerGenerated;
            TypeParameterSymbol? constrainedToType = GetConstrainedToType(boundEventAccess.EventSymbol, boundEventAccess.ReceiverOpt);
            return new EventReferenceOperation(@event, constrainedToType.GetPublicSymbol(), instance, _semanticModel, syntax, type, isImplicit);
        }
 
        private IEventAssignmentOperation CreateBoundEventAssignmentOperatorOperation(BoundEventAssignmentOperator boundEventAssignmentOperator)
        {
            IOperation eventReference = CreateBoundEventAccessOperation(boundEventAssignmentOperator);
            IOperation handlerValue = Create(boundEventAssignmentOperator.Argument);
            SyntaxNode syntax = boundEventAssignmentOperator.Syntax;
            bool adds = boundEventAssignmentOperator.IsAddition;
            ITypeSymbol? type = boundEventAssignmentOperator.GetPublicTypeSymbol();
            bool isImplicit = boundEventAssignmentOperator.WasCompilerGenerated;
            return new EventAssignmentOperation(eventReference, handlerValue, adds, _semanticModel, syntax, type, isImplicit);
        }
 
        private IParameterReferenceOperation CreateBoundParameterOperation(BoundParameter boundParameter)
        {
            IParameterSymbol parameter = boundParameter.ParameterSymbol.GetPublicSymbol();
            SyntaxNode syntax = boundParameter.Syntax;
            ITypeSymbol? type = boundParameter.GetPublicTypeSymbol();
            bool isImplicit = boundParameter.WasCompilerGenerated;
            return new ParameterReferenceOperation(parameter, _semanticModel, syntax, type, isImplicit);
        }
 
        internal ILiteralOperation CreateBoundLiteralOperation(BoundLiteral boundLiteral, bool @implicit = false)
        {
            SyntaxNode syntax = boundLiteral.Syntax;
            ITypeSymbol? type = boundLiteral.GetPublicTypeSymbol();
            ConstantValue? constantValue = boundLiteral.ConstantValueOpt;
            bool isImplicit = boundLiteral.WasCompilerGenerated || @implicit;
            return new LiteralOperation(_semanticModel, syntax, type, constantValue, isImplicit);
        }
 
        private IUtf8StringOperation CreateBoundUtf8StringOperation(BoundUtf8String boundNode)
        {
            SyntaxNode syntax = boundNode.Syntax;
            ITypeSymbol? type = boundNode.GetPublicTypeSymbol();
            bool isImplicit = boundNode.WasCompilerGenerated;
            return new Utf8StringOperation(boundNode.Value, _semanticModel, syntax, type, isImplicit);
        }
 
        private IAnonymousObjectCreationOperation CreateBoundAnonymousObjectCreationExpressionOperation(BoundAnonymousObjectCreationExpression boundAnonymousObjectCreationExpression)
        {
            SyntaxNode syntax = boundAnonymousObjectCreationExpression.Syntax;
            ITypeSymbol? type = boundAnonymousObjectCreationExpression.GetPublicTypeSymbol();
            Debug.Assert(type is not null);
            bool isImplicit = boundAnonymousObjectCreationExpression.WasCompilerGenerated;
            ImmutableArray<IOperation> initializers = GetAnonymousObjectCreationInitializers(boundAnonymousObjectCreationExpression.Arguments, boundAnonymousObjectCreationExpression.Declarations, syntax, type, isImplicit);
            return new AnonymousObjectCreationOperation(initializers, _semanticModel, syntax, type, isImplicit);
        }
 
        private IOperation CreateBoundObjectCreationExpressionOperation(BoundObjectCreationExpression boundObjectCreationExpression)
        {
            MethodSymbol constructor = boundObjectCreationExpression.Constructor;
            SyntaxNode syntax = boundObjectCreationExpression.Syntax;
            ITypeSymbol? type = boundObjectCreationExpression.GetPublicTypeSymbol();
            ConstantValue? constantValue = boundObjectCreationExpression.ConstantValueOpt;
            bool isImplicit = boundObjectCreationExpression.WasCompilerGenerated;
 
            Debug.Assert(constructor is not null);
 
            if (boundObjectCreationExpression.ResultKind == LookupResultKind.OverloadResolutionFailure || constructor.OriginalDefinition is ErrorMethodSymbol)
            {
                var children = CreateFromArray<BoundNode, IOperation>(((IBoundInvalidNode)boundObjectCreationExpression).InvalidNodeChildren);
                return new InvalidOperation(children, _semanticModel, syntax, type, constantValue, isImplicit);
            }
            else if (boundObjectCreationExpression.Type.IsAnonymousType)
            {
                // Workaround for https://github.com/dotnet/roslyn/issues/28157
                Debug.Assert(isImplicit);
                Debug.Assert(type is not null);
                ImmutableArray<IOperation> initializers = GetAnonymousObjectCreationInitializers(
                    boundObjectCreationExpression.Arguments,
                    declarations: ImmutableArray<BoundAnonymousPropertyDeclaration>.Empty,
                    syntax,
                    type,
                    isImplicit);
                return new AnonymousObjectCreationOperation(initializers, _semanticModel, syntax, type, isImplicit);
            }
 
            ImmutableArray<IArgumentOperation> arguments = DeriveArguments(boundObjectCreationExpression);
            IObjectOrCollectionInitializerOperation? initializer = (IObjectOrCollectionInitializerOperation?)Create(boundObjectCreationExpression.InitializerExpressionOpt);
 
            return new ObjectCreationOperation(constructor.GetPublicSymbol(), initializer, arguments, _semanticModel, syntax, type, constantValue, isImplicit);
        }
 
        private IOperation CreateBoundWithExpressionOperation(BoundWithExpression boundWithExpression)
        {
            IOperation operand = Create(boundWithExpression.Receiver);
            IObjectOrCollectionInitializerOperation initializer = (IObjectOrCollectionInitializerOperation)Create(boundWithExpression.InitializerExpression);
            MethodSymbol? constructor = boundWithExpression.CloneMethod;
            SyntaxNode syntax = boundWithExpression.Syntax;
            ITypeSymbol? type = boundWithExpression.GetPublicTypeSymbol();
            bool isImplicit = boundWithExpression.WasCompilerGenerated;
            return new WithOperation(operand, constructor.GetPublicSymbol(), initializer, _semanticModel, syntax, type, isImplicit);
        }
 
        private IDynamicObjectCreationOperation CreateBoundDynamicObjectCreationExpressionOperation(BoundDynamicObjectCreationExpression boundDynamicObjectCreationExpression)
        {
            IObjectOrCollectionInitializerOperation? initializer = (IObjectOrCollectionInitializerOperation?)Create(boundDynamicObjectCreationExpression.InitializerExpressionOpt);
            ImmutableArray<IOperation> arguments = CreateFromArray<BoundExpression, IOperation>(boundDynamicObjectCreationExpression.Arguments);
            ImmutableArray<string?> argumentNames = boundDynamicObjectCreationExpression.ArgumentNamesOpt.NullToEmpty();
            ImmutableArray<RefKind> argumentRefKinds = boundDynamicObjectCreationExpression.ArgumentRefKindsOpt.NullToEmpty();
            SyntaxNode syntax = boundDynamicObjectCreationExpression.Syntax;
            ITypeSymbol? type = boundDynamicObjectCreationExpression.GetPublicTypeSymbol();
            bool isImplicit = boundDynamicObjectCreationExpression.WasCompilerGenerated;
            return new DynamicObjectCreationOperation(initializer, arguments, argumentNames, argumentRefKinds, _semanticModel, syntax, type, isImplicit);
        }
 
        internal IOperation CreateBoundDynamicInvocationExpressionReceiver(BoundNode receiver)
        {
            switch (receiver)
            {
                case BoundObjectOrCollectionValuePlaceholder implicitReceiver:
                    return CreateBoundDynamicMemberAccessOperation(implicitReceiver, typeArgumentsOpt: ImmutableArray<TypeSymbol>.Empty, memberName: "Add",
                                                                   implicitReceiver.Syntax, type: null, isImplicit: true);
 
                case BoundMethodGroup methodGroup:
                    return CreateBoundDynamicMemberAccessOperation(methodGroup.ReceiverOpt, TypeMap.AsTypeSymbols(methodGroup.TypeArgumentsOpt), methodGroup.Name,
                                                                   methodGroup.Syntax, methodGroup.GetPublicTypeSymbol(), methodGroup.WasCompilerGenerated);
 
                default:
                    return Create(receiver);
            }
        }
 
        private IDynamicInvocationOperation CreateBoundDynamicInvocationExpressionOperation(BoundDynamicInvocation boundDynamicInvocation)
        {
            IOperation operation = CreateBoundDynamicInvocationExpressionReceiver(boundDynamicInvocation.Expression);
            ImmutableArray<IOperation> arguments = CreateFromArray<BoundExpression, IOperation>(boundDynamicInvocation.Arguments);
            ImmutableArray<string?> argumentNames = boundDynamicInvocation.ArgumentNamesOpt.NullToEmpty();
            ImmutableArray<RefKind> argumentRefKinds = boundDynamicInvocation.ArgumentRefKindsOpt.NullToEmpty();
            SyntaxNode syntax = boundDynamicInvocation.Syntax;
            ITypeSymbol? type = boundDynamicInvocation.GetPublicTypeSymbol();
            bool isImplicit = boundDynamicInvocation.WasCompilerGenerated;
            return new DynamicInvocationOperation(operation, arguments, argumentNames, argumentRefKinds, _semanticModel, syntax, type, isImplicit);
        }
 
        internal IOperation CreateBoundDynamicIndexerAccessExpressionReceiver(BoundExpression indexer)
        {
            switch (indexer)
            {
                case BoundDynamicIndexerAccess boundDynamicIndexerAccess:
                    return Create(boundDynamicIndexerAccess.Receiver);
 
                case BoundObjectInitializerMember boundObjectInitializerMember:
                    return CreateImplicitReceiver(boundObjectInitializerMember.Syntax, boundObjectInitializerMember.ReceiverType);
 
                default:
                    throw ExceptionUtilities.UnexpectedValue(indexer.Kind);
            }
        }
 
        internal ImmutableArray<IOperation> CreateBoundDynamicIndexerAccessArguments(BoundExpression indexer)
        {
            switch (indexer)
            {
                case BoundDynamicIndexerAccess boundDynamicAccess:
                    return CreateFromArray<BoundExpression, IOperation>(boundDynamicAccess.Arguments);
 
                case BoundObjectInitializerMember boundObjectInitializerMember:
                    Debug.Assert(!boundObjectInitializerMember.Expanded);
                    return CreateFromArray<BoundExpression, IOperation>(boundObjectInitializerMember.Arguments);
 
                default:
                    throw ExceptionUtilities.UnexpectedValue(indexer.Kind);
            }
        }
 
        private IDynamicIndexerAccessOperation CreateBoundDynamicIndexerAccessExpressionOperation(BoundDynamicIndexerAccess boundDynamicIndexerAccess)
        {
            IOperation operation = CreateBoundDynamicIndexerAccessExpressionReceiver(boundDynamicIndexerAccess);
            ImmutableArray<IOperation> arguments = CreateBoundDynamicIndexerAccessArguments(boundDynamicIndexerAccess);
            ImmutableArray<string?> argumentNames = boundDynamicIndexerAccess.ArgumentNamesOpt.NullToEmpty();
            ImmutableArray<RefKind> argumentRefKinds = boundDynamicIndexerAccess.ArgumentRefKindsOpt.NullToEmpty();
            SyntaxNode syntax = boundDynamicIndexerAccess.Syntax;
            ITypeSymbol? type = boundDynamicIndexerAccess.GetPublicTypeSymbol();
            bool isImplicit = boundDynamicIndexerAccess.WasCompilerGenerated;
            return new DynamicIndexerAccessOperation(operation, arguments, argumentNames, argumentRefKinds, _semanticModel, syntax, type, isImplicit);
        }
 
        private IObjectOrCollectionInitializerOperation CreateBoundObjectInitializerExpressionOperation(BoundObjectInitializerExpression boundObjectInitializerExpression)
        {
            ImmutableArray<IOperation> initializers = CreateFromArray<BoundExpression, IOperation>(BoundObjectCreationExpression.GetChildInitializers(boundObjectInitializerExpression));
            SyntaxNode syntax = boundObjectInitializerExpression.Syntax;
            ITypeSymbol? type = boundObjectInitializerExpression.GetPublicTypeSymbol();
            bool isImplicit = boundObjectInitializerExpression.WasCompilerGenerated;
            return new ObjectOrCollectionInitializerOperation(initializers, _semanticModel, syntax, type, isImplicit);
        }
 
        private IObjectOrCollectionInitializerOperation CreateBoundCollectionInitializerExpressionOperation(BoundCollectionInitializerExpression boundCollectionInitializerExpression)
        {
            ImmutableArray<IOperation> initializers = CreateFromArray<BoundExpression, IOperation>(BoundObjectCreationExpression.GetChildInitializers(boundCollectionInitializerExpression));
            SyntaxNode syntax = boundCollectionInitializerExpression.Syntax;
            ITypeSymbol? type = boundCollectionInitializerExpression.GetPublicTypeSymbol();
            bool isImplicit = boundCollectionInitializerExpression.WasCompilerGenerated;
            return new ObjectOrCollectionInitializerOperation(initializers, _semanticModel, syntax, type, isImplicit);
        }
 
        private IOperation CreateBoundObjectInitializerMemberOperation(BoundObjectInitializerMember boundObjectInitializerMember, bool isObjectOrCollectionInitializer = false)
        {
            Symbol? memberSymbol = boundObjectInitializerMember.MemberSymbol;
            SyntaxNode syntax = boundObjectInitializerMember.Syntax;
            ITypeSymbol? type = boundObjectInitializerMember.GetPublicTypeSymbol();
            bool isImplicit = boundObjectInitializerMember.WasCompilerGenerated;
 
            if ((object?)memberSymbol == null)
            {
                Debug.Assert(boundObjectInitializerMember.Type.IsDynamic());
 
                IOperation operation = CreateBoundDynamicIndexerAccessExpressionReceiver(boundObjectInitializerMember);
                ImmutableArray<IOperation> arguments = CreateBoundDynamicIndexerAccessArguments(boundObjectInitializerMember);
                ImmutableArray<string?> argumentNames = boundObjectInitializerMember.ArgumentNamesOpt.NullToEmpty();
                ImmutableArray<RefKind> argumentRefKinds = boundObjectInitializerMember.ArgumentRefKindsOpt.NullToEmpty();
                return new DynamicIndexerAccessOperation(operation, arguments, argumentNames, argumentRefKinds, _semanticModel, syntax, type, isImplicit);
            }
 
            switch (memberSymbol.Kind)
            {
                case SymbolKind.Field:
                    var field = (FieldSymbol)memberSymbol;
                    bool isDeclaration = false;
                    return new FieldReferenceOperation(field.GetPublicSymbol(), isDeclaration, createReceiver(), _semanticModel, syntax, type, constantValue: null, isImplicit);
                case SymbolKind.Event:
                    var eventSymbol = (EventSymbol)memberSymbol;
                    return new EventReferenceOperation(eventSymbol.GetPublicSymbol(), constrainedToType: null, createReceiver(), _semanticModel, syntax, type, isImplicit);
                case SymbolKind.Property:
                    var property = (PropertySymbol)memberSymbol;
 
                    ImmutableArray<IArgumentOperation> arguments;
                    if (!boundObjectInitializerMember.Arguments.IsEmpty)
                    {
                        // In nested member initializers, the property is not actually set. Instead, it is retrieved for a series of Add method calls or nested property setter calls,
                        // so we need to use the getter for this property
                        MethodSymbol? accessor = isObjectOrCollectionInitializer || property.RefKind != RefKind.None
                            ? property.GetOwnOrInheritedGetMethod()
                            : property.GetOwnOrInheritedSetMethod();
                        if (accessor == null || boundObjectInitializerMember.ResultKind == LookupResultKind.OverloadResolutionFailure || accessor.OriginalDefinition is ErrorMethodSymbol)
                        {
                            var children = CreateFromArray<BoundNode, IOperation>(((IBoundInvalidNode)boundObjectInitializerMember).InvalidNodeChildren);
                            return new InvalidOperation(children, _semanticModel, syntax, type, constantValue: null, isImplicit);
                        }
 
                        arguments = DeriveArguments(boundObjectInitializerMember);
                    }
                    else
                    {
                        arguments = ImmutableArray<IArgumentOperation>.Empty;
                    }
 
                    return new PropertyReferenceOperation(property.GetPublicSymbol(), constrainedToType: null, arguments, createReceiver(), _semanticModel, syntax, type, isImplicit);
                default:
                    throw ExceptionUtilities.UnexpectedValue(memberSymbol.Kind);
            }
 
            IOperation? createReceiver() => memberSymbol?.IsStatic == true ?
                    null :
                    CreateImplicitReceiver(boundObjectInitializerMember.Syntax, boundObjectInitializerMember.ReceiverType);
        }
 
        private IOperation CreateBoundDynamicObjectInitializerMemberOperation(BoundDynamicObjectInitializerMember boundDynamicObjectInitializerMember)
        {
            IOperation instanceReceiver = CreateImplicitReceiver(boundDynamicObjectInitializerMember.Syntax, boundDynamicObjectInitializerMember.ReceiverType);
            string memberName = boundDynamicObjectInitializerMember.MemberName;
            ImmutableArray<ITypeSymbol> typeArguments = ImmutableArray<ITypeSymbol>.Empty;
            ITypeSymbol containingType = boundDynamicObjectInitializerMember.ReceiverType.GetPublicSymbol();
            SyntaxNode syntax = boundDynamicObjectInitializerMember.Syntax;
            ITypeSymbol? type = boundDynamicObjectInitializerMember.GetPublicTypeSymbol();
            bool isImplicit = boundDynamicObjectInitializerMember.WasCompilerGenerated;
 
            return new DynamicMemberReferenceOperation(instanceReceiver, memberName, typeArguments, containingType, _semanticModel, syntax, type, isImplicit);
        }
 
        private IOperation CreateBoundCollectionElementInitializerOperation(BoundCollectionElementInitializer boundCollectionElementInitializer)
        {
            MethodSymbol addMethod = boundCollectionElementInitializer.AddMethod;
            IOperation? receiver = CreateReceiverOperation(boundCollectionElementInitializer.ImplicitReceiverOpt, addMethod);
            ImmutableArray<IArgumentOperation> arguments = DeriveArguments(boundCollectionElementInitializer);
            SyntaxNode syntax = boundCollectionElementInitializer.Syntax;
            ITypeSymbol? type = boundCollectionElementInitializer.GetPublicTypeSymbol();
            ConstantValue? constantValue = boundCollectionElementInitializer.ConstantValueOpt;
            bool isImplicit = boundCollectionElementInitializer.WasCompilerGenerated;
 
            if (IsMethodInvalid(boundCollectionElementInitializer.ResultKind, addMethod))
            {
                var children = CreateFromArray<BoundNode, IOperation>(((IBoundInvalidNode)boundCollectionElementInitializer).InvalidNodeChildren);
                return new InvalidOperation(children, _semanticModel, syntax, type, constantValue, isImplicit);
            }
 
            bool isVirtual = IsCallVirtual(addMethod, boundCollectionElementInitializer.ImplicitReceiverOpt);
            return new InvocationOperation(addMethod.GetPublicSymbol(), constrainedToType: null, receiver, isVirtual, arguments, _semanticModel, syntax, type, isImplicit);
        }
 
        private IDynamicMemberReferenceOperation CreateBoundDynamicMemberAccessOperation(BoundDynamicMemberAccess boundDynamicMemberAccess)
        {
            return CreateBoundDynamicMemberAccessOperation(boundDynamicMemberAccess.Receiver, TypeMap.AsTypeSymbols(boundDynamicMemberAccess.TypeArgumentsOpt), boundDynamicMemberAccess.Name,
                boundDynamicMemberAccess.Syntax, boundDynamicMemberAccess.GetPublicTypeSymbol(), boundDynamicMemberAccess.WasCompilerGenerated);
        }
 
        private IDynamicMemberReferenceOperation CreateBoundDynamicMemberAccessOperation(
            BoundExpression? receiver,
            ImmutableArray<TypeSymbol> typeArgumentsOpt,
            string memberName,
            SyntaxNode syntaxNode,
            ITypeSymbol? type,
            bool isImplicit)
        {
            ITypeSymbol? containingType = null;
            if (receiver?.Kind == BoundKind.TypeExpression)
            {
                containingType = receiver.GetPublicTypeSymbol();
                receiver = null;
            }
 
            ImmutableArray<ITypeSymbol> typeArguments = ImmutableArray<ITypeSymbol>.Empty;
            if (!typeArgumentsOpt.IsDefault)
            {
                typeArguments = typeArgumentsOpt.GetPublicSymbols();
            }
 
            IOperation? instance = Create(receiver);
            return new DynamicMemberReferenceOperation(instance, memberName, typeArguments, containingType, _semanticModel, syntaxNode, type, isImplicit);
        }
 
        private IDynamicInvocationOperation CreateBoundDynamicCollectionElementInitializerOperation(BoundDynamicCollectionElementInitializer boundCollectionElementInitializer)
        {
            IOperation operation = CreateBoundDynamicInvocationExpressionReceiver(boundCollectionElementInitializer.Expression);
            ImmutableArray<IOperation> arguments = CreateFromArray<BoundExpression, IOperation>(boundCollectionElementInitializer.Arguments);
            SyntaxNode syntax = boundCollectionElementInitializer.Syntax;
            ITypeSymbol? type = boundCollectionElementInitializer.GetPublicTypeSymbol();
            bool isImplicit = boundCollectionElementInitializer.WasCompilerGenerated;
            return new DynamicInvocationOperation(operation, arguments, argumentNames: ImmutableArray<string?>.Empty, argumentRefKinds: ImmutableArray<RefKind>.Empty, _semanticModel, syntax, type, isImplicit);
        }
 
        private IOperation CreateUnboundLambdaOperation(UnboundLambda unboundLambda)
        {
            // We want to ensure that we never see the UnboundLambda node, and that we don't end up having two different IOperation
            // nodes for the lambda expression. So, we ask the semantic model for the IOperation node for the unbound lambda syntax.
            // We are counting on the fact that will do the error recovery and actually create the BoundLambda node appropriate for
            // this syntax node.
            BoundLambda boundLambda = unboundLambda.BindForErrorRecovery();
            return Create(boundLambda);
        }
 
        private IAnonymousFunctionOperation CreateBoundLambdaOperation(BoundLambda boundLambda)
        {
            IMethodSymbol symbol = boundLambda.Symbol.GetPublicSymbol();
            IBlockOperation body = (IBlockOperation)Create(boundLambda.Body);
            SyntaxNode syntax = boundLambda.Syntax;
            bool isImplicit = boundLambda.WasCompilerGenerated;
            return new AnonymousFunctionOperation(symbol, body, _semanticModel, syntax, isImplicit);
        }
 
        private ILocalFunctionOperation CreateBoundLocalFunctionStatementOperation(BoundLocalFunctionStatement boundLocalFunctionStatement)
        {
            IBlockOperation? body = (IBlockOperation?)Create(boundLocalFunctionStatement.Body);
            IBlockOperation? ignoredBody = boundLocalFunctionStatement is { BlockBody: { }, ExpressionBody: { } exprBody }
                ? (IBlockOperation?)Create(exprBody)
                : null;
            IMethodSymbol symbol = boundLocalFunctionStatement.Symbol.GetPublicSymbol();
            SyntaxNode syntax = boundLocalFunctionStatement.Syntax;
            bool isImplicit = boundLocalFunctionStatement.WasCompilerGenerated;
            return new LocalFunctionOperation(symbol, body, ignoredBody, _semanticModel, syntax, isImplicit);
        }
 
        private IOperation CreateBoundConversionOperation(BoundConversion boundConversion, bool forceOperandImplicitLiteral = false)
        {
            Debug.Assert(!forceOperandImplicitLiteral || boundConversion.Operand is BoundLiteral);
            bool isImplicit = boundConversion.WasCompilerGenerated || !boundConversion.ExplicitCastInCode || forceOperandImplicitLiteral;
            BoundExpression boundOperand = boundConversion.Operand;
 
            if (boundConversion.ConversionKind == ConversionKind.InterpolatedStringHandler)
            {
                Debug.Assert(!forceOperandImplicitLiteral);
                Debug.Assert(!boundOperand.GetInterpolatedStringHandlerData().IsDefault);
                return CreateInterpolatedStringHandler(boundConversion);
            }
 
            if (boundConversion.ConversionKind == CSharp.ConversionKind.MethodGroup)
            {
                SyntaxNode syntax = boundConversion.Syntax;
                ITypeSymbol? type = boundConversion.GetPublicTypeSymbol();
                Debug.Assert(!forceOperandImplicitLiteral);
 
                if (boundConversion.Type is FunctionPointerTypeSymbol)
                {
                    Debug.Assert(boundConversion.SymbolOpt is object);
                    return new AddressOfOperation(
                        CreateBoundMethodGroupSingleMethodOperation((BoundMethodGroup)boundConversion.Operand, boundConversion.SymbolOpt, suppressVirtualCalls: false),
                        _semanticModel, syntax, type, boundConversion.WasCompilerGenerated);
                }
 
                // We don't check HasErrors on the conversion here because if we actually have a MethodGroup conversion,
                // overload resolution succeeded. The resulting method could be invalid for other reasons, but we don't
                // hide the resolved method.
                IOperation target = CreateDelegateTargetOperation(boundConversion);
 
                // If this was an explicit tuple expression conversion, such as ((Action, int))(M, 1), we will be "explicit", because the
                // original conversion was explicit in code, but the syntax node for this delegate creation and the nested method group will
                // be the same. We therefore need to mark this node as implicit to ensure we don't have two explicit nodes for the same syntax.
                Debug.Assert(isImplicit || target.Syntax != syntax || target.IsImplicit || boundConversion.ConversionGroupOpt != null);
 
                isImplicit = isImplicit || (target.Syntax == syntax && !target.IsImplicit);
 
                return new DelegateCreationOperation(target, _semanticModel, syntax, type, isImplicit);
            }
            else
            {
                SyntaxNode syntax = boundConversion.Syntax;
 
                if (syntax.IsMissing)
                {
                    // If the underlying syntax IsMissing, then that means we're in case where the compiler generated a piece of syntax to fill in for
                    // an error, such as this case:
                    //
                    //  int i = ;
                    //
                    // Semantic model has a special case here that we match: if the underlying syntax is missing, don't create a conversion expression,
                    // and instead directly return the operand, which will be a BoundBadExpression. When we generate a node for the BoundBadExpression,
                    // the resulting IOperation will also have a null Type.
                    Debug.Assert(boundOperand.Kind == BoundKind.BadExpression ||
                                 ((boundOperand as BoundLambda)?.Body.Statements.SingleOrDefault() as BoundReturnStatement)?.
                                     ExpressionOpt?.Kind == BoundKind.BadExpression);
                    Debug.Assert(!forceOperandImplicitLiteral);
                    return Create(boundOperand);
                }
 
                BoundConversion correctedConversionNode = boundConversion;
                Conversion conversion = boundConversion.Conversion;
 
                if (boundOperand.Syntax == boundConversion.Syntax)
                {
                    if (boundOperand.Kind == BoundKind.ConvertedTupleLiteral && TypeSymbol.Equals(boundOperand.Type, boundConversion.Type, TypeCompareKind.ConsiderEverything2))
                    {
                        // Erase this conversion, this is an artificial conversion added on top of BoundConvertedTupleLiteral
                        // in Binder.CreateTupleLiteralConversion
                        Debug.Assert(!forceOperandImplicitLiteral);
                        return Create(boundOperand);
                    }
                    else
                    {
                        // Make this conversion implicit
                        isImplicit = true;
                    }
                }
 
                if (boundConversion.ExplicitCastInCode && conversion.IsIdentity && boundOperand.Kind == BoundKind.Conversion)
                {
                    var nestedConversion = (BoundConversion)boundOperand;
                    BoundExpression nestedOperand = nestedConversion.Operand;
 
                    if (nestedConversion.Syntax == nestedOperand.Syntax && nestedConversion.ExplicitCastInCode &&
                        nestedOperand.Kind == BoundKind.ConvertedTupleLiteral &&
                        !TypeSymbol.Equals(nestedConversion.Type, nestedOperand.Type, TypeCompareKind.ConsiderEverything2))
                    {
                        // Let's erase the nested conversion, this is an artificial conversion added on top of BoundConvertedTupleLiteral
                        // in Binder.CreateTupleLiteralConversion.
                        // We need to use conversion information from the nested conversion because that is where the real conversion
                        // information is stored.
                        conversion = nestedConversion.Conversion;
                        correctedConversionNode = nestedConversion;
                    }
                }
 
                ITypeSymbol? type = boundConversion.GetPublicTypeSymbol();
                ConstantValue? constantValue = boundConversion.ConstantValueOpt;
 
                // If this is a lambda or method group conversion to a delegate type, we return a delegate creation instead of a conversion
                if ((boundOperand.Kind == BoundKind.Lambda ||
                     boundOperand.Kind == BoundKind.UnboundLambda ||
                     boundOperand.Kind == BoundKind.MethodGroup) &&
                    boundConversion.Type.IsDelegateType())
                {
                    IOperation target = CreateDelegateTargetOperation(correctedConversionNode);
                    return new DelegateCreationOperation(target, _semanticModel, syntax, type, isImplicit);
                }
                else
                {
                    bool isTryCast = false;
                    // Checked conversions only matter if the conversion is a Numeric conversion. Don't have true unless the conversion is actually numeric.
                    bool isChecked = boundConversion.Checked && (conversion.IsNumeric || (boundConversion.SymbolOpt is not null && SyntaxFacts.IsCheckedOperator(boundConversion.SymbolOpt.Name)));
                    IOperation operand = forceOperandImplicitLiteral
                        ? CreateBoundLiteralOperation((BoundLiteral)correctedConversionNode.Operand, @implicit: true)
                        : Create(correctedConversionNode.Operand);
                    return new ConversionOperation(operand, conversion, isTryCast, isChecked, _semanticModel, syntax, type, constantValue, isImplicit);
                }
            }
        }
 
        private IConversionOperation CreateBoundAsOperatorOperation(BoundAsOperator boundAsOperator)
        {
            IOperation operand = Create(boundAsOperator.Operand);
            SyntaxNode syntax = boundAsOperator.Syntax;
            Conversion conversion = BoundNode.GetConversion(boundAsOperator.OperandConversion, boundAsOperator.OperandPlaceholder);
            bool isTryCast = true;
            bool isChecked = false;
            ITypeSymbol? type = boundAsOperator.GetPublicTypeSymbol();
            bool isImplicit = boundAsOperator.WasCompilerGenerated;
            return new ConversionOperation(operand, conversion, isTryCast, isChecked, _semanticModel, syntax, type, constantValue: null, isImplicit);
        }
 
        private IDelegateCreationOperation CreateBoundDelegateCreationExpressionOperation(BoundDelegateCreationExpression boundDelegateCreationExpression)
        {
            IOperation target = CreateDelegateTargetOperation(boundDelegateCreationExpression);
            SyntaxNode syntax = boundDelegateCreationExpression.Syntax;
            ITypeSymbol? type = boundDelegateCreationExpression.GetPublicTypeSymbol();
            bool isImplicit = boundDelegateCreationExpression.WasCompilerGenerated;
            return new DelegateCreationOperation(target, _semanticModel, syntax, type, isImplicit);
        }
 
        private IMethodReferenceOperation CreateBoundMethodGroupSingleMethodOperation(BoundMethodGroup boundMethodGroup, MethodSymbol methodSymbol, bool suppressVirtualCalls)
        {
            TypeParameterSymbol? constrainedToType = GetConstrainedToType(methodSymbol, boundMethodGroup.ReceiverOpt);
            bool isVirtual = constrainedToType is not null || ((methodSymbol.IsAbstract || methodSymbol.IsOverride || methodSymbol.IsVirtual) && !suppressVirtualCalls);
            IOperation? instance = CreateReceiverOperation(boundMethodGroup.ReceiverOpt, methodSymbol);
            SyntaxNode bindingSyntax = boundMethodGroup.Syntax;
            ITypeSymbol? bindingType = null;
            bool isImplicit = boundMethodGroup.WasCompilerGenerated;
            return new MethodReferenceOperation(methodSymbol.GetPublicSymbol(), constrainedToType.GetPublicSymbol(), isVirtual, instance, _semanticModel, bindingSyntax, bindingType, isImplicit);
        }
 
        private IIsTypeOperation CreateBoundIsOperatorOperation(BoundIsOperator boundIsOperator)
        {
            IOperation value = Create(boundIsOperator.Operand);
            ITypeSymbol? typeOperand = boundIsOperator.TargetType.GetPublicTypeSymbol();
            Debug.Assert(typeOperand is not null);
            SyntaxNode syntax = boundIsOperator.Syntax;
            ITypeSymbol? type = boundIsOperator.GetPublicTypeSymbol();
            bool isNegated = false;
            bool isImplicit = boundIsOperator.WasCompilerGenerated;
            return new IsTypeOperation(value, typeOperand, isNegated, _semanticModel, syntax, type, isImplicit);
        }
 
        private ISizeOfOperation CreateBoundSizeOfOperatorOperation(BoundSizeOfOperator boundSizeOfOperator)
        {
            ITypeSymbol? typeOperand = boundSizeOfOperator.SourceType.GetPublicTypeSymbol();
            Debug.Assert(typeOperand is not null);
            SyntaxNode syntax = boundSizeOfOperator.Syntax;
            ITypeSymbol? type = boundSizeOfOperator.GetPublicTypeSymbol();
            ConstantValue? constantValue = boundSizeOfOperator.ConstantValueOpt;
            bool isImplicit = boundSizeOfOperator.WasCompilerGenerated;
            return new SizeOfOperation(typeOperand, _semanticModel, syntax, type, constantValue, isImplicit);
        }
 
        private ITypeOfOperation CreateBoundTypeOfOperatorOperation(BoundTypeOfOperator boundTypeOfOperator)
        {
            ITypeSymbol? typeOperand = boundTypeOfOperator.SourceType.GetPublicTypeSymbol();
            Debug.Assert(typeOperand is not null);
            SyntaxNode syntax = boundTypeOfOperator.Syntax;
            ITypeSymbol? type = boundTypeOfOperator.GetPublicTypeSymbol();
            bool isImplicit = boundTypeOfOperator.WasCompilerGenerated;
            return new TypeOfOperation(typeOperand, _semanticModel, syntax, type, isImplicit);
        }
 
        private IArrayCreationOperation CreateBoundArrayCreationOperation(BoundArrayCreation boundArrayCreation)
        {
            ImmutableArray<IOperation> dimensionSizes = CreateFromArray<BoundExpression, IOperation>(boundArrayCreation.Bounds);
            IArrayInitializerOperation? arrayInitializer = (IArrayInitializerOperation?)Create(boundArrayCreation.InitializerOpt);
            SyntaxNode syntax = boundArrayCreation.Syntax;
            ITypeSymbol? type = boundArrayCreation.GetPublicTypeSymbol();
            bool isImplicit = boundArrayCreation.WasCompilerGenerated ||
                              (boundArrayCreation.InitializerOpt?.Syntax == syntax && !boundArrayCreation.InitializerOpt.WasCompilerGenerated);
            return new ArrayCreationOperation(dimensionSizes, arrayInitializer, _semanticModel, syntax, type, isImplicit);
        }
 
        private IArrayInitializerOperation CreateBoundArrayInitializationOperation(BoundArrayInitialization boundArrayInitialization)
        {
            ImmutableArray<IOperation> elementValues = CreateFromArray<BoundExpression, IOperation>(boundArrayInitialization.Initializers);
            SyntaxNode syntax = boundArrayInitialization.Syntax;
            bool isImplicit = boundArrayInitialization.WasCompilerGenerated;
            return new ArrayInitializerOperation(elementValues, _semanticModel, syntax, isImplicit);
        }
 
        private ICollectionExpressionOperation CreateBoundCollectionExpression(BoundCollectionExpression expr)
        {
            SyntaxNode syntax = expr.Syntax;
            ITypeSymbol? collectionType = expr.GetPublicTypeSymbol();
            bool isImplicit = expr.WasCompilerGenerated;
            IMethodSymbol? constructMethod = getConstructMethod((CSharpCompilation)_semanticModel.Compilation, expr).GetPublicSymbol();
            ImmutableArray<IOperation> elements = expr.Elements.SelectAsArray((element, expr) => CreateBoundCollectionExpressionElement(expr, element), expr);
            return new CollectionExpressionOperation(
                constructMethod,
                elements,
                _semanticModel,
                syntax,
                collectionType,
                isImplicit);
 
            static MethodSymbol? getConstructMethod(CSharpCompilation compilation, BoundCollectionExpression expr)
            {
                switch (expr.CollectionTypeKind)
                {
                    case CollectionExpressionTypeKind.None:
                    case CollectionExpressionTypeKind.Array:
                    case CollectionExpressionTypeKind.ArrayInterface:
                    case CollectionExpressionTypeKind.ReadOnlySpan:
                    case CollectionExpressionTypeKind.Span:
                        return null;
                    case CollectionExpressionTypeKind.ImplementsIEnumerable:
                        return (expr.CollectionCreation as BoundObjectCreationExpression)?.Constructor;
                    case CollectionExpressionTypeKind.CollectionBuilder:
                        return expr.CollectionBuilderMethod;
                    default:
                        throw ExceptionUtilities.UnexpectedValue(expr.CollectionTypeKind);
                }
            }
        }
 
        private IOperation CreateBoundCollectionExpressionElement(BoundCollectionExpression expr, BoundNode element)
        {
            return element is BoundCollectionExpressionSpreadElement spreadElement ?
                CreateBoundCollectionExpressionSpreadElement(expr, spreadElement) :
                Create(Binder.GetUnderlyingCollectionExpressionElement(expr, (BoundExpression)element, throwOnErrors: false));
        }
 
        private ISpreadOperation CreateBoundCollectionExpressionSpreadElement(BoundCollectionExpression expr, BoundCollectionExpressionSpreadElement element)
        {
            var iteratorItem = element.IteratorBody is { } iteratorBody ?
                Binder.GetUnderlyingCollectionExpressionElement(expr, ((BoundExpressionStatement)iteratorBody).Expression, throwOnErrors: false) :
                null;
            var collection = Create(element.Expression);
            SyntaxNode syntax = element.Syntax;
            bool isImplicit = element.WasCompilerGenerated;
            var elementType = element.EnumeratorInfoOpt?.ElementType.GetPublicSymbol();
            var elementConversion = BoundNode.GetConversion(iteratorItem, element.ElementPlaceholder);
            return new SpreadOperation(
                collection,
                elementType: elementType,
                elementConversion,
                _semanticModel,
                syntax,
                isImplicit);
        }
 
        private IDefaultValueOperation CreateBoundDefaultLiteralOperation(BoundDefaultLiteral boundDefaultLiteral)
        {
            SyntaxNode syntax = boundDefaultLiteral.Syntax;
            ConstantValue? constantValue = boundDefaultLiteral.ConstantValueOpt;
            bool isImplicit = boundDefaultLiteral.WasCompilerGenerated;
            return new DefaultValueOperation(_semanticModel, syntax, type: null, constantValue, isImplicit);
        }
 
        private IDefaultValueOperation CreateBoundDefaultExpressionOperation(BoundDefaultExpression boundDefaultExpression)
        {
            SyntaxNode syntax = boundDefaultExpression.Syntax;
            ITypeSymbol? type = boundDefaultExpression.GetPublicTypeSymbol();
            ConstantValue? constantValue = ((BoundExpression)boundDefaultExpression).ConstantValueOpt;
            bool isImplicit = boundDefaultExpression.WasCompilerGenerated;
            return new DefaultValueOperation(_semanticModel, syntax, type, constantValue, isImplicit);
        }
 
        private IInstanceReferenceOperation CreateBoundBaseReferenceOperation(BoundBaseReference boundBaseReference)
        {
            InstanceReferenceKind referenceKind = InstanceReferenceKind.ContainingTypeInstance;
            SyntaxNode syntax = boundBaseReference.Syntax;
            ITypeSymbol? type = boundBaseReference.GetPublicTypeSymbol();
            bool isImplicit = boundBaseReference.WasCompilerGenerated;
            return new InstanceReferenceOperation(referenceKind, _semanticModel, syntax, type, isImplicit);
        }
 
        private IInstanceReferenceOperation CreateBoundThisReferenceOperation(BoundThisReference boundThisReference)
        {
            InstanceReferenceKind referenceKind = InstanceReferenceKind.ContainingTypeInstance;
            SyntaxNode syntax = boundThisReference.Syntax;
            ITypeSymbol? type = boundThisReference.GetPublicTypeSymbol();
            bool isImplicit = boundThisReference.WasCompilerGenerated;
            return new InstanceReferenceOperation(referenceKind, _semanticModel, syntax, type, isImplicit);
        }
 
        private IOperation CreateBoundAssignmentOperatorOrMemberInitializerOperation(BoundAssignmentOperator boundAssignmentOperator)
        {
            return IsMemberInitializer(boundAssignmentOperator) ?
                (IOperation)CreateBoundMemberInitializerOperation(boundAssignmentOperator) :
                CreateBoundAssignmentOperatorOperation(boundAssignmentOperator);
        }
 
        private static bool IsMemberInitializer(BoundAssignmentOperator boundAssignmentOperator) =>
            boundAssignmentOperator.Right?.Kind == BoundKind.ObjectInitializerExpression ||
            boundAssignmentOperator.Right?.Kind == BoundKind.CollectionInitializerExpression;
 
        private ISimpleAssignmentOperation CreateBoundAssignmentOperatorOperation(BoundAssignmentOperator boundAssignmentOperator)
        {
            Debug.Assert(!IsMemberInitializer(boundAssignmentOperator));
 
            IOperation target = Create(boundAssignmentOperator.Left);
            IOperation value = Create(boundAssignmentOperator.Right);
            bool isRef = boundAssignmentOperator.IsRef;
            SyntaxNode syntax = boundAssignmentOperator.Syntax;
            ITypeSymbol? type = boundAssignmentOperator.GetPublicTypeSymbol();
            ConstantValue? constantValue = boundAssignmentOperator.ConstantValueOpt;
            bool isImplicit = boundAssignmentOperator.WasCompilerGenerated;
            return new SimpleAssignmentOperation(isRef, target, value, _semanticModel, syntax, type, constantValue, isImplicit);
        }
 
        private IMemberInitializerOperation CreateBoundMemberInitializerOperation(BoundAssignmentOperator boundAssignmentOperator)
        {
            Debug.Assert(IsMemberInitializer(boundAssignmentOperator));
            IOperation initializedMember = CreateMemberInitializerInitializedMember(boundAssignmentOperator.Left);
            IObjectOrCollectionInitializerOperation initializer = (IObjectOrCollectionInitializerOperation)Create(boundAssignmentOperator.Right);
            SyntaxNode syntax = boundAssignmentOperator.Syntax;
            ITypeSymbol? type = boundAssignmentOperator.GetPublicTypeSymbol();
            bool isImplicit = boundAssignmentOperator.WasCompilerGenerated;
            return new MemberInitializerOperation(initializedMember, initializer, _semanticModel, syntax, type, isImplicit);
        }
 
        private ICompoundAssignmentOperation CreateBoundCompoundAssignmentOperatorOperation(BoundCompoundAssignmentOperator boundCompoundAssignmentOperator)
        {
            IOperation target = Create(boundCompoundAssignmentOperator.Left);
            IOperation value = Create(boundCompoundAssignmentOperator.Right);
            BinaryOperatorKind operatorKind = Helper.DeriveBinaryOperatorKind(boundCompoundAssignmentOperator.Operator.Kind);
            Conversion inConversion = BoundNode.GetConversion(boundCompoundAssignmentOperator.LeftConversion, boundCompoundAssignmentOperator.LeftPlaceholder);
            Conversion outConversion = BoundNode.GetConversion(boundCompoundAssignmentOperator.FinalConversion, boundCompoundAssignmentOperator.FinalPlaceholder);
            bool isLifted = boundCompoundAssignmentOperator.Operator.Kind.IsLifted();
            var method = boundCompoundAssignmentOperator.Operator.Method;
            bool isChecked = boundCompoundAssignmentOperator.Operator.Kind.IsChecked() || (method is not null && SyntaxFacts.IsCheckedOperator(method.Name));
            IMethodSymbol? operatorMethod = method.GetPublicSymbol();
            SyntaxNode syntax = boundCompoundAssignmentOperator.Syntax;
            ITypeSymbol? type = boundCompoundAssignmentOperator.GetPublicTypeSymbol();
            bool isImplicit = boundCompoundAssignmentOperator.WasCompilerGenerated;
            return new CompoundAssignmentOperation(inConversion, outConversion, operatorKind, isLifted, isChecked, operatorMethod,
                                                   GetConstrainedToTypeForOperator(method, boundCompoundAssignmentOperator.Operator.ConstrainedToTypeOpt).GetPublicSymbol(),
                                                   target, value, _semanticModel, syntax, type, isImplicit);
        }
 
        private static TypeParameterSymbol? GetConstrainedToTypeForOperator(MethodSymbol? operatorMethod, TypeSymbol? constrainedToTypeOpt)
        {
            if (operatorMethod is not null && operatorMethod.IsStatic && (operatorMethod.IsAbstract || operatorMethod.IsVirtual) &&
                constrainedToTypeOpt is TypeParameterSymbol typeParameter)
            {
                return typeParameter;
            }
 
            return null;
        }
 
        private IIncrementOrDecrementOperation CreateBoundIncrementOperatorOperation(BoundIncrementOperator boundIncrementOperator)
        {
            OperationKind operationKind = Helper.IsDecrement(boundIncrementOperator.OperatorKind) ? OperationKind.Decrement : OperationKind.Increment;
            bool isPostfix = Helper.IsPostfixIncrementOrDecrement(boundIncrementOperator.OperatorKind);
            bool isLifted = boundIncrementOperator.OperatorKind.IsLifted();
            bool isChecked = boundIncrementOperator.OperatorKind.IsChecked() || (boundIncrementOperator.MethodOpt is not null && SyntaxFacts.IsCheckedOperator(boundIncrementOperator.MethodOpt.Name));
            IOperation target = Create(boundIncrementOperator.Operand);
            IMethodSymbol? operatorMethod = boundIncrementOperator.MethodOpt.GetPublicSymbol();
            SyntaxNode syntax = boundIncrementOperator.Syntax;
            ITypeSymbol? type = boundIncrementOperator.GetPublicTypeSymbol();
            bool isImplicit = boundIncrementOperator.WasCompilerGenerated;
            return new IncrementOrDecrementOperation(isPostfix, isLifted, isChecked, target, operatorMethod,
                                                     GetConstrainedToTypeForOperator(boundIncrementOperator.MethodOpt, boundIncrementOperator.ConstrainedToTypeOpt).GetPublicSymbol(),
                                                     operationKind, _semanticModel, syntax, type, isImplicit);
        }
 
        private IInvalidOperation CreateBoundBadExpressionOperation(BoundBadExpression boundBadExpression)
        {
            SyntaxNode syntax = boundBadExpression.Syntax;
            // We match semantic model here: if the expression IsMissing, we have a null type, rather than the ErrorType of the bound node.
            ITypeSymbol? type = syntax.IsMissing ? null : boundBadExpression.GetPublicTypeSymbol();
 
            // if child has syntax node point to same syntax node as bad expression, then this invalid expression is implicit
            bool isImplicit = boundBadExpression.WasCompilerGenerated || boundBadExpression.ChildBoundNodes.Any(static (e, boundBadExpression) => e?.Syntax == boundBadExpression.Syntax, boundBadExpression);
            var children = CreateFromArray<BoundExpression, IOperation>(boundBadExpression.ChildBoundNodes);
            return new InvalidOperation(children, _semanticModel, syntax, type, constantValue: null, isImplicit);
        }
 
        private ITypeParameterObjectCreationOperation CreateBoundNewTOperation(BoundNewT boundNewT)
        {
            IObjectOrCollectionInitializerOperation? initializer = (IObjectOrCollectionInitializerOperation?)Create(boundNewT.InitializerExpressionOpt);
            SyntaxNode syntax = boundNewT.Syntax;
            ITypeSymbol? type = boundNewT.GetPublicTypeSymbol();
            bool isImplicit = boundNewT.WasCompilerGenerated;
            return new TypeParameterObjectCreationOperation(initializer, _semanticModel, syntax, type, isImplicit);
        }
 
        private INoPiaObjectCreationOperation CreateNoPiaObjectCreationExpressionOperation(BoundNoPiaObjectCreationExpression creation)
        {
            IObjectOrCollectionInitializerOperation? initializer = (IObjectOrCollectionInitializerOperation?)Create(creation.InitializerExpressionOpt);
            SyntaxNode syntax = creation.Syntax;
            ITypeSymbol? type = creation.GetPublicTypeSymbol();
            bool isImplicit = creation.WasCompilerGenerated;
            return new NoPiaObjectCreationOperation(initializer, _semanticModel, syntax, type, isImplicit);
        }
 
        private IUnaryOperation CreateBoundUnaryOperatorOperation(BoundUnaryOperator boundUnaryOperator)
        {
            UnaryOperatorKind unaryOperatorKind = Helper.DeriveUnaryOperatorKind(boundUnaryOperator.OperatorKind);
            IOperation operand = Create(boundUnaryOperator.Operand);
            IMethodSymbol? operatorMethod = boundUnaryOperator.MethodOpt.GetPublicSymbol();
            SyntaxNode syntax = boundUnaryOperator.Syntax;
            ITypeSymbol? type = boundUnaryOperator.GetPublicTypeSymbol();
            ConstantValue? constantValue = boundUnaryOperator.ConstantValueOpt;
            bool isLifted = boundUnaryOperator.OperatorKind.IsLifted();
            bool isChecked = boundUnaryOperator.OperatorKind.IsChecked() || (boundUnaryOperator.MethodOpt is not null && SyntaxFacts.IsCheckedOperator(boundUnaryOperator.MethodOpt.Name));
            bool isImplicit = boundUnaryOperator.WasCompilerGenerated;
            return new UnaryOperation(unaryOperatorKind, operand, isLifted, isChecked, operatorMethod,
                                      GetConstrainedToTypeForOperator(boundUnaryOperator.MethodOpt, boundUnaryOperator.ConstrainedToTypeOpt).GetPublicSymbol(),
                                      _semanticModel, syntax, type, constantValue, isImplicit);
        }
        private IOperation CreateBoundBinaryOperatorBase(BoundBinaryOperatorBase boundBinaryOperatorBase)
        {
            if (boundBinaryOperatorBase is BoundBinaryOperator { InterpolatedStringHandlerData: not null } binary)
            {
                return CreateBoundInterpolatedStringBinaryOperator(binary);
            }
 
            // Binary operators can be nested _many_ levels deep, and cause a stack overflow if we manually recurse.
            // To solve this, we use a manual stack for the left side.
            var stack = ArrayBuilder<BoundBinaryOperatorBase>.GetInstance();
            BoundBinaryOperatorBase? currentBinary = boundBinaryOperatorBase;
 
            do
            {
                stack.Push(currentBinary);
                currentBinary = currentBinary.Left as BoundBinaryOperatorBase;
            } while (currentBinary is not null and not BoundBinaryOperator { InterpolatedStringHandlerData: not null });
 
            Debug.Assert(stack.Count > 0);
            IOperation? left = null;
 
            while (stack.TryPop(out currentBinary))
            {
                left ??= Create(currentBinary.Left);
                IOperation right = Create(currentBinary.Right);
                left = currentBinary switch
                {
                    BoundBinaryOperator binaryOp => CreateBoundBinaryOperatorOperation(binaryOp, left, right),
                    BoundUserDefinedConditionalLogicalOperator logicalOp => createBoundUserDefinedConditionalLogicalOperator(logicalOp, left, right),
                    { Kind: var kind } => throw ExceptionUtilities.UnexpectedValue(kind)
                };
            }
 
            Debug.Assert(left is not null && stack.Count == 0);
            stack.Free();
            return left;
 
            IBinaryOperation createBoundUserDefinedConditionalLogicalOperator(BoundUserDefinedConditionalLogicalOperator boundBinaryOperator, IOperation left, IOperation right)
            {
                BinaryOperatorKind operatorKind = Helper.DeriveBinaryOperatorKind(boundBinaryOperator.OperatorKind);
                IMethodSymbol operatorMethod = boundBinaryOperator.LogicalOperator.GetPublicSymbol();
                IMethodSymbol unaryOperatorMethod = boundBinaryOperator.OperatorKind.Operator() == CSharp.BinaryOperatorKind.And ?
                                                        boundBinaryOperator.FalseOperator.GetPublicSymbol() :
                                                        boundBinaryOperator.TrueOperator.GetPublicSymbol();
                SyntaxNode syntax = boundBinaryOperator.Syntax;
                ITypeSymbol? type = boundBinaryOperator.GetPublicTypeSymbol();
                ConstantValue? constantValue = boundBinaryOperator.ConstantValueOpt;
                bool isLifted = boundBinaryOperator.OperatorKind.IsLifted();
                bool isChecked = boundBinaryOperator.OperatorKind.IsChecked();
                bool isCompareText = false;
                bool isImplicit = boundBinaryOperator.WasCompilerGenerated;
                TypeSymbol? constrainedToTypeOpt = GetConstrainedToTypeForOperator(boundBinaryOperator.LogicalOperator, boundBinaryOperator.ConstrainedToTypeOpt) ??
                                                   GetConstrainedToTypeForOperator(boundBinaryOperator.OperatorKind.Operator() == CSharp.BinaryOperatorKind.And ?
                                                                                       boundBinaryOperator.FalseOperator :
                                                                                       boundBinaryOperator.TrueOperator,
                                                                                   boundBinaryOperator.ConstrainedToTypeOpt);
                return new BinaryOperation(operatorKind, left, right, isLifted, isChecked, isCompareText, operatorMethod, constrainedToTypeOpt.GetPublicSymbol(), unaryOperatorMethod,
                                           _semanticModel, syntax, type, constantValue, isImplicit);
            }
        }
 
        private IBinaryOperation CreateBoundBinaryOperatorOperation(BoundBinaryOperator boundBinaryOperator, IOperation left, IOperation right)
        {
            BinaryOperatorKind operatorKind = Helper.DeriveBinaryOperatorKind(boundBinaryOperator.OperatorKind);
            IMethodSymbol? operatorMethod = boundBinaryOperator.Method.GetPublicSymbol();
            IMethodSymbol? unaryOperatorMethod = null;
 
            // For dynamic logical operator MethodOpt is actually the unary true/false operator
            if (boundBinaryOperator.Type.IsDynamic() &&
                (operatorKind == BinaryOperatorKind.ConditionalAnd || operatorKind == BinaryOperatorKind.ConditionalOr) &&
                operatorMethod?.Parameters.Length == 1)
            {
                unaryOperatorMethod = operatorMethod;
                operatorMethod = null;
            }
 
            SyntaxNode syntax = boundBinaryOperator.Syntax;
            ITypeSymbol? type = boundBinaryOperator.GetPublicTypeSymbol();
            ConstantValue? constantValue = boundBinaryOperator.ConstantValueOpt;
            bool isLifted = boundBinaryOperator.OperatorKind.IsLifted();
            bool isChecked = boundBinaryOperator.OperatorKind.IsChecked() || (boundBinaryOperator.Method is not null && SyntaxFacts.IsCheckedOperator(boundBinaryOperator.Method.Name));
            bool isCompareText = false;
            bool isImplicit = boundBinaryOperator.WasCompilerGenerated;
            return new BinaryOperation(operatorKind, left, right, isLifted, isChecked, isCompareText,
                                       operatorMethod,
                                       GetConstrainedToTypeForOperator(boundBinaryOperator.Method, boundBinaryOperator.ConstrainedToType).GetPublicSymbol(),
                                       unaryOperatorMethod,
                                       _semanticModel, syntax, type, constantValue, isImplicit);
        }
 
        private IOperation CreateBoundInterpolatedStringBinaryOperator(BoundBinaryOperator boundBinaryOperator)
        {
            Debug.Assert(boundBinaryOperator.InterpolatedStringHandlerData is not null);
            Func<BoundInterpolatedString, int, (CSharpOperationFactory, InterpolatedStringHandlerData), IOperation> createInterpolatedString
                = createInterpolatedStringOperand;
 
            Func<BoundBinaryOperator, IOperation, IOperation, (CSharpOperationFactory, InterpolatedStringHandlerData), IOperation> createBinaryOperator
                = createBoundBinaryOperatorOperation;
 
            return boundBinaryOperator.RewriteInterpolatedStringAddition((this, boundBinaryOperator.InterpolatedStringHandlerData.GetValueOrDefault()), createInterpolatedString, createBinaryOperator);
 
            static IInterpolatedStringOperation createInterpolatedStringOperand(
                BoundInterpolatedString boundInterpolatedString,
                int i,
                (CSharpOperationFactory @this, InterpolatedStringHandlerData Data) arg)
                => arg.@this.CreateBoundInterpolatedStringExpressionOperation(boundInterpolatedString, arg.Data.PositionInfo[i]);
 
            static IBinaryOperation createBoundBinaryOperatorOperation(
                BoundBinaryOperator boundBinaryOperator,
                IOperation left,
                IOperation right,
                (CSharpOperationFactory @this, InterpolatedStringHandlerData _) arg)
                => arg.@this.CreateBoundBinaryOperatorOperation(boundBinaryOperator, left, right);
        }
 
        private ITupleBinaryOperation CreateBoundTupleBinaryOperatorOperation(BoundTupleBinaryOperator boundTupleBinaryOperator)
        {
            IOperation left = Create(boundTupleBinaryOperator.Left);
            IOperation right = Create(boundTupleBinaryOperator.Right);
            BinaryOperatorKind operatorKind = Helper.DeriveBinaryOperatorKind(boundTupleBinaryOperator.OperatorKind);
            SyntaxNode syntax = boundTupleBinaryOperator.Syntax;
            ITypeSymbol? type = boundTupleBinaryOperator.GetPublicTypeSymbol();
            bool isImplicit = boundTupleBinaryOperator.WasCompilerGenerated;
            return new TupleBinaryOperation(operatorKind, left, right, _semanticModel, syntax, type, isImplicit);
        }
 
        private IConditionalOperation CreateBoundConditionalOperatorOperation(BoundConditionalOperator boundConditionalOperator)
        {
            IOperation condition = Create(boundConditionalOperator.Condition);
            IOperation whenTrue = Create(boundConditionalOperator.Consequence);
            IOperation whenFalse = Create(boundConditionalOperator.Alternative);
            bool isRef = boundConditionalOperator.IsRef;
            SyntaxNode syntax = boundConditionalOperator.Syntax;
            ITypeSymbol? type = boundConditionalOperator.GetPublicTypeSymbol();
            ConstantValue? constantValue = boundConditionalOperator.ConstantValueOpt;
            bool isImplicit = boundConditionalOperator.WasCompilerGenerated;
            return new ConditionalOperation(condition, whenTrue, whenFalse, isRef, _semanticModel, syntax, type, constantValue, isImplicit);
        }
 
        private ICoalesceOperation CreateBoundNullCoalescingOperatorOperation(BoundNullCoalescingOperator boundNullCoalescingOperator)
        {
            IOperation value = Create(boundNullCoalescingOperator.LeftOperand);
            IOperation whenNull = Create(boundNullCoalescingOperator.RightOperand);
            SyntaxNode syntax = boundNullCoalescingOperator.Syntax;
            ITypeSymbol? type = boundNullCoalescingOperator.GetPublicTypeSymbol();
            ConstantValue? constantValue = boundNullCoalescingOperator.ConstantValueOpt;
            bool isImplicit = boundNullCoalescingOperator.WasCompilerGenerated;
            Conversion valueConversion = BoundNode.GetConversion(boundNullCoalescingOperator.LeftConversion, boundNullCoalescingOperator.LeftPlaceholder);
 
            if (valueConversion.Exists && !valueConversion.IsIdentity &&
                boundNullCoalescingOperator.Type.Equals(boundNullCoalescingOperator.LeftOperand.Type?.StrippedType(), TypeCompareKind.IgnoreCustomModifiersAndArraySizesAndLowerBounds | TypeCompareKind.IgnoreNullableModifiersForReferenceTypes))
            {
                valueConversion = Conversion.Identity;
            }
 
            return new CoalesceOperation(value, whenNull, valueConversion, _semanticModel, syntax, type, constantValue, isImplicit);
        }
 
        private IOperation CreateBoundNullCoalescingAssignmentOperatorOperation(BoundNullCoalescingAssignmentOperator boundNode)
        {
            IOperation target = Create(boundNode.LeftOperand);
            IOperation value = Create(boundNode.RightOperand);
            SyntaxNode syntax = boundNode.Syntax;
            ITypeSymbol? type = boundNode.GetPublicTypeSymbol();
            bool isImplicit = boundNode.WasCompilerGenerated;
 
            return new CoalesceAssignmentOperation(target, value, _semanticModel, syntax, type, isImplicit);
        }
 
        private IAwaitOperation CreateBoundAwaitExpressionOperation(BoundAwaitExpression boundAwaitExpression)
        {
            IOperation awaitedValue = Create(boundAwaitExpression.Expression);
            SyntaxNode syntax = boundAwaitExpression.Syntax;
            ITypeSymbol? type = boundAwaitExpression.GetPublicTypeSymbol();
            bool isImplicit = boundAwaitExpression.WasCompilerGenerated;
            return new AwaitOperation(awaitedValue, _semanticModel, syntax, type, isImplicit);
        }
 
        private IArrayElementReferenceOperation CreateBoundArrayAccessOperation(BoundArrayAccess boundArrayAccess)
        {
            IOperation arrayReference = Create(boundArrayAccess.Expression);
            ImmutableArray<IOperation> indices = CreateFromArray<BoundExpression, IOperation>(boundArrayAccess.Indices);
            SyntaxNode syntax = boundArrayAccess.Syntax;
            ITypeSymbol? type = boundArrayAccess.GetPublicTypeSymbol();
            bool isImplicit = boundArrayAccess.WasCompilerGenerated;
 
            return new ArrayElementReferenceOperation(arrayReference, indices, _semanticModel, syntax, type, isImplicit);
        }
 
        private IOperation CreateBoundImplicitIndexerAccessOperation(BoundImplicitIndexerAccess boundIndexerAccess)
        {
            IOperation instance = Create(boundIndexerAccess.Receiver);
            IOperation argument = Create(boundIndexerAccess.Argument);
            SyntaxNode syntax = boundIndexerAccess.Syntax;
            ITypeSymbol? type = boundIndexerAccess.GetPublicTypeSymbol();
            bool isImplicit = boundIndexerAccess.WasCompilerGenerated;
 
            if (boundIndexerAccess.LengthOrCountAccess.Kind == BoundKind.ArrayLength)
            {
                return new ArrayElementReferenceOperation(instance, ImmutableArray.Create(argument), _semanticModel, syntax, type, isImplicit);
            }
 
            var lengthSymbol = Binder.GetPropertySymbol(boundIndexerAccess.LengthOrCountAccess, out _, out _).GetPublicSymbol();
            var indexerSymbol = Binder.GetIndexerOrImplicitIndexerSymbol(boundIndexerAccess).GetPublicSymbol();
 
            Debug.Assert(lengthSymbol is not null);
            Debug.Assert(indexerSymbol is not null);
 
            return new ImplicitIndexerReferenceOperation(instance, argument, lengthSymbol, indexerSymbol, _semanticModel, syntax, type, isImplicit);
        }
 
        private IInlineArrayAccessOperation CreateBoundInlineArrayAccessOperation(BoundInlineArrayAccess boundInlineArrayAccess)
        {
            IOperation arrayReference = Create(boundInlineArrayAccess.Expression);
            IOperation argument = Create(boundInlineArrayAccess.Argument);
            SyntaxNode syntax = boundInlineArrayAccess.Syntax;
            ITypeSymbol? type = boundInlineArrayAccess.GetPublicTypeSymbol();
            bool isImplicit = boundInlineArrayAccess.WasCompilerGenerated;
 
            return new InlineArrayAccessOperation(arrayReference, argument, _semanticModel, syntax, type, isImplicit);
        }
 
        private INameOfOperation CreateBoundNameOfOperatorOperation(BoundNameOfOperator boundNameOfOperator)
        {
            IOperation argument = Create(boundNameOfOperator.Argument);
            SyntaxNode syntax = boundNameOfOperator.Syntax;
            ITypeSymbol? type = boundNameOfOperator.GetPublicTypeSymbol();
            ConstantValue constantValue = boundNameOfOperator.ConstantValueOpt;
            bool isImplicit = boundNameOfOperator.WasCompilerGenerated;
            return new NameOfOperation(argument, _semanticModel, syntax, type, constantValue, isImplicit);
        }
 
        private IThrowOperation CreateBoundThrowExpressionOperation(BoundThrowExpression boundThrowExpression)
        {
            IOperation expression = Create(boundThrowExpression.Expression);
            SyntaxNode syntax = boundThrowExpression.Syntax;
            ITypeSymbol? type = boundThrowExpression.GetPublicTypeSymbol();
            bool isImplicit = boundThrowExpression.WasCompilerGenerated;
            return new ThrowOperation(expression, _semanticModel, syntax, type, isImplicit);
        }
 
        private IAddressOfOperation CreateBoundAddressOfOperatorOperation(BoundAddressOfOperator boundAddressOfOperator)
        {
            IOperation reference = Create(boundAddressOfOperator.Operand);
            SyntaxNode syntax = boundAddressOfOperator.Syntax;
            ITypeSymbol? type = boundAddressOfOperator.GetPublicTypeSymbol();
            bool isImplicit = boundAddressOfOperator.WasCompilerGenerated;
            return new AddressOfOperation(reference, _semanticModel, syntax, type, isImplicit);
        }
 
        private IInstanceReferenceOperation CreateBoundImplicitReceiverOperation(BoundImplicitReceiver boundImplicitReceiver)
        {
            InstanceReferenceKind referenceKind = InstanceReferenceKind.ImplicitReceiver;
            SyntaxNode syntax = boundImplicitReceiver.Syntax;
            ITypeSymbol? type = boundImplicitReceiver.GetPublicTypeSymbol();
            bool isImplicit = boundImplicitReceiver.WasCompilerGenerated;
            return new InstanceReferenceOperation(referenceKind, _semanticModel, syntax, type, isImplicit);
        }
 
        private IConditionalAccessOperation CreateBoundConditionalAccessOperation(BoundConditionalAccess boundConditionalAccess)
        {
            IOperation operation = Create(boundConditionalAccess.Receiver);
            IOperation whenNotNull = Create(boundConditionalAccess.AccessExpression);
            SyntaxNode syntax = boundConditionalAccess.Syntax;
            ITypeSymbol? type = boundConditionalAccess.GetPublicTypeSymbol();
            bool isImplicit = boundConditionalAccess.WasCompilerGenerated;
 
            return new ConditionalAccessOperation(operation, whenNotNull, _semanticModel, syntax, type, isImplicit);
        }
 
        private IConditionalAccessInstanceOperation CreateBoundConditionalReceiverOperation(BoundConditionalReceiver boundConditionalReceiver)
        {
            SyntaxNode syntax = boundConditionalReceiver.Syntax;
            ITypeSymbol? type = boundConditionalReceiver.GetPublicTypeSymbol();
            bool isImplicit = boundConditionalReceiver.WasCompilerGenerated;
            return new ConditionalAccessInstanceOperation(_semanticModel, syntax, type, isImplicit);
        }
 
        private IFieldInitializerOperation CreateBoundFieldEqualsValueOperation(BoundFieldEqualsValue boundFieldEqualsValue)
        {
            ImmutableArray<IFieldSymbol> initializedFields = ImmutableArray.Create<IFieldSymbol>(boundFieldEqualsValue.Field.GetPublicSymbol());
            IOperation value = Create(boundFieldEqualsValue.Value);
            SyntaxNode syntax = boundFieldEqualsValue.Syntax;
            bool isImplicit = boundFieldEqualsValue.WasCompilerGenerated;
            return new FieldInitializerOperation(initializedFields, boundFieldEqualsValue.Locals.GetPublicSymbols(), value, _semanticModel, syntax, isImplicit);
        }
 
        private IPropertyInitializerOperation CreateBoundPropertyEqualsValueOperation(BoundPropertyEqualsValue boundPropertyEqualsValue)
        {
            ImmutableArray<IPropertySymbol> initializedProperties = ImmutableArray.Create<IPropertySymbol>(boundPropertyEqualsValue.Property.GetPublicSymbol());
            IOperation value = Create(boundPropertyEqualsValue.Value);
            SyntaxNode syntax = boundPropertyEqualsValue.Syntax;
            bool isImplicit = boundPropertyEqualsValue.WasCompilerGenerated;
            return new PropertyInitializerOperation(initializedProperties, boundPropertyEqualsValue.Locals.GetPublicSymbols(), value, _semanticModel, syntax, isImplicit);
        }
 
        private IParameterInitializerOperation CreateBoundParameterEqualsValueOperation(BoundParameterEqualsValue boundParameterEqualsValue)
        {
            IParameterSymbol parameter = boundParameterEqualsValue.Parameter.GetPublicSymbol();
            IOperation value = Create(boundParameterEqualsValue.Value);
            SyntaxNode syntax = boundParameterEqualsValue.Syntax;
            bool isImplicit = boundParameterEqualsValue.WasCompilerGenerated;
            return new ParameterInitializerOperation(parameter, boundParameterEqualsValue.Locals.GetPublicSymbols(), value, _semanticModel, syntax, isImplicit);
        }
 
        private IBlockOperation CreateBoundBlockOperation(BoundBlock boundBlock)
        {
            ImmutableArray<IOperation> operations = CreateFromArray<BoundStatement, IOperation>(boundBlock.Statements);
            ImmutableArray<ILocalSymbol> locals = boundBlock.Locals.GetPublicSymbols();
            SyntaxNode syntax = boundBlock.Syntax;
            bool isImplicit = boundBlock.WasCompilerGenerated;
            return new BlockOperation(operations, locals, _semanticModel, syntax, isImplicit);
        }
 
        private IBranchOperation CreateBoundContinueStatementOperation(BoundContinueStatement boundContinueStatement)
        {
            ILabelSymbol target = boundContinueStatement.Label.GetPublicSymbol();
            BranchKind branchKind = BranchKind.Continue;
            SyntaxNode syntax = boundContinueStatement.Syntax;
            bool isImplicit = boundContinueStatement.WasCompilerGenerated;
            return new BranchOperation(target, branchKind, _semanticModel, syntax, isImplicit);
        }
 
        private IBranchOperation CreateBoundBreakStatementOperation(BoundBreakStatement boundBreakStatement)
        {
            ILabelSymbol target = boundBreakStatement.Label.GetPublicSymbol();
            BranchKind branchKind = BranchKind.Break;
            SyntaxNode syntax = boundBreakStatement.Syntax;
            bool isImplicit = boundBreakStatement.WasCompilerGenerated;
            return new BranchOperation(target, branchKind, _semanticModel, syntax, isImplicit);
        }
 
        private IReturnOperation CreateBoundYieldBreakStatementOperation(BoundYieldBreakStatement boundYieldBreakStatement)
        {
            IOperation? returnedValue = null;
            SyntaxNode syntax = boundYieldBreakStatement.Syntax;
            bool isImplicit = boundYieldBreakStatement.WasCompilerGenerated;
            return new ReturnOperation(returnedValue, OperationKind.YieldBreak, _semanticModel, syntax, isImplicit);
        }
 
        private IBranchOperation CreateBoundGotoStatementOperation(BoundGotoStatement boundGotoStatement)
        {
            ILabelSymbol target = boundGotoStatement.Label.GetPublicSymbol();
            BranchKind branchKind = BranchKind.GoTo;
            SyntaxNode syntax = boundGotoStatement.Syntax;
            bool isImplicit = boundGotoStatement.WasCompilerGenerated;
            return new BranchOperation(target, branchKind, _semanticModel, syntax, isImplicit);
        }
 
        private IEmptyOperation CreateBoundNoOpStatementOperation(BoundNoOpStatement boundNoOpStatement)
        {
            SyntaxNode syntax = boundNoOpStatement.Syntax;
            bool isImplicit = boundNoOpStatement.WasCompilerGenerated;
            return new EmptyOperation(_semanticModel, syntax, isImplicit);
        }
 
        private IConditionalOperation CreateBoundIfStatementOperation(BoundIfStatement boundIfStatement)
        {
            var stack = ArrayBuilder<BoundIfStatement>.GetInstance();
 
            IOperation? whenFalse;
            while (true)
            {
                stack.Push(boundIfStatement);
 
                var alternative = boundIfStatement.AlternativeOpt;
 
                if (alternative is BoundIfStatement elseIfStatement)
                {
                    boundIfStatement = elseIfStatement;
                }
                else
                {
                    whenFalse = Create(alternative);
                    break;
                }
            }
 
            ConditionalOperation result;
            do
            {
                boundIfStatement = stack.Pop();
                IOperation condition = Create(boundIfStatement.Condition);
                IOperation whenTrue = Create(boundIfStatement.Consequence);
                bool isRef = false;
                SyntaxNode syntax = boundIfStatement.Syntax;
                ITypeSymbol? type = null;
                ConstantValue? constantValue = null;
                bool isImplicit = boundIfStatement.WasCompilerGenerated;
                result = new ConditionalOperation(condition, whenTrue, whenFalse, isRef, _semanticModel, syntax, type, constantValue, isImplicit);
                whenFalse = result;
            }
            while (stack.Any());
 
            stack.Free();
            return result;
        }
 
        private IWhileLoopOperation CreateBoundWhileStatementOperation(BoundWhileStatement boundWhileStatement)
        {
            IOperation condition = Create(boundWhileStatement.Condition);
            IOperation body = Create(boundWhileStatement.Body);
            ImmutableArray<ILocalSymbol> locals = boundWhileStatement.Locals.GetPublicSymbols();
            ILabelSymbol continueLabel = boundWhileStatement.ContinueLabel.GetPublicSymbol();
            ILabelSymbol exitLabel = boundWhileStatement.BreakLabel.GetPublicSymbol();
            bool conditionIsTop = true;
            bool conditionIsUntil = false;
            SyntaxNode syntax = boundWhileStatement.Syntax;
            bool isImplicit = boundWhileStatement.WasCompilerGenerated;
            return new WhileLoopOperation(condition, conditionIsTop, conditionIsUntil, ignoredCondition: null, body, locals, continueLabel, exitLabel, _semanticModel, syntax, isImplicit);
        }
 
        private IWhileLoopOperation CreateBoundDoStatementOperation(BoundDoStatement boundDoStatement)
        {
            IOperation condition = Create(boundDoStatement.Condition);
            IOperation body = Create(boundDoStatement.Body);
            ILabelSymbol continueLabel = boundDoStatement.ContinueLabel.GetPublicSymbol();
            ILabelSymbol exitLabel = boundDoStatement.BreakLabel.GetPublicSymbol();
            bool conditionIsTop = false;
            bool conditionIsUntil = false;
            ImmutableArray<ILocalSymbol> locals = boundDoStatement.Locals.GetPublicSymbols();
            SyntaxNode syntax = boundDoStatement.Syntax;
            bool isImplicit = boundDoStatement.WasCompilerGenerated;
            return new WhileLoopOperation(condition, conditionIsTop, conditionIsUntil, ignoredCondition: null, body, locals, continueLabel, exitLabel, _semanticModel, syntax, isImplicit);
        }
 
        private IForLoopOperation CreateBoundForStatementOperation(BoundForStatement boundForStatement)
        {
            ImmutableArray<IOperation> before = CreateFromArray<BoundStatement, IOperation>(ToStatements(boundForStatement.Initializer));
            IOperation? condition = Create(boundForStatement.Condition);
            ImmutableArray<IOperation> atLoopBottom = CreateFromArray<BoundStatement, IOperation>(ToStatements(boundForStatement.Increment));
            IOperation body = Create(boundForStatement.Body);
            ImmutableArray<ILocalSymbol> locals = boundForStatement.OuterLocals.GetPublicSymbols();
            ImmutableArray<ILocalSymbol> conditionLocals = boundForStatement.InnerLocals.GetPublicSymbols();
            ILabelSymbol continueLabel = boundForStatement.ContinueLabel.GetPublicSymbol();
            ILabelSymbol exitLabel = boundForStatement.BreakLabel.GetPublicSymbol();
            SyntaxNode syntax = boundForStatement.Syntax;
            bool isImplicit = boundForStatement.WasCompilerGenerated;
            return new ForLoopOperation(before, conditionLocals, condition, atLoopBottom, body, locals, continueLabel, exitLabel, _semanticModel, syntax, isImplicit);
        }
 
        internal ForEachLoopOperationInfo? GetForEachLoopOperatorInfo(BoundForEachStatement boundForEachStatement)
        {
            ForEachEnumeratorInfo? enumeratorInfoOpt = boundForEachStatement.EnumeratorInfoOpt;
            ForEachLoopOperationInfo? info;
 
            if (enumeratorInfoOpt != null)
            {
                var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
                var compilation = (CSharpCompilation)_semanticModel.Compilation;
 
                var iDisposable = enumeratorInfoOpt.IsAsync
                                    ? compilation.GetWellKnownType(WellKnownType.System_IAsyncDisposable)
                                    : compilation.GetSpecialType(SpecialType.System_IDisposable);
 
                info = new ForEachLoopOperationInfo(enumeratorInfoOpt.ElementType.GetPublicSymbol(),
                                                    enumeratorInfoOpt.GetEnumeratorInfo.Method.GetPublicSymbol(),
                                                    ((PropertySymbol)enumeratorInfoOpt.CurrentPropertyGetter.AssociatedSymbol).GetPublicSymbol(),
                                                    enumeratorInfoOpt.MoveNextInfo.Method.GetPublicSymbol(),
                                                    isAsynchronous: enumeratorInfoOpt.IsAsync,
                                                    inlineArrayConversion: enumeratorInfoOpt.InlineArraySpanType is WellKnownType.Unknown ? null : Conversion.InlineArray,
                                                    collectionIsInlineArrayValue: enumeratorInfoOpt.InlineArrayUsedAsValue,
                                                    needsDispose: enumeratorInfoOpt.NeedsDisposal,
                                                    knownToImplementIDisposable: enumeratorInfoOpt.NeedsDisposal ?
                                                                                     compilation.Conversions.
                                                                                         HasImplicitConversionToOrImplementsVarianceCompatibleInterface(enumeratorInfoOpt.GetEnumeratorInfo.Method.ReturnType,
                                                                                                                            iDisposable,
                                                                                                                            ref discardedUseSiteInfo,
                                                                                                                            needSupportForRefStructInterfaces: out _) :
                                                                                     false,
                                                    enumeratorInfoOpt.PatternDisposeInfo?.Method.GetPublicSymbol(),
                                                    BoundNode.GetConversion(enumeratorInfoOpt.CurrentConversion, enumeratorInfoOpt.CurrentPlaceholder),
                                                    BoundNode.GetConversion(boundForEachStatement.ElementConversion, boundForEachStatement.ElementPlaceholder),
                                                    getEnumeratorArguments: createArgumentOperations(enumeratorInfoOpt.GetEnumeratorInfo),
                                                    moveNextArguments: createArgumentOperations(enumeratorInfoOpt.MoveNextInfo),
                                                    disposeArguments: enumeratorInfoOpt.PatternDisposeInfo is object
                                                        ? CreateDisposeArguments(enumeratorInfoOpt.PatternDisposeInfo)
                                                        : default);
            }
            else
            {
                info = null;
            }
 
            return info;
 
            ImmutableArray<IArgumentOperation> createArgumentOperations(MethodArgumentInfo? info)
            {
                if (info == null)
                {
                    return default;
                }
                if (info.Arguments.Length == 0)
                {
                    return ImmutableArray<IArgumentOperation>.Empty;
                }
                var args = DeriveArguments(
                    info.Method,
                    info.Arguments,
                    argumentsToParametersOpt: default,
                    info.DefaultArguments,
                    invokedAsExtensionMethod: info.Method.IsExtensionMethod
                );
                return Operation.SetParentOperation(args, null);
            }
        }
 
        internal IOperation CreateBoundForEachStatementLoopControlVariable(BoundForEachStatement boundForEachStatement)
        {
            if (boundForEachStatement.DeconstructionOpt != null)
            {
                return Create(boundForEachStatement.DeconstructionOpt.DeconstructionAssignment.Left);
            }
            else if (boundForEachStatement.IterationErrorExpressionOpt != null)
            {
                return Create(boundForEachStatement.IterationErrorExpressionOpt);
            }
            else
            {
                Debug.Assert(boundForEachStatement.IterationVariables.Length == 1);
                var local = boundForEachStatement.IterationVariables[0];
                // We use iteration variable type syntax as the underlying syntax node as there is no variable declarator syntax in the syntax tree.
                var declaratorSyntax = boundForEachStatement.IterationVariableType.Syntax;
                return new VariableDeclaratorOperation(local.GetPublicSymbol(), initializer: null, ignoredArguments: ImmutableArray<IOperation>.Empty, semanticModel: _semanticModel, syntax: declaratorSyntax, isImplicit: false);
            }
        }
 
        private IForEachLoopOperation CreateBoundForEachStatementOperation(BoundForEachStatement boundForEachStatement)
        {
            IOperation loopControlVariable = CreateBoundForEachStatementLoopControlVariable(boundForEachStatement);
            // Strip identity conversion added by compiler on top of inline array. Conversion produces an rvalue, but we need the IOperation tree to preserve lvalue-ness of the original collection. 
            IOperation collection = Create(boundForEachStatement.EnumeratorInfoOpt?.InlineArraySpanType is null or WellKnownType.Unknown ||
                                           boundForEachStatement.Expression is not BoundConversion { Conversion.IsIdentity: true, ExplicitCastInCode: false, Operand: BoundExpression operand } ?
                                               boundForEachStatement.Expression :
                                               operand);
            var nextVariables = ImmutableArray<IOperation>.Empty;
            IOperation body = Create(boundForEachStatement.Body);
            ForEachLoopOperationInfo? info = GetForEachLoopOperatorInfo(boundForEachStatement);
 
            ImmutableArray<ILocalSymbol> locals = boundForEachStatement.IterationVariables.GetPublicSymbols();
 
            ILabelSymbol continueLabel = boundForEachStatement.ContinueLabel.GetPublicSymbol();
            ILabelSymbol exitLabel = boundForEachStatement.BreakLabel.GetPublicSymbol();
            SyntaxNode syntax = boundForEachStatement.Syntax;
            bool isImplicit = boundForEachStatement.WasCompilerGenerated;
            bool isAsynchronous = boundForEachStatement.AwaitOpt != null;
            return new ForEachLoopOperation(loopControlVariable, collection, nextVariables, info, isAsynchronous, body, locals, continueLabel, exitLabel, _semanticModel, syntax, isImplicit);
        }
 
        private ITryOperation CreateBoundTryStatementOperation(BoundTryStatement boundTryStatement)
        {
            var body = (IBlockOperation)Create(boundTryStatement.TryBlock);
            ImmutableArray<ICatchClauseOperation> catches = CreateFromArray<BoundCatchBlock, ICatchClauseOperation>(boundTryStatement.CatchBlocks);
            var @finally = (IBlockOperation?)Create(boundTryStatement.FinallyBlockOpt);
            SyntaxNode syntax = boundTryStatement.Syntax;
            bool isImplicit = boundTryStatement.WasCompilerGenerated;
            return new TryOperation(body, catches, @finally, exitLabel: null, _semanticModel, syntax, isImplicit);
        }
 
        private ICatchClauseOperation CreateBoundCatchBlockOperation(BoundCatchBlock boundCatchBlock)
        {
            IOperation? exceptionDeclarationOrExpression = CreateVariableDeclarator((BoundLocal?)boundCatchBlock.ExceptionSourceOpt);
            // The exception filter prologue is introduced during lowering, so should be null here.
            Debug.Assert(boundCatchBlock.ExceptionFilterPrologueOpt is null);
            IOperation? filter = Create(boundCatchBlock.ExceptionFilterOpt);
            IBlockOperation handler = (IBlockOperation)Create(boundCatchBlock.Body);
            ITypeSymbol exceptionType = boundCatchBlock.ExceptionTypeOpt.GetPublicSymbol() ?? _semanticModel.Compilation.ObjectType;
            ImmutableArray<ILocalSymbol> locals = boundCatchBlock.Locals.GetPublicSymbols();
            SyntaxNode syntax = boundCatchBlock.Syntax;
            bool isImplicit = boundCatchBlock.WasCompilerGenerated;
            return new CatchClauseOperation(exceptionDeclarationOrExpression, exceptionType, locals, filter, handler, _semanticModel, syntax, isImplicit);
        }
 
        private IFixedOperation CreateBoundFixedStatementOperation(BoundFixedStatement boundFixedStatement)
        {
            IVariableDeclarationGroupOperation variables = (IVariableDeclarationGroupOperation)Create(boundFixedStatement.Declarations);
            IOperation body = Create(boundFixedStatement.Body);
            ImmutableArray<ILocalSymbol> locals = boundFixedStatement.Locals.GetPublicSymbols();
            SyntaxNode syntax = boundFixedStatement.Syntax;
            bool isImplicit = boundFixedStatement.WasCompilerGenerated;
            return new FixedOperation(locals, variables, body, _semanticModel, syntax, isImplicit);
        }
 
        private IUsingOperation CreateBoundUsingStatementOperation(BoundUsingStatement boundUsingStatement)
        {
            Debug.Assert((boundUsingStatement.DeclarationsOpt == null) != (boundUsingStatement.ExpressionOpt == null));
            Debug.Assert(boundUsingStatement.ExpressionOpt is object || boundUsingStatement.Locals.Length > 0);
            IOperation resources = Create(boundUsingStatement.DeclarationsOpt ?? (BoundNode)boundUsingStatement.ExpressionOpt!);
            IOperation body = Create(boundUsingStatement.Body);
            ImmutableArray<ILocalSymbol> locals = boundUsingStatement.Locals.GetPublicSymbols();
            bool isAsynchronous = boundUsingStatement.AwaitOpt != null;
            DisposeOperationInfo disposeOperationInfo = boundUsingStatement.PatternDisposeInfoOpt is object
                                                         ? new DisposeOperationInfo(
                                                                 disposeMethod: boundUsingStatement.PatternDisposeInfoOpt.Method.GetPublicSymbol(),
                                                                 disposeArguments: CreateDisposeArguments(boundUsingStatement.PatternDisposeInfoOpt))
                                                         : default;
            SyntaxNode syntax = boundUsingStatement.Syntax;
            bool isImplicit = boundUsingStatement.WasCompilerGenerated;
            return new UsingOperation(resources, body, locals, isAsynchronous, disposeOperationInfo, _semanticModel, syntax, isImplicit);
        }
 
        private IThrowOperation CreateBoundThrowStatementOperation(BoundThrowStatement boundThrowStatement)
        {
            IOperation? thrownObject = Create(boundThrowStatement.ExpressionOpt);
            SyntaxNode syntax = boundThrowStatement.Syntax;
            ITypeSymbol? statementType = null;
            bool isImplicit = boundThrowStatement.WasCompilerGenerated;
            return new ThrowOperation(thrownObject, _semanticModel, syntax, statementType, isImplicit);
        }
 
        private IReturnOperation CreateBoundReturnStatementOperation(BoundReturnStatement boundReturnStatement)
        {
            IOperation? returnedValue = Create(boundReturnStatement.ExpressionOpt);
            SyntaxNode syntax = boundReturnStatement.Syntax;
            bool isImplicit = boundReturnStatement.WasCompilerGenerated;
            return new ReturnOperation(returnedValue, OperationKind.Return, _semanticModel, syntax, isImplicit);
        }
 
        private IReturnOperation CreateBoundYieldReturnStatementOperation(BoundYieldReturnStatement boundYieldReturnStatement)
        {
            IOperation returnedValue = Create(boundYieldReturnStatement.Expression);
            SyntaxNode syntax = boundYieldReturnStatement.Syntax;
            bool isImplicit = boundYieldReturnStatement.WasCompilerGenerated;
            return new ReturnOperation(returnedValue, OperationKind.YieldReturn, _semanticModel, syntax, isImplicit);
        }
 
        private ILockOperation CreateBoundLockStatementOperation(BoundLockStatement boundLockStatement)
        {
            // If there is no Enter2 method, then there will be no lock taken reference
            bool legacyMode = _semanticModel.Compilation.CommonGetWellKnownTypeMember(WellKnownMember.System_Threading_Monitor__Enter2) == null;
            ILocalSymbol? lockTakenSymbol =
                legacyMode ? null : new SynthesizedLocal((_semanticModel.GetEnclosingSymbol(boundLockStatement.Syntax.SpanStart) as IMethodSymbol).GetSymbol(),
                                                         TypeWithAnnotations.Create(((CSharpCompilation)_semanticModel.Compilation).GetSpecialType(SpecialType.System_Boolean)),
                                                         SynthesizedLocalKind.LockTaken,
                                                         syntaxOpt: boundLockStatement.Argument.Syntax).GetPublicSymbol();
            IOperation lockedValue = Create(boundLockStatement.Argument);
            IOperation body = Create(boundLockStatement.Body);
            SyntaxNode syntax = boundLockStatement.Syntax;
            bool isImplicit = boundLockStatement.WasCompilerGenerated;
 
            return new LockOperation(lockedValue, body, lockTakenSymbol, _semanticModel, syntax, isImplicit);
        }
 
        private IInvalidOperation CreateBoundBadStatementOperation(BoundBadStatement boundBadStatement)
        {
            SyntaxNode syntax = boundBadStatement.Syntax;
 
            // if child has syntax node point to same syntax node as bad statement, then this invalid statement is implicit
            bool isImplicit = boundBadStatement.WasCompilerGenerated || boundBadStatement.ChildBoundNodes.Any(static (e, boundBadStatement) => e?.Syntax == boundBadStatement.Syntax, boundBadStatement);
            var children = CreateFromArray<BoundNode, IOperation>(boundBadStatement.ChildBoundNodes);
            return new InvalidOperation(children, _semanticModel, syntax, type: null, constantValue: null, isImplicit);
        }
 
        private IOperation CreateBoundLocalDeclarationOperation(BoundLocalDeclaration boundLocalDeclaration)
        {
            var node = boundLocalDeclaration.Syntax;
            var kind = node.Kind();
 
            SyntaxNode varStatement;
            SyntaxNode varDeclaration;
            switch (kind)
            {
                case SyntaxKind.LocalDeclarationStatement:
                    {
                        var statement = (LocalDeclarationStatementSyntax)node;
 
                        // this happen for simple int i = 0;
                        // var statement points to LocalDeclarationStatementSyntax
                        varStatement = statement;
 
                        varDeclaration = statement.Declaration;
                        break;
                    }
                case SyntaxKind.VariableDeclarator:
                    {
                        // this happen for 'for loop' initializer
                        // We generate a DeclarationGroup for this scenario to maintain tree shape consistency across IOperation.
                        // var statement points to VariableDeclarationSyntax
                        Debug.Assert(node.Parent != null);
                        varStatement = node.Parent;
 
                        varDeclaration = node.Parent;
                        break;
                    }
                default:
                    {
                        Debug.Fail($"Unexpected syntax: {kind}");
 
                        // otherwise, they points to whatever bound nodes are pointing to.
                        varStatement = varDeclaration = node;
                        break;
                    }
            }
 
            bool multiVariableImplicit = boundLocalDeclaration.WasCompilerGenerated;
            ImmutableArray<IVariableDeclaratorOperation> declarators = CreateVariableDeclarator(boundLocalDeclaration, varDeclaration);
            ImmutableArray<IOperation> ignoredDimensions = CreateIgnoredDimensions(boundLocalDeclaration);
            IVariableDeclarationOperation multiVariableDeclaration = new VariableDeclarationOperation(declarators, initializer: null, ignoredDimensions, _semanticModel, varDeclaration, multiVariableImplicit);
            // In the case of a for loop, varStatement and varDeclaration will be the same syntax node.
            // We can only have one explicit operation, so make sure this node is implicit in that scenario.
            bool isImplicit = (varStatement == varDeclaration) || boundLocalDeclaration.WasCompilerGenerated;
            return new VariableDeclarationGroupOperation(ImmutableArray.Create(multiVariableDeclaration), _semanticModel, varStatement, isImplicit);
        }
 
        private IOperation CreateBoundMultipleLocalDeclarationsBaseOperation(BoundMultipleLocalDeclarationsBase boundMultipleLocalDeclarations)
        {
            // The syntax for the boundMultipleLocalDeclarations can either be a LocalDeclarationStatement or a VariableDeclaration, depending on the context
            // (using/fixed statements vs variable declaration)
            // We generate a DeclarationGroup for these scenarios (using/fixed) to maintain tree shape consistency across IOperation.
            SyntaxNode declarationGroupSyntax = boundMultipleLocalDeclarations.Syntax;
            SyntaxNode declarationSyntax = declarationGroupSyntax.IsKind(SyntaxKind.LocalDeclarationStatement) ?
                    ((LocalDeclarationStatementSyntax)declarationGroupSyntax).Declaration :
                    declarationGroupSyntax;
 
            bool declarationIsImplicit = boundMultipleLocalDeclarations.WasCompilerGenerated;
            ImmutableArray<IVariableDeclaratorOperation> declarators = CreateVariableDeclarator(boundMultipleLocalDeclarations, declarationSyntax);
            ImmutableArray<IOperation> ignoredDimensions = CreateIgnoredDimensions(boundMultipleLocalDeclarations);
            IVariableDeclarationOperation multiVariableDeclaration = new VariableDeclarationOperation(declarators, initializer: null, ignoredDimensions, _semanticModel, declarationSyntax, declarationIsImplicit);
 
            // If the syntax was the same, we're in a fixed statement or using statement. We make the Group operation implicit in this scenario, as the
            // syntax itself is a VariableDeclaration. We do this for using declarations as well, but since that doesn't have a separate parent bound
            // node, we need to check the current node for that explicitly.
            bool isImplicit = declarationGroupSyntax == declarationSyntax || boundMultipleLocalDeclarations.WasCompilerGenerated || boundMultipleLocalDeclarations is BoundUsingLocalDeclarations;
            var variableDeclaration = new VariableDeclarationGroupOperation(ImmutableArray.Create(multiVariableDeclaration), _semanticModel, declarationGroupSyntax, isImplicit);
 
            if (boundMultipleLocalDeclarations is BoundUsingLocalDeclarations usingDecl)
            {
                return new UsingDeclarationOperation(
                    variableDeclaration,
                    isAsynchronous: usingDecl.AwaitOpt is object,
                    disposeInfo: usingDecl.PatternDisposeInfoOpt is object
                                   ? new DisposeOperationInfo(
                                           disposeMethod: usingDecl.PatternDisposeInfoOpt.Method.GetPublicSymbol(),
                                           disposeArguments: CreateDisposeArguments(usingDecl.PatternDisposeInfoOpt))
                                   : default,
                     _semanticModel,
                    declarationGroupSyntax,
                    isImplicit: boundMultipleLocalDeclarations.WasCompilerGenerated);
            }
 
            return variableDeclaration;
        }
 
        private ILabeledOperation CreateBoundLabelStatementOperation(BoundLabelStatement boundLabelStatement)
        {
            ILabelSymbol label = boundLabelStatement.Label.GetPublicSymbol();
            SyntaxNode syntax = boundLabelStatement.Syntax;
            bool isImplicit = boundLabelStatement.WasCompilerGenerated;
            return new LabeledOperation(label, operation: null, _semanticModel, syntax, isImplicit);
        }
 
        private ILabeledOperation CreateBoundLabeledStatementOperation(BoundLabeledStatement boundLabeledStatement)
        {
            ILabelSymbol label = boundLabeledStatement.Label.GetPublicSymbol();
            IOperation labeledStatement = Create(boundLabeledStatement.Body);
            SyntaxNode syntax = boundLabeledStatement.Syntax;
            bool isImplicit = boundLabeledStatement.WasCompilerGenerated;
            return new LabeledOperation(label, labeledStatement, _semanticModel, syntax, isImplicit);
        }
 
        private IExpressionStatementOperation CreateBoundExpressionStatementOperation(BoundExpressionStatement boundExpressionStatement)
        {
            // lambda body can point to expression directly and binder can insert expression statement there. and end up statement pointing to
            // expression syntax node since there is no statement syntax node to point to. this will mark such one as implicit since it doesn't
            // actually exist in code
            bool isImplicit = boundExpressionStatement.WasCompilerGenerated || boundExpressionStatement.Syntax == boundExpressionStatement.Expression.Syntax;
            SyntaxNode syntax = boundExpressionStatement.Syntax;
 
            // If we're creating the tree for a speculatively-bound constructor initializer, there can be a bound sequence as the child node here
            // that corresponds to the lifetime of any declared variables.
            IOperation expression = Create(boundExpressionStatement.Expression);
            if (boundExpressionStatement.Expression is BoundSequence sequence)
            {
                Debug.Assert(boundExpressionStatement.Syntax == sequence.Value.Syntax);
                isImplicit = true;
            }
 
            return new ExpressionStatementOperation(expression, _semanticModel, syntax, isImplicit);
        }
 
        internal IOperation CreateBoundTupleOperation(BoundTupleExpression boundTupleExpression, bool createDeclaration = true)
        {
            SyntaxNode syntax = boundTupleExpression.Syntax;
            bool isImplicit = boundTupleExpression.WasCompilerGenerated;
            ITypeSymbol? type = boundTupleExpression.GetPublicTypeSymbol();
 
            if (syntax is DeclarationExpressionSyntax declarationExpressionSyntax)
            {
                syntax = declarationExpressionSyntax.Designation;
                if (createDeclaration)
                {
                    var tupleOperation = CreateBoundTupleOperation(boundTupleExpression, createDeclaration: false);
                    return new DeclarationExpressionOperation(tupleOperation, _semanticModel, declarationExpressionSyntax, type, isImplicit: false);
                }
            }
 
            TypeSymbol? naturalType = boundTupleExpression switch
            {
                BoundTupleLiteral { Type: var t } => t,
                BoundConvertedTupleLiteral { SourceTuple: { Type: var t } } => t,
                BoundConvertedTupleLiteral => null,
                { Kind: var kind } => throw ExceptionUtilities.UnexpectedValue(kind)
            };
 
            ImmutableArray<IOperation> elements = CreateFromArray<BoundExpression, IOperation>(boundTupleExpression.Arguments);
            return new TupleOperation(elements, naturalType.GetPublicSymbol(), _semanticModel, syntax, type, isImplicit);
        }
 
        private IInterpolatedStringOperation CreateBoundInterpolatedStringExpressionOperation(BoundInterpolatedString boundInterpolatedString, ImmutableArray<(bool IsLiteral, bool HasAlignment, bool HasFormat)>? positionInfo = null)
        {
            Debug.Assert(positionInfo == null || boundInterpolatedString.InterpolationData is null or { BuilderType: null });
 
            if (positionInfo is null && boundInterpolatedString.InterpolationData is { BuilderType: not null, PositionInfo: var info })
            {
                positionInfo = info[0];
            }
 
            ImmutableArray<IInterpolatedStringContentOperation> parts = CreateBoundInterpolatedStringContentOperation(boundInterpolatedString.Parts, positionInfo);
            SyntaxNode syntax = boundInterpolatedString.Syntax;
            ITypeSymbol? type = boundInterpolatedString.GetPublicTypeSymbol();
            ConstantValue? constantValue = boundInterpolatedString.ConstantValueOpt;
            bool isImplicit = boundInterpolatedString.WasCompilerGenerated;
            return new InterpolatedStringOperation(parts, _semanticModel, syntax, type, constantValue, isImplicit);
        }
 
        internal ImmutableArray<IInterpolatedStringContentOperation> CreateBoundInterpolatedStringContentOperation(ImmutableArray<BoundExpression> parts, ImmutableArray<(bool IsLiteral, bool HasAlignment, bool HasFormat)>? positionInfo)
        {
            return positionInfo is { } info ? createHandlerInterpolatedStringContent(info) : createNonHandlerInterpolatedStringContent();
 
            ImmutableArray<IInterpolatedStringContentOperation> createNonHandlerInterpolatedStringContent()
            {
                var builder = ArrayBuilder<IInterpolatedStringContentOperation>.GetInstance(parts.Length);
                foreach (var part in parts)
                {
                    if (part.Kind == BoundKind.StringInsert)
                    {
                        builder.Add((IInterpolatedStringContentOperation)Create(part));
                    }
                    else
                    {
                        builder.Add(CreateBoundInterpolatedStringTextOperation((BoundLiteral)part));
                    }
                }
 
                return builder.ToImmutableAndFree();
            }
 
            ImmutableArray<IInterpolatedStringContentOperation> createHandlerInterpolatedStringContent(ImmutableArray<(bool IsLiteral, bool HasAlignment, bool HasFormat)> positionInfo)
            {
                // For interpolated string handlers, we want to deconstruct the `AppendLiteral`/`AppendFormatted` calls into
                // their relevant components.
                // https://github.com/dotnet/roslyn/issues/54505 we need to handle interpolated strings used as handler conversions.
 
                Debug.Assert(parts.Length == positionInfo.Length);
                var builder = ArrayBuilder<IInterpolatedStringContentOperation>.GetInstance(parts.Length);
 
                for (int i = 0; i < parts.Length; i++)
                {
                    var part = parts[i];
                    var currentPosition = positionInfo[i];
 
                    BoundExpression value;
                    BoundExpression? alignment;
                    BoundExpression? format;
 
                    switch (part)
                    {
                        case BoundCall call:
                            (value, alignment, format) = getCallInfo(call.Arguments, call.ArgumentNamesOpt, currentPosition);
                            break;
 
                        case BoundDynamicInvocation dynamicInvocation:
                            (value, alignment, format) = getCallInfo(dynamicInvocation.Arguments, dynamicInvocation.ArgumentNamesOpt, currentPosition);
                            break;
 
                        case BoundBadExpression bad:
                            Debug.Assert(bad.ChildBoundNodes.Length ==
                                2 + // initial value + receiver is added to the end
                                (currentPosition.HasAlignment ? 1 : 0) +
                                (currentPosition.HasFormat ? 1 : 0));
 
                            value = bad.ChildBoundNodes[0];
                            if (currentPosition.IsLiteral)
                            {
                                alignment = format = null;
                            }
                            else
                            {
                                alignment = currentPosition.HasAlignment ? bad.ChildBoundNodes[1] : null;
                                format = currentPosition.HasFormat ? bad.ChildBoundNodes[^2] : null;
                            }
                            break;
 
                        default:
                            throw ExceptionUtilities.UnexpectedValue(part.Kind);
                    }
 
                    // We are intentionally not checking the part for implicitness here. The part is a generated AppendLiteral or AppendFormatted call,
                    // and will always be marked as CompilerGenerated. However, our existing behavior for non-builder interpolated strings does not mark
                    // the BoundLiteral or BoundStringInsert components as compiler generated. This generates a non-implicit IInterpolatedStringTextOperation
                    // with an implicit literal underneath, and a non-implicit IInterpolationOperation with non-implicit underlying components.
                    bool isImplicit = false;
                    if (currentPosition.IsLiteral)
                    {
                        Debug.Assert(alignment is null);
                        Debug.Assert(format is null);
                        IOperation valueOperation = value switch
                        {
                            BoundLiteral l => CreateBoundLiteralOperation(l, @implicit: true),
                            BoundConversion { Operand: BoundLiteral } c => CreateBoundConversionOperation(c, forceOperandImplicitLiteral: true),
                            _ => throw ExceptionUtilities.UnexpectedValue(value.Kind),
                        };
 
                        Debug.Assert(valueOperation.IsImplicit);
 
                        builder.Add(new InterpolatedStringTextOperation(valueOperation, _semanticModel, part.Syntax, isImplicit));
                    }
                    else
                    {
                        IOperation valueOperation = Create(value);
                        IOperation? alignmentOperation = Create(alignment);
                        IOperation? formatOperation = Create(format);
 
                        Debug.Assert(valueOperation.Syntax != part.Syntax);
 
                        builder.Add(new InterpolationOperation(valueOperation, alignmentOperation, formatOperation, _semanticModel, part.Syntax, isImplicit));
                    }
                }
 
                return builder.ToImmutableAndFree();
 
                static (BoundExpression Value, BoundExpression? Alignment, BoundExpression? Format) getCallInfo(ImmutableArray<BoundExpression> arguments, ImmutableArray<string?> argumentNamesOpt, (bool IsLiteral, bool HasAlignment, bool HasFormat) currentPosition)
                {
                    BoundExpression value = arguments[0];
 
                    if (currentPosition.IsLiteral || argumentNamesOpt.IsDefault)
                    {
                        // There was no alignment/format component, as binding will qualify those parameters by name
                        return (value, null, null);
                    }
                    else
                    {
                        var alignmentIndex = argumentNamesOpt.IndexOf("alignment");
                        BoundExpression? alignment = alignmentIndex == -1 ? null : arguments[alignmentIndex];
                        var formatIndex = argumentNamesOpt.IndexOf("format");
                        BoundExpression? format = formatIndex == -1 ? null : arguments[formatIndex];
 
                        return (value, alignment, format);
                    }
                }
            }
        }
 
        private IInterpolationOperation CreateBoundInterpolationOperation(BoundStringInsert boundStringInsert)
        {
            IOperation expression = Create(boundStringInsert.Value);
            IOperation? alignment = Create(boundStringInsert.Alignment);
            IOperation? formatString = Create(boundStringInsert.Format);
            SyntaxNode syntax = boundStringInsert.Syntax;
            bool isImplicit = boundStringInsert.WasCompilerGenerated;
            return new InterpolationOperation(expression, alignment, formatString, _semanticModel, syntax, isImplicit);
        }
 
        private IInterpolatedStringTextOperation CreateBoundInterpolatedStringTextOperation(BoundLiteral boundNode)
        {
            IOperation text = CreateBoundLiteralOperation(boundNode, @implicit: true);
            SyntaxNode syntax = boundNode.Syntax;
            bool isImplicit = boundNode.WasCompilerGenerated;
            return new InterpolatedStringTextOperation(text, _semanticModel, syntax, isImplicit);
        }
 
        private IInterpolatedStringHandlerCreationOperation CreateInterpolatedStringHandler(BoundConversion conversion)
        {
            Debug.Assert(conversion.Conversion.IsInterpolatedStringHandler);
 
            InterpolatedStringHandlerData interpolationData = conversion.Operand.GetInterpolatedStringHandlerData();
            var construction = Create(interpolationData.Construction);
            var content = createContent(conversion.Operand);
            var isImplicit = conversion.WasCompilerGenerated || !conversion.ExplicitCastInCode;
            return new InterpolatedStringHandlerCreationOperation(
                construction,
                interpolationData.HasTrailingHandlerValidityParameter,
                interpolationData.UsesBoolReturns,
                content,
                _semanticModel,
                conversion.Syntax,
                conversion.GetPublicTypeSymbol(),
                isImplicit);
 
            IOperation createContent(BoundExpression current)
            {
                switch (current)
                {
                    case BoundBinaryOperator binaryOperator:
                        var left = createContent(binaryOperator.Left);
                        var right = createContent(binaryOperator.Right);
                        return new InterpolatedStringAdditionOperation(left, right, _semanticModel, current.Syntax, current.WasCompilerGenerated);
 
                    case BoundInterpolatedString interpolatedString:
                        var parts = interpolatedString.Parts.SelectAsArray(
                            static IInterpolatedStringContentOperation (part, @this) =>
                            {
                                var methodName = part switch
                                {
                                    BoundCall { Method.Name: var name } => name,
                                    BoundDynamicInvocation { Expression: BoundMethodGroup { Name: var name } } => name,
                                    { HasErrors: true } => "",
                                    _ => throw ExceptionUtilities.UnexpectedValue(part.Kind)
                                };
 
                                var operationKind = methodName switch
                                {
                                    "" => OperationKind.InterpolatedStringAppendInvalid,
                                    BoundInterpolatedString.AppendLiteralMethod => OperationKind.InterpolatedStringAppendLiteral,
                                    BoundInterpolatedString.AppendFormattedMethod => OperationKind.InterpolatedStringAppendFormatted,
                                    _ => throw ExceptionUtilities.UnexpectedValue(methodName)
                                };
 
                                return new InterpolatedStringAppendOperation(@this.Create(part), operationKind, @this._semanticModel, part.Syntax, isImplicit: true);
                            }, this);
 
                        return new InterpolatedStringOperation(
                            parts,
                            _semanticModel,
                            interpolatedString.Syntax,
                            interpolatedString.GetPublicTypeSymbol(),
                            interpolatedString.ConstantValueOpt,
                            isImplicit: interpolatedString.WasCompilerGenerated);
 
                    default:
                        throw ExceptionUtilities.UnexpectedValue(current.Kind);
                }
            }
        }
 
        private IOperation CreateBoundInterpolatedStringArgumentPlaceholder(BoundInterpolatedStringArgumentPlaceholder placeholder)
        {
            SyntaxNode syntax = placeholder.Syntax;
            bool isImplicit = true;
            ITypeSymbol? type = placeholder.GetPublicTypeSymbol();
 
            if (placeholder.ArgumentIndex == BoundInterpolatedStringArgumentPlaceholder.UnspecifiedParameter)
            {
                return new InvalidOperation(ImmutableArray<IOperation>.Empty, _semanticModel, syntax, type, placeholder.ConstantValueOpt, isImplicit);
            }
 
            const int NonArgumentIndex = -1;
 
            var (placeholderKind, argumentIndex) = placeholder.ArgumentIndex switch
            {
                >= 0 and var index => (InterpolatedStringArgumentPlaceholderKind.CallsiteArgument, index),
                BoundInterpolatedStringArgumentPlaceholder.InstanceParameter => (InterpolatedStringArgumentPlaceholderKind.CallsiteReceiver, NonArgumentIndex),
                BoundInterpolatedStringArgumentPlaceholder.TrailingConstructorValidityParameter => (InterpolatedStringArgumentPlaceholderKind.TrailingValidityArgument, NonArgumentIndex),
                _ => throw ExceptionUtilities.UnexpectedValue(placeholder.ArgumentIndex)
            };
 
            return new InterpolatedStringHandlerArgumentPlaceholderOperation(argumentIndex, placeholderKind, _semanticModel, syntax, isImplicit);
        }
 
        private IOperation CreateBoundInterpolatedStringHandlerPlaceholder(BoundInterpolatedStringHandlerPlaceholder placeholder)
        {
            return new InstanceReferenceOperation(
                InstanceReferenceKind.InterpolatedStringHandler,
                _semanticModel,
                placeholder.Syntax,
                placeholder.GetPublicTypeSymbol(),
                isImplicit: placeholder.WasCompilerGenerated);
        }
 
        private IConstantPatternOperation CreateBoundConstantPatternOperation(BoundConstantPattern boundConstantPattern)
        {
            IOperation value = Create(boundConstantPattern.Value);
            SyntaxNode syntax = boundConstantPattern.Syntax;
            bool isImplicit = boundConstantPattern.WasCompilerGenerated;
            TypeSymbol inputType = boundConstantPattern.InputType;
            TypeSymbol narrowedType = boundConstantPattern.NarrowedType;
            return new ConstantPatternOperation(value, inputType.GetPublicSymbol(), narrowedType.GetPublicSymbol(), _semanticModel, syntax, isImplicit);
        }
 
        private IOperation CreateBoundRelationalPatternOperation(BoundRelationalPattern boundRelationalPattern)
        {
            BinaryOperatorKind operatorKind = Helper.DeriveBinaryOperatorKind(boundRelationalPattern.Relation);
            IOperation value = Create(boundRelationalPattern.Value);
            SyntaxNode syntax = boundRelationalPattern.Syntax;
            bool isImplicit = boundRelationalPattern.WasCompilerGenerated;
            TypeSymbol inputType = boundRelationalPattern.InputType;
            TypeSymbol narrowedType = boundRelationalPattern.NarrowedType;
            return new RelationalPatternOperation(operatorKind, value, inputType.GetPublicSymbol(), narrowedType.GetPublicSymbol(), _semanticModel, syntax, isImplicit);
        }
 
        private IDeclarationPatternOperation CreateBoundDeclarationPatternOperation(BoundDeclarationPattern boundDeclarationPattern)
        {
            ISymbol? variable = boundDeclarationPattern.Variable.GetPublicSymbol();
            if (variable == null && boundDeclarationPattern.VariableAccess?.Kind == BoundKind.DiscardExpression)
            {
                variable = ((BoundDiscardExpression)boundDeclarationPattern.VariableAccess).ExpressionSymbol.GetPublicSymbol();
            }
 
            ITypeSymbol inputType = boundDeclarationPattern.InputType.GetPublicSymbol();
            ITypeSymbol narrowedType = boundDeclarationPattern.NarrowedType.GetPublicSymbol();
            bool acceptsNull = boundDeclarationPattern.IsVar;
            ITypeSymbol? matchedType = acceptsNull ? null : boundDeclarationPattern.DeclaredType.GetPublicTypeSymbol();
            SyntaxNode syntax = boundDeclarationPattern.Syntax;
            bool isImplicit = boundDeclarationPattern.WasCompilerGenerated;
            return new DeclarationPatternOperation(matchedType, acceptsNull, variable, inputType, narrowedType, _semanticModel, syntax, isImplicit);
        }
 
        private IRecursivePatternOperation CreateBoundRecursivePatternOperation(BoundRecursivePattern boundRecursivePattern)
        {
            ITypeSymbol matchedType = (boundRecursivePattern.DeclaredType?.Type ?? boundRecursivePattern.InputType.StrippedType()).GetPublicSymbol();
            ImmutableArray<IPatternOperation> deconstructionSubpatterns = boundRecursivePattern.Deconstruction is { IsDefault: false } deconstructions
                ? deconstructions.SelectAsArray((p, fac) => (IPatternOperation)fac.Create(p.Pattern), this)
                : ImmutableArray<IPatternOperation>.Empty;
            ImmutableArray<IPropertySubpatternOperation> propertySubpatterns = boundRecursivePattern.Properties is { IsDefault: false } properties
                ? properties.SelectAsArray((p, arg) => arg.Fac.CreatePropertySubpattern(p, arg.MatchedType), (Fac: this, MatchedType: matchedType))
                : ImmutableArray<IPropertySubpatternOperation>.Empty;
            return new RecursivePatternOperation(
                matchedType,
                boundRecursivePattern.DeconstructMethod.GetPublicSymbol(),
                deconstructionSubpatterns,
                propertySubpatterns,
                boundRecursivePattern.Variable.GetPublicSymbol(),
                boundRecursivePattern.InputType.GetPublicSymbol(),
                boundRecursivePattern.NarrowedType.GetPublicSymbol(),
                _semanticModel,
                boundRecursivePattern.Syntax,
                isImplicit: boundRecursivePattern.WasCompilerGenerated);
        }
 
        private IRecursivePatternOperation CreateBoundRecursivePatternOperation(BoundITuplePattern boundITuplePattern)
        {
            ImmutableArray<IPatternOperation> deconstructionSubpatterns = boundITuplePattern.Subpatterns is { IsDefault: false } subpatterns
                ? subpatterns.SelectAsArray((p, fac) => (IPatternOperation)fac.Create(p.Pattern), this)
                : ImmutableArray<IPatternOperation>.Empty;
 
            return new RecursivePatternOperation(
                boundITuplePattern.InputType.StrippedType().GetPublicSymbol(),
                boundITuplePattern.GetLengthMethod.ContainingType.GetPublicSymbol(),
                deconstructionSubpatterns,
                propertySubpatterns: ImmutableArray<IPropertySubpatternOperation>.Empty,
                declaredSymbol: null,
                boundITuplePattern.InputType.GetPublicSymbol(),
                boundITuplePattern.NarrowedType.GetPublicSymbol(),
                _semanticModel,
                boundITuplePattern.Syntax,
                isImplicit: boundITuplePattern.WasCompilerGenerated);
        }
 
        private IOperation CreateBoundTypePatternOperation(BoundTypePattern boundTypePattern)
        {
            return new TypePatternOperation(
                matchedType: boundTypePattern.NarrowedType.GetPublicSymbol(),
                inputType: boundTypePattern.InputType.GetPublicSymbol(),
                narrowedType: boundTypePattern.NarrowedType.GetPublicSymbol(),
                semanticModel: _semanticModel,
                syntax: boundTypePattern.Syntax,
                isImplicit: boundTypePattern.WasCompilerGenerated);
        }
 
        private IOperation CreateBoundSlicePatternOperation(BoundSlicePattern boundNode)
        {
            return new SlicePatternOperation(
                sliceSymbol: boundNode.Pattern is null ? null :
                    Binder.GetIndexerOrImplicitIndexerSymbol(boundNode.IndexerAccess).GetPublicSymbol(),
                pattern: (IPatternOperation?)Create(boundNode.Pattern),
                inputType: boundNode.InputType.GetPublicSymbol(),
                narrowedType: boundNode.NarrowedType.GetPublicSymbol(),
                _semanticModel,
                boundNode.Syntax,
                isImplicit: boundNode.WasCompilerGenerated);
        }
 
        private IOperation CreateBoundListPatternOperation(BoundListPattern boundNode)
        {
            return new ListPatternOperation(
                lengthSymbol: Binder.GetPropertySymbol(boundNode.LengthAccess, out _, out _).GetPublicSymbol(),
                indexerSymbol: Binder.GetIndexerOrImplicitIndexerSymbol(boundNode.IndexerAccess).GetPublicSymbol(),
                patterns: boundNode.Subpatterns.SelectAsArray((p, fac) => (IPatternOperation)fac.Create(p), this),
                declaredSymbol: boundNode.Variable.GetPublicSymbol(),
                inputType: boundNode.InputType.GetPublicSymbol(),
                narrowedType: boundNode.NarrowedType.GetPublicSymbol(),
                _semanticModel,
                boundNode.Syntax,
                isImplicit: boundNode.WasCompilerGenerated);
        }
 
        private IOperation CreateBoundNegatedPatternOperation(BoundNegatedPattern boundNegatedPattern)
        {
            return new NegatedPatternOperation(
                (IPatternOperation)Create(boundNegatedPattern.Negated),
                boundNegatedPattern.InputType.GetPublicSymbol(),
                boundNegatedPattern.NarrowedType.GetPublicSymbol(),
                _semanticModel,
                boundNegatedPattern.Syntax,
                isImplicit: boundNegatedPattern.WasCompilerGenerated);
        }
 
        private IOperation CreateBoundBinaryPatternOperation(BoundBinaryPattern boundBinaryPattern)
        {
            if (boundBinaryPattern.Left is not BoundBinaryPattern)
            {
                return createOperation(this, boundBinaryPattern, left: (IPatternOperation)Create(boundBinaryPattern.Left));
            }
 
            // Use a manual stack to avoid overflowing on deeply-nested binary patterns
            var stack = ArrayBuilder<BoundBinaryPattern>.GetInstance();
            BoundBinaryPattern? current = boundBinaryPattern;
 
            do
            {
                stack.Push(current);
                current = current.Left as BoundBinaryPattern;
            } while (current != null);
 
            current = stack.Pop();
            var result = (IPatternOperation)Create(current.Left);
            do
            {
                result = createOperation(this, current, result);
            } while (stack.TryPop(out current));
 
            stack.Free();
            return result;
 
            static BinaryPatternOperation createOperation(CSharpOperationFactory @this, BoundBinaryPattern boundBinaryPattern, IPatternOperation left)
            {
                return new BinaryPatternOperation(
                            boundBinaryPattern.Disjunction ? BinaryOperatorKind.Or : BinaryOperatorKind.And,
                            left,
                            (IPatternOperation)@this.Create(boundBinaryPattern.Right),
                            boundBinaryPattern.InputType.GetPublicSymbol(),
                            boundBinaryPattern.NarrowedType.GetPublicSymbol(),
                            @this._semanticModel,
                            boundBinaryPattern.Syntax,
                            isImplicit: boundBinaryPattern.WasCompilerGenerated);
            }
        }
 
        private ISwitchOperation CreateBoundSwitchStatementOperation(BoundSwitchStatement boundSwitchStatement)
        {
            IOperation value = Create(boundSwitchStatement.Expression);
            ImmutableArray<ISwitchCaseOperation> cases = CreateFromArray<BoundSwitchSection, ISwitchCaseOperation>(boundSwitchStatement.SwitchSections);
            ImmutableArray<ILocalSymbol> locals = boundSwitchStatement.InnerLocals.GetPublicSymbols();
            ILabelSymbol exitLabel = boundSwitchStatement.BreakLabel.GetPublicSymbol();
            SyntaxNode syntax = boundSwitchStatement.Syntax;
            bool isImplicit = boundSwitchStatement.WasCompilerGenerated;
            return new SwitchOperation(locals, value, cases, exitLabel, _semanticModel, syntax, isImplicit);
        }
 
        private ISwitchCaseOperation CreateBoundSwitchSectionOperation(BoundSwitchSection boundSwitchSection)
        {
            ImmutableArray<ICaseClauseOperation> clauses = CreateFromArray<BoundSwitchLabel, ICaseClauseOperation>(boundSwitchSection.SwitchLabels);
            ImmutableArray<IOperation> body = CreateFromArray<BoundStatement, IOperation>(boundSwitchSection.Statements);
            ImmutableArray<ILocalSymbol> locals = boundSwitchSection.Locals.GetPublicSymbols();
 
            return new SwitchCaseOperation(clauses, body, locals, condition: null, _semanticModel, boundSwitchSection.Syntax, isImplicit: boundSwitchSection.WasCompilerGenerated);
        }
 
        private ISwitchExpressionOperation CreateBoundSwitchExpressionOperation(BoundConvertedSwitchExpression boundSwitchExpression)
        {
            IOperation value = Create(boundSwitchExpression.Expression);
            ImmutableArray<ISwitchExpressionArmOperation> arms = CreateFromArray<BoundSwitchExpressionArm, ISwitchExpressionArmOperation>(boundSwitchExpression.SwitchArms);
 
            bool isExhaustive;
            if (boundSwitchExpression.DefaultLabel != null)
            {
                Debug.Assert(boundSwitchExpression.DefaultLabel is GeneratedLabelSymbol);
                isExhaustive = false;
            }
            else
            {
                isExhaustive = true;
            }
 
            return new SwitchExpressionOperation(
                value,
                arms,
                isExhaustive,
                _semanticModel,
                boundSwitchExpression.Syntax,
                boundSwitchExpression.GetPublicTypeSymbol(),
                boundSwitchExpression.WasCompilerGenerated);
        }
 
        private ISwitchExpressionArmOperation CreateBoundSwitchExpressionArmOperation(BoundSwitchExpressionArm boundSwitchExpressionArm)
        {
            IPatternOperation pattern = (IPatternOperation)Create(boundSwitchExpressionArm.Pattern);
            IOperation? guard = Create(boundSwitchExpressionArm.WhenClause);
            IOperation value = Create(boundSwitchExpressionArm.Value);
            return new SwitchExpressionArmOperation(
                pattern,
                guard,
                value,
                boundSwitchExpressionArm.Locals.GetPublicSymbols(),
                _semanticModel,
                boundSwitchExpressionArm.Syntax,
                boundSwitchExpressionArm.WasCompilerGenerated);
        }
 
        private ICaseClauseOperation CreateBoundSwitchLabelOperation(BoundSwitchLabel boundSwitchLabel)
        {
            SyntaxNode syntax = boundSwitchLabel.Syntax;
            bool isImplicit = boundSwitchLabel.WasCompilerGenerated;
            LabelSymbol label = boundSwitchLabel.Label;
 
            if (boundSwitchLabel.Syntax.Kind() == SyntaxKind.DefaultSwitchLabel)
            {
                Debug.Assert(boundSwitchLabel.Pattern.Kind == BoundKind.DiscardPattern);
                return new DefaultCaseClauseOperation(label.GetPublicSymbol(), _semanticModel, syntax, isImplicit);
            }
            else if (boundSwitchLabel.WhenClause == null &&
                     boundSwitchLabel.Pattern.Kind == BoundKind.ConstantPattern &&
                     boundSwitchLabel.Pattern is BoundConstantPattern cp &&
                     cp.InputType.IsValidV6SwitchGoverningType())
            {
                return new SingleValueCaseClauseOperation(Create(cp.Value), label.GetPublicSymbol(), _semanticModel, syntax, isImplicit);
            }
            else
            {
                IPatternOperation pattern = (IPatternOperation)Create(boundSwitchLabel.Pattern);
                IOperation? guard = Create(boundSwitchLabel.WhenClause);
                return new PatternCaseClauseOperation(label.GetPublicSymbol(), pattern, guard, _semanticModel, syntax, isImplicit);
            }
        }
 
        private IIsPatternOperation CreateBoundIsPatternExpressionOperation(BoundIsPatternExpression boundIsPatternExpression)
        {
            IOperation value = Create(boundIsPatternExpression.Expression);
            IPatternOperation pattern = (IPatternOperation)Create(boundIsPatternExpression.Pattern);
            SyntaxNode syntax = boundIsPatternExpression.Syntax;
            ITypeSymbol? type = boundIsPatternExpression.GetPublicTypeSymbol();
            bool isImplicit = boundIsPatternExpression.WasCompilerGenerated;
            return new IsPatternOperation(value, pattern, _semanticModel, syntax, type, isImplicit);
        }
 
        private IOperation CreateBoundQueryClauseOperation(BoundQueryClause boundQueryClause)
        {
            if (boundQueryClause.Syntax.Kind() != SyntaxKind.QueryExpression)
            {
                // Currently we have no IOperation APIs for different query clauses or continuation.
                return Create(boundQueryClause.Value);
            }
 
            IOperation operation = Create(boundQueryClause.Value);
            SyntaxNode syntax = boundQueryClause.Syntax;
            ITypeSymbol? type = boundQueryClause.GetPublicTypeSymbol();
            bool isImplicit = boundQueryClause.WasCompilerGenerated;
            return new TranslatedQueryOperation(operation, _semanticModel, syntax, type, isImplicit);
        }
 
        private IOperation CreateBoundRangeVariableOperation(BoundRangeVariable boundRangeVariable)
        {
            // We do not have operation nodes for the bound range variables, just it's value.
            return Create(boundRangeVariable.Value);
        }
 
        private IOperation CreateBoundDiscardExpressionOperation(BoundDiscardExpression boundNode)
        {
            return new DiscardOperation(
                ((DiscardSymbol)boundNode.ExpressionSymbol).GetPublicSymbol(),
                _semanticModel,
                boundNode.Syntax,
                boundNode.GetPublicTypeSymbol(),
                isImplicit: boundNode.WasCompilerGenerated);
        }
 
        private IOperation CreateFromEndIndexExpressionOperation(BoundFromEndIndexExpression boundIndex)
        {
            return new UnaryOperation(
                UnaryOperatorKind.Hat,
                Create(boundIndex.Operand),
                isLifted: boundIndex.Type.IsNullableType(),
                isChecked: false,
                operatorMethod: null,
                constrainedToType: null,
                _semanticModel,
                boundIndex.Syntax,
                boundIndex.GetPublicTypeSymbol(),
                constantValue: null,
                isImplicit: boundIndex.WasCompilerGenerated);
        }
 
        private IOperation CreateRangeExpressionOperation(BoundRangeExpression boundRange)
        {
            IOperation? left = Create(boundRange.LeftOperandOpt);
            IOperation? right = Create(boundRange.RightOperandOpt);
            return new RangeOperation(
                left, right,
                isLifted: boundRange.Type.IsNullableType(),
                boundRange.MethodOpt.GetPublicSymbol(),
                _semanticModel,
                boundRange.Syntax,
                boundRange.GetPublicTypeSymbol(),
                isImplicit: boundRange.WasCompilerGenerated);
        }
 
        private IOperation CreateBoundDiscardPatternOperation(BoundDiscardPattern boundNode)
        {
            return new DiscardPatternOperation(
                inputType: boundNode.InputType.GetPublicSymbol(),
                narrowedType: boundNode.NarrowedType.GetPublicSymbol(),
                _semanticModel,
                boundNode.Syntax,
                isImplicit: boundNode.WasCompilerGenerated);
        }
 
        internal IPropertySubpatternOperation CreatePropertySubpattern(BoundPropertySubpattern subpattern, ITypeSymbol matchedType)
        {
            // We treat `c is { ... .Prop: <pattern> }` as `c is { ...: { Prop: <pattern> } }`
 
            SyntaxNode subpatternSyntax = subpattern.Syntax;
            BoundPropertySubpatternMember? member = subpattern.Member;
            IPatternOperation pattern = (IPatternOperation)Create(subpattern.Pattern);
            if (member is null)
            {
                var reference = OperationFactory.CreateInvalidOperation(_semanticModel, subpatternSyntax, ImmutableArray<IOperation>.Empty, isImplicit: true);
                return new PropertySubpatternOperation(reference, pattern, _semanticModel, subpatternSyntax, isImplicit: false);
            }
 
            // Create an operation for last property access:
            // `{ SingleProp: <pattern operation> }`
            // or
            // `.LastProp: <pattern operation>` portion (treated as `{ LastProp: <pattern operation> }`)
            var nameSyntax = member.Syntax;
            var inputType = getInputType(member, matchedType);
            IPropertySubpatternOperation? result = createPropertySubpattern(member.Symbol, pattern, inputType, nameSyntax, isSingle: member.Receiver is null);
 
            while (member.Receiver is not null)
            {
                member = member.Receiver;
                nameSyntax = member.Syntax;
                ITypeSymbol previousType = inputType;
                inputType = getInputType(member, matchedType);
 
                // Create an operation for a preceding property access:
                // { PrecedingProp: <previous pattern operation> }
                IPatternOperation nestedPattern = new RecursivePatternOperation(
                    matchedType: previousType, deconstructSymbol: null, deconstructionSubpatterns: ImmutableArray<IPatternOperation>.Empty,
                    propertySubpatterns: ImmutableArray.Create(result), declaredSymbol: null,
                    previousType, narrowedType: previousType, semanticModel: _semanticModel, nameSyntax, isImplicit: true);
 
                result = createPropertySubpattern(member.Symbol, nestedPattern, inputType, nameSyntax, isSingle: false);
            }
 
            return result;
 
            IPropertySubpatternOperation createPropertySubpattern(Symbol? symbol, IPatternOperation pattern, ITypeSymbol receiverType, SyntaxNode nameSyntax, bool isSingle)
            {
                Debug.Assert(nameSyntax is not null);
                IOperation reference;
                switch (symbol)
                {
                    case FieldSymbol field:
                        {
                            var constantValue = field.GetConstantValue(ConstantFieldsInProgress.Empty, earlyDecodingWellKnownAttributes: false);
                            reference = new FieldReferenceOperation(field.GetPublicSymbol(), isDeclaration: false,
                                createReceiver(), _semanticModel, nameSyntax, type: field.Type.GetPublicSymbol(), constantValue, isImplicit: false);
                            break;
                        }
                    case PropertySymbol property:
                        {
                            reference = new PropertyReferenceOperation(property.GetPublicSymbol(), constrainedToType: null, ImmutableArray<IArgumentOperation>.Empty,
                                createReceiver(), _semanticModel, nameSyntax, type: property.Type.GetPublicSymbol(), isImplicit: false);
                            break;
                        }
                    default:
                        {
                            // We should expose the symbol in this case somehow:
                            // https://github.com/dotnet/roslyn/issues/33175
                            reference = OperationFactory.CreateInvalidOperation(_semanticModel, nameSyntax, ImmutableArray<IOperation>.Empty, isImplicit: false);
                            break;
                        }
                }
 
                var syntaxForPropertySubpattern = isSingle ? subpatternSyntax : nameSyntax;
                return new PropertySubpatternOperation(reference, pattern, _semanticModel, syntaxForPropertySubpattern, isImplicit: !isSingle);
 
                IOperation? createReceiver()
                    => symbol?.IsStatic == false ? new InstanceReferenceOperation(InstanceReferenceKind.PatternInput, _semanticModel, nameSyntax!, receiverType, isImplicit: true) : null;
            }
 
            static ITypeSymbol getInputType(BoundPropertySubpatternMember member, ITypeSymbol matchedType)
                => member.Receiver?.Type.StrippedType().GetPublicSymbol() ?? matchedType;
        }
 
        private IInstanceReferenceOperation CreateCollectionValuePlaceholderOperation(BoundObjectOrCollectionValuePlaceholder placeholder)
        {
            InstanceReferenceKind referenceKind = InstanceReferenceKind.ImplicitReceiver;
            SyntaxNode syntax = placeholder.Syntax;
            ITypeSymbol? type = placeholder.GetPublicTypeSymbol();
            bool isImplicit = placeholder.WasCompilerGenerated;
            return new InstanceReferenceOperation(referenceKind, _semanticModel, syntax, type, isImplicit);
        }
 
        private ImmutableArray<IArgumentOperation> CreateDisposeArguments(MethodArgumentInfo patternDisposeInfo)
        {
            // can't be an extension method for dispose
            Debug.Assert(!patternDisposeInfo.Method.IsStatic);
 
            if (patternDisposeInfo.Method.ParameterCount == 0)
            {
                return ImmutableArray<IArgumentOperation>.Empty;
            }
 
            var args = DeriveArguments(
                            patternDisposeInfo.Method,
                            patternDisposeInfo.Arguments,
                            argumentsToParametersOpt: default,
                            patternDisposeInfo.DefaultArguments,
                            invokedAsExtensionMethod: false);
 
            return Operation.SetParentOperation(args, null);
        }
    }
}