| author | |
| committer | |
| log | e761e0ac18aa1eab22b9547d702067910ed82c4a |
| tree | 2051a37e060386eab8b52152b6f478bc6e54945a |
| parent | 36a33c99e3bc0825ada2ce1b374c32b4d6dee6b0 |
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 #83873 files changed, 109 insertions(+), 40 deletions(-)
src/translate_c.zig+25-8| ... | ... | @@ -2357,7 +2357,6 @@ fn transInitListExprVector( |
| 2357 | 2357 | expr: *const clang.InitListExpr, |
| 2358 | 2358 | ty: *const clang.Type, |
| 2359 | 2359 | ) TransError!Node { |
| 2360 | ||
| 2361 | 2360 | const qt = getExprQualType(c, @ptrCast(*const clang.Expr, expr)); |
| 2362 | 2361 | const vector_type = try transQualType(c, scope, qt, loc); |
| 2363 | 2362 | const init_count = expr.getNumInits(); |
| ... | ... | @@ -2700,9 +2699,19 @@ fn transSwitch( |
| 2700 | 2699 | scope: *Scope, |
| 2701 | 2700 | stmt: *const clang.SwitchStmt, |
| 2702 | 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 | 2712 | var cond_scope = Scope.Condition{ |
| 2704 | 2713 | .base = .{ |
| 2705 | .parent = scope, | |
| 2714 | .parent = base_scope, | |
| 2706 | 2715 | .id = .condition, |
| 2707 | 2716 | }, |
| 2708 | 2717 | }; |
| ... | ... | @@ -2725,8 +2734,8 @@ fn transSwitch( |
| 2725 | 2734 | .CaseStmtClass => { |
| 2726 | 2735 | var items = std.ArrayList(Node).init(c.gpa); |
| 2727 | 2736 | 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); | |
| 2730 | 2739 | |
| 2731 | 2740 | if (items.items.len == 0) { |
| 2732 | 2741 | has_default = true; |
| ... | ... | @@ -2751,7 +2760,7 @@ fn transSwitch( |
| 2751 | 2760 | else => break, |
| 2752 | 2761 | }; |
| 2753 | 2762 | |
| 2754 | const res = try transSwitchProngStmt(c, scope, sub, it, end_it); | |
| 2763 | const res = try transSwitchProngStmt(c, base_scope, sub, it, end_it); | |
| 2755 | 2764 | |
| 2756 | 2765 | const switch_else = try Tag.switch_else.create(c.arena, res); |
| 2757 | 2766 | try cases.append(switch_else); |
| ... | ... | @@ -2765,10 +2774,15 @@ fn transSwitch( |
| 2765 | 2774 | try cases.append(else_prong); |
| 2766 | 2775 | } |
| 2767 | 2776 | |
| 2768 | return Tag.@"switch".create(c.arena, .{ | |
| 2777 | const switch_node = try Tag.@"switch".create(c.arena, .{ | |
| 2769 | 2778 | .cond = switch_expr, |
| 2770 | 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 | } |
| 2773 | 2787 | |
| 2774 | 2788 | /// Collects all items for this case, returns the first statement after the labels. |
| ... | ... | @@ -2818,7 +2832,7 @@ fn transSwitchProngStmt( |
| 2818 | 2832 | parent_end_it: clang.CompoundStmt.ConstBodyIterator, |
| 2819 | 2833 | ) TransError!Node { |
| 2820 | 2834 | switch (stmt.getStmtClass()) { |
| 2821 | .BreakStmtClass => return Tag.empty_block.init(), | |
| 2835 | .BreakStmtClass => return Tag.@"break".init(), | |
| 2822 | 2836 | .ReturnStmtClass => return transStmt(c, scope, stmt, .unused), |
| 2823 | 2837 | .CaseStmtClass, .DefaultStmtClass => unreachable, |
| 2824 | 2838 | else => { |
| ... | ... | @@ -2847,7 +2861,10 @@ fn transSwitchProngStmtInline( |
| 2847 | 2861 | try block.statements.append(result); |
| 2848 | 2862 | return; |
| 2849 | 2863 | }, |
| 2850 | .BreakStmtClass => return, | |
| 2864 | .BreakStmtClass => { | |
| 2865 | try block.statements.append(Tag.@"break".init()); | |
| 2866 | return; | |
| 2867 | }, | |
| 2851 | 2868 | .CaseStmtClass => { |
| 2852 | 2869 | var sub = @ptrCast(*const clang.CaseStmt, it[0]).getSubStmt(); |
| 2853 | 2870 | while (true) switch (sub.getStmtClass()) { |
test/run_translated_c.zig+43| ... | ... | @@ -1410,4 +1410,47 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void { |
| 1410 | 1410 | \\} |
| 1411 | 1411 | , ""); |
| 1412 | 1412 | |
| 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 | 2072 | \\pub export fn switch_fn(arg_i: c_int) void { |
| 2073 | 2073 | \\ var i = arg_i; |
| 2074 | 2074 | \\ 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; | |
| 2091 | 2081 | \\ 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; | |
| 2098 | 2114 | \\ 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; | |
| 2109 | 2118 | \\ } |
| 2110 | 2119 | \\} |
| 2111 | 2120 | }); |