diff options
| author | Evan Haas <evan@lagerdata.com> | 2021-02-08 11:43:57 -0800 |
|---|---|---|
| committer | Veikka Tuominen <git@vexu.eu> | 2021-02-10 20:23:27 +0200 |
| commit | a2ec77041bc4a58d922cd3f8db923b7272b1f8f7 (patch) | |
| tree | 2543c29e0d80afe56639e5bcaad1942eb2f939f8 /src | |
| parent | 1480c428065c01c6feff22ce84021c2e0e30aa9b (diff) | |
| download | zig-a2ec77041bc4a58d922cd3f8db923b7272b1f8f7.tar.gz zig-a2ec77041bc4a58d922cd3f8db923b7272b1f8f7.zip | |
translate-c: call @boolToInt on return value when necessary
In C, if a function has return type `int` and the return expression
is a boolean expression, there is no implicit cast. Therefore the
translated Zig code needs to call @boolToInt() on the result.
Written with feedback from @Vexu
Fixes #6215
Diffstat (limited to 'src')
| -rw-r--r-- | src/translate_c.zig | 44 |
1 files changed, 40 insertions, 4 deletions
diff --git a/src/translate_c.zig b/src/translate_c.zig index e3652ddbfb..82a7695c13 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -78,6 +78,10 @@ const Scope = struct { mangle_count: u32 = 0, lbrace: ast.TokenIndex, + /// When the block corresponds to a function, keep track of the return type + /// so that the return expression can be cast, if necessary + return_type: ?clang.QualType = null, + fn init(c: *Context, parent: *Scope, labeled: bool) !Block { var blk = Block{ .base = .{ @@ -209,6 +213,21 @@ const Scope = struct { } } + fn findBlockReturnType(inner: *Scope, c: *Context) ?clang.QualType { + var scope = inner; + while (true) { + switch (scope.id) { + .Root => return null, + .Block => { + const block = @fieldParentPtr(Block, "base", scope); + if (block.return_type) |qt| return qt; + scope = scope.parent.?; + }, + else => scope = scope.parent.?, + } + } + } + fn getAlias(scope: *Scope, name: []const u8) []const u8 { return switch (scope.id) { .Root => return name, @@ -580,6 +599,8 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { else => break fn_type, } } else unreachable; + const fn_ty = @ptrCast(*const clang.FunctionType, fn_type); + const return_qt = fn_ty.getReturnType(); const proto_node = switch (fn_type.getTypeClass()) { .FunctionProto => blk: { @@ -617,7 +638,9 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { // actual function definition with body const body_stmt = fn_decl.getBody(); var block_scope = try Scope.Block.init(rp.c, &c.global_scope.base, false); + block_scope.return_type = return_qt; defer block_scope.deinit(); + var scope = &block_scope.base; var param_id: c_uint = 0; @@ -667,10 +690,7 @@ fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void { }; // add return statement if the function didn't have one blk: { - const fn_ty = @ptrCast(*const clang.FunctionType, fn_type); - if (fn_ty.getNoReturnAttr()) break :blk; - const return_qt = fn_ty.getReturnType(); if (isCVoid(return_qt)) break :blk; if (block_scope.statements.items.len > 0) { @@ -2018,16 +2038,32 @@ fn transIntegerLiteral( return maybeSuppressResult(rp, scope, result_used, &as_node.base); } +/// In C if a function has return type `int` and the return value is a boolean +/// expression, there is no implicit cast. So the translated Zig will need to +/// call @boolToInt +fn zigShouldCastBooleanReturnToInt(node: ?*ast.Node, qt: ?clang.QualType) bool { + if (node == null or qt == null) return false; + return isBoolRes(node.?) and cIsNativeInt(qt.?); +} + fn transReturnStmt( rp: RestorePoint, scope: *Scope, expr: *const clang.ReturnStmt, ) TransError!*ast.Node { const return_kw = try appendToken(rp.c, .Keyword_return, "return"); - const rhs: ?*ast.Node = if (expr.getRetValue()) |val_expr| + var rhs: ?*ast.Node = if (expr.getRetValue()) |val_expr| try transExprCoercing(rp, scope, val_expr, .used, .r_value) else null; + const return_qt = scope.findBlockReturnType(rp.c); + if (zigShouldCastBooleanReturnToInt(rhs, return_qt)) { + const bool_to_int_node = try rp.c.createBuiltinCall("@boolToInt", 1); + bool_to_int_node.params()[0] = rhs.?; + bool_to_int_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + + rhs = &bool_to_int_node.base; + } const return_expr = try ast.Node.ControlFlowExpression.create(rp.c.arena, .{ .ltoken = return_kw, .tag = .Return, |
