File: Lowering\LocalRewriter\LocalRewriter_BinaryOperators.vb
Web Access
Project: src\src\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj (Microsoft.CodeAnalysis.VisualBasic)
' 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.Immutable
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
 
Namespace Microsoft.CodeAnalysis.VisualBasic
 
    Partial Friend Class LocalRewriter
 
        Public Overrides Function VisitUserDefinedBinaryOperator(node As BoundUserDefinedBinaryOperator) As BoundNode
            If _inExpressionLambda Then
                Return node.Update(node.OperatorKind, DirectCast(Visit(node.UnderlyingExpression), BoundExpression), node.Checked, node.Type)
            End If
 
            If (node.OperatorKind And BinaryOperatorKind.Lifted) <> 0 Then
                Return RewriteLiftedUserDefinedBinaryOperator(node)
            End If
 
            Return Visit(node.UnderlyingExpression)
        End Function
 
        Public Overrides Function VisitUserDefinedShortCircuitingOperator(node As BoundUserDefinedShortCircuitingOperator) As BoundNode
            If _inExpressionLambda Then
                ' If we are inside expression lambda we need to rewrite it into just a bitwise operator call
                Dim placeholder As BoundRValuePlaceholder = node.LeftOperandPlaceholder
                Dim leftOperand As BoundExpression = node.LeftOperand
 
                If placeholder IsNot Nothing Then
                    Debug.Assert(leftOperand IsNot Nothing)
                    AddPlaceholderReplacement(placeholder, VisitExpression(leftOperand))
                End If
 
                Dim rewritten = DirectCast(VisitExpression(node.BitwiseOperator), BoundUserDefinedBinaryOperator)
 
                If placeholder IsNot Nothing Then
                    RemovePlaceholderReplacement(placeholder)
                End If
 
                ' NOTE: Everything but 'rewritten' will be discarded by ExpressionLambdaRewriter
                '       we keep the node only to make sure we can replace 'And' with 'AndAlso'
                '       and 'Or' with 'OrElse' when generating proper factory calls.
                Return node.Update(node.LeftOperand, node.LeftOperandPlaceholder, node.LeftTest, rewritten, node.Type)
            End If
 
            ' The rewrite that should happen here is:
            '     If(LeftTest(temp = left), temp, BitwiseOperator)
 
            Dim temp As New SynthesizedLocal(_currentMethodOrLambda, node.LeftOperand.Type, SynthesizedLocalKind.LoweringTemp)
 
            Dim tempAccess As New BoundLocal(node.Syntax, temp, True, temp.Type)
 
            AddPlaceholderReplacement(node.LeftOperandPlaceholder,
                                          New BoundAssignmentOperator(node.Syntax,
                                                                      tempAccess,
                                                                      VisitExpressionNode(node.LeftOperand),
                                                                      True, temp.Type))
 
            Dim rewrittenTest = VisitExpressionNode(node.LeftTest)
 
            tempAccess = tempAccess.MakeRValue()
 
            UpdatePlaceholderReplacement(node.LeftOperandPlaceholder, tempAccess)
 
            Dim rewrittenBitwise = VisitExpressionNode(node.BitwiseOperator)
 
            RemovePlaceholderReplacement(node.LeftOperandPlaceholder)
 
            Return New BoundSequence(node.Syntax,
                                     ImmutableArray.Create(Of LocalSymbol)(temp),
                                     ImmutableArray(Of BoundExpression).Empty,
                                     MakeTernaryConditionalExpression(node.Syntax,
                                                                    rewrittenTest,
                                                                    tempAccess,
                                                                    rewrittenBitwise),
                                     temp.Type)
        End Function
 
        Public Overrides Function VisitBinaryOperator(node As BoundBinaryOperator) As BoundNode
            ' Do not blow the stack due to a deep recursion on the left. 
 
            Dim optimizeForConditionalBranch As Boolean = (node.OperatorKind And BinaryOperatorKind.OptimizableForConditionalBranch) <> 0
            Dim optimizeChildForConditionalBranch As Boolean = optimizeForConditionalBranch
 
            Dim child As BoundExpression = GetLeftOperand(node, optimizeChildForConditionalBranch)
 
            If child.Kind <> BoundKind.BinaryOperator Then
                Return RewriteBinaryOperatorSimple(node, optimizeForConditionalBranch)
            End If
 
            Dim stack = ArrayBuilder(Of (Binary As BoundBinaryOperator, OptimizeForConditionalBranch As Boolean)).GetInstance()
            stack.Push((node, optimizeForConditionalBranch))
 
            Dim binary As BoundBinaryOperator = DirectCast(child, BoundBinaryOperator)
 
            Do
                If optimizeChildForConditionalBranch Then
                    Select Case (binary.OperatorKind And BinaryOperatorKind.OpMask)
                        Case BinaryOperatorKind.AndAlso, BinaryOperatorKind.OrElse
                            Exit Select
                        Case Else
                            optimizeChildForConditionalBranch = False
                    End Select
                End If
 
                stack.Push((binary, optimizeChildForConditionalBranch))
 
                child = GetLeftOperand(binary, optimizeChildForConditionalBranch)
 
                If child.Kind <> BoundKind.BinaryOperator Then
                    Exit Do
                End If
 
                binary = DirectCast(child, BoundBinaryOperator)
            Loop
 
            Dim left = VisitExpressionNode(child)
 
            Do
                Dim tuple As (Binary As BoundBinaryOperator, OptimizeForConditionalBranch As Boolean) = stack.Pop()
                binary = tuple.Binary
 
                Dim right = VisitExpression(GetRightOperand(binary, tuple.OptimizeForConditionalBranch))
 
                If (binary.OperatorKind And BinaryOperatorKind.Lifted) <> 0 Then
                    left = FinishRewriteOfLiftedIntrinsicBinaryOperator(binary, left, right, tuple.OptimizeForConditionalBranch)
                Else
                    left = TransformRewrittenBinaryOperator(binary.Update(binary.OperatorKind, left, right, binary.Checked, binary.ConstantValueOpt, Me.VisitType(binary.Type)))
                End If
            Loop While binary IsNot node
 
            Debug.Assert(stack.Count = 0)
            stack.Free()
 
            Return left
        End Function
 
        Private Shared Function GetLeftOperand(binary As BoundBinaryOperator, ByRef optimizeForConditionalBranch As Boolean) As BoundExpression
            If optimizeForConditionalBranch AndAlso (binary.OperatorKind And BinaryOperatorKind.OpMask) <> BinaryOperatorKind.OrElse Then
                Debug.Assert((binary.OperatorKind And BinaryOperatorKind.OpMask) = BinaryOperatorKind.AndAlso)
                ' If left operand is evaluated to Null, three-valued Boolean logic dictates that the right operand of AndAlso
                ' should still be evaluated. So, we cannot simply snap the left operand to Boolean.
                optimizeForConditionalBranch = False
            End If
 
            Return binary.Left.GetMostEnclosedParenthesizedExpression()
        End Function
 
        Private Shared Function GetRightOperand(binary As BoundBinaryOperator, adjustIfOptimizableForConditionalBranch As Boolean) As BoundExpression
            If adjustIfOptimizableForConditionalBranch Then
                Return LocalRewriter.AdjustIfOptimizableForConditionalBranch(binary.Right, Nothing)
            Else
                Return binary.Right
            End If
        End Function
 
        Private Function RewriteBinaryOperatorSimple(node As BoundBinaryOperator, optimizeForConditionalBranch As Boolean) As BoundNode
            If (node.OperatorKind And BinaryOperatorKind.Lifted) <> 0 Then
                Return RewriteLiftedIntrinsicBinaryOperatorSimple(node, optimizeForConditionalBranch)
            End If
 
            Return TransformRewrittenBinaryOperator(DirectCast(MyBase.VisitBinaryOperator(node), BoundBinaryOperator))
        End Function
 
        Private Function ReplaceMyGroupCollectionPropertyGetWithUnderlyingField(operand As BoundExpression) As BoundExpression
 
            If operand.HasErrors Then
                Return operand
            End If
 
            ' See Semantics::AlterForMyGroup
            ' "goo.Form1 IS something" is translated to "goo.m_Form1 IS something" when
            ' Form1 is a property generated by MyGroupCollection
            ' Otherwise 'goo.Form1 IS Nothing" would be always false because 'goo.Form1'
            ' property call creates an instance on the fly.
 
            Select Case operand.Kind
                Case BoundKind.DirectCast
                    ' Dig through possible DirectCast conversions
                    Dim cast = DirectCast(operand, BoundDirectCast)
                    Return cast.Update(ReplaceMyGroupCollectionPropertyGetWithUnderlyingField(cast.Operand),
                                       cast.ConversionKind, cast.SuppressVirtualCalls, cast.ConstantValueOpt, cast.RelaxationLambdaOpt, cast.Type)
 
                Case BoundKind.Conversion
                    ' Dig through possible conversion. For example, in context of an expression tree it is not changed to DirectCast conversion.
                    Dim cast = DirectCast(operand, BoundConversion)
                    Return cast.Update(ReplaceMyGroupCollectionPropertyGetWithUnderlyingField(cast.Operand),
                                       cast.ConversionKind, cast.Checked, cast.ExplicitCastInCode, cast.ConstantValueOpt,
                                       cast.ExtendedInfoOpt,
                                       cast.Type)
 
                Case BoundKind.Call
                    Dim boundCall = DirectCast(operand, BoundCall)
 
                    If boundCall.Method.MethodKind = MethodKind.PropertyGet AndAlso
                       boundCall.Method.AssociatedSymbol IsNot Nothing AndAlso
                       boundCall.Method.AssociatedSymbol.IsMyGroupCollectionProperty Then
                        Return New BoundFieldAccess(boundCall.Syntax,
                                                    boundCall.ReceiverOpt,
                                                    DirectCast(boundCall.Method.AssociatedSymbol, PropertySymbol).AssociatedField,
                                                    isLValue:=False,
                                                    type:=boundCall.Type)
                    End If
 
                Case BoundKind.PropertyAccess
                    ' Can get here when we are inside a lambda converted to an expression tree.
                    Debug.Assert(_inExpressionLambda)
                    Dim propertyAccess = DirectCast(operand, BoundPropertyAccess)
 
                    If propertyAccess.AccessKind = PropertyAccessKind.Get AndAlso
                       propertyAccess.PropertySymbol.IsMyGroupCollectionProperty Then
                        Return New BoundFieldAccess(propertyAccess.Syntax,
                                                    propertyAccess.ReceiverOpt,
                                                    propertyAccess.PropertySymbol.AssociatedField,
                                                    isLValue:=False,
                                                    type:=propertyAccess.Type)
                    End If
 
                Case Else
                    Debug.Assert(operand.Kind <> BoundKind.Parenthesized) ' Must have been removed by now.
            End Select
 
            Return operand
        End Function
 
        Private Function TransformRewrittenBinaryOperator(node As BoundBinaryOperator) As BoundExpression
            Dim opKind = node.OperatorKind
 
            Debug.Assert((opKind And BinaryOperatorKind.Lifted) = 0)
 
            Select Case (opKind And BinaryOperatorKind.OpMask)
                Case BinaryOperatorKind.Is, BinaryOperatorKind.IsNot
                    node = node.Update(node.OperatorKind,
                                       ReplaceMyGroupCollectionPropertyGetWithUnderlyingField(node.Left),
                                       ReplaceMyGroupCollectionPropertyGetWithUnderlyingField(node.Right),
                                       node.Checked,
                                       node.ConstantValueOpt,
                                       node.Type)
 
                    If (node.Left.Type IsNot Nothing AndAlso node.Left.Type.IsNullableType) OrElse
                       (node.Right.Type IsNot Nothing AndAlso node.Right.Type.IsNullableType) Then
 
                        Return RewriteNullableIsOrIsNotOperator(node)
                    End If
 
                Case BinaryOperatorKind.Concatenate  ' Concat needs to be done before expr trees, so in LocalRewriter instead of VBSemanticsRewriter
                    If node.Type.IsObjectType() Then
                        Return RewriteObjectBinaryOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__ConcatenateObjectObjectObject)
                    Else
                        Return RewriteConcatenateOperator(node)
                    End If
 
                Case BinaryOperatorKind.Like
                    If node.Left.Type.IsObjectType() Then
                        Return RewriteLikeOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_LikeOperator__LikeObjectObjectObjectCompareMethod)
                    Else
                        Return RewriteLikeOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_LikeOperator__LikeStringStringStringCompareMethod)
                    End If
 
                Case BinaryOperatorKind.Equals
                    Dim leftType = node.Left.Type
                    ' NOTE: For some reason Dev11 seems to still ignore inside the expression tree the fact that the target 
                    '       type of the binary operator is Boolean and used Object op Object => Object helpers even in this case 
                    '       despite what is said in comments in RuntimeMembers CodeGenerator::GetHelperForObjRelOp
                    ' TODO: Recheck
 
                    If node.Type.IsObjectType() OrElse Me._inExpressionLambda AndAlso leftType.IsObjectType() Then
                        Return RewriteObjectComparisonOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__CompareObjectEqualObjectObjectBoolean)
                    ElseIf node.Type.IsBooleanType() Then
 
                        If leftType.IsObjectType() Then
                            Return RewriteObjectComparisonOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__ConditionalCompareObjectEqualObjectObjectBoolean)
                        ElseIf leftType.IsStringType() Then
                            Return RewriteStringComparisonOperator(node)
                        ElseIf leftType.IsDecimalType() Then
                            Return RewriteDecimalComparisonOperator(node)
                        ElseIf leftType.IsDateTimeType() Then
                            Return RewriteDateComparisonOperator(node)
                        End If
                    End If
 
                Case BinaryOperatorKind.NotEquals
                    Dim leftType = node.Left.Type
                    ' NOTE: See comment above
 
                    If node.Type.IsObjectType() OrElse Me._inExpressionLambda AndAlso leftType.IsObjectType() Then
                        Return RewriteObjectComparisonOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__CompareObjectNotEqualObjectObjectBoolean)
                    ElseIf node.Type.IsBooleanType() Then
 
                        If leftType.IsObjectType() Then
                            Return RewriteObjectComparisonOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__ConditionalCompareObjectNotEqualObjectObjectBoolean)
                        ElseIf leftType.IsStringType() Then
                            Return RewriteStringComparisonOperator(node)
                        ElseIf leftType.IsDecimalType() Then
                            Return RewriteDecimalComparisonOperator(node)
                        ElseIf leftType.IsDateTimeType() Then
                            Return RewriteDateComparisonOperator(node)
                        End If
                    End If
 
                Case BinaryOperatorKind.LessThanOrEqual
                    Dim leftType = node.Left.Type
                    ' NOTE: See comment above
 
                    If node.Type.IsObjectType() OrElse Me._inExpressionLambda AndAlso leftType.IsObjectType() Then
                        Return RewriteObjectComparisonOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__CompareObjectLessEqualObjectObjectBoolean)
                    ElseIf node.Type.IsBooleanType() Then
 
                        If leftType.IsObjectType() Then
                            Return RewriteObjectComparisonOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__ConditionalCompareObjectLessEqualObjectObjectBoolean)
                        ElseIf leftType.IsStringType() Then
                            Return RewriteStringComparisonOperator(node)
                        ElseIf leftType.IsDecimalType() Then
                            Return RewriteDecimalComparisonOperator(node)
                        ElseIf leftType.IsDateTimeType() Then
                            Return RewriteDateComparisonOperator(node)
                        End If
                    End If
 
                Case BinaryOperatorKind.GreaterThanOrEqual
                    Dim leftType = node.Left.Type
                    ' NOTE: See comment above
 
                    If node.Type.IsObjectType() OrElse Me._inExpressionLambda AndAlso leftType.IsObjectType() Then
                        Return RewriteObjectComparisonOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__CompareObjectGreaterEqualObjectObjectBoolean)
                    ElseIf node.Type.IsBooleanType() Then
 
                        If leftType.IsObjectType() Then
                            Return RewriteObjectComparisonOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__ConditionalCompareObjectGreaterEqualObjectObjectBoolean)
                        ElseIf leftType.IsStringType() Then
                            Return RewriteStringComparisonOperator(node)
                        ElseIf leftType.IsDecimalType() Then
                            Return RewriteDecimalComparisonOperator(node)
                        ElseIf leftType.IsDateTimeType() Then
                            Return RewriteDateComparisonOperator(node)
                        End If
                    End If
 
                Case BinaryOperatorKind.LessThan
                    Dim leftType = node.Left.Type
                    ' NOTE: See comment above
 
                    If node.Type.IsObjectType() OrElse Me._inExpressionLambda AndAlso leftType.IsObjectType() Then
                        Return RewriteObjectComparisonOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__CompareObjectLessObjectObjectBoolean)
                    ElseIf node.Type.IsBooleanType() Then
 
                        If leftType.IsObjectType() Then
                            Return RewriteObjectComparisonOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__ConditionalCompareObjectLessObjectObjectBoolean)
                        ElseIf leftType.IsStringType() Then
                            Return RewriteStringComparisonOperator(node)
                        ElseIf leftType.IsDecimalType() Then
                            Return RewriteDecimalComparisonOperator(node)
                        ElseIf leftType.IsDateTimeType() Then
                            Return RewriteDateComparisonOperator(node)
                        End If
                    End If
 
                Case BinaryOperatorKind.GreaterThan
                    Dim leftType = node.Left.Type
                    ' NOTE: See comment above
 
                    If node.Type.IsObjectType() OrElse Me._inExpressionLambda AndAlso leftType.IsObjectType() Then
                        Return RewriteObjectComparisonOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__CompareObjectGreaterObjectObjectBoolean)
                    ElseIf node.Type.IsBooleanType() Then
 
                        If leftType.IsObjectType() Then
                            Return RewriteObjectComparisonOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__ConditionalCompareObjectGreaterObjectObjectBoolean)
                        ElseIf leftType.IsStringType() Then
                            Return RewriteStringComparisonOperator(node)
                        ElseIf leftType.IsDecimalType() Then
                            Return RewriteDecimalComparisonOperator(node)
                        ElseIf leftType.IsDateTimeType() Then
                            Return RewriteDateComparisonOperator(node)
                        End If
                    End If
 
                Case BinaryOperatorKind.Add
                    If node.Type.IsObjectType() AndAlso Not _inExpressionLambda Then
                        Return RewriteObjectBinaryOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__AddObjectObjectObject)
                    ElseIf node.Type.IsDecimalType() Then
                        Return RewriteDecimalBinaryOperator(node, SpecialMember.System_Decimal__AddDecimalDecimal)
                    End If
 
                Case BinaryOperatorKind.Subtract
                    If node.Type.IsObjectType() AndAlso Not _inExpressionLambda Then
                        Return RewriteObjectBinaryOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__SubtractObjectObjectObject)
                    ElseIf node.Type.IsDecimalType() Then
                        Return RewriteDecimalBinaryOperator(node, SpecialMember.System_Decimal__SubtractDecimalDecimal)
                    End If
 
                Case BinaryOperatorKind.Multiply
                    If node.Type.IsObjectType() AndAlso Not _inExpressionLambda Then
                        Return RewriteObjectBinaryOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__MultiplyObjectObjectObject)
                    ElseIf node.Type.IsDecimalType() Then
                        Return RewriteDecimalBinaryOperator(node, SpecialMember.System_Decimal__MultiplyDecimalDecimal)
                    End If
 
                Case BinaryOperatorKind.Modulo
                    If node.Type.IsObjectType() AndAlso Not _inExpressionLambda Then
                        Return RewriteObjectBinaryOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__ModObjectObjectObject)
                    ElseIf node.Type.IsDecimalType() Then
                        Return RewriteDecimalBinaryOperator(node, SpecialMember.System_Decimal__RemainderDecimalDecimal)
                    End If
 
                Case BinaryOperatorKind.Divide
                    If node.Type.IsObjectType() AndAlso Not _inExpressionLambda Then
                        Return RewriteObjectBinaryOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__DivideObjectObjectObject)
                    ElseIf node.Type.IsDecimalType() Then
                        Return RewriteDecimalBinaryOperator(node, SpecialMember.System_Decimal__DivideDecimalDecimal)
                    End If
 
                Case BinaryOperatorKind.IntegerDivide
                    If node.Type.IsObjectType() AndAlso Not _inExpressionLambda Then
                        Return RewriteObjectBinaryOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__IntDivideObjectObjectObject)
                    End If
 
                Case BinaryOperatorKind.Power
                    If node.Type.IsObjectType() AndAlso Not _inExpressionLambda Then
                        Return RewriteObjectBinaryOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__ExponentObjectObjectObject)
                    Else
                        Return RewritePowOperator(node)
                    End If
 
                Case BinaryOperatorKind.LeftShift
                    If node.Type.IsObjectType() AndAlso Not _inExpressionLambda Then
                        Return RewriteObjectBinaryOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__LeftShiftObjectObjectObject)
                    End If
 
                Case BinaryOperatorKind.RightShift
                    If node.Type.IsObjectType() AndAlso Not _inExpressionLambda Then
                        Return RewriteObjectBinaryOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__RightShiftObjectObjectObject)
                    End If
 
                Case BinaryOperatorKind.OrElse, BinaryOperatorKind.AndAlso
                    If node.Type.IsObjectType() AndAlso Not _inExpressionLambda Then
                        Return RewriteObjectShortCircuitOperator(node)
                    End If
 
                Case BinaryOperatorKind.Xor
                    If node.Type.IsObjectType() AndAlso Not _inExpressionLambda Then
                        Return RewriteObjectBinaryOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__XorObjectObjectObject)
                    End If
 
                Case BinaryOperatorKind.Or
                    If node.Type.IsObjectType() AndAlso Not _inExpressionLambda Then
                        Return RewriteObjectBinaryOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__OrObjectObjectObject)
                    End If
 
                Case BinaryOperatorKind.And
                    If node.Type.IsObjectType() AndAlso Not _inExpressionLambda Then
                        Return RewriteObjectBinaryOperator(node, WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__AndObjectObjectObject)
                    End If
 
            End Select
 
            Return node
        End Function
 
        Private Function RewriteDateComparisonOperator(node As BoundBinaryOperator) As BoundExpression
            If _inExpressionLambda Then
                Return node
            End If
 
            Debug.Assert(node.Left.Type.IsDateTimeType())
            Debug.Assert(node.Right.Type.IsDateTimeType())
            Debug.Assert(node.Type.IsBooleanType())
 
            Dim result As BoundExpression = node
            Dim left As BoundExpression = node.Left
            Dim right As BoundExpression = node.Right
 
            If left.Type.IsDateTimeType() AndAlso right.Type.IsDateTimeType() Then
 
                ' Rewrite as follows:
                ' DateTime.Compare(left, right) [Operator] 0
 
                Const memberId As SpecialMember = SpecialMember.System_DateTime__CompareDateTimeDateTime
                Dim memberSymbol = DirectCast(ContainingAssembly.GetSpecialTypeMember(memberId), MethodSymbol)
 
                If Not ReportMissingOrBadRuntimeHelper(node, memberId, memberSymbol) Then
                    Debug.Assert(memberSymbol.ReturnType.SpecialType = SpecialType.System_Int32)
 
                    Dim compare = New BoundCall(node.Syntax, memberSymbol, Nothing, Nothing,
                                                ImmutableArray.Create(left, right), Nothing, memberSymbol.ReturnType)
 
                    result = New BoundBinaryOperator(node.Syntax,
                                                     node.OperatorKind And BinaryOperatorKind.OpMask,
                                                     compare,
                                                     New BoundLiteral(node.Syntax, ConstantValue.Create(0I), memberSymbol.ReturnType),
                                                     False,
                                                     node.Type)
                End If
            End If
 
            Return result
        End Function
 
        Private Function RewriteDecimalComparisonOperator(node As BoundBinaryOperator) As BoundExpression
            If _inExpressionLambda Then
                Return node
            End If
 
            Debug.Assert(node.Left.Type.IsDecimalType())
            Debug.Assert(node.Right.Type.IsDecimalType())
            Debug.Assert(node.Type.IsBooleanType())
 
            Dim result As BoundExpression = node
            Dim left As BoundExpression = node.Left
            Dim right As BoundExpression = node.Right
 
            If left.Type.IsDecimalType() AndAlso right.Type.IsDecimalType() Then
 
                ' Rewrite as follows:
                ' Decimal.Compare(left, right) [Operator] 0
 
                Const memberId As SpecialMember = SpecialMember.System_Decimal__CompareDecimalDecimal
                Dim memberSymbol = DirectCast(ContainingAssembly.GetSpecialTypeMember(memberId), MethodSymbol)
 
                If Not ReportMissingOrBadRuntimeHelper(node, memberId, memberSymbol) Then
                    Debug.Assert(memberSymbol.ReturnType.SpecialType = SpecialType.System_Int32)
 
                    Dim compare = New BoundCall(node.Syntax, memberSymbol, Nothing, Nothing,
                                                ImmutableArray.Create(left, right), Nothing, memberSymbol.ReturnType)
 
                    result = New BoundBinaryOperator(node.Syntax,
                                                     node.OperatorKind And BinaryOperatorKind.OpMask,
                                                     compare,
                                                     New BoundLiteral(node.Syntax, ConstantValue.Create(0I), memberSymbol.ReturnType),
                                                     False,
                                                     node.Type)
                End If
            End If
 
            Return result
        End Function
 
        Private Function RewriteObjectShortCircuitOperator(node As BoundBinaryOperator) As BoundExpression
            Debug.Assert(node.Type.IsObjectType())
 
            Dim result As BoundExpression = node
            Dim rewrittenLeft As BoundExpression = node.Left
            Dim rewrittenRight As BoundExpression = node.Right
 
            If rewrittenLeft.Type.IsObjectType() AndAlso rewrittenRight.Type.IsObjectType() Then
 
                ' This operator translates into:
                '       DirectCast(ToBoolean(left) OrElse/AndAlso ToBoolean(right), Object)
                ' Result is boxed Boolean.
 
                ' Dev10 uses complex routine in IL gen that emits the calls and also avoids boxing+calling the helper
                ' for result of nested OrElse/AndAlso. I will try to achieve the same effect by digging into DirectCast node
                ' on each side. Since, expressions are rewritten bottom-up, we don't need to look deeper than one level.
                ' Note, we may unwrap unnecessary DirectCast node that wasn't created by this function for nested OrElse/AndAlso, 
                ' but this should not have any negative or observable side effect.
 
                Dim left As BoundExpression = rewrittenLeft
                Dim right As BoundExpression = rewrittenRight
 
                If left.Kind = BoundKind.DirectCast Then
                    Dim cast = DirectCast(left, BoundDirectCast)
                    If cast.Operand.Type.IsBooleanType() Then
                        ' Just get rid of DirectCast node.
                        left = cast.Operand
                    End If
                End If
 
                If right.Kind = BoundKind.DirectCast Then
                    Dim cast = DirectCast(right, BoundDirectCast)
                    If cast.Operand.Type.IsBooleanType() Then
                        ' Just get rid of DirectCast node.
                        right = cast.Operand
                    End If
                End If
 
                If left Is rewrittenLeft OrElse right Is rewrittenRight Then
                    ' Need to call ToBoolean
                    Const memberId As WellKnownMember = WellKnownMember.Microsoft_VisualBasic_CompilerServices_Conversions__ToBooleanObject
                    Dim memberSymbol = DirectCast(Compilation.GetWellKnownTypeMember(memberId), MethodSymbol)
 
                    If Not ReportMissingOrBadRuntimeHelper(node, memberId, memberSymbol) Then
 
                        Debug.Assert(memberSymbol.ReturnType.IsBooleanType())
                        Debug.Assert(memberSymbol.Parameters(0).Type.IsObjectType())
 
                        If left Is rewrittenLeft Then
                            left = New BoundCall(node.Syntax, memberSymbol, Nothing, Nothing,
                                                 ImmutableArray.Create(left), Nothing, memberSymbol.ReturnType)
                        End If
 
                        If right Is rewrittenRight Then
                            right = New BoundCall(node.Syntax, memberSymbol, Nothing, Nothing,
                                                  ImmutableArray.Create(right), Nothing, memberSymbol.ReturnType)
                        End If
                    End If
                End If
 
                If left IsNot rewrittenLeft AndAlso right IsNot rewrittenRight Then
                    ' left and right are successfully rewritten
                    Debug.Assert(left.Type.IsBooleanType() AndAlso right.Type.IsBooleanType())
 
                    Dim op = New BoundBinaryOperator(node.Syntax, node.OperatorKind And BinaryOperatorKind.OpMask, left, right, False, left.Type)
 
                    ' Box result of the operator
                    result = New BoundDirectCast(node.Syntax, op, ConversionKind.WideningValue, node.Type, Nothing)
                End If
 
            Else
                Throw ExceptionUtilities.Unreachable
            End If
 
            Return result
        End Function
 
        Private Function RewritePowOperator(node As BoundBinaryOperator) As BoundExpression
            If _inExpressionLambda Then
                Return node
            End If
 
            Dim result As BoundExpression = node
            Dim left As BoundExpression = node.Left
            Dim right As BoundExpression = node.Right
 
            If node.Type.IsDoubleType() AndAlso left.Type.IsDoubleType() AndAlso right.Type.IsDoubleType() Then
 
                ' Rewrite as follows:
                ' Math.Pow(left, right)
 
                Const memberId As WellKnownMember = WellKnownMember.System_Math__PowDoubleDouble
                Dim memberSymbol = DirectCast(Compilation.GetWellKnownTypeMember(memberId), MethodSymbol)
 
                If Not ReportMissingOrBadRuntimeHelper(node, memberId, memberSymbol) Then
                    Debug.Assert(memberSymbol.ReturnType.IsDoubleType())
 
                    result = New BoundCall(node.Syntax, memberSymbol, Nothing, Nothing,
                                           ImmutableArray.Create(left, right), Nothing, memberSymbol.ReturnType)
                End If
            End If
 
            Return result
        End Function
 
        Private Function RewriteDecimalBinaryOperator(node As BoundBinaryOperator, member As SpecialMember) As BoundExpression
            If _inExpressionLambda Then
                Return node
            End If
 
            Debug.Assert(node.Left.Type.IsDecimalType())
            Debug.Assert(node.Right.Type.IsDecimalType())
            Debug.Assert(node.Type.IsDecimalType())
 
            Dim result As BoundExpression = node
            Dim left As BoundExpression = node.Left
            Dim right As BoundExpression = node.Right
 
            If left.Type.IsDecimalType() AndAlso right.Type.IsDecimalType() Then
 
                ' Call Decimal.member(left, right)
                Dim memberSymbol = DirectCast(ContainingAssembly.GetSpecialTypeMember(member), MethodSymbol)
 
                If Not ReportMissingOrBadRuntimeHelper(node, member, memberSymbol) Then
                    Debug.Assert(memberSymbol.ReturnType.IsDecimalType())
                    result = New BoundCall(node.Syntax, memberSymbol, Nothing, Nothing,
                                           ImmutableArray.Create(left, right), Nothing, memberSymbol.ReturnType)
                End If
            End If
 
            Return result
        End Function
 
        Private Function RewriteStringComparisonOperator(node As BoundBinaryOperator) As BoundExpression
            Debug.Assert(node.Left.Type.IsStringType())
            Debug.Assert(node.Right.Type.IsStringType())
            Debug.Assert(node.Type.IsBooleanType())
 
            Dim result As BoundExpression = node
            Dim left As BoundExpression = node.Left
            Dim right As BoundExpression = node.Right
            Dim compareText As Boolean = (node.OperatorKind And BinaryOperatorKind.CompareText) <> 0
 
            ' Rewrite as follows:
            ' Operators.CompareString(left, right, compareText)  [Operator] 0
 
            ' Prefer embedded version of the member if present
            Dim embeddedOperatorsType As NamedTypeSymbol = Compilation.GetWellKnownType(WellKnownType.Microsoft_VisualBasic_CompilerServices_EmbeddedOperators)
            Dim compareStringMember As WellKnownMember =
                If(embeddedOperatorsType.IsErrorType AndAlso TypeOf embeddedOperatorsType Is MissingMetadataTypeSymbol,
                   WellKnownMember.Microsoft_VisualBasic_CompilerServices_Operators__CompareStringStringStringBoolean,
                   WellKnownMember.Microsoft_VisualBasic_CompilerServices_EmbeddedOperators__CompareStringStringStringBoolean)
            Dim memberSymbol = DirectCast(Compilation.GetWellKnownTypeMember(compareStringMember), MethodSymbol)
 
            If Not ReportMissingOrBadRuntimeHelper(node, compareStringMember, memberSymbol) Then
                Debug.Assert(memberSymbol.ReturnType.SpecialType = SpecialType.System_Int32)
                Debug.Assert(memberSymbol.Parameters(2).Type.IsBooleanType())
 
                Dim compare = New BoundCall(node.Syntax, memberSymbol, Nothing, Nothing,
                                       ImmutableArray.Create(left,
                                                             right,
                                                             New BoundLiteral(node.Syntax, ConstantValue.Create(compareText), memberSymbol.Parameters(2).Type)),
                                        Nothing,
                                        memberSymbol.ReturnType)
 
                result = New BoundBinaryOperator(node.Syntax, (node.OperatorKind And BinaryOperatorKind.OpMask),
                                                 compare, New BoundLiteral(node.Syntax, ConstantValue.Create(0I), memberSymbol.ReturnType),
                                                 False, node.Type)
            End If
 
            Return result
        End Function
 
        Private Function RewriteObjectComparisonOperator(node As BoundBinaryOperator, member As WellKnownMember) As BoundExpression
            Debug.Assert(node.Left.Type.IsObjectType())
            Debug.Assert(node.Right.Type.IsObjectType())
            Debug.Assert(node.Type.IsObjectType() OrElse node.Type.IsBooleanType())
 
            Dim result As BoundExpression = node
            Dim left As BoundExpression = node.Left
            Dim right As BoundExpression = node.Right
            Dim compareText As Boolean = (node.OperatorKind And BinaryOperatorKind.CompareText) <> 0
 
            ' Call member(left, right, compareText)
            Dim memberSymbol = DirectCast(Compilation.GetWellKnownTypeMember(member), MethodSymbol)
 
            If Not ReportMissingOrBadRuntimeHelper(node, member, memberSymbol) Then
                Debug.Assert(memberSymbol.ReturnType Is node.Type OrElse Me._inExpressionLambda AndAlso memberSymbol.ReturnType.IsObjectType)
                Debug.Assert(memberSymbol.Parameters(2).Type.IsBooleanType())
 
                result = New BoundCall(node.Syntax, memberSymbol, Nothing, Nothing,
                                       ImmutableArray.Create(left,
                                                             right,
                                                             New BoundLiteral(node.Syntax, ConstantValue.Create(compareText), memberSymbol.Parameters(2).Type)),
                                        Nothing,
                                        memberSymbol.ReturnType,
                                        suppressObjectClone:=True)
 
                If Me._inExpressionLambda AndAlso memberSymbol.ReturnType.IsObjectType AndAlso node.Type.IsBooleanType Then
                    result = New BoundConversion(node.Syntax, DirectCast(result, BoundExpression), ConversionKind.NarrowingBoolean, node.Checked, False, node.Type)
                End If
            End If
 
            Return result
        End Function
 
        Private Function RewriteLikeOperator(node As BoundBinaryOperator, member As WellKnownMember) As BoundExpression
            Dim left As BoundExpression = node.Left
            Dim right As BoundExpression = node.Right
            Debug.Assert((node.Type.IsObjectType() AndAlso left.Type.IsObjectType() AndAlso right.Type.IsObjectType()) OrElse
                         (node.Type.IsBooleanType() AndAlso left.Type.IsStringType() AndAlso right.Type.IsStringType()))
 
            Dim result As BoundExpression = node
            Dim compareText As Boolean = (node.OperatorKind And BinaryOperatorKind.CompareText) <> 0
 
            ' Call member(left, right, if (compareText, 1, 0))
            Dim memberSymbol = DirectCast(Compilation.GetWellKnownTypeMember(member), MethodSymbol)
 
            If Not ReportMissingOrBadRuntimeHelper(node, member, memberSymbol) Then
                Debug.Assert(memberSymbol.ReturnType Is node.Type)
                Debug.Assert(memberSymbol.Parameters(2).Type.IsEnumType())
                Debug.Assert(memberSymbol.Parameters(2).Type.GetEnumUnderlyingTypeOrSelf().SpecialType = SpecialType.System_Int32)
 
                result = New BoundCall(node.Syntax, memberSymbol, Nothing, Nothing,
                                       ImmutableArray.Create(left,
                                                             right,
                                                             New BoundLiteral(node.Syntax, ConstantValue.Create(If(compareText, 1I, 0I)), memberSymbol.Parameters(2).Type)),
                                        Nothing,
                                        memberSymbol.ReturnType,
                                        suppressObjectClone:=True)
            End If
 
            Return result
        End Function
 
        Private Function RewriteObjectBinaryOperator(node As BoundBinaryOperator, member As WellKnownMember) As BoundExpression
            Dim left As BoundExpression = node.Left
            Dim right As BoundExpression = node.Right
            Debug.Assert(left.Type.IsObjectType())
            Debug.Assert(right.Type.IsObjectType())
            Debug.Assert(node.Type.IsObjectType())
 
            Dim result As BoundExpression = node
 
            ' Call member(left, right)
            Dim memberSymbol = DirectCast(Compilation.GetWellKnownTypeMember(member), MethodSymbol)
 
            If Not ReportMissingOrBadRuntimeHelper(node, member, memberSymbol) Then
                result = New BoundCall(node.Syntax, memberSymbol, Nothing, Nothing,
                                       ImmutableArray.Create(left, right), Nothing, memberSymbol.ReturnType, suppressObjectClone:=True)
            End If
 
            Return result
        End Function
 
        Private Function RewriteLiftedIntrinsicBinaryOperatorSimple(node As BoundBinaryOperator, optimizeForConditionalBranch As Boolean) As BoundNode
            Dim left As BoundExpression = VisitExpressionNode(node.Left)
            Dim right As BoundExpression = VisitExpressionNode(GetRightOperand(node, optimizeForConditionalBranch))
 
            Return FinishRewriteOfLiftedIntrinsicBinaryOperator(node, left, right, optimizeForConditionalBranch)
        End Function
 
        Private Function FinishRewriteOfLiftedIntrinsicBinaryOperator(node As BoundBinaryOperator, left As BoundExpression, right As BoundExpression, optimizeForConditionalBranch As Boolean) As BoundExpression
            Debug.Assert((node.OperatorKind And BinaryOperatorKind.Lifted) <> 0)
            Debug.Assert(Not optimizeForConditionalBranch OrElse
                         (node.OperatorKind And BinaryOperatorKind.OpMask) = BinaryOperatorKind.OrElse OrElse
                         (node.OperatorKind And BinaryOperatorKind.OpMask) = BinaryOperatorKind.AndAlso)
 
            Dim leftHasValue = HasValue(left)
            Dim rightHasValue = HasValue(right)
 
            Dim leftHasNoValue = HasNoValue(left)
            Dim rightHasNoValue = HasNoValue(right)
 
            ' The goal of optimization is to eliminate the need to deal with instances of Nullable(Of Boolean) type as early as possible,
            ' and, as a result, simplify evaluation of built-in OrElse/AndAlso operators by eliminating the need to use three-valued Boolean logic.
            ' The optimization is possible because when an entire Boolean Expression is evaluated to Null, that has the same effect as if result
            ' of evaluation was False. However, we do want to preserve the original order of evaluation, according to language rules. 
            If optimizeForConditionalBranch AndAlso node.Type.IsNullableOfBoolean() AndAlso left.Type.IsNullableOfBoolean() AndAlso right.Type.IsNullableOfBoolean() AndAlso
               (leftHasValue OrElse Not Me._inExpressionLambda OrElse (node.OperatorKind And BinaryOperatorKind.OpMask) = BinaryOperatorKind.OrElse) Then
 
                Return RewriteAndOptimizeLiftedIntrinsicLogicalShortCircuitingOperator(node, left, right, leftHasNoValue, leftHasValue, rightHasNoValue, rightHasValue)
            End If
 
            If Me._inExpressionLambda Then
                Return node.Update(node.OperatorKind, left, right, node.Checked, node.ConstantValueOpt, node.Type)
            End If
 
            ' Check for trivial (no nulls, two nulls) Cases
 
            '== TWO NULLS
            If leftHasNoValue And rightHasNoValue Then
                ' return new R?()
                Return NullableNull(left, node.Type)
            End If
 
            '== NO NULLS
            If leftHasValue And rightHasValue Then
                ' return new R?(UnliftedOp(left, right))
                Dim unliftedOp = ApplyUnliftedBinaryOp(node,
                                                      NullableValueOrDefault(left),
                                                      NullableValueOrDefault(right))
 
                Return WrapInNullable(unliftedOp, node.Type)
            End If
 
            ' non-trivial cases rewrite differently when operands are boolean
            If node.Left.Type.IsNullableOfBoolean Then
                Select Case (node.OperatorKind And BinaryOperatorKind.OpMask)
                    'boolean context makes no difference for Xor.
                    Case BinaryOperatorKind.And,
                        BinaryOperatorKind.Or,
                        BinaryOperatorKind.AndAlso,
                        BinaryOperatorKind.OrElse
 
                        Return RewriteLiftedBooleanBinaryOperator(node, left, right, leftHasNoValue, rightHasNoValue, leftHasValue, rightHasValue)
                End Select
            End If
 
            '== ONE NULL
            ' result is null. No need to do the Op, even if checked.
            If leftHasNoValue Or rightHasNoValue Then
                'Reducing to {[left | right] ; Null }
                Dim notNullOperand = If(leftHasNoValue, right, left)
                Dim nullOperand = NullableNull(If(leftHasNoValue, left, right), node.Type)
 
                Return MakeSequence(notNullOperand, nullOperand)
            End If
 
            ' At this point both operands are not known to be nulls, both may need to be evaluated
            ' We may also statically know if one is definitely not a null
            ' we cannot know, though, if whole operator yields null or not.
 
            If rightHasValue Then
                Dim whenNotNull As BoundExpression = Nothing
                Dim whenNull As BoundExpression = Nothing
                If IsConditionalAccess(left, whenNotNull, whenNull) Then
                    Dim rightValue = NullableValueOrDefault(right)
 
                    If (rightValue.IsConstant OrElse rightValue.Kind = BoundKind.Local OrElse rightValue.Kind = BoundKind.Parameter) AndAlso
                       HasValue(whenNotNull) AndAlso HasNoValue(whenNull) Then
 
                        Return UpdateConditionalAccess(left,
                                                       WrapInNullable(ApplyUnliftedBinaryOp(node,
                                                                                            NullableValueOrDefault(whenNotNull),
                                                                                            rightValue),
                                                                      node.Type),
                                                       NullableNull(whenNull, node.Type))
                    End If
                End If
            End If
 
            Dim temps As ArrayBuilder(Of LocalSymbol) = Nothing
            Dim inits As ArrayBuilder(Of BoundExpression) = Nothing
 
            Dim leftHasValueExpr As BoundExpression = Nothing
            Dim rightHasValueExpr As BoundExpression = Nothing
 
            Dim processedLeft = ProcessNullableOperand(left,
                                                       leftHasValueExpr,
                                                       temps,
                                                       inits,
                                                       RightCantChangeLeftLocal(left, right),
                                                       leftHasValue)
 
            ' left is done when right is running, so right cannot change if it is a local
            Dim processedRight = ProcessNullableOperand(right,
                                                        rightHasValueExpr,
                                                        temps,
                                                        inits,
                                                        doNotCaptureLocals:=True,
                                                        operandHasValue:=rightHasValue)
 
            Dim value As BoundExpression = Nothing
 
            Dim operatorHasValue As BoundExpression = MakeBooleanBinaryExpression(node.Syntax,
                                                             BinaryOperatorKind.And,
                                                             leftHasValueExpr,
                                                             rightHasValueExpr)
 
            Dim unliftedOpOnCaptured = ApplyUnliftedBinaryOp(node,
                                                            processedLeft,
                                                            processedRight)
 
            value = MakeTernaryConditionalExpression(node.Syntax,
                                              operatorHasValue,
                                              WrapInNullable(unliftedOpOnCaptured, node.Type),
                                              NullableNull(node.Syntax, node.Type))
 
            ' if we used temps, arrange a sequence for them.
            If temps IsNot Nothing Then
                value = New BoundSequence(node.Syntax,
                                     temps.ToImmutableAndFree,
                                     inits.ToImmutableAndFree,
                                     value,
                                     value.Type)
            End If
 
            Return value
        End Function
 
        ''' <summary>
        ''' The goal of optimization is to eliminate the need to deal with instances of Nullable(Of Boolean) type as early as possible,
        ''' and, as a result, simplify evaluation of built-in OrElse/AndAlso operators by eliminating the need to use three-valued Boolean logic.
        ''' The optimization is possible because when an entire Boolean Expression is evaluated to Null, that has the same effect as if result
        ''' of evaluation was False. However, we do want to preserve the original order of evaluation, according to language rules. 
        ''' This method returns an expression that still has Nullable(Of Boolean) type, but that expression is much simpler and can be 
        ''' further simplified by the consumer.
        ''' </summary>
        Private Function RewriteAndOptimizeLiftedIntrinsicLogicalShortCircuitingOperator(node As BoundBinaryOperator,
                                                                                         left As BoundExpression, right As BoundExpression,
                                                                                         leftHasNoValue As Boolean, leftHasValue As Boolean,
                                                                                         rightHasNoValue As Boolean, rightHasValue As Boolean) As BoundExpression
 
            Debug.Assert(leftHasValue OrElse Not Me._inExpressionLambda OrElse (node.OperatorKind And BinaryOperatorKind.OpMask) = BinaryOperatorKind.OrElse)
            Dim booleanResult As BoundExpression = Nothing
 
            If Not Me._inExpressionLambda Then
                If leftHasNoValue And rightHasNoValue Then
                    ' return new R?(), the consumer will take care of optimizing it out if possible. 
                    Return NullableNull(left, node.Type)
                End If
 
                If (node.OperatorKind And BinaryOperatorKind.OpMask) = BinaryOperatorKind.OrElse Then
                    If leftHasNoValue Then
                        ' There is nothing to evaluate on the left, the result is True only if Right is True
                        Return right
                    ElseIf rightHasNoValue Then
                        ' There is nothing to evaluate on the right, the result is True only if Left is True
                        Return left
                    End If
                Else
                    Debug.Assert((node.OperatorKind And BinaryOperatorKind.OpMask) = BinaryOperatorKind.AndAlso)
 
                    If leftHasNoValue Then
                        ' We can return False in this case. There is nothing to evaluate on the left, but we still need to evaluate the right
                        booleanResult = EvaluateOperandAndReturnFalse(node, right, rightHasValue)
                    ElseIf rightHasNoValue Then
                        ' We can return False in this case. There is nothing to evaluate on the right, but we still need to evaluate the left
                        booleanResult = EvaluateOperandAndReturnFalse(node, left, leftHasValue)
                    ElseIf Not leftHasValue Then
                        ' We cannot tell whether Left is Null or not.
                        ' For [x AndAlso y] we can produce Boolean result as follows:
                        '
                        ' tempX = x
                        ' (Not tempX.HasValue OrElse tempX.GetValueOrDefault()) AndAlso
                        ' (y.GetValueOrDefault() AndAlso tempX.HasValue)
                        '
                        Dim leftTemp As SynthesizedLocal = Nothing
                        Dim leftInit As BoundExpression = Nothing
 
                        ' Right may be a method that takes Left byref - " local AndAlso TakesArgByref(local) "
                        ' So in general we must capture Left even if it is a local.
                        Dim capturedLeft = CaptureNullableIfNeeded(left, leftTemp, leftInit, RightCantChangeLeftLocal(left, right))
 
                        booleanResult = MakeBooleanBinaryExpression(node.Syntax,
                                            BinaryOperatorKind.AndAlso,
                                            MakeBooleanBinaryExpression(node.Syntax,
                                                BinaryOperatorKind.OrElse,
                                                New BoundUnaryOperator(node.Syntax,
                                                                       UnaryOperatorKind.Not,
                                                                       NullableHasValue(capturedLeft),
                                                                       False,
                                                                       node.Type.GetNullableUnderlyingType()),
                                                NullableValueOrDefault(capturedLeft)),
                                            MakeBooleanBinaryExpression(node.Syntax,
                                                BinaryOperatorKind.AndAlso,
                                                NullableValueOrDefault(right),
                                                NullableHasValue(capturedLeft)))
 
                        ' if we used temp, put it in a sequence
                        Debug.Assert((leftTemp Is Nothing) = (leftInit Is Nothing))
                        If leftTemp IsNot Nothing Then
                            booleanResult = New BoundSequence(node.Syntax,
                                                              ImmutableArray.Create(Of LocalSymbol)(leftTemp),
                                                              ImmutableArray.Create(Of BoundExpression)(leftInit),
                                                              booleanResult,
                                                              booleanResult.Type)
                        End If
                    End If
                End If
            End If
 
            If booleanResult Is Nothing Then
                ' UnliftedOp(left.GetValueOrDefault(), right.GetValueOrDefault()))
                ' For AndAlso, this optimization is valid only when we know that left has value
                Debug.Assert(leftHasValue OrElse (node.OperatorKind And BinaryOperatorKind.OpMask) = BinaryOperatorKind.OrElse)
 
                booleanResult = ApplyUnliftedBinaryOp(node, NullableValueOrDefaultWithOperandHasValue(left, leftHasValue), NullableValueOrDefaultWithOperandHasValue(right, rightHasValue))
            End If
 
            ' return new R?(booleanResult), the consumer will take care of optimizing out the creation of this Nullable(Of Boolean) instance, if possible.
            Return WrapInNullable(booleanResult, node.Type)
        End Function
 
        Private Function EvaluateOperandAndReturnFalse(node As BoundBinaryOperator, operand As BoundExpression, operandHasValue As Boolean) As BoundExpression
            Debug.Assert(node.Type.IsNullableOfBoolean())
            Debug.Assert(operand.Type.IsNullableOfBoolean())
 
            Dim result = New BoundLiteral(node.Syntax, ConstantValue.False, node.Type.GetNullableUnderlyingType())
            Return New BoundSequence(node.Syntax, ImmutableArray(Of LocalSymbol).Empty,
                                     ImmutableArray.Create(If(operandHasValue, NullableValueOrDefault(operand), operand)),
                                     result, result.Type)
        End Function
 
        Private Function NullableValueOrDefaultWithOperandHasValue(operand As BoundExpression, operandHasValue As Boolean) As BoundExpression
            Debug.Assert(operand.Type.IsNullableOfBoolean())
 
            If Not Me._inExpressionLambda OrElse operandHasValue Then
                Return NullableValueOrDefault(operand)
            Else
                ' In expression tree this will be shown as Coalesce, which is preferred over a GetValueOrDefault call  
                Return New BoundNullableIsTrueOperator(operand.Syntax, operand, operand.Type.GetNullableUnderlyingType())
            End If
        End Function
 
        Private Function RewriteLiftedBooleanBinaryOperator(node As BoundBinaryOperator,
                                                            left As BoundExpression,
                                                            right As BoundExpression,
                                                            leftHasNoValue As Boolean,
                                                            rightHasNoValue As Boolean,
                                                            leftHasValue As Boolean,
                                                            rightHasValue As Boolean) As BoundExpression
 
            Debug.Assert(left.Type.IsNullableOfBoolean AndAlso right.Type.IsNullableOfBoolean AndAlso node.Type.IsNullableOfBoolean)
            Debug.Assert(Not (leftHasNoValue And rightHasNoValue))
            Debug.Assert(Not (leftHasValue And rightHasValue))
 
            Dim nullableOfBoolean = node.Type
            Dim booleanType = nullableOfBoolean.GetNullableUnderlyingType
 
            Dim op = node.OperatorKind And BinaryOperatorKind.OpMask
            Dim isOr As Boolean = (op = BinaryOperatorKind.OrElse) OrElse
                                    (op = BinaryOperatorKind.Or)
 
            '== ONE NULL
            If leftHasNoValue Or rightHasNoValue Then
                Dim notNullOperand As BoundExpression
                Dim nullOperand As BoundExpression
                Dim operandHasValue As Boolean
 
                If rightHasNoValue Then
                    notNullOperand = left
                    nullOperand = right
                    operandHasValue = leftHasValue
                Else
                    notNullOperand = right
                    nullOperand = left
                    operandHasValue = rightHasValue
                End If
 
                If operandHasValue Then
                    ' reduce "Operand [And | AndAlso] NULL" ---> "If(Operand, NULL, False)".
                    ' reduce "Operand [Or | OrElse]   NULL" ---> "If(Operand, True, NULL)".
 
                    Dim syntax = notNullOperand.Syntax
                    Dim condition = NullableValueOrDefault(notNullOperand)
 
                    Return MakeTernaryConditionalExpression(node.Syntax,
                            condition,
                            If(isOr,
                                NullableTrue(syntax, nullableOfBoolean),
                                NullableNull(nullOperand, nullableOfBoolean)),
                            If(isOr,
                                NullableNull(nullOperand, nullableOfBoolean),
                                NullableFalse(syntax, nullableOfBoolean)))
                Else
 
                    ' Dev10 uses AndAlso, but since operands are captured and hasValue is basically an access to a local 
                    ' so it makes sense to use And and avoid branching.
                    ' reduce "Operand [And | AndAlso] NULL" --> "If(Operand.HasValue And Not Operand.GetValueOrDefault, Operand, NULL)".
                    ' reduce "Operand [Or | OrElse]   NULL" --> "If(Operand.GetValueOrDefault,                          Operand, NULL)".
                    Dim temp As SynthesizedLocal = Nothing
                    Dim tempInit As BoundExpression = Nothing
 
                    ' we have only one operand to evaluate, do not capture locals.
                    Dim capturedOperand = CaptureNullableIfNeeded(notNullOperand, temp, tempInit, doNotCaptureLocals:=True)
 
                    Dim syntax = notNullOperand.Syntax
                    Dim capturedOperandValue = NullableValueOrDefault(capturedOperand)
 
                    Dim condition = If(isOr,
                                       NullableValueOrDefault(capturedOperand),
                                       MakeBooleanBinaryExpression(syntax, BinaryOperatorKind.And,
                                                            NullableHasValue(capturedOperand),
                                                            New BoundUnaryOperator(syntax, UnaryOperatorKind.Not,
                                                                NullableValueOrDefault(capturedOperand),
                                                                False,
                                                                booleanType)))
 
                    Dim result As BoundExpression =
                        MakeTernaryConditionalExpression(node.Syntax,
                            condition,
                            capturedOperand,
                            NullableNull(nullOperand, nullableOfBoolean))
 
                    ' if we used a temp, arrange a sequence for it and its initialization
                    If temp IsNot Nothing Then
                        result = New BoundSequence(node.Syntax,
                                             ImmutableArray.Create(Of LocalSymbol)(temp),
                                             ImmutableArray.Create(tempInit),
                                             result,
                                             result.Type)
                    End If
 
                    Return result
                End If
            End If
 
            '== GENERAL CASE
 
            ' x And y is rewritten into:
            '
            ' tempX = x
            ' [tempY = y] ' if not short-circuiting
            ' If (tempX.HasValue AndAlso Not tempX.GetValueOrDefault(),
            '   False?,                             ' result based on the left operand
            '   If ((tempY = y).HasValue,           ' if short-circuiting, otherwise just "y.HasValue"
            '       If (tempY.GetValueOrDefault(),  ' innermost If
            '           tempX,
            '           False?),
            '       Null?)
            '
            ' Other operators rewrite using the same template, but constants and conditions are slightly different.
            ' Additionally we observe if HasValue is known statically or capturing may not be needed
            ' so some of the Ifs may be folded
 
            Dim IsShortCircuited = (op = BinaryOperatorKind.AndAlso Or op = BinaryOperatorKind.OrElse)
 
            Dim leftTemp As SynthesizedLocal = Nothing
            Dim leftInit As BoundExpression = Nothing
            Dim capturedLeft As BoundExpression = left
 
            ' Capture left operand if we do not know whether it has value (so that we could call HasValue and ValueOrDefault).
            If Not leftHasValue Then
                ' Right may be a method that takes Left byref - " local And TakesArgByref(local) "
                ' So in general we must capture Left even if it is a local.
                capturedLeft = CaptureNullableIfNeeded(left, leftTemp, leftInit, RightCantChangeLeftLocal(left, right))
            End If
 
            Dim rightTemp As SynthesizedLocal = Nothing
            Dim rightInit As BoundExpression = Nothing
            Dim capturedRight As BoundExpression = right
 
            ' Capture right operand if we do not know whether it has value and we are not short-circuiting
            ' on optimized left operand (in which case right operand is used only once).
            ' When we are short circuiting, the right operand will be captured at the point where it is
            ' evaluated (notice "tempY = y" in the template).
            If Not rightHasValue AndAlso Not (leftHasValue AndAlso IsShortCircuited) Then
                ' when evaluating Right, left is already evaluated so we leave right local as-is.
                ' nothing can change it.
                capturedRight = CaptureNullableIfNeeded(capturedRight, rightTemp, rightInit, doNotCaptureLocals:=True)
            End If
 
            Dim OperandValue As BoundExpression = If(leftHasValue, capturedRight, capturedLeft)
            Dim ConstValue As BoundExpression = NullableOfBooleanValue(node.Syntax, isOr, nullableOfBoolean)
 
            ' innermost If
            Dim value As BoundExpression = MakeTernaryConditionalExpression(node.Syntax,
                If(leftHasValue,
                   NullableValueOrDefault(capturedLeft),
                   NullableValueOrDefault(capturedRight)),
                If(isOr,
                   ConstValue,
                   OperandValue),
                If(isOr,
                   OperandValue,
                   ConstValue))
 
            If Not leftHasValue Then
                If Not rightHasValue Then
                    ' second nested if - when need to look at Right
                    '
                    ' If (right.HasValue, 
                    '       nestedIf,
                    '       Null)
                    '
                    ' note that we use init of the Right as a target of HasValue when short-circuiting 
                    ' it will run only if we do not get result after looking at left
                    '
                    ' NOTE: when not short circuiting we use captured right and evaluate init 
                    ' unconditionally before all the ifs.
 
                    Dim conditionOperand As BoundExpression
                    If IsShortCircuited Then
                        ' use init if we have one.
                        ' if Right is trivial local or const, may not have init, just use capturedRight
                        conditionOperand = If(rightInit, capturedRight)
 
                        ' make sure init can no longer be used
                        rightInit = Nothing
                    Else
                        conditionOperand = capturedRight
                    End If
 
                    value = MakeTernaryConditionalExpression(node.Syntax,
                        NullableHasValue(conditionOperand),
                        value,
                        NullableNull(node.Syntax, nullableOfBoolean))
                End If
 
                If Not rightHasValue OrElse IsShortCircuited Then
                    Dim capturedLeftValue As BoundExpression = NullableValueOrDefault(capturedLeft)
 
                    Dim leftCapturedOrInit As BoundExpression
 
                    If rightInit IsNot Nothing OrElse leftInit Is Nothing Then
                        leftCapturedOrInit = capturedLeft
                    Else
                        leftCapturedOrInit = leftInit
                        leftInit = Nothing
                    End If
 
                    ' Outermost If (do we know result after looking at first operand ?):
                    '
                    ' Or -
                    ' If (left.HasValue AndAlso left.Value, ...
 
                    ' And -
                    ' If (left.HasValue AndAlso Not left.Value, ...
                    '
                    'TODO: when not initializing right temp, can use And. (fewer branches)
                    value = MakeTernaryConditionalExpression(node.Syntax,
                        MakeBooleanBinaryExpression(node.Syntax,
                            BinaryOperatorKind.AndAlso,
                            NullableHasValue(leftCapturedOrInit),
                            If(isOr,
                               capturedLeftValue,
                               New BoundUnaryOperator(node.Syntax,
                                                      UnaryOperatorKind.Not,
                                                      capturedLeftValue,
                                                      False,
                                                      booleanType))),
                        NullableOfBooleanValue(node.Syntax, isOr, nullableOfBoolean),
                        value)
                End If
            End If
 
            ' if we used temps, and did not embed inits, put them in a sequence
            If leftTemp IsNot Nothing OrElse rightTemp IsNot Nothing Then
                Dim temps = ArrayBuilder(Of LocalSymbol).GetInstance
                Dim inits = ArrayBuilder(Of BoundExpression).GetInstance
 
                If leftTemp IsNot Nothing Then
                    temps.Add(leftTemp)
                    If leftInit IsNot Nothing Then
                        inits.Add(leftInit)
                    End If
                End If
 
                If rightTemp IsNot Nothing Then
                    temps.Add(rightTemp)
                    If rightInit IsNot Nothing Then
                        inits.Add(rightInit)
                    End If
                End If
 
                value = New BoundSequence(node.Syntax,
                                         temps.ToImmutableAndFree,
                                         inits.ToImmutableAndFree,
                                         value,
                                         value.Type)
            End If
 
            Return value
        End Function
 
        Private Function RewriteNullableIsOrIsNotOperator(node As BoundBinaryOperator) As BoundExpression
            Dim left As BoundExpression = node.Left
            Dim right As BoundExpression = node.Right
 
            Debug.Assert(left.IsNothingLiteral OrElse right.IsNothingLiteral)
            Debug.Assert(node.OperatorKind = BinaryOperatorKind.Is OrElse node.OperatorKind = BinaryOperatorKind.IsNot)
 
            If _inExpressionLambda Then
                Return node
            End If
 
            Return RewriteNullableIsOrIsNotOperator((node.OperatorKind And BinaryOperatorKind.OpMask) = BinaryOperatorKind.Is, If(left.IsNothingLiteral, right, left), node.Type)
        End Function
 
        Private Function RewriteNullableIsOrIsNotOperator(isIs As Boolean, operand As BoundExpression, resultType As TypeSymbol) As BoundExpression
            Debug.Assert(resultType.IsBooleanType())
            Debug.Assert(operand.Type.IsNullableType)
 
            If HasNoValue(operand) Then
                Return New BoundLiteral(operand.Syntax,
                                        If(isIs,
                                            ConstantValue.True,
                                            ConstantValue.False),
                                        resultType)
 
            ElseIf HasValue(operand) Then
                Return MakeSequence(operand, New BoundLiteral(operand.Syntax,
                                    If(isIs,
                                        ConstantValue.False,
                                        ConstantValue.True),
                                    resultType))
            Else
 
                Dim whenNotNull As BoundExpression = Nothing
                Dim whenNull As BoundExpression = Nothing
                If IsConditionalAccess(operand, whenNotNull, whenNull) Then
                    If HasNoValue(whenNull) Then
                        Return UpdateConditionalAccess(operand,
                                                       RewriteNullableIsOrIsNotOperator(isIs, whenNotNull, resultType),
                                                       RewriteNullableIsOrIsNotOperator(isIs, whenNull, resultType))
                    End If
                End If
 
                Dim result As BoundExpression
 
                result = NullableHasValue(operand)
                If isIs Then
                    result = New BoundUnaryOperator(result.Syntax,
                                                    UnaryOperatorKind.Not,
                                                    result,
                                                    False,
                                                    resultType)
                End If
 
                Return result
            End If
        End Function
 
        Private Function RewriteLiftedUserDefinedBinaryOperator(node As BoundUserDefinedBinaryOperator) As BoundNode
            '
            ' Lifted user defined operator has structure as the following:
            '            
            '                    |          
            '             [implicit wrap]
            '                    |
            '                  CALL
            '                   /\
            '  [implicit unwrap]   [implicit unwrap]
            '         |                   |
            '       LEFT                RIGHT
            '
            ' Implicit left/right unwrapping conversions if present are always L? -> L and R? -> R
            ' They are encoded as a disparity between CALL argument types and parameter types of the call symbol.
            '
            ' Implicit wrapping conversion of the result, if present, is always T -> T?
            '
            ' The rewrite is:
            '   If (LEFT.HasValue And RIGHT.HasValue, CALL(LEFT, RIGHT), Null)
            '
            ' Note that the result of the operator is nullable type. 
 
            Dim left = Me.VisitExpressionNode(node.Left)
            Dim right = Me.VisitExpressionNode(node.Right)
            Dim operatorCall = node.Call
 
            Dim resultType = operatorCall.Type
 
            Debug.Assert(resultType.IsNullableType())
            Dim whenHasNoValue = NullableNull(node.Syntax, resultType)
 
            Debug.Assert(left.Type.IsNullableType() AndAlso right.Type.IsNullableType(), "left and right must be nullable")
 
            Dim leftHasNoValue As Boolean = HasNoValue(left)
            Dim rightHasNoValue As Boolean = HasNoValue(right)
 
            ' TWO NULLS
            If (leftHasNoValue And rightHasNoValue) Then
                Return whenHasNoValue
            End If
 
            ' ONE NULL
            If (leftHasNoValue Or rightHasNoValue) Then
                Return MakeSequence(If(leftHasNoValue, right, left), whenHasNoValue)
            End If
 
            Dim temps As ArrayBuilder(Of LocalSymbol) = Nothing
            Dim inits As ArrayBuilder(Of BoundExpression) = Nothing
 
            ' PREPARE OPERANDS
            Dim leftHasValue As Boolean = HasValue(left)
            Dim rightHasValue As Boolean = HasValue(right)
 
            Dim leftCallInput As BoundExpression
            Dim rightCallInput As BoundExpression
            Dim condition As BoundExpression = Nothing
 
            If leftHasValue Then
                leftCallInput = NullableValueOrDefault(left)
 
                If rightHasValue Then
                    rightCallInput = NullableValueOrDefault(right)
                Else
                    leftCallInput = CaptureNullableIfNeeded(leftCallInput, temps, inits, doNotCaptureLocals:=True)
                    rightCallInput = ProcessNullableOperand(right, condition, temps, inits, doNotCaptureLocals:=True)
                End If
            ElseIf rightHasValue Then
                leftCallInput = ProcessNullableOperand(left, condition, temps, inits, doNotCaptureLocals:=True)
                rightCallInput = NullableValueOrDefault(right)
                rightCallInput = CaptureNullableIfNeeded(rightCallInput, temps, inits, doNotCaptureLocals:=True)
            Else
                Dim leftHasValueExpression As BoundExpression = Nothing
                Dim rightHasValueExpression As BoundExpression = Nothing
 
                leftCallInput = ProcessNullableOperand(left, leftHasValueExpression, temps, inits, doNotCaptureLocals:=True)
                rightCallInput = ProcessNullableOperand(right, rightHasValueExpression, temps, inits, doNotCaptureLocals:=True)
 
                condition = MakeBooleanBinaryExpression(node.Syntax, BinaryOperatorKind.And, leftHasValueExpression, rightHasValueExpression)
            End If
 
            Debug.Assert(leftCallInput.Type.IsSameTypeIgnoringAll(operatorCall.Method.Parameters(0).Type),
                         "operator must take either unwrapped values or not-nullable left directly")
            Debug.Assert(rightCallInput.Type.IsSameTypeIgnoringAll(operatorCall.Method.Parameters(1).Type),
                         "operator must take either unwrapped values or not-nullable right directly")
 
            Dim whenHasValue As BoundExpression = operatorCall.Update(operatorCall.Method,
                                                                       Nothing,
                                                                       operatorCall.ReceiverOpt,
                                                                       ImmutableArray.Create(Of BoundExpression)(leftCallInput, rightCallInput),
                                                                       Nothing,
                                                                       operatorCall.ConstantValueOpt,
                                                                       isLValue:=operatorCall.IsLValue,
                                                                       suppressObjectClone:=operatorCall.SuppressObjectClone,
                                                                       type:=operatorCall.Method.ReturnType)
 
            If Not whenHasValue.Type.IsSameTypeIgnoringAll(resultType) Then
                whenHasValue = WrapInNullable(whenHasValue, resultType)
            End If
 
            Debug.Assert(whenHasValue.Type.IsSameTypeIgnoringAll(resultType), "result type must be same as resultType")
 
            ' RESULT
 
            If leftHasValue And rightHasValue Then
                Debug.Assert(temps Is Nothing AndAlso inits Is Nothing AndAlso condition Is Nothing)
                Return whenHasValue
 
            Else
                Dim result As BoundExpression = MakeTernaryConditionalExpression(node.Syntax,
                                                               condition,
                                                               whenHasValue,
                                                               whenHasNoValue)
 
                ' if we used a temp, arrange a sequence for it
                If temps IsNot Nothing Then
                    result = New BoundSequence(node.Syntax,
                                         temps.ToImmutableAndFree,
                                         inits.ToImmutableAndFree,
                                         result,
                                         result.Type)
                End If
 
                Return result
            End If
 
        End Function
 
        Private Function ApplyUnliftedBinaryOp(originalOperator As BoundBinaryOperator,
                                               left As BoundExpression,
                                               right As BoundExpression) As BoundExpression
 
            Debug.Assert(Not left.Type.IsNullableType)
            Debug.Assert(Not right.Type.IsNullableType)
 
            'return UnliftedOP(left, right)
            Dim unliftedOpKind = originalOperator.OperatorKind And (Not BinaryOperatorKind.Lifted)
 
            Return MakeBinaryExpression(originalOperator.Syntax,
                                        unliftedOpKind,
                                        left,
                                        right,
                                        originalOperator.Checked,
                                        originalOperator.Type.GetNullableUnderlyingType)
        End Function
    End Class
 
End Namespace