mirror of
https://github.com/ziglang/zig.git
synced 2026-02-15 22:09:49 +00:00
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
This commit is contained in:
parent
36a33c99e3
commit
e761e0ac18
@ -2357,7 +2357,6 @@ fn transInitListExprVector(
|
||||
expr: *const clang.InitListExpr,
|
||||
ty: *const clang.Type,
|
||||
) TransError!Node {
|
||||
|
||||
const qt = getExprQualType(c, @ptrCast(*const clang.Expr, expr));
|
||||
const vector_type = try transQualType(c, scope, qt, loc);
|
||||
const init_count = expr.getNumInits();
|
||||
@ -2700,9 +2699,19 @@ fn transSwitch(
|
||||
scope: *Scope,
|
||||
stmt: *const clang.SwitchStmt,
|
||||
) TransError!Node {
|
||||
var loop_scope = Scope{
|
||||
.parent = scope,
|
||||
.id = .loop,
|
||||
};
|
||||
|
||||
var block_scope = try Scope.Block.init(c, &loop_scope, false);
|
||||
defer block_scope.deinit();
|
||||
|
||||
const base_scope = &block_scope.base;
|
||||
|
||||
var cond_scope = Scope.Condition{
|
||||
.base = .{
|
||||
.parent = scope,
|
||||
.parent = base_scope,
|
||||
.id = .condition,
|
||||
},
|
||||
};
|
||||
@ -2725,8 +2734,8 @@ fn transSwitch(
|
||||
.CaseStmtClass => {
|
||||
var items = std.ArrayList(Node).init(c.gpa);
|
||||
defer items.deinit();
|
||||
const sub = try transCaseStmt(c, scope, it[0], &items);
|
||||
const res = try transSwitchProngStmt(c, scope, sub, it, end_it);
|
||||
const sub = try transCaseStmt(c, base_scope, it[0], &items);
|
||||
const res = try transSwitchProngStmt(c, base_scope, sub, it, end_it);
|
||||
|
||||
if (items.items.len == 0) {
|
||||
has_default = true;
|
||||
@ -2751,7 +2760,7 @@ fn transSwitch(
|
||||
else => break,
|
||||
};
|
||||
|
||||
const res = try transSwitchProngStmt(c, scope, sub, it, end_it);
|
||||
const res = try transSwitchProngStmt(c, base_scope, sub, it, end_it);
|
||||
|
||||
const switch_else = try Tag.switch_else.create(c.arena, res);
|
||||
try cases.append(switch_else);
|
||||
@ -2765,10 +2774,15 @@ fn transSwitch(
|
||||
try cases.append(else_prong);
|
||||
}
|
||||
|
||||
return Tag.@"switch".create(c.arena, .{
|
||||
const switch_node = try Tag.@"switch".create(c.arena, .{
|
||||
.cond = switch_expr,
|
||||
.cases = try c.arena.dupe(Node, cases.items),
|
||||
});
|
||||
try block_scope.statements.append(switch_node);
|
||||
try block_scope.statements.append(Tag.@"break".init());
|
||||
const while_body = try block_scope.complete(c);
|
||||
|
||||
return Tag.while_true.create(c.arena, while_body);
|
||||
}
|
||||
|
||||
/// Collects all items for this case, returns the first statement after the labels.
|
||||
@ -2818,7 +2832,7 @@ fn transSwitchProngStmt(
|
||||
parent_end_it: clang.CompoundStmt.ConstBodyIterator,
|
||||
) TransError!Node {
|
||||
switch (stmt.getStmtClass()) {
|
||||
.BreakStmtClass => return Tag.empty_block.init(),
|
||||
.BreakStmtClass => return Tag.@"break".init(),
|
||||
.ReturnStmtClass => return transStmt(c, scope, stmt, .unused),
|
||||
.CaseStmtClass, .DefaultStmtClass => unreachable,
|
||||
else => {
|
||||
@ -2847,7 +2861,10 @@ fn transSwitchProngStmtInline(
|
||||
try block.statements.append(result);
|
||||
return;
|
||||
},
|
||||
.BreakStmtClass => return,
|
||||
.BreakStmtClass => {
|
||||
try block.statements.append(Tag.@"break".init());
|
||||
return;
|
||||
},
|
||||
.CaseStmtClass => {
|
||||
var sub = @ptrCast(*const clang.CaseStmt, it[0]).getSubStmt();
|
||||
while (true) switch (sub.getStmtClass()) {
|
||||
|
||||
@ -1410,4 +1410,47 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
|
||||
\\}
|
||||
, "");
|
||||
|
||||
cases.add("break from switch statement. Issue #8387",
|
||||
\\#include <stdlib.h>
|
||||
\\int switcher(int x) {
|
||||
\\ switch (x) {
|
||||
\\ case 0: // no braces
|
||||
\\ x += 1;
|
||||
\\ break;
|
||||
\\ case 1: // conditional break
|
||||
\\ if (x == 1) {
|
||||
\\ x += 1;
|
||||
\\ break;
|
||||
\\ }
|
||||
\\ x += 100;
|
||||
\\ case 2: { // braces with fallthrough
|
||||
\\ x += 1;
|
||||
\\ }
|
||||
\\ case 3: // fallthrough to return statement
|
||||
\\ x += 1;
|
||||
\\ case 42: { // random out of order case
|
||||
\\ x += 1;
|
||||
\\ return x;
|
||||
\\ }
|
||||
\\ case 4: { // break within braces
|
||||
\\ x += 1;
|
||||
\\ break;
|
||||
\\ }
|
||||
\\ case 5:
|
||||
\\ x += 1; // fallthrough to default
|
||||
\\ default:
|
||||
\\ x += 1;
|
||||
\\ }
|
||||
\\ return x;
|
||||
\\}
|
||||
\\int main(void) {
|
||||
\\ int expected[] = {1, 2, 5, 5, 5, 7, 7};
|
||||
\\ for (int i = 0; i < sizeof(expected) / sizeof(int); i++) {
|
||||
\\ int res = switcher(i);
|
||||
\\ if (res != expected[i]) abort();
|
||||
\\ }
|
||||
\\ if (switcher(42) != 43) abort();
|
||||
\\ return 0;
|
||||
\\}
|
||||
, "");
|
||||
}
|
||||
|
||||
@ -2072,40 +2072,49 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
|
||||
\\pub export fn switch_fn(arg_i: c_int) void {
|
||||
\\ var i = arg_i;
|
||||
\\ var res: c_int = 0;
|
||||
\\ switch (i) {
|
||||
\\ @as(c_int, 0) => {
|
||||
\\ res = 1;
|
||||
\\ res = 2;
|
||||
\\ res = @as(c_int, 3) * i;
|
||||
\\ },
|
||||
\\ @as(c_int, 1)...@as(c_int, 3) => {
|
||||
\\ res = 2;
|
||||
\\ res = @as(c_int, 3) * i;
|
||||
\\ },
|
||||
\\ else => {
|
||||
\\ res = @as(c_int, 3) * i;
|
||||
\\ },
|
||||
\\ @as(c_int, 7) => {
|
||||
\\ {
|
||||
\\ res = 7;
|
||||
\\ while (true) {
|
||||
\\ switch (i) {
|
||||
\\ @as(c_int, 0) => {
|
||||
\\ res = 1;
|
||||
\\ res = 2;
|
||||
\\ res = @as(c_int, 3) * i;
|
||||
\\ break;
|
||||
\\ }
|
||||
\\ },
|
||||
\\ @as(c_int, 4), @as(c_int, 5) => {
|
||||
\\ res = 69;
|
||||
\\ {
|
||||
\\ res = 5;
|
||||
\\ },
|
||||
\\ @as(c_int, 1)...@as(c_int, 3) => {
|
||||
\\ res = 2;
|
||||
\\ res = @as(c_int, 3) * i;
|
||||
\\ break;
|
||||
\\ },
|
||||
\\ else => {
|
||||
\\ res = @as(c_int, 3) * i;
|
||||
\\ break;
|
||||
\\ },
|
||||
\\ @as(c_int, 7) => {
|
||||
\\ {
|
||||
\\ res = 7;
|
||||
\\ break;
|
||||
\\ }
|
||||
\\ },
|
||||
\\ @as(c_int, 4), @as(c_int, 5) => {
|
||||
\\ res = 69;
|
||||
\\ {
|
||||
\\ res = 5;
|
||||
\\ return;
|
||||
\\ }
|
||||
\\ },
|
||||
\\ @as(c_int, 6) => {
|
||||
\\ while (true) {
|
||||
\\ switch (res) {
|
||||
\\ @as(c_int, 9) => break,
|
||||
\\ else => {},
|
||||
\\ }
|
||||
\\ break;
|
||||
\\ }
|
||||
\\ res = 1;
|
||||
\\ return;
|
||||
\\ }
|
||||
\\ },
|
||||
\\ @as(c_int, 6) => {
|
||||
\\ switch (res) {
|
||||
\\ @as(c_int, 9) => {},
|
||||
\\ else => {},
|
||||
\\ }
|
||||
\\ res = 1;
|
||||
\\ return;
|
||||
\\ },
|
||||
\\ },
|
||||
\\ }
|
||||
\\ break;
|
||||
\\ }
|
||||
\\}
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user