Commit 1b0e90f70b

Andrew Kelley <superjoe30@gmail.com>
2017-11-26 06:58:11
translate-c supports switch statements
1 parent 687e359
Changed files (2)
src/translate_c.cpp
@@ -69,6 +69,7 @@ enum TransScopeId {
     TransScopeIdVar,
     TransScopeIdBlock,
     TransScopeIdRoot,
+    TransScopeIdWhile,
 };
 
 struct TransScope {
@@ -79,6 +80,8 @@ struct TransScope {
 struct TransScopeSwitch {
     TransScope base;
     AstNode *switch_node;
+    uint32_t case_index;
+    bool found_default;
 };
 
 struct TransScopeVar {
@@ -96,12 +99,19 @@ struct TransScopeRoot {
     TransScope base;
 };
 
+struct TransScopeWhile {
+    TransScope base;
+    AstNode *node;
+};
+
 static TransScopeRoot *trans_scope_root_create(Context *c);
+static TransScopeWhile *trans_scope_while_create(Context *c, TransScope *parent_scope);
 static TransScopeBlock *trans_scope_block_create(Context *c, TransScope *parent_scope);
 static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scope, Buf *wanted_name);
-//static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope);
+static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope);
 
 static TransScopeBlock *trans_scope_block_find(TransScope *scope);
+static TransScopeSwitch *trans_scope_switch_find(TransScope *scope);
 
 static AstNode *resolve_record_decl(Context *c, const RecordDecl *record_decl);
 static AstNode *resolve_enum_decl(Context *c, const EnumDecl *enum_decl);
@@ -238,6 +248,12 @@ static AstNode *trans_create_node_addr_of(Context *c, bool is_const, bool is_vol
     return 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;
+    return bool_node;
+}
+
 static AstNode *trans_create_node_str_lit_c(Context *c, Buf *buf) {
     AstNode *node = trans_create_node(c, NodeTypeStringLiteral);
     node->data.string_literal.buf = buf;
@@ -965,22 +981,30 @@ static AstNode *trans_qual_type(Context *c, QualType qt, const SourceLocation &s
     return trans_type(c, qt.getTypePtr(), source_loc);
 }
 
-static AstNode *trans_compound_stmt(Context *c, TransScope *scope, const CompoundStmt *stmt,
-        TransScope **out_node_scope)
+static int trans_compound_stmt_inline(Context *c, TransScope *scope, const CompoundStmt *stmt,
+        AstNode *block_node, TransScope **out_node_scope)
 {
-    TransScopeBlock *child_scope_block = trans_scope_block_create(c, scope);
-    scope = &child_scope_block->base;
+    assert(block_node->type == NodeTypeBlock);
     for (CompoundStmt::const_body_iterator it = stmt->body_begin(), end_it = stmt->body_end(); it != end_it; ++it) {
         AstNode *child_node;
         scope = trans_stmt(c, scope, *it, &child_node);
         if (scope == nullptr)
-            return nullptr;
+            return ErrorUnexpected;
         if (child_node != nullptr)
-            child_scope_block->node->data.block.statements.append(child_node);
+            block_node->data.block.statements.append(child_node);
     }
     if (out_node_scope != nullptr) {
-        *out_node_scope = &child_scope_block->base;
+        *out_node_scope = scope;
     }
+    return ErrorNone;
+}
+
+static AstNode *trans_compound_stmt(Context *c, TransScope *scope, const CompoundStmt *stmt,
+        TransScope **out_node_scope)
+{
+    TransScopeBlock *child_scope_block = trans_scope_block_create(c, scope);
+    if (trans_compound_stmt_inline(c, &child_scope_block->base, stmt, child_scope_block->node, out_node_scope))
+        return nullptr;
     return child_scope_block->node;
 }
 
@@ -2081,17 +2105,18 @@ static int trans_local_declaration(Context *c, TransScope *scope, const DeclStmt
 }
 
 static AstNode *trans_while_loop(Context *c, TransScope *scope, const WhileStmt *stmt) {
-    AstNode *while_node = trans_create_node(c, NodeTypeWhileExpr);
+    TransScopeWhile *while_scope = trans_scope_while_create(c, scope);
 
-    while_node->data.while_expr.condition = trans_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue);
-    if (while_node->data.while_expr.condition == nullptr)
+    while_scope->node->data.while_expr.condition = trans_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue);
+    if (while_scope->node->data.while_expr.condition == nullptr)
         return nullptr;
 
-    TransScope *body_scope = trans_stmt(c, scope, stmt->getBody(), &while_node->data.while_expr.body);
+    TransScope *body_scope = trans_stmt(c, &while_scope->base, stmt->getBody(),
+            &while_scope->node->data.while_expr.body);
     if (body_scope == nullptr) 
         return nullptr;
 
-    return while_node;
+    return while_scope->node;
 }
 
 static AstNode *trans_if_statement(Context *c, TransScope *scope, const IfStmt *stmt) {
@@ -2201,11 +2226,9 @@ static AstNode *trans_unary_expr_or_type_trait_expr(Context *c, TransScope *scop
 }
 
 static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt *stmt) {
-    AstNode *while_node = trans_create_node(c, NodeTypeWhileExpr);
+    TransScopeWhile *while_scope = trans_scope_while_create(c, parent_scope);
 
-    AstNode *true_node = trans_create_node(c, NodeTypeBoolLiteral);
-    true_node->data.bool_literal.value = true;
-    while_node->data.while_expr.condition = true_node;
+    while_scope->node->data.while_expr.condition = trans_create_node_bool(c, true);
 
     AstNode *body_node;
     TransScope *child_scope;
@@ -2222,7 +2245,7 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt
         // zig: }
 
         // We call the low level function so that we can set child_scope to the scope of the generated block.
-        if (trans_stmt_extra(c, parent_scope, stmt->getBody(), ResultUsedNo, TransRValue, &body_node,
+        if (trans_stmt_extra(c, &while_scope->base, stmt->getBody(), ResultUsedNo, TransRValue, &body_node,
             nullptr, &child_scope))
         {
             return nullptr;
@@ -2237,7 +2260,7 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt
         // zig:   a;
         // zig:   if (!cond) break;
         // zig: }
-        TransScopeBlock *child_block_scope = trans_scope_block_create(c, parent_scope);
+        TransScopeBlock *child_block_scope = trans_scope_block_create(c, &while_scope->base);
         body_node = child_block_scope->node;
         AstNode *child_statement;
         child_scope = trans_stmt(c, &child_block_scope->base, stmt->getBody(), &child_statement);
@@ -2254,89 +2277,206 @@ static AstNode *trans_do_loop(Context *c, TransScope *parent_scope, const DoStmt
 
     body_node->data.block.statements.append(terminator_node);
 
-    while_node->data.while_expr.body = body_node;
+    while_scope->node->data.while_expr.body = body_node;
 
-    return while_node;
+    return while_scope->node;
 }
 
-//static AstNode *trans_switch_stmt(Context *c, TransScope *scope, const SwitchStmt *stmt) {
-//    AstNode *switch_block_node = trans_create_node(c, NodeTypeBlock);
-//    AstNode *switch_node = trans_create_node(c, NodeTypeSwitchExpr);
-//    const DeclStmt *var_decl_stmt = stmt->getConditionVariableDeclStmt();
-//    if (var_decl_stmt != nullptr) {
-//        AstNode *vars_node = trans_stmt(c, switch_block_node, var_decl_stmt);
-//        if (vars_node == nullptr)
-//            return nullptr;
-//        if (vars_node != nullptr)
-//            switch_block_node->data.block.statements.append(vars_node);
-//    }
-//    switch_block_node->data.block.statements.append(switch_node);
-//
-//    const Expr *cond_expr = stmt->getCond();
-//    assert(cond_expr != nullptr);
-//
-//    AstNode *expr_node = trans_expr(c, ResultUsedYes, switch_block_node, cond_expr, TransRValue);
-//    if (expr_node == nullptr)
-//        return nullptr;
-//    switch_node->data.switch_expr.expr = expr_node;
-//
-//    AstNode *body_node = trans_stmt(c, switch_block_node, stmt->getBody());
-//    if (body_node == nullptr)
-//        return nullptr;
-//    if (body_node != nullptr)
-//        switch_block_node->data.block.statements.append(body_node);
-//
-//    return switch_block_node;
-//}
+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;
+
+    TransScopeSwitch *switch_scope;
+
+    const DeclStmt *var_decl_stmt = stmt->getConditionVariableDeclStmt();
+    if (var_decl_stmt == nullptr) {
+        switch_scope = trans_scope_switch_create(c, &block_scope->base);
+    } else {
+        AstNode *vars_node;
+        TransScope *var_scope = trans_stmt(c, &block_scope->base, var_decl_stmt, &vars_node);
+        if (var_scope == nullptr)
+            return nullptr;
+        if (vars_node != nullptr)
+            block_scope->node->data.block.statements.append(vars_node);
+        switch_scope = trans_scope_switch_create(c, var_scope);
+    }
+    block_scope->node->data.block.statements.append(switch_scope->switch_node);
+
+    const Expr *cond_expr = stmt->getCond();
+    assert(cond_expr != nullptr);
+
+    AstNode *expr_node = trans_expr(c, ResultUsedYes, &block_scope->base, cond_expr, TransRValue);
+    if (expr_node == nullptr)
+        return nullptr;
+    switch_scope->switch_node->data.switch_expr.expr = expr_node;
+
+    AstNode *body_node;
+    const Stmt *body_stmt = stmt->getBody();
+    if (body_stmt->getStmtClass() == Stmt::CompoundStmtClass) {
+        if (trans_compound_stmt_inline(c, &switch_scope->base, (const CompoundStmt *)body_stmt,
+            block_scope->node, nullptr))
+        {
+            return nullptr;
+        }
+    } else {
+        TransScope *body_scope = trans_stmt(c, &switch_scope->base, body_stmt, &body_node);
+        if (body_scope == nullptr)
+            return nullptr;
+        if (body_node != nullptr)
+            block_scope->node->data.block.statements.append(body_node);
+    }
+
+    if (!switch_scope->found_default && !stmt->isAllEnumCasesCovered()) {
+        AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng);
+        prong_node->data.switch_prong.expr = trans_create_node(c, NodeTypeBreak);
+        switch_scope->switch_node->data.switch_expr.prongs.append(prong_node);
+    }
+
+    // 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));
+
+    return while_scope->node;
+}
+
+static int trans_switch_case(Context *c, TransScope *parent_scope, const CaseStmt *stmt, AstNode **out_node,
+        TransScope **out_scope)
+{
+    *out_node = nullptr;
+
+    if (stmt->getRHS() != nullptr) {
+        emit_warning(c, stmt->getLocStart(), "TODO support GNU switch case a ... b extension");
+        return ErrorUnexpected;
+    }
+
+    TransScopeSwitch *switch_scope = trans_scope_switch_find(parent_scope);
+    assert(switch_scope != nullptr);
+
+    Buf *label_name = buf_sprintf("case_%" PRIu32, switch_scope->case_index);
+    switch_scope->case_index += 1;
+
+    {
+        // Add the prong
+        AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng);
+        AstNode *item_node = trans_expr(c, ResultUsedYes, &switch_scope->base, stmt->getLHS(), TransRValue);
+        if (item_node == nullptr)
+            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;
+
+        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);
+
+    AstNode *sub_stmt_node;
+    TransScope *new_scope = trans_stmt(c, parent_scope, stmt->getSubStmt(), &sub_stmt_node);
+    if (new_scope == nullptr)
+        return ErrorUnexpected;
+    if (sub_stmt_node != nullptr)
+        scope_block->node->data.block.statements.append(sub_stmt_node);
+
+    *out_scope = new_scope;
+    return ErrorNone;
+}
+
+static int trans_switch_default(Context *c, TransScope *parent_scope, const DefaultStmt *stmt, AstNode **out_node,
+        TransScope **out_scope)
+{
+    *out_node = nullptr;
+
+    TransScopeSwitch *switch_scope = trans_scope_switch_find(parent_scope);
+    assert(switch_scope != nullptr);
+
+    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;
+
+        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);
+
+    AstNode *sub_stmt_node;
+    TransScope *new_scope = trans_stmt(c, parent_scope, stmt->getSubStmt(), &sub_stmt_node);
+    if (new_scope == nullptr)
+        return ErrorUnexpected;
+    if (sub_stmt_node != nullptr)
+        scope_block->node->data.block.statements.append(sub_stmt_node);
+
+    *out_scope = new_scope;
+    return ErrorNone;
+}
 
 static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForStmt *stmt) {
     AstNode *loop_block_node;
-    TransScope *inner_scope;
-    AstNode *while_node = trans_create_node(c, NodeTypeWhileExpr);
+    TransScopeWhile *while_scope;
+    TransScope *cond_scope;
     const Stmt *init_stmt = stmt->getInit();
     if (init_stmt == nullptr) {
-        loop_block_node = while_node;
-        inner_scope = parent_scope;
+        while_scope = trans_scope_while_create(c, parent_scope);
+        loop_block_node = while_scope->node;
+        cond_scope = parent_scope;
     } else {
         TransScopeBlock *child_scope = trans_scope_block_create(c, parent_scope);
         loop_block_node = child_scope->node;
-        inner_scope = &child_scope->base;
 
         AstNode *vars_node;
-        inner_scope = trans_stmt(c, &child_scope->base, init_stmt, &vars_node);
-        if (inner_scope == nullptr)
+        cond_scope = trans_stmt(c, &child_scope->base, init_stmt, &vars_node);
+        if (cond_scope == nullptr)
             return nullptr;
         if (vars_node != nullptr)
             child_scope->node->data.block.statements.append(vars_node);
 
-        child_scope->node->data.block.statements.append(while_node);
+        while_scope = trans_scope_while_create(c, cond_scope);
+
+        child_scope->node->data.block.statements.append(while_scope->node);
     }
 
     const Stmt *cond_stmt = stmt->getCond();
     if (cond_stmt == nullptr) {
-        AstNode *true_node = trans_create_node(c, NodeTypeBoolLiteral);
-        true_node->data.bool_literal.value = true;
-        while_node->data.while_expr.condition = true_node;
+        while_scope->node->data.while_expr.condition = trans_create_node_bool(c, true);
     } else {
-        TransScope *cond_scope = trans_stmt(c, inner_scope, cond_stmt, &while_node->data.while_expr.condition);
-        if (cond_scope == nullptr)
+        TransScope *end_cond_scope = trans_stmt(c, cond_scope, cond_stmt,
+                &while_scope->node->data.while_expr.condition);
+        if (end_cond_scope == nullptr)
             return nullptr;
     }
 
     const Stmt *inc_stmt = stmt->getInc();
     if (inc_stmt != nullptr) {
         AstNode *inc_node;
-        TransScope *inc_scope = trans_stmt(c, inner_scope, inc_stmt, &inc_node);
+        TransScope *inc_scope = trans_stmt(c, cond_scope, inc_stmt, &inc_node);
         if (inc_scope == nullptr)
             return nullptr;
-        while_node->data.while_expr.continue_expr = inc_node;
+        while_scope->node->data.while_expr.continue_expr = inc_node;
     }
 
-    AstNode *child_statement;
-    TransScope *body_scope = trans_stmt(c, inner_scope, stmt->getBody(), &child_statement);
+    AstNode *body_statement;
+    TransScope *body_scope = trans_stmt(c, &while_scope->base, stmt->getBody(), &body_statement);
     if (body_scope == nullptr)
         return nullptr;
-    while_node->data.while_expr.body = child_statement;
+    while_scope->node->data.while_expr.body = body_statement;
 
     return loop_block_node;
 }
@@ -2371,9 +2511,8 @@ static int wrap_stmt(AstNode **out_node, TransScope **out_scope, TransScope *in_
     if (result_node == nullptr)
         return ErrorUnexpected;
     *out_node = result_node;
-    if (out_scope != nullptr) {
+    if (out_scope != nullptr)
         *out_scope = in_scope;
-    }
     return ErrorNone;
 }
 
@@ -2456,18 +2595,13 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt,
         case Stmt::ParenExprClass:
             return wrap_stmt(out_node, out_child_scope, scope,
                     trans_expr(c, result_used, scope, ((const ParenExpr*)stmt)->getSubExpr(), lrvalue));
-//        case Stmt::SwitchStmtClass:
-//            return wrap_stmt(out_node, out_child_scope, scope,
-//            trans_switch_stmt(c, scope, (const SwitchStmt *)stmt));
         case Stmt::SwitchStmtClass:
-            emit_warning(c, stmt->getLocStart(), "TODO handle C SwitchStmtClass");
-            return ErrorUnexpected;
+            return wrap_stmt(out_node, out_child_scope, scope,
+                    trans_switch_stmt(c, scope, (const SwitchStmt *)stmt));
         case Stmt::CaseStmtClass:
-            emit_warning(c, stmt->getLocStart(), "TODO handle C CaseStmtClass");
-            return ErrorUnexpected;
+            return trans_switch_case(c, scope, (const CaseStmt *)stmt, out_node, out_child_scope);
         case Stmt::DefaultStmtClass:
-            emit_warning(c, stmt->getLocStart(), "TODO handle C DefaultStmtClass");
-            return ErrorUnexpected;
+            return trans_switch_default(c, scope, (const DefaultStmt *)stmt, out_node, out_child_scope);
         case Stmt::NoStmtClass:
             emit_warning(c, stmt->getLocStart(), "TODO handle C NoStmtClass");
             return ErrorUnexpected;
@@ -2981,7 +3115,8 @@ static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope
         TransLRValue lrval)
 {
     AstNode *result_node;
-    if (trans_stmt_extra(c, scope, expr, result_used, lrval, &result_node, nullptr, nullptr)) {
+    TransScope *result_scope;
+    if (trans_stmt_extra(c, scope, expr, result_used, lrval, &result_node, &result_scope, nullptr)) {
         return nullptr;
     }
     return result_node;
@@ -3536,6 +3671,14 @@ static TransScopeRoot *trans_scope_root_create(Context *c) {
     return result;
 }
 
+static TransScopeWhile *trans_scope_while_create(Context *c, TransScope *parent_scope) {
+    TransScopeWhile *result = allocate<TransScopeWhile>(1);
+    result->base.id = TransScopeIdWhile;
+    result->base.parent = parent_scope;
+    result->node = trans_create_node(c, NodeTypeWhileExpr);
+    return result;
+}
+
 static TransScopeBlock *trans_scope_block_create(Context *c, TransScope *parent_scope) {
     TransScopeBlock *result = allocate<TransScopeBlock>(1);
     result->base.id = TransScopeIdBlock;
@@ -3553,13 +3696,13 @@ static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scop
     return result;
 }
 
-//static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope) {
-//    TransScopeSwitch *result = allocate<TransScopeSwitch>(1);
-//    result->base.id = TransScopeIdSwitch;
-//    result->base.parent = parent_scope;
-//    result->switch_node = trans_create_node(c, NodeTypeSwitchExpr);
-//    return result;
-//}
+static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope) {
+    TransScopeSwitch *result = allocate<TransScopeSwitch>(1);
+    result->base.id = TransScopeIdSwitch;
+    result->base.parent = parent_scope;
+    result->switch_node = trans_create_node(c, NodeTypeSwitchExpr);
+    return result;
+}
 
 static TransScopeBlock *trans_scope_block_find(TransScope *scope) {
     while (scope != nullptr) {
@@ -3571,6 +3714,16 @@ static TransScopeBlock *trans_scope_block_find(TransScope *scope) {
     return nullptr;
 }
 
+static TransScopeSwitch *trans_scope_switch_find(TransScope *scope) {
+    while (scope != nullptr) {
+        if (scope->id == TransScopeIdSwitch) {
+            return (TransScopeSwitch *)scope;
+        }
+        scope = scope->parent;
+    }
+    return nullptr;
+}
+
 static void render_aliases(Context *c) {
     for (size_t i = 0; i < c->aliases.length; i += 1) {
         Alias *alias = &c->aliases.at(i);
test/translate_c.zig
@@ -1001,46 +1001,46 @@ pub fn addCases(cases: &tests.TranslateCContext) {
         \\}
     );
 
-    //cases.add("switch statement",
-    //    \\int foo(int x) {
-    //    \\    switch (x) {
-    //    \\        case 1:
-    //    \\            x += 1;
-    //    \\        case 2:
-    //    \\            break;
-    //    \\        case 3:
-    //    \\        case 4:
-    //    \\            return x + 1;
-    //    \\        default:
-    //    \\            return 10;
-    //    \\    }
-    //    \\    return x + 13;
-    //    \\}
-    //,
-    //    \\fn foo(_x: i32) -> i32 {
-    //    \\    var x = _x;
-    //    \\    switch (x) {
-    //    \\        1 => goto switch_case_1;
-    //    \\        2 => goto switch_case_2;
-    //    \\        3 => goto switch_case_3;
-    //    \\        4 => goto switch_case_4;
-    //    \\        else => goto switch_default;
-    //    \\    }
-    //    \\switch_case_1:
-    //    \\    x += 1;
-    //    \\    goto switch_case_2;
-    //    \\switch_case_2:
-    //    \\    goto switch_end;
-    //    \\switch_case_3:
-    //    \\    goto switch_case_4;
-    //    \\switch_case_4:
-    //    \\    return x += 1;
-    //    \\switch_default:
-    //    \\    return 10;
-    //    \\switch_end:
-    //    \\    return x + 13;
-    //    \\}
-    //);
+    cases.add("switch statement",
+        \\int foo(int x) {
+        \\    switch (x) {
+        \\        case 1:
+        \\            x += 1;
+        \\        case 2:
+        \\            break;
+        \\        case 3:
+        \\        case 4:
+        \\            return x + 1;
+        \\        default:
+        \\            return 10;
+        \\    }
+        \\    return x + 13;
+        \\}
+    ,
+        \\fn foo(_arg_x: c_int) -> c_int {
+        \\    var x = _arg_x;
+        \\    while (true) {
+        \\        switch (x) {
+        \\            1 => goto case_0,
+        \\            2 => goto case_1,
+        \\            3 => goto case_2,
+        \\            4 => goto case_3,
+        \\            else => goto default,
+        \\        };
+        \\    case_0:
+        \\        x += 1;
+        \\    case_1:
+        \\        break;
+        \\    case_2:
+        \\    case_3:
+        \\        return x + 1;
+        \\    default:
+        \\        return 10;
+        \\        break;
+        \\    };
+        \\    return x + 13;
+        \\}
+    );
 }