|
' 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.Runtime.InteropServices
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic
' Binding of binary and unary operators is implemented in this part.
Partial Friend Class Binder
Private Function BindIsExpression(
node As BinaryExpressionSyntax,
diagnostics As BindingDiagnosticBag
) As BoundExpression
Debug.Assert(node.Kind = SyntaxKind.IsExpression OrElse node.Kind = SyntaxKind.IsNotExpression)
Dim [isNot] As Boolean = (node.Kind = SyntaxKind.IsNotExpression)
' The function below will make sure they are RValues.
Dim left As BoundExpression = BindExpression(node.Left, diagnostics)
Dim right As BoundExpression = BindExpression(node.Right, diagnostics)
Return BindIsExpression(left, right, node, [isNot], diagnostics)
End Function
Private Function BindIsExpression(
left As BoundExpression,
right As BoundExpression,
node As SyntaxNode,
[isNot] As Boolean,
diagnostics As BindingDiagnosticBag
) As BoundExpression
left = MakeRValue(left, diagnostics)
right = MakeRValue(right, diagnostics)
' Suppress Lock conversion warnings for Is operator.
Dim needsFilterDiagnostics = diagnostics.AccumulatesDiagnostics
Dim conversionDiagnostics = If(needsFilterDiagnostics, BindingDiagnosticBag.GetInstance(diagnostics), diagnostics)
left = ValidateAndConvertIsExpressionArgument(left, right, [isNot], conversionDiagnostics)
right = ValidateAndConvertIsExpressionArgument(right, left, [isNot], conversionDiagnostics)
If needsFilterDiagnostics Then
Debug.Assert(conversionDiagnostics IsNot diagnostics)
diagnostics.AddDependencies(conversionDiagnostics)
Dim sourceBag = conversionDiagnostics.DiagnosticBag
Debug.Assert(sourceBag IsNot Nothing)
If Not sourceBag.IsEmptyWithoutResolution Then
For Each diagnostic In sourceBag.AsEnumerableWithoutResolution()
Dim code As Integer
Dim diagnosticWithInfo = TryCast(diagnostic, DiagnosticWithInfo)
If diagnosticWithInfo IsNot Nothing AndAlso diagnosticWithInfo.HasLazyInfo Then
code = diagnosticWithInfo.LazyInfo.Code
Else
code = diagnostic.Code
End If
If code <> ERRID.WRN_ConvertingLock Then
diagnostics.Add(diagnostic)
End If
Next
End If
conversionDiagnostics.Free()
End If
Dim result As BoundExpression
Dim booleanType = GetSpecialType(SpecialType.System_Boolean, node, diagnostics)
result = New BoundBinaryOperator(node,
If([isNot], BinaryOperatorKind.IsNot, BinaryOperatorKind.Is),
left,
right,
checked:=False,
type:=booleanType,
hasErrors:=booleanType.IsErrorType())
' TODO: Add rewrite for Nullable.
Return result
End Function
''' <summary>
''' Validate and apply appropriate conversion for the target argument of Is/IsNot expression.
''' </summary>
Private Function ValidateAndConvertIsExpressionArgument(
targetArgument As BoundExpression,
otherArgument As BoundExpression,
[isNot] As Boolean,
diagnostics As BindingDiagnosticBag
) As BoundExpression
Dim targetArgumentType As TypeSymbol = targetArgument.Type
Dim result As BoundExpression
If targetArgument.IsNothingLiteral() Then
result = targetArgument
ElseIf targetArgumentType.IsErrorType() Then
result = targetArgument
ElseIf targetArgumentType.IsReferenceType Then
result = ApplyImplicitConversion(targetArgument.Syntax,
GetSpecialType(SpecialType.System_Object, targetArgument.Syntax, diagnostics),
targetArgument,
diagnostics)
ElseIf targetArgumentType.IsNullableType() Then
If Not otherArgument.HasErrors AndAlso Not otherArgument.IsNothingLiteral() Then
ReportDiagnostic(diagnostics, targetArgument.Syntax,
If([isNot], ERRID.ERR_IsNotOperatorNullable1, ERRID.ERR_IsOperatorNullable1),
targetArgumentType)
End If
result = targetArgument
ElseIf targetArgumentType.IsTypeParameter() AndAlso Not targetArgumentType.IsValueType Then
If Not otherArgument.HasErrors AndAlso Not otherArgument.IsNothingLiteral() Then
ReportDiagnostic(diagnostics, targetArgument.Syntax,
If([isNot], ERRID.ERR_IsNotOperatorGenericParam1, ERRID.ERR_IsOperatorGenericParam1),
targetArgumentType)
End If
' If any of the left or right operands of the Is or IsNot operands
' are entities of type parameters types, then they need to be boxed.
result = ApplyImplicitConversion(targetArgument.Syntax,
GetSpecialType(SpecialType.System_Object, targetArgument.Syntax, diagnostics),
targetArgument,
diagnostics)
Else
ReportDiagnostic(diagnostics, targetArgument.Syntax,
If([isNot], ERRID.ERR_IsNotOpRequiresReferenceTypes1, ERRID.ERR_IsOperatorRequiresReferenceTypes1),
targetArgumentType)
result = targetArgument
End If
Return result
End Function
Private Function BindBinaryOperator(
node As BinaryExpressionSyntax,
isOperandOfConditionalBranch As Boolean,
diagnostics As BindingDiagnosticBag
) As BoundExpression
' Some tools, such as ASP .NET, generate expressions containing thousands
' of string concatenations. For this reason, for string concatenations,
' avoid the usual recursion along the left side of the parse. Also, attempt
' to flatten whole sequences of string literal concatenations to avoid
' allocating space for intermediate results.
Dim preliminaryOperatorKind As BinaryOperatorKind = OverloadResolution.MapBinaryOperatorKind(node.Kind)
Dim propagateIsOperandOfConditionalBranch = isOperandOfConditionalBranch AndAlso
(preliminaryOperatorKind = BinaryOperatorKind.AndAlso OrElse
preliminaryOperatorKind = BinaryOperatorKind.OrElse)
Dim binary As BinaryExpressionSyntax = node
Dim child As ExpressionSyntax
Do
child = binary.Left
Select Case child.Kind
Case SyntaxKind.AddExpression,
SyntaxKind.ConcatenateExpression,
SyntaxKind.LikeExpression,
SyntaxKind.EqualsExpression,
SyntaxKind.NotEqualsExpression,
SyntaxKind.LessThanOrEqualExpression,
SyntaxKind.GreaterThanOrEqualExpression,
SyntaxKind.LessThanExpression,
SyntaxKind.GreaterThanExpression,
SyntaxKind.SubtractExpression,
SyntaxKind.MultiplyExpression,
SyntaxKind.ExponentiateExpression,
SyntaxKind.DivideExpression,
SyntaxKind.ModuloExpression,
SyntaxKind.IntegerDivideExpression,
SyntaxKind.LeftShiftExpression,
SyntaxKind.RightShiftExpression,
SyntaxKind.ExclusiveOrExpression,
SyntaxKind.OrExpression,
SyntaxKind.AndExpression
If propagateIsOperandOfConditionalBranch Then
Exit Do
End If
Case SyntaxKind.OrElseExpression,
SyntaxKind.AndAlsoExpression
Exit Select
Case Else
Exit Do
End Select
binary = DirectCast(child, BinaryExpressionSyntax)
Loop
Dim left As BoundExpression = BindValue(child, diagnostics, propagateIsOperandOfConditionalBranch)
Do
binary = DirectCast(child.Parent, BinaryExpressionSyntax)
Dim right As BoundExpression = BindValue(binary.Right, diagnostics, propagateIsOperandOfConditionalBranch)
left = BindBinaryOperator(binary, left, right, binary.OperatorToken.Kind,
OverloadResolution.MapBinaryOperatorKind(binary.Kind),
If(binary Is node, isOperandOfConditionalBranch, propagateIsOperandOfConditionalBranch),
diagnostics)
child = binary
Loop While child IsNot node
Return left
End Function
Private Function BindBinaryOperator(
node As SyntaxNode,
left As BoundExpression,
right As BoundExpression,
operatorTokenKind As SyntaxKind,
preliminaryOperatorKind As BinaryOperatorKind,
isOperandOfConditionalBranch As Boolean,
diagnostics As BindingDiagnosticBag,
Optional isSelectCase As Boolean = False
) As BoundExpression
Debug.Assert(left.IsValue)
Debug.Assert(right.IsValue)
Dim originalDiagnostics = diagnostics
If (left.HasErrors OrElse right.HasErrors) Then
' Suppress any additional diagnostics by overriding DiagnosticBag.
diagnostics = BindingDiagnosticBag.Discarded
End If
' Deal with NOTHING literal as an input.
ConvertNothingLiterals(preliminaryOperatorKind, left, right, diagnostics)
left = MakeRValue(left, diagnostics)
right = MakeRValue(right, diagnostics)
If (left.HasErrors OrElse right.HasErrors) Then
' Suppress any additional diagnostics by overriding DiagnosticBag.
If diagnostics Is originalDiagnostics Then
diagnostics = BindingDiagnosticBag.Discarded
End If
End If
Dim leftType As TypeSymbol = left.Type
Dim rightType As TypeSymbol = right.Type
Dim leftIsDBNull As Boolean = leftType.IsDBNullType()
Dim rightIsDBNull As Boolean = rightType.IsDBNullType()
'§11.16 Concatenation Operator
'A System.DBNull value is converted to the literal Nothing typed as String.
If (preliminaryOperatorKind = BinaryOperatorKind.Concatenate AndAlso leftIsDBNull <> rightIsDBNull) OrElse
(preliminaryOperatorKind = BinaryOperatorKind.Add AndAlso
((leftType.IsStringType() AndAlso rightIsDBNull) OrElse (leftIsDBNull AndAlso rightType.IsStringType))) Then
Debug.Assert(leftIsDBNull Xor rightIsDBNull)
If leftIsDBNull Then
leftType = SubstituteDBNullWithNothingString(left, rightType, diagnostics)
Else
rightType = SubstituteDBNullWithNothingString(right, leftType, diagnostics)
End If
End If
' For comparison operators, the result 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.
Dim intrinsicOperatorType As SpecialType = SpecialType.None
Dim userDefinedOperator As OverloadResolution.OverloadResolutionResult = Nothing
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
Dim operatorKind As BinaryOperatorKind = OverloadResolution.ResolveBinaryOperator(preliminaryOperatorKind, left, right, Me,
True,
intrinsicOperatorType,
userDefinedOperator,
useSiteInfo)
If diagnostics.Add(node, useSiteInfo) Then
' Suppress additional diagnostics
diagnostics = BindingDiagnosticBag.Discarded
End If
If operatorKind = BinaryOperatorKind.UserDefined Then
Dim bestCandidate As OverloadResolution.Candidate = If(userDefinedOperator.BestResult.HasValue,
userDefinedOperator.BestResult.Value.Candidate,
Nothing)
If bestCandidate Is Nothing OrElse
Not bestCandidate.IsLifted OrElse
(OverloadResolution.IsValidInLiftedSignature(bestCandidate.Parameters(0).Type) AndAlso
OverloadResolution.IsValidInLiftedSignature(bestCandidate.Parameters(1).Type) AndAlso
OverloadResolution.IsValidInLiftedSignature(bestCandidate.ReturnType)) Then
If preliminaryOperatorKind = BinaryOperatorKind.AndAlso OrElse preliminaryOperatorKind = BinaryOperatorKind.OrElse Then
Return BindUserDefinedShortCircuitingOperator(node, preliminaryOperatorKind, left, right,
userDefinedOperator, diagnostics)
Else
Return BindUserDefinedNonShortCircuitingBinaryOperator(node, preliminaryOperatorKind, left, right,
userDefinedOperator, diagnostics)
End If
End If
operatorKind = BinaryOperatorKind.Error
End If
If operatorKind = BinaryOperatorKind.Error Then
ReportUndefinedOperatorError(node, left, right, operatorTokenKind, preliminaryOperatorKind, diagnostics)
Return New BoundBinaryOperator(node, preliminaryOperatorKind Or BinaryOperatorKind.Error, left, right, CheckOverflow, ErrorTypeSymbol.UnknownResultType, hasErrors:=True)
End If
' We are dealing with intrinsic operator
' Get the symbol for operand type
Dim operandType As TypeSymbol
If intrinsicOperatorType = SpecialType.None Then
' Must be a bitwise operation with enum type.
Debug.Assert(leftType.GetNullableUnderlyingTypeOrSelf().IsEnumType() AndAlso
leftType.GetNullableUnderlyingTypeOrSelf().IsSameTypeIgnoringAll(rightType.GetNullableUnderlyingTypeOrSelf()))
If (operatorKind And BinaryOperatorKind.Lifted) = 0 OrElse leftType.IsNullableType() Then
operandType = leftType
Else
Debug.Assert(rightType.IsNullableType())
operandType = rightType
End If
Else
operandType = GetSpecialTypeForBinaryOperator(node, leftType, rightType, intrinsicOperatorType,
(operatorKind And BinaryOperatorKind.Lifted) <> 0, diagnostics)
End If
' Get the symbol for result type
Dim operatorResultType As TypeSymbol = operandType
Dim forceToBooleanType As TypeSymbol = Nothing
Select Case preliminaryOperatorKind
Case BinaryOperatorKind.Equals,
BinaryOperatorKind.NotEquals,
BinaryOperatorKind.LessThanOrEqual,
BinaryOperatorKind.GreaterThanOrEqual,
BinaryOperatorKind.LessThan,
BinaryOperatorKind.GreaterThan,
BinaryOperatorKind.Like
If OptionCompareText AndAlso (operandType.IsObjectType() OrElse operandType.IsStringType()) Then
operatorKind = operatorKind Or BinaryOperatorKind.CompareText
End If
If Not operatorResultType.IsObjectType() OrElse
(isOperandOfConditionalBranch AndAlso preliminaryOperatorKind <> BinaryOperatorKind.Like) Then
Dim booleanType As TypeSymbol = GetSpecialTypeForBinaryOperator(node, leftType, rightType, SpecialType.System_Boolean,
False, diagnostics)
If (operatorKind And BinaryOperatorKind.Lifted) <> 0 Then
operatorResultType = GetNullableTypeForBinaryOperator(leftType, rightType, booleanType)
If (preliminaryOperatorKind = BinaryOperatorKind.Equals OrElse preliminaryOperatorKind = BinaryOperatorKind.NotEquals) AndAlso
(IsKnownToBeNullableNothing(left) OrElse IsKnownToBeNullableNothing(right)) Then
ReportDiagnostic(diagnostics, node,
ErrorFactory.ErrorInfo(
If(preliminaryOperatorKind = BinaryOperatorKind.Equals,
ERRID.WRN_EqualToLiteralNothing, ERRID.WRN_NotEqualToLiteralNothing)))
End If
Else
If Not operatorResultType.IsObjectType() Then
operatorResultType = booleanType
Else
' I believe this is just an optimization to prevent Object from bubbling up the tree.
Debug.Assert(isOperandOfConditionalBranch AndAlso preliminaryOperatorKind <> BinaryOperatorKind.Like)
forceToBooleanType = booleanType
End If
End If
End If
End Select
If operandType.GetNullableUnderlyingTypeOrSelf().IsErrorType() OrElse
operatorResultType.GetNullableUnderlyingTypeOrSelf().IsErrorType() OrElse
(forceToBooleanType IsNot Nothing AndAlso forceToBooleanType.GetNullableUnderlyingTypeOrSelf().IsErrorType()) Then
' Suppress any additional diagnostics by overriding DiagnosticBag.
If diagnostics Is originalDiagnostics Then
diagnostics = BindingDiagnosticBag.Discarded
End If
End If
Dim hasError As Boolean = False
' Option Strict disallows all operations on Object operands. Or, at least, warn.
If OptionStrict = VisualBasic.OptionStrict.On Then
Dim reportedAnEror As Boolean = False
If leftType.IsObjectType Then
ReportBinaryOperatorOnObject(operatorTokenKind, left, preliminaryOperatorKind, diagnostics)
reportedAnEror = True
End If
If rightType.IsObjectType() Then
ReportBinaryOperatorOnObject(operatorTokenKind, right, preliminaryOperatorKind, diagnostics)
reportedAnEror = True
End If
If reportedAnEror Then
hasError = True
' Suppress any additional diagnostics by overriding DiagnosticBag.
If diagnostics Is originalDiagnostics Then
diagnostics = BindingDiagnosticBag.Discarded
End If
End If
ElseIf OptionStrict = VisualBasic.OptionStrict.Custom Then 'warn if option strict is off
If Not isSelectCase OrElse preliminaryOperatorKind <> BinaryOperatorKind.OrElse Then
Dim errorId = If(isSelectCase, ERRID.WRN_ObjectMathSelectCase,
If(preliminaryOperatorKind = BinaryOperatorKind.Equals, ERRID.WRN_ObjectMath1,
If(preliminaryOperatorKind = BinaryOperatorKind.NotEquals, ERRID.WRN_ObjectMath1Not, ERRID.WRN_ObjectMath2)))
If leftType.IsObjectType Then
ReportDiagnostic(diagnostics, left.Syntax, ErrorFactory.ErrorInfo(errorId, operatorTokenKind))
End If
If rightType.IsObjectType Then
ReportDiagnostic(diagnostics, right.Syntax, ErrorFactory.ErrorInfo(errorId, operatorTokenKind))
End If
End If
End If
' Apply conversions to operands.
Dim explicitSemanticForConcatArgument As Boolean = False
' Concatenation will apply conversions to its operands as if the
' conversions were explicit. Effectively, the use of the concatenation
' operator is treated as an explicit conversion to String.
If preliminaryOperatorKind = BinaryOperatorKind.Concatenate Then
explicitSemanticForConcatArgument = True
Debug.Assert((operatorKind And BinaryOperatorKind.Lifted) = 0)
If operandType.IsStringType() Then
If left.Type.IsNullableType Then
left = ForceLiftToEmptyString(left, operandType, diagnostics)
End If
If right.Type.IsNullableType Then
right = ForceLiftToEmptyString(right, operandType, diagnostics)
End If
End If
End If
Dim beforeConversion As BoundExpression = left
left = ApplyConversion(left.Syntax, operandType, left, explicitSemanticForConcatArgument, diagnostics,
explicitSemanticForConcatArgument:=explicitSemanticForConcatArgument)
If explicitSemanticForConcatArgument AndAlso left IsNot beforeConversion AndAlso left.Kind = BoundKind.Conversion Then
Dim conversion = DirectCast(left, BoundConversion)
left = conversion.Update(conversion.Operand, conversion.ConversionKind, conversion.Checked, explicitCastInCode:=False,
constantValueOpt:=conversion.ConstantValueOpt, extendedInfoOpt:=conversion.ExtendedInfoOpt,
type:=conversion.Type)
End If
If (preliminaryOperatorKind = BinaryOperatorKind.LeftShift OrElse preliminaryOperatorKind = BinaryOperatorKind.RightShift) AndAlso
Not operandType.IsObjectType() Then
Dim rightTargetType As TypeSymbol = GetSpecialTypeForBinaryOperator(node, leftType, rightType, SpecialType.System_Int32,
False, diagnostics)
'§11.18 Shift Operators
'The type of the right operand must be implicitly convertible to Integer
' If operator is lifted, convert right operand to Nullable(Of Integer)
If (operatorKind And BinaryOperatorKind.Lifted) <> 0 Then
rightTargetType = GetNullableTypeForBinaryOperator(leftType, rightType, rightTargetType)
End If
right = ApplyImplicitConversion(right.Syntax, rightTargetType, right, diagnostics)
Else
beforeConversion = right
right = ApplyConversion(right.Syntax, operandType, right, explicitSemanticForConcatArgument, diagnostics,
explicitSemanticForConcatArgument:=explicitSemanticForConcatArgument)
If explicitSemanticForConcatArgument AndAlso right IsNot beforeConversion AndAlso right.Kind = BoundKind.Conversion Then
Dim conversion = DirectCast(right, BoundConversion)
right = conversion.Update(conversion.Operand, conversion.ConversionKind, conversion.Checked, explicitCastInCode:=False,
constantValueOpt:=conversion.ConstantValueOpt, extendedInfoOpt:=conversion.ExtendedInfoOpt,
type:=conversion.Type)
End If
End If
If (operatorKind And BinaryOperatorKind.OpMask) = BinaryOperatorKind.Add AndAlso operatorResultType.IsStringType() Then
' Transform the addition into a string concatenation. This won't use a runtime helper - it will turn into System.String::Concat
operatorKind = (operatorKind And (Not BinaryOperatorKind.OpMask))
operatorKind = operatorKind Or BinaryOperatorKind.Concatenate
End If
' Perform constant folding.
Dim value As ConstantValue = Nothing
If Not (left.HasErrors OrElse right.HasErrors) Then
Dim integerOverflow As Boolean = False
Dim divideByZero As Boolean = False
Dim lengthOutOfLimit As Boolean = False
value = OverloadResolution.TryFoldConstantBinaryOperator(operatorKind,
left,
right,
operatorResultType,
integerOverflow,
divideByZero,
lengthOutOfLimit)
If value IsNot Nothing Then
If divideByZero Then
Debug.Assert(value.IsBad)
ReportDiagnostic(diagnostics, node, ErrorFactory.ErrorInfo(ERRID.ERR_ZeroDivide))
ElseIf lengthOutOfLimit Then
Debug.Assert(value.IsBad)
ReportDiagnostic(diagnostics, right.Syntax, ErrorFactory.ErrorInfo(ERRID.ERR_ConstantStringTooLong))
ElseIf (value.IsBad OrElse integerOverflow) Then
' Overflows are reported regardless of the value of OptionRemoveIntegerOverflowChecks, Dev10 behavior.
ReportDiagnostic(diagnostics, node, ErrorFactory.ErrorInfo(ERRID.ERR_ExpressionOverflow1, operatorResultType))
' there should be no constant value in case of overflows.
If Not value.IsBad Then
value = ConstantValue.Bad
End If
End If
End If
End If
Dim result As BoundExpression = New BoundBinaryOperator(node, operatorKind Or If(isOperandOfConditionalBranch, BinaryOperatorKind.IsOperandOfConditionalBranch, Nothing),
left, right, CheckOverflow, value, operatorResultType, hasError)
If forceToBooleanType IsNot Nothing Then
Debug.Assert(forceToBooleanType.IsBooleanType())
result = ApplyConversion(node, forceToBooleanType, result, isExplicit:=True, diagnostics:=diagnostics)
End If
Return result
End Function
''' <summary>
''' This helper is used to wrap nullable argument into something that would return null string if argument is null.
'''
''' Unlike conversion to a string where nullable nulls result in an exception,
''' concatenation requires that nullable nulls are treated as null strings.
''' Note that conversion is treated as explicit conversion.
''' </summary>
Private Function ForceLiftToEmptyString(left As BoundExpression, stringType As TypeSymbol, diagnostics As BindingDiagnosticBag) As BoundExpression
Debug.Assert(stringType.IsStringType)
Dim nothingStr = New BoundLiteral(left.Syntax, ConstantValue.Nothing, stringType).MakeCompilerGenerated()
Return AnalyzeConversionAndCreateBinaryConditionalExpression(left.Syntax,
left,
nothingStr,
Nothing,
stringType,
False,
diagnostics,
explicitConversion:=True).MakeCompilerGenerated()
End Function
Private Function BindUserDefinedNonShortCircuitingBinaryOperator(
node As SyntaxNode,
opKind As BinaryOperatorKind,
left As BoundExpression,
right As BoundExpression,
<[In]> ByRef userDefinedOperator As OverloadResolution.OverloadResolutionResult,
diagnostics As BindingDiagnosticBag
) As BoundUserDefinedBinaryOperator
Debug.Assert(userDefinedOperator.Candidates.Length > 0)
opKind = opKind Or BinaryOperatorKind.UserDefined
Dim result As BoundExpression
If userDefinedOperator.BestResult.HasValue Then
Dim bestCandidate As OverloadResolution.CandidateAnalysisResult = userDefinedOperator.BestResult.Value
result = CreateBoundCallOrPropertyAccess(node, node, TypeCharacter.None,
New BoundMethodGroup(node, Nothing,
ImmutableArray.Create(Of MethodSymbol)(
DirectCast(bestCandidate.Candidate.UnderlyingSymbol, MethodSymbol)),
LookupResultKind.Good, Nothing,
QualificationKind.Unqualified).MakeCompilerGenerated(),
ImmutableArray.Create(Of BoundExpression)(left, right),
bestCandidate,
userDefinedOperator.AsyncLambdaSubToFunctionMismatch,
diagnostics)
If bestCandidate.Candidate.IsLifted Then
opKind = opKind Or BinaryOperatorKind.Lifted
End If
Else
result = ReportOverloadResolutionFailureAndProduceBoundNode(node, LookupResultKind.Good,
ImmutableArray.Create(Of BoundExpression)(left, right),
Nothing, userDefinedOperator, diagnostics,
callerInfoOpt:=Nothing)
End If
Return New BoundUserDefinedBinaryOperator(node, opKind, result, CheckOverflow, result.Type)
End Function
''' <summary>
''' This function builds a bound tree representing an overloaded short circuiting expression
''' after determining that the necessary semantic conditions are met.
'''
''' An expression of the form:
'''
''' x AndAlso y (where the type of x is X and the type of y is Y)
'''
''' is an overloaded short circuit operation if X and Y are user-defined types and an
''' applicable operator And exists after applying normal operator resolution rules.
'''
''' Given an applicable And operator declared in type T, the following must be true:
'''
''' - The return type and parameter types must be T.
''' - T must contain a declaration of operator IsFalse.
'''
''' If these conditions are met, the expression "x AndAlso y" is translated into:
'''
''' !T.IsFalse(temp = x) ? T.And(temp, y) : temp
'''
''' The temporary is necessary for evaluating x only once. Similarly, "x OrElse y" is
''' translated into:
'''
''' !T.IsTrue(temp = x) ? T.Or(temp, y) : temp
''' </summary>
Private Function BindUserDefinedShortCircuitingOperator(
node As SyntaxNode,
opKind As BinaryOperatorKind,
left As BoundExpression,
right As BoundExpression,
<[In]> ByRef bitwiseOperator As OverloadResolution.OverloadResolutionResult,
diagnostics As BindingDiagnosticBag
) As BoundUserDefinedShortCircuitingOperator
Debug.Assert(opKind = BinaryOperatorKind.AndAlso OrElse opKind = BinaryOperatorKind.OrElse)
Debug.Assert(bitwiseOperator.Candidates.Length > 0)
Dim bitwiseKind As BinaryOperatorKind = If(opKind = BinaryOperatorKind.AndAlso, BinaryOperatorKind.And, BinaryOperatorKind.Or) Or BinaryOperatorKind.UserDefined
Dim operatorType As TypeSymbol
Dim leftOperand As BoundExpression = Nothing
Dim leftPlaceholder As BoundRValuePlaceholder = Nothing
Dim test As BoundExpression = Nothing
Dim bitwise As BoundUserDefinedBinaryOperator
Dim hasErrors As Boolean = False
If Not bitwiseOperator.BestResult.HasValue Then
' This will take care of the diagnostic.
bitwise = BindUserDefinedNonShortCircuitingBinaryOperator(node, bitwiseKind, left, right, bitwiseOperator, diagnostics)
operatorType = bitwise.Type
hasErrors = True
GoTo Done
End If
Dim bitwiseAnalysis As OverloadResolution.CandidateAnalysisResult = bitwiseOperator.BestResult.Value
Dim bitwiseCandidate As OverloadResolution.Candidate = bitwiseAnalysis.Candidate
operatorType = bitwiseCandidate.ReturnType
If bitwiseCandidate.IsLifted Then
bitwiseKind = bitwiseKind Or BinaryOperatorKind.Lifted
End If
If Not operatorType.IsSameTypeIgnoringAll(bitwiseCandidate.Parameters(0).Type) OrElse
Not operatorType.IsSameTypeIgnoringAll(bitwiseCandidate.Parameters(1).Type) Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_UnacceptableLogicalOperator3,
bitwiseCandidate.UnderlyingSymbol,
bitwiseCandidate.UnderlyingSymbol.ContainingType,
SyntaxFacts.GetText(If(opKind = BinaryOperatorKind.AndAlso,
SyntaxKind.AndAlsoKeyword, SyntaxKind.OrElseKeyword)))
bitwise = BindUserDefinedNonShortCircuitingBinaryOperator(node, bitwiseKind, left, right, bitwiseOperator,
BindingDiagnosticBag.Discarded) ' Ignore any additional diagnostics.
hasErrors = True
GoTo Done
End If
leftPlaceholder = New BoundRValuePlaceholder(left.Syntax, operatorType).MakeCompilerGenerated()
' Find IsTrue/IsFalse operator
Dim leftCheckOperator As OverloadResolution.OverloadResolutionResult
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
If opKind = BinaryOperatorKind.AndAlso Then
leftCheckOperator = OverloadResolution.ResolveIsFalseOperator(leftPlaceholder, Me, useSiteInfo)
Else
leftCheckOperator = OverloadResolution.ResolveIsTrueOperator(leftPlaceholder, Me, useSiteInfo)
End If
If diagnostics.Add(node, useSiteInfo) Then
' Suppress additional diagnostics
diagnostics = BindingDiagnosticBag.Discarded
End If
If Not leftCheckOperator.BestResult.HasValue Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_ConditionOperatorRequired3,
operatorType,
SyntaxFacts.GetText(If(opKind = BinaryOperatorKind.AndAlso, SyntaxKind.IsFalseKeyword, SyntaxKind.IsTrueKeyword)),
SyntaxFacts.GetText(If(opKind = BinaryOperatorKind.AndAlso, SyntaxKind.AndAlsoKeyword, SyntaxKind.OrElseKeyword)))
bitwise = BindUserDefinedNonShortCircuitingBinaryOperator(node, bitwiseKind, left, right, bitwiseOperator,
BindingDiagnosticBag.Discarded) ' Ignore any additional diagnostics.
leftPlaceholder = Nothing
hasErrors = True
GoTo Done
End If
Dim checkCandidate As OverloadResolution.Candidate = leftCheckOperator.BestResult.Value.Candidate
Debug.Assert(checkCandidate.ReturnType.IsBooleanType() OrElse checkCandidate.ReturnType.IsNullableOfBoolean())
If Not operatorType.IsSameTypeIgnoringAll(checkCandidate.Parameters(0).Type) Then
ReportDiagnostic(diagnostics, node, ERRID.ERR_BinaryOperands3,
SyntaxFacts.GetText(If(opKind = BinaryOperatorKind.AndAlso, SyntaxKind.AndAlsoKeyword, SyntaxKind.OrElseKeyword)),
left.Type, right.Type)
hasErrors = True
diagnostics = BindingDiagnosticBag.Discarded ' Ignore any additional diagnostics.
bitwise = BindUserDefinedNonShortCircuitingBinaryOperator(node, bitwiseKind, left, right, bitwiseOperator, diagnostics)
Else
' Convert the operands to the operator type.
Dim argumentInfo As (Arguments As ImmutableArray(Of BoundExpression), DefaultArguments As BitVector) =
PassArguments(node, bitwiseAnalysis, ImmutableArray.Create(Of BoundExpression)(left, right), diagnostics)
Debug.Assert(argumentInfo.DefaultArguments.IsNull)
bitwiseAnalysis.ConversionsOpt = Nothing
bitwise = New BoundUserDefinedBinaryOperator(node, bitwiseKind,
CreateBoundCallOrPropertyAccess(node, node, TypeCharacter.None,
New BoundMethodGroup(node, Nothing,
ImmutableArray.Create(Of MethodSymbol)(
DirectCast(bitwiseCandidate.UnderlyingSymbol, MethodSymbol)),
LookupResultKind.Good, Nothing,
QualificationKind.Unqualified).MakeCompilerGenerated(),
ImmutableArray.Create(Of BoundExpression)(leftPlaceholder, argumentInfo.Arguments(1)),
bitwiseAnalysis,
bitwiseOperator.AsyncLambdaSubToFunctionMismatch,
diagnostics),
CheckOverflow,
operatorType)
leftOperand = argumentInfo.Arguments(0)
End If
Dim testOp As BoundUserDefinedUnaryOperator = BindUserDefinedUnaryOperator(node,
If(opKind = BinaryOperatorKind.AndAlso,
UnaryOperatorKind.IsFalse,
UnaryOperatorKind.IsTrue),
leftPlaceholder,
leftCheckOperator,
diagnostics).MakeCompilerGenerated()
testOp.UnderlyingExpression.SetWasCompilerGenerated()
If hasErrors Then
leftPlaceholder = Nothing
End If
If checkCandidate.IsLifted Then
test = ApplyNullableIsTrueOperator(testOp, checkCandidate.ReturnType.GetNullableUnderlyingTypeOrSelf())
Else
test = testOp
End If
Done:
Debug.Assert(hasErrors OrElse (leftOperand IsNot Nothing AndAlso leftPlaceholder IsNot Nothing AndAlso test IsNot Nothing))
Debug.Assert(Not hasErrors OrElse (leftOperand Is Nothing AndAlso leftPlaceholder Is Nothing))
bitwise.UnderlyingExpression.SetWasCompilerGenerated()
bitwise.SetWasCompilerGenerated()
Return New BoundUserDefinedShortCircuitingOperator(node, leftOperand, leftPlaceholder, test, bitwise, operatorType, hasErrors)
End Function
Private Shared Sub ReportBinaryOperatorOnObject(
operatorTokenKind As SyntaxKind,
operand As BoundExpression,
preliminaryOperatorKind As BinaryOperatorKind,
diagnostics As BindingDiagnosticBag
)
ReportDiagnostic(diagnostics, operand.Syntax,
ErrorFactory.ErrorInfo(
If(preliminaryOperatorKind = BinaryOperatorKind.Equals OrElse preliminaryOperatorKind = BinaryOperatorKind.NotEquals,
ERRID.ERR_StrictDisallowsObjectComparison1, ERRID.ERR_StrictDisallowsObjectOperand1),
operatorTokenKind))
End Sub
''' <summary>
''' Returns Symbol for String type.
''' </summary>
Private Function SubstituteDBNullWithNothingString(
ByRef dbNullOperand As BoundExpression,
otherOperandType As TypeSymbol,
diagnostics As BindingDiagnosticBag
) As TypeSymbol
Dim stringType As TypeSymbol
If otherOperandType.IsStringType() Then
stringType = otherOperandType
Else
stringType = GetSpecialType(SpecialType.System_String, dbNullOperand.Syntax, diagnostics)
End If
dbNullOperand = New BoundConversion(dbNullOperand.Syntax, dbNullOperand, ConversionKind.Widening,
checked:=False, explicitCastInCode:=False, type:=stringType,
constantValueOpt:=ConstantValue.Nothing)
Return stringType
End Function
''' <summary>
''' Get symbol for a special type, reuse symbols for operand types to avoid type
''' lookups and construction of new instances of symbols.
''' </summary>
Private Function GetSpecialTypeForBinaryOperator(
node As SyntaxNode,
leftType As TypeSymbol,
rightType As TypeSymbol,
specialType As SpecialType,
makeNullable As Boolean,
diagnostics As BindingDiagnosticBag
) As TypeSymbol
Debug.Assert(specialType <> Microsoft.CodeAnalysis.SpecialType.None)
Debug.Assert(Not makeNullable OrElse leftType.IsNullableType() OrElse rightType.IsNullableType())
Dim resultType As TypeSymbol
Dim leftNullableUnderlying = leftType.GetNullableUnderlyingTypeOrSelf()
Dim leftSpecialType = leftNullableUnderlying.SpecialType
Dim rightNullableUnderlying = rightType.GetNullableUnderlyingTypeOrSelf()
Dim rightSpecialType = rightNullableUnderlying.SpecialType
If leftSpecialType = specialType Then
If Not makeNullable Then
resultType = leftNullableUnderlying
ElseIf leftType.IsNullableType() Then
resultType = leftType
ElseIf rightSpecialType = specialType Then
Debug.Assert(makeNullable AndAlso rightType.IsNullableType())
resultType = rightType
Else
Debug.Assert(makeNullable AndAlso
rightType.IsNullableType() AndAlso
Not leftType.IsNullableType())
resultType = DirectCast(rightType.OriginalDefinition, NamedTypeSymbol).Construct(leftType)
End If
ElseIf rightSpecialType = specialType Then
If Not makeNullable Then
resultType = rightNullableUnderlying
ElseIf rightType.IsNullableType() Then
resultType = rightType
Else
Debug.Assert(makeNullable AndAlso
Not rightType.IsNullableType() AndAlso
leftType.IsNullableType())
resultType = DirectCast(leftType.OriginalDefinition, NamedTypeSymbol).Construct(rightNullableUnderlying)
End If
Else
resultType = GetSpecialType(specialType, node, diagnostics)
If makeNullable Then
If leftType.IsNullableType() Then
resultType = DirectCast(leftType.OriginalDefinition, NamedTypeSymbol).Construct(resultType)
Else
Debug.Assert(rightType.IsNullableType())
resultType = DirectCast(rightType.OriginalDefinition, NamedTypeSymbol).Construct(resultType)
End If
End If
End If
Return resultType
End Function
''' <summary>
''' Get symbol for a Nullable type of particular type, reuse symbols for operand types to avoid type
''' lookups and construction of new instances of symbols.
''' </summary>
Private Shared Function GetNullableTypeForBinaryOperator(
leftType As TypeSymbol,
rightType As TypeSymbol,
ofType As TypeSymbol
) As TypeSymbol
Dim leftIsNullable = leftType.IsNullableType()
Dim rightIsNullable = rightType.IsNullableType()
Dim ofSpecialType = ofType.SpecialType
Debug.Assert(leftIsNullable OrElse rightIsNullable)
If ofSpecialType <> SpecialType.None Then
If leftIsNullable AndAlso leftType.GetNullableUnderlyingType().SpecialType = ofSpecialType Then
Return leftType
ElseIf rightIsNullable AndAlso rightType.GetNullableUnderlyingType().SpecialType = ofSpecialType Then
Return rightType
End If
End If
If leftIsNullable Then
Return DirectCast(leftType.OriginalDefinition, NamedTypeSymbol).Construct(ofType)
Else
Return DirectCast(rightType.OriginalDefinition, NamedTypeSymbol).Construct(ofType)
End If
End Function
Private Shared Function IsKnownToBeNullableNothing(expr As BoundExpression) As Boolean
Dim cast = expr
' TODO: Add handling for TryCast, similar to DirectCast
While cast.Kind = BoundKind.Conversion OrElse cast.Kind = BoundKind.DirectCast
If cast.HasErrors Then
Return False
End If
Dim resultType As TypeSymbol = Nothing
Select Case cast.Kind
Case BoundKind.Conversion
Dim conv = DirectCast(cast, BoundConversion)
resultType = conv.Type
cast = conv.Operand
Case BoundKind.DirectCast
Dim conv = DirectCast(cast, BoundDirectCast)
resultType = conv.Type
cast = conv.Operand
End Select
If resultType Is Nothing OrElse Not (resultType.IsNullableType() OrElse resultType.IsObjectType()) Then
Return False
End If
End While
Return cast.IsNothingLiteral()
End Function
Private Sub ReportUndefinedOperatorError(
syntax As SyntaxNode,
left As BoundExpression,
right As BoundExpression,
operatorTokenKind As SyntaxKind,
operatorKind As BinaryOperatorKind,
diagnostics As BindingDiagnosticBag
)
Dim leftType = left.Type
Dim rightType = right.Type
Debug.Assert(leftType IsNot Nothing)
Debug.Assert(rightType IsNot Nothing)
If leftType.IsErrorType() OrElse rightType.IsErrorType() Then
Return ' Let's not report more errors.
End If
Dim operatorTokenText = SyntaxFacts.GetText(operatorTokenKind)
If OverloadResolution.UseUserDefinedBinaryOperators(operatorKind, leftType, rightType) AndAlso
Not leftType.CanContainUserDefinedOperators(useSiteInfo:=CompoundUseSiteInfo(Of AssemblySymbol).Discarded) AndAlso Not rightType.CanContainUserDefinedOperators(useSiteInfo:=CompoundUseSiteInfo(Of AssemblySymbol).Discarded) AndAlso
(operatorKind = BinaryOperatorKind.Equals OrElse operatorKind = BinaryOperatorKind.NotEquals) AndAlso
leftType.IsReferenceType() AndAlso rightType.IsReferenceType() Then
ReportDiagnostic(diagnostics, syntax, ERRID.ERR_ReferenceComparison3, operatorTokenText, leftType, rightType)
ElseIf IsIEnumerableOfXElement(leftType, CompoundUseSiteInfo(Of AssemblySymbol).Discarded) Then
ReportDiagnostic(diagnostics, syntax, ERRID.ERR_BinaryOperandsForXml4, operatorTokenText, leftType, rightType, leftType)
ElseIf IsIEnumerableOfXElement(rightType, CompoundUseSiteInfo(Of AssemblySymbol).Discarded) Then
ReportDiagnostic(diagnostics, syntax, ERRID.ERR_BinaryOperandsForXml4, operatorTokenText, leftType, rightType, rightType)
Else
ReportDiagnostic(diagnostics, syntax, ERRID.ERR_BinaryOperands3, operatorTokenText, leftType, rightType)
End If
End Sub
''' <summary>
''' §11.12.2 Object Operands
''' The value Nothing is treated as the default value of the type of
''' the other operand in a binary operator expression. In a unary operator expression,
''' or if both operands are Nothing in a binary operator expression,
''' the type of the operation is Integer or the only result type of the operator,
''' if the operator does not result in Integer.
''' </summary>
Private Sub ConvertNothingLiterals(
operatorKind As BinaryOperatorKind,
ByRef left As BoundExpression,
ByRef right As BoundExpression,
diagnostics As BindingDiagnosticBag
)
Debug.Assert((operatorKind And BinaryOperatorKind.OpMask) = operatorKind AndAlso operatorKind <> 0)
Dim rightType As TypeSymbol
Dim leftType As TypeSymbol
If left.IsNothingLiteral() Then
If right.IsNothingLiteral() Then
' Both are NOTHING
Dim defaultRightSpecialType As SpecialType
Select Case operatorKind
Case BinaryOperatorKind.Concatenate,
BinaryOperatorKind.Like
defaultRightSpecialType = SpecialType.System_String
Case BinaryOperatorKind.OrElse,
BinaryOperatorKind.AndAlso
defaultRightSpecialType = SpecialType.System_Boolean
Case BinaryOperatorKind.Add,
BinaryOperatorKind.Equals,
BinaryOperatorKind.NotEquals,
BinaryOperatorKind.LessThanOrEqual,
BinaryOperatorKind.GreaterThanOrEqual,
BinaryOperatorKind.LessThan,
BinaryOperatorKind.GreaterThan,
BinaryOperatorKind.Subtract,
BinaryOperatorKind.Multiply,
BinaryOperatorKind.Power,
BinaryOperatorKind.Divide,
BinaryOperatorKind.Modulo,
BinaryOperatorKind.IntegerDivide,
BinaryOperatorKind.LeftShift,
BinaryOperatorKind.RightShift,
BinaryOperatorKind.Xor,
BinaryOperatorKind.Or,
BinaryOperatorKind.And
defaultRightSpecialType = SpecialType.System_Int32
Case Else
Throw ExceptionUtilities.UnexpectedValue(operatorKind)
End Select
rightType = GetSpecialType(defaultRightSpecialType, right.Syntax, diagnostics)
right = ApplyImplicitConversion(right.Syntax,
rightType,
right, diagnostics)
Else
rightType = right.Type
If rightType Is Nothing Then
Return
End If
End If
Debug.Assert(rightType IsNot Nothing)
Dim defaultLeftSpecialType As SpecialType = SpecialType.None
Select Case operatorKind
Case BinaryOperatorKind.Concatenate,
BinaryOperatorKind.Like
If rightType.GetNullableUnderlyingTypeOrSelf().GetEnumUnderlyingTypeOrSelf().IsIntrinsicType() OrElse
rightType.IsCharSZArray() OrElse
rightType.IsDBNullType() Then
' For & and Like, a Nothing operand is typed String unless the other operand
' is non-intrinsic (VSW#240203).
' The same goes for DBNull (VSW#278518)
' The same goes for enum types (VSW#288077)
defaultLeftSpecialType = SpecialType.System_String
End If
Case BinaryOperatorKind.LeftShift,
BinaryOperatorKind.RightShift
' Nothing should default to Integer for Shift operations.
defaultLeftSpecialType = SpecialType.System_Int32
End Select
If defaultLeftSpecialType = SpecialType.None OrElse defaultLeftSpecialType = rightType.SpecialType Then
leftType = rightType
Else
leftType = GetSpecialType(defaultLeftSpecialType, left.Syntax, diagnostics)
End If
left = ApplyImplicitConversion(left.Syntax,
leftType,
left, diagnostics)
ElseIf right.IsNothingLiteral() Then
leftType = left.Type
If leftType Is Nothing Then
Return
End If
rightType = leftType
Select Case operatorKind
Case BinaryOperatorKind.Concatenate,
BinaryOperatorKind.Like
If leftType.GetNullableUnderlyingTypeOrSelf().GetEnumUnderlyingTypeOrSelf().IsIntrinsicType() OrElse
leftType.IsCharSZArray() OrElse
leftType.IsDBNullType() Then
' For & and Like, a Nothing operand is typed String unless the other operand
' is non-intrinsic (VSW#240203).
' The same goes for DBNull (VSW#278518)
' The same goes for enum types (VSW#288077)
If leftType.SpecialType <> SpecialType.System_String Then
rightType = GetSpecialType(SpecialType.System_String, right.Syntax, diagnostics)
End If
End If
End Select
right = ApplyImplicitConversion(right.Syntax,
rightType,
right, diagnostics)
End If
End Sub
Private Function BindUnaryOperator(node As UnaryExpressionSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression
Dim operand As BoundExpression = BindValue(node.Operand, diagnostics)
Dim preliminaryOperatorKind As UnaryOperatorKind = OverloadResolution.MapUnaryOperatorKind(node.Kind)
If Not operand.HasErrors AndAlso operand.IsNothingLiteral Then
'§11.12.2 Object Operands
'In a unary operator expression, or if both operands are Nothing in a
'binary operator expression, the type of the operation is Integer
Dim int32Type = GetSpecialType(SpecialType.System_Int32, node.Operand, diagnostics)
operand = ApplyImplicitConversion(node.Operand, int32Type, operand, diagnostics)
Else
operand = MakeRValue(operand, diagnostics)
End If
If operand.HasErrors Then
' Suppress any additional diagnostics by overriding DiagnosticBag.
diagnostics = BindingDiagnosticBag.Discarded
End If
Dim intrinsicOperatorType As SpecialType = SpecialType.None
Dim userDefinedOperator As OverloadResolution.OverloadResolutionResult = Nothing
Dim useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics)
Dim operatorKind As UnaryOperatorKind = OverloadResolution.ResolveUnaryOperator(preliminaryOperatorKind, operand, Me, intrinsicOperatorType, userDefinedOperator, useSiteInfo)
If diagnostics.Add(node, useSiteInfo) Then
' Suppress additional diagnostics
diagnostics = BindingDiagnosticBag.Discarded
End If
If operatorKind = UnaryOperatorKind.UserDefined Then
Dim bestCandidate As OverloadResolution.Candidate = If(userDefinedOperator.BestResult.HasValue,
userDefinedOperator.BestResult.Value.Candidate,
Nothing)
If bestCandidate Is Nothing OrElse
Not bestCandidate.IsLifted OrElse
(OverloadResolution.IsValidInLiftedSignature(bestCandidate.Parameters(0).Type) AndAlso
OverloadResolution.IsValidInLiftedSignature(bestCandidate.ReturnType)) Then
Return BindUserDefinedUnaryOperator(node, preliminaryOperatorKind, operand, userDefinedOperator, diagnostics)
End If
operatorKind = UnaryOperatorKind.Error
End If
If operatorKind = UnaryOperatorKind.Error Then
ReportUndefinedOperatorError(node, operand, diagnostics)
Return New BoundUnaryOperator(node, preliminaryOperatorKind Or UnaryOperatorKind.Error, operand, CheckOverflow, ErrorTypeSymbol.UnknownResultType, HasErrors:=True)
End If
' We are dealing with intrinsic operator
Dim operandType As TypeSymbol = operand.Type
Dim resultType As TypeSymbol = Nothing
If intrinsicOperatorType = SpecialType.None Then
Debug.Assert(operandType.GetNullableUnderlyingTypeOrSelf().IsEnumType())
resultType = operandType
Else
If operandType.GetNullableUnderlyingTypeOrSelf().SpecialType = intrinsicOperatorType Then
resultType = operandType
Else
resultType = GetSpecialType(intrinsicOperatorType, node.Operand, diagnostics)
If operandType.IsNullableType() Then
resultType = DirectCast(operandType.OriginalDefinition, NamedTypeSymbol).Construct(resultType)
End If
End If
End If
Debug.Assert(((operatorKind And UnaryOperatorKind.Lifted) <> 0) = resultType.IsNullableType())
' Option Strict disallows all unary operations on Object operands. Otherwise just warn.
If operandType.SpecialType = SpecialType.System_Object Then
If OptionStrict = VisualBasic.OptionStrict.On Then
ReportDiagnostic(diagnostics, node.Operand, ErrorFactory.ErrorInfo(ERRID.ERR_StrictDisallowsObjectOperand1, node.OperatorToken))
ElseIf OptionStrict = VisualBasic.OptionStrict.Custom Then
ReportDiagnostic(diagnostics, node.Operand, ErrorFactory.ErrorInfo(ERRID.WRN_ObjectMath2, node.OperatorToken))
End If
End If
operand = ApplyImplicitConversion(node.Operand, resultType, operand, diagnostics)
Dim constantValue As ConstantValue = Nothing
If Not operand.HasErrors Then
Dim integerOverflow As Boolean = False
constantValue = OverloadResolution.TryFoldConstantUnaryOperator(operatorKind, operand, resultType, integerOverflow)
' Overflows are reported regardless of the value of OptionRemoveIntegerOverflowChecks, Dev10 behavior.
If constantValue IsNot Nothing AndAlso (constantValue.IsBad OrElse integerOverflow) Then
ReportDiagnostic(diagnostics, node, ErrorFactory.ErrorInfo(ERRID.ERR_ExpressionOverflow1, resultType))
' there should be no constant value in case of overflows.
If Not constantValue.IsBad Then
constantValue = constantValue.Bad
End If
End If
End If
Return New BoundUnaryOperator(node, operatorKind, operand, CheckOverflow, constantValue, resultType)
End Function
Private Function BindUserDefinedUnaryOperator(
node As SyntaxNode,
opKind As UnaryOperatorKind,
operand As BoundExpression,
<[In]> ByRef userDefinedOperator As OverloadResolution.OverloadResolutionResult,
diagnostics As BindingDiagnosticBag
) As BoundUserDefinedUnaryOperator
Debug.Assert(userDefinedOperator.Candidates.Length > 0)
Dim result As BoundExpression
opKind = opKind Or UnaryOperatorKind.UserDefined
If userDefinedOperator.BestResult.HasValue Then
Dim bestCandidate As OverloadResolution.CandidateAnalysisResult = userDefinedOperator.BestResult.Value
result = CreateBoundCallOrPropertyAccess(node, node, TypeCharacter.None,
New BoundMethodGroup(node, Nothing,
ImmutableArray.Create(Of MethodSymbol)(
DirectCast(bestCandidate.Candidate.UnderlyingSymbol, MethodSymbol)),
LookupResultKind.Good, Nothing,
QualificationKind.Unqualified).MakeCompilerGenerated(),
ImmutableArray.Create(Of BoundExpression)(operand),
bestCandidate,
userDefinedOperator.AsyncLambdaSubToFunctionMismatch,
diagnostics)
If bestCandidate.Candidate.IsLifted Then
opKind = opKind Or UnaryOperatorKind.Lifted
End If
Else
result = ReportOverloadResolutionFailureAndProduceBoundNode(node, LookupResultKind.Good,
ImmutableArray.Create(Of BoundExpression)(operand),
Nothing, userDefinedOperator, diagnostics,
callerInfoOpt:=Nothing)
End If
Return New BoundUserDefinedUnaryOperator(node, opKind, result, result.Type)
End Function
Private Shared Sub ReportUndefinedOperatorError(
syntax As UnaryExpressionSyntax,
operand As BoundExpression,
diagnostics As BindingDiagnosticBag
)
If operand.Type.IsErrorType() Then
Return ' Let's not report more errors.
End If
ReportDiagnostic(diagnostics, syntax, ErrorFactory.ErrorInfo(ERRID.ERR_UnaryOperand2, syntax.OperatorToken, operand.Type))
End Sub
End Class
End Namespace
|