Commit aa2ca3f02c

Andrew Kelley <superjoe30@gmail.com>
2017-11-26 21:58:49
translate-c: better way to translate switch
previously `continue` would be handled incorrectly
1 parent 1b0e90f
Changed files (2)
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) {
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;
         \\}