diff --git a/src/translate_c.zig b/src/translate_c.zig index 47c601677e..541b854da7 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -1219,8 +1219,10 @@ fn transCompoundStmtInline( const end_it = stmt.body_end(); while (it != end_it) : (it += 1) { const result = try transStmt(c, &block.base, it[0], .unused); - if (result.tag() == .declaration) continue; - try block.statements.append(result); + switch (result.tag()) { + .declaration, .empty_block => {}, + else => try block.statements.append(result), + } } } @@ -1395,6 +1397,10 @@ fn transImplicitCastExpr( .BuiltinFnToFnPtr => { return transExpr(c, scope, sub_expr, result_used); }, + .ToVoid => { + // Should only appear in the rhs and lhs of a ConditionalOperator + return transExpr(c, scope, sub_expr, .unused); + }, else => |kind| return fail( c, error.UnsupportedTranslation, @@ -2032,10 +2038,8 @@ fn transZeroInitExpr( typedef_decl.getUnderlyingType().getTypePtr(), ); }, - else => {}, + else => return Tag.std_mem_zeroes.create(c.arena, try transType(c, scope, ty, source_loc)), } - - return fail(c, error.UnsupportedType, source_loc, "type does not have an implicit init value", .{}); } fn transImplicitValueInitExpr( @@ -2118,7 +2122,7 @@ fn transDoWhileLoop( defer cond_scope.deinit(); const cond = try transBoolExpr(c, &cond_scope.base, @ptrCast(*const clang.Expr, stmt.getCond()), .used); const if_not_break = switch (cond.tag()) { - .false_literal => Tag.@"break".init(), + .false_literal => return transStmt(c, scope, stmt.getBody(), .unused), .true_literal => { const body_node = try transStmt(c, scope, stmt.getBody(), .unused); return Tag.while_true.create(c.arena, body_node); @@ -2396,8 +2400,10 @@ fn transSwitchProngStmtInline( }, else => { const result = try transStmt(c, &block.base, it[0], .unused); - if (result.tag() == .declaration) continue; - try block.statements.append(result); + switch (result.tag()) { + .declaration, .empty_block => {}, + else => try block.statements.append(result), + } }, } } @@ -2479,8 +2485,10 @@ fn transStmtExpr(c: *Context, scope: *Scope, stmt: *const clang.StmtExpr, used: const end_it = comp.body_end(); while (it != end_it - 1) : (it += 1) { const result = try transStmt(c, &block_scope.base, it[0], .unused); - if (result.tag() == .declaration) continue; - try block_scope.statements.append(result); + switch (result.tag()) { + .declaration, .empty_block => {}, + else => try block_scope.statements.append(result), + } } const break_node = try Tag.break_val.create(c.arena, .{ .label = block_scope.label, @@ -3126,12 +3134,12 @@ fn transConditionalOperator(c: *Context, scope: *Scope, stmt: *const clang.Condi const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used); - var then_body = try transExpr(c, scope, true_expr, .used); + var then_body = try transExpr(c, scope, true_expr, used); if (!res_is_bool and isBoolRes(then_body)) { then_body = try Tag.bool_to_int.create(c.arena, then_body); } - var else_body = try transExpr(c, scope, false_expr, .used); + var else_body = try transExpr(c, scope, false_expr, used); if (!res_is_bool and isBoolRes(else_body)) { else_body = try Tag.bool_to_int.create(c.arena, else_body); } @@ -3141,7 +3149,8 @@ fn transConditionalOperator(c: *Context, scope: *Scope, stmt: *const clang.Condi .then = then_body, .@"else" = else_body, }); - return maybeSuppressResult(c, scope, used, if_node); + // Clang inserts ImplicitCast(ToVoid)'s to both rhs and lhs so we don't need to supress the result here. + return if_node; } fn maybeSuppressResult( @@ -4794,7 +4803,8 @@ fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node { while (true) { switch (m.next().?) { .Asterisk => { - if (m.peek().? == .RParen) { + const next = m.peek().?; + if (next == .RParen or next == .Nl or next == .Eof) { // type *) // last token of `node` diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index 2306d1c36f..be331f39dc 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -1891,30 +1891,35 @@ fn addSemicolonIfNeeded(c: *Context, node: Node) !void { .var_decl, .var_simple, .arg_redecl, .alias, .enum_redecl, .block, .empty_block, .block_single, .@"switch" => {}, .while_true => { const payload = node.castTag(.while_true).?.data; - return addSemicolonIfNotBlock(c, payload); + return addSemicolonIfNotBlock(c, payload, .yes_if); }, .@"while" => { const payload = node.castTag(.@"while").?.data; - return addSemicolonIfNotBlock(c, payload.body); + return addSemicolonIfNotBlock(c, payload.body, .yes_if); }, .@"if" => { const payload = node.castTag(.@"if").?.data; if (payload.@"else") |some| - return addSemicolonIfNotBlock(c, some); - return addSemicolonIfNotBlock(c, payload.then); + return addSemicolonIfNotBlock(c, some, .no_if); + return addSemicolonIfNotBlock(c, payload.then, .no_if); }, else => _ = try c.addToken(.semicolon, ";"), } } -fn addSemicolonIfNotBlock(c: *Context, node: Node) !void { +fn addSemicolonIfNotBlock(c: *Context, node: Node, if_needs_semicolon: enum{ yes_if, no_if}) !void { switch (node.tag()) { .block, .empty_block, .block_single => {}, .@"if" => { + if (if_needs_semicolon == .yes_if) { + _ = try c.addToken(.semicolon, ";"); + return; + } + const payload = node.castTag(.@"if").?.data; if (payload.@"else") |some| - return addSemicolonIfNotBlock(c, some); - return addSemicolonIfNotBlock(c, payload.then); + return addSemicolonIfNotBlock(c, some, .no_if); + return addSemicolonIfNotBlock(c, payload.then, .no_if); }, else => _ = try c.addToken(.semicolon, ";"), } diff --git a/test/translate_c.zig b/test/translate_c.zig index a2d1bf417e..34e6897c94 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -3,6 +3,62 @@ const std = @import("std"); const CrossTarget = std.zig.CrossTarget; pub fn addCases(cases: *tests.TranslateCContext) void { + cases.add("if as while stmt has semicolon", + \\void foo() { + \\ while (1) if (1) { + \\ int a = 1; + \\ } else { + \\ int b = 2; + \\ } + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ while (true) if (true) { + \\ var a: c_int = 1; + \\ } else { + \\ var b: c_int = 2; + \\ }; + \\} + }); + + cases.add("conditional operator cast to void", + \\int bar(); + \\void foo() { + \\ int a; + \\ a ? a = 2 : bar(); + \\} + , &[_][]const u8{ + \\pub extern fn bar(...) c_int; + \\pub export fn foo() void { + \\ var a: c_int = undefined; + \\ if (a != 0) a = 2 else _ = bar(); + \\} + }); + + cases.add("struct in struct init to zero", + \\struct Foo { + \\ int a; + \\ struct Bar { + \\ int a; + \\ } b; + \\} a = {}; + \\#define PTR void * + , &[_][]const u8{ + \\pub const struct_Bar = extern struct { + \\ a: c_int, + \\}; + \\pub const struct_Foo = extern struct { + \\ a: c_int, + \\ b: struct_Bar, + \\}; + \\pub export var a: struct_Foo = struct_Foo{ + \\ .a = 0, + \\ .b = @import("std").mem.zeroes(struct_Bar), + \\}; + , + \\pub const PTR = ?*c_void; + }); + cases.add("scoped enum", \\void foo() { \\ enum Foo { @@ -330,9 +386,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub export fn foo() void { \\ while (false) while (false) {}; \\ while (true) while (false) {}; - \\ while (true) while (true) { - \\ break; - \\ }; + \\ while (true) {} \\} }); @@ -1044,13 +1098,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ ;;;;; \\} , &[_][]const u8{ - \\pub export fn foo() void { - \\ {} - \\ {} - \\ {} - \\ {} - \\ {} - \\} + \\pub export fn foo() void {} }); if (std.Target.current.os.tag != .windows) { @@ -3050,9 +3098,7 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} , &[_][]const u8{ \\pub fn foo() callconv(.C) void { - \\ if (true) while (true) { - \\ break; - \\ }; + \\ if (true) {} \\} });