diff options
author | Søren Sandmann Pedersen <ssp@redhat.com> | 2012-06-16 22:40:50 -0400 |
---|---|---|
committer | Søren Sandmann Pedersen <ssp@redhat.com> | 2012-06-16 22:40:50 -0400 |
commit | 88cc707876ff3299f36e290aa4f691adeb13450a (patch) | |
tree | 4e71ed55dadd8ab481805e970b3668fd7525fda9 | |
parent | eb483ba44ce9953e0845fe99c452dcfa028a2a0d (diff) |
Fix bug with switch statements and goto
Previously, the condition expression in a switch statement would be
generated once, and then each case would do "dup; compare". After the
switch, the condition expression would be popped. The problem with
this is that if you jump out of the switch, the expression would be
left on the stack.
So instead generate a temporary variable to hold the condition
expression, then load that variable for each case.
-rw-r--r-- | graph.c | 9 | ||||
-rw-r--r-- | parser.c | 23 | ||||
-rw-r--r-- | type-check.c | 2 |
3 files changed, 26 insertions, 8 deletions
@@ -559,14 +559,18 @@ graph_switch_statement (ast_switch_statement_t *switch_, node_t *node) for (i = 0; cases[i] != NULL; ++i) cases[i]->common.label_node = node_new_label (NULL, (ast_t *)cases[i]); - node = graph_expression (switch_->condition, node); for (i = 0; cases[i] != NULL; ++i) { if (cases[i]->common.type == AST_EXPRESSION_CASE) { ast_expression_case_t *case_ = &cases[i]->expression; - node = node_new_dup (node, ast); + /* We code generate the condition expression for each case. This is not + * a problem because the parser has made sure that the expression + * is just a variable load so there are no issues with the expression + * having side-effects. + */ + node = graph_expression (switch_->condition, node); node = node_new_literal (case_->expr->common.constant_value, case_->expr->common.type_spec, node, ast); @@ -606,7 +610,6 @@ graph_switch_statement (ast_switch_statement_t *switch_, node_t *node) } node = node_insert_after (switch_->done_node, node); - node = node_new_pop (node, ast); return node; } @@ -655,8 +655,6 @@ parse_switch_statement (const token_t *in, const token_t *t = in; ast_expression_t *condition; ast_case_t **cases; - ast_statement_t *done = - ast_statement_new_label (unique_name ("break_switch")); if ((t = parse_token (t, TOKEN_SWITCH)) && (t = parse_token (t, TOKEN_LPAREN)) && @@ -666,9 +664,26 @@ parse_switch_statement (const token_t *in, (t = parse_cases (t, &cases)) && (t = parse_token (t, TOKEN_RBRACE))) { + /* Switch statements are desugared into a nest of if + * statements because it's quite difficult to implement + * switch statements without relying + */ + const char *cond_name = unique_name ("switch_condition"); + ast_definition_t *variable = ast_definition_new_variable ( + cond_name, ast_type_spec_new (AST_INFERRED_TYPE)); + ast_expression_t *cond_expr = ast_expression_new_binary ( + AST_ASSIGN, + ast_expression_new_identifier (cond_name), + condition); + + condition->common.type_inferrer = &variable->variable; + *result = ast_statement_new_compound ( - ast_statement_new_switch (condition, cases), - done); + ast_statement_new_compound ( + ast_statement_new_definition (variable), + ast_statement_new_expression (cond_expr)), + ast_statement_new_switch ( + ast_expression_new_identifier (cond_name), cases)); return t; } diff --git a/type-check.c b/type-check.c index f200d1a..9470237 100644 --- a/type-check.c +++ b/type-check.c @@ -870,9 +870,9 @@ type_check_pass2 (ast_t *ast) } else if (ast_is (ast, AST_STATEMENT, AST_SWITCH_STATEMENT)) { - int i; ast_type_spec_t *cond_type = get_type (ast->statement.switch_.condition); ast_case_t **cases = ast->statement.switch_.cases; + int i; for (i = 0; cases[i] != NULL; ++i) { |