diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 27066a08b6..3c9ff6e264 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -82,6 +82,7 @@ struct TransScopeSwitch { AstNode *switch_node; uint32_t case_index; bool found_default; + Buf *end_label_name; }; struct TransScopeVar { @@ -248,6 +249,18 @@ static AstNode *trans_create_node_addr_of(Context *c, bool is_const, bool is_vol return node; } +static AstNode *trans_create_node_goto(Context *c, Buf *label_name) { + AstNode *goto_node = trans_create_node(c, NodeTypeGoto); + goto_node->data.goto_expr.name = label_name; + return goto_node; +} + +static AstNode *trans_create_node_label(Context *c, Buf *label_name) { + AstNode *label_node = trans_create_node(c, NodeTypeLabel); + label_node->data.label.name = label_name; + return label_node; +} + static AstNode *trans_create_node_bool(Context *c, bool value) { AstNode *bool_node = trans_create_node(c, NodeTypeBoolLiteral); bool_node->data.bool_literal.value = value; @@ -2283,11 +2296,7 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt } static AstNode *trans_switch_stmt(Context *c, TransScope *parent_scope, const SwitchStmt *stmt) { - TransScopeWhile *while_scope = trans_scope_while_create(c, parent_scope); - while_scope->node->data.while_expr.condition = trans_create_node_bool(c, true); - - TransScopeBlock *block_scope = trans_scope_block_create(c, &while_scope->base); - while_scope->node->data.while_expr.body = block_scope->node; + TransScopeBlock *block_scope = trans_scope_block_create(c, parent_scope); TransScopeSwitch *switch_scope; @@ -2305,6 +2314,10 @@ static AstNode *trans_switch_stmt(Context *c, TransScope *parent_scope, const Sw } block_scope->node->data.block.statements.append(switch_scope->switch_node); + // TODO avoid name collisions + Buf *end_label_name = buf_create_from_str("end"); + switch_scope->end_label_name = end_label_name; + const Expr *cond_expr = stmt->getCond(); assert(cond_expr != nullptr); @@ -2336,9 +2349,11 @@ static AstNode *trans_switch_stmt(Context *c, TransScope *parent_scope, const Sw } // This is necessary if the last switch case "falls through" the end of the switch block - block_scope->node->data.block.statements.append(trans_create_node(c, NodeTypeBreak)); + block_scope->node->data.block.statements.append(trans_create_node_goto(c, end_label_name)); - return while_scope->node; + block_scope->node->data.block.statements.append(trans_create_node_label(c, end_label_name)); + + return block_scope->node; } static int trans_switch_case(Context *c, TransScope *parent_scope, const CaseStmt *stmt, AstNode **out_node, @@ -2365,18 +2380,13 @@ static int trans_switch_case(Context *c, TransScope *parent_scope, const CaseStm return ErrorUnexpected; prong_node->data.switch_prong.items.append(item_node); - AstNode *goto_node = trans_create_node(c, NodeTypeGoto); - goto_node->data.goto_expr.name = label_name; - prong_node->data.switch_prong.expr = goto_node; + prong_node->data.switch_prong.expr = trans_create_node_goto(c, label_name); switch_scope->switch_node->data.switch_expr.prongs.append(prong_node); } - AstNode *label_node = trans_create_node(c, NodeTypeLabel); - label_node->data.label.name = label_name; - TransScopeBlock *scope_block = trans_scope_block_find(parent_scope); - scope_block->node->data.block.statements.append(label_node); + scope_block->node->data.block.statements.append(trans_create_node_label(c, label_name)); AstNode *sub_stmt_node; TransScope *new_scope = trans_stmt(c, parent_scope, stmt->getSubStmt(), &sub_stmt_node); @@ -2399,23 +2409,19 @@ static int trans_switch_default(Context *c, TransScope *parent_scope, const Defa Buf *label_name = buf_sprintf("default"); - AstNode *label_node = trans_create_node(c, NodeTypeLabel); - label_node->data.label.name = label_name; - { // Add the prong AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng); - AstNode *goto_node = trans_create_node(c, NodeTypeGoto); - goto_node->data.goto_expr.name = label_name; - prong_node->data.switch_prong.expr = goto_node; + prong_node->data.switch_prong.expr = trans_create_node_goto(c, label_name); switch_scope->switch_node->data.switch_expr.prongs.append(prong_node); switch_scope->found_default = true; } TransScopeBlock *scope_block = trans_scope_block_find(parent_scope); - scope_block->node->data.block.statements.append(label_node); + scope_block->node->data.block.statements.append(trans_create_node_label(c, label_name)); + AstNode *sub_stmt_node; TransScope *new_scope = trans_stmt(c, parent_scope, stmt->getSubStmt(), &sub_stmt_node); @@ -2500,7 +2506,17 @@ static AstNode *trans_string_literal(Context *c, TransScope *scope, const String } static AstNode *trans_break_stmt(Context *c, TransScope *scope, const BreakStmt *stmt) { - return trans_create_node(c, NodeTypeBreak); + TransScope *cur_scope = scope; + while (cur_scope != nullptr) { + if (cur_scope->id == TransScopeIdWhile) { + return trans_create_node(c, NodeTypeBreak); + } else if (cur_scope->id == TransScopeIdSwitch) { + TransScopeSwitch *switch_scope = (TransScopeSwitch *)cur_scope; + return trans_create_node_goto(c, switch_scope->end_label_name); + } + cur_scope = cur_scope->parent; + } + zig_unreachable(); } static AstNode *trans_continue_stmt(Context *c, TransScope *scope, const ContinueStmt *stmt) { diff --git a/test/translate_c.zig b/test/translate_c.zig index e9f5e7de42..4323a867f8 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1019,7 +1019,7 @@ pub fn addCases(cases: &tests.TranslateCContext) { , \\fn foo(_arg_x: c_int) -> c_int { \\ var x = _arg_x; - \\ while (true) { + \\ { \\ switch (x) { \\ 1 => goto case_0, \\ 2 => goto case_1, @@ -1030,13 +1030,14 @@ pub fn addCases(cases: &tests.TranslateCContext) { \\ case_0: \\ x += 1; \\ case_1: - \\ break; + \\ goto end; \\ case_2: \\ case_3: \\ return x + 1; \\ default: \\ return 10; - \\ break; + \\ goto end; + \\ end: \\ }; \\ return x + 13; \\}