From 122a9bad3973bb9f7f48c91eb45cbf0513cf19c3 Mon Sep 17 00:00:00 2001 From: Vexu Date: Thu, 19 Dec 2019 01:38:42 +0200 Subject: [PATCH] translate-c-2 fix some casts --- src-self-hosted/clang.zig | 2 + src-self-hosted/translate_c.zig | 64 ++++- src/zig_clang.cpp | 10 + src/zig_clang.h | 2 + test/translate_c.zig | 415 ++++++++++++++++++-------------- 5 files changed, 306 insertions(+), 187 deletions(-) diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index bf7adce1e0..10b46bc9a9 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -761,6 +761,8 @@ pub extern fn ZigClangEnumType_getDecl(record_ty: ?*const struct_ZigClangEnumTyp pub extern fn ZigClangRecordDecl_getCanonicalDecl(record_decl: ?*const struct_ZigClangRecordDecl) ?*const struct_ZigClangTagDecl; pub extern fn ZigClangEnumDecl_getCanonicalDecl(self: ?*const struct_ZigClangEnumDecl) ?*const struct_ZigClangTagDecl; pub extern fn ZigClangTypedefNameDecl_getCanonicalDecl(self: ?*const struct_ZigClangTypedefNameDecl) ?*const struct_ZigClangTypedefNameDecl; +pub extern fn ZigClangFunctionDecl_getCanonicalDecl(self: ?*const struct_ZigClangFunctionDecl) ?*const struct_ZigClangFunctionDecl; +pub extern fn ZigClangVarDecl_getCanonicalDecl(self: ?*const struct_ZigClangVarDecl) ?*const struct_ZigClangVarDecl; pub extern fn ZigClangRecordDecl_getDefinition(self: ?*const struct_ZigClangRecordDecl) ?*const struct_ZigClangRecordDecl; pub extern fn ZigClangEnumDecl_getDefinition(self: ?*const struct_ZigClangEnumDecl) ?*const struct_ZigClangEnumDecl; pub extern fn ZigClangRecordDecl_getLocation(self: ?*const struct_ZigClangRecordDecl) struct_ZigClangSourceLocation; diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 2247c4dc91..0c34cd56e1 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -375,7 +375,8 @@ fn declVisitor(c: *Context, decl: *const ZigClangDecl) Error!void { } fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { - if (c.decl_table.contains(@ptrToInt(fn_decl))) return; // Avoid processing this decl twice + if (c.decl_table.contains(@ptrToInt(ZigClangFunctionDecl_getCanonicalDecl(fn_decl)))) + return; // Avoid processing this decl twice const rp = makeRestorePoint(c); const fn_name = try c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, fn_decl))); _ = try c.decl_table.put(@ptrToInt(fn_decl), fn_name); @@ -442,7 +443,8 @@ fn visitFnDecl(c: *Context, fn_decl: *const ZigClangFunctionDecl) Error!void { } fn visitVarDecl(c: *Context, var_decl: *const ZigClangVarDecl) Error!void { - if (c.decl_table.contains(@ptrToInt(var_decl))) return; // Avoid processing this decl twice + if (c.decl_table.contains(@ptrToInt(ZigClangVarDecl_getCanonicalDecl(var_decl)))) + return; // Avoid processing this decl twice const rp = makeRestorePoint(c); const visib_tok = try appendToken(c, .Keyword_pub, "pub"); @@ -1115,10 +1117,17 @@ fn transDeclStmt(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangDeclStmt) node.type_node = try transQualType(rp, qual_type, loc); node.eq_token = try appendToken(c, .Equal, "="); - node.init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr| + var init_node = if (ZigClangVarDecl_getInit(var_decl)) |expr| try transExpr(rp, scope, expr, .used, .r_value) else try transCreateNodeUndefinedLiteral(c); + if (isBoolRes(init_node)) { + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@boolToInt"); + try builtin_node.params.push(init_node); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + init_node = &builtin_node.base; + } + node.init_node = init_node; node.semicolon_token = try appendToken(c, .Semicolon, ";"); try block_scope.block_node.statements.push(&node.base); }, @@ -1300,14 +1309,9 @@ fn finishBoolExpr( return finishBoolExpr(rp, scope, loc, ZigClangQualType_getTypePtr(underlying_type), node, used); }, .Enum => { - const enum_ty = @ptrCast(*const ZigClangEnumType, ty); - const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@enumToInt"); - try builtin_node.params.push(node); - builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); - const op_token = try appendToken(rp.c, .BangEqual, "!="); const rhs_node = try transCreateNodeInt(rp.c, 0); - return transCreateNodeInfixOp(rp, scope, &builtin_node.base, .BangEqual, op_token, rhs_node, used, false); + return transCreateNodeInfixOp(rp, scope, node, .BangEqual, op_token, rhs_node, used, false); }, .Elaborated => { const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ty); @@ -1472,6 +1476,31 @@ fn transCCast( builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); return &builtin_node.base; } + if (ZigClangQualType_getTypeClass(src_type) == .Elaborated) { + const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ZigClangQualType_getTypePtr(src_type)); + return transCCast(rp, scope, loc, dst_type, ZigClangElaboratedType_getNamedType(elaborated_ty), expr); + } + if (ZigClangQualType_getTypeClass(dst_type) == .Elaborated) { + const elaborated_ty = @ptrCast(*const ZigClangElaboratedType, ZigClangQualType_getTypePtr(dst_type)); + return transCCast(rp, scope, loc, ZigClangElaboratedType_getNamedType(elaborated_ty), src_type, expr); + } + if (ZigClangQualType_getTypeClass(src_type) == .Enum and + ZigClangQualType_getTypeClass(dst_type) != .Enum) { + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@enumToInt"); + try builtin_node.params.push(expr); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + return &builtin_node.base; + } + // TODO + // if (ZigClangQualType_getTypeClass(dst_type) == .Enum and + // ZigClangQualType_getTypeClass(src_type) != .Enum) { + // const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@intToEnum"); + // try builtin_node.params.push(try transQualType(rp, dst_type, loc)); + // _ = try appendToken(rp.c, .Comma, ","); + // try builtin_node.params.push(expr); + // builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + // return &builtin_node.base; + // } // TODO: maybe widen to increase size // TODO: maybe bitcast to change sign // TODO: maybe truncate to reduce size @@ -2443,7 +2472,13 @@ fn transCreateNodeAssign( if (result_used == .unused) { const lhs_node = try transExpr(rp, scope, lhs, .used, .l_value); const eq_token = try appendToken(rp.c, .Equal, "="); - const rhs_node = try transExpr(rp, scope, rhs, .used, .r_value); + var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value); + if (isBoolRes(rhs_node)) { + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@boolToInt"); + try builtin_node.params.push(rhs_node); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + rhs_node = &builtin_node.base; + } if (scope.id != .Condition) _ = try appendToken(rp.c, .Semicolon, ";"); @@ -2471,7 +2506,14 @@ fn transCreateNodeAssign( const node = try transCreateNodeVarDecl(rp.c, false, true, tmp); node.eq_token = try appendToken(rp.c, .Equal, "="); - node.init_node = try transExpr(rp, scope, rhs, .used, .r_value); + var rhs_node = try transExpr(rp, scope, rhs, .used, .r_value); + if (isBoolRes(rhs_node)) { + const builtin_node = try transCreateNodeBuiltinFnCall(rp.c, "@boolToInt"); + try builtin_node.params.push(rhs_node); + builtin_node.rparen_token = try appendToken(rp.c, .RParen, ")"); + rhs_node = &builtin_node.base; + } + node.init_node = rhs_node; node.semicolon_token = try appendToken(rp.c, .Semicolon, ";"); try block_scope.block_node.statements.push(&node.base); diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index d87b769f5e..93646de97b 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -1571,6 +1571,16 @@ const ZigClangTypedefNameDecl *ZigClangTypedefNameDecl_getCanonicalDecl(const Zi return reinterpret_cast(decl); } +const ZigClangFunctionDecl *ZigClangFunctionDecl_getCanonicalDecl(const ZigClangFunctionDecl *self) { + const clang::FunctionDecl *decl = reinterpret_cast(self)->getCanonicalDecl(); + return reinterpret_cast(decl); +} + +const ZigClangVarDecl *ZigClangVarDecl_getCanonicalDecl(const ZigClangVarDecl *self) { + const clang::VarDecl *decl = reinterpret_cast(self)->getCanonicalDecl(); + return reinterpret_cast(decl); +} + const ZigClangRecordDecl *ZigClangRecordDecl_getDefinition(const ZigClangRecordDecl *zig_record_decl) { const clang::RecordDecl *record_decl = reinterpret_cast(zig_record_decl); const clang::RecordDecl *definition = record_decl->getDefinition(); diff --git a/src/zig_clang.h b/src/zig_clang.h index 8dd0b9b3d5..ce71612468 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -856,6 +856,8 @@ ZIG_EXTERN_C const struct ZigClangEnumDecl *ZigClangEnumType_getDecl(const struc ZIG_EXTERN_C const struct ZigClangTagDecl *ZigClangRecordDecl_getCanonicalDecl(const struct ZigClangRecordDecl *record_decl); ZIG_EXTERN_C const struct ZigClangTagDecl *ZigClangEnumDecl_getCanonicalDecl(const struct ZigClangEnumDecl *); ZIG_EXTERN_C const struct ZigClangTypedefNameDecl *ZigClangTypedefNameDecl_getCanonicalDecl(const struct ZigClangTypedefNameDecl *); +ZIG_EXTERN_C const struct ZigClangFunctionDecl *ZigClangFunctionDecl_getCanonicalDecl(const ZigClangFunctionDecl *self); +ZIG_EXTERN_C const struct ZigClangVarDecl *ZigClangVarDecl_getCanonicalDecl(const ZigClangVarDecl *self); ZIG_EXTERN_C const struct ZigClangRecordDecl *ZigClangRecordDecl_getDefinition(const struct ZigClangRecordDecl *); ZIG_EXTERN_C const struct ZigClangEnumDecl *ZigClangEnumDecl_getDefinition(const struct ZigClangEnumDecl *); diff --git a/test/translate_c.zig b/test/translate_c.zig index 7f349c54c4..0ecbc56e7c 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -716,6 +716,38 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.addC_both("while on non-bool", + \\int while_none_bool(int a, float b, void *c) { + \\ while (a) return 0; + \\ while (b) return 1; + \\ while (c) return 2; + \\ return 3; + \\} + , &[_][]const u8{ + \\pub export fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { + \\ while (a != 0) return 0; + \\ while (b != 0) return 1; + \\ while (c != null) return 2; + \\ return 3; + \\} + }); + + cases.addC_both("for on non-bool", + \\int for_none_bool(int a, float b, void *c) { + \\ for (;a;) return 0; + \\ for (;b;) return 1; + \\ for (;c;) return 2; + \\ return 3; + \\} + , &[_][]const u8{ + \\pub export fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { + \\ while (a != 0) return 0; + \\ while (b != 0) return 1; + \\ while (c != null) return 2; + \\ return 3; + \\} + }); + /////////////// Cases that pass for only stage2 //////////////// cases.add_2("Parameterless function prototypes", @@ -1369,18 +1401,18 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\pub const SomeTypedef = c_int; \\pub export fn and_or_non_bool(a: c_int, b: f32, c: ?*c_void) c_int { \\ var d: enum_Foo = @as(enum_Foo, FooA); - \\ var e: c_int = ((a != 0) and (b != 0)); - \\ var f: c_int = ((b != 0) and (c != null)); - \\ var g: c_int = ((a != 0) and (c != null)); - \\ var h: c_int = ((a != 0) or (b != 0)); - \\ var i: c_int = ((b != 0) or (c != null)); - \\ var j: c_int = ((a != 0) or (c != null)); - \\ var k: c_int = ((a != 0) or (@enumToInt(@as(c_uint, d)) != 0)); - \\ var l: c_int = ((@enumToInt(@as(c_uint, d)) != 0) and (b != 0)); - \\ var m: c_int = ((c != null) or (@enumToInt(@as(c_uint, d)) != 0)); + \\ var e: c_int = @boolToInt(((a != 0) and (b != 0))); + \\ var f: c_int = @boolToInt(((b != 0) and (c != null))); + \\ var g: c_int = @boolToInt(((a != 0) and (c != null))); + \\ var h: c_int = @boolToInt(((a != 0) or (b != 0))); + \\ var i: c_int = @boolToInt(((b != 0) or (c != null))); + \\ var j: c_int = @boolToInt(((a != 0) or (c != null))); + \\ var k: c_int = @boolToInt(((a != 0) or (@enumToInt(d) != 0))); + \\ var l: c_int = @boolToInt(((@enumToInt(d) != 0) and (b != 0))); + \\ var m: c_int = @boolToInt(((c != null) or (@enumToInt(d) != 0))); \\ var td: SomeTypedef = 44; - \\ var o: c_int = ((td != 0) or (b != 0)); - \\ var p: c_int = ((c != null) and (td != 0)); + \\ var o: c_int = @boolToInt(((td != 0) or (b != 0))); + \\ var p: c_int = @boolToInt(((c != null) and (td != 0))); \\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p); \\} , @@ -1437,13 +1469,13 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} , &[_][]const u8{ \\pub export fn test_comparisons(a: c_int, b: c_int) c_int { - \\ var c: c_int = (a < b); - \\ var d: c_int = (a > b); - \\ var e: c_int = (a <= b); - \\ var f: c_int = (a >= b); - \\ var g: c_int = (c < d); - \\ var h: c_int = (e < f); - \\ var i: c_int = (g < h); + \\ var c: c_int = @boolToInt((a < b)); + \\ var d: c_int = @boolToInt((a > b)); + \\ var e: c_int = @boolToInt((a <= b)); + \\ var f: c_int = @boolToInt((a >= b)); + \\ var g: c_int = @boolToInt((c < d)); + \\ var h: c_int = @boolToInt((e < f)); + \\ var i: c_int = @boolToInt((g < h)); \\ return i; \\} }); @@ -1560,6 +1592,69 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); + cases.add_2("logical and, logical or", + \\int max(int a, int b) { + \\ if (a < b || a == b) + \\ return b; + \\ if (a >= b && a == b) + \\ return a; + \\ return a; + \\} + , &[_][]const u8{ + \\pub export fn max(a: c_int, b: c_int) c_int { + \\ if (((a < b) or (a == b))) return b; + \\ if (((a >= b) and (a == b))) return a; + \\ return a; + \\} + }); + + cases.add_2("if statement", + \\int max(int a, int b) { + \\ if (a < b) + \\ return b; + \\ + \\ if (a < b) + \\ return b; + \\ else + \\ return a; + \\ + \\ if (a < b) ; else ; + \\} + , &[_][]const u8{ + \\pub export fn max(a: c_int, b: c_int) c_int { + \\ if ((a < b)) return b; + \\ if ((a < b)) return b else return a; + \\ if ((a < b)) {} else {} + \\} + }); + + cases.add_2("if on non-bool", + \\enum SomeEnum { A, B, C }; + \\int if_none_bool(int a, float b, void *c, enum SomeEnum d) { + \\ if (a) return 0; + \\ if (b) return 1; + \\ if (c) return 2; + \\ if (d) return 3; + \\ return 4; + \\} + , &[_][]const u8{ + \\pub const A = enum_SomeEnum.A; + \\pub const B = enum_SomeEnum.B; + \\pub const C = enum_SomeEnum.C; + \\pub const enum_SomeEnum = extern enum { + \\ A, + \\ B, + \\ C, + \\}; + \\pub export fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int { + \\ if (a != 0) return 0; + \\ if (b != 0) return 1; + \\ if (c != null) return 2; + \\ if (d != 0) return 3; + \\ return 4; + \\} + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// cases.addAllowWarnings("simple data types", @@ -1651,85 +1746,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("if statement", - \\int max(int a, int b) { - \\ if (a < b) - \\ return b; - \\ - \\ if (a < b) - \\ return b; - \\ else - \\ return a; - \\ - \\ if (a < b) ; else ; - \\} - , &[_][]const u8{ - \\pub export fn max(a: c_int, b: c_int) c_int { - \\ if (a < b) return b; - \\ if (a < b) return b else return a; - \\ if (a < b) {} else {} - \\} - }); - - cases.addC("logical and, logical or", - \\int max(int a, int b) { - \\ if (a < b || a == b) - \\ return b; - \\ if (a >= b && a == b) - \\ return a; - \\ return a; - \\} - , &[_][]const u8{ - \\pub export fn max(a: c_int, b: c_int) c_int { - \\ if ((a < b) or (a == b)) return b; - \\ if ((a >= b) and (a == b)) return a; - \\ return a; - \\} - }); - - cases.addC("logical and, logical or, on non-bool values", // Note this gets cut off by extra C symbols being injected in middle: `pub const Foo = enum_Foo;` - \\enum Foo { - \\ FooA, - \\ FooB, - \\ FooC, - \\}; - \\int and_or_non_bool(int a, float b, void *c) { - \\ enum Foo d = FooA; - \\ int e = (a && b); - \\ int f = (b && c); - \\ int g = (a && c); - \\ int h = (a || b); - \\ int i = (b || c); - \\ int j = (a || c); - \\ int k = (a || d); - \\ int l = (d && b); - \\ int m = (c || d); - \\ return (((((((e + f) + g) + h) + i) + j) + k) + l) + m; - \\} - , &[_][]const u8{ - \\pub const FooA = enum_Foo.A; - \\pub const FooB = enum_Foo.B; - \\pub const FooC = enum_Foo.C; - \\pub const enum_Foo = extern enum { - \\ A, - \\ B, - \\ C, - \\}; - \\pub export fn and_or_non_bool(a: c_int, b: f32, c: ?*c_void) c_int { - \\ var d: enum_Foo = @as(enum_Foo, FooA); - \\ var e: c_int = (a != 0) and (b != 0); - \\ var f: c_int = (b != 0) and (c != null); - \\ var g: c_int = (a != 0) and (c != null); - \\ var h: c_int = (a != 0) or (b != 0); - \\ var i: c_int = (b != 0) or (c != null); - \\ var j: c_int = (a != 0) or (c != null); - \\ var k: c_int = (a != 0) or (@as(c_uint, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0))); - \\ var l: c_int = (@as(c_uint, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0))) and (b != 0); - \\ var m: c_int = (c != null) or (@as(c_uint, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0))); - \\ return (((((((e + f) + g) + h) + i) + j) + k) + l) + m; - \\} - }); - cases.addC("shift right assign with a fixed size type", \\#include \\int log2(uint32_t a) { @@ -2043,26 +2059,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add("variable name shadowing", - \\int foo(void) { - \\ int x = 1; - \\ { - \\ int x = 2; - \\ x += 1; - \\ } - \\ return x; - \\} - , &[_][]const u8{ - \\pub fn foo() c_int { - \\ var x: c_int = 1; - \\ { - \\ var x_0: c_int = 2; - \\ x_0 += 1; - \\ } - \\ return x; - \\} - }); - cases.add("bin not", \\int foo(int x) { \\ return ~x; @@ -2099,65 +2095,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add("if on non-bool", - \\enum SomeEnum { A, B, C }; - \\int if_none_bool(int a, float b, void *c, enum SomeEnum d) { - \\ if (a) return 0; - \\ if (b) return 1; - \\ if (c) return 2; - \\ if (d) return 3; - \\ return 4; - \\} - , &[_][]const u8{ - \\pub const A = enum_SomeEnum.A; - \\pub const B = enum_SomeEnum.B; - \\pub const C = enum_SomeEnum.C; - \\pub const enum_SomeEnum = extern enum { - \\ A, - \\ B, - \\ C, - \\}; - \\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int { - \\ if (a != 0) return 0; - \\ if (b != 0) return 1; - \\ if (c != null) return 2; - \\ if (d != @bitCast(enum_SomeEnum, @as(@TagType(enum_SomeEnum), 0))) return 3; - \\ return 4; - \\} - }); - - cases.add("while on non-bool", - \\int while_none_bool(int a, float b, void *c) { - \\ while (a) return 0; - \\ while (b) return 1; - \\ while (c) return 2; - \\ return 3; - \\} - , &[_][]const u8{ - \\pub fn while_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { - \\ while (a != 0) return 0; - \\ while (b != 0) return 1; - \\ while (c != null) return 2; - \\ return 3; - \\} - }); - - cases.add("for on non-bool", - \\int for_none_bool(int a, float b, void *c) { - \\ for (;a;) return 0; - \\ for (;b;) return 1; - \\ for (;c;) return 2; - \\ return 3; - \\} - , &[_][]const u8{ - \\pub fn for_none_bool(a: c_int, b: f32, c: ?*c_void) c_int { - \\ while (a != 0) return 0; - \\ while (b != 0) return 1; - \\ while (c != null) return 2; - \\ return 3; - \\} - }); - cases.addC("implicit casts", \\#include \\ @@ -2675,4 +2612,130 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ return array[index]; \\} }); + + cases.addC("logical and, logical or", + \\int max(int a, int b) { + \\ if (a < b || a == b) + \\ return b; + \\ if (a >= b && a == b) + \\ return a; + \\ return a; + \\} + , &[_][]const u8{ + \\pub export fn max(a: c_int, b: c_int) c_int { + \\ if ((a < b) or (a == b)) return b; + \\ if ((a >= b) and (a == b)) return a; + \\ return a; + \\} + }); + + cases.addC("if statement", + \\int max(int a, int b) { + \\ if (a < b) + \\ return b; + \\ + \\ if (a < b) + \\ return b; + \\ else + \\ return a; + \\ + \\ if (a < b) ; else ; + \\} + , &[_][]const u8{ + \\pub export fn max(a: c_int, b: c_int) c_int { + \\ if (a < b) return b; + \\ if (a < b) return b else return a; + \\ if (a < b) {} else {} + \\} + }); + + cases.addC("logical and, logical or, on non-bool values", // Note this gets cut off by extra C symbols being injected in middle: `pub const Foo = enum_Foo;` + \\enum Foo { + \\ FooA, + \\ FooB, + \\ FooC, + \\}; + \\int and_or_non_bool(int a, float b, void *c) { + \\ enum Foo d = FooA; + \\ int e = (a && b); + \\ int f = (b && c); + \\ int g = (a && c); + \\ int h = (a || b); + \\ int i = (b || c); + \\ int j = (a || c); + \\ int k = (a || d); + \\ int l = (d && b); + \\ int m = (c || d); + \\ return (((((((e + f) + g) + h) + i) + j) + k) + l) + m; + \\} + , &[_][]const u8{ + \\pub const FooA = enum_Foo.A; + \\pub const FooB = enum_Foo.B; + \\pub const FooC = enum_Foo.C; + \\pub const enum_Foo = extern enum { + \\ A, + \\ B, + \\ C, + \\}; + \\pub export fn and_or_non_bool(a: c_int, b: f32, c: ?*c_void) c_int { + \\ var d: enum_Foo = @as(enum_Foo, FooA); + \\ var e: c_int = (a != 0) and (b != 0); + \\ var f: c_int = (b != 0) and (c != null); + \\ var g: c_int = (a != 0) and (c != null); + \\ var h: c_int = (a != 0) or (b != 0); + \\ var i: c_int = (b != 0) or (c != null); + \\ var j: c_int = (a != 0) or (c != null); + \\ var k: c_int = (a != 0) or (@as(c_uint, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0))); + \\ var l: c_int = (@as(c_uint, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0))) and (b != 0); + \\ var m: c_int = (c != null) or (@as(c_uint, d) != @bitCast(enum_Foo, @as(@TagType(enum_Foo), 0))); + \\ return (((((((e + f) + g) + h) + i) + j) + k) + l) + m; + \\} + }); + + cases.add("variable name shadowing", + \\int foo(void) { + \\ int x = 1; + \\ { + \\ int x = 2; + \\ x += 1; + \\ } + \\ return x; + \\} + , &[_][]const u8{ + \\pub fn foo() c_int { + \\ var x: c_int = 1; + \\ { + \\ var x_0: c_int = 2; + \\ x_0 += 1; + \\ } + \\ return x; + \\} + }); + + cases.add("if on non-bool", + \\enum SomeEnum { A, B, C }; + \\int if_none_bool(int a, float b, void *c, enum SomeEnum d) { + \\ if (a) return 0; + \\ if (b) return 1; + \\ if (c) return 2; + \\ if (d) return 3; + \\ return 4; + \\} + , &[_][]const u8{ + \\pub const A = enum_SomeEnum.A; + \\pub const B = enum_SomeEnum.B; + \\pub const C = enum_SomeEnum.C; + \\pub const enum_SomeEnum = extern enum { + \\ A, + \\ B, + \\ C, + \\}; + \\pub fn if_none_bool(a: c_int, b: f32, c: ?*c_void, d: enum_SomeEnum) c_int { + \\ if (a != 0) return 0; + \\ if (b != 0) return 1; + \\ if (c != null) return 2; + \\ if (d != @bitCast(enum_SomeEnum, @as(@TagType(enum_SomeEnum), 0))) return 3; + \\ return 4; + \\} + }); }