Commit 1702b413f7

Evan Haas <evan@lagerdata.com>
2021-01-14 01:57:06
translate-c: ensure bools are cast to int when necessary
Fixes two scenarios where @boolToInt() calls were missing: 1. Boolean expression cast to different-size int (char, long, etc) 2. Boolean expression used as parameter for function with int argument
1 parent 3568626
Changed files (2)
src/translate_c.zig
@@ -2082,13 +2082,20 @@ fn transCCast(
         // 3. Bit-cast to correct signed-ness
         const src_type_is_signed = cIsSignedInteger(src_type) or cIsEnum(src_type);
         const src_int_type = if (cIsInteger(src_type)) src_type else cIntTypeForEnum(src_type);
-        const src_int_expr = if (cIsInteger(src_type)) expr else try transEnumToInt(rp.c, expr);
+        var src_int_expr = if (cIsInteger(src_type)) expr else try transEnumToInt(rp.c, expr);
 
         // @bitCast(dest_type, intermediate_value)
         const cast_node = try rp.c.createBuiltinCall("@bitCast", 2);
         cast_node.params()[0] = try transQualType(rp, dst_type, loc);
         _ = try appendToken(rp.c, .Comma, ",");
 
+        if (isBoolRes(src_int_expr)) {
+            const bool_to_int_node = try rp.c.createBuiltinCall("@boolToInt", 1);
+            bool_to_int_node.params()[0] = src_int_expr;
+            bool_to_int_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+            src_int_expr = &bool_to_int_node.base;
+        }
+
         switch (cIntTypeCmp(dst_type, src_int_type)) {
             .lt => {
                 // @truncate(SameSignSmallerInt, src_int_expr)
@@ -3113,7 +3120,26 @@ fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, r
         if (i != 0) {
             _ = try appendToken(rp.c, .Comma, ",");
         }
-        call_params[i] = try transExpr(rp, scope, args[i], .used, .r_value);
+        var call_param = try transExpr(rp, scope, args[i], .used, .r_value);
+
+        // In C the result type of a boolean expression is int. If this result is passed as
+        // an argument to a function whose parameter is also int, there is no cast. Therefore
+        // in Zig we'll need to cast it from bool to u1 (which will safely coerce to c_int).
+        if (fn_ty) |ty| {
+            switch (ty) {
+                .Proto => |fn_proto| {
+                    const param_qt = fn_proto.getParamType(@intCast(c_uint, i));
+                    if (isBoolRes(call_param) and cIsNativeInt(param_qt)) {
+                        const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);
+                        builtin_node.params()[0] = call_param;
+                        builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
+                        call_param = &builtin_node.base;
+                    }
+                },
+                else => {},
+            }
+        }
+        call_params[i] = call_param;
     }
     node.rtoken = try appendToken(rp.c, .RParen, ")");
 
@@ -4125,6 +4151,13 @@ fn cIsSignedInteger(qt: clang.QualType) bool {
     };
 }
 
+fn cIsNativeInt(qt: clang.QualType) bool {
+    const c_type = qualTypeCanon(qt);
+    if (c_type.getTypeClass() != .Builtin) return false;
+    const builtin_ty = @ptrCast(*const clang.BuiltinType, c_type);
+    return builtin_ty.getKind() == .Int;
+}
+
 fn cIsFloating(qt: clang.QualType) bool {
     const c_type = qualTypeCanon(qt);
     if (c_type.getTypeClass() != .Builtin) return false;
test/run_translated_c.zig
@@ -703,4 +703,20 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
         \\    return 0;
         \\}
     , "");
+
+    cases.add("Cast boolean expression result to int",
+        \\#include <stdlib.h>
+        \\char foo(char c) { return c; }
+        \\int  bar(int i)  { return i; }
+        \\long baz(long l) { return l; }
+        \\int main() {
+        \\    if (foo(1 == 2)) abort();
+        \\    if (!foo(1 == 1)) abort();
+        \\    if (bar(1 == 2)) abort();
+        \\    if (!bar(1 == 1)) abort();
+        \\    if (baz(1 == 2)) abort();
+        \\    if (!baz(1 == 1)) abort();
+        \\    return 0;
+        \\}
+    , "");
 }