Skip to content

Commit e761e0a

Browse files
ehaasandrewrk
authored andcommitted
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 ziglang#8387
1 parent 36a33c9 commit e761e0a

File tree

3 files changed

+109
-40
lines changed

3 files changed

+109
-40
lines changed

src/translate_c.zig

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -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
});

0 commit comments

Comments
 (0)