mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
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
This commit is contained in:
parent
1480c42806
commit
a2ec77041b
@ -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,
|
||||
|
||||
@ -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;
|
||||
\\}
|
||||
, "");
|
||||
}
|
||||
|
||||
@ -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));
|
||||
\\}
|
||||
});
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user