' 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.

Imports System.Collections.Generic
Imports System.Collections.Immutable
Imports System.Runtime.InteropServices
Imports System.Threading
Imports Microsoft.Cci
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Collections
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.CodeGen
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports TypeKind = Microsoft.CodeAnalysis.TypeKind

Namespace Microsoft.CodeAnalysis.VisualBasic

    Partial Friend NotInheritable Class AsyncRewriter
        Inherits StateMachineRewriter(Of CapturedSymbolOrExpression)

        Partial Friend Class AsyncMethodToClassRewriter
            Inherits StateMachineMethodToClassRewriter

            Public Function VisitExpression(expression As BoundExpression) As BoundExpression
                Return DirectCast(Me.Visit(expression), BoundExpression)
            End Function

            Public Overrides Function VisitSpillSequence(node As BoundSpillSequence) As BoundNode
                Dim statements As ImmutableArray(Of BoundStatement) = Me.VisitList(node.Statements)
                Dim valueOpt As BoundExpression = Me.VisitExpression(node.ValueOpt)
                Dim rewrittenType As TypeSymbol = VisitType(node.Type)

                If valueOpt Is Nothing OrElse valueOpt.Kind <> BoundKind.SpillSequence Then
                    Return node.Update(node.Locals, node.SpillFields, statements, valueOpt, rewrittenType)
                End If

                Dim spillSeq = DirectCast(valueOpt, BoundSpillSequence)
                Debug.Assert(TypeSymbol.Equals(rewrittenType, spillSeq.Type, TypeCompareKind.ConsiderEverything))

                Return node.Update(
                    node.Locals.Concat(spillSeq.Locals),
                    node.SpillFields.Concat(spillSeq.SpillFields),
                    statements.Concat(spillSeq.Statements),
                    spillSeq.ValueOpt,
                    rewrittenType)
            End Function

            Public Overrides Function VisitSequence(node As BoundSequence) As BoundNode
                Dim rewritten = DirectCast(MyBase.VisitSequence(node), BoundSequence)
                Dim locals As ImmutableArray(Of LocalSymbol) = rewritten.Locals
                Dim sideEffects As ImmutableArray(Of BoundExpression) = rewritten.SideEffects
                Dim valueOpt As BoundExpression = rewritten.ValueOpt

                Dim sideEffectsRequireSpill As Boolean = NeedsSpill(sideEffects)
                Dim valueRequiresSpill As Boolean = NeedsSpill(valueOpt)

                If Not sideEffectsRequireSpill AndAlso Not valueRequiresSpill Then
                    Return rewritten
                End If

                Dim builder As New SpillBuilder()

                builder.AddLocals(locals)

                If sideEffectsRequireSpill Then
                    For Each sideEffect In sideEffects
                        builder.AddStatement(MakeExpressionStatement(sideEffect, builder))
                    Next
                End If

                If valueRequiresSpill Then
                    Dim spill = DirectCast(valueOpt, BoundSpillSequence)
                    builder.AddSpill(spill)
                    valueOpt = spill.ValueOpt
                End If

                Return builder.BuildSequenceAndFree(Me.F, valueOpt)
            End Function

            Private Function MakeExpressionStatement(expression As BoundExpression, ByRef builder As SpillBuilder) As BoundStatement
                If NeedsSpill(expression) Then
                    Debug.Assert(expression.Kind = BoundKind.SpillSequence)
                    Dim spill = DirectCast(expression, BoundSpillSequence)
                    builder.AssumeFieldsIfNeeded(spill)
                    Return Me.RewriteSpillSequenceIntoBlock(spill, True)
                Else
                    Return Me.F.ExpressionStatement(expression)
                End If
            End Function

            Public Overrides Function VisitCall(node As BoundCall) As BoundNode
                Dim rewritten = DirectCast(MyBase.VisitCall(node), BoundCall)
                Dim receiverOpt As BoundExpression = rewritten.ReceiverOpt
                Dim arguments As ImmutableArray(Of BoundExpression) = rewritten.Arguments

                Dim builder As SpillBuilder

                If Not NeedsSpill(arguments) Then
                    If Not NeedsSpill(receiverOpt) Then
                        Return rewritten
                    End If

                    builder = New SpillBuilder()
                    Dim spill = DirectCast(receiverOpt, BoundSpillSequence)
                    builder.AddSpill(spill)
                    receiverOpt = spill.ValueOpt
                Else
                    builder = New SpillBuilder()

                    If Not Unspillable(receiverOpt) Then

                        If CodeGenerator.IsPossibleReferenceTypeReceiverOfConstrainedCall(receiverOpt) AndAlso
                           Not CodeGenerator.ReceiverIsKnownToReferToTempIfReferenceType(receiverOpt) AndAlso
                           Not CodeGenerator.IsSafeToDereferenceReceiverRefAfterEvaluatingArguments(node.Arguments) Then

                            Dim receiverType = receiverOpt.Type

                            If receiverType.IsReferenceType Then
                                receiverOpt = SpillRValue(receiverOpt.MakeRValue(), builder:=builder)
                            Else
                                receiverOpt = SpillLValue(receiverOpt, isReceiver:=True, evaluateSideEffects:=True, builder:=builder)
                            End If

                            Dim conditionalReceiver = TryCast(node.ReceiverOpt, BoundConditionalAccessReceiverPlaceholder)

                            If conditionalReceiver IsNot Nothing Then

                                If _conditionalAccessReceiverPlaceholderReplacementInfo Is Nothing OrElse
                                   _conditionalAccessReceiverPlaceholderReplacementInfo.PlaceholderId <> conditionalReceiver.PlaceholderId Then
                                    Throw ExceptionUtilities.Unreachable
                                End If

                                Debug.Assert(_conditionalAccessReceiverPlaceholderReplacementInfo.IsSpilled)
                                Debug.Assert(Not _conditionalAccessReceiverPlaceholderReplacementInfo.ForceCaptureIfReferenceType)
                                _conditionalAccessReceiverPlaceholderReplacementInfo.ForceCaptureIfReferenceType = True

                            ElseIf Not receiverType.IsReferenceType Then
                                ' A case where T is actually a class must be handled specially.
                                ' Taking a reference to a class instance is fragile because the value behind the 
                                ' reference might change while arguments are evaluated. However, the call should be
                                ' performed on the instance that is behind reference at the time we push the
                                ' reference to the stack. So, for a class we need to emit a reference to a temporary
                                ' location, rather than to the original location

                                Dim referenceReceiverBuilder As New SpillBuilder()
                                Dim spilledReferenceReceiver As BoundExpression = SpillRValue(receiverOpt.MakeRValue(), referenceReceiverBuilder)
                                spilledReferenceReceiver = referenceReceiverBuilder.BuildSequenceAndFree(Me.F, spilledReferenceReceiver)
                                Dim referenceReceiverSpillSequence = TryCast(spilledReferenceReceiver, BoundSpillSequence)

                                If referenceReceiverSpillSequence IsNot Nothing Then
                                    ' If condition `(object)default(T) != null` is true at execution time,
                                    ' the T is a value type. And it is a reference type otherwise.
                                    Dim isValueTypeCheck = Me.F.ReferenceIsNotNothing(Me.F.DirectCast(Me.F.DirectCast(Me.F.Null(), receiverType),
                                                                                      Me.F.SpecialType(SpecialType.System_Object)))

                                    builder.AssumeFieldsIfNeeded(referenceReceiverSpillSequence)
                                    builder.AddLocals(referenceReceiverSpillSequence.Locals)
                                    builder.AddStatement(Me.F.If(Me.F.Not(isValueTypeCheck), Me.F.StatementList(referenceReceiverSpillSequence.Statements)))

                                    spilledReferenceReceiver = referenceReceiverSpillSequence.ValueOpt
                                End If

                                receiverOpt = New BoundComplexConditionalAccessReceiver(receiverOpt.Syntax, receiverOpt, spilledReferenceReceiver, receiverType)
                            End If

                        Else
                            receiverOpt = SpillLValue(receiverOpt, isReceiver:=True, evaluateSideEffects:=True, builder:=builder)
                        End If
                    End If

                    arguments = SpillExpressionList(builder, arguments)
                End If

                Return builder.BuildSequenceAndFree(Me.F,
                                                    rewritten.Update(rewritten.Method,
                                                                     rewritten.MethodGroupOpt,
                                                                     receiverOpt,
                                                                     arguments,
                                                                     rewritten.DefaultArguments,
                                                                     rewritten.ConstantValueOpt,
                                                                     isLValue:=rewritten.IsLValue,
                                                                     suppressObjectClone:=rewritten.SuppressObjectClone,
                                                                     type:=rewritten.Type))
            End Function

            Public Overrides Function VisitObjectCreationExpression(node As BoundObjectCreationExpression) As BoundNode
                Dim rewritten = DirectCast(MyBase.VisitObjectCreationExpression(node), BoundObjectCreationExpression)
                Dim arguments As ImmutableArray(Of BoundExpression) = rewritten.Arguments
                Debug.Assert(rewritten.InitializerOpt Is Nothing)

                If Not NeedsSpill(arguments) Then
                    Return rewritten
                End If

                Dim builder As New SpillBuilder()
                arguments = SpillExpressionList(builder, arguments)

                Return builder.BuildSequenceAndFree(Me.F,
                                                    rewritten.Update(rewritten.ConstructorOpt,
                                                                     arguments,
                                                                     rewritten.DefaultArguments,
                                                                     rewritten.InitializerOpt,
                                                                     rewritten.Type))
            End Function

            Public Overrides Function VisitDelegateCreationExpression(node As BoundDelegateCreationExpression) As BoundNode
                Dim rewritten = DirectCast(MyBase.VisitDelegateCreationExpression(node), BoundDelegateCreationExpression)
                Dim receiverOpt As BoundExpression = rewritten.ReceiverOpt
                Debug.Assert(rewritten.RelaxationLambdaOpt Is Nothing)
                Debug.Assert(rewritten.RelaxationReceiverPlaceholderOpt Is Nothing)

                If Not NeedsSpill(receiverOpt) Then
                    Return rewritten
                End If

                Debug.Assert(receiverOpt.Kind = BoundKind.SpillSequence)
                Dim spill = DirectCast(receiverOpt, BoundSpillSequence)
                Return SpillSequenceWithNewValue(spill,
                                                 rewritten.Update(spill.ValueOpt,
                                                                  rewritten.Method,
                                                                  rewritten.RelaxationLambdaOpt,
                                                                  rewritten.RelaxationReceiverPlaceholderOpt,
                                                                  rewritten.MethodGroupOpt,
                                                                  rewritten.Type))
            End Function

            Public Overrides Function VisitBinaryOperator(node As BoundBinaryOperator) As BoundNode
                Dim rewritten = DirectCast(MyBase.VisitBinaryOperator(node), BoundBinaryOperator)
                Dim left As BoundExpression = rewritten.Left
                Dim right As BoundExpression = rewritten.Right

                If Not NeedsSpill(left) AndAlso Not NeedsSpill(right) Then
                    Return rewritten
                End If

                Dim builder As New SpillBuilder()
                Dim operatorKind As BinaryOperatorKind = rewritten.OperatorKind And BinaryOperatorKind.OpMask
                Debug.Assert(operatorKind = (rewritten.OperatorKind And Not (BinaryOperatorKind.IsOperandOfConditionalBranch Or BinaryOperatorKind.OptimizableForConditionalBranch)))
                If operatorKind = BinaryOperatorKind.AndAlso OrElse operatorKind = BinaryOperatorKind.OrElse Then
                    ' NOTE: Short circuit operators need to evaluate the right optionally
                    Dim spilledLeft = SpillValue(left, builder)

                    Dim tempLocal As LocalSymbol = Me.F.SynthesizedLocal(rewritten.Type)
                    builder.AddLocal(tempLocal)

                    builder.AddStatement(
                        If(operatorKind = BinaryOperatorKind.AndAlso,
                           Me.F.If(condition:=spilledLeft,
                                   thenClause:=MakeAssignmentStatement(right, tempLocal, builder),
                                   elseClause:=MakeAssignmentStatement(Me.F.Literal(False), tempLocal)),
                           Me.F.If(condition:=spilledLeft,
                                   thenClause:=MakeAssignmentStatement(Me.F.Literal(True), tempLocal),
                                   elseClause:=MakeAssignmentStatement(right, tempLocal, builder))))

                    Return builder.BuildSequenceAndFree(Me.F,
                                                        Me.F.Local(tempLocal, False))
                Else
                    ' Regular binary operator
                    Dim newArgs As ImmutableArray(Of BoundExpression) = SpillExpressionList(builder, left, right)
                    Return builder.BuildSequenceAndFree(Me.F,
                                                        rewritten.Update(rewritten.OperatorKind,
                                                                         newArgs(0),
                                                                         newArgs(1),
                                                                         rewritten.Checked,
                                                                         rewritten.ConstantValueOpt,
                                                                         rewritten.Type))
                End If
            End Function

            Public Overrides Function VisitAssignmentOperator(node As BoundAssignmentOperator) As BoundNode
                Return ProcessRewrittenAssignmentOperator(DirectCast(MyBase.VisitAssignmentOperator(node), BoundAssignmentOperator))
            End Function

            Friend Function ProcessRewrittenAssignmentOperator(rewritten As BoundAssignmentOperator) As BoundExpression
                Debug.Assert(rewritten.LeftOnTheRightOpt Is Nothing)

                Dim left As BoundExpression = rewritten.Left
                Dim right As BoundExpression = rewritten.Right

                Dim leftRequiresSpill As Boolean = NeedsSpill(left)
                Dim rightRequiresSpill As Boolean = NeedsSpill(right)

                If Not leftRequiresSpill AndAlso Not rightRequiresSpill Then
                    Return rewritten
                End If

                If Not rightRequiresSpill Then
                    ' only lvalue contains await
                    '     Spill(l_sideEffects, lvalue) = rvalue
                    ' is rewritten as:
                    '     Spill(l_sideEffects, lvalue = rvalue)
                    Debug.Assert(left.Kind = BoundKind.SpillSequence)
                    Dim spillSequence = DirectCast(left, BoundSpillSequence)
                    Return SpillSequenceWithNewValue(spillSequence,
                                                     rewritten.Update(spillSequence.ValueOpt,
                                                                      rewritten.LeftOnTheRightOpt,
                                                                      right,
                                                                      rewritten.SuppressObjectClone,
                                                                      rewritten.Type))

                End If

                Dim builder As New SpillBuilder()

                Debug.Assert(left.IsLValue)
                Dim spilledLeft As BoundExpression = SpillLValue(left, isReceiver:=False, evaluateSideEffects:=True, builder:=builder, isAssignmentTarget:=True)

                Dim rightAsSpillSequence = DirectCast(right, BoundSpillSequence)
                builder.AddSpill(rightAsSpillSequence)
                Return builder.BuildSequenceAndFree(Me.F,
                                                    rewritten.Update(spilledLeft,
                                                                     rewritten.LeftOnTheRightOpt,
                                                                     rightAsSpillSequence.ValueOpt,
                                                                     rewritten.SuppressObjectClone,
                                                                     rewritten.Type))
            End Function

            Public Overrides Function VisitReferenceAssignment(node As BoundReferenceAssignment) As BoundNode
                Dim origByRefLocal As BoundLocal = node.ByRefLocal
                Dim local As LocalSymbol = origByRefLocal.LocalSymbol
                Dim rewrittenType As TypeSymbol = VisitType(node.Type)

                If Not Me.Proxies.ContainsKey(local) Then
                    ' ByRef local was not captured
                    Dim rewrittenLeft As BoundLocal = DirectCast(Me.VisitExpression(origByRefLocal), BoundLocal)

                    Dim rewrittenRight As BoundExpression = Me.VisitExpression(node.LValue)
                    Debug.Assert(rewrittenRight.IsLValue)
                    Dim rightRequiresSpill As Boolean = NeedsSpill(rewrittenRight)

                    If Not rightRequiresSpill Then
                        Return node.Update(rewrittenLeft, rewrittenRight, node.IsLValue, rewrittenType)

                    Else
                        ' The right is spilled, but the left is still a ByRef local
                        ' Rewrite 
                        '       ReferenceAssignment(<by-ref-local> = Spill( ..., <l-value> ) )
                        ' Into 
                        '       Spill( ..., ReferenceAssignment( <by-ref-local>, <l-value> ) )

                        Debug.Assert(rewrittenRight.Kind = BoundKind.SpillSequence)
                        Dim rightSpill = DirectCast(rewrittenRight, BoundSpillSequence)

                        Return SpillSequenceWithNewValue(rightSpill,
                                                         node.Update(rewrittenLeft,
                                                                     rightSpill.ValueOpt,
                                                                     node.IsLValue,
                                                                     rewrittenType))
                    End If
                End If

                ' Here we have an assignment expression that is initializing a ref 
                ' local variable, and the ref local variable is to be lifted.
                Dim capturedLocal As CapturedSymbolOrExpression = Me.Proxies(local)

                ' This builder will collect initializers for the captured local, 
                ' these initializers are supposed to make sure all the parts of 
                ' the capture are properly assigned
                Dim initializersBuilder = ArrayBuilder(Of BoundExpression).GetInstance()
                capturedLocal.CreateCaptureInitializationCode(Me, initializersBuilder)

                Dim materializedCapture As BoundExpression = capturedLocal.Materialize(Me, node.IsLValue)

                ' Now... we actually do not need to rewrite or process 'right' in any way
                ' because we must have captured its essential parts in the local's capture.
                ' So we just create a sequence with these parts 

                If initializersBuilder.Count = 0 Then
                    initializersBuilder.Free()
                    Return materializedCapture
                End If

                initializersBuilder.Add(materializedCapture)
                Return Me.F.Sequence(initializersBuilder.ToArrayAndFree)
            End Function

            Public Overrides Function VisitFieldAccess(node As BoundFieldAccess) As BoundNode
                Dim rewritten = DirectCast(MyBase.VisitFieldAccess(node), BoundFieldAccess)
                Dim receiverOpt As BoundExpression = rewritten.ReceiverOpt

                If Not NeedsSpill(receiverOpt) Then
                    Return rewritten
                End If

                Debug.Assert(receiverOpt.Kind = BoundKind.SpillSequence)
                Dim spillSequence = DirectCast(receiverOpt, BoundSpillSequence)

                Return SpillSequenceWithNewValue(spillSequence,
                                                 rewritten.Update(spillSequence.ValueOpt,
                                                                  rewritten.FieldSymbol,
                                                                  rewritten.IsLValue,
                                                                  rewritten.SuppressVirtualCalls,
                                                                  constantsInProgressOpt:=Nothing,
                                                                  rewritten.Type))
            End Function

            Public Overrides Function VisitDirectCast(node As BoundDirectCast) As BoundNode
                Dim rewritten = DirectCast(MyBase.VisitDirectCast(node), BoundDirectCast)
                Dim operand As BoundExpression = rewritten.Operand
                Debug.Assert(rewritten.RelaxationLambdaOpt Is Nothing)

                If Not NeedsSpill(operand) Then
                    Return rewritten
                End If

                Debug.Assert(operand.Kind = BoundKind.SpillSequence)
                Dim spillSequence = DirectCast(operand, BoundSpillSequence)

                Return SpillSequenceWithNewValue(spillSequence,
                                                 rewritten.Update(spillSequence.ValueOpt,
                                                                  rewritten.ConversionKind,
                                                                  rewritten.SuppressVirtualCalls,
                                                                  rewritten.ConstantValueOpt,
                                                                  rewritten.RelaxationLambdaOpt,
                                                                  rewritten.Type))
            End Function

            Public Overrides Function VisitTryCast(node As BoundTryCast) As BoundNode
                Dim rewritten = DirectCast(MyBase.VisitTryCast(node), BoundTryCast)
                Dim operand As BoundExpression = rewritten.Operand
                Debug.Assert(rewritten.RelaxationLambdaOpt Is Nothing)

                If Not NeedsSpill(operand) Then
                    Return rewritten
                End If

                Debug.Assert(operand.Kind = BoundKind.SpillSequence)
                Dim spillSequence = DirectCast(operand, BoundSpillSequence)

                Return SpillSequenceWithNewValue(spillSequence,
                                                 rewritten.Update(spillSequence.ValueOpt,
                                                                  rewritten.ConversionKind,
                                                                  rewritten.ConstantValueOpt,
                                                                  rewritten.RelaxationLambdaOpt,
                                                                  rewritten.Type))
            End Function

            Public Overrides Function VisitConversion(node As BoundConversion) As BoundNode
                Dim rewritten = DirectCast(MyBase.VisitConversion(node), BoundConversion)
                Dim operand As BoundExpression = rewritten.Operand
                Debug.Assert(rewritten.ExtendedInfoOpt Is Nothing)

                If Not NeedsSpill(operand) Then
                    Return rewritten
                End If

                Debug.Assert(operand.Kind = BoundKind.SpillSequence)
                Dim spillSequence = DirectCast(operand, BoundSpillSequence)

                Return SpillSequenceWithNewValue(spillSequence,
                                                 rewritten.Update(spillSequence.ValueOpt,
                                                                  rewritten.ConversionKind,
                                                                  rewritten.Checked,
                                                                  rewritten.ExplicitCastInCode,
                                                                  rewritten.ConstantValueOpt,
                                                                  rewritten.ExtendedInfoOpt,
                                                                  rewritten.Type))
            End Function

            Public Overrides Function VisitLValueToRValueWrapper(node As BoundLValueToRValueWrapper) As BoundNode
                Dim rewritten = DirectCast(MyBase.VisitLValueToRValueWrapper(node), BoundLValueToRValueWrapper)
                Debug.Assert(Not rewritten.IsLValue)

                Dim operand As BoundExpression = rewritten.UnderlyingLValue
                Debug.Assert(operand.IsLValue)

                If Not NeedsSpill(operand) Then
                    Return rewritten
                End If

                Debug.Assert(operand.Kind = BoundKind.SpillSequence)
                Dim spillSequence = DirectCast(operand, BoundSpillSequence)

                Return SpillSequenceWithNewValue(spillSequence,
                                                 rewritten.Update(spillSequence.ValueOpt,
                                                                  rewritten.Type))
            End Function

            Public Overrides Function VisitTernaryConditionalExpression(node As BoundTernaryConditionalExpression) As BoundNode
                Dim rewritten = DirectCast(MyBase.VisitTernaryConditionalExpression(node), BoundTernaryConditionalExpression)
                Dim condition As BoundExpression = rewritten.Condition
                Dim whenTrue As BoundExpression = rewritten.WhenTrue
                Dim whenFalse As BoundExpression = rewritten.WhenFalse

                Dim conditionRequiresSpill As Boolean = NeedsSpill(condition)

                If Not conditionRequiresSpill AndAlso Not NeedsSpill(whenTrue) AndAlso Not NeedsSpill(whenFalse) Then
                    Return rewritten
                End If

                Dim builder As New SpillBuilder()

                If conditionRequiresSpill Then
                    Debug.Assert(condition.Kind = BoundKind.SpillSequence)
                    condition = SpillRValue(condition, builder)
                End If

                Dim sequenceValueOpt As BoundExpression

                If Not rewritten.Type.IsVoidType() Then
                    Dim tempLocal As LocalSymbol = Me.F.SynthesizedLocal(rewritten.Type)

                    builder.AddLocal(tempLocal)

                    builder.AddStatement(
                        Me.F.If(
                            condition:=condition,
                            thenClause:=MakeAssignmentStatement(whenTrue, tempLocal, builder),
                            elseClause:=MakeAssignmentStatement(whenFalse, tempLocal, builder)))

                    sequenceValueOpt = Me.F.Local(tempLocal, False)
                Else
                    builder.AddStatement(
                        Me.F.If(
                            condition:=condition,
                            thenClause:=MakeExpressionStatement(whenTrue, builder),
                            elseClause:=MakeExpressionStatement(whenFalse, builder)))

                    sequenceValueOpt = Nothing
                End If

                Return builder.BuildSequenceAndFree(Me.F, sequenceValueOpt)
            End Function

            Private Function MakeAssignmentStatement(expression As BoundExpression, temp As LocalSymbol, <[In], Out> ByRef builder As SpillBuilder) As BoundStatement
                If NeedsSpill(expression) Then
                    Debug.Assert(expression.Kind = BoundKind.SpillSequence)
                    Dim spill = DirectCast(expression, BoundSpillSequence)
                    builder.AssumeFieldsIfNeeded(spill)
                    Return RewriteSpillSequenceIntoBlock(spill, False, Me.F.Assignment(Me.F.Local(temp, True), spill.ValueOpt))
                Else
                    Return Me.F.Assignment(Me.F.Local(temp, True), expression)
                End If
            End Function

            Private Function MakeAssignmentStatement(expression As BoundExpression, temp As LocalSymbol) As BoundStatement
                Debug.Assert(Not NeedsSpill(expression))
                Return Me.F.Assignment(Me.F.Local(temp, True), expression)
            End Function

            Private Class ConditionalAccessReceiverPlaceholderReplacementInfo
                Public ReadOnly PlaceholderId As Integer
                Public IsSpilled As Boolean
                Public ForceCaptureIfReferenceType As Boolean

                Public Sub New(placeholderId As Integer)
                    Me.PlaceholderId = placeholderId
                    Me.IsSpilled = False
                    Me.ForceCaptureIfReferenceType = False
                End Sub

            End Class

            Private _conditionalAccessReceiverPlaceholderReplacementInfo As ConditionalAccessReceiverPlaceholderReplacementInfo = Nothing

            Public Overrides Function VisitLoweredConditionalAccess(node As BoundLoweredConditionalAccess) As BoundNode
                Dim type As TypeSymbol = Me.VisitType(node.Type)

                Dim receiver As BoundExpression = DirectCast(Me.Visit(node.Receiver), BoundExpression)
                Dim receiverNeedsSpill = NeedsSpill(receiver)

                Dim saveConditionalAccessReceiverPlaceholderReplacementInfo = _conditionalAccessReceiverPlaceholderReplacementInfo
                Dim conditionalAccessReceiverPlaceholderReplacementInfo As New ConditionalAccessReceiverPlaceholderReplacementInfo(node.PlaceholderId)
                _conditionalAccessReceiverPlaceholderReplacementInfo = conditionalAccessReceiverPlaceholderReplacementInfo

                Dim whenNotNull As BoundExpression = DirectCast(Me.Visit(node.WhenNotNull), BoundExpression)
                Dim whenNotNullNeedsSpill = NeedsSpill(whenNotNull)

                Debug.Assert(conditionalAccessReceiverPlaceholderReplacementInfo Is Nothing OrElse
                             (Not conditionalAccessReceiverPlaceholderReplacementInfo.IsSpilled OrElse whenNotNullNeedsSpill))

                _conditionalAccessReceiverPlaceholderReplacementInfo = Nothing

                Dim whenNullOpt As BoundExpression = DirectCast(Me.Visit(node.WhenNullOpt), BoundExpression)
                Dim whenNullNeedsSpill = If(whenNullOpt IsNot Nothing, NeedsSpill(whenNullOpt), False)

                _conditionalAccessReceiverPlaceholderReplacementInfo = saveConditionalAccessReceiverPlaceholderReplacementInfo

                If Not receiverNeedsSpill AndAlso Not whenNotNullNeedsSpill AndAlso Not whenNullNeedsSpill Then
                    Return node.Update(receiver,
                                       node.CaptureReceiver,
                                       node.PlaceholderId,
                                       whenNotNull,
                                       whenNullOpt,
                                       type)
                End If

                If Not whenNotNullNeedsSpill AndAlso Not whenNullNeedsSpill Then
                    Debug.Assert(receiverNeedsSpill)
                    Dim spill = DirectCast(receiver, BoundSpillSequence)
                    Return SpillSequenceWithNewValue(spill, node.Update(spill.ValueOpt,
                                                                        node.CaptureReceiver,
                                                                        node.PlaceholderId,
                                                                        whenNotNull,
                                                                        whenNullOpt,
                                                                        type))
                End If

                Dim builder As New SpillBuilder()

                If receiverNeedsSpill Then
                    Dim spill = DirectCast(receiver, BoundSpillSequence)
                    builder.AddSpill(spill)
                    receiver = spill.ValueOpt
                End If

                Dim nullCheckTarget As BoundExpression
                Dim placeholderReplacement As BoundExpression

                If node.CaptureReceiver OrElse conditionalAccessReceiverPlaceholderReplacementInfo.IsSpilled Then
                    ' Nullable receiver is used to invoke GetValueOrDefault method, which is never spills receiver (takes no arguments)
                    Debug.Assert(Not receiver.Type.IsNullableType() OrElse Not conditionalAccessReceiverPlaceholderReplacementInfo.IsSpilled)
                    Debug.Assert(Not receiver.Type.IsNullableType() OrElse node.CaptureReceiver)

                    ' Let's spill or capture it then.
                    If node.CaptureReceiver OrElse conditionalAccessReceiverPlaceholderReplacementInfo.ForceCaptureIfReferenceType Then
                        Debug.Assert(Not receiver.Type.IsNullableType() OrElse Not conditionalAccessReceiverPlaceholderReplacementInfo.ForceCaptureIfReferenceType)

                        ' We need to capture the receiver by value.
                        Dim capturedReceiver As BoundExpression

                        ' However, when receiver is not known to be a reference type, we will not be using captured value for a value type receiver.
                        ' In that case, we should perform real spilling first in order to avoid evaluating the side-effects more than one time, 
                        ' to enforce order of evaluation and to decrease the size of the code that we will duplicate (1 - receiver evaluation during capture,
                        ' 2 - value type receiver evaluation). 
                        If Not receiver.Type.IsReferenceType AndAlso Not receiver.Type.IsNullableType() Then
                            receiver = SpillValue(receiver, isReceiver:=True, evaluateSideEffects:=True, builder:=builder)
                        End If

                        ' If receiver is not spilled, we can use a local to capture receiver's value. If receiver is spilled, use SpillRValue to accomplish this
                        ' because values of locals are not preserved across awaits. 
                        If conditionalAccessReceiverPlaceholderReplacementInfo.IsSpilled Then
                            Debug.Assert(Not receiver.Type.IsNullableType())

                            If node.CaptureReceiver OrElse receiver.Type.IsReferenceType Then
                                nullCheckTarget = SpillRValue(receiver.MakeRValue(), builder:=builder)
                                capturedReceiver = nullCheckTarget
                            Else
                                Debug.Assert(conditionalAccessReceiverPlaceholderReplacementInfo.ForceCaptureIfReferenceType)

                                Dim referenceReceiverBuilder As New SpillBuilder()
                                Dim spilledReferenceReceiver As BoundExpression = SpillRValue(receiver.MakeRValue(), referenceReceiverBuilder)
                                spilledReferenceReceiver = referenceReceiverBuilder.BuildSequenceAndFree(Me.F, spilledReferenceReceiver)
                                Dim referenceReceiverSpillSequence = TryCast(spilledReferenceReceiver, BoundSpillSequence)

                                If referenceReceiverSpillSequence IsNot Nothing Then
                                    ' If condition `(object)default(T) != null` is true at execution time,
                                    ' the T is a value type. And it is a reference type otherwise.
                                    Dim isValueTypeCheck = Me.F.ReferenceIsNotNothing(Me.F.DirectCast(Me.F.DirectCast(Me.F.Null(), receiver.Type),
                                                                                    Me.F.SpecialType(SpecialType.System_Object)))

                                    builder.AssumeFieldsIfNeeded(referenceReceiverSpillSequence)
                                    builder.AddLocals(referenceReceiverSpillSequence.Locals)
                                    builder.AddStatement(Me.F.If(Me.F.Not(isValueTypeCheck), Me.F.StatementList(referenceReceiverSpillSequence.Statements)))

                                    spilledReferenceReceiver = referenceReceiverSpillSequence.ValueOpt
                                End If

                                capturedReceiver = spilledReferenceReceiver
                                nullCheckTarget = New BoundComplexConditionalAccessReceiver(Me.F.Syntax,
                                                                                            receiver,
                                                                                            capturedReceiver,
                                                                                            receiver.Type)
                            End If
                        Else
                            Dim receiverLocal As LocalSymbol = Nothing
                            receiverLocal = Me.F.SynthesizedLocal(receiver.Type)
                            builder.AddLocal(receiverLocal)

                            nullCheckTarget = Me.F.AssignmentExpression(Me.F.Local(receiverLocal, isLValue:=True), receiver.MakeRValue())
                            capturedReceiver = Me.F.Local(receiverLocal, isLValue:=True)
                        End If

                        If nullCheckTarget.Type.IsReferenceType OrElse nullCheckTarget.Type.IsNullableType() Then
                            placeholderReplacement = capturedReceiver
                        Else
                            Debug.Assert(Not nullCheckTarget.Type.IsValueType)
                            Debug.Assert(nullCheckTarget.Type.IsTypeParameter())

                            ' Note, as part of "receiver IsNot Nothing" check below, we are capturing receiver only for reference types. 
                            ' When this happens, in order to avoid a race, we shouldn't reevaluate the receiver again, the capture 
                            ' should be the target of the access. However, when receiver is a value type, in order to preserve
                            ' side-effects, the original receiver should be the target of the access. That is why we don't even
                            ' capture it at run-time.
                            placeholderReplacement = New BoundComplexConditionalAccessReceiver(Me.F.Syntax,
                                                                                                receiver,
                                                                                                capturedReceiver,
                                                                                                receiver.Type)
                        End If
                    Else
                        Debug.Assert(Not receiver.Type.IsNullableType())
                        Debug.Assert(conditionalAccessReceiverPlaceholderReplacementInfo.IsSpilled)
                        placeholderReplacement = SpillValue(receiver, isReceiver:=True, evaluateSideEffects:=True, builder:=builder)
                        nullCheckTarget = placeholderReplacement.MakeRValue()
                    End If
                Else
                    placeholderReplacement = receiver
                    nullCheckTarget = placeholderReplacement.MakeRValue()
                End If

                ' We need to revisit the whenNotNull expression to replace placeholder
                Dim rewriter As New ConditionalAccessReceiverPlaceholderReplacement(node.PlaceholderId, placeholderReplacement, RecursionDepth)
                whenNotNull = DirectCast(rewriter.Visit(whenNotNull), BoundExpression)
                Debug.Assert(rewriter.Replaced)

                Dim condition As BoundExpression

                ' We need to a add a null check for the receiver
                If nullCheckTarget.Type.IsReferenceType Then
                    condition = Me.F.ReferenceIsNotNothing(nullCheckTarget)
                ElseIf nullCheckTarget.Type.IsNullableType() Then
                    Dim hasValue = DirectCast(Me.CompilationState.Compilation.GetSpecialTypeMember(SpecialMember.System_Nullable_T_get_HasValue), MethodSymbol)
                    Debug.Assert(hasValue IsNot Nothing)
                    condition = Me.F.Call(nullCheckTarget, hasValue.AsMember(DirectCast(nullCheckTarget.Type, NamedTypeSymbol)))
                Else
                    Debug.Assert(Not nullCheckTarget.Type.IsValueType)
                    Debug.Assert(nullCheckTarget.Type.IsTypeParameter())

                    ' The "receiver IsNot Nothing" check becomes
                    ' Not <receiver's type is reference type> OrElse receiver IsNot Nothing 
                    ' The <receiver's type is reference type> is performed by boxing default value of receiver's type and checking if it is a null reference. 

                    Dim notReferenceType = Me.F.ReferenceIsNotNothing(Me.F.DirectCast(Me.F.DirectCast(Me.F.Null(),
                                                                                                        nullCheckTarget.Type),
                                                                                        Me.F.SpecialType(SpecialType.System_Object)))

                    condition = Me.F.LogicalOrElse(notReferenceType,
                                                    Me.F.ReferenceIsNotNothing(Me.F.DirectCast(nullCheckTarget,
                                                                                                Me.F.SpecialType(SpecialType.System_Object))))
                End If

                If whenNullOpt Is Nothing Then
                    Debug.Assert(type.IsVoidType())
                    builder.AddStatement(
                    Me.F.If(condition:=condition,
                            thenClause:=MakeExpressionStatement(whenNotNull, builder)))

                    Return builder.BuildSequenceAndFree(Me.F, expression:=Nothing)
                Else
                    Debug.Assert(Not type.IsVoidType())
                    Dim tempLocal As LocalSymbol = Me.F.SynthesizedLocal(type)

                    builder.AddLocal(tempLocal)

                    builder.AddStatement(Me.F.If(condition:=condition,
                                                 thenClause:=MakeAssignmentStatement(whenNotNull, tempLocal, builder),
                                                 elseClause:=MakeAssignmentStatement(whenNullOpt, tempLocal, builder)))

                    Return builder.BuildSequenceAndFree(Me.F, expression:=Me.F.Local(tempLocal, False))
                End If
            End Function

            Private NotInheritable Class ConditionalAccessReceiverPlaceholderReplacement
                Inherits BoundTreeRewriterWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator

                Private ReadOnly _placeholderId As Integer
                Private ReadOnly _replaceWith As BoundExpression
                Private _replaced As Boolean

                Public Sub New(placeholderId As Integer, replaceWith As BoundExpression, recursionDepth As Integer)
                    MyBase.New(recursionDepth)
                    Me._placeholderId = placeholderId
                    Me._replaceWith = replaceWith
                End Sub

                Public ReadOnly Property Replaced As Boolean
                    Get
                        Return _replaced
                    End Get
                End Property

                Public Overrides Function VisitConditionalAccessReceiverPlaceholder(node As BoundConditionalAccessReceiverPlaceholder) As BoundNode
                    If _placeholderId = node.PlaceholderId Then
                        Debug.Assert(Not _replaced)
                        _replaced = True

                        Dim result = _replaceWith

                        If node.IsLValue Then
                            Debug.Assert(result.IsLValue)
                            Return result
                        Else
                            Return result.MakeRValue()
                        End If
                    End If

                    Return node
                End Function
            End Class

            Public Overrides Function VisitConditionalAccessReceiverPlaceholder(node As BoundConditionalAccessReceiverPlaceholder) As BoundNode
                If _conditionalAccessReceiverPlaceholderReplacementInfo Is Nothing OrElse _conditionalAccessReceiverPlaceholderReplacementInfo.PlaceholderId <> node.PlaceholderId Then
                    Throw ExceptionUtilities.Unreachable
                End If

                Return MyBase.VisitConditionalAccessReceiverPlaceholder(node)
            End Function

            Public Overrides Function VisitComplexConditionalAccessReceiver(node As BoundComplexConditionalAccessReceiver) As BoundNode
                Dim result = DirectCast(MyBase.VisitComplexConditionalAccessReceiver(node), BoundComplexConditionalAccessReceiver)

                If NeedsSpill(result.ValueTypeReceiver) OrElse NeedsSpill(result.ReferenceTypeReceiver) Then
                    Throw ExceptionUtilities.Unreachable
                End If

                Return result
            End Function

            Public Overrides Function VisitArrayCreation(node As BoundArrayCreation) As BoundNode
                Debug.Assert(node.ArrayLiteralOpt Is Nothing)

                Dim bounds As ImmutableArray(Of BoundExpression) = Me.VisitList(node.Bounds)
                Dim rewrittenInitializer As BoundArrayInitialization = DirectCast(Me.Visit(node.InitializerOpt), BoundArrayInitialization)
                Dim rewrittenType As TypeSymbol = Me.VisitType(node.Type)

                Dim boundsRequiresSpill As Boolean = NeedsSpill(bounds)
                Dim initRequiresSpill As Boolean = ArrayInitializerNeedsSpill(rewrittenInitializer)

                If Not boundsRequiresSpill AndAlso Not initRequiresSpill Then
                    Debug.Assert(rewrittenInitializer Is Nothing OrElse rewrittenInitializer.Kind = BoundKind.ArrayInitialization)
                    Return node.Update(node.IsParamArrayArgument,
                                       bounds,
                                       DirectCast(rewrittenInitializer, BoundArrayInitialization),
                                       Nothing,
                                       Nothing,
                                       rewrittenType)
                End If

                Dim builder As New SpillBuilder()
                bounds = SpillExpressionList(builder, bounds)

                If rewrittenInitializer IsNot Nothing Then
                    rewrittenInitializer = rewrittenInitializer.Update(
                                                SpillExpressionList(builder, rewrittenInitializer.Initializers),
                                                rewrittenInitializer.Type)
                End If

                Return builder.BuildSequenceAndFree(Me.F,
                                                    node.Update(node.IsParamArrayArgument,
                                                                bounds,
                                                                DirectCast(rewrittenInitializer, BoundArrayInitialization),
                                                                Nothing,
                                                                Nothing,
                                                                rewrittenType))

            End Function

            Private Function VisitArrayInitializationParts(node As BoundArrayInitialization) As BoundExpression
                Debug.Assert(node IsNot Nothing)

                Dim parts As ImmutableArray(Of BoundExpression) = node.Initializers
                Dim partCount As Integer = parts.Length

                Dim rewrittenParts(partCount - 1) As BoundExpression

                For index = 0 To partCount - 1
                    Dim part As BoundExpression = parts(index)
                    rewrittenParts(index) = If(part.Kind = BoundKind.ArrayInitialization,
                                               VisitArrayInitializationParts(
                                                   DirectCast(part, BoundArrayInitialization)),
                                               VisitExpression(part))
                Next

                Dim rewrittenType As TypeSymbol = VisitType(node.Type)
                Return node.Update(rewrittenParts.AsImmutableOrNull, rewrittenType)
            End Function

            Public Overrides Function VisitArrayInitialization(node As BoundArrayInitialization) As BoundNode
                If node Is Nothing Then
                    Return Nothing
                End If

                Return VisitArrayInitializationParts(DirectCast(node, BoundArrayInitialization))
            End Function

            Public Overrides Function VisitArrayAccess(node As BoundArrayAccess) As BoundNode
                Dim rewritten = DirectCast(MyBase.VisitArrayAccess(node), BoundArrayAccess)
                Dim expression As BoundExpression = rewritten.Expression
                Dim indices As ImmutableArray(Of BoundExpression) = rewritten.Indices

                If Not NeedsSpill(expression) AndAlso Not NeedsSpill(indices) Then
                    Return rewritten
                End If

                Dim builder As New SpillBuilder()

                Dim allExpressions = ImmutableArray.Create(Of BoundExpression)(expression).Concat(indices)
                Dim allSpilledExpressions = SpillExpressionList(builder, allExpressions)

                Return builder.BuildSequenceAndFree(Me.F,
                                                    rewritten.Update(expression:=allSpilledExpressions.First(),
                                                                     indices:=allSpilledExpressions.RemoveAt(0),
                                                                     rewritten.IsLValue,
                                                                     rewritten.Type))
            End Function

            Public Overrides Function VisitArrayLength(node As BoundArrayLength) As BoundNode
                Dim rewritten = DirectCast(MyBase.VisitArrayLength(node), BoundArrayLength)
                Dim expression As BoundExpression = rewritten.Expression

                If Not NeedsSpill(expression) Then
                    Return rewritten
                End If

                Debug.Assert(expression.Kind = BoundKind.SpillSequence)
                Dim spill = DirectCast(expression, BoundSpillSequence)
                Return SpillSequenceWithNewValue(spill,
                                                 rewritten.Update(spill.ValueOpt,
                                                                  rewritten.Type))
            End Function

            Public Overrides Function VisitUnaryOperator(node As BoundUnaryOperator) As BoundNode
                Dim rewritten = DirectCast(MyBase.VisitUnaryOperator(node), BoundUnaryOperator)
                Dim operand As BoundExpression = rewritten.Operand

                If Not NeedsSpill(operand) Then
                    Return rewritten
                End If

                Debug.Assert(operand.Kind = BoundKind.SpillSequence)
                Dim spill = DirectCast(operand, BoundSpillSequence)
                Return SpillSequenceWithNewValue(spill,
                                                 rewritten.Update(rewritten.OperatorKind,
                                                                  spill.ValueOpt,
                                                                  rewritten.Checked,
                                                                  rewritten.ConstantValueOpt,
                                                                  rewritten.Type))
            End Function

            Public Overrides Function VisitBinaryConditionalExpression(node As BoundBinaryConditionalExpression) As BoundNode
                Dim rewritten = DirectCast(MyBase.VisitBinaryConditionalExpression(node), BoundBinaryConditionalExpression)
                Debug.Assert(rewritten.ConvertedTestExpression Is Nothing)
                Debug.Assert(rewritten.TestExpressionPlaceholder Is Nothing)
                Dim testExpression As BoundExpression = rewritten.TestExpression
                Dim elseExpression As BoundExpression = rewritten.ElseExpression

                If Not NeedsSpill(testExpression) AndAlso Not NeedsSpill(elseExpression) Then
                    Return rewritten
                End If

                Dim builder As New SpillBuilder()

                Dim tempLocal As LocalSymbol = Me.F.SynthesizedLocal(rewritten.Type)
                builder.AddLocal(tempLocal)

                builder.AddStatement(MakeAssignmentStatement(testExpression, tempLocal, builder))

                builder.AddStatement(
                    Me.F.If(
                        condition:=Me.F.ReferenceIsNothing(Me.F.Local(tempLocal, False)),
                        thenClause:=MakeAssignmentStatement(elseExpression, tempLocal, builder)))

                Return builder.BuildSequenceAndFree(Me.F,
                                                    Me.F.Local(tempLocal, False))
            End Function

            Public Overrides Function VisitTypeOf(node As BoundTypeOf) As BoundNode
                Dim rewritten = DirectCast(MyBase.VisitTypeOf(node), BoundTypeOf)
                Dim operand As BoundExpression = rewritten.Operand

                If Not NeedsSpill(operand) Then
                    Return rewritten
                End If

                Debug.Assert(operand.Kind = BoundKind.SpillSequence)
                Dim spill = DirectCast(operand, BoundSpillSequence)
                Return SpillSequenceWithNewValue(spill,
                                                 rewritten.Update(spill.ValueOpt,
                                                                  rewritten.IsTypeOfIsNotExpression,
                                                                  rewritten.TargetType,
                                                                  rewritten.Type))
            End Function

            Public Overrides Function VisitSequencePointExpression(node As BoundSequencePointExpression) As BoundNode
                Dim rewritten = DirectCast(MyBase.VisitSequencePointExpression(node), BoundSequencePointExpression)

                Dim expression As BoundExpression = rewritten.Expression

                If Not NeedsSpill(expression) Then
                    Return rewritten
                End If

                Debug.Assert(expression.Kind = BoundKind.SpillSequence)
                Dim spill = DirectCast(expression, BoundSpillSequence)
                Return SpillSequenceWithNewValue(spill,
                                                 rewritten.Update(spill.ValueOpt,
                                                                  rewritten.Type))
            End Function

        End Class
    End Class

End Namespace
