Chromium Code Reviews| Index: runtime/vm/parser.cc |
| =================================================================== |
| --- runtime/vm/parser.cc (revision 27964) |
| +++ runtime/vm/parser.cc (working copy) |
| @@ -6044,7 +6044,55 @@ |
| } |
| +// Check that all case expressions are of the same type, either int, String, |
| +// double or any other class that does not override the == operator. |
| +// The expressions are compile-time constants and are thus in the form |
| +// of a LiteralNode. |
| +void Parser::CheckCaseExpressions(const GrowableArray<LiteralNode*>& values) { |
| + const intptr_t num_expressions = values.length(); |
| + if (num_expressions == 0) { |
| + return; |
| + } |
| + const Instance& first_value = values[0]->literal(); |
| + for (intptr_t i = 0; i < num_expressions; i++) { |
|
Ivan Posva
2013/09/27 00:24:59
Why don't you start with i = 1? Otherwise you just
hausner
2013/09/27 00:31:34
Because I still need to check that the first value
|
| + const Instance& val = values[i]->literal(); |
| + const intptr_t val_pos = values[i]->token_pos(); |
| + if (first_value.IsInteger()) { |
| + if (!val.IsInteger()) { |
| + ErrorMsg(val_pos, "expected case expression of type int"); |
| + } |
| + continue; |
| + } |
| + if (first_value.IsString()) { |
| + if (!val.IsString()) { |
| + ErrorMsg(val_pos, "expected case expression of type String"); |
| + } |
| + continue; |
| + } |
| + if (first_value.IsDouble()) { |
| + if (!val.IsDouble()) { |
| + ErrorMsg(val_pos, "expected case expression of type double"); |
| + } |
| + continue; |
| + } |
| + if (val.clazz() != first_value.clazz()) { |
| + ErrorMsg(val_pos, "all case expressions must be of same type"); |
| + } |
| + Class& cls = Class::Handle(val.clazz()); |
| + const Function& equal_op = Function::Handle( |
| + Resolver::ResolveDynamicAnyArgs(cls, Symbols::EqualOperator())); |
|
Ivan Posva
2013/09/27 00:24:59
Can't you hoist this check out of the loop? Once y
hausner
2013/09/27 00:31:34
Good point. If I hoist it out I have to duplicate
Ivan Posva
2013/09/27 00:44:45
You can hoist the whole testing out. Most of the t
hausner
2013/09/27 00:49:16
The whole reason why int and String are handled se
|
| + ASSERT(!equal_op.IsNull()); |
| + cls = equal_op.Owner(); |
| + if (!cls.IsObjectClass()) { |
| + ErrorMsg(val_pos, |
| + "type class of case expression must not implement operator =="); |
| + } |
| + } |
| +} |
| + |
| + |
| CaseNode* Parser::ParseCaseClause(LocalVariable* switch_expr_value, |
| + GrowableArray<LiteralNode*>* case_expr_values, |
| SourceLabel* case_label) { |
| TRACE_PARSER("ParseCaseClause"); |
| bool default_seen = false; |
| @@ -6059,6 +6107,9 @@ |
| ConsumeToken(); // Keyword case. |
| const intptr_t expr_pos = TokenPos(); |
| AstNode* expr = ParseExpr(kRequireConst, kConsumeCascades); |
| + ASSERT(expr->IsLiteralNode()); |
| + case_expr_values->Add(expr->AsLiteralNode()); |
| + |
| AstNode* switch_expr_load = new LoadLocalNode(case_pos, |
| switch_expr_value); |
| AstNode* case_comparison = new ComparisonNode(expr_pos, |
| @@ -6147,6 +6198,7 @@ |
| // Parse case clauses |
| bool default_seen = false; |
| + GrowableArray<LiteralNode*> case_expr_values; |
| while (true) { |
| // Check for statement label |
| SourceLabel* case_label = NULL; |
| @@ -6177,7 +6229,8 @@ |
| if (default_seen) { |
| ErrorMsg("no case clauses allowed after default clause"); |
| } |
| - CaseNode* case_clause = ParseCaseClause(temp_variable, case_label); |
| + CaseNode* case_clause = |
| + ParseCaseClause(temp_variable, &case_expr_values, case_label); |
| default_seen = case_clause->contains_default(); |
| current_block_->statements->Add(case_clause); |
| } else if (CurrentToken() != Token::kRBRACE) { |
| @@ -6189,8 +6242,9 @@ |
| } |
| } |
| - // TODO(hausner): Check that all expressions in case clauses are |
| - // of the same class, or implement int or String (issue 7307). |
| + // Check that all expressions in case clauses are of the same class, |
| + // or implement int, double or String. |
| + CheckCaseExpressions(case_expr_values); |
| // Check for unresolved label references. |
| SourceLabel* unresolved_label = |