authorgravatar for evan@lagerdata.comEvan Haas <evan@lagerdata.com> 2021-03-18 23:13:59-07:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2021-03-22 10:48:08+02:00
logdce612ac2be5aa8a1aa0ee8dd670d7e875624216
tree5d551dbee5d5cb9f5270f40ea62057961440fc3f
parent187af14599a083f728f79c4a57ceed30fd01f85d

translate-c: Ensure assignments are within a block when necessary

Ensures that if an assignment statement is the sole statement within a C if statement, for loop, do loop, or do while loop, then when translated it resides within a block, even though it does not in the original C. Fixes the following invalid translation: `if (1) if (1) 2;` -> `if (true) if (true) _ = @as(c_int, 2);` To this: ```zig if (true) if (true) { _ = @as(c_int, 2); }; ``` Fixes #8159

3 files changed, 62 insertions(+), 9 deletions(-)

src/translate_c.zig+36-5
......@@ -1063,6 +1063,7 @@ fn transStmt(
10631063 const gen_sel = @ptrCast(*const clang.GenericSelectionExpr, stmt);
10641064 return transExpr(c, scope, gen_sel.getResultExpr(), result_used);
10651065 },
1066 // When adding new cases here, see comment for maybeBlockify()
10661067 else => {
10671068 return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)});
10681069 },
......@@ -2242,6 +2243,35 @@ fn transImplicitValueInitExpr(
22422243 return transZeroInitExpr(c, scope, source_loc, ty);
22432244}
22442245
2246/// If a statement can possibly translate to a Zig assignment (either directly because it's
2247/// an assignment in C or indirectly via result assignment to `_`) AND it's the sole statement
2248/// in the body of an if statement or loop, then we need to put the statement into its own block.
2249/// The `else` case here corresponds to statements that could result in an assignment. If a statement
2250/// class never needs a block, add its enum to the top prong.
2251fn maybeBlockify(c: *Context, scope: *Scope, stmt: *const clang.Stmt) TransError!Node {
2252 switch (stmt.getStmtClass()) {
2253 .BreakStmtClass,
2254 .CompoundStmtClass,
2255 .ContinueStmtClass,
2256 .DeclRefExprClass,
2257 .DeclStmtClass,
2258 .DoStmtClass,
2259 .ForStmtClass,
2260 .IfStmtClass,
2261 .ReturnStmtClass,
2262 .NullStmtClass,
2263 .WhileStmtClass,
2264 => return transStmt(c, scope, stmt, .unused),
2265 else => {
2266 var block_scope = try Scope.Block.init(c, scope, false);
2267 defer block_scope.deinit();
2268 const result = try transStmt(c, &block_scope.base, stmt, .unused);
2269 try block_scope.statements.append(result);
2270 return block_scope.complete(c);
2271 },
2272 }
2273}
2274
22452275fn transIfStmt(
22462276 c: *Context,
22472277 scope: *Scope,
......@@ -2259,9 +2289,10 @@ fn transIfStmt(
22592289 const cond_expr = @ptrCast(*const clang.Expr, stmt.getCond());
22602290 const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used);
22612291
2262 const then_body = try transStmt(c, scope, stmt.getThen(), .unused);
2292 const then_body = try maybeBlockify(c, scope, stmt.getThen());
2293
22632294 const else_body = if (stmt.getElse()) |expr|
2264 try transStmt(c, scope, expr, .unused)
2295 try maybeBlockify(c, scope, expr)
22652296 else
22662297 null;
22672298 return Tag.@"if".create(c.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body });
......@@ -2286,7 +2317,7 @@ fn transWhileLoop(
22862317 .parent = scope,
22872318 .id = .loop,
22882319 };
2289 const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused);
2320 const body = try maybeBlockify(c, &loop_scope, stmt.getBody());
22902321 return Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = null });
22912322}
22922323
......@@ -2312,7 +2343,7 @@ fn transDoWhileLoop(
23122343 const if_not_break = switch (cond.tag()) {
23132344 .false_literal => return transStmt(c, scope, stmt.getBody(), .unused),
23142345 .true_literal => {
2315 const body_node = try transStmt(c, scope, stmt.getBody(), .unused);
2346 const body_node = try maybeBlockify(c, scope, stmt.getBody());
23162347 return Tag.while_true.create(c.arena, body_node);
23172348 },
23182349 else => try Tag.if_not_break.create(c.arena, cond),
......@@ -2388,7 +2419,7 @@ fn transForLoop(
23882419 else
23892420 null;
23902421
2391 const body = try transStmt(c, &loop_scope, stmt.getBody(), .unused);
2422 const body = try maybeBlockify(c, &loop_scope, stmt.getBody());
23922423 const while_node = try Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr });
23932424 if (block_scope) |*bs| {
23942425 try bs.statements.append(while_node);
test/run_translated_c.zig+14
......@@ -1244,4 +1244,18 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
12441244 \\ return 0;
12451245 \\}
12461246 , "");
1247
1248 cases.add("convert single-statement bodies into blocks for if/else/for/while. issue #8159",
1249 \\#include <stdlib.h>
1250 \\int foo() { return 1; }
1251 \\int main(void) {
1252 \\ int i = 0;
1253 \\ if (i == 0) if (i == 0) if (i != 0) i = 1;
1254 \\ if (i != 0) i = 1; else if (i == 0) if (i == 0) i += 1;
1255 \\ for (; i < 10;) for (; i < 10;) i++;
1256 \\ while (i == 100) while (i == 100) foo();
1257 \\ if (0) do do "string"; while(1); while(1);
1258 \\ return 0;
1259 \\}
1260 , "");
12471261}
test/translate_c.zig+12-4
......@@ -1934,7 +1934,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
19341934 , &[_][]const u8{
19351935 \\pub export fn foo() c_int {
19361936 \\ var a: c_int = 5;
1937 \\ while (true) a = 2;
1937 \\ while (true) {
1938 \\ a = 2;
1939 \\ }
19381940 \\ while (true) {
19391941 \\ var a_1: c_int = 4;
19401942 \\ a_1 = 9;
......@@ -1947,7 +1949,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
19471949 \\ var a_1: c_int = 2;
19481950 \\ a_1 = 12;
19491951 \\ }
1950 \\ while (true) a = 7;
1952 \\ while (true) {
1953 \\ a = 7;
1954 \\ }
19511955 \\ return 0;
19521956 \\}
19531957 });
......@@ -2008,7 +2012,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
20082012 \\}
20092013 , &[_][]const u8{
20102014 \\pub export fn bar() c_int {
2011 \\ if ((if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6)) != 0) _ = @as(c_int, 2);
2015 \\ if ((if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6)) != 0) {
2016 \\ _ = @as(c_int, 2);
2017 \\ }
20122018 \\ return if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6);
20132019 \\}
20142020 });
......@@ -2389,7 +2395,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
23892395 \\pub const yes = [*c]u8;
23902396 \\pub export fn foo() void {
23912397 \\ var a: yes = undefined;
2392 \\ if (a != null) _ = @as(c_int, 2);
2398 \\ if (a != null) {
2399 \\ _ = @as(c_int, 2);
2400 \\ }
23932401 \\}
23942402 });
23952403