File: Semantics\Operators.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 System.Diagnostics
Imports System.Linq
Imports System.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.Collections
Imports Microsoft.CodeAnalysis.PooledObjects
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports TypeKind = Microsoft.CodeAnalysis.TypeKind
 
Namespace Microsoft.CodeAnalysis.VisualBasic
 
    Partial Friend Class OverloadResolution
 
        ''' <summary>
        ''' A map from Operator name to number of parameters and kind.
        ''' </summary>
        Private Shared ReadOnly s_operatorNames As Dictionary(Of String, OperatorInfo)
 
        ''' <summary>
        ''' Operator kind and expected number of parameters.
        ''' </summary>
        Friend Structure OperatorInfo
            Private ReadOnly _Id As Integer
 
            Public Sub New(op As UnaryOperatorKind)
                _Id = (1 Or (op << 2))
            End Sub
 
            Public Sub New(op As BinaryOperatorKind)
                _Id = (2 Or (op << 2))
            End Sub
 
            Public ReadOnly Property ParamCount As Integer
                Get
                    Return (_Id And 3)
                End Get
            End Property
 
            Public ReadOnly Property IsBinary As Boolean
                Get
                    Return ParamCount = 2
                End Get
            End Property
 
            Public ReadOnly Property IsUnary As Boolean
                Get
                    Return ParamCount = 1
                End Get
            End Property
 
            Public ReadOnly Property UnaryOperatorKind As UnaryOperatorKind
                Get
                    If Not IsUnary Then
                        Return UnaryOperatorKind.Error
                    End If
 
                    Return CType(_Id >> 2, UnaryOperatorKind)
                End Get
            End Property
 
            Public ReadOnly Property BinaryOperatorKind As BinaryOperatorKind
                Get
                    If Not IsBinary Then
                        Return BinaryOperatorKind.Error
                    End If
 
                    Return CType(_Id >> 2, BinaryOperatorKind)
                End Get
            End Property
        End Structure
 
        Friend Shared Function GetOperatorInfo(name As String) As OperatorInfo
            Dim result As OperatorInfo = Nothing
 
            If name.Length > 3 AndAlso IdentifierComparison.Equals("op_", name.Substring(0, 3)) AndAlso s_operatorNames.TryGetValue(name, result) Then
                Return result
            End If
 
            Return Nothing
        End Function
 
        Shared Sub New()
            Dim operators As New Dictionary(Of String, OperatorInfo)(IdentifierComparison.Comparer)
 
            operators.Add(WellKnownMemberNames.OnesComplementOperatorName, New OperatorInfo(UnaryOperatorKind.Not))
            operators.Add(WellKnownMemberNames.TrueOperatorName, New OperatorInfo(UnaryOperatorKind.IsTrue))
            operators.Add(WellKnownMemberNames.FalseOperatorName, New OperatorInfo(UnaryOperatorKind.IsFalse))
            operators.Add(WellKnownMemberNames.UnaryPlusOperatorName, New OperatorInfo(UnaryOperatorKind.Plus))
            operators.Add(WellKnownMemberNames.AdditionOperatorName, New OperatorInfo(BinaryOperatorKind.Add))
            operators.Add(WellKnownMemberNames.UnaryNegationOperatorName, New OperatorInfo(UnaryOperatorKind.Minus))
            operators.Add(WellKnownMemberNames.SubtractionOperatorName, New OperatorInfo(BinaryOperatorKind.Subtract))
            operators.Add(WellKnownMemberNames.MultiplyOperatorName, New OperatorInfo(BinaryOperatorKind.Multiply))
            operators.Add(WellKnownMemberNames.DivisionOperatorName, New OperatorInfo(BinaryOperatorKind.Divide))
            operators.Add(WellKnownMemberNames.IntegerDivisionOperatorName, New OperatorInfo(BinaryOperatorKind.IntegerDivide))
            operators.Add(WellKnownMemberNames.ModulusOperatorName, New OperatorInfo(BinaryOperatorKind.Modulo))
            operators.Add(WellKnownMemberNames.ExponentOperatorName, New OperatorInfo(BinaryOperatorKind.Power))
            operators.Add(WellKnownMemberNames.EqualityOperatorName, New OperatorInfo(BinaryOperatorKind.Equals))
            operators.Add(WellKnownMemberNames.InequalityOperatorName, New OperatorInfo(BinaryOperatorKind.NotEquals))
            operators.Add(WellKnownMemberNames.LessThanOperatorName, New OperatorInfo(BinaryOperatorKind.LessThan))
            operators.Add(WellKnownMemberNames.GreaterThanOperatorName, New OperatorInfo(BinaryOperatorKind.GreaterThan))
            operators.Add(WellKnownMemberNames.LessThanOrEqualOperatorName, New OperatorInfo(BinaryOperatorKind.LessThanOrEqual))
            operators.Add(WellKnownMemberNames.GreaterThanOrEqualOperatorName, New OperatorInfo(BinaryOperatorKind.GreaterThanOrEqual))
            operators.Add(WellKnownMemberNames.LikeOperatorName, New OperatorInfo(BinaryOperatorKind.Like))
            operators.Add(WellKnownMemberNames.ConcatenateOperatorName, New OperatorInfo(BinaryOperatorKind.Concatenate))
            operators.Add(WellKnownMemberNames.BitwiseAndOperatorName, New OperatorInfo(BinaryOperatorKind.And))
            operators.Add(WellKnownMemberNames.BitwiseOrOperatorName, New OperatorInfo(BinaryOperatorKind.Or))
            operators.Add(WellKnownMemberNames.ExclusiveOrOperatorName, New OperatorInfo(BinaryOperatorKind.Xor))
            operators.Add(WellKnownMemberNames.LeftShiftOperatorName, New OperatorInfo(BinaryOperatorKind.LeftShift))
            operators.Add(WellKnownMemberNames.RightShiftOperatorName, New OperatorInfo(BinaryOperatorKind.RightShift))
            operators.Add(WellKnownMemberNames.ImplicitConversionName, New OperatorInfo(UnaryOperatorKind.Implicit))
            operators.Add(WellKnownMemberNames.ExplicitConversionName, New OperatorInfo(UnaryOperatorKind.Explicit))
 
            ' These cannot be declared in source, but can be imported.
            operators.Add(WellKnownMemberNames.LogicalNotOperatorName, New OperatorInfo(UnaryOperatorKind.Not))
            operators.Add(WellKnownMemberNames.LogicalAndOperatorName, New OperatorInfo(BinaryOperatorKind.And))
            operators.Add(WellKnownMemberNames.LogicalOrOperatorName, New OperatorInfo(BinaryOperatorKind.Or))
            operators.Add(WellKnownMemberNames.UnsignedLeftShiftOperatorName, New OperatorInfo(BinaryOperatorKind.LeftShift))
            operators.Add(WellKnownMemberNames.UnsignedRightShiftOperatorName, New OperatorInfo(BinaryOperatorKind.RightShift))
 
            s_operatorNames = operators
        End Sub
 
        Friend Shared Function GetOperatorTokenKind(name As String) As SyntaxKind
            Dim opInfo As OperatorInfo = GetOperatorInfo(name)
            Return GetOperatorTokenKind(opInfo)
        End Function
 
        Friend Shared Function GetOperatorTokenKind(opInfo As OperatorInfo) As SyntaxKind
            If opInfo.IsUnary Then
                Return GetOperatorTokenKind(opInfo.UnaryOperatorKind)
            Else
                Return GetOperatorTokenKind(opInfo.BinaryOperatorKind)
            End If
        End Function
 
        Friend Shared Function GetOperatorTokenKind(op As UnaryOperatorKind) As SyntaxKind
            Select Case op
                Case UnaryOperatorKind.IsFalse
                    Return SyntaxKind.IsFalseKeyword
                Case UnaryOperatorKind.IsTrue
                    Return SyntaxKind.IsTrueKeyword
                Case UnaryOperatorKind.Minus
                    Return SyntaxKind.MinusToken
                Case UnaryOperatorKind.Not
                    Return SyntaxKind.NotKeyword
                Case UnaryOperatorKind.Plus
                    Return SyntaxKind.PlusToken
                Case UnaryOperatorKind.Implicit, UnaryOperatorKind.Explicit
                    Return SyntaxKind.CTypeKeyword
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(op)
            End Select
        End Function
 
        Friend Shared Function GetOperatorTokenKind(op As BinaryOperatorKind) As SyntaxKind
            Select Case op
                Case BinaryOperatorKind.Add
                    Return SyntaxKind.PlusToken
                Case BinaryOperatorKind.Subtract
                    Return SyntaxKind.MinusToken
                Case BinaryOperatorKind.Multiply
                    Return SyntaxKind.AsteriskToken
                Case BinaryOperatorKind.Divide
                    Return SyntaxKind.SlashToken
                Case BinaryOperatorKind.IntegerDivide
                    Return SyntaxKind.BackslashToken
                Case BinaryOperatorKind.Modulo
                    Return SyntaxKind.ModKeyword
                Case BinaryOperatorKind.Power
                    Return SyntaxKind.CaretToken
                Case BinaryOperatorKind.Equals
                    Return SyntaxKind.EqualsToken
                Case BinaryOperatorKind.NotEquals
                    Return SyntaxKind.LessThanGreaterThanToken
                Case BinaryOperatorKind.LessThan
                    Return SyntaxKind.LessThanToken
                Case BinaryOperatorKind.GreaterThan
                    Return SyntaxKind.GreaterThanToken
                Case BinaryOperatorKind.LessThanOrEqual
                    Return SyntaxKind.LessThanEqualsToken
                Case BinaryOperatorKind.GreaterThanOrEqual
                    Return SyntaxKind.GreaterThanEqualsToken
                Case BinaryOperatorKind.Like
                    Return SyntaxKind.LikeKeyword
                Case BinaryOperatorKind.Concatenate
                    Return SyntaxKind.AmpersandToken
                Case BinaryOperatorKind.And
                    Return SyntaxKind.AndKeyword
                Case BinaryOperatorKind.Or
                    Return SyntaxKind.OrKeyword
                Case BinaryOperatorKind.Xor
                    Return SyntaxKind.XorKeyword
                Case BinaryOperatorKind.LeftShift
                    Return SyntaxKind.LessThanLessThanToken
                Case BinaryOperatorKind.RightShift
                    Return SyntaxKind.GreaterThanGreaterThanToken
                Case BinaryOperatorKind.AndAlso
                    Return SyntaxKind.AndAlsoKeyword
                Case BinaryOperatorKind.OrElse
                    Return SyntaxKind.OrElseKeyword
                Case BinaryOperatorKind.Is
                    Return SyntaxKind.IsKeyword
                Case BinaryOperatorKind.IsNot
                    Return SyntaxKind.IsNotKeyword
 
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(op)
            End Select
        End Function
 
        Friend Shared Function TryGetOperatorName(op As BinaryOperatorKind, isChecked As Boolean) As String
 
            Select Case (op And BinaryOperatorKind.OpMask)
                Case BinaryOperatorKind.Add
                    Return If(isChecked, WellKnownMemberNames.CheckedAdditionOperatorName, WellKnownMemberNames.AdditionOperatorName)
                Case BinaryOperatorKind.Concatenate
                    Return WellKnownMemberNames.ConcatenateOperatorName
                Case BinaryOperatorKind.Like
                    Return WellKnownMemberNames.LikeOperatorName
                Case BinaryOperatorKind.Equals
                    Return WellKnownMemberNames.EqualityOperatorName
                Case BinaryOperatorKind.NotEquals
                    Return WellKnownMemberNames.InequalityOperatorName
                Case BinaryOperatorKind.LessThanOrEqual
                    Return WellKnownMemberNames.LessThanOrEqualOperatorName
                Case BinaryOperatorKind.GreaterThanOrEqual
                    Return WellKnownMemberNames.GreaterThanOrEqualOperatorName
                Case BinaryOperatorKind.LessThan
                    Return WellKnownMemberNames.LessThanOperatorName
                Case BinaryOperatorKind.GreaterThan
                    Return WellKnownMemberNames.GreaterThanOperatorName
                Case BinaryOperatorKind.Subtract
                    Return If(isChecked, WellKnownMemberNames.CheckedSubtractionOperatorName, WellKnownMemberNames.SubtractionOperatorName)
                Case BinaryOperatorKind.Multiply
                    Return If(isChecked, WellKnownMemberNames.CheckedMultiplyOperatorName, WellKnownMemberNames.MultiplyOperatorName)
                Case BinaryOperatorKind.Power
                    Return WellKnownMemberNames.ExponentOperatorName
                Case BinaryOperatorKind.Divide
                    Return WellKnownMemberNames.DivisionOperatorName
                Case BinaryOperatorKind.Modulo
                    Return WellKnownMemberNames.ModulusOperatorName
                Case BinaryOperatorKind.IntegerDivide
                    Return If(isChecked, WellKnownMemberNames.CheckedDivisionOperatorName, WellKnownMemberNames.IntegerDivisionOperatorName)
                Case BinaryOperatorKind.LeftShift
                    Return WellKnownMemberNames.LeftShiftOperatorName
                Case BinaryOperatorKind.RightShift
                    Return WellKnownMemberNames.RightShiftOperatorName
                Case BinaryOperatorKind.Xor
                    Return WellKnownMemberNames.ExclusiveOrOperatorName
                Case BinaryOperatorKind.Or
                    Return WellKnownMemberNames.BitwiseOrOperatorName
                Case BinaryOperatorKind.And
                    Return WellKnownMemberNames.BitwiseAndOperatorName
 
                Case Else
                    Return Nothing
                    'Case BinaryOperatorKind.OrElse
                    'Case BinaryOperatorKind.AndAlso
                    'Case BinaryOperatorKind.Is
                    'Case BinaryOperatorKind.IsNot
            End Select
        End Function
 
        Friend Shared Function TryGetOperatorName(op As UnaryOperatorKind, isChecked As Boolean) As String
 
            Select Case (op And UnaryOperatorKind.OpMask)
                Case UnaryOperatorKind.Plus
                    Return WellKnownMemberNames.UnaryPlusOperatorName
                Case UnaryOperatorKind.Minus
                    Return If(isChecked, WellKnownMemberNames.CheckedUnaryNegationOperatorName, WellKnownMemberNames.UnaryNegationOperatorName)
                Case UnaryOperatorKind.Not
                    Return WellKnownMemberNames.OnesComplementOperatorName
                Case UnaryOperatorKind.Implicit
                    Return WellKnownMemberNames.ImplicitConversionName
                Case UnaryOperatorKind.Explicit
                    Return WellKnownMemberNames.ExplicitConversionName
                Case UnaryOperatorKind.IsTrue
                    Return WellKnownMemberNames.TrueOperatorName
                Case UnaryOperatorKind.IsFalse
                    Return WellKnownMemberNames.FalseOperatorName
 
                Case Else
                    Return Nothing
            End Select
        End Function
 
        Friend Shared Function ValidateOverloadedOperator(
            method As MethodSymbol,
            opInfo As OperatorInfo
        ) As Boolean
            Return ValidateOverloadedOperator(method, opInfo, diagnosticsOpt:=Nothing, assemblyBeingBuiltOpt:=Nothing)
        End Function
 
        Friend Shared Function ValidateOverloadedOperator(
            method As MethodSymbol,
            opInfo As OperatorInfo,
            diagnosticsOpt As BindingDiagnosticBag,
            assemblyBeingBuiltOpt As AssemblySymbol
        ) As Boolean
            Debug.Assert(method.IsMethodKindBasedOnSyntax OrElse diagnosticsOpt Is Nothing)
            Debug.Assert(opInfo.ParamCount <> 0)
            Debug.Assert(diagnosticsOpt Is Nothing OrElse assemblyBeingBuiltOpt IsNot Nothing)
 
            If method.ParameterCount <> opInfo.ParamCount Then
                Return False
            End If
 
            Dim result As Boolean = True
            Dim containingType As NamedTypeSymbol = method.ContainingType
            Dim targetsContainingType As Boolean = False
            Dim targetMismatchError As ERRID
            Dim isConversion As Boolean = False
            Dim useSiteInfo = If(diagnosticsOpt IsNot Nothing, New CompoundUseSiteInfo(Of AssemblySymbol)(diagnosticsOpt, assemblyBeingBuiltOpt), CompoundUseSiteInfo(Of AssemblySymbol).Discarded)
 
            If opInfo.IsUnary Then
                Select Case opInfo.UnaryOperatorKind
                    Case UnaryOperatorKind.Implicit, UnaryOperatorKind.Explicit
                        isConversion = True
                        targetMismatchError = ERRID.ERR_ConvParamMustBeContainingType1
                        If OverloadedOperatorTargetsContainingType(containingType, method.ReturnType) Then
                            targetsContainingType = True
                        End If
                    Case Else
                        targetMismatchError = ERRID.ERR_UnaryParamMustBeContainingType1
 
                        If Not method.ReturnType.IsBooleanType() Then
                            Select Case opInfo.UnaryOperatorKind
                                Case UnaryOperatorKind.IsTrue
                                    If diagnosticsOpt IsNot Nothing Then
                                        diagnosticsOpt.Add(ErrorFactory.ErrorInfo(ERRID.ERR_OperatorRequiresBoolReturnType1, SyntaxFacts.GetText(SyntaxKind.IsTrueKeyword)), method.Locations(0))
                                        result = False
                                    Else
                                        Return False
                                    End If
                                Case UnaryOperatorKind.IsFalse
                                    If diagnosticsOpt IsNot Nothing Then
                                        diagnosticsOpt.Add(ErrorFactory.ErrorInfo(ERRID.ERR_OperatorRequiresBoolReturnType1, SyntaxFacts.GetText(SyntaxKind.IsFalseKeyword)), method.Locations(0))
                                        result = False
                                    Else
                                        Return False
                                    End If
                            End Select
                        End If
 
                End Select
 
            Else
                Debug.Assert(opInfo.IsBinary)
                targetMismatchError = ERRID.ERR_BinaryParamMustBeContainingType1
 
                Select Case opInfo.BinaryOperatorKind
                    Case BinaryOperatorKind.LeftShift, BinaryOperatorKind.RightShift
                        If method.Parameters(1).Type.GetNullableUnderlyingTypeOrSelf().SpecialType <> Microsoft.CodeAnalysis.SpecialType.System_Int32 Then
                            If diagnosticsOpt IsNot Nothing Then
                                diagnosticsOpt.Add(ErrorFactory.ErrorInfo(ERRID.ERR_OperatorRequiresIntegerParameter1,
                                                                       SyntaxFacts.GetText(If(opInfo.BinaryOperatorKind = BinaryOperatorKind.LeftShift,
                                                                                              SyntaxKind.LessThanLessThanToken,
                                                                                              SyntaxKind.GreaterThanGreaterThanToken))), method.Locations(0))
                                result = False
                            Else
                                Return False
                            End If
                        End If
                End Select
            End If
 
            For Each param In method.Parameters
                If OverloadedOperatorTargetsContainingType(containingType, param.Type) Then
                    targetsContainingType = True
                End If
 
                If param.IsByRef Then
                    If diagnosticsOpt IsNot Nothing Then
                        ' Diagnostic has been reported when we were interpreting parameter's modifiers.
                        Debug.Assert(Not method.IsMethodKindBasedOnSyntax)
                        result = False
                    Else
                        Return False
                    End If
                End If
            Next
 
            If Not targetsContainingType Then
                If diagnosticsOpt IsNot Nothing Then
                    diagnosticsOpt.Add(ErrorFactory.ErrorInfo(targetMismatchError, method.ContainingSymbol), method.Locations(0))
                    result = False
                Else
                    Return False
                End If
            ElseIf isConversion Then
                ' Conversion operators specific checks
                Dim sourceType As TypeSymbol = method.Parameters(0).Type
                Dim targetType As TypeSymbol = method.ReturnType
 
                If sourceType.IsObjectType() Then
                    If diagnosticsOpt IsNot Nothing Then
                        diagnosticsOpt.Add(ErrorFactory.ErrorInfo(ERRID.ERR_ConversionFromObject), method.Locations(0))
                        result = False
                    Else
                        Return False
                    End If
                ElseIf targetType.IsObjectType() Then
                    If diagnosticsOpt IsNot Nothing Then
                        diagnosticsOpt.Add(ErrorFactory.ErrorInfo(ERRID.ERR_ConversionToObject), method.Locations(0))
                        result = False
                    Else
                        Return False
                    End If
                ElseIf sourceType.IsInterfaceType() Then
                    If diagnosticsOpt IsNot Nothing Then
                        diagnosticsOpt.Add(ErrorFactory.ErrorInfo(ERRID.ERR_ConversionFromInterfaceType), method.Locations(0))
                        result = False
                    Else
                        Return False
                    End If
                ElseIf targetType.IsInterfaceType() Then
                    If diagnosticsOpt IsNot Nothing Then
                        diagnosticsOpt.Add(ErrorFactory.ErrorInfo(ERRID.ERR_ConversionToInterfaceType), method.Locations(0))
                        result = False
                    Else
                        Return False
                    End If
                ElseIf If(containingType.SpecialType = SpecialType.System_Nullable_T,
                          sourceType Is targetType,
                          sourceType.GetNullableUnderlyingTypeOrSelf() Is targetType.GetNullableUnderlyingTypeOrSelf()) Then
                    If diagnosticsOpt IsNot Nothing Then
                        diagnosticsOpt.Add(ErrorFactory.ErrorInfo(ERRID.ERR_ConversionToSameType), method.Locations(0))
                        result = False
                    Else
                        Return False
                    End If
                ElseIf (sourceType.Kind = SymbolKind.NamedType OrElse sourceType.Kind = SymbolKind.TypeParameter) AndAlso
                       (targetType.Kind = SymbolKind.NamedType OrElse targetType.Kind = SymbolKind.TypeParameter) Then
                    If Conversions.HasWideningDirectCastConversionButNotEnumTypeConversion(targetType, sourceType, useSiteInfo) Then
                        If diagnosticsOpt IsNot Nothing Then
                            diagnosticsOpt.Add(ErrorFactory.ErrorInfo(If(targetType Is method.ContainingSymbol,
                                                                      ERRID.ERR_ConversionFromBaseType,
                                                                      ERRID.ERR_ConversionToDerivedType)), method.Locations(0))
                            result = False
                        Else
                            Return False
                        End If
                    ElseIf Conversions.HasWideningDirectCastConversionButNotEnumTypeConversion(sourceType, targetType, useSiteInfo) Then
                        If diagnosticsOpt IsNot Nothing Then
                            diagnosticsOpt.Add(ErrorFactory.ErrorInfo(If(targetType Is method.ContainingSymbol,
                                                                      ERRID.ERR_ConversionFromDerivedType,
                                                                      ERRID.ERR_ConversionToBaseType)), method.Locations(0))
                            result = False
                        Else
                            Return False
                        End If
                    End If
                End If
            End If
 
            If result AndAlso diagnosticsOpt IsNot Nothing Then
                diagnosticsOpt.Add(method.Locations(0), useSiteInfo)
            End If
 
            Return result
        End Function
 
        Private Shared Function OverloadedOperatorTargetsContainingType(containingType As NamedTypeSymbol, typeFromSignature As TypeSymbol) As Boolean
            If containingType.SpecialType = SpecialType.System_Nullable_T Then
                Return typeFromSignature Is containingType
            Else
                Return TypeSymbol.Equals(typeFromSignature.GetNullableUnderlyingTypeOrSelf().GetTupleUnderlyingTypeOrSelf(), containingType.GetTupleUnderlyingTypeOrSelf(), TypeCompareKind.ConsiderEverything)
            End If
        End Function
 
        Public Shared Function MapUnaryOperatorKind(opCode As SyntaxKind) As UnaryOperatorKind
            Dim result As UnaryOperatorKind
 
            Select Case opCode
                Case SyntaxKind.UnaryPlusExpression
                    result = UnaryOperatorKind.Plus
                Case SyntaxKind.UnaryMinusExpression
                    result = UnaryOperatorKind.Minus
                Case SyntaxKind.NotExpression
                    result = UnaryOperatorKind.Not
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(opCode)
            End Select
 
            Return result
        End Function
 
        ''' <summary>
        ''' Returns UnaryOperatorKind.Error in case of error, otherwise adjusted operator kind.
        ''' </summary>
        Public Shared Function ResolveUnaryOperator(
            opCode As UnaryOperatorKind,
            operand As BoundExpression,
            binder As Binder,
            <Out()> ByRef intrinsicOperatorType As SpecialType,
            <Out()> ByRef userDefinedOperator As OverloadResolutionResult,
            <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
        ) As UnaryOperatorKind
            Debug.Assert((opCode And UnaryOperatorKind.IntrinsicOpMask) = opCode AndAlso opCode <> UnaryOperatorKind.Error)
 
            opCode = (opCode And UnaryOperatorKind.IntrinsicOpMask)
            intrinsicOperatorType = SpecialType.None
            userDefinedOperator = Nothing
 
            Dim operandType = operand.Type
 
            ' First, dig through Nullable
            Dim nullableUnderlying = operandType.GetNullableUnderlyingTypeOrSelf()
            Dim operandIsNullable = (operandType IsNot nullableUnderlying)
 
            ' Now dig through Enum
            Dim enumUnderlying = nullableUnderlying.GetEnumUnderlyingTypeOrSelf()
            Dim operandIsEnum = (enumUnderlying IsNot nullableUnderlying)
 
            ' Filter out unexpected underlying types for Nullable and enum types
            If (operandIsEnum OrElse operandIsNullable) AndAlso
                (enumUnderlying.IsStringType() OrElse enumUnderlying.IsObjectType()) Then
                Return UnaryOperatorKind.Error
            End If
 
            Dim sourceType = enumUnderlying
 
            If sourceType.SpecialType <> SpecialType.System_Object AndAlso
               Not sourceType.IsIntrinsicType() Then
 
                If operandType.CanContainUserDefinedOperators(useSiteInfo) Then
                    userDefinedOperator = ResolveUserDefinedUnaryOperator(operand, opCode, binder, includeEliminatedCandidates:=False, useSiteInfo:=useSiteInfo)
 
                    If Not userDefinedOperator.BestResult.HasValue AndAlso userDefinedOperator.Candidates.Length = 0 Then
                        userDefinedOperator = ResolveUserDefinedUnaryOperator(operand, opCode, binder, includeEliminatedCandidates:=True, useSiteInfo:=useSiteInfo)
 
                        If userDefinedOperator.Candidates.Length = 0 Then
                            Return UnaryOperatorKind.Error
                        End If
                    End If
 
                    Return UnaryOperatorKind.UserDefined
                End If
 
                Return UnaryOperatorKind.Error
            End If
 
            Dim result As UnaryOperatorKind = UnaryOperatorKind.Error
 
            If operandIsEnum AndAlso opCode = UnaryOperatorKind.Not AndAlso sourceType.IsIntegralType() Then
                '§11.17 Logical Operators
                'The enumerated type operators do the bitwise operation on the underlying type of 
                'the enumerated type, but the return value is the enumerated type.
                result = UnaryOperatorKind.Not
            Else
                intrinsicOperatorType = ResolveNotLiftedIntrinsicUnaryOperator(opCode, sourceType.SpecialType)
 
                If intrinsicOperatorType <> SpecialType.None Then
                    result = opCode
                End If
            End If
 
            If result <> UnaryOperatorKind.Error AndAlso operandIsNullable Then
                result = result Or UnaryOperatorKind.Lifted
            End If
 
            Return result
        End Function
 
        ''' <summary>
        ''' Returns result type of the operator or SpecialType.None if operator is not supported.
        ''' </summary>
        Friend Shared Function ResolveNotLiftedIntrinsicUnaryOperator(
            opCode As UnaryOperatorKind,
            operandSpecialType As SpecialType
        ) As SpecialType
 
            Dim intrinsicOperatorType As SpecialType
 
            Select Case opCode
                Case UnaryOperatorKind.Not
 
                    '§11.17 Logical Operators
                    'Bo	SB	By	Sh	US	In	UI	Lo	UL	De	Si	Do	Da	Ch	St	Ob
                    'Bo	SB	By	Sh	US	In	UI	Lo	UL	Lo	Lo	Lo	Err	Err	Lo	Ob
 
                    Select Case operandSpecialType
                        Case SpecialType.System_Boolean,
                             SpecialType.System_SByte,
                             SpecialType.System_Byte,
                             SpecialType.System_Int16,
                             SpecialType.System_UInt16,
                             SpecialType.System_Int32,
                             SpecialType.System_UInt32,
                             SpecialType.System_Int64,
                             SpecialType.System_UInt64,
                             SpecialType.System_Object
 
                            intrinsicOperatorType = operandSpecialType
 
                        Case SpecialType.System_Decimal,
                             SpecialType.System_Single,
                             SpecialType.System_Double,
                             SpecialType.System_String
 
                            intrinsicOperatorType = SpecialType.System_Int64
 
                        Case Else
                            intrinsicOperatorType = SpecialType.None
                    End Select
 
                Case UnaryOperatorKind.Plus
 
                    '§11.13.1 Unary Plus Operator
                    'Bo	SB	By	Sh	US	In	UI	Lo	UL	De	Si	Do	Da	Ch	St	Ob
                    'Sh	SB	By	Sh	US	In	UI	Lo	UL	De	Si	Do	Err	Err	Do	Ob
 
                    Select Case operandSpecialType
                        Case SpecialType.System_Boolean
                            intrinsicOperatorType = SpecialType.System_Int16
 
                        Case SpecialType.System_SByte,
                             SpecialType.System_Byte,
                             SpecialType.System_Int16,
                             SpecialType.System_UInt16,
                             SpecialType.System_Int32,
                             SpecialType.System_UInt32,
                             SpecialType.System_Int64,
                             SpecialType.System_UInt64,
                             SpecialType.System_Decimal,
                             SpecialType.System_Single,
                             SpecialType.System_Double,
                             SpecialType.System_Object
 
                            intrinsicOperatorType = operandSpecialType
 
                        Case SpecialType.System_String
                            intrinsicOperatorType = SpecialType.System_Double
 
                        Case Else
                            intrinsicOperatorType = SpecialType.None
                    End Select
 
                Case UnaryOperatorKind.Minus
 
                    '§11.13.2 Unary Minus Operator
                    'Bo	SB	By	Sh	US	In	UI	Lo	UL	De	Si	Do	Da	Ch	St	Ob
                    'Sh	SB	Sh	Sh	In	In	Lo	Lo	De	De	Si	Do	Err	Err	Do	Ob
 
                    Select Case operandSpecialType
                        Case SpecialType.System_Boolean,
                             SpecialType.System_Byte
 
                            intrinsicOperatorType = SpecialType.System_Int16
 
                        Case SpecialType.System_SByte,
                             SpecialType.System_Int16,
                             SpecialType.System_Int32,
                             SpecialType.System_Int64,
                             SpecialType.System_Decimal,
                             SpecialType.System_Single,
                             SpecialType.System_Double,
                             SpecialType.System_Object
 
                            intrinsicOperatorType = operandSpecialType
 
                        Case SpecialType.System_UInt16
                            intrinsicOperatorType = SpecialType.System_Int32
 
                        Case SpecialType.System_UInt32
                            intrinsicOperatorType = SpecialType.System_Int64
 
                        Case SpecialType.System_UInt64
                            intrinsicOperatorType = SpecialType.System_Decimal
 
                        Case SpecialType.System_String
                            intrinsicOperatorType = SpecialType.System_Double
 
                        Case Else
                            intrinsicOperatorType = SpecialType.None
                    End Select
 
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(opCode)
            End Select
 
            Return intrinsicOperatorType
        End Function
 
        ''' <summary>
        ''' Attempts to fold unary operator applied to a constant expression. 
        ''' 
        ''' Returns Nothing if operator cannot be folded.
        ''' 
        ''' If folding failed due to non-integer overflow, ConstantValue.Bad is returned. Consumer 
        ''' is responsible for reporting appropriate diagnostics.
        ''' 
        ''' If integer overflow occurs, integerOverflow is set to True and ConstantValue for overflowed result is returned. 
        ''' Consumer is responsible for reporting appropriate diagnostics and potentially discarding the result.
        ''' </summary>
        Public Shared Function TryFoldConstantUnaryOperator(
            op As UnaryOperatorKind,
            operand As BoundExpression,
            resultType As TypeSymbol,
            ByRef integerOverflow As Boolean
        ) As ConstantValue
 
            Debug.Assert(operand IsNot Nothing)
            Debug.Assert(resultType IsNot Nothing)
 
            integerOverflow = False
 
            Dim operandValue As ConstantValue = operand.ConstantValueOpt
 
            If operandValue Is Nothing OrElse operandValue.IsBad OrElse resultType.IsErrorType() Then
                ' Not a constant
                Return Nothing
            End If
 
            Dim operandType = operand.Type
            Dim result As ConstantValue = Nothing
            Dim underlyingResultType = resultType.GetEnumUnderlyingTypeOrSelf()
 
            If operandType.AllowsCompileTimeOperations() AndAlso
               underlyingResultType.AllowsCompileTimeOperations() Then
 
                Debug.Assert(underlyingResultType.IsValidForConstantValue(operandValue))
 
                ' Attempt folding
                If underlyingResultType.IsIntegralType() Then
                    Dim value As Int64 = GetConstantValueAsInt64(operandValue)
 
                    Select Case (op And UnaryOperatorKind.IntrinsicOpMask)
                        Case UnaryOperatorKind.Plus
                            ' Nothing to do
                        Case UnaryOperatorKind.Minus
                            Debug.Assert(Not underlyingResultType.IsUnsignedIntegralType())
                            If value = Int64.MinValue Then
                                integerOverflow = True
                            End If
 
                            value = -value
 
                        Case UnaryOperatorKind.Not
                            value = Not value
 
                        Case Else
                            Throw ExceptionUtilities.UnexpectedValue(op)
                    End Select
 
                    Dim detectedIntegerOverflow = integerOverflow
                    Dim discriminator = underlyingResultType.GetConstantValueTypeDiscriminator()
 
                    result = GetConstantValue(discriminator, NarrowIntegralResult(value, discriminator, discriminator, integerOverflow))
 
                    integerOverflow = (op And UnaryOperatorKind.IntrinsicOpMask) = UnaryOperatorKind.Minus AndAlso (integerOverflow OrElse detectedIntegerOverflow)
 
                ElseIf underlyingResultType.IsFloatingType Then
                    Dim value As Double = If(underlyingResultType.IsSingleType(), operandValue.SingleValue, operandValue.DoubleValue)
 
                    Select Case (op And UnaryOperatorKind.IntrinsicOpMask)
                        Case UnaryOperatorKind.Plus
                            ' Nothing to do
                        Case UnaryOperatorKind.Minus
                            value = -value
 
                        Case Else
                            Throw ExceptionUtilities.UnexpectedValue(op)
                    End Select
 
                    Dim overflow As Boolean = False
                    value = NarrowFloatingResult(value, underlyingResultType.GetConstantValueTypeDiscriminator(), overflow)
 
                    ' We have decided to ignore overflows in compile-time evaluation
                    ' of floating expressions.
 
                    If underlyingResultType.IsSingleType() Then
                        result = ConstantValue.Create(CType(value, Single))
                    Else
                        result = ConstantValue.Create(value)
                    End If
 
                ElseIf underlyingResultType.IsDecimalType() Then
                    Dim value As Decimal = operandValue.DecimalValue
 
                    Select Case (op And UnaryOperatorKind.IntrinsicOpMask)
                        Case UnaryOperatorKind.Plus
                            ' Nothing to do
                        Case UnaryOperatorKind.Minus
                            value = Decimal.Negate(value)
 
                        Case Else
                            Throw ExceptionUtilities.UnexpectedValue(op)
                    End Select
 
                    result = ConstantValue.Create(value)
 
                ElseIf underlyingResultType.IsBooleanType() Then
 
                    Debug.Assert((op And UnaryOperatorKind.IntrinsicOpMask) = UnaryOperatorKind.Not)
                    result = ConstantValue.Create(Not operandValue.BooleanValue)
                End If
 
            End If
 
            Return result
        End Function
 
        Public Shared Function MapBinaryOperatorKind(opCode As SyntaxKind) As BinaryOperatorKind
            Dim result As BinaryOperatorKind
 
            Select Case opCode
                Case SyntaxKind.AddExpression : result = BinaryOperatorKind.Add
                Case SyntaxKind.ConcatenateExpression : result = BinaryOperatorKind.Concatenate
                Case SyntaxKind.LikeExpression : result = BinaryOperatorKind.Like
                Case SyntaxKind.EqualsExpression : result = BinaryOperatorKind.Equals
                Case SyntaxKind.NotEqualsExpression : result = BinaryOperatorKind.NotEquals
                Case SyntaxKind.LessThanOrEqualExpression : result = BinaryOperatorKind.LessThanOrEqual
                Case SyntaxKind.GreaterThanOrEqualExpression : result = BinaryOperatorKind.GreaterThanOrEqual
                Case SyntaxKind.LessThanExpression : result = BinaryOperatorKind.LessThan
                Case SyntaxKind.GreaterThanExpression : result = BinaryOperatorKind.GreaterThan
                Case SyntaxKind.SubtractExpression : result = BinaryOperatorKind.Subtract
                Case SyntaxKind.MultiplyExpression : result = BinaryOperatorKind.Multiply
                Case SyntaxKind.ExponentiateExpression : result = BinaryOperatorKind.Power
                Case SyntaxKind.DivideExpression : result = BinaryOperatorKind.Divide
                Case SyntaxKind.ModuloExpression : result = BinaryOperatorKind.Modulo
                Case SyntaxKind.IntegerDivideExpression : result = BinaryOperatorKind.IntegerDivide
                Case SyntaxKind.LeftShiftExpression : result = BinaryOperatorKind.LeftShift
                Case SyntaxKind.RightShiftExpression : result = BinaryOperatorKind.RightShift
                Case SyntaxKind.ExclusiveOrExpression : result = BinaryOperatorKind.Xor
                Case SyntaxKind.OrExpression : result = BinaryOperatorKind.Or
                Case SyntaxKind.OrElseExpression : result = BinaryOperatorKind.OrElse
                Case SyntaxKind.AndExpression : result = BinaryOperatorKind.And
                Case SyntaxKind.AndAlsoExpression : result = BinaryOperatorKind.AndAlso
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(opCode)
            End Select
 
            Return result
        End Function
 
        ''' <summary>
        ''' Returns UnaryOperatorKind.Error in case of error, otherwise adjusted operator kind.
        ''' 
        ''' For comparison operators, the operator type computed here is not
        ''' the result type of the comparison (which is typically boolean),
        ''' but is the type to which the operands are to be converted. For
        ''' other operators, the type computed here is both the result type
        ''' and the common operand type.
        ''' </summary>
        Public Shared Function ResolveBinaryOperator(
            opCode As BinaryOperatorKind,
            left As BoundExpression,
            right As BoundExpression,
            binder As Binder,
            considerUserDefinedOrLateBound As Boolean,
            <Out()> ByRef intrinsicOperatorType As SpecialType,
            <Out()> ByRef userDefinedOperator As OverloadResolutionResult,
            <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
        ) As BinaryOperatorKind
 
            Debug.Assert((opCode And BinaryOperatorKind.OpMask) = opCode AndAlso opCode <> BinaryOperatorKind.Error)
 
            opCode = (opCode And BinaryOperatorKind.OpMask)
            intrinsicOperatorType = SpecialType.None
            userDefinedOperator = Nothing
 
            Dim leftType As TypeSymbol = left.Type
            Dim rightType As TypeSymbol = right.Type
 
            ' First, dig through Nullable
            Dim leftNullableUnderlying = leftType.GetNullableUnderlyingTypeOrSelf()
            Dim leftIsNullable = (leftType IsNot leftNullableUnderlying)
            Dim rightNullableUnderlying = rightType.GetNullableUnderlyingTypeOrSelf()
            Dim rightIsNullable = (rightType IsNot rightNullableUnderlying)
 
            ' Now dig through Enum
            Dim leftEnumUnderlying = leftNullableUnderlying.GetEnumUnderlyingTypeOrSelf()
            Dim leftIsEnum = (leftEnumUnderlying IsNot leftNullableUnderlying)
            Dim rightEnumUnderlying = rightNullableUnderlying.GetEnumUnderlyingTypeOrSelf()
            Dim rightIsEnum = (rightEnumUnderlying IsNot rightNullableUnderlying)
 
            ' Filter out unexpected underlying types for Nullable and enum types
            If ((leftIsEnum OrElse leftIsNullable) AndAlso
                (leftEnumUnderlying.IsStringType() OrElse leftEnumUnderlying.IsObjectType() OrElse leftEnumUnderlying.IsCharSZArray())) OrElse
               ((rightIsEnum OrElse rightIsNullable) AndAlso
                (rightEnumUnderlying.IsStringType() OrElse rightEnumUnderlying.IsObjectType() OrElse rightEnumUnderlying.IsCharSZArray())) Then
                Return BinaryOperatorKind.Error
            End If
 
            If UseUserDefinedBinaryOperators(opCode, leftType, rightType) Then
 
                If considerUserDefinedOrLateBound Then
                    If leftType.CanContainUserDefinedOperators(useSiteInfo) OrElse rightType.CanContainUserDefinedOperators(useSiteInfo) OrElse
                       (opCode = BinaryOperatorKind.Subtract AndAlso
                        leftType.GetNullableUnderlyingTypeOrSelf().IsDateTimeType() AndAlso
                        rightType.GetNullableUnderlyingTypeOrSelf().IsDateTimeType()) Then ' Let (Date - Date) use operator overloading.
 
                        userDefinedOperator = ResolveUserDefinedBinaryOperator(left, right, opCode, binder, includeEliminatedCandidates:=False, useSiteInfo:=useSiteInfo)
 
                        If userDefinedOperator.ResolutionIsLateBound Then
                            intrinsicOperatorType = SpecialType.System_Object
                            Return opCode
                        ElseIf Not userDefinedOperator.BestResult.HasValue AndAlso userDefinedOperator.Candidates.Length = 0 Then
                            userDefinedOperator = ResolveUserDefinedBinaryOperator(left, right, opCode, binder, includeEliminatedCandidates:=True, useSiteInfo:=CompoundUseSiteInfo(Of AssemblySymbol).Discarded)
 
                            If userDefinedOperator.Candidates.Length = 0 Then
                                Return BinaryOperatorKind.Error
                            End If
                        End If
 
                        Return BinaryOperatorKind.UserDefined
                    Else
                        ' An operator with an Object operand and a Type Parameter operand
                        ' is latebound if the Type Parameter has no class constraint.
                        Dim latebound As Boolean = False
                        If leftType.IsObjectType() Then
                            If rightType.IsTypeParameter() AndAlso DirectCast(rightType, TypeParameterSymbol).GetNonInterfaceConstraint(useSiteInfo) Is Nothing Then
                                latebound = True
                            End If
                        ElseIf rightType.IsObjectType() Then
                            If leftType.IsTypeParameter() AndAlso DirectCast(leftType, TypeParameterSymbol).GetNonInterfaceConstraint(useSiteInfo) Is Nothing Then
                                latebound = True
                            End If
                        End If
 
                        If latebound Then
                            intrinsicOperatorType = SpecialType.System_Object
                            Return opCode
                        End If
                    End If
                End If
 
                Return BinaryOperatorKind.Error
            End If
 
            Dim result As BinaryOperatorKind = BinaryOperatorKind.Error
 
            If leftIsEnum AndAlso rightIsEnum AndAlso
               (opCode = BinaryOperatorKind.Xor OrElse opCode = BinaryOperatorKind.And OrElse opCode = BinaryOperatorKind.Or) AndAlso
               leftNullableUnderlying.IsSameTypeIgnoringAll(rightNullableUnderlying) Then
                '§11.17 Logical Operators
                'The enumerated type operators do the bitwise operation on the underlying 
                'type of the enumerated type, but the return value is the enumerated type.
                result = opCode
 
                If leftIsNullable OrElse rightIsNullable Then
                    result = result Or BinaryOperatorKind.Lifted
                End If
            Else
                Dim leftSpecialType = leftEnumUnderlying.SpecialType
                Dim rightSpecialType = rightEnumUnderlying.SpecialType
 
                ' Operands of type 1-dimensional array of Char are treated as if they
                ' were of type String.
                If leftSpecialType = SpecialType.None AndAlso leftEnumUnderlying.IsCharSZArray() Then
                    leftSpecialType = SpecialType.System_String
                End If
 
                If rightSpecialType = SpecialType.None AndAlso rightEnumUnderlying.IsCharSZArray() Then
                    rightSpecialType = SpecialType.System_String
                End If
 
                intrinsicOperatorType = ResolveNotLiftedIntrinsicBinaryOperator(opCode, leftSpecialType, rightSpecialType)
 
                If intrinsicOperatorType <> SpecialType.None Then
                    result = opCode
                End If
 
                ' Like and '&' don't have lifted form.
                ' Disallow nullable lifting if result is not a value type
                If result <> BinaryOperatorKind.Error AndAlso
                   (leftIsNullable OrElse rightIsNullable) AndAlso
                   intrinsicOperatorType <> SpecialType.None AndAlso
                   intrinsicOperatorType <> SpecialType.System_String AndAlso
                   intrinsicOperatorType <> SpecialType.System_Object AndAlso
                    opCode <> BinaryOperatorKind.Concatenate AndAlso opCode <> BinaryOperatorKind.Like Then
                    result = result Or BinaryOperatorKind.Lifted
                End If
            End If
 
            Return result
        End Function
 
        Public Shared Function UseUserDefinedBinaryOperators(opCode As BinaryOperatorKind, leftType As TypeSymbol, rightType As TypeSymbol) As Boolean
            Dim leftEnumUnderlying = leftType.GetNullableUnderlyingTypeOrSelf().GetEnumUnderlyingTypeOrSelf()
            Dim rightEnumUnderlying = rightType.GetNullableUnderlyingTypeOrSelf().GetEnumUnderlyingTypeOrSelf()
 
            ' Operands of type 1-dimensional array of Char are treated as if they
            ' were of type String.
            If (leftEnumUnderlying.SpecialType <> SpecialType.System_Object AndAlso
                   Not leftEnumUnderlying.IsIntrinsicType() AndAlso
                   Not leftEnumUnderlying.IsCharSZArray()) OrElse
               (rightEnumUnderlying.SpecialType <> SpecialType.System_Object AndAlso
                   Not rightEnumUnderlying.IsIntrinsicType() AndAlso
                   Not rightEnumUnderlying.IsCharSZArray()) OrElse
               (leftEnumUnderlying.IsDateTimeType() AndAlso rightEnumUnderlying.IsDateTimeType() AndAlso
                   opCode = BinaryOperatorKind.Subtract) Then ' Let (Date - Date) use operator overloading.
                Return True
            End If
 
            Return False
        End Function
 
        ''' <summary>
        ''' Attempts to fold binary operator applied to constant expressions. 
        ''' 
        ''' Returns Nothing if operator cannot be folded.
        ''' 
        ''' If folding failed due to non-integer overflow or divide by zero, 
        ''' ConstantValue.Bad is returned. Consumer is responsible for reporting appropriate diagnostics.
        ''' 
        ''' If divide by zero occurs, divideByZero is set to True.
        ''' 
        ''' If integer overflow occurs, integerOverflow is set to True and ConstantValue for overflowed result is returned. 
        ''' Consumer is responsible for reporting appropriate diagnostics and potentially discarding the result.
        ''' </summary>
        Public Shared Function TryFoldConstantBinaryOperator(
            operatorKind As BinaryOperatorKind,
            left As BoundExpression,
            right As BoundExpression,
            resultType As TypeSymbol,
            ByRef integerOverflow As Boolean,
            ByRef divideByZero As Boolean,
            ByRef lengthOutOfLimit As Boolean
        ) As ConstantValue
 
            Debug.Assert(left IsNot Nothing)
            Debug.Assert(right IsNot Nothing)
            Debug.Assert(resultType IsNot Nothing)
 
            integerOverflow = False
            divideByZero = False
            lengthOutOfLimit = False
 
            Dim leftConstantValue As ConstantValue = left.ConstantValueOpt
            Dim rightConstantValue As ConstantValue = right.ConstantValueOpt
 
            If leftConstantValue Is Nothing OrElse leftConstantValue.IsBad OrElse
               rightConstantValue Is Nothing OrElse rightConstantValue.IsBad OrElse
               resultType.IsErrorType() Then
                ' Not a constant
                Return Nothing
            End If
 
            Dim leftType = left.Type
            Dim rightType = right.Type
            Dim op As BinaryOperatorKind = (operatorKind And BinaryOperatorKind.OpMask)
 
            Dim result As ConstantValue = Nothing
 
            If op <> BinaryOperatorKind.Like AndAlso
                (operatorKind And BinaryOperatorKind.CompareText) = 0 AndAlso
                leftType.AllowsCompileTimeOperations() AndAlso
                rightType.AllowsCompileTimeOperations() AndAlso
                resultType.AllowsCompileTimeOperations() Then
 
                Debug.Assert(leftType.IsSameTypeIgnoringAll(rightType) OrElse
                             op = BinaryOperatorKind.LeftShift OrElse op = BinaryOperatorKind.RightShift)
 
                Dim leftUnderlying = leftType.GetEnumUnderlyingTypeOrSelf()
                Dim resultUnderlying = resultType.GetEnumUnderlyingTypeOrSelf()
 
                If leftUnderlying.IsIntegralType() OrElse leftUnderlying.IsCharType() OrElse leftUnderlying.IsDateTimeType() Then
                    result = FoldIntegralCharOrDateTimeBinaryOperator(
                                                op,
                                                leftConstantValue,
                                                rightConstantValue,
                                                leftUnderlying,
                                                resultUnderlying,
                                                integerOverflow,
                                                divideByZero)
 
                ElseIf leftUnderlying.IsFloatingType() Then
                    result = FoldFloatingBinaryOperator(
                                                op,
                                                leftConstantValue,
                                                rightConstantValue,
                                                leftUnderlying,
                                                resultUnderlying)
 
                ElseIf leftUnderlying.IsDecimalType() Then
                    result = FoldDecimalBinaryOperator(
                                                op,
                                                leftConstantValue,
                                                rightConstantValue,
                                                resultUnderlying,
                                                divideByZero)
 
                ElseIf leftUnderlying.IsStringType() Then
                    ' During normal compilation we never fold string comparison with 
                    ' Option Compare Text in effect. However, it looks like Dev10 goes through
                    ' this code path in EE and folds comparison regardless of Option Compare.
                    ' I am not sure if we need this in Roslyn, will ignore Option Compare Text for now.
                    Debug.Assert((operatorKind And BinaryOperatorKind.CompareText) = 0)
                    result = FoldStringBinaryOperator(
                                                op,
                                                leftConstantValue,
                                                rightConstantValue)
 
                    If result.IsBad Then
                        lengthOutOfLimit = True
                    End If
 
                ElseIf leftUnderlying.IsBooleanType() Then
                    result = FoldBooleanBinaryOperator(
                                                op,
                                                leftConstantValue,
                                                rightConstantValue)
                End If
 
                Debug.Assert(result IsNot Nothing)
            End If
 
            Return result
        End Function
 
        Private Shared Function FoldIntegralCharOrDateTimeBinaryOperator(
            op As BinaryOperatorKind,
            left As ConstantValue,
            right As ConstantValue,
            operandType As TypeSymbol,
            resultType As TypeSymbol,
            ByRef integerOverflow As Boolean,
            ByRef divideByZero As Boolean
        ) As ConstantValue
            Debug.Assert(operandType.IsIntegralType() OrElse operandType.IsCharType() OrElse operandType.IsDateTimeType())
            Debug.Assert((op And BinaryOperatorKind.OpMask) = op)
            Debug.Assert(Not integerOverflow)
            Debug.Assert(Not divideByZero)
 
            Dim result As ConstantValue
 
            Dim leftValue As Long = GetConstantValueAsInt64(left)
            Dim rightValue As Long = GetConstantValueAsInt64(right)
 
            If resultType.IsBooleanType() Then
                Dim resultValue As Boolean
 
                Select Case op
                    Case BinaryOperatorKind.Equals
                        resultValue = If(operandType.IsUnsignedIntegralType(),
                                         UncheckedCULng(leftValue) = UncheckedCULng(rightValue),
                                         leftValue = rightValue)
 
                    Case BinaryOperatorKind.NotEquals
                        resultValue = If(operandType.IsUnsignedIntegralType(),
                                         UncheckedCULng(leftValue) <> UncheckedCULng(rightValue),
                                         leftValue <> rightValue)
 
                    Case BinaryOperatorKind.LessThanOrEqual
                        resultValue = If(operandType.IsUnsignedIntegralType(),
                                         UncheckedCULng(leftValue) <= UncheckedCULng(rightValue),
                                         leftValue <= rightValue)
 
                    Case BinaryOperatorKind.GreaterThanOrEqual
                        resultValue = If(operandType.IsUnsignedIntegralType(),
                                         UncheckedCULng(leftValue) >= UncheckedCULng(rightValue),
                                         leftValue >= rightValue)
 
                    Case BinaryOperatorKind.LessThan
                        resultValue = If(operandType.IsUnsignedIntegralType(),
                                         UncheckedCULng(leftValue) < UncheckedCULng(rightValue),
                                         leftValue < rightValue)
 
                    Case BinaryOperatorKind.GreaterThan
                        resultValue = If(operandType.IsUnsignedIntegralType(),
                                         UncheckedCULng(leftValue) > UncheckedCULng(rightValue),
                                         leftValue > rightValue)
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(op)
                End Select
 
                result = ConstantValue.Create(resultValue)
 
            Else
                ' Compute the result in 64-bit arithmetic, and determine if the
                ' operation overflows the result type.
                Dim resultValue As Long
                Dim leftDiscriminator = operandType.GetConstantValueTypeDiscriminator()
                Dim resultDiscriminator = resultType.GetConstantValueTypeDiscriminator()
 
                Select Case op
                    Case BinaryOperatorKind.Add
                        resultValue = NarrowIntegralResult(leftValue + rightValue, leftDiscriminator, resultDiscriminator, integerOverflow)
 
                        If Not resultType.IsUnsignedIntegralType() Then
                            If (rightValue > 0 AndAlso resultValue < leftValue) OrElse
                               (rightValue < 0 AndAlso resultValue > leftValue) Then
                                integerOverflow = True
                            End If
 
                        ElseIf UncheckedCULng(resultValue) < UncheckedCULng(leftValue) Then
                            integerOverflow = True
                        End If
 
                    Case BinaryOperatorKind.Subtract
 
                        resultValue = NarrowIntegralResult(leftValue - rightValue, leftDiscriminator, resultDiscriminator, integerOverflow)
 
                        If Not resultType.IsUnsignedIntegralType() Then
                            If (rightValue > 0 AndAlso resultValue > leftValue) OrElse
                               (rightValue < 0 AndAlso resultValue < leftValue) Then
                                integerOverflow = True
                            End If
 
                        ElseIf UncheckedCULng(resultValue) > UncheckedCULng(leftValue) Then
                            integerOverflow = True
                        End If
 
                    Case BinaryOperatorKind.Multiply
 
                        resultValue = Multiply(leftValue, rightValue, leftDiscriminator, resultDiscriminator, integerOverflow)
 
                    Case BinaryOperatorKind.IntegerDivide
 
                        If rightValue = 0 Then
                            divideByZero = True
                        Else
                            resultValue = NarrowIntegralResult(
                                If(resultType.IsUnsignedIntegralType(),
                                    UncheckedCLng(UncheckedCULng(leftValue) \ UncheckedCULng(rightValue)),
                                    UncheckedIntegralDiv(leftValue, rightValue)),
                                leftDiscriminator, resultDiscriminator, integerOverflow)
 
                            If Not resultType.IsUnsignedIntegralType() AndAlso leftValue = Int64.MinValue AndAlso rightValue = -1 Then
                                integerOverflow = True
                            End If
                        End If
 
                    Case BinaryOperatorKind.Modulo
 
                        If rightValue = 0 Then
                            divideByZero = True
                        Else
                            If resultType.IsUnsignedIntegralType() Then
                                resultValue = UncheckedCLng(UncheckedCULng(leftValue) Mod UncheckedCULng(rightValue))
 
                                ' // 64-bit processors crash on 0, -1 (Bug: dd71694)
                            ElseIf rightValue <> -1L Then
                                resultValue = leftValue Mod rightValue
                            Else
                                resultValue = 0
                            End If
                        End If
 
                    Case BinaryOperatorKind.Xor
 
                        resultValue = leftValue Xor rightValue
 
                    Case BinaryOperatorKind.Or
 
                        resultValue = leftValue Or rightValue
 
                    Case BinaryOperatorKind.And
 
                        resultValue = leftValue And rightValue
 
                    Case BinaryOperatorKind.LeftShift
 
                        resultValue = leftValue << (CType(rightValue, Integer) And CodeGen.CodeGenerator.GetShiftSizeMask(operandType))
 
                        ' // Round-trip the result through a cast.  We do this for two reasons:
                        ' // a) Bits may have shifted off the end and need to be stripped away.
                        ' // b) The sign bit may have changed which requires the result to be sign-extended.
 
                        Dim overflowTemp As Boolean = False
                        resultValue = NarrowIntegralResult(resultValue, leftDiscriminator, resultDiscriminator, overflowTemp)
 
                    Case BinaryOperatorKind.RightShift
 
                        If resultType.IsUnsignedIntegralType() Then
                            resultValue = UncheckedCLng((UncheckedCULng(leftValue) >>
                                                         (CType(rightValue, Integer) And CodeGen.CodeGenerator.GetShiftSizeMask(operandType))))
                        Else
                            resultValue = leftValue >> (CType(rightValue, Integer) And CodeGen.CodeGenerator.GetShiftSizeMask(operandType))
                        End If
 
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(op)
                End Select
 
                If divideByZero Then
                    result = ConstantValue.Bad
                Else
                    result = GetConstantValue(resultDiscriminator, resultValue)
                End If
            End If
 
            Return result
        End Function
 
        Private Shared Function FoldFloatingBinaryOperator(
            op As BinaryOperatorKind,
            left As ConstantValue,
            right As ConstantValue,
            operandType As TypeSymbol,
            resultType As TypeSymbol
        ) As ConstantValue
            Debug.Assert(operandType.IsFloatingType())
            Debug.Assert((op And BinaryOperatorKind.OpMask) = op)
 
            Dim result As ConstantValue
 
            Dim leftValue As Double = If(operandType.IsSingleType, left.SingleValue, left.DoubleValue)
            Dim rightValue As Double = If(operandType.IsSingleType, right.SingleValue, right.DoubleValue)
 
            If resultType.IsBooleanType() Then
                Dim resultValue As Boolean
 
                Select Case op
 
                    Case BinaryOperatorKind.Equals
                        resultValue = (leftValue = rightValue)
 
                    Case BinaryOperatorKind.NotEquals
                        resultValue = (leftValue <> rightValue)
 
                    Case BinaryOperatorKind.LessThanOrEqual
                        resultValue = (leftValue <= rightValue)
 
                    Case BinaryOperatorKind.GreaterThanOrEqual
                        resultValue = (leftValue >= rightValue)
 
                    Case BinaryOperatorKind.LessThan
                        resultValue = (leftValue < rightValue)
 
                    Case BinaryOperatorKind.GreaterThan
                        resultValue = (leftValue > rightValue)
 
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(op)
                End Select
 
                result = ConstantValue.Create(resultValue)
 
            Else
 
                ' Compute the result in 64-bit arithmetic, and determine if the
                ' operation overflows the result type.
 
                Dim resultValue As Double = 0
 
                Select Case op
 
                    Case BinaryOperatorKind.Add
                        resultValue = leftValue + rightValue
 
                    Case BinaryOperatorKind.Subtract
                        resultValue = leftValue - rightValue
 
                    Case BinaryOperatorKind.Multiply
                        resultValue = leftValue * rightValue
 
                    Case BinaryOperatorKind.Power
 
                        ' VSW#463059: Special case CRT changes to match CLR behavior.
                        If Double.IsInfinity(rightValue) Then
                            If leftValue.Equals(1.0) Then
                                resultValue = leftValue
                                Exit Select
                            End If
 
                            If leftValue.Equals(-1.0) Then
                                resultValue = Double.NaN
                                Exit Select
                            End If
 
                        ElseIf (
                            Double.IsNaN(rightValue)
                        ) Then
                            resultValue = Double.NaN
                            Exit Select
                        End If
 
                        resultValue = Math.Pow(leftValue, rightValue)
 
                    Case BinaryOperatorKind.Divide
 
                        ' We have decided not to detect zerodivide in compile-time
                        ' evaluation of floating expressions.
                        resultValue = leftValue / rightValue
 
                    Case BinaryOperatorKind.Modulo
 
                        ' We have decided not to detect zerodivide in compile-time
                        ' evaluation of floating expressions.
 
                        ' Note, that Math.IEEERemainder(leftValue, rightValue) might give different result.
                        ' Dev10 compiler used fmod function here and it behaves differently. It looks like
                        ' ILOpCode.Rem operation, that we use to emit Mod operator, produces result consistent 
                        ' with fmod. 
                        resultValue = leftValue Mod rightValue
 
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(op)
                End Select
 
                Dim overflow As Boolean = False
                resultValue = NarrowFloatingResult(resultValue, resultType.GetConstantValueTypeDiscriminator(), overflow)
 
                ' We have decided not to detect overflow in compile-time
                ' evaluation of floating expressions.
 
                If resultType.IsSingleType() Then
                    result = ConstantValue.Create(CType(resultValue, Single))
                Else
                    result = ConstantValue.Create(resultValue)
                End If
 
            End If
 
            Return result
        End Function
 
        Private Shared Function FoldDecimalBinaryOperator(
            op As BinaryOperatorKind,
            left As ConstantValue,
            right As ConstantValue,
            resultType As TypeSymbol,
            ByRef divideByZero As Boolean
        ) As ConstantValue
            Debug.Assert((op And BinaryOperatorKind.OpMask) = op)
            Debug.Assert(Not divideByZero)
 
            Dim result As ConstantValue
 
            Dim leftValue As Decimal = left.DecimalValue
            Dim rightValue As Decimal = right.DecimalValue
 
            If resultType.IsBooleanType() Then
                Dim resultValue As Boolean = False
                Dim comparisonResult As Integer = leftValue.CompareTo(rightValue)
 
                Select Case op
 
                    Case BinaryOperatorKind.Equals
                        resultValue = (comparisonResult = 0)
 
                    Case BinaryOperatorKind.NotEquals
                        resultValue = Not (comparisonResult = 0)
 
                    Case BinaryOperatorKind.LessThanOrEqual
                        resultValue = (comparisonResult <= 0)
 
                    Case BinaryOperatorKind.GreaterThanOrEqual
                        resultValue = (comparisonResult >= 0)
 
                    Case BinaryOperatorKind.LessThan
                        resultValue = (comparisonResult < 0)
 
                    Case BinaryOperatorKind.GreaterThan
                        resultValue = (comparisonResult > 0)
 
                    Case Else
                        Throw ExceptionUtilities.UnexpectedValue(op)
                End Select
 
                result = ConstantValue.Create(resultValue)
            Else
                Dim resultValue As Decimal
                Dim overflow As Boolean = False
 
                Try
                    Select Case op
                        Case BinaryOperatorKind.Add
                            resultValue = Decimal.Add(leftValue, rightValue)
 
                        Case BinaryOperatorKind.Subtract
                            resultValue = Decimal.Subtract(leftValue, rightValue)
 
                        Case BinaryOperatorKind.Multiply
                            resultValue = Decimal.Multiply(leftValue, rightValue)
 
                        Case BinaryOperatorKind.Divide
                            resultValue = Decimal.Divide(leftValue, rightValue)
 
                        Case BinaryOperatorKind.Modulo
                            resultValue = Decimal.Remainder(leftValue, rightValue)
 
                        Case Else
                            Throw ExceptionUtilities.UnexpectedValue(op)
                    End Select
 
                Catch ex As OverflowException
                    overflow = True
                Catch ex As DivideByZeroException
                    divideByZero = True
                End Try
 
                If overflow OrElse divideByZero Then
                    result = ConstantValue.Bad
                Else
                    result = ConstantValue.Create(resultValue)
                End If
            End If
 
            Return result
        End Function
 
        ''' <summary>
        ''' Returns ConstantValue.Bad if, and only if, compound string length is out of supported limit.
        ''' </summary>
        Private Shared Function FoldStringBinaryOperator(
            op As BinaryOperatorKind,
            left As ConstantValue,
            right As ConstantValue
        ) As ConstantValue
            Debug.Assert((op And BinaryOperatorKind.OpMask) = op)
 
            Dim result As ConstantValue
 
            Select Case op
                Case BinaryOperatorKind.Concatenate
 
                    Dim leftValue As Rope = If(left.IsNothing, Rope.Empty, left.RopeValue)
                    Dim rightValue As Rope = If(right.IsNothing, Rope.Empty, right.RopeValue)
 
                    Dim newLength = CLng(leftValue.Length) + CLng(rightValue.Length)
 
                    If newLength > Integer.MaxValue Then
                        Return ConstantValue.Bad
                    End If
 
                    Try
                        result = ConstantValue.CreateFromRope(Rope.Concat(leftValue, rightValue))
                    Catch e As System.OutOfMemoryException
                        Return ConstantValue.Bad
                    End Try
 
                Case BinaryOperatorKind.GreaterThan,
                     BinaryOperatorKind.LessThan,
                     BinaryOperatorKind.GreaterThanOrEqual,
                     BinaryOperatorKind.LessThanOrEqual,
                     BinaryOperatorKind.Equals,
                     BinaryOperatorKind.NotEquals
 
                    Dim leftValue As String = If(left.IsNothing, String.Empty, left.StringValue)
                    Dim rightValue As String = If(right.IsNothing, String.Empty, right.StringValue)
 
                    Dim stringComparisonSucceeds As Boolean = False
 
                    Dim comparisonResult As Integer = String.Compare(leftValue, rightValue, StringComparison.Ordinal)
 
                    Select Case op
                        Case BinaryOperatorKind.Equals
                            stringComparisonSucceeds = (comparisonResult = 0)
 
                        Case BinaryOperatorKind.NotEquals
                            stringComparisonSucceeds = (comparisonResult <> 0)
 
                        Case BinaryOperatorKind.GreaterThan
                            stringComparisonSucceeds = (comparisonResult > 0)
 
                        Case BinaryOperatorKind.GreaterThanOrEqual
                            stringComparisonSucceeds = (comparisonResult >= 0)
 
                        Case BinaryOperatorKind.LessThan
                            stringComparisonSucceeds = (comparisonResult < 0)
 
                        Case BinaryOperatorKind.LessThanOrEqual
                            stringComparisonSucceeds = (comparisonResult <= 0)
 
                    End Select
 
                    result = ConstantValue.Create(stringComparisonSucceeds)
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(op)
            End Select
 
            Debug.Assert(Not result.IsBad)
            Return result
        End Function
 
        Private Shared Function FoldBooleanBinaryOperator(
            op As BinaryOperatorKind,
            left As ConstantValue,
            right As ConstantValue
        ) As ConstantValue
            Debug.Assert((op And BinaryOperatorKind.OpMask) = op)
 
            Dim result As ConstantValue
 
            Dim leftValue As Boolean = left.BooleanValue
            Dim rightValue As Boolean = right.BooleanValue
 
            Dim operationSucceeds As Boolean = False
 
            Select Case op
 
                Case BinaryOperatorKind.Equals
                    operationSucceeds = (leftValue = rightValue)
 
                Case BinaryOperatorKind.NotEquals
                    operationSucceeds = (leftValue <> rightValue)
 
                Case BinaryOperatorKind.GreaterThan
                    ' Amazingly, False > True.
                    operationSucceeds = (leftValue = False AndAlso rightValue = True)
 
                Case BinaryOperatorKind.GreaterThanOrEqual
                    operationSucceeds = (leftValue = False OrElse rightValue = True)
 
                Case BinaryOperatorKind.LessThan
                    operationSucceeds = (leftValue = True AndAlso rightValue = False)
 
                Case BinaryOperatorKind.LessThanOrEqual
                    operationSucceeds = (leftValue = True OrElse rightValue = False)
 
                Case BinaryOperatorKind.Xor
                    operationSucceeds = (leftValue Xor rightValue)
 
                Case BinaryOperatorKind.OrElse,
                     BinaryOperatorKind.Or
 
                    operationSucceeds = (leftValue OrElse rightValue)
 
                Case BinaryOperatorKind.AndAlso,
                     BinaryOperatorKind.And
                    operationSucceeds = (leftValue AndAlso rightValue)
 
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(op)
            End Select
 
            result = ConstantValue.Create(operationSucceeds)
 
            Return result
        End Function
 
        ''' <summary>
        ''' Returns result type of the operator or SpecialType.None if operator is not supported.
        ''' </summary>
        Friend Shared Function ResolveNotLiftedIntrinsicBinaryOperator(
            opCode As BinaryOperatorKind,
            left As SpecialType,
            right As SpecialType
        ) As SpecialType
 
            Dim leftIndex = left.TypeToIndex()
            Dim rightIndex = right.TypeToIndex()
 
            If Not (leftIndex.HasValue AndAlso rightIndex.HasValue) Then
                Return SpecialType.None
            End If
 
            Dim tableKind As BinaryOperatorTables.TableKind
 
            Select Case opCode
                Case BinaryOperatorKind.Add
                    tableKind = BinaryOperatorTables.TableKind.Addition
 
                Case BinaryOperatorKind.Subtract,
                     BinaryOperatorKind.Multiply,
                     BinaryOperatorKind.Modulo
                    tableKind = BinaryOperatorTables.TableKind.SubtractionMultiplicationModulo
 
                Case BinaryOperatorKind.Divide
                    tableKind = BinaryOperatorTables.TableKind.Division
 
                Case BinaryOperatorKind.IntegerDivide
                    tableKind = BinaryOperatorTables.TableKind.IntegerDivision
 
                Case BinaryOperatorKind.Power
                    tableKind = BinaryOperatorTables.TableKind.Power
 
                Case BinaryOperatorKind.LeftShift,
                     BinaryOperatorKind.RightShift
                    tableKind = BinaryOperatorTables.TableKind.Shift
 
                Case BinaryOperatorKind.OrElse,
                     BinaryOperatorKind.AndAlso
                    tableKind = BinaryOperatorTables.TableKind.Logical
 
                Case BinaryOperatorKind.Concatenate,
                     BinaryOperatorKind.Like
                    tableKind = BinaryOperatorTables.TableKind.ConcatenationLike
 
                Case BinaryOperatorKind.Equals,
                     BinaryOperatorKind.NotEquals,
                     BinaryOperatorKind.LessThanOrEqual,
                     BinaryOperatorKind.GreaterThanOrEqual,
                     BinaryOperatorKind.LessThan,
                     BinaryOperatorKind.GreaterThan
                    tableKind = BinaryOperatorTables.TableKind.Relational
 
                Case BinaryOperatorKind.Xor,
                     BinaryOperatorKind.Or,
                     BinaryOperatorKind.And
                    tableKind = BinaryOperatorTables.TableKind.Bitwise
 
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(opCode)
            End Select
 
            Return CType(BinaryOperatorTables.Table(tableKind, leftIndex.Value, rightIndex.Value), SpecialType)
        End Function
 
        Private Class BinaryOperatorTables
 
            Public Enum TableKind
                Addition
                SubtractionMultiplicationModulo
                Division
                Power
                IntegerDivision
                Shift
                Logical
                Bitwise
                Relational
                ConcatenationLike
            End Enum
 
            ' PERF: Using SByte instead of SpecialType because we want the compiler to use array literal initialization.
            '       The most natural type choice, Enum arrays, are not blittable due to a CLR limitation.
            Public Shared ReadOnly Table(,,) As SByte
 
            Shared Sub New()
 
                Const tErr As SByte = SpecialType.None
                Const tObj As SByte = SpecialType.System_Object
                Const tStr As SByte = SpecialType.System_String
                Const tDbl As SByte = SpecialType.System_Double
                Const tSBy As SByte = SpecialType.System_SByte
                Const tShr As SByte = SpecialType.System_Int16
                Const tInt As SByte = SpecialType.System_Int32
                Const tLng As SByte = SpecialType.System_Int64
                Const tDec As SByte = SpecialType.System_Decimal
                Const tSng As SByte = SpecialType.System_Single
                Const tByt As SByte = SpecialType.System_Byte
                Const tUSh As SByte = SpecialType.System_UInt16
                Const tUIn As SByte = SpecialType.System_UInt32
                Const tULn As SByte = SpecialType.System_UInt64
                Const tBoo As SByte = SpecialType.System_Boolean
                Const tChr As SByte = SpecialType.System_Char
                Const tDat As SByte = SpecialType.System_DateTime
 
                Table =
                {     '  Obj   Str   Bool  Char  SByt  Shrt  Int   Long  Byte  UShr  UInt  ULng  Sngl  Dbl   Dec   Date
                    { ' Addition
                        {tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj}, ' Obj
                        {tObj, tStr, tDbl, tStr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tStr}, ' Str
                        {tObj, tDbl, tShr, tErr, tSBy, tShr, tInt, tLng, tShr, tInt, tLng, tDec, tSng, tDbl, tDec, tErr}, ' Bool
                        {tObj, tStr, tErr, tStr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr}, ' Char
                        {tObj, tDbl, tSBy, tErr, tSBy, tShr, tInt, tLng, tShr, tInt, tLng, tDec, tSng, tDbl, tDec, tErr}, ' SByt
                        {tObj, tDbl, tShr, tErr, tShr, tShr, tInt, tLng, tShr, tInt, tLng, tDec, tSng, tDbl, tDec, tErr}, ' Shrt
                        {tObj, tDbl, tInt, tErr, tInt, tInt, tInt, tLng, tInt, tInt, tLng, tDec, tSng, tDbl, tDec, tErr}, ' Int
                        {tObj, tDbl, tLng, tErr, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tDec, tSng, tDbl, tDec, tErr}, ' Long
                        {tObj, tDbl, tShr, tErr, tShr, tShr, tInt, tLng, tByt, tUSh, tUIn, tULn, tSng, tDbl, tDec, tErr}, ' Byte
                        {tObj, tDbl, tInt, tErr, tInt, tInt, tInt, tLng, tUSh, tUSh, tUIn, tULn, tSng, tDbl, tDec, tErr}, ' UShr
                        {tObj, tDbl, tLng, tErr, tLng, tLng, tLng, tLng, tUIn, tUIn, tUIn, tULn, tSng, tDbl, tDec, tErr}, ' UInt
                        {tObj, tDbl, tDec, tErr, tDec, tDec, tDec, tDec, tULn, tULn, tULn, tULn, tSng, tDbl, tDec, tErr}, ' ULng
                        {tObj, tDbl, tSng, tErr, tSng, tSng, tSng, tSng, tSng, tSng, tSng, tSng, tSng, tDbl, tSng, tErr}, ' Sngl
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tErr}, ' Dbl
                        {tObj, tDbl, tDec, tErr, tDec, tDec, tDec, tDec, tDec, tDec, tDec, tDec, tSng, tDbl, tDec, tErr}, ' Dec
                        {tObj, tStr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tStr}  ' Date
                    },'  Obj   Str   Bool  Char  SByt  Shrt  Int   Long  Byte  UShr  UInt  ULng  Sngl  Dbl   Dec   Date
                    { ' Subtraction, Multiplication, and Modulo
                        {tObj, tObj, tObj, tErr, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tErr}, ' Obj 
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tErr}, ' Str 
                        {tObj, tDbl, tShr, tErr, tSBy, tShr, tInt, tLng, tShr, tInt, tLng, tDec, tSng, tDbl, tDec, tErr}, ' Bool 
                        {tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr}, ' Char 
                        {tObj, tDbl, tSBy, tErr, tSBy, tShr, tInt, tLng, tShr, tInt, tLng, tDec, tSng, tDbl, tDec, tErr}, ' SByt 
                        {tObj, tDbl, tShr, tErr, tShr, tShr, tInt, tLng, tShr, tInt, tLng, tDec, tSng, tDbl, tDec, tErr}, ' Shrt 
                        {tObj, tDbl, tInt, tErr, tInt, tInt, tInt, tLng, tInt, tInt, tLng, tDec, tSng, tDbl, tDec, tErr}, ' Int 
                        {tObj, tDbl, tLng, tErr, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tDec, tSng, tDbl, tDec, tErr}, ' Long 
                        {tObj, tDbl, tShr, tErr, tShr, tShr, tInt, tLng, tByt, tUSh, tUIn, tULn, tSng, tDbl, tDec, tErr}, ' Byte 
                        {tObj, tDbl, tInt, tErr, tInt, tInt, tInt, tLng, tUSh, tUSh, tUIn, tULn, tSng, tDbl, tDec, tErr}, ' UShr 
                        {tObj, tDbl, tLng, tErr, tLng, tLng, tLng, tLng, tUIn, tUIn, tUIn, tULn, tSng, tDbl, tDec, tErr}, ' UInt 
                        {tObj, tDbl, tDec, tErr, tDec, tDec, tDec, tDec, tULn, tULn, tULn, tULn, tSng, tDbl, tDec, tErr}, ' ULng 
                        {tObj, tDbl, tSng, tErr, tSng, tSng, tSng, tSng, tSng, tSng, tSng, tSng, tSng, tDbl, tSng, tErr}, ' Sngl 
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tErr}, ' Dbl 
                        {tObj, tDbl, tDec, tErr, tDec, tDec, tDec, tDec, tDec, tDec, tDec, tDec, tSng, tDbl, tDec, tErr}, ' Dec 
                        {tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr}  ' Date 
                    }, ' Special Note:  Date - Date is actually TimeSpan, but that cannot be encoded in this table.^^^^
                    { ' Division
                        {tObj, tObj, tObj, tErr, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tErr}, ' Obj
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tErr}, ' Str
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tSng, tDbl, tDec, tErr}, ' Bool
                        {tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr}, ' Char
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tSng, tDbl, tDec, tErr}, ' SByt
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tSng, tDbl, tDec, tErr}, ' Shrt
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tSng, tDbl, tDec, tErr}, ' Int
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tSng, tDbl, tDec, tErr}, ' Long
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tSng, tDbl, tDec, tErr}, ' Byte
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tSng, tDbl, tDec, tErr}, ' UShr
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tSng, tDbl, tDec, tErr}, ' UInt
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tSng, tDbl, tDec, tErr}, ' ULng
                        {tObj, tDbl, tSng, tErr, tSng, tSng, tSng, tSng, tSng, tSng, tSng, tSng, tSng, tDbl, tSng, tErr}, ' Sngl
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tErr}, ' Dbl
                        {tObj, tDbl, tDec, tErr, tDec, tDec, tDec, tDec, tDec, tDec, tDec, tDec, tSng, tDbl, tDec, tErr}, ' Dec
                        {tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr}  ' Date
                    },'  Obj   Str   Bool  Char  SByt  Shrt  Int   Long  Byte  UShr  UInt  ULng  Sngl  Dbl   Dec   Date
                    { ' Power
                        {tObj, tObj, tObj, tErr, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tErr}, ' Obj
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tErr}, ' Str
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tErr}, ' Bool
                        {tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr}, ' Char
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tErr}, ' SByt
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tErr}, ' Shrt
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tErr}, ' Int
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tErr}, ' Long
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tErr}, ' Byte
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tErr}, ' UShr
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tErr}, ' UInt
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tErr}, ' ULng
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tErr}, ' Sngl
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tErr}, ' Dbl
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tErr}, ' Dec
                        {tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr}  ' Date
                    },'  Obj   Str   Bool  Char  SByt  Shrt  Int   Long  Byte  UShr  UInt  ULng  Sngl  Dbl   Dec   Date
                    { ' Integer Division
                        {tObj, tObj, tObj, tErr, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tErr}, ' Obj
                        {tObj, tLng, tLng, tErr, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tErr}, ' Str
                        {tObj, tLng, tShr, tErr, tSBy, tShr, tInt, tLng, tShr, tInt, tLng, tLng, tLng, tLng, tLng, tErr}, ' Bool
                        {tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr}, ' Char
                        {tObj, tLng, tSBy, tErr, tSBy, tShr, tInt, tLng, tShr, tInt, tLng, tLng, tLng, tLng, tLng, tErr}, ' SByt
                        {tObj, tLng, tShr, tErr, tShr, tShr, tInt, tLng, tShr, tInt, tLng, tLng, tLng, tLng, tLng, tErr}, ' Shrt
                        {tObj, tLng, tInt, tErr, tInt, tInt, tInt, tLng, tInt, tInt, tLng, tLng, tLng, tLng, tLng, tErr}, ' Int
                        {tObj, tLng, tLng, tErr, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tErr}, ' Long
                        {tObj, tLng, tShr, tErr, tShr, tShr, tInt, tLng, tByt, tUSh, tUIn, tULn, tLng, tLng, tLng, tErr}, ' Byte
                        {tObj, tLng, tInt, tErr, tInt, tInt, tInt, tLng, tUSh, tUSh, tUIn, tULn, tLng, tLng, tLng, tErr}, ' UShr
                        {tObj, tLng, tLng, tErr, tLng, tLng, tLng, tLng, tUIn, tUIn, tUIn, tULn, tLng, tLng, tLng, tErr}, ' UInt
                        {tObj, tLng, tLng, tErr, tLng, tLng, tLng, tLng, tULn, tULn, tULn, tULn, tLng, tLng, tLng, tErr}, ' ULng
                        {tObj, tLng, tLng, tErr, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tErr}, ' Sngl
                        {tObj, tLng, tLng, tErr, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tErr}, ' Dbl
                        {tObj, tLng, tLng, tErr, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tErr}, ' Dec
                        {tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr}  ' Date
                    },'  Obj   Str   Bool  Char  SByt  Shrt  Int   Long  Byte  UShr  UInt  ULng  Sngl  Dbl   Dec   Date
                    {  ' Shift. Note: The right operand serves little purpose in this table, however a table is utilized nonetheless to make the most use of already existing code which analyzes binary operators.
                        {tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj}, ' Obj
                        {tObj, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng}, ' Str
                        {tObj, tShr, tShr, tShr, tShr, tShr, tShr, tShr, tShr, tShr, tShr, tShr, tShr, tShr, tShr, tShr}, ' Bool
                        {tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr}, ' Char
                        {tObj, tSBy, tSBy, tSBy, tSBy, tSBy, tSBy, tSBy, tSBy, tSBy, tSBy, tSBy, tSBy, tSBy, tSBy, tSBy}, ' SByt
                        {tObj, tShr, tShr, tShr, tShr, tShr, tShr, tShr, tShr, tShr, tShr, tShr, tShr, tShr, tShr, tShr}, ' Shrt
                        {tObj, tInt, tInt, tInt, tInt, tInt, tInt, tInt, tInt, tInt, tInt, tInt, tInt, tInt, tInt, tInt}, ' Int
                        {tObj, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng}, ' Long
                        {tObj, tByt, tByt, tByt, tByt, tByt, tByt, tByt, tByt, tByt, tByt, tByt, tByt, tByt, tByt, tByt}, ' Byte
                        {tObj, tUSh, tUSh, tUSh, tUSh, tUSh, tUSh, tUSh, tUSh, tUSh, tUSh, tUSh, tUSh, tUSh, tUSh, tUSh}, ' UShr
                        {tObj, tUIn, tUIn, tUIn, tUIn, tUIn, tUIn, tUIn, tUIn, tUIn, tUIn, tUIn, tUIn, tUIn, tUIn, tUIn}, ' UInt
                        {tObj, tULn, tULn, tULn, tULn, tULn, tULn, tULn, tULn, tULn, tULn, tULn, tULn, tULn, tULn, tULn}, ' ULng
                        {tObj, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng}, ' Sngl
                        {tObj, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng}, ' Dbl
                        {tObj, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng}, ' Dec
                        {tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr}  ' Date
                    },'  Obj   Str   Bool  Char  SByt  Shrt  Int   Long  Byte  UShr  UInt  ULng  Sngl  Dbl   Dec   Date
                    {  ' Logical Operators
                        {tObj, tObj, tObj, tErr, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tErr}, ' Obj
                        {tObj, tBoo, tBoo, tErr, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tErr}, ' Str
                        {tObj, tBoo, tBoo, tErr, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tErr}, ' Bool
                        {tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr}, ' Char
                        {tObj, tBoo, tBoo, tErr, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tErr}, ' SByt
                        {tObj, tBoo, tBoo, tErr, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tErr}, ' Shrt
                        {tObj, tBoo, tBoo, tErr, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tErr}, ' Int
                        {tObj, tBoo, tBoo, tErr, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tErr}, ' Long
                        {tObj, tBoo, tBoo, tErr, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tErr}, ' Byte
                        {tObj, tBoo, tBoo, tErr, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tErr}, ' UShr
                        {tObj, tBoo, tBoo, tErr, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tErr}, ' UInt
                        {tObj, tBoo, tBoo, tErr, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tErr}, ' ULng
                        {tObj, tBoo, tBoo, tErr, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tErr}, ' Sngl
                        {tObj, tBoo, tBoo, tErr, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tErr}, ' Dbl
                        {tObj, tBoo, tBoo, tErr, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tBoo, tErr}, ' Dec
                        {tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr}  ' Date
                    },'  Obj   Str   Bool  Char  SByt  Shrt  Int   Long  Byte  UShr  UInt  ULng  Sngl  Dbl   Dec   Date
                    { ' Bitwise Operators
                        {tObj, tObj, tObj, tErr, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tErr}, ' Obj
                        {tObj, tLng, tBoo, tErr, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tErr}, ' Str
                        {tObj, tBoo, tBoo, tErr, tSBy, tShr, tInt, tLng, tShr, tInt, tLng, tLng, tLng, tLng, tLng, tErr}, ' Bool
                        {tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr}, ' Char
                        {tObj, tLng, tSBy, tErr, tSBy, tShr, tInt, tLng, tShr, tInt, tLng, tLng, tLng, tLng, tLng, tErr}, ' SByt
                        {tObj, tLng, tShr, tErr, tShr, tShr, tInt, tLng, tShr, tInt, tLng, tLng, tLng, tLng, tLng, tErr}, ' Shrt
                        {tObj, tLng, tInt, tErr, tInt, tInt, tInt, tLng, tInt, tInt, tLng, tLng, tLng, tLng, tLng, tErr}, ' Int
                        {tObj, tLng, tLng, tErr, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tErr}, ' Long
                        {tObj, tLng, tShr, tErr, tShr, tShr, tInt, tLng, tByt, tUSh, tUIn, tULn, tLng, tLng, tLng, tErr}, ' Byte
                        {tObj, tLng, tInt, tErr, tInt, tInt, tInt, tLng, tUSh, tUSh, tUIn, tULn, tLng, tLng, tLng, tErr}, ' UShr
                        {tObj, tLng, tLng, tErr, tLng, tLng, tLng, tLng, tUIn, tUIn, tUIn, tULn, tLng, tLng, tLng, tErr}, ' UInt
                        {tObj, tLng, tLng, tErr, tLng, tLng, tLng, tLng, tULn, tULn, tULn, tULn, tLng, tLng, tLng, tErr}, ' ULng
                        {tObj, tLng, tLng, tErr, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tErr}, ' Sngl
                        {tObj, tLng, tLng, tErr, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tErr}, ' Dbl
                        {tObj, tLng, tLng, tErr, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tErr}, ' Dec
                        {tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr}  ' Date
                    },'  Obj   Str   Bool  Char  SByt  Shrt  Int   Long  Byte  UShr  UInt  ULng  Sngl  Dbl   Dec   Date
                    { ' Relational Operators -- This one is a little unusual because it lists the type of the relational operation, even though the result type is always boolean
                        {tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj}, ' Obj
                        {tObj, tStr, tBoo, tStr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDat}, ' Str
                        {tObj, tBoo, tBoo, tErr, tSBy, tShr, tInt, tLng, tShr, tInt, tLng, tDec, tSng, tDbl, tDec, tErr}, ' Bool
                        {tObj, tStr, tErr, tChr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr}, ' Char
                        {tObj, tDbl, tSBy, tErr, tSBy, tShr, tInt, tLng, tShr, tInt, tLng, tDec, tSng, tDbl, tDec, tErr}, ' SByt
                        {tObj, tDbl, tShr, tErr, tShr, tShr, tInt, tLng, tShr, tInt, tLng, tDec, tSng, tDbl, tDec, tErr}, ' Shrt
                        {tObj, tDbl, tInt, tErr, tInt, tInt, tInt, tLng, tInt, tInt, tLng, tDec, tSng, tDbl, tDec, tErr}, ' Int
                        {tObj, tDbl, tLng, tErr, tLng, tLng, tLng, tLng, tLng, tLng, tLng, tDec, tSng, tDbl, tDec, tErr}, ' Long
                        {tObj, tDbl, tShr, tErr, tShr, tShr, tInt, tLng, tByt, tUSh, tUIn, tULn, tSng, tDbl, tDec, tErr}, ' Byte
                        {tObj, tDbl, tInt, tErr, tInt, tInt, tInt, tLng, tUSh, tUSh, tUIn, tULn, tSng, tDbl, tDec, tErr}, ' UShr
                        {tObj, tDbl, tLng, tErr, tLng, tLng, tLng, tLng, tUIn, tUIn, tUIn, tULn, tSng, tDbl, tDec, tErr}, ' UInt
                        {tObj, tDbl, tDec, tErr, tDec, tDec, tDec, tDec, tULn, tULn, tULn, tULn, tSng, tDbl, tDec, tErr}, ' ULng
                        {tObj, tDbl, tSng, tErr, tSng, tSng, tSng, tSng, tSng, tSng, tSng, tSng, tSng, tDbl, tSng, tErr}, ' Sngl
                        {tObj, tDbl, tDbl, tErr, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tDbl, tErr}, ' Dbl
                        {tObj, tDbl, tDec, tErr, tDec, tDec, tDec, tDec, tDec, tDec, tDec, tDec, tSng, tDbl, tDec, tErr}, ' Dec
                        {tObj, tDat, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tErr, tDat}  ' Date
                    },'  Obj   Str   Bool  Char  SByt  Shrt  Int   Long  Byte  UShr  UInt  ULng  Sngl  Dbl   Dec   Date
                    { ' Concatenation and Like
                        {tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj, tObj}, ' Obj 
                        {tObj, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr}, ' Str 
                        {tObj, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr}, ' Bool 
                        {tObj, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr}, ' Char 
                        {tObj, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr}, ' SByt 
                        {tObj, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr}, ' Shrt 
                        {tObj, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr}, ' Int 
                        {tObj, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr}, ' Long 
                        {tObj, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr}, ' Byte 
                        {tObj, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr}, ' UShr 
                        {tObj, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr}, ' UInt 
                        {tObj, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr}, ' ULng 
                        {tObj, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr}, ' Sngl 
                        {tObj, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr}, ' Dbl 
                        {tObj, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr}, ' Date 
                        {tObj, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr, tStr}  ' Dec 
                    }
                }
 
            End Sub
        End Class
 
        Public Shared Function ResolveUserDefinedConversion(
            source As TypeSymbol,
            destination As TypeSymbol,
            <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
        ) As KeyValuePair(Of ConversionKind, MethodSymbol)
            Debug.Assert(Not source.IsErrorType())
            Debug.Assert(Not destination.IsErrorType())
 
            Dim result As KeyValuePair(Of ConversionKind, MethodSymbol) = Nothing
            Dim opSet = ArrayBuilder(Of MethodSymbol).GetInstance()
 
            ' 8.11 User-Defined Conversions
            ' When determining what user-defined conversion to use, the most specific widening conversion will be used;
            ' if no widening conversion is most specific, the most specific narrowing conversion will be used. If there
            ' is no most specific narrowing conversion, then the conversion is undefined and a compile-time error occurs.
            ' When collecting the candidate user-defined conversions for a type T?, the user-defined conversion operators
            ' defined by T are used instead. If the type being converted to is also a nullable value type, then any of
            ' T's user-defined conversions operators that involve only non-nullable value types are lifted. A conversion
            ' operator from T to S is lifted to be a conversion from T? to S? 
            '
            ' !!! Dev10 implementation doesn't match the spec here (the behavior is duplicated):
            ' !!! 1) If there were applicable Widening CType operators applicable according to the
            ' !!!    "Most Specific Widening Conversion" section, Narrowing CType operators are not considered at all.
            ' !!!    Nullable lifting isn't considered too.
            ' !!! 2) With "Most Specific Narrowing Conversion" behavior is slightly different. If there is a conversion
            ' !!!    operator that converts from the most specific source type to the most specific target type, then
            ' !!!    nullable lifting isn't considered.
 
            CollectUserDefinedConversionOperators(source, destination, opSet, useSiteInfo)
 
            If opSet.Count = 0 Then
                opSet.Free()
                Return result
            End If
 
            Dim conversionKinds = ArrayBuilder(Of KeyValuePair(Of ConversionKind, ConversionKind)).GetInstance()
            conversionKinds.ZeroInit(opSet.Count)
 
            Dim applicable = BitVector.Create(opSet.Count)
            Dim bestMatch As MethodSymbol = Nothing
 
            If DetermineMostSpecificWideningConversion(source, destination, opSet, conversionKinds, applicable, bestMatch, suppressViabilityChecks:=False, useSiteInfo:=useSiteInfo) Then
                If bestMatch IsNot Nothing Then
                    result = New KeyValuePair(Of ConversionKind, MethodSymbol)(ConversionKind.Widening Or ConversionKind.UserDefined, bestMatch)
                End If
 
                GoTo Done
            ElseIf opSet.Count = 0 Then
                GoTo Done
            End If
 
            If DetermineMostSpecificNarrowingConversion(source, destination, opSet, conversionKinds, applicable, bestMatch, suppressViabilityChecks:=False, useSiteInfo:=useSiteInfo) Then
                If bestMatch IsNot Nothing Then
                    result = New KeyValuePair(Of ConversionKind, MethodSymbol)(ConversionKind.Narrowing Or ConversionKind.UserDefined, bestMatch)
                End If
 
                GoTo Done
            ElseIf opSet.Count = 0 Then
                GoTo Done
            End If
 
            ' Try nullable lifting.
            If source.IsNullableType() AndAlso destination.IsNullableType() Then
 
                applicable.Clear()
                conversionKinds.ZeroInit(opSet.Count)
 
                Dim sourceUnderlying As TypeSymbol = source.GetNullableUnderlyingType()
                Dim destinationUnderlying As TypeSymbol = destination.GetNullableUnderlyingType()
 
                If Not (sourceUnderlying.IsErrorType() OrElse destinationUnderlying.IsErrorType()) Then
                    ' All candidates applicable to the underlying types should be applicable to the original types, no reason to 
                    ' do viability checks for the second time.
 
                    If DetermineMostSpecificWideningConversion(sourceUnderlying, destinationUnderlying, opSet, conversionKinds, applicable, bestMatch, suppressViabilityChecks:=True, useSiteInfo:=useSiteInfo) Then
                        If bestMatch IsNot Nothing Then
                            Debug.Assert(Not bestMatch.Parameters(0).Type.IsNullableType())
                            Debug.Assert(Not bestMatch.ReturnType.IsNullableType())
                            result = New KeyValuePair(Of ConversionKind, MethodSymbol)(ConversionKind.Widening Or
                                                                                       ConversionKind.UserDefined Or
                                                                                       ConversionKind.Nullable, bestMatch)
                        End If
                    ElseIf DetermineMostSpecificNarrowingConversion(sourceUnderlying, destinationUnderlying, opSet, conversionKinds, applicable, bestMatch, suppressViabilityChecks:=True, useSiteInfo:=useSiteInfo) Then
                        If bestMatch IsNot Nothing Then
                            Debug.Assert(Not bestMatch.Parameters(0).Type.IsNullableType())
                            Debug.Assert(Not bestMatch.ReturnType.IsNullableType())
                            result = New KeyValuePair(Of ConversionKind, MethodSymbol)(ConversionKind.Narrowing Or
                                                                                       ConversionKind.UserDefined Or
                                                                                       ConversionKind.Nullable, bestMatch)
                        End If
                    End If
                End If
            End If
Done:
            conversionKinds.Free()
            opSet.Free()
 
            Return result
        End Function
 
        ''' <summary>
        ''' Returns True if resolution of user defined conversions is complete, i.e. there were operators
        ''' applicable for the "Most Specific Widening Conversion" purposes. 
        ''' This, however, doesn't mean that resolution is successful.
        ''' </summary>
        Private Shared Function DetermineMostSpecificWideningConversion(
            source As TypeSymbol,
            destination As TypeSymbol,
            opSet As ArrayBuilder(Of MethodSymbol),
            conversionKinds As ArrayBuilder(Of KeyValuePair(Of ConversionKind, ConversionKind)),
            <[In]()> ByRef applicable As BitVector,
            <Out()> ByRef bestMatch As MethodSymbol,
            suppressViabilityChecks As Boolean,
            <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
        ) As Boolean
#If DEBUG Then
            Debug.Assert(bestMatch Is Nothing)
            For i As Integer = 0 To opSet.Count - 1
                Debug.Assert(Not applicable(i))
            Next
#End If
 
            bestMatch = Nothing
 
            Dim bestMatchDegreeOfGenericity As Integer = -1 ' (-1) - unknown, 0 - neither type refers to generic type parameters, 1 - one type, 2 - two types
            Dim bestMatchIsAmbiguous As Boolean = False
            Dim mostSpecificSourceType As TypeSymbol = Nothing
            Dim mostSpecificTargetType As TypeSymbol = Nothing
            Dim applicableCount As Integer = 0
 
            ' 8.11.1 Most Specific Widening Conversion
            ' -	First, all of the candidate conversion operators are collected. The candidate conversion operators are all
            '   of the user-defined widening conversion operators in the source type and all of the user-defined widening
            '   conversion operators in the target type. 
            ' -	Then, all non-applicable conversion operators are removed from the set. A conversion operator is applicable
            '   to a source type and target type if there is an intrinsic widening conversion operator from the source type
            '   to the operand type and there is an intrinsic widening conversion operator from the result of the operator
            '   to the target type. If there are no applicable conversion operators, then there is no most specific widening
            '   conversion.
            ' -	Then, the most specific source type of the applicable conversion operators is determined:
            '     -	If any of the conversion operators convert directly from the source type, then the source type is the
            '       most specific source type.
            '     -	Otherwise, the most specific source type is the most encompassed type in the combined set of source types
            '       of the conversion operators. If no most encompassed type can be found, then there is no most specific
            '       widening conversion.
            ' -	Then, the most specific target type of the applicable conversion operators is determined:
            '     -	If any of the conversion operators convert directly to the target type, then the target type is the most
            '       specific target type.
            '     -	Otherwise, the most specific target type is the most encompassing type in the combined set of target types
            '       of the conversion operators. If no most encompassing type can be found, then there is no most specific
            '       widening conversion.
            ' -	Then, if exactly one conversion operator converts from the most specific source type to the most specific target
            '   type, then this is the most specific conversion operator. If more than one such operator exists, then there is
            '   no most specific widening conversion.
 
            ' 8.11 User-Defined Conversions
            ' -	If an intrinsic widening conversion exists from a type A to a type B,
            '   and if neither A nor B are interfaces, then A is encompassed by B, and
            '   B encompasses A.
            ' -	The most encompassing type in a set of types is the one type that encompasses
            '   all other types in the set. If no single type encompasses all other types, then
            '   the set has no most encompassing type. In intuitive terms, the most encompassing
            '   type is the "largest" type in the set—the one type to which each of the other
            '   types can be converted through a widening conversion.
            ' -	The most encompassed type in a set of types is the one type that is encompassed
            '   by all other types in the set. If no single type is encompassed by all other types,
            '   then the set has no most encompassed type. In intuitive terms, the most encompassed
            '   type is the "smallest" type in the set—the one type that can be converted from each
            '   of the other types through a narrowing conversion.
 
            Dim viableCandidates As Integer = 0
 
            For i As Integer = 0 To opSet.Count - 1
                Dim method As MethodSymbol = opSet(i)
 
                Dim currentIndex As Integer = viableCandidates
                viableCandidates += 1
 
                If currentIndex < i Then
                    Debug.Assert(Not suppressViabilityChecks)
                    opSet(currentIndex) = method
                Else
                    Debug.Assert(currentIndex = i)
                End If
 
                If Not IsWidening(method) Then
                    Continue For
                End If
 
                Dim conversionIn As ConversionKind
                Dim conversionOut As ConversionKind
 
                If ClassifyConversionOperatorInOutConversions(source, destination, method, conversionIn, conversionOut, suppressViabilityChecks, useSiteInfo) Then
                    conversionKinds(currentIndex) = New KeyValuePair(Of ConversionKind, ConversionKind)(conversionIn, conversionOut)
                Else
                    'opSet(currentIndex) = Nothing
                    Debug.Assert(Not suppressViabilityChecks)
                    viableCandidates = currentIndex
                    Continue For
                End If
 
                If bestMatch Is Nothing Then
                    If Not (Conversions.IsWideningConversion(conversionIn) AndAlso Conversions.IsWideningConversion(conversionOut)) Then
                        Continue For
                    End If
                Else
                    If Not (Conversions.IsIdentityConversion(conversionIn) AndAlso Conversions.IsIdentityConversion(conversionOut)) Then
                        Continue For
                    End If
 
                    ' Potential ambiguity, let's attempt to resolve based on genericity.
                    bestMatch = LeastGenericConversionOperator(bestMatch, method, bestMatchDegreeOfGenericity, bestMatchIsAmbiguous)
 
                    If bestMatchIsAmbiguous AndAlso bestMatchDegreeOfGenericity = 0 Then
                        ' We will not be able to get rid of this ambiguity.
                        Exit For
                    End If
 
                    Continue For
                End If
 
                If Conversions.IsIdentityConversion(conversionIn) AndAlso Conversions.IsIdentityConversion(conversionOut) Then
                    Debug.Assert(bestMatch Is Nothing)
                    bestMatch = method
                    applicable.Clear()
                    applicableCount = 0
                Else
                    If Conversions.IsIdentityConversion(conversionIn) Then
                        mostSpecificSourceType = source
                    End If
 
                    If Conversions.IsIdentityConversion(conversionOut) Then
                        mostSpecificTargetType = destination
                    End If
 
                    applicable(currentIndex) = True
                    applicableCount += 1
                End If
            Next
 
            opSet.Clip(viableCandidates)
            conversionKinds.Clip(viableCandidates)
 
#If DEBUG Then
            For i As Integer = 0 To opSet.Count - 1
                Debug.Assert(opSet(i) IsNot Nothing)
            Next
#End If
 
            If bestMatch IsNot Nothing Then
                If bestMatchIsAmbiguous Then
                    bestMatch = Nothing
                End If
 
                Return True
            End If
 
            If applicableCount > 0 Then
                Debug.Assert(bestMatch Is Nothing)
                Debug.Assert(Not bestMatchIsAmbiguous)
 
                If applicableCount > 1 Then
                    ' Try to choose most specific among applicable candidates. 
                    Dim typeSet As ArrayBuilder(Of TypeSymbol) = Nothing
 
                    If mostSpecificSourceType Is Nothing Then
                        typeSet = ArrayBuilder(Of TypeSymbol).GetInstance()
 
                        For i As Integer = 0 To opSet.Count - 1
                            If Not applicable(i) Then
                                Continue For
                            End If
 
                            typeSet.Add(opSet(i).Parameters(0).Type)
                        Next
 
                        Debug.Assert(typeSet.Count = applicableCount)
 
                        mostSpecificSourceType = MostEncompassed(typeSet, useSiteInfo)
                    End If
 
                    If mostSpecificTargetType Is Nothing AndAlso mostSpecificSourceType IsNot Nothing Then
                        If typeSet Is Nothing Then
                            typeSet = ArrayBuilder(Of TypeSymbol).GetInstance()
                        Else
                            typeSet.Clear()
                        End If
 
                        For i As Integer = 0 To opSet.Count - 1
                            If Not applicable(i) Then
                                Continue For
                            End If
 
                            typeSet.Add(opSet(i).ReturnType)
                        Next
 
                        Debug.Assert(typeSet.Count = applicableCount)
 
                        mostSpecificTargetType = MostEncompassing(typeSet, useSiteInfo)
                    End If
 
                    If typeSet IsNot Nothing Then
                        typeSet.Free()
                    End If
 
                    If mostSpecificSourceType IsNot Nothing AndAlso mostSpecificTargetType IsNot Nothing Then
                        bestMatch = ChooseMostSpecificConversionOperator(opSet, applicable, mostSpecificSourceType, mostSpecificTargetType, bestMatchIsAmbiguous)
                    End If
 
                    If bestMatch IsNot Nothing AndAlso bestMatchIsAmbiguous Then
                        bestMatch = Nothing
                    End If
                Else
                    For i As Integer = 0 To opSet.Count - 1
                        If applicable(i) Then
                            bestMatch = opSet(i)
                            Exit For
                        End If
                    Next
 
                    Debug.Assert(bestMatch IsNot Nothing)
                End If
 
                Return True
            End If
 
            Debug.Assert(bestMatch Is Nothing)
            Return False
        End Function
 
        Private Shared Function ChooseMostSpecificConversionOperator(
            opSet As ArrayBuilder(Of MethodSymbol),
            applicable As BitVector,
            mostSpecificSourceType As TypeSymbol,
            mostSpecificTargetType As TypeSymbol,
            <Out()> ByRef bestMatchIsAmbiguous As Boolean
        ) As MethodSymbol
            Dim bestMatchDegreeOfGenericity As Integer = -1 ' (-1) - unknown, 0 - neither type refers to generic type parameters, 1 - one type, 2 - two types
            Dim bestMatch As MethodSymbol = Nothing
            bestMatchIsAmbiguous = False
 
            For i As Integer = 0 To opSet.Count - 1
                If Not applicable(i) Then
                    Continue For
                End If
 
                Dim method As MethodSymbol = opSet(i)
 
                If Not (mostSpecificSourceType.IsSameTypeIgnoringAll(method.Parameters(0).Type) AndAlso
                        mostSpecificTargetType.IsSameTypeIgnoringAll(method.ReturnType)) Then
                    Continue For
                End If
 
                If bestMatch Is Nothing Then
                    bestMatch = method
                Else
                    ' Potential ambiguity, let's attempt to resolve based on genericity.
                    bestMatch = LeastGenericConversionOperator(bestMatch, method, bestMatchDegreeOfGenericity, bestMatchIsAmbiguous)
 
                    If bestMatchIsAmbiguous AndAlso bestMatchDegreeOfGenericity = 0 Then
                        ' We will not be able to get rid of this ambiguity.
                        Exit For
                    End If
                End If
            Next
 
            Return bestMatch
        End Function
 
        ''' <summary>
        ''' Returns false if operator should be ignored.
        ''' </summary>
        Private Shared Function ClassifyConversionOperatorInOutConversions(
            source As TypeSymbol,
            destination As TypeSymbol,
            method As MethodSymbol,
            <Out()> ByRef conversionIn As ConversionKind,
            <Out()> ByRef conversionOut As ConversionKind,
            suppressViabilityChecks As Boolean,
            <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
        ) As Boolean
            Dim inputType As TypeSymbol = method.Parameters(0).Type
            Dim outputType As TypeSymbol = method.ReturnType
 
            If Not suppressViabilityChecks Then
                If Not IsConversionOperatorViableBasedOnTypesInvolved(method, inputType, outputType, useSiteInfo) Then
                    conversionIn = Nothing
                    conversionOut = Nothing
                    Return False
                End If
            Else
                Debug.Assert(IsConversionOperatorViableBasedOnTypesInvolved(method, inputType, outputType, CompoundUseSiteInfo(Of AssemblySymbol).Discarded))
            End If
 
            ' If source is an array literal then use ClassifyArrayLiteralConversion
            Dim arrayLiteralType = TryCast(source, ArrayLiteralTypeSymbol)
            If arrayLiteralType IsNot Nothing Then
                Dim arrayLiteral = arrayLiteralType.ArrayLiteral
                conversionIn = Conversions.ClassifyArrayLiteralConversion(arrayLiteral, inputType, arrayLiteral.Binder, useSiteInfo)
                If Conversions.IsWideningConversion(conversionIn) AndAlso
                    IsSameTypeIgnoringAll(arrayLiteralType, inputType) Then
                    conversionIn = ConversionKind.Identity
                End If
            Else
                conversionIn = Conversions.ClassifyPredefinedConversion(source, inputType, useSiteInfo)
            End If
 
            conversionOut = Conversions.ClassifyPredefinedConversion(outputType, destination, useSiteInfo)
            Return True
        End Function
 
        Private Shared Function IsConversionOperatorViableBasedOnTypesInvolved(
            method As MethodSymbol,
            inputType As TypeSymbol,
            outputType As TypeSymbol,
            <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
        ) As Boolean
            If inputType.IsErrorType() OrElse outputType.IsErrorType() Then
                Return False
            End If
 
            ' Ignore user defined conversions between types that already have intrinsic conversions.
            ' This could happen for generics after generic param substitution.
            If Not method.ContainingType.IsDefinition Then
                Dim localUseSiteInfo = If(useSiteInfo.AccumulatesDependencies, New CompoundUseSiteInfo(Of AssemblySymbol)(useSiteInfo.AssemblyBeingBuilt), CompoundUseSiteInfo(Of AssemblySymbol).DiscardedDependencies)
                If Conversions.ConversionExists(Conversions.ClassifyPredefinedConversion(inputType, outputType, localUseSiteInfo)) OrElse
                   Not localUseSiteInfo.Diagnostics.IsNullOrEmpty Then
                    useSiteInfo.MergeAndClear(localUseSiteInfo)
                    Return False
                End If
 
                useSiteInfo.MergeAndClear(localUseSiteInfo)
            End If
 
            Return True
        End Function
 
        ''' <summary>
        ''' Returns True if resolution of user defined conversions is complete, i.e. there was an operator
        ''' that converts from the most specific source type to the most specific target type. 
        ''' This, however, doesn't mean that resolution is successful.
        ''' </summary>
        Private Shared Function DetermineMostSpecificNarrowingConversion(
            source As TypeSymbol,
            destination As TypeSymbol,
            opSet As ArrayBuilder(Of MethodSymbol),
            conversionKinds As ArrayBuilder(Of KeyValuePair(Of ConversionKind, ConversionKind)),
            <[In]()> ByRef applicable As BitVector,
            <Out()> ByRef bestMatch As MethodSymbol,
            suppressViabilityChecks As Boolean,
            <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
        ) As Boolean
#If DEBUG Then
            Debug.Assert(bestMatch Is Nothing)
            For i As Integer = 0 To opSet.Count - 1
                Debug.Assert(Not applicable(i))
            Next
#End If
            bestMatch = Nothing
 
            Dim bestMatchDegreeOfGenericity As Integer = -1 ' (-1) - unknown, 0 - neither type refers to generic type parameters, 1 - one type, 2 - two types
            Dim bestMatchIsAmbiguous As Boolean = False
            Dim mostSpecificSourceType As TypeSymbol = Nothing
            Dim mostSpecificTargetType As TypeSymbol = Nothing
            Dim applicableCount As Integer = 0
 
            ' 8.11.2 Most Specific Narrowing Conversion
            ' Determining the most specific user-defined narrowing conversion operator between two types is accomplished using the following steps:
            ' - First, all of the candidate conversion operators are collected. The candidate conversion operators are all
            '   of the user-defined conversion operators in the source type and all of the user-defined conversion operators
            '   in the target type. 
            ' - Then, all non-applicable conversion operators are removed from the set. A conversion operator is applicable
            '   to a source type and target type if there is an intrinsic conversion operator from the source type to the
            '   operand type and there is an intrinsic conversion operator from the result of the operator to the target type.
            '   If there are no applicable conversion operators, then there is no most specific narrowing conversion.
            ' - Then, the most specific source type of the applicable conversion operators is determined:
            '      - If any of the conversion operators convert directly from the source type, then the source type is
            '        the most specific source type.
            '      - Otherwise, if any of the conversion operators convert from types that encompass the source type,
            '        then the most specific source type is the most encompassed type in the combined set of source types
            '        of those conversion operators. If no most encompassed type can be found, then there is no most
            '        specific narrowing conversion.
            '      - Otherwise, the most specific source type is the most encompassing type in the combined set of source
            '        types of the conversion operators. If no most encompassing type can be found, then there is no most
            '        specific narrowing conversion.
            ' - Then, the most specific target type of the applicable conversion operators is determined:
            '      - If any of the conversion operators convert directly to the target type, then the target type is
            '        the most specific target type.
            '      - Otherwise, if any of the conversion operators convert to types that are encompassed by the target type,
            '        then the most specific target type is the most encompassing type in the combined set of source types of
            '        those conversion operators. If no most encompassing type can be found, then there is no most specific
            '        narrowing conversion.
            '      - Otherwise, the most specific target type is the most encompassed type in the combined set of target
            '        types of the conversion operators. If no most encompassed type can be found, then there is no most
            '        specific narrowing conversion.
            ' - Then, if exactly one conversion operator converts from the most specific source type to the most specific
            '   target type, then this is the most specific conversion operator. If more than one such operator exists,
            '   then there is no most specific narrowing conversion.
 
            ' 8.11 User-Defined Conversions
            ' -	If an intrinsic widening conversion exists from a type A to a type B,
            '   and if neither A nor B are interfaces, then A is encompassed by B, and
            '   B encompasses A.
            ' -	The most encompassing type in a set of types is the one type that encompasses
            '   all other types in the set. If no single type encompasses all other types, then
            '   the set has no most encompassing type. In intuitive terms, the most encompassing
            '   type is the "largest" type in the set—the one type to which each of the other
            '   types can be converted through a widening conversion.
            ' -	The most encompassed type in a set of types is the one type that is encompassed
            '   by all other types in the set. If no single type is encompassed by all other types,
            '   then the set has no most encompassed type. In intuitive terms, the most encompassed
            '   type is the "smallest" type in the set—the one type that can be converted from each
            '   of the other types through a narrowing conversion.
 
            Dim haveWideningInConversions As Integer = 0
            Dim haveWideningOutConversions As Integer = 0
 
            Dim viableCandidates As Integer = 0
 
            For i As Integer = 0 To opSet.Count - 1
                Dim method As MethodSymbol = opSet(i)
 
                Dim currentIndex As Integer = viableCandidates
                viableCandidates += 1
 
                If currentIndex < i Then
                    Debug.Assert(Not suppressViabilityChecks)
                    opSet(currentIndex) = method
                    conversionKinds(currentIndex) = conversionKinds(i)
                Else
                    Debug.Assert(currentIndex = i)
                End If
 
                Dim conversionIn As ConversionKind
                Dim conversionOut As ConversionKind
 
                If IsWidening(method) Then
                    ' When we reach this point, a widening operator is either removed from the opSet (because it was "bad" in one way or another)
                    ' or it was not applicable for the purpose of the "Most Specific Widening Conversion", but conversion information should have been 
                    ' captured in conversionKinds for it.
                    Dim conversion As KeyValuePair(Of ConversionKind, ConversionKind) = conversionKinds(currentIndex)
                    conversionIn = conversion.Key
                    conversionOut = conversion.Value
 
                    ' If the operator would be an acceptable candidate for the "Most Specific Widening Conversion", we shouldn't have
                    ' reached this place.
                    Debug.Assert(Not (Conversions.IsWideningConversion(conversionIn) AndAlso Conversions.IsWideningConversion(conversionOut)))
 
                ElseIf ClassifyConversionOperatorInOutConversions(source, destination, method, conversionIn, conversionOut, suppressViabilityChecks, useSiteInfo) Then
                    conversionKinds(currentIndex) = New KeyValuePair(Of ConversionKind, ConversionKind)(conversionIn, conversionOut)
                Else
                    'opSet(currentIndex) = Nothing
                    Debug.Assert(Not suppressViabilityChecks)
                    viableCandidates = currentIndex
                    Continue For
                End If
 
                If bestMatch Is Nothing Then
                    If Not (Conversions.ConversionExists(conversionIn) AndAlso Conversions.ConversionExists(conversionOut)) Then
                        Continue For
                    End If
                Else
                    If Not (Conversions.IsIdentityConversion(conversionIn) AndAlso Conversions.IsIdentityConversion(conversionOut)) Then
                        Continue For
                    End If
 
                    ' Potential ambiguity, let's attempt to resolve based on genericity.
                    bestMatch = LeastGenericConversionOperator(bestMatch, method, bestMatchDegreeOfGenericity, bestMatchIsAmbiguous)
 
                    If bestMatchIsAmbiguous AndAlso bestMatchDegreeOfGenericity = 0 Then
                        ' We will not be able to get rid of this ambiguity.
                        Exit For
                    End If
 
                    Continue For
                End If
 
                If Conversions.IsIdentityConversion(conversionIn) AndAlso Conversions.IsIdentityConversion(conversionOut) Then
                    Debug.Assert(bestMatch Is Nothing)
                    bestMatch = method
                    applicable.Clear()
                    applicableCount = 0
                Else
                    If Conversions.IsWideningConversion(conversionIn) Then
                        If Conversions.IsIdentityConversion(conversionIn) Then
                            mostSpecificSourceType = source
                        Else
                            haveWideningInConversions += 1
                        End If
 
                        If Conversions.IsWideningConversion(conversionOut) Then
                            If Conversions.IsIdentityConversion(conversionOut) Then
                                mostSpecificTargetType = destination
                            Else
                                haveWideningOutConversions += 1
                            End If
                        Else
                            Debug.Assert(Conversions.IsNarrowingConversion(conversionOut))
                        End If
                    Else
                        Debug.Assert(Conversions.IsNarrowingConversion(conversionIn))
 
                        If Conversions.IsIdentityConversion(conversionOut) Then
                            mostSpecificTargetType = destination
                        ElseIf Not Conversions.IsNarrowingConversion(conversionOut) Then
                            Debug.Assert(Conversions.IsWideningConversion(conversionOut) AndAlso Not Conversions.IsIdentityConversion(conversionOut))
                            ' Note that {Narrowing in, Widening (non-identity) out} operator is not considered as an applicable candidate.
                            ' In fact, an operator like this cannot exist unless nullable in/out conversions are involved.
                            ' Basically we would be dealing with an operator that converts from type derived from source to type derived from destination,
                            ' it would have to be defined in one of those types. When we collect operators we only visit source, destination and their
                            ' bases. So, in order for such an operator to be found, there must be an inheritance relationship between source and
                            ' destination and the operator must be defined in a type that is in between of them in the inheritance hierarchy. Thus,
                            ' there would be an inheritance relationship between parameter type ant return type of the operator, which makes the operator
                            ' inapplicable - there would be a predefined conversion between the types.
                            ' Ignoring an operator like this even when nullable conversions are involved, allows us to consider it as a candidate for a 
                            ' lifting, otherwise it would be treated as a not-lifted narrowing.
                            Continue For
                        Else
                            Debug.Assert(Conversions.IsNarrowingConversion(conversionOut))
                            ' When intrinsic types are involved, conversion from intrinsic type T to Nullable(Of S) can be narrowing,
                            ' it is always widening (if exists) for user-defined types. For reasons described above, we might need to ignore 
                            ' an operator like this.
                            If source.IsNullableType() AndAlso destination.IsNullableType() AndAlso
                               method.ReturnType.IsIntrinsicType() Then
                                Continue For
                            End If
                        End If
                    End If
 
                    applicable(currentIndex) = True
                    applicableCount += 1
                End If
            Next
 
            opSet.Clip(viableCandidates)
            conversionKinds.Clip(viableCandidates)
 
#If DEBUG Then
            For i As Integer = 0 To opSet.Count - 1
                Debug.Assert(opSet(i) IsNot Nothing)
            Next
#End If
            If bestMatch IsNot Nothing Then
                If bestMatchIsAmbiguous Then
                    bestMatch = Nothing
                End If
 
                Return True
            End If
 
            If applicableCount > 0 Then
                Debug.Assert(bestMatch Is Nothing)
                Debug.Assert(Not bestMatchIsAmbiguous)
 
                If applicableCount > 1 Then
                    ' Try to choose most specific among applicable candidates. 
                    Dim typeSet As ArrayBuilder(Of TypeSymbol) = Nothing
 
                    If mostSpecificSourceType Is Nothing Then
                        typeSet = ArrayBuilder(Of TypeSymbol).GetInstance()
 
                        For i As Integer = 0 To opSet.Count - 1
                            If Not applicable(i) Then
                                Continue For
                            End If
 
                            If haveWideningInConversions <> 0 Then
                                If Not Conversions.IsWideningConversion(conversionKinds(i).Key) Then
                                    Continue For
                                End If
                            Else
                                Debug.Assert(Conversions.IsNarrowingConversion(conversionKinds(i).Key))
                            End If
 
                            typeSet.Add(opSet(i).Parameters(0).Type)
                        Next
 
                        Debug.Assert(typeSet.Count = applicableCount OrElse (haveWideningInConversions <> 0 AndAlso typeSet.Count = haveWideningInConversions))
 
                        If haveWideningInConversions <> 0 Then
                            mostSpecificSourceType = MostEncompassed(typeSet, useSiteInfo)
                        Else
                            mostSpecificSourceType = MostEncompassing(typeSet, useSiteInfo)
                        End If
                    End If
 
                    If mostSpecificTargetType Is Nothing AndAlso mostSpecificSourceType IsNot Nothing Then
                        If typeSet Is Nothing Then
                            typeSet = ArrayBuilder(Of TypeSymbol).GetInstance()
                        Else
                            typeSet.Clear()
                        End If
 
                        For i As Integer = 0 To opSet.Count - 1
                            If Not applicable(i) Then
                                Continue For
                            End If
 
                            If haveWideningOutConversions <> 0 Then
                                If Not Conversions.IsWideningConversion(conversionKinds(i).Value) Then
                                    Continue For
                                End If
                            Else
                                Debug.Assert(Conversions.IsNarrowingConversion(conversionKinds(i).Value))
                            End If
 
                            typeSet.Add(opSet(i).ReturnType)
                        Next
 
                        Debug.Assert(typeSet.Count = applicableCount OrElse (haveWideningOutConversions <> 0 AndAlso typeSet.Count = haveWideningOutConversions))
 
                        If haveWideningOutConversions <> 0 Then
                            mostSpecificTargetType = MostEncompassing(typeSet, useSiteInfo)
                        Else
                            mostSpecificTargetType = MostEncompassed(typeSet, useSiteInfo)
                        End If
                    End If
 
                    If typeSet IsNot Nothing Then
                        typeSet.Free()
                    End If
 
                    If mostSpecificSourceType IsNot Nothing AndAlso mostSpecificTargetType IsNot Nothing Then
                        bestMatch = ChooseMostSpecificConversionOperator(opSet, applicable, mostSpecificSourceType, mostSpecificTargetType, bestMatchIsAmbiguous)
                    End If
 
                    If bestMatch IsNot Nothing AndAlso bestMatchIsAmbiguous Then
                        bestMatch = Nothing
                        Return True
                    End If
                Else
                    For i As Integer = 0 To opSet.Count - 1
                        If applicable(i) Then
                            bestMatch = opSet(i)
                            Exit For
                        End If
                    Next
 
                    Debug.Assert(bestMatch IsNot Nothing)
                End If
            End If
 
            Return bestMatch IsNot Nothing
        End Function
 
        ''' <summary>
        ''' The most encompassed type in a set of types is the one type that is encompassed
        ''' by all other types in the set. If no single type is encompassed by all other types,
        ''' then the set has no most encompassed type. In intuitive terms, the most encompassed
        ''' type is the "smallest" type in the set—the one type that can be converted from each
        ''' of the other types through a narrowing conversion.
        ''' </summary>
        Private Shared Function MostEncompassed(typeSet As ArrayBuilder(Of TypeSymbol), <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As TypeSymbol
            Dim best As TypeSymbol = Nothing
 
            For i As Integer = 0 To typeSet.Count - 1
                Dim type As TypeSymbol = typeSet(i)
 
                If best IsNot Nothing AndAlso best.IsSameTypeIgnoringAll(type) Then
                    Continue For
                End If
 
                Debug.Assert(Not type.IsErrorType())
 
                For j As Integer = 0 To typeSet.Count - 1
                    If i = j Then
                        Continue For
                    End If
 
                    Dim conv As ConversionKind = Conversions.ClassifyPredefinedConversion(type, typeSet(j), useSiteInfo)
 
                    If Not Conversions.IsWideningConversion(conv) Then
                        ' type is not encompassed by the other type
                        GoTo Next_i
                    End If
                Next
 
                If best Is Nothing Then
                    best = type
                Else
                    Debug.Assert(Not best.IsSameTypeIgnoringAll(type))
                    best = Nothing ' More than one type 
                    Exit For
                End If
Next_i:
            Next
 
            Return best
        End Function
 
        ''' <summary>
        ''' The most encompassing type in a set of types is the one type that encompasses
        ''' all other types in the set. If no single type encompasses all other types, then
        ''' the set has no most encompassing type. In intuitive terms, the most encompassing
        ''' type is the "largest" type in the set—the one type to which each of the other
        ''' types can be converted through a widening conversion.
        ''' </summary>
        Private Shared Function MostEncompassing(typeSet As ArrayBuilder(Of TypeSymbol), <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As TypeSymbol
            Dim best As TypeSymbol = Nothing
 
            For i As Integer = 0 To typeSet.Count - 1
                Dim type As TypeSymbol = typeSet(i)
 
                If best IsNot Nothing AndAlso best.IsSameTypeIgnoringAll(type) Then
                    Continue For
                End If
 
                Debug.Assert(Not type.IsErrorType())
 
                For j As Integer = 0 To typeSet.Count - 1
                    If i = j Then
                        Continue For
                    End If
 
                    Dim conv As ConversionKind = Conversions.ClassifyPredefinedConversion(typeSet(j), type, useSiteInfo)
 
                    If Not Conversions.IsWideningConversion(conv) Then
                        ' type is not encompassing the other type
                        GoTo Next_i
                    End If
                Next
 
                If best Is Nothing Then
                    best = type
                Else
                    Debug.Assert(Not best.IsSameTypeIgnoringAll(type))
                    best = Nothing ' More than one type 
                    Exit For
                End If
Next_i:
            Next
 
            Return best
        End Function
 
        Private Shared Function LeastGenericConversionOperator(
            method1 As MethodSymbol,
            method2 As MethodSymbol,
            <[In], Out()> ByRef bestDegreeOfGenericity As Integer,
            <[In], Out()> ByRef isAmbiguous As Boolean
        ) As MethodSymbol
            If bestDegreeOfGenericity = -1 Then
                bestDegreeOfGenericity = DetermineConversionOperatorDegreeOfGenericity(method1)
            End If
 
            Dim method2DegreeOfGenericity As Integer = DetermineConversionOperatorDegreeOfGenericity(method2)
 
            ' The less degree of genericity, the better.
            If bestDegreeOfGenericity < method2DegreeOfGenericity Then
                ' isAmbiguous state doesn't change.
                Return method1
            ElseIf method2DegreeOfGenericity < bestDegreeOfGenericity Then
                isAmbiguous = False
                bestDegreeOfGenericity = method2DegreeOfGenericity
                Return method2
            End If
 
            isAmbiguous = True
            Return method1
        End Function
 
        ''' <summary>
        ''' Returns number of types in the list of {input type, output type} that refer to a generic type parameter.
        ''' </summary>
        Private Shared Function DetermineConversionOperatorDegreeOfGenericity(method As MethodSymbol) As Integer
            If Not method.ContainingType.IsGenericType Then
                Return 0
            End If
 
            Dim result As Integer = 0
            Dim definition As MethodSymbol = method.OriginalDefinition
 
            If DetectReferencesToGenericParameters(definition.Parameters(0).Type, TypeParameterKind.Type, BitVector.Null) <> TypeParameterKind.None Then
                result += 1
            End If
 
            If DetectReferencesToGenericParameters(definition.ReturnType, TypeParameterKind.Type, BitVector.Null) <> TypeParameterKind.None Then
                result += 1
            End If
 
            Debug.Assert(result > 0)
            Return result
        End Function
 
        ''' <summary>
        ''' A quick check whether given conversion operator is a widening operator.
        ''' </summary>
        Friend Shared Function IsWidening(method As MethodSymbol) As Boolean
            Debug.Assert(method.MethodKind = MethodKind.Conversion)
            Dim forth As Char = method.Name(3)
 
            If forth = "I"c OrElse forth = "i"c Then
                Return True
            End If
 
            Debug.Assert(forth = "E"c OrElse forth = "e"c)
            Return False
        End Function
 
        ''' <summary>
        ''' Collect user-defined conversion operators.
        ''' Operators declared in the same type are grouped together. 
        ''' Within a group, widening operators are followed by narrowing operators.
        ''' </summary>
        Private Shared Sub CollectUserDefinedConversionOperators(
            source As TypeSymbol,
            destination As TypeSymbol,
            opSet As ArrayBuilder(Of MethodSymbol),
            <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
        )
            CollectUserDefinedOperators(source, destination, MethodKind.Conversion,
                                        WellKnownMemberNames.ImplicitConversionName, New OperatorInfo(UnaryOperatorKind.Implicit),
                                        WellKnownMemberNames.ExplicitConversionName, New OperatorInfo(UnaryOperatorKind.Explicit),
                                        opSet, useSiteInfo)
        End Sub
 
        ''' <summary>
        ''' Collect user-defined operators.
        ''' Operators declared in the same type are grouped together. 
        ''' Within a group, name1 operators are followed by name2 operators.
        ''' </summary>
        Friend Shared Sub CollectUserDefinedOperators(
            type1 As TypeSymbol,
            type2 As TypeSymbol,
            opKind As MethodKind,
            name1 As String,
            name1Info As OperatorInfo,
            name2Opt As String,
            name2InfoOpt As OperatorInfo,
            opSet As ArrayBuilder(Of MethodSymbol),
            <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
        )
            type1 = GetTypeToLookForOperatorsIn(type1, useSiteInfo)
 
            If type2 IsNot Nothing Then
                type2 = GetTypeToLookForOperatorsIn(type2, useSiteInfo)
            End If
 
            Dim commonAncestor As NamedTypeSymbol = Nothing
 
            If type1 IsNot Nothing AndAlso type1.Kind = SymbolKind.NamedType AndAlso Not type1.IsInterfaceType() Then
                Dim current = DirectCast(type1, NamedTypeSymbol)
 
                Do
                    If type2 IsNot Nothing AndAlso commonAncestor Is Nothing AndAlso
                       type2.IsOrDerivedFrom(current, useSiteInfo) Then
                        commonAncestor = current
                    End If
 
                    ' Note, intentionally using non-short-circuiting Or operator.
                    If CollectUserDefinedOperators(current, name1, opKind, name1Info, opSet) Or
                       (name2Opt IsNot Nothing AndAlso CollectUserDefinedOperators(current, name2Opt, opKind, name2InfoOpt, opSet)) Then
                        Exit Do
                    End If
 
                    current = current.BaseTypeWithDefinitionUseSiteDiagnostics(useSiteInfo)
                Loop While current IsNot Nothing
            End If
 
            If type2 IsNot Nothing AndAlso type2.Kind = SymbolKind.NamedType AndAlso Not type2.IsInterfaceType() Then
                Dim current = DirectCast(type2, NamedTypeSymbol)
 
                Do
                    If commonAncestor IsNot Nothing AndAlso commonAncestor.IsSameTypeIgnoringAll(current) Then
                        Exit Do
                    End If
 
                    ' Note, intentionally using non-short-circuiting Or operator.
                    If CollectUserDefinedOperators(current, name1, opKind, name1Info, opSet) Or
                       (name2Opt IsNot Nothing AndAlso CollectUserDefinedOperators(current, name2Opt, opKind, name2InfoOpt, opSet)) Then
                        Exit Do
                    End If
 
                    current = current.BaseTypeWithDefinitionUseSiteDiagnostics(useSiteInfo)
                Loop While current IsNot Nothing
            End If
 
        End Sub
 
        ''' <summary>
        ''' Returns True if we should stop climbing inheritance hierarchy.
        ''' </summary>
        Private Shared Function CollectUserDefinedOperators(
            type As TypeSymbol,
            opName As String,
            opKind As MethodKind,
            opInfo As OperatorInfo,
            opSet As ArrayBuilder(Of MethodSymbol)
        ) As Boolean
            Dim stopClimbing As Boolean = False
 
            For Each member In type.GetMembers(opName)
                If member.Kind = SymbolKind.Method Then
                    Dim method = DirectCast(member, MethodSymbol)
 
                    If method.MethodKind = opKind Then
                        If method.IsShadows Then
                            stopClimbing = True
                        End If
 
                        ' Operators that were declared in syntax may not satisfy all the constraints on user-defined operators -
                        ' they require extra validation.
                        If Not method.IsMethodKindBasedOnSyntax OrElse ValidateOverloadedOperator(method.OriginalDefinition, opInfo) Then
                            opSet.Add(method)
                        End If
                    End If
                End If
            Next
 
            Return stopClimbing
        End Function
 
        ''' <summary>
        ''' Given the type of operator's argument, return corresponding type to
        ''' look for operator in. Can return Nothing.
        ''' </summary>
        Private Shared Function GetTypeToLookForOperatorsIn(type As TypeSymbol, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As TypeSymbol
            type = type.GetNullableUnderlyingTypeOrSelf()
 
            If type.Kind = SymbolKind.TypeParameter Then
                type = DirectCast(type, TypeParameterSymbol).GetNonInterfaceConstraint(useSiteInfo)
            End If
 
            Return type
        End Function
 
        Public Shared Function ResolveIsTrueOperator(argument As BoundExpression, binder As Binder, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As OverloadResolutionResult
            Dim opSet = ArrayBuilder(Of MethodSymbol).GetInstance()
 
            CollectUserDefinedOperators(argument.Type, Nothing, MethodKind.UserDefinedOperator,
                                        WellKnownMemberNames.TrueOperatorName, New OperatorInfo(UnaryOperatorKind.IsTrue),
                                        Nothing, Nothing,
                                        opSet, useSiteInfo)
 
            Dim result = OperatorInvocationOverloadResolution(opSet, argument, Nothing, binder, lateBindingIsAllowed:=False, includeEliminatedCandidates:=False,
                                                              useSiteInfo:=useSiteInfo)
            opSet.Free()
            Return result
        End Function
 
        Public Shared Function ResolveIsFalseOperator(argument As BoundExpression, binder As Binder, <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)) As OverloadResolutionResult
            Dim opSet = ArrayBuilder(Of MethodSymbol).GetInstance()
 
            CollectUserDefinedOperators(argument.Type, Nothing, MethodKind.UserDefinedOperator,
                                        WellKnownMemberNames.FalseOperatorName, New OperatorInfo(UnaryOperatorKind.IsFalse),
                                        Nothing, Nothing,
                                        opSet, useSiteInfo)
 
            Dim result = OperatorInvocationOverloadResolution(opSet, argument, Nothing, binder, lateBindingIsAllowed:=False, includeEliminatedCandidates:=False,
                                                              useSiteInfo:=useSiteInfo)
            opSet.Free()
            Return result
        End Function
 
        Public Shared Function ResolveUserDefinedUnaryOperator(
            argument As BoundExpression,
            opKind As UnaryOperatorKind,
            binder As Binder,
            <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol),
            Optional includeEliminatedCandidates As Boolean = False
        ) As OverloadResolutionResult
            Dim opSet = ArrayBuilder(Of MethodSymbol).GetInstance()
 
            Select Case opKind
                Case UnaryOperatorKind.Not
                    Dim opInfo As New OperatorInfo(UnaryOperatorKind.Not)
                    CollectUserDefinedOperators(argument.Type, Nothing, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.OnesComplementOperatorName, opInfo,
                                                WellKnownMemberNames.LogicalNotOperatorName, opInfo,
                                                opSet, useSiteInfo)
                Case UnaryOperatorKind.Minus
                    CollectUserDefinedOperators(argument.Type, Nothing, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.UnaryNegationOperatorName, New OperatorInfo(UnaryOperatorKind.Minus),
                                                Nothing, Nothing,
                                                opSet, useSiteInfo)
                Case UnaryOperatorKind.Plus
                    CollectUserDefinedOperators(argument.Type, Nothing, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.UnaryPlusOperatorName, New OperatorInfo(UnaryOperatorKind.Minus),
                                                Nothing, Nothing,
                                                opSet, useSiteInfo)
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(opKind)
            End Select
 
            Dim result = OperatorInvocationOverloadResolution(opSet, argument, Nothing, binder, lateBindingIsAllowed:=False, includeEliminatedCandidates:=includeEliminatedCandidates,
                                                              useSiteInfo:=useSiteInfo)
            opSet.Free()
            Return result
        End Function
 
        Public Shared Function ResolveUserDefinedBinaryOperator(
            left As BoundExpression,
            right As BoundExpression,
            opKind As BinaryOperatorKind,
            binder As Binder,
            <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol),
            Optional includeEliminatedCandidates As Boolean = False
        ) As OverloadResolutionResult
            Dim opSet = ArrayBuilder(Of MethodSymbol).GetInstance()
 
            Select Case opKind
                Case BinaryOperatorKind.Add
                    CollectUserDefinedOperators(left.Type, right.Type, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.AdditionOperatorName, New OperatorInfo(opKind),
                                                Nothing, Nothing,
                                                opSet, useSiteInfo)
                Case BinaryOperatorKind.Subtract
                    CollectUserDefinedOperators(left.Type, right.Type, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.SubtractionOperatorName, New OperatorInfo(opKind),
                                                Nothing, Nothing,
                                                opSet, useSiteInfo)
                Case BinaryOperatorKind.Multiply
                    CollectUserDefinedOperators(left.Type, right.Type, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.MultiplyOperatorName, New OperatorInfo(opKind),
                                                Nothing, Nothing,
                                                opSet, useSiteInfo)
                Case BinaryOperatorKind.Divide
                    CollectUserDefinedOperators(left.Type, right.Type, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.DivisionOperatorName, New OperatorInfo(opKind),
                                                Nothing, Nothing,
                                                opSet, useSiteInfo)
                Case BinaryOperatorKind.IntegerDivide
                    CollectUserDefinedOperators(left.Type, right.Type, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.IntegerDivisionOperatorName, New OperatorInfo(opKind),
                                                Nothing, Nothing,
                                                opSet, useSiteInfo)
                Case BinaryOperatorKind.Modulo
                    CollectUserDefinedOperators(left.Type, right.Type, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.ModulusOperatorName, New OperatorInfo(opKind),
                                                Nothing, Nothing,
                                                opSet, useSiteInfo)
                Case BinaryOperatorKind.Power
                    CollectUserDefinedOperators(left.Type, right.Type, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.ExponentOperatorName, New OperatorInfo(opKind),
                                                Nothing, Nothing,
                                                opSet, useSiteInfo)
                Case BinaryOperatorKind.Equals
                    CollectUserDefinedOperators(left.Type, right.Type, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.EqualityOperatorName, New OperatorInfo(opKind),
                                                Nothing, Nothing,
                                                opSet, useSiteInfo)
                Case BinaryOperatorKind.NotEquals
                    CollectUserDefinedOperators(left.Type, right.Type, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.InequalityOperatorName, New OperatorInfo(opKind),
                                                Nothing, Nothing,
                                                opSet, useSiteInfo)
                Case BinaryOperatorKind.LessThan
                    CollectUserDefinedOperators(left.Type, right.Type, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.LessThanOperatorName, New OperatorInfo(opKind),
                                                Nothing, Nothing,
                                                opSet, useSiteInfo)
                Case BinaryOperatorKind.GreaterThan
                    CollectUserDefinedOperators(left.Type, right.Type, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.GreaterThanOperatorName, New OperatorInfo(opKind),
                                                Nothing, Nothing,
                                                opSet, useSiteInfo)
                Case BinaryOperatorKind.LessThanOrEqual
                    CollectUserDefinedOperators(left.Type, right.Type, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.LessThanOrEqualOperatorName, New OperatorInfo(opKind),
                                                Nothing, Nothing,
                                                opSet, useSiteInfo)
                Case BinaryOperatorKind.GreaterThanOrEqual
                    CollectUserDefinedOperators(left.Type, right.Type, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.GreaterThanOrEqualOperatorName, New OperatorInfo(opKind),
                                                Nothing, Nothing,
                                                opSet, useSiteInfo)
                Case BinaryOperatorKind.Like
                    CollectUserDefinedOperators(left.Type, right.Type, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.LikeOperatorName, New OperatorInfo(opKind),
                                                Nothing, Nothing,
                                                opSet, useSiteInfo)
                Case BinaryOperatorKind.Concatenate
                    CollectUserDefinedOperators(left.Type, right.Type, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.ConcatenateOperatorName, New OperatorInfo(opKind),
                                                Nothing, Nothing,
                                                opSet, useSiteInfo)
                Case BinaryOperatorKind.And, BinaryOperatorKind.AndAlso
                    Dim opInfo As New OperatorInfo(opKind)
                    CollectUserDefinedOperators(left.Type, right.Type, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.BitwiseAndOperatorName, opInfo,
                                                WellKnownMemberNames.LogicalAndOperatorName, opInfo,
                                                opSet, useSiteInfo)
 
                Case BinaryOperatorKind.Or, BinaryOperatorKind.OrElse
                    Dim opInfo As New OperatorInfo(opKind)
                    CollectUserDefinedOperators(left.Type, right.Type, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.BitwiseOrOperatorName, opInfo,
                                                WellKnownMemberNames.LogicalOrOperatorName, opInfo,
                                                opSet, useSiteInfo)
                Case BinaryOperatorKind.Xor
                    CollectUserDefinedOperators(left.Type, right.Type, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.ExclusiveOrOperatorName, New OperatorInfo(opKind),
                                                Nothing, Nothing,
                                                opSet, useSiteInfo)
                Case BinaryOperatorKind.LeftShift
                    Dim opInfo As New OperatorInfo(opKind)
                    CollectUserDefinedOperators(left.Type, right.Type, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.LeftShiftOperatorName, opInfo,
                                                WellKnownMemberNames.UnsignedLeftShiftOperatorName, opInfo,
                                                opSet, useSiteInfo)
                Case BinaryOperatorKind.RightShift
                    Dim opInfo As New OperatorInfo(opKind)
                    CollectUserDefinedOperators(left.Type, right.Type, MethodKind.UserDefinedOperator,
                                                WellKnownMemberNames.RightShiftOperatorName, opInfo,
                                                WellKnownMemberNames.UnsignedRightShiftOperatorName, opInfo,
                                                opSet, useSiteInfo)
                Case Else
                    Throw ExceptionUtilities.UnexpectedValue(opKind)
            End Select
 
            Dim result = OperatorInvocationOverloadResolution(opSet, left, right, binder, lateBindingIsAllowed:=True, includeEliminatedCandidates:=includeEliminatedCandidates,
                                                              useSiteInfo:=useSiteInfo)
            opSet.Free()
            Return result
        End Function
 
        Private Shared Function OperatorInvocationOverloadResolution(
            opSet As ArrayBuilder(Of MethodSymbol),
            argument1 As BoundExpression,
            argument2 As BoundExpression,
            binder As Binder,
            lateBindingIsAllowed As Boolean,
            includeEliminatedCandidates As Boolean,
            <[In], Out> ByRef useSiteInfo As CompoundUseSiteInfo(Of AssemblySymbol)
        ) As OverloadResolutionResult
 
            If opSet.Count = 0 Then
                If lateBindingIsAllowed Then
                    Return New OverloadResolutionResult(ImmutableArray(Of CandidateAnalysisResult).Empty,
                                                        argument1.Type.IsObjectType OrElse argument2.Type.IsObjectType,
                                                        False,
                                                        asyncLambdaSubToFunctionMismatch:=Nothing)
                Else
                    Return New OverloadResolutionResult(ImmutableArray(Of CandidateAnalysisResult).Empty, False, False, asyncLambdaSubToFunctionMismatch:=Nothing)
                End If
            End If
 
            Dim nullableOfT As NamedTypeSymbol = opSet(0).ContainingAssembly.GetSpecialType(SpecialType.System_Nullable_T)
            Dim liftOperators As Boolean = nullableOfT.GetUseSiteInfo().DiagnosticInfo Is Nothing
 
            Dim candidates = ArrayBuilder(Of CandidateAnalysisResult).GetInstance()
 
            For Each method In opSet
                Debug.Assert(method.ParameterCount = If(argument2 Is Nothing, 1, 2))
                Debug.Assert(method.Arity = 0)
                Debug.Assert(Not method.IsSub)
 
                If method.HasUnsupportedMetadata Then
                    If includeEliminatedCandidates Then
                        candidates.Add(New CandidateAnalysisResult(New OperatorCandidate(method), CandidateAnalysisResultState.HasUnsupportedMetadata))
                    End If
 
                    Continue For
                End If
 
                Dim methodUseSiteInfo As UseSiteInfo(Of AssemblySymbol) = method.GetUseSiteInfo()
                useSiteInfo.Add(methodUseSiteInfo)
 
                If methodUseSiteInfo.DiagnosticInfo IsNot Nothing Then
                    If includeEliminatedCandidates Then
                        candidates.Add(New CandidateAnalysisResult(New OperatorCandidate(method), CandidateAnalysisResultState.HasUseSiteError))
                    End If
 
                    Continue For
                End If
 
                CombineCandidates(candidates, New CandidateAnalysisResult(New OperatorCandidate(method)), method.ParameterCount, Nothing, useSiteInfo)
 
                If liftOperators Then
                    Dim param1 As ParameterSymbol = method.Parameters(0)
                    Dim type1 As TypeSymbol = param1.Type
                    Dim isNullable1 As Boolean = type1.IsNullableType()
                    Dim canLift1 As Boolean = Not isNullable1 AndAlso type1.IsValueType() AndAlso Not type1.IsRestrictedType()
 
                    Dim param2 As ParameterSymbol = Nothing
                    Dim type2 As TypeSymbol = Nothing
                    Dim isNullable2 As Boolean = False
                    Dim canLift2 As Boolean = False
 
                    If argument2 IsNot Nothing AndAlso Not isNullable1 Then
                        param2 = method.Parameters(1)
                        type2 = param2.Type
                        isNullable2 = type2.IsNullableType()
                        canLift2 = Not isNullable2 AndAlso type2.IsValueType() AndAlso Not type2.IsRestrictedType()
                    End If
 
                    If (canLift1 OrElse canLift2) AndAlso Not isNullable1 AndAlso Not isNullable2 Then
                        ' Should lift this operator.
                        If canLift1 Then
                            param1 = LiftParameterSymbol(param1, nullableOfT)
                        End If
 
                        If canLift2 Then
                            param2 = LiftParameterSymbol(param2, nullableOfT)
                        End If
 
                        Dim returnType As TypeSymbol = method.ReturnType
                        If CanLiftType(returnType) Then
                            returnType = nullableOfT.Construct(returnType)
                        End If
 
                        CombineCandidates(candidates,
                                          New CandidateAnalysisResult(New LiftedOperatorCandidate(method,
                                                                                               If(argument2 Is Nothing,
                                                                                                  ImmutableArray.Create(param1),
                                                                                                  ImmutableArray.Create(Of ParameterSymbol)(param1, param2)),
                                                                                              returnType)),
                                          method.ParameterCount, Nothing, useSiteInfo)
                    End If
                End If
            Next
 
            ' TODO: Need to get rid of dependency on method group. For now create fake one.
            Dim methodGroup = New BoundMethodGroup(argument1.Syntax, Nothing, ImmutableArray(Of MethodSymbol).Empty, LookupResultKind.Good, Nothing, QualificationKind.Unqualified)
 
            Dim result As OverloadResolutionResult = ResolveOverloading(methodGroup, candidates,
                                                                        If(argument2 Is Nothing,
                                                                           ImmutableArray.Create(argument1),
                                                                           ImmutableArray.Create(Of BoundExpression)(argument1, argument2)),
                                                                        Nothing, Nothing, lateBindingIsAllowed, binder:=binder,
                                                                        asyncLambdaSubToFunctionMismatch:=Nothing,
                                                                        callerInfoOpt:=Nothing, forceExpandedForm:=False,
                                                                        useSiteInfo:=useSiteInfo)
            candidates.Free()
 
            Return result
        End Function
 
        Friend Shared Function CanLiftType(type As TypeSymbol) As Boolean
            Return Not type.IsNullableType AndAlso type.IsValueType() AndAlso Not type.IsRestrictedType()
        End Function
 
        Friend Shared Function IsValidInLiftedSignature(type As TypeSymbol) As Boolean
            Return type.IsNullableType() ' Note, Dev11 has changed implementation of this function, I've taken this into account. 
        End Function
 
        Private Shared Function LiftParameterSymbol(param As ParameterSymbol, nullableOfT As NamedTypeSymbol) As ParameterSymbol
 
            If param.IsDefinition Then
                Return New LiftedParameterSymbol(param, nullableOfT.Construct(param.Type))
            Else
                Dim definition As ParameterSymbol = param.OriginalDefinition
 
                Return SubstitutedParameterSymbol.CreateMethodParameter(DirectCast(param.ContainingSymbol, SubstitutedMethodSymbol),
                                                                        New LiftedParameterSymbol(definition, nullableOfT.Construct(definition.Type)))
            End If
        End Function
 
        Private NotInheritable Class LiftedParameterSymbol
            Inherits ParameterSymbol
 
            Private ReadOnly _parameterToLift As ParameterSymbol
            Private ReadOnly _type As TypeSymbol
 
            Public Sub New(parameter As ParameterSymbol, type As TypeSymbol)
                Debug.Assert(parameter.IsDefinition)
                Debug.Assert(type.IsNullableType())
                _parameterToLift = parameter
                _type = type
            End Sub
 
            Public Overrides ReadOnly Property Name As String
                Get
                    Return _parameterToLift.Name
                End Get
            End Property
 
            Friend Overrides Function GetUseSiteInfo() As UseSiteInfo(Of AssemblySymbol)
                Return _parameterToLift.GetUseSiteInfo()
            End Function
 
            Public Overrides ReadOnly Property ContainingSymbol As Symbol
                Get
                    Return _parameterToLift.ContainingSymbol
                End Get
            End Property
 
            Public Overrides ReadOnly Property CustomModifiers As ImmutableArray(Of CustomModifier)
                Get
                    Return _parameterToLift.CustomModifiers
                End Get
            End Property
 
            Public Overrides ReadOnly Property RefCustomModifiers As ImmutableArray(Of CustomModifier)
                Get
                    Return _parameterToLift.RefCustomModifiers
                End Get
            End Property
 
            Public Overrides ReadOnly Property DeclaringSyntaxReferences As ImmutableArray(Of SyntaxReference)
                Get
                    Return ImmutableArray(Of SyntaxReference).Empty
                End Get
            End Property
 
            Friend Overrides ReadOnly Property ExplicitDefaultConstantValue(inProgress As SymbolsInProgress(Of ParameterSymbol)) As ConstantValue
                Get
                    Return _parameterToLift.ExplicitDefaultConstantValue(inProgress)
                End Get
            End Property
 
            Public Overrides ReadOnly Property HasExplicitDefaultValue As Boolean
                Get
                    Return _parameterToLift.HasExplicitDefaultValue
                End Get
            End Property
 
            Public Overrides ReadOnly Property IsByRef As Boolean
                Get
                    Return _parameterToLift.IsByRef
                End Get
            End Property
 
            Friend Overrides ReadOnly Property IsExplicitByRef As Boolean
                Get
                    Return _parameterToLift.IsExplicitByRef
                End Get
            End Property
 
            Public Overrides ReadOnly Property IsOptional As Boolean
                Get
                    Return _parameterToLift.IsOptional
                End Get
            End Property
 
            Friend Overrides ReadOnly Property IsMetadataOut As Boolean
                Get
                    Return _parameterToLift.IsMetadataOut
                End Get
            End Property
 
            Friend Overrides ReadOnly Property IsMetadataIn As Boolean
                Get
                    Return _parameterToLift.IsMetadataIn
                End Get
            End Property
 
            Friend Overrides ReadOnly Property MarshallingInformation As MarshalPseudoCustomAttributeData
                Get
                    Return _parameterToLift.MarshallingInformation
                End Get
            End Property
 
            Friend Overrides ReadOnly Property HasOptionCompare As Boolean
                Get
                    Return _parameterToLift.HasOptionCompare
                End Get
            End Property
 
            Friend Overrides ReadOnly Property IsIDispatchConstant As Boolean
                Get
                    Return _parameterToLift.IsIDispatchConstant
                End Get
            End Property
 
            Friend Overrides ReadOnly Property IsIUnknownConstant As Boolean
                Get
                    Return _parameterToLift.IsIUnknownConstant
                End Get
            End Property
 
            Friend Overrides ReadOnly Property IsCallerLineNumber As Boolean
                Get
                    Return _parameterToLift.IsCallerLineNumber
                End Get
            End Property
 
            Friend Overrides ReadOnly Property IsCallerMemberName As Boolean
                Get
                    Return _parameterToLift.IsCallerMemberName
                End Get
            End Property
 
            Friend Overrides ReadOnly Property IsCallerFilePath As Boolean
                Get
                    Return _parameterToLift.IsCallerFilePath
                End Get
            End Property
 
            Friend Overrides ReadOnly Property CallerArgumentExpressionParameterIndex As Integer
                Get
                    Throw ExceptionUtilities.Unreachable
                End Get
            End Property
 
            Public Overrides ReadOnly Property IsParamArray As Boolean
                Get
                    Return _parameterToLift.IsParamArray
                End Get
            End Property
 
            Public Overrides ReadOnly Property Locations As ImmutableArray(Of Location)
                Get
                    Return ImmutableArray(Of Location).Empty
                End Get
            End Property
 
            Public Overrides ReadOnly Property Ordinal As Integer
                Get
                    Return _parameterToLift.Ordinal
                End Get
            End Property
 
            Public Overrides ReadOnly Property Type As TypeSymbol
                Get
                    Return _type
                End Get
            End Property
        End Class
    End Class
 
End Namespace