Commit a2ec77041b
Changed files (3)
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,
test/run_translated_c.zig
@@ -874,4 +874,39 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
\\ return 0;
\\}
, "");
+
+ cases.add("Return boolean expression as int; issue #6215",
+ \\#include <stdlib.h>
+ \\#include <stdbool.h>
+ \\bool actual_bool(void) { return 4 - 1 < 4;}
+ \\char char_bool_ret(void) { return 0 || 1; }
+ \\short short_bool_ret(void) { return 0 < 1; }
+ \\int int_bool_ret(void) { return 1 && 1; }
+ \\long long_bool_ret(void) { return !(0 > 1); }
+ \\static int GLOBAL = 1;
+ \\int nested_scopes(int a, int b) {
+ \\ if (a == 1) {
+ \\ int target = 1;
+ \\ return b == target;
+ \\ } else {
+ \\ int target = 2;
+ \\ if (b == target) {
+ \\ return GLOBAL == 1;
+ \\ }
+ \\ return target == 2;
+ \\ }
+ \\}
+ \\int main(void) {
+ \\ if (!actual_bool()) abort();
+ \\ if (!char_bool_ret()) abort();
+ \\ if (!short_bool_ret()) abort();
+ \\ if (!int_bool_ret()) abort();
+ \\ if (!long_bool_ret()) abort();
+ \\ if (!nested_scopes(1, 1)) abort();
+ \\ if (nested_scopes(1, 2)) abort();
+ \\ if (!nested_scopes(0, 2)) abort();
+ \\ if (!nested_scopes(0, 3)) abort();
+ \\ return 1 != 1;
+ \\}
+ , "");
}
test/translate_c.zig
@@ -1305,10 +1305,10 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ var a: c_int = undefined;
\\ var b: f32 = undefined;
\\ var c: ?*c_void = undefined;
- \\ return !(a == @as(c_int, 0));
- \\ return !(a != 0);
- \\ return !(b != 0);
- \\ return !(c != null);
+ \\ return @boolToInt(!(a == @as(c_int, 0)));
+ \\ return @boolToInt(!(a != 0));
+ \\ return @boolToInt(!(b != 0));
+ \\ return @boolToInt(!(c != null));
\\}
});