authorgravatar for evan@lagerdata.comEvan Haas <evan@lagerdata.com> 2021-04-08 09:25:52-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-13 11:45:03-07:00
loge761e0ac18aa1eab22b9547d702067910ed82c4a
tree2051a37e060386eab8b52152b6f478bc6e54945a
parent36a33c99e3bc0825ada2ce1b374c32b4d6dee6b0

translate-c: wrap switch statements in a while (true) loop

This allows `break` statements to be directly translated from the original C. Add a break statement as the last statement of the while loop to ensure we don't have an infinite loop if no breaks / returns are hit in the switch. Fixes #8387

3 files changed, 109 insertions(+), 40 deletions(-)

src/translate_c.zig+25-8
...@@ -2357,7 +2357,6 @@ fn transInitListExprVector(...@@ -2357,7 +2357,6 @@ fn transInitListExprVector(
2357 expr: *const clang.InitListExpr,2357 expr: *const clang.InitListExpr,
2358 ty: *const clang.Type,2358 ty: *const clang.Type,
2359) TransError!Node {2359) TransError!Node {
2360
2361 const qt = getExprQualType(c, @ptrCast(*const clang.Expr, expr));2360 const qt = getExprQualType(c, @ptrCast(*const clang.Expr, expr));
2362 const vector_type = try transQualType(c, scope, qt, loc);2361 const vector_type = try transQualType(c, scope, qt, loc);
2363 const init_count = expr.getNumInits();2362 const init_count = expr.getNumInits();
...@@ -2700,9 +2699,19 @@ fn transSwitch(...@@ -2700,9 +2699,19 @@ fn transSwitch(
2700 scope: *Scope,2699 scope: *Scope,
2701 stmt: *const clang.SwitchStmt,2700 stmt: *const clang.SwitchStmt,
2702) TransError!Node {2701) TransError!Node {
2702 var loop_scope = Scope{
2703 .parent = scope,
2704 .id = .loop,
2705 };
2706
2707 var block_scope = try Scope.Block.init(c, &loop_scope, false);
2708 defer block_scope.deinit();
2709
2710 const base_scope = &block_scope.base;
2711
2703 var cond_scope = Scope.Condition{2712 var cond_scope = Scope.Condition{
2704 .base = .{2713 .base = .{
2705 .parent = scope,2714 .parent = base_scope,
2706 .id = .condition,2715 .id = .condition,
2707 },2716 },
2708 };2717 };
...@@ -2725,8 +2734,8 @@ fn transSwitch(...@@ -2725,8 +2734,8 @@ fn transSwitch(
2725 .CaseStmtClass => {2734 .CaseStmtClass => {
2726 var items = std.ArrayList(Node).init(c.gpa);2735 var items = std.ArrayList(Node).init(c.gpa);
2727 defer items.deinit();2736 defer items.deinit();
2728 const sub = try transCaseStmt(c, scope, it[0], &items);2737 const sub = try transCaseStmt(c, base_scope, it[0], &items);
2729 const res = try transSwitchProngStmt(c, scope, sub, it, end_it);2738 const res = try transSwitchProngStmt(c, base_scope, sub, it, end_it);
27302739
2731 if (items.items.len == 0) {2740 if (items.items.len == 0) {
2732 has_default = true;2741 has_default = true;
...@@ -2751,7 +2760,7 @@ fn transSwitch(...@@ -2751,7 +2760,7 @@ fn transSwitch(
2751 else => break,2760 else => break,
2752 };2761 };
27532762
2754 const res = try transSwitchProngStmt(c, scope, sub, it, end_it);2763 const res = try transSwitchProngStmt(c, base_scope, sub, it, end_it);
27552764
2756 const switch_else = try Tag.switch_else.create(c.arena, res);2765 const switch_else = try Tag.switch_else.create(c.arena, res);
2757 try cases.append(switch_else);2766 try cases.append(switch_else);
...@@ -2765,10 +2774,15 @@ fn transSwitch(...@@ -2765,10 +2774,15 @@ fn transSwitch(
2765 try cases.append(else_prong);2774 try cases.append(else_prong);
2766 }2775 }
27672776
2768 return Tag.@"switch".create(c.arena, .{2777 const switch_node = try Tag.@"switch".create(c.arena, .{
2769 .cond = switch_expr,2778 .cond = switch_expr,
2770 .cases = try c.arena.dupe(Node, cases.items),2779 .cases = try c.arena.dupe(Node, cases.items),
2771 });2780 });
2781 try block_scope.statements.append(switch_node);
2782 try block_scope.statements.append(Tag.@"break".init());
2783 const while_body = try block_scope.complete(c);
2784
2785 return Tag.while_true.create(c.arena, while_body);
2772}2786}
27732787
2774/// Collects all items for this case, returns the first statement after the labels.2788/// Collects all items for this case, returns the first statement after the labels.
...@@ -2818,7 +2832,7 @@ fn transSwitchProngStmt(...@@ -2818,7 +2832,7 @@ fn transSwitchProngStmt(
2818 parent_end_it: clang.CompoundStmt.ConstBodyIterator,2832 parent_end_it: clang.CompoundStmt.ConstBodyIterator,
2819) TransError!Node {2833) TransError!Node {
2820 switch (stmt.getStmtClass()) {2834 switch (stmt.getStmtClass()) {
2821 .BreakStmtClass => return Tag.empty_block.init(),2835 .BreakStmtClass => return Tag.@"break".init(),
2822 .ReturnStmtClass => return transStmt(c, scope, stmt, .unused),2836 .ReturnStmtClass => return transStmt(c, scope, stmt, .unused),
2823 .CaseStmtClass, .DefaultStmtClass => unreachable,2837 .CaseStmtClass, .DefaultStmtClass => unreachable,
2824 else => {2838 else => {
...@@ -2847,7 +2861,10 @@ fn transSwitchProngStmtInline(...@@ -2847,7 +2861,10 @@ fn transSwitchProngStmtInline(
2847 try block.statements.append(result);2861 try block.statements.append(result);
2848 return;2862 return;
2849 },2863 },
2850 .BreakStmtClass => return,2864 .BreakStmtClass => {
2865 try block.statements.append(Tag.@"break".init());
2866 return;
2867 },
2851 .CaseStmtClass => {2868 .CaseStmtClass => {
2852 var sub = @ptrCast(*const clang.CaseStmt, it[0]).getSubStmt();2869 var sub = @ptrCast(*const clang.CaseStmt, it[0]).getSubStmt();
2853 while (true) switch (sub.getStmtClass()) {2870 while (true) switch (sub.getStmtClass()) {
test/run_translated_c.zig+43
...@@ -1410,4 +1410,47 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {...@@ -1410,4 +1410,47 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
1410 \\}1410 \\}
1411 , "");1411 , "");
14121412
1413 cases.add("break from switch statement. Issue #8387",
1414 \\#include <stdlib.h>
1415 \\int switcher(int x) {
1416 \\ switch (x) {
1417 \\ case 0: // no braces
1418 \\ x += 1;
1419 \\ break;
1420 \\ case 1: // conditional break
1421 \\ if (x == 1) {
1422 \\ x += 1;
1423 \\ break;
1424 \\ }
1425 \\ x += 100;
1426 \\ case 2: { // braces with fallthrough
1427 \\ x += 1;
1428 \\ }
1429 \\ case 3: // fallthrough to return statement
1430 \\ x += 1;
1431 \\ case 42: { // random out of order case
1432 \\ x += 1;
1433 \\ return x;
1434 \\ }
1435 \\ case 4: { // break within braces
1436 \\ x += 1;
1437 \\ break;
1438 \\ }
1439 \\ case 5:
1440 \\ x += 1; // fallthrough to default
1441 \\ default:
1442 \\ x += 1;
1443 \\ }
1444 \\ return x;
1445 \\}
1446 \\int main(void) {
1447 \\ int expected[] = {1, 2, 5, 5, 5, 7, 7};
1448 \\ for (int i = 0; i < sizeof(expected) / sizeof(int); i++) {
1449 \\ int res = switcher(i);
1450 \\ if (res != expected[i]) abort();
1451 \\ }
1452 \\ if (switcher(42) != 43) abort();
1453 \\ return 0;
1454 \\}
1455 , "");
1413}1456}
test/translate_c.zig+41-32
...@@ -2072,40 +2072,49 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -2072,40 +2072,49 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
2072 \\pub export fn switch_fn(arg_i: c_int) void {2072 \\pub export fn switch_fn(arg_i: c_int) void {
2073 \\ var i = arg_i;2073 \\ var i = arg_i;
2074 \\ var res: c_int = 0;2074 \\ var res: c_int = 0;
2075 \\ switch (i) {2075 \\ while (true) {
2076 \\ @as(c_int, 0) => {2076 \\ switch (i) {
2077 \\ res = 1;2077 \\ @as(c_int, 0) => {
2078 \\ res = 2;2078 \\ res = 1;
2079 \\ res = @as(c_int, 3) * i;2079 \\ res = 2;
2080 \\ },2080 \\ res = @as(c_int, 3) * i;
2081 \\ @as(c_int, 1)...@as(c_int, 3) => {
2082 \\ res = 2;
2083 \\ res = @as(c_int, 3) * i;
2084 \\ },
2085 \\ else => {
2086 \\ res = @as(c_int, 3) * i;
2087 \\ },
2088 \\ @as(c_int, 7) => {
2089 \\ {
2090 \\ res = 7;
2091 \\ break;2081 \\ break;
2092 \\ }2082 \\ },
2093 \\ },2083 \\ @as(c_int, 1)...@as(c_int, 3) => {
2094 \\ @as(c_int, 4), @as(c_int, 5) => {2084 \\ res = 2;
2095 \\ res = 69;2085 \\ res = @as(c_int, 3) * i;
2096 \\ {2086 \\ break;
2097 \\ res = 5;2087 \\ },
2088 \\ else => {
2089 \\ res = @as(c_int, 3) * i;
2090 \\ break;
2091 \\ },
2092 \\ @as(c_int, 7) => {
2093 \\ {
2094 \\ res = 7;
2095 \\ break;
2096 \\ }
2097 \\ },
2098 \\ @as(c_int, 4), @as(c_int, 5) => {
2099 \\ res = 69;
2100 \\ {
2101 \\ res = 5;
2102 \\ return;
2103 \\ }
2104 \\ },
2105 \\ @as(c_int, 6) => {
2106 \\ while (true) {
2107 \\ switch (res) {
2108 \\ @as(c_int, 9) => break,
2109 \\ else => {},
2110 \\ }
2111 \\ break;
2112 \\ }
2113 \\ res = 1;
2098 \\ return;2114 \\ return;
2099 \\ }2115 \\ },
2100 \\ },2116 \\ }
2101 \\ @as(c_int, 6) => {2117 \\ break;
2102 \\ switch (res) {
2103 \\ @as(c_int, 9) => {},
2104 \\ else => {},
2105 \\ }
2106 \\ res = 1;
2107 \\ return;
2108 \\ },
2109 \\ }2118 \\ }
2110 \\}2119 \\}
2111 });2120 });