/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/VisualBasic/Portable/Binding/Binder_SelectCase.vb
725 строк
39 KB
AlekseyTs
Merge remote-tracking branch 'dotnet/master' into UsedAssemblies_13
24 янв 2020, 03:48
24 янв 2020, 03:48
5f06615
Код
Авторство
О чём код?
' 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.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 Binder #Region "Bind select case statement" Private Function BindSelectBlock(node As SelectBlockSyntax, diagnostics As BindingDiagnosticBag) As BoundStatement Debug.Assert(node IsNot Nothing) ' Bind select expression Dim selectExprStatementSyntax = node.SelectStatement Dim expression = BindSelectExpression(selectExprStatementSyntax.Expression, diagnostics) If expression.HasErrors Then diagnostics = BindingDiagnosticBag.Discarded End If Dim exprPlaceHolder = New BoundRValuePlaceholder(selectExprStatementSyntax.Expression, expression.Type) exprPlaceHolder.SetWasCompilerGenerated() Dim expressionStmt = New BoundExpressionStatement(selectExprStatementSyntax, expression) ' Get the binder for the select block. This defines the exit label. Dim selectBinder = GetBinder(DirectCast(node, VisualBasicSyntaxNode)) ' Flag to determine if we need to generate switch table based code or If list based code. ' See OptimizeSelectStatement method for more details. Dim recommendSwitchTable = False ' Bind case blocks. Dim caseBlocks As ImmutableArray(Of BoundCaseBlock) = selectBinder.BindCaseBlocks( node.CaseBlocks, exprPlaceHolder, convertCaseElements:=expression.Type.IsIntrinsicOrEnumType(), recommendSwitchTable:=recommendSwitchTable, diagnostics:=diagnostics) ' Create the bound node. Return New BoundSelectStatement(node, expressionStmt, exprPlaceHolder, caseBlocks, recommendSwitchTable, exitLabel:=selectBinder.GetExitLabel(SyntaxKind.ExitSelectStatement)) End Function Private Function BindSelectExpression(node As ExpressionSyntax, diagnostics As BindingDiagnosticBag) As BoundExpression ' SPEC: A Select Case statement executes statements based on the value of an expression. ' SPEC: The expression must be classified as a value. ' We want to generate select case specific diagnostics if select expression is ' AddressOf expression or Lambda expression. ' For remaining expression kinds, bind the select expression as value. ' We also need to handle SyntaxKind.ParenthesizedExpression here. ' We might have a AddressOf expression or Lambda expression within a parenthesized expression. ' We want to generate ERRID.ERR_AddressOfInSelectCaseExpr/ERRID.ERR_LambdaInSelectCaseExpr for this case. ' See test BindingErrorTests.BC36635ERR_LambdaInSelectCaseExpr. Dim errorId As ERRID = Nothing Select Case node.Kind Case SyntaxKind.ParenthesizedExpression Dim parenthesizedExpr = DirectCast(node, ParenthesizedExpressionSyntax) Dim boundExpression = BindSelectExpression(parenthesizedExpr.Expression, diagnostics) Return New BoundParenthesized(node, boundExpression, boundExpression.Type) Case SyntaxKind.AddressOfExpression errorId = ERRID.ERR_AddressOfInSelectCaseExpr Case SyntaxKind.MultiLineFunctionLambdaExpression, SyntaxKind.MultiLineSubLambdaExpression, SyntaxKind.SingleLineFunctionLambdaExpression, SyntaxKind.SingleLineSubLambdaExpression errorId = ERRID.ERR_LambdaInSelectCaseExpr End Select Dim boundExpr = BindExpression(node, diagnostics) If boundExpr.HasErrors() Then boundExpr = MakeRValue(boundExpr, diagnostics) ElseIf errorId <> Nothing Then ReportDiagnostic(diagnostics, node, errorId) boundExpr = MakeRValueAndIgnoreDiagnostics(boundExpr) Else boundExpr = MakeRValue(boundExpr, diagnostics) End If Return boundExpr End Function Private Function BindCaseBlocks( caseBlocks As SyntaxList(Of CaseBlockSyntax), selectExpression As BoundRValuePlaceholder, convertCaseElements As Boolean, ByRef recommendSwitchTable As Boolean, diagnostics As BindingDiagnosticBag ) As ImmutableArray(Of BoundCaseBlock) If Not caseBlocks.IsEmpty() Then Dim caseBlocksBuilder = ArrayBuilder(Of BoundCaseBlock).GetInstance() ' Bind case blocks. For Each caseBlock In caseBlocks caseBlocksBuilder.Add(BindCaseBlock(caseBlock, selectExpression, convertCaseElements, diagnostics)) Next Return OptimizeSelectStatement(selectExpression, caseBlocksBuilder, recommendSwitchTable, diagnostics) End If Return ImmutableArray(Of BoundCaseBlock).Empty End Function Private Function BindCaseBlock( node As CaseBlockSyntax, selectExpression As BoundRValuePlaceholder, convertCaseElements As Boolean, diagnostics As BindingDiagnosticBag ) As BoundCaseBlock Dim caseStatement As BoundCaseStatement = BindCaseStatement(node.CaseStatement, selectExpression, convertCaseElements, diagnostics) Dim statementsSyntax As SyntaxList(Of StatementSyntax) = node.Statements Dim bodyBinder = GetBinder(statementsSyntax) Dim body As BoundBlock = bodyBinder.BindBlock(node, statementsSyntax, diagnostics).MakeCompilerGenerated() Return New BoundCaseBlock(node, caseStatement, body) End Function Private Function BindCaseStatement( node As CaseStatementSyntax, selectExpressionOpt As BoundRValuePlaceholder, convertCaseElements As Boolean, diagnostics As BindingDiagnosticBag ) As BoundCaseStatement Dim caseClauses As ImmutableArray(Of BoundCaseClause) If node.Kind = SyntaxKind.CaseStatement Then Dim caseClauseBuilder = ArrayBuilder(Of BoundCaseClause).GetInstance() ' Bind case clauses. For Each caseClause In node.Cases caseClauseBuilder.Add(BindCaseClause(caseClause, selectExpressionOpt, convertCaseElements, diagnostics)) Next caseClauses = caseClauseBuilder.ToImmutableAndFree() Else Debug.Assert(node.Kind = SyntaxKind.CaseElseStatement) caseClauses = ImmutableArray(Of BoundCaseClause).Empty End If Return New BoundCaseStatement(node, caseClauses, conditionOpt:=Nothing) End Function Private Function BindCaseClause( node As CaseClauseSyntax, selectExpressionOpt As BoundRValuePlaceholder, convertCaseElements As Boolean, diagnostics As BindingDiagnosticBag ) As BoundCaseClause Select Case node.Kind Case SyntaxKind.CaseEqualsClause, SyntaxKind.CaseNotEqualsClause, SyntaxKind.CaseGreaterThanClause, SyntaxKind.CaseGreaterThanOrEqualClause, SyntaxKind.CaseLessThanClause, SyntaxKind.CaseLessThanOrEqualClause Return BindRelationalCaseClause(DirectCast(node, RelationalCaseClauseSyntax), selectExpressionOpt, convertCaseElements, diagnostics) Case SyntaxKind.SimpleCaseClause Return BindSimpleCaseClause(DirectCast(node, SimpleCaseClauseSyntax), selectExpressionOpt, convertCaseElements, diagnostics) Case SyntaxKind.RangeCaseClause Return BindRangeCaseClause(DirectCast(node, RangeCaseClauseSyntax), selectExpressionOpt, convertCaseElements, diagnostics) Case Else Throw ExceptionUtilities.UnexpectedValue(node.Kind) End Select End Function Private Function BindRelationalCaseClause( node As RelationalCaseClauseSyntax, selectExpressionOpt As BoundRValuePlaceholder, convertCaseElements As Boolean, diagnostics As BindingDiagnosticBag ) As BoundCaseClause ' SPEC: A Case clause may take two forms. ' SPEC: One form is an optional Is keyword, a comparison operator, and an expression. ' SPEC: The expression is converted to the type of the Select expression; ' SPEC: if the expression is not implicitly convertible to the type of ' SPEC: the Select expression, a compile-time error occurs. ' SPEC: If the Select expression is E, the comparison operator is Op, ' SPEC: and the Case expression is E1, the case is evaluated as E OP E1. ' SPEC: The operator must be valid for the types of the two expressions; ' SPEC: otherwise a compile-time error occurs. ' Bind relational case clause as binary operator: E OP E1. ' BindBinaryOperator will generate the appropriate diagnostics. Debug.Assert(SyntaxFacts.IsRelationalOperator(node.OperatorToken.Kind) OrElse node.ContainsDiagnostics) Dim operatorKind As BinaryOperatorKind Select Case node.Kind Case SyntaxKind.CaseEqualsClause : operatorKind = BinaryOperatorKind.Equals Case SyntaxKind.CaseNotEqualsClause : operatorKind = BinaryOperatorKind.NotEquals Case SyntaxKind.CaseLessThanOrEqualClause : operatorKind = BinaryOperatorKind.LessThanOrEqual Case SyntaxKind.CaseGreaterThanOrEqualClause : operatorKind = BinaryOperatorKind.GreaterThanOrEqual Case SyntaxKind.CaseLessThanClause : operatorKind = BinaryOperatorKind.LessThan Case SyntaxKind.CaseGreaterThanClause : operatorKind = BinaryOperatorKind.GreaterThan Case Else Throw ExceptionUtilities.UnexpectedValue(node.Kind) End Select Dim conditionOpt As BoundExpression = Nothing Dim operandE1 As BoundExpression = BindCaseClauseExpression( expressionSyntax:=node.Value, caseClauseSyntax:=node, selectExpressionOpt:=selectExpressionOpt, operatorTokenKind:=node.OperatorToken.Kind, operatorKind:=operatorKind, convertCaseElements:=convertCaseElements, conditionOpt:=conditionOpt, diagnostics:=diagnostics) Return New BoundRelationalCaseClause(node, operatorKind, operandE1, conditionOpt) End Function Private Function BindSimpleCaseClause( node As SimpleCaseClauseSyntax, selectExpressionOpt As BoundRValuePlaceholder, convertCaseElements As Boolean, diagnostics As BindingDiagnosticBag ) As BoundCaseClause ' SPEC: The other form is an expression optionally followed by the keyword To and ' SPEC: a second expression. Both expressions are converted to the type of the ' SPEC: Select expression; if either expression is not implicitly convertible to ' SPEC: the type of the Select expression, a compile-time error occurs. ' SPEC: If the Select expression is E, the first Case expression is E1, ' SPEC: and the second Case expression is E2, the Case is evaluated either ' SPEC: as E = E1 (if no E2 is specified) or (E >= E1) And (E <= E2). ' SPEC: The operators must be valid for the types of the two expressions; ' SPEC: otherwise a compile-time error occurs. Dim conditionOpt As BoundExpression = Nothing ' Bind the Case clause as E = E1 (no E2 is specified) Dim value As BoundExpression = BindCaseClauseExpression( expressionSyntax:=node.Value, caseClauseSyntax:=node, selectExpressionOpt:=selectExpressionOpt, operatorTokenKind:=SyntaxKind.EqualsToken, operatorKind:=BinaryOperatorKind.Equals, convertCaseElements:=convertCaseElements, conditionOpt:=conditionOpt, diagnostics:=diagnostics) Return New BoundSimpleCaseClause(node, value, conditionOpt) End Function Private Function BindRangeCaseClause( node As RangeCaseClauseSyntax, selectExpressionOpt As BoundRValuePlaceholder, convertCaseElements As Boolean, diagnostics As BindingDiagnosticBag ) As BoundCaseClause ' SPEC: The other form is an expression optionally followed by the keyword To and ' SPEC: a second expression. Both expressions are converted to the type of the ' SPEC: Select expression; if either expression is not implicitly convertible to ' SPEC: the type of the Select expression, a compile-time error occurs. ' SPEC: If the Select expression is E, the first Case expression is E1, ' SPEC: and the second Case expression is E2, the Case is evaluated either ' SPEC: as E = E1 (if no E2 is specified) or (E >= E1) And (E <= E2). ' SPEC: The operators must be valid for the types of the two expressions; ' SPEC: otherwise a compile-time error occurs. Dim lowerBoundConditionOpt As BoundExpression = Nothing ' Bind case clause lower bound value (E >= E1) Dim lowerBound As BoundExpression = BindCaseClauseExpression( expressionSyntax:=node.LowerBound, caseClauseSyntax:=node, selectExpressionOpt:=selectExpressionOpt, operatorTokenKind:=SyntaxKind.GreaterThanEqualsToken, operatorKind:=BinaryOperatorKind.GreaterThanOrEqual, convertCaseElements:=convertCaseElements, conditionOpt:=lowerBoundConditionOpt, diagnostics:=diagnostics) ' Bind case clause upper bound value (E <= E2) Dim upperBoundConditionOpt As BoundExpression = Nothing Dim upperBound As BoundExpression = BindCaseClauseExpression( expressionSyntax:=node.UpperBound, caseClauseSyntax:=node, selectExpressionOpt:=selectExpressionOpt, operatorTokenKind:=SyntaxKind.LessThanEqualsToken, operatorKind:=BinaryOperatorKind.LessThanOrEqual, convertCaseElements:=convertCaseElements, conditionOpt:=upperBoundConditionOpt, diagnostics:=diagnostics) Return New BoundRangeCaseClause(node, lowerBound, upperBound, lowerBoundConditionOpt, upperBoundConditionOpt) End Function Private Function BindCaseClauseExpression( expressionSyntax As ExpressionSyntax, caseClauseSyntax As CaseClauseSyntax, selectExpressionOpt As BoundRValuePlaceholder, operatorTokenKind As SyntaxKind, operatorKind As BinaryOperatorKind, convertCaseElements As Boolean, ByRef conditionOpt As BoundExpression, diagnostics As BindingDiagnosticBag ) As BoundExpression Dim caseExpr As BoundExpression = BindValue(expressionSyntax, diagnostics) If selectExpressionOpt Is Nothing Then ' In error scenarios, such as a Case statement outside of a ' Select statement, the Select expression may be Nothing. conditionOpt = Nothing Return MakeRValue(caseExpr, diagnostics) End If If convertCaseElements AndAlso caseExpr.Type.IsIntrinsicOrEnumType() Then ' SPEC: The expression is converted to the type of the Select expression; ' SPEC: if the expression is not implicitly convertible to the type of the Select expression, a compile-time error occurs. Debug.Assert(selectExpressionOpt.Type IsNot Nothing) Return ApplyImplicitConversion(expressionSyntax, selectExpressionOpt.Type, caseExpr, diagnostics) Else ' SPEC: If the Select expression is E, the comparison operator is Op, ' SPEC: and the Case expression is E1, the case is evaluated as E OP E1. ' Bind binary operator "selectExpression OP caseExpr" to generate necessary diagnostics. conditionOpt = BindBinaryOperator( node:=caseClauseSyntax, left:=selectExpressionOpt, right:=caseExpr, operatorTokenKind:=operatorTokenKind, preliminaryOperatorKind:=operatorKind, isOperandOfConditionalBranch:=False, diagnostics:=diagnostics, isSelectCase:=True).MakeCompilerGenerated() Return Nothing End If End Function #Region "Helper methods for Binding Select case statement" ' This function is identical to the Semantics::OptimizeSelectStatement in native compiler. ' It performs two primary tasks: ' 1) Determines what kind of byte codes will be used for select: Switch Table or If List. ' Ideally we would like to delay these kind of optimizations till rewriting/emit phase. ' However, the computation of the case statement conditional would be redundant if ' we are eventually going to emit Switch table based byte code. Hence we match the ' native compiler behavior and check RecommendSwitchTable() here and store this ' value in the BoundSelectStatement node to be reused in the rewriter. ' 2) If we are going to generate If list based byte code, this function computes the ' conditional expression for case statements and stores it in the BoundCaseStatement nodes. ' Condition for each case statement "Case clause1, clause2, ..., clauseN" ' is computed as "clause1 OrElse clause2 OrElse ... OrElse clauseN" expression. Private Function OptimizeSelectStatement( selectExpression As BoundRValuePlaceholder, caseBlockBuilder As ArrayBuilder(Of BoundCaseBlock), ByRef generateSwitchTable As Boolean, diagnostics As BindingDiagnosticBag ) As ImmutableArray(Of BoundCaseBlock) Debug.Assert(Not selectExpression.HasErrors) generateSwitchTable = RecommendSwitchTable(selectExpression, caseBlockBuilder, diagnostics) ' CONSIDER: Do we need to compute the case statement conditional expression ' CONSIDER: even for generateSwitchTable case? We might want to do so to ' CONSIDER: maintain consistency of bound nodes coming out of the binder. ' CONSIDER: With the current design, value of BoundCaseStatement.ConditionOpt field ' CONSIDER: is dependent on the value of generateSwitchTable. If Not generateSwitchTable AndAlso caseBlockBuilder.Any() Then Dim booleanType = GetSpecialType(SpecialType.System_Boolean, selectExpression.Syntax, diagnostics) Dim caseClauseBuilder = ArrayBuilder(Of BoundCaseClause).GetInstance() For index = 0 To caseBlockBuilder.Count - 1 Dim caseBlock = caseBlockBuilder(index) If caseBlock.Syntax.Kind <> SyntaxKind.CaseElseBlock AndAlso Not caseBlock.CaseStatement.Syntax.IsMissing Then Dim caseStatement = caseBlock.CaseStatement Dim caseStatementSyntax = caseStatement.Syntax Dim caseStatementCondition As BoundExpression = Nothing Debug.Assert(caseStatement.CaseClauses.Any()) Dim clausesChanged = False ' Compute conditional expression for case statement For Each caseClause In caseStatement.CaseClauses Dim clauseCondition As BoundExpression = Nothing ' Compute conditional expression for case clause, if not already computed. Dim newCaseClause = ComputeCaseClauseCondition(caseClause, clauseCondition, selectExpression, diagnostics) caseClauseBuilder.Add(newCaseClause) clausesChanged = clausesChanged OrElse Not newCaseClause.Equals(caseClause) Debug.Assert(clauseCondition IsNot Nothing) If caseStatementCondition Is Nothing Then caseStatementCondition = clauseCondition Else ' caseStatementCondition = caseStatementCondition OrElse clauseCondition caseStatementCondition = BindBinaryOperator( node:=caseStatementSyntax, left:=caseStatementCondition, right:=clauseCondition, operatorTokenKind:=SyntaxKind.OrElseKeyword, preliminaryOperatorKind:=BinaryOperatorKind.OrElse, isOperandOfConditionalBranch:=False, diagnostics:=diagnostics, isSelectCase:=True).MakeCompilerGenerated() End If Next Dim newCaseClauses As ImmutableArray(Of BoundCaseClause) If clausesChanged Then newCaseClauses = caseClauseBuilder.ToImmutable() Else newCaseClauses = caseStatement.CaseClauses End If caseClauseBuilder.Clear() caseStatementCondition = ApplyImplicitConversion(caseStatementCondition.Syntax, booleanType, caseStatementCondition, diagnostics:=diagnostics, isOperandOfConditionalBranch:=True) caseStatement = caseStatement.Update(newCaseClauses, caseStatementCondition) caseBlockBuilder(index) = caseBlock.Update(caseStatement, caseBlock.Body) End If Next caseClauseBuilder.Free() End If Return caseBlockBuilder.ToImmutableAndFree() End Function Private Function ComputeCaseClauseCondition(caseClause As BoundCaseClause, <Out()> ByRef conditionOpt As BoundExpression, selectExpression As BoundRValuePlaceholder, diagnostics As BindingDiagnosticBag) As BoundCaseClause Select Case caseClause.Kind Case BoundKind.RelationalCaseClause Return ComputeRelationalCaseClauseCondition(DirectCast(caseClause, BoundRelationalCaseClause), conditionOpt, selectExpression, diagnostics) Case BoundKind.SimpleCaseClause Return ComputeSimpleCaseClauseCondition(DirectCast(caseClause, BoundSimpleCaseClause), conditionOpt, selectExpression, diagnostics) Case BoundKind.RangeCaseClause Return ComputeRangeCaseClauseCondition(DirectCast(caseClause, BoundRangeCaseClause), conditionOpt, selectExpression, diagnostics) Case Else Throw ExceptionUtilities.UnexpectedValue(caseClause.Kind) End Select End Function Private Function ComputeRelationalCaseClauseCondition(boundClause As BoundRelationalCaseClause, <Out()> ByRef conditionOpt As BoundExpression, selectExpression As BoundRValuePlaceholder, diagnostics As BindingDiagnosticBag) As BoundCaseClause Dim syntax = DirectCast(boundClause.Syntax, RelationalCaseClauseSyntax) ' Exactly one of the operand or condition must be non-null Debug.Assert(boundClause.ConditionOpt IsNot Nothing Xor boundClause.ValueOpt IsNot Nothing) conditionOpt = If(boundClause.ConditionOpt, BindBinaryOperator(node:=syntax, left:=selectExpression, right:=boundClause.ValueOpt, operatorTokenKind:=syntax.OperatorToken.Kind, preliminaryOperatorKind:=boundClause.OperatorKind, isOperandOfConditionalBranch:=False, diagnostics:=diagnostics, isSelectCase:=True).MakeCompilerGenerated()) Return boundClause.Update(boundClause.OperatorKind, valueOpt:=Nothing, conditionOpt:=conditionOpt) End Function Private Function ComputeSimpleCaseClauseCondition(boundClause As BoundSimpleCaseClause, <Out()> ByRef conditionOpt As BoundExpression, selectExpression As BoundRValuePlaceholder, diagnostics As BindingDiagnosticBag) As BoundCaseClause ' Exactly one of the value or condition must be non-null Debug.Assert(boundClause.ConditionOpt IsNot Nothing Xor boundClause.ValueOpt IsNot Nothing) conditionOpt = If(boundClause.ConditionOpt, BindBinaryOperator(node:=boundClause.Syntax, left:=selectExpression, right:=boundClause.ValueOpt, operatorTokenKind:=SyntaxKind.EqualsToken, preliminaryOperatorKind:=BinaryOperatorKind.Equals, isOperandOfConditionalBranch:=False, diagnostics:=diagnostics, isSelectCase:=True).MakeCompilerGenerated()) Return boundClause.Update(valueOpt:=Nothing, conditionOpt:=conditionOpt) End Function Private Function ComputeRangeCaseClauseCondition(boundClause As BoundRangeCaseClause, <Out()> ByRef conditionOpt As BoundExpression, selectExpression As BoundRValuePlaceholder, diagnostics As BindingDiagnosticBag) As BoundCaseClause Dim syntax = boundClause.Syntax ' Exactly one of the LowerBoundOpt or LowerBoundConditionOpt must be non-null Debug.Assert(boundClause.LowerBoundOpt IsNot Nothing Xor boundClause.LowerBoundConditionOpt IsNot Nothing) Dim lowerBoundConditionOpt = boundClause.LowerBoundConditionOpt If lowerBoundConditionOpt Is Nothing Then lowerBoundConditionOpt = BindBinaryOperator( node:=boundClause.Syntax, left:=selectExpression, right:=boundClause.LowerBoundOpt, operatorTokenKind:=SyntaxKind.GreaterThanEqualsToken, preliminaryOperatorKind:=BinaryOperatorKind.GreaterThanOrEqual, isOperandOfConditionalBranch:=False, diagnostics:=diagnostics, isSelectCase:=True).MakeCompilerGenerated() End If ' Exactly one of the UpperBoundOpt or UpperBoundConditionOpt must be non-null Debug.Assert(boundClause.UpperBoundOpt IsNot Nothing Xor boundClause.UpperBoundConditionOpt IsNot Nothing) Dim upperBoundConditionOpt = boundClause.UpperBoundConditionOpt If upperBoundConditionOpt Is Nothing Then upperBoundConditionOpt = BindBinaryOperator( node:=syntax, left:=selectExpression, right:=boundClause.UpperBoundOpt, operatorTokenKind:=SyntaxKind.LessThanEqualsToken, preliminaryOperatorKind:=BinaryOperatorKind.LessThanOrEqual, isOperandOfConditionalBranch:=False, diagnostics:=diagnostics, isSelectCase:=True).MakeCompilerGenerated() End If conditionOpt = BindBinaryOperator( node:=syntax, left:=lowerBoundConditionOpt, right:=upperBoundConditionOpt, operatorTokenKind:=SyntaxKind.AndAlsoKeyword, preliminaryOperatorKind:=BinaryOperatorKind.AndAlso, isOperandOfConditionalBranch:=False, diagnostics:=diagnostics, isSelectCase:=True).MakeCompilerGenerated() Return boundClause.Update(lowerBoundOpt:=Nothing, upperBoundOpt:=Nothing, lowerBoundConditionOpt:=lowerBoundConditionOpt, upperBoundConditionOpt:=upperBoundConditionOpt) End Function ' Helper method to determine if we must rewrite the select case statement as an IF list or a SWITCH table Private Function RecommendSwitchTable(selectExpr As BoundRValuePlaceholder, caseBlocks As ArrayBuilder(Of BoundCaseBlock), diagnostics As BindingDiagnosticBag) As Boolean ' We can rewrite select case statement as an IF list or a SWITCH table ' This function determines which method to use. ' The conditions for choosing the SWITCH table are: ' select case expression type must be integral/boolean/string (see TypeSymbolExtensions.IsValidTypeForSwitchTable()) ' no "Is <relop>" cases, except for <relop> = BinaryOperatorKind.Equals ' no "<lb> To <ub>" cases for string type ' for integral/boolean type, case values must be (or expand to, as in ranges) integer constants ' for string type, all case values must be string constants and OptionCompareText must be False. ' beneficial over IF lists (as per a threshold on size ratio) ' ranges must have lower bound first ' We also generate warnings for Invalid range clauses in this function. ' Ideally we would like to generate them in BindRangeCaseClause. ' However, Dev10 doesn't do this check individually for each CaseClause. ' It is performed only if bounds for all clauses in the Select are integer constants and ' all clauses are either range clauses or equality clause. ' Doing this differently will produce warnings in more scenarios - breaking change. If Not caseBlocks.Any() OrElse Not selectExpr.Type.IsValidTypeForSwitchTable() Then Return False End If Dim isSelectExprStringType = selectExpr.Type.IsStringType If isSelectExprStringType AndAlso OptionCompareText Then Return False End If Dim recommendSwitch = True For Each caseBlock In caseBlocks For Each caseClause In caseBlock.CaseStatement.CaseClauses Select Case caseClause.Kind Case BoundKind.RelationalCaseClause Dim relationalClause = DirectCast(caseClause, BoundRelationalCaseClause) ' Exactly one of the operand or condition must be non-null Debug.Assert(relationalClause.ValueOpt IsNot Nothing Xor relationalClause.ConditionOpt IsNot Nothing) Dim operand = relationalClause.ValueOpt If operand Is Nothing OrElse relationalClause.OperatorKind <> BinaryOperatorKind.Equals OrElse operand.ConstantValueOpt Is Nothing OrElse Not SwitchConstantValueHelper.IsValidSwitchCaseLabelConstant(operand.ConstantValueOpt) Then Return False End If Case BoundKind.RangeCaseClause ' TODO: RecommendSwitchTable for Range clause. ' TODO: If we decide to implement it we will need to ' TODO: add heuristic to determine when the range is ' TODO: big enough to prefer IF lists over SWITCH table. ' TODO: We will also need to add logic in the emitter ' TODO: to iterate through ConstantValues in a range. ' For now we use IF lists if we encounter BoundRangeCaseClause Dim rangeCaseClause = DirectCast(caseClause, BoundRangeCaseClause) ' Exactly one of the LowerBoundOpt or LowerBoundConditionOpt must be non-null Debug.Assert(rangeCaseClause.LowerBoundOpt IsNot Nothing Xor rangeCaseClause.LowerBoundConditionOpt IsNot Nothing) ' Exactly one of the UpperBoundOpt or UpperBoundConditionOpt must be non-null Debug.Assert(rangeCaseClause.UpperBoundOpt IsNot Nothing Xor rangeCaseClause.UpperBoundConditionOpt IsNot Nothing) Dim lowerBound = rangeCaseClause.LowerBoundOpt Dim upperBound = rangeCaseClause.UpperBoundOpt If lowerBound Is Nothing OrElse upperBound Is Nothing OrElse lowerBound.ConstantValueOpt Is Nothing OrElse upperBound.ConstantValueOpt Is Nothing OrElse Not SwitchConstantValueHelper.IsValidSwitchCaseLabelConstant(lowerBound.ConstantValueOpt) OrElse Not SwitchConstantValueHelper.IsValidSwitchCaseLabelConstant(upperBound.ConstantValueOpt) Then Return False End If recommendSwitch = False Case Else Dim simpleCaseClause = DirectCast(caseClause, BoundSimpleCaseClause) ' Exactly one of the value or condition must be non-null Debug.Assert(simpleCaseClause.ValueOpt IsNot Nothing Xor simpleCaseClause.ConditionOpt IsNot Nothing) Dim value = simpleCaseClause.ValueOpt If value Is Nothing OrElse value.ConstantValueOpt Is Nothing OrElse Not SwitchConstantValueHelper.IsValidSwitchCaseLabelConstant(value.ConstantValueOpt) Then Return False End If End Select Next Next ' TODO: beneficial over IF lists (as per a threshold on size ratio) Return Not ReportInvalidSelectCaseRange(caseBlocks, diagnostics) AndAlso recommendSwitch End Function ' Function reports WRN_SelectCaseInvalidRange for any invalid select case range. ' Returns True if an invalid range was found, False otherwise. Private Function ReportInvalidSelectCaseRange(caseBlocks As ArrayBuilder(Of BoundCaseBlock), diagnostics As BindingDiagnosticBag) As Boolean For Each caseBlock In caseBlocks For Each caseClause In caseBlock.CaseStatement.CaseClauses Select Case caseClause.Kind Case BoundKind.RangeCaseClause Dim rangeCaseClause = DirectCast(caseClause, BoundRangeCaseClause) Dim lowerBound = rangeCaseClause.LowerBoundOpt Dim upperBound = rangeCaseClause.UpperBoundOpt Debug.Assert(lowerBound IsNot Nothing) Debug.Assert(lowerBound.ConstantValueOpt IsNot Nothing) Debug.Assert(upperBound IsNot Nothing) Debug.Assert(upperBound.ConstantValueOpt IsNot Nothing) Debug.Assert(rangeCaseClause.LowerBoundConditionOpt Is Nothing) Debug.Assert(rangeCaseClause.UpperBoundConditionOpt Is Nothing) If IsInvalidSelectCaseRange(lowerBound.ConstantValueOpt, upperBound.ConstantValueOpt) Then ReportDiagnostic(diagnostics, rangeCaseClause.Syntax, ERRID.WRN_SelectCaseInvalidRange) Return True End If End Select Next Next Return False End Function Private Shared Function IsInvalidSelectCaseRange(lbConstantValue As ConstantValue, ubConstantValue As ConstantValue) As Boolean Debug.Assert(lbConstantValue IsNot Nothing) Debug.Assert(ubConstantValue IsNot Nothing) Debug.Assert(lbConstantValue.SpecialType = ubConstantValue.SpecialType) Select Case lbConstantValue.SpecialType Case SpecialType.System_Boolean, SpecialType.System_Byte, SpecialType.System_UInt16, SpecialType.System_UInt32, SpecialType.System_UInt64 Return lbConstantValue.UInt64Value > ubConstantValue.UInt64Value Case SpecialType.System_SByte, SpecialType.System_Int16, SpecialType.System_Int32, SpecialType.System_Int64, SpecialType.System_Char Return lbConstantValue.Int64Value > ubConstantValue.Int64Value Case SpecialType.System_Single, SpecialType.System_Double Return lbConstantValue.DoubleValue > ubConstantValue.DoubleValue Case SpecialType.System_Decimal Return lbConstantValue.DecimalValue > ubConstantValue.DecimalValue Case Else Return False End Select End Function #End Region #End Region End Class End Namespace