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(
23572357 expr: *const clang.InitListExpr,
23582358 ty: *const clang.Type,
23592359) TransError!Node {
2360
23612360 const qt = getExprQualType(c, @ptrCast(*const clang.Expr, expr));
23622361 const vector_type = try transQualType(c, scope, qt, loc);
23632362 const init_count = expr.getNumInits();
......@@ -2700,9 +2699,19 @@ fn transSwitch(
27002699 scope: *Scope,
27012700 stmt: *const clang.SwitchStmt,
27022701) 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
27032712 var cond_scope = Scope.Condition{
27042713 .base = .{
2705 .parent = scope,
2714 .parent = base_scope,
27062715 .id = .condition,
27072716 },
27082717 };
......@@ -2725,8 +2734,8 @@ fn transSwitch(
27252734 .CaseStmtClass => {
27262735 var items = std.ArrayList(Node).init(c.gpa);
27272736 defer items.deinit();
2728 const sub = try transCaseStmt(c, scope, it[0], &items);
2729 const res = try transSwitchProngStmt(c, scope, sub, it, end_it);
2737 const sub = try transCaseStmt(c, base_scope, it[0], &items);
2738 const res = try transSwitchProngStmt(c, base_scope, sub, it, end_it);
27302739
27312740 if (items.items.len == 0) {
27322741 has_default = true;
......@@ -2751,7 +2760,7 @@ fn transSwitch(
27512760 else => break,
27522761 };
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
27562765 const switch_else = try Tag.switch_else.create(c.arena, res);
27572766 try cases.append(switch_else);
......@@ -2765,10 +2774,15 @@ fn transSwitch(
27652774 try cases.append(else_prong);
27662775 }
27672776
2768 return Tag.@"switch".create(c.arena, .{
2777 const switch_node = try Tag.@"switch".create(c.arena, .{
27692778 .cond = switch_expr,
27702779 .cases = try c.arena.dupe(Node, cases.items),
27712780 });
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);
27722786}
27732787
27742788/// Collects all items for this case, returns the first statement after the labels.
......@@ -2818,7 +2832,7 @@ fn transSwitchProngStmt(
28182832 parent_end_it: clang.CompoundStmt.ConstBodyIterator,
28192833) TransError!Node {
28202834 switch (stmt.getStmtClass()) {
2821 .BreakStmtClass => return Tag.empty_block.init(),
2835 .BreakStmtClass => return Tag.@"break".init(),
28222836 .ReturnStmtClass => return transStmt(c, scope, stmt, .unused),
28232837 .CaseStmtClass, .DefaultStmtClass => unreachable,
28242838 else => {
......@@ -2847,7 +2861,10 @@ fn transSwitchProngStmtInline(
28472861 try block.statements.append(result);
28482862 return;
28492863 },
2850 .BreakStmtClass => return,
2864 .BreakStmtClass => {
2865 try block.statements.append(Tag.@"break".init());
2866 return;
2867 },
28512868 .CaseStmtClass => {
28522869 var sub = @ptrCast(*const clang.CaseStmt, it[0]).getSubStmt();
28532870 while (true) switch (sub.getStmtClass()) {
test/run_translated_c.zig+43
......@@ -1410,4 +1410,47 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
14101410 \\}
14111411 , "");
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 , "");
14131456}
test/translate_c.zig+41-32
......@@ -2072,40 +2072,49 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
20722072 \\pub export fn switch_fn(arg_i: c_int) void {
20732073 \\ var i = arg_i;
20742074 \\ var res: c_int = 0;
2075 \\ switch (i) {
2076 \\ @as(c_int, 0) => {
2077 \\ res = 1;
2078 \\ res = 2;
2079 \\ res = @as(c_int, 3) * i;
2080 \\ },
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;
2075 \\ while (true) {
2076 \\ switch (i) {
2077 \\ @as(c_int, 0) => {
2078 \\ res = 1;
2079 \\ res = 2;
2080 \\ res = @as(c_int, 3) * i;
20912081 \\ break;
2092 \\ }
2093 \\ },
2094 \\ @as(c_int, 4), @as(c_int, 5) => {
2095 \\ res = 69;
2096 \\ {
2097 \\ res = 5;
2082 \\ },
2083 \\ @as(c_int, 1)...@as(c_int, 3) => {
2084 \\ res = 2;
2085 \\ res = @as(c_int, 3) * i;
2086 \\ break;
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;
20982114 \\ return;
2099 \\ }
2100 \\ },
2101 \\ @as(c_int, 6) => {
2102 \\ switch (res) {
2103 \\ @as(c_int, 9) => {},
2104 \\ else => {},
2105 \\ }
2106 \\ res = 1;
2107 \\ return;
2108 \\ },
2115 \\ },
2116 \\ }
2117 \\ break;
21092118 \\ }
21102119 \\}
21112120 });