authorgravatar for evan@lagerdata.comEvan Haas <evan@lagerdata.com> 2021-01-13 16:57:06-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-15 12:35:54-08:00
log1702b413f74d80fedd8803d60a69461b1e0b5b51
tree58234c8778ae7cf26c9eedbf23078ff52485f4ef
parent35686262f503372bb9f38a71b891fd19b30adecd

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

2 files changed, 51 insertions(+), 2 deletions(-)

src/translate_c.zig+35-2
...@@ -2082,13 +2082,20 @@ fn transCCast(...@@ -2082,13 +2082,20 @@ fn transCCast(
2082 // 3. Bit-cast to correct signed-ness2082 // 3. Bit-cast to correct signed-ness
2083 const src_type_is_signed = cIsSignedInteger(src_type) or cIsEnum(src_type);2083 const src_type_is_signed = cIsSignedInteger(src_type) or cIsEnum(src_type);
2084 const src_int_type = if (cIsInteger(src_type)) src_type else cIntTypeForEnum(src_type);2084 const src_int_type = if (cIsInteger(src_type)) src_type else cIntTypeForEnum(src_type);
2085 const src_int_expr = if (cIsInteger(src_type)) expr else try transEnumToInt(rp.c, expr);2085 var src_int_expr = if (cIsInteger(src_type)) expr else try transEnumToInt(rp.c, expr);
20862086
2087 // @bitCast(dest_type, intermediate_value)2087 // @bitCast(dest_type, intermediate_value)
2088 const cast_node = try rp.c.createBuiltinCall("@bitCast", 2);2088 const cast_node = try rp.c.createBuiltinCall("@bitCast", 2);
2089 cast_node.params()[0] = try transQualType(rp, dst_type, loc);2089 cast_node.params()[0] = try transQualType(rp, dst_type, loc);
2090 _ = try appendToken(rp.c, .Comma, ",");2090 _ = try appendToken(rp.c, .Comma, ",");
20912091
2092 if (isBoolRes(src_int_expr)) {
2093 const bool_to_int_node = try rp.c.createBuiltinCall("@boolToInt", 1);
2094 bool_to_int_node.params()[0] = src_int_expr;
2095 bool_to_int_node.rparen_token = try appendToken(rp.c, .RParen, ")");
2096 src_int_expr = &bool_to_int_node.base;
2097 }
2098
2092 switch (cIntTypeCmp(dst_type, src_int_type)) {2099 switch (cIntTypeCmp(dst_type, src_int_type)) {
2093 .lt => {2100 .lt => {
2094 // @truncate(SameSignSmallerInt, src_int_expr)2101 // @truncate(SameSignSmallerInt, src_int_expr)
...@@ -3113,7 +3120,26 @@ fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, r...@@ -3113,7 +3120,26 @@ fn transCallExpr(rp: RestorePoint, scope: *Scope, stmt: *const clang.CallExpr, r
3113 if (i != 0) {3120 if (i != 0) {
3114 _ = try appendToken(rp.c, .Comma, ",");3121 _ = try appendToken(rp.c, .Comma, ",");
3115 }3122 }
3116 call_params[i] = try transExpr(rp, scope, args[i], .used, .r_value);3123 var call_param = try transExpr(rp, scope, args[i], .used, .r_value);
3124
3125 // In C the result type of a boolean expression is int. If this result is passed as
3126 // an argument to a function whose parameter is also int, there is no cast. Therefore
3127 // in Zig we'll need to cast it from bool to u1 (which will safely coerce to c_int).
3128 if (fn_ty) |ty| {
3129 switch (ty) {
3130 .Proto => |fn_proto| {
3131 const param_qt = fn_proto.getParamType(@intCast(c_uint, i));
3132 if (isBoolRes(call_param) and cIsNativeInt(param_qt)) {
3133 const builtin_node = try rp.c.createBuiltinCall("@boolToInt", 1);
3134 builtin_node.params()[0] = call_param;
3135 builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")");
3136 call_param = &builtin_node.base;
3137 }
3138 },
3139 else => {},
3140 }
3141 }
3142 call_params[i] = call_param;
3117 }3143 }
3118 node.rtoken = try appendToken(rp.c, .RParen, ")");3144 node.rtoken = try appendToken(rp.c, .RParen, ")");
31193145
...@@ -4125,6 +4151,13 @@ fn cIsSignedInteger(qt: clang.QualType) bool {...@@ -4125,6 +4151,13 @@ fn cIsSignedInteger(qt: clang.QualType) bool {
4125 };4151 };
4126}4152}
41274153
4154fn cIsNativeInt(qt: clang.QualType) bool {
4155 const c_type = qualTypeCanon(qt);
4156 if (c_type.getTypeClass() != .Builtin) return false;
4157 const builtin_ty = @ptrCast(*const clang.BuiltinType, c_type);
4158 return builtin_ty.getKind() == .Int;
4159}
4160
4128fn cIsFloating(qt: clang.QualType) bool {4161fn cIsFloating(qt: clang.QualType) bool {
4129 const c_type = qualTypeCanon(qt);4162 const c_type = qualTypeCanon(qt);
4130 if (c_type.getTypeClass() != .Builtin) return false;4163 if (c_type.getTypeClass() != .Builtin) return false;
test/run_translated_c.zig+16
...@@ -703,4 +703,20 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {...@@ -703,4 +703,20 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
703 \\ return 0;703 \\ return 0;
704 \\}704 \\}
705 , "");705 , "");
706
707 cases.add("Cast boolean expression result to int",
708 \\#include <stdlib.h>
709 \\char foo(char c) { return c; }
710 \\int bar(int i) { return i; }
711 \\long baz(long l) { return l; }
712 \\int main() {
713 \\ if (foo(1 == 2)) abort();
714 \\ if (!foo(1 == 1)) abort();
715 \\ if (bar(1 == 2)) abort();
716 \\ if (!bar(1 == 1)) abort();
717 \\ if (baz(1 == 2)) abort();
718 \\ if (!baz(1 == 1)) abort();
719 \\ return 0;
720 \\}
721 , "");
706}722}