From 6d7025d0c5c619773f0f5cc49edbd30713a7328d Mon Sep 17 00:00:00 2001 From: Vexu Date: Tue, 17 Dec 2019 23:28:13 +0200 Subject: [PATCH] translate-c-2 various fixes to get more tests passing --- src-self-hosted/clang.zig | 17 +- src-self-hosted/translate_c.zig | 21 +- test/translate_c.zig | 698 ++++++++++++++++++-------------- 3 files changed, 423 insertions(+), 313 deletions(-) diff --git a/src-self-hosted/clang.zig b/src-self-hosted/clang.zig index 3fa937db9c..b44f3a2389 100644 --- a/src-self-hosted/clang.zig +++ b/src-self-hosted/clang.zig @@ -793,16 +793,16 @@ pub extern fn ZigClangInitListExpr_getInit(self: ?*const struct_ZigClangInitList pub extern fn ZigClangInitListExpr_getArrayFiller(self: ?*const struct_ZigClangInitListExpr) *const ZigClangExpr; pub extern fn ZigClangInitListExpr_getNumInits(self: ?*const struct_ZigClangInitListExpr) c_uint; pub extern fn ZigClangAPValue_getKind(self: ?*const struct_ZigClangAPValue) ZigClangAPValueKind; -pub extern fn ZigClangAPValue_getInt(self: ?*const struct_ZigClangAPValue) ?*const struct_ZigClangAPSInt; +pub extern fn ZigClangAPValue_getInt(self: ?*const struct_ZigClangAPValue) *const struct_ZigClangAPSInt; pub extern fn ZigClangAPValue_getArrayInitializedElts(self: ?*const struct_ZigClangAPValue) c_uint; pub extern fn ZigClangAPValue_getArraySize(self: ?*const struct_ZigClangAPValue) c_uint; pub extern fn ZigClangAPValue_getLValueBase(self: ?*const struct_ZigClangAPValue) struct_ZigClangAPValueLValueBase; -pub extern fn ZigClangAPSInt_isSigned(self: ?*const struct_ZigClangAPSInt) bool; -pub extern fn ZigClangAPSInt_isNegative(self: ?*const struct_ZigClangAPSInt) bool; -pub extern fn ZigClangAPSInt_negate(self: ?*const struct_ZigClangAPSInt) ?*const struct_ZigClangAPSInt; -pub extern fn ZigClangAPSInt_free(self: ?*const struct_ZigClangAPSInt) void; -pub extern fn ZigClangAPSInt_getRawData(self: ?*const struct_ZigClangAPSInt) [*:0]const u64; -pub extern fn ZigClangAPSInt_getNumWords(self: ?*const struct_ZigClangAPSInt) c_uint; +pub extern fn ZigClangAPSInt_isSigned(self: *const struct_ZigClangAPSInt) bool; +pub extern fn ZigClangAPSInt_isNegative(self: *const struct_ZigClangAPSInt) bool; +pub extern fn ZigClangAPSInt_negate(self: *const struct_ZigClangAPSInt) *const struct_ZigClangAPSInt; +pub extern fn ZigClangAPSInt_free(self: *const struct_ZigClangAPSInt) void; +pub extern fn ZigClangAPSInt_getRawData(self: *const struct_ZigClangAPSInt) [*:0]const u64; +pub extern fn ZigClangAPSInt_getNumWords(self: *const struct_ZigClangAPSInt) c_uint; pub extern fn ZigClangAPInt_getLimitedValue(self: *const struct_ZigClangAPInt, limit: u64) u64; pub extern fn ZigClangAPValueLValueBase_dyn_cast_Expr(self: struct_ZigClangAPValueLValueBase) ?*const struct_ZigClangExpr; @@ -1074,3 +1074,6 @@ pub extern fn ZigClangCaseStmt_getSubStmt(*const ZigClangCaseStmt) *const ZigCla pub extern fn ZigClangDefaultStmt_getSubStmt(*const ZigClangDefaultStmt) *const ZigClangStmt; pub extern fn ZigClangExpr_EvaluateAsConstantExpr(*const ZigClangExpr, *ZigClangExprEvalResult, ZigClangExpr_ConstExprUsage, *const ZigClangASTContext) bool; + +pub extern fn ZigClangPredefinedExpr_getFunctionName(*const ZigClangPredefinedExpr) *const ZigClangStringLiteral; + diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 0f7f1b99bc..0933b7aa20 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -861,6 +861,7 @@ fn transStmt( .CaseStmtClass => return transCase(rp, scope, @ptrCast(*const ZigClangCaseStmt, stmt)), .DefaultStmtClass => return transDefault(rp, scope, @ptrCast(*const ZigClangDefaultStmt, stmt)), .ConstantExprClass => return transConstantExpr(rp, scope, @ptrCast(*const ZigClangExpr, stmt), result_used), + .PredefinedExprClass => return transPredefinedExpr(rp, scope, @ptrCast(*const ZigClangPredefinedExpr, stmt), result_used), else => { return revertAndWarn( rp, @@ -1748,6 +1749,10 @@ fn transConstantExpr(rp: RestorePoint, scope: *Scope, expr: *const ZigClangExpr, return maybeSuppressResult(rp, scope, used, try transCreateNodeAPInt(rp.c, ZigClangAPValue_getInt(&result.Val))); } +fn transPredefinedExpr(rp: RestorePoint, scope: *Scope, expr: *const ZigClangPredefinedExpr, used: ResultUsed) TransError!*ast.Node { + return transStringLiteral(rp, scope, ZigClangPredefinedExpr_getFunctionName(expr), used); +} + fn transCPtrCast( rp: RestorePoint, loc: ZigClangSourceLocation, @@ -2191,11 +2196,17 @@ fn transCreateNodePtrType( return node; } -fn transCreateNodeAPInt(c: *Context, int: ?*const ZigClangAPSInt) !*ast.Node { - const num_limbs = ZigClangAPSInt_getNumWords(int.?); +fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node { + const num_limbs = ZigClangAPSInt_getNumWords(int); + var aps_int = int; + const is_negative = ZigClangAPSInt_isSigned(int) and ZigClangAPSInt_isNegative(int); + if (is_negative) + aps_int = ZigClangAPSInt_negate(aps_int); var big = try std.math.big.Int.initCapacity(c.a(), num_limbs); + if (is_negative) + big.negate(); defer big.deinit(); - const data = ZigClangAPSInt_getRawData(int.?); + const data = ZigClangAPSInt_getRawData(aps_int); var i: @TypeOf(num_limbs) = 0; while (i < num_limbs) : (i += 1) big.limbs[i] = data[i]; const str = big.toString(c.a(), 10) catch |err| switch (err) { @@ -2207,6 +2218,8 @@ fn transCreateNodeAPInt(c: *Context, int: ?*const ZigClangAPSInt) !*ast.Node { node.* = .{ .token = token, }; + if (is_negative) + ZigClangAPSInt_free(aps_int); return &node.base; } @@ -2803,7 +2816,7 @@ fn finishTransFnProto( const param = ZigClangFunctionDecl_getParamDecl(fn_decl.?, @intCast(c_uint, i)); var param_name: []const u8 = try rp.c.str(ZigClangDecl_getName_bytes_begin(@ptrCast(*const ZigClangDecl, param))); if (param_name.len < 1) - param_name = "arg"[0..]; + param_name = try std.fmt.allocPrint(rp.c.a(), "arg_{}", .{rp.c.getMangle()}); const checked_param_name = if (try scope.createAlias(rp.c, param_name)) |a| blk: { try fndef_scope.params.push(.{ .name = param_name, .alias = a }); break :blk a; diff --git a/test/translate_c.zig b/test/translate_c.zig index be31ee3cbb..aebeddc856 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -426,6 +426,180 @@ pub fn addCases(cases: *tests.TranslateCContext) void { }, ); + cases.addC_both("null statements", + \\void foo(void) { + \\ ;;;;; + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ {} + \\ {} + \\ {} + \\ {} + \\ {} + \\} + }); + + if (builtin.os != builtin.Os.windows) { + // Windows treats this as an enum with type c_int + cases.add_both("big negative enum init values when C ABI supports long long enums", + \\enum EnumWithInits { + \\ VAL01 = 0, + \\ VAL02 = 1, + \\ VAL03 = 2, + \\ VAL04 = 3, + \\ VAL05 = -1, + \\ VAL06 = -2, + \\ VAL07 = -3, + \\ VAL08 = -4, + \\ VAL09 = VAL02 + VAL08, + \\ VAL10 = -1000012000, + \\ VAL11 = -1000161000, + \\ VAL12 = -1000174001, + \\ VAL13 = VAL09, + \\ VAL14 = VAL10, + \\ VAL15 = VAL11, + \\ VAL16 = VAL13, + \\ VAL17 = (VAL16 - VAL10 + 1), + \\ VAL18 = 0x1000000000000000L, + \\ VAL19 = VAL18 + VAL18 + VAL18 - 1, + \\ VAL20 = VAL19 + VAL19, + \\ VAL21 = VAL20 + 0xFFFFFFFFFFFFFFFF, + \\ VAL22 = 0xFFFFFFFFFFFFFFFF + 1, + \\ VAL23 = 0xFFFFFFFFFFFFFFFF, + \\}; + , &[_][]const u8{ + \\pub const enum_EnumWithInits = extern enum(c_longlong) { + \\ VAL01 = 0, + \\ VAL02 = 1, + \\ VAL03 = 2, + \\ VAL04 = 3, + \\ VAL05 = -1, + \\ VAL06 = -2, + \\ VAL07 = -3, + \\ VAL08 = -4, + \\ VAL09 = -3, + \\ VAL10 = -1000012000, + \\ VAL11 = -1000161000, + \\ VAL12 = -1000174001, + \\ VAL13 = -3, + \\ VAL14 = -1000012000, + \\ VAL15 = -1000161000, + \\ VAL16 = -3, + \\ VAL17 = 1000011998, + \\ VAL18 = 1152921504606846976, + \\ VAL19 = 3458764513820540927, + \\ VAL20 = 6917529027641081854, + \\ VAL21 = 6917529027641081853, + \\ VAL22 = 0, + \\ VAL23 = -1, + \\}; + }); + } + + cases.addC_both("predefined expressions", + \\void foo(void) { + \\ __func__; + \\ __FUNCTION__; + \\ __PRETTY_FUNCTION__; + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ _ = "foo"; + \\ _ = "foo"; + \\ _ = "void foo(void)"; + \\} + }); + + cases.addC_both("ignore result, no function arguments", + \\void foo() { + \\ int a; + \\ 1; + \\ "hey"; + \\ 1 + 1; + \\ 1 - 1; + \\ a = 1; + \\} + , &[_][]const u8{ + \\pub export fn foo() void { + \\ var a: c_int = undefined; + \\ _ = 1; + \\ _ = "hey"; + \\ _ = (1 + 1); + \\ _ = (1 - 1); + \\ a = 1; + \\} + }); + + cases.add_2("qualified struct and enum", + \\struct Foo { + \\ int x; + \\ int y; + \\}; + \\enum Bar { + \\ BarA, + \\ BarB, + \\}; + \\void func(struct Foo *a, enum Bar **b); + , &[_][]const u8{ + \\pub const struct_Foo = extern struct { + \\ x: c_int, + \\ y: c_int, + \\}; + \\pub const BarA = enum_Bar.A; + \\pub const BarB = enum_Bar.B; + \\pub const enum_Bar = extern enum { + \\ A, + \\ B, + \\}; + \\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void; + , + \\pub const Foo = struct_Foo; + \\pub const Bar = enum_Bar; + }); + + cases.add_both("constant size array", + \\void func(int array[20]); + , &[_][]const u8{ + \\pub extern fn func(array: [*c]c_int) void; + }); + + cases.add_both("__cdecl doesn't mess up function pointers", + \\void foo(void (__cdecl *fn_ptr)(void)); + , &[_][]const u8{ + \\pub extern fn foo(fn_ptr: ?extern fn () void) void; + }); + + cases.addC_both("void cast", + \\void foo(int a) { + \\ (void) a; + \\} + , &[_][]const u8{ + \\pub export fn foo(a: c_int) void { + \\ _ = a; + \\} + }); + + cases.addC_both("implicit cast to void *", + \\void *foo(unsigned short *x) { + \\ return x; + \\} + , &[_][]const u8{ + \\pub export fn foo(x: [*c]c_ushort) ?*c_void { + \\ return @ptrCast(?*c_void, x); + \\} + }); + + cases.addC_both("null pointer implicit cast", + \\int* foo(void) { + \\ return 0; + \\} + , &[_][]const u8{ + \\pub export fn foo() [*c]c_int { + \\ return null; + \\} + }); + /////////////// Cases that pass for only stage2 //////////////// cases.add_2("Parameterless function prototypes", @@ -904,145 +1078,75 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\}; }); + cases.add_2("undefined array global", + \\int array[100] = {}; + , &[_][]const u8{ + \\pub export var array: [100]c_int = .{0} ** 100; + }); + + cases.add_2("restrict -> noalias", + \\void foo(void *restrict bar, void *restrict); + , &[_][]const u8{ + \\pub extern fn foo(noalias bar: ?*c_void, noalias arg_1: ?*c_void) void; + }); + + cases.add_2("assign", + \\int max(int a) { + \\ int tmp; + \\ tmp = a; + \\ a = tmp; + \\} + , &[_][]const u8{ + \\pub export fn max(a: c_int) c_int { + \\ var tmp: c_int = undefined; + \\ tmp = a; + \\ a = tmp; + \\} + }); + + cases.add_2("chaining assign", + \\void max(int a) { + \\ int b, c; + \\ c = b = a; + \\} + , &[_][]const u8{ + \\pub export fn max(a: c_int) void { + \\ var b: c_int = undefined; + \\ var c: c_int = undefined; + \\ c = blk: { + \\ const _tmp_1 = a; + \\ b = _tmp_1; + \\ break :blk _tmp_1; + \\ }; + \\} + }); + + cases.add_2("anonymous enum", + \\enum { + \\ One, + \\ Two, + \\}; + , &[_][]const u8{ + \\pub const One = enum_unnamed_1.One; + \\pub const Two = enum_unnamed_1.Two; + \\pub const enum_unnamed_1 = extern enum { + \\ One, + \\ Two, + \\}; + }); + + cases.add_2("c style cast", + \\int float_to_int(float a) { + \\ return (int)a; + \\} + , &[_][]const u8{ + \\pub export fn float_to_int(a: f32) c_int { + \\ return @floatToInt(c_int, a); + \\} + }); + /////////////// Cases for only stage1 which are TODO items for stage2 //////////////// - if (builtin.os != builtin.Os.windows) { - // Windows treats this as an enum with type c_int - cases.add("big negative enum init values when C ABI supports long long enums", - \\enum EnumWithInits { - \\ VAL01 = 0, - \\ VAL02 = 1, - \\ VAL03 = 2, - \\ VAL04 = 3, - \\ VAL05 = -1, - \\ VAL06 = -2, - \\ VAL07 = -3, - \\ VAL08 = -4, - \\ VAL09 = VAL02 + VAL08, - \\ VAL10 = -1000012000, - \\ VAL11 = -1000161000, - \\ VAL12 = -1000174001, - \\ VAL13 = VAL09, - \\ VAL14 = VAL10, - \\ VAL15 = VAL11, - \\ VAL16 = VAL13, - \\ VAL17 = (VAL16 - VAL10 + 1), - \\ VAL18 = 0x1000000000000000L, - \\ VAL19 = VAL18 + VAL18 + VAL18 - 1, - \\ VAL20 = VAL19 + VAL19, - \\ VAL21 = VAL20 + 0xFFFFFFFFFFFFFFFF, - \\ VAL22 = 0xFFFFFFFFFFFFFFFF + 1, - \\ VAL23 = 0xFFFFFFFFFFFFFFFF, - \\}; - , &[_][]const u8{ - \\pub const enum_EnumWithInits = extern enum(c_longlong) { - \\ VAL01 = 0, - \\ VAL02 = 1, - \\ VAL03 = 2, - \\ VAL04 = 3, - \\ VAL05 = -1, - \\ VAL06 = -2, - \\ VAL07 = -3, - \\ VAL08 = -4, - \\ VAL09 = -3, - \\ VAL10 = -1000012000, - \\ VAL11 = -1000161000, - \\ VAL12 = -1000174001, - \\ VAL13 = -3, - \\ VAL14 = -1000012000, - \\ VAL15 = -1000161000, - \\ VAL16 = -3, - \\ VAL17 = 1000011998, - \\ VAL18 = 1152921504606846976, - \\ VAL19 = 3458764513820540927, - \\ VAL20 = 6917529027641081854, - \\ VAL21 = 6917529027641081853, - \\ VAL22 = 0, - \\ VAL23 = -1, - \\}; - }); - } - - cases.add("predefined expressions", - \\void foo(void) { - \\ __func__; - \\ __FUNCTION__; - \\ __PRETTY_FUNCTION__; - \\} - , &[_][]const u8{ - \\pub fn foo() void { - \\ _ = "foo"; - \\ _ = "foo"; - \\ _ = "void foo(void)"; - \\} - }); - - cases.add("ignore result, no function arguments", - \\void foo() { - \\ int a; - \\ 1; - \\ "hey"; - \\ 1 + 1; - \\ 1 - 1; - \\ a = 1; - \\} - , &[_][]const u8{ - \\pub fn foo() void { - \\ var a: c_int = undefined; - \\ _ = 1; - \\ _ = "hey"; - \\ _ = (1 + 1); - \\ _ = (1 - 1); - \\ a = 1; - \\} - }); - - cases.add("for loop with var init but empty body", - \\void foo(void) { - \\ for (int x = 0; x < 10; x++); - \\} - , &[_][]const u8{ - \\pub fn foo() void { - \\ { - \\ var x: c_int = 0; - \\ while (x < 10) : (x += 1) {} - \\ } - \\} - }); - - cases.add("do while with empty body", - \\void foo(void) { - \\ do ; while (1); - \\} - , &[_][]const u8{ // TODO this should be if (1 != 0) break - \\pub fn foo() void { - \\ while (true) { - \\ {} - \\ if (!1) break; - \\ } - \\} - }); - - cases.add("for with empty body", - \\void foo(void) { - \\ for (;;); - \\} - , &[_][]const u8{ - \\pub fn foo() void { - \\ while (true) {} - \\} - }); - - cases.add("while with empty body", - \\void foo(void) { - \\ while (1); - \\} - , &[_][]const u8{ - \\pub fn foo() void { - \\ while (1 != 0) {} - \\} - }); - cases.addAllowWarnings("simple data types", \\#include \\int foo(char a, unsigned char b, signed char c); @@ -1067,56 +1171,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add("restrict -> noalias", - \\void foo(void *restrict bar, void *restrict); - , &[_][]const u8{ - \\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void; - }); - - cases.add("qualified struct and enum", - \\struct Foo { - \\ int x; - \\ int y; - \\}; - \\enum Bar { - \\ BarA, - \\ BarB, - \\}; - \\void func(struct Foo *a, enum Bar **b); - , &[_][]const u8{ - \\pub const struct_Foo = extern struct { - \\ x: c_int, - \\ y: c_int, - \\}; - , - \\pub const enum_Bar = extern enum { - \\ A, - \\ B, - \\}; - , - \\pub const BarA = enum_Bar.A; - , - \\pub const BarB = enum_Bar.B; - , - \\pub extern fn func(a: [*c]struct_Foo, b: [*c]([*c]enum_Bar)) void; - , - \\pub const Foo = struct_Foo; - , - \\pub const Bar = enum_Bar; - }); - - cases.add("constant size array", - \\void func(int array[20]); - , &[_][]const u8{ - \\pub extern fn func(array: [*c]c_int) void; - }); - - cases.add("__cdecl doesn't mess up function pointers", - \\void foo(void (__cdecl *fn_ptr)(void)); - , &[_][]const u8{ - \\pub extern fn foo(fn_ptr: ?extern fn () void) void; - }); - cases.add("macro defines string literal with hex", \\#define FOO "aoeu\xab derp" \\#define FOO2 "aoeu\x0007a derp" @@ -1266,38 +1320,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("assign", - \\int max(int a) { - \\ int tmp; - \\ tmp = a; - \\ a = tmp; - \\} - , &[_][]const u8{ - \\pub export fn max(_arg_a: c_int) c_int { - \\ var a = _arg_a; - \\ var tmp: c_int = undefined; - \\ tmp = a; - \\ a = tmp; - \\} - }); - - cases.addC("chaining assign", - \\void max(int a) { - \\ int b, c; - \\ c = b = a; - \\} - , &[_][]const u8{ - \\pub export fn max(a: c_int) void { - \\ var b: c_int = undefined; - \\ var c: c_int = undefined; - \\ c = (x: { - \\ const _tmp = a; - \\ b = _tmp; - \\ break :x _tmp; - \\ }); - \\} - }); - cases.addC("shift right assign with a fixed size type", \\#include \\int log2(uint32_t a) { @@ -1318,16 +1340,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.add("anonymous enum", - \\enum { - \\ One, - \\ Two, - \\}; - , &[_][]const u8{ - \\pub const One = 0; - \\pub const Two = 1; - }); - cases.addC("function call", \\static void bar(void) { } \\static int baz(void) { return 0; } @@ -1362,26 +1374,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("null statements", - \\void foo(void) { - \\ ;;;;; - \\} - , &[_][]const u8{ - \\pub export fn foo() void { - \\ {} - \\ {} - \\ {} - \\ {} - \\ {} - \\} - }); - - cases.add("undefined array global", - \\int array[100]; - , &[_][]const u8{ - \\pub var array: [100]c_int = undefined; - }); - cases.addC("array access", \\int array[100]; \\int foo(int index) { @@ -1394,36 +1386,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("c style cast", - \\int float_to_int(float a) { - \\ return (int)a; - \\} - , &[_][]const u8{ - \\pub export fn float_to_int(a: f32) c_int { - \\ return @as(c_int, a); - \\} - }); - - cases.addC("void cast", - \\void foo(int a) { - \\ (void) a; - \\} - , &[_][]const u8{ - \\pub export fn foo(a: c_int) void { - \\ _ = a; - \\} - }); - - cases.addC("implicit cast to void *", - \\void *foo(unsigned short *x) { - \\ return x; - \\} - , &[_][]const u8{ - \\pub export fn foo(x: [*c]c_ushort) ?*c_void { - \\ return @ptrCast(?*c_void, x); - \\} - }); - cases.addC("sizeof", \\#include \\size_t size_of(void) { @@ -1435,29 +1397,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\} }); - cases.addC("null pointer implicit cast", - \\int* foo(void) { - \\ return 0; - \\} - , &[_][]const u8{ - \\pub export fn foo() [*c]c_int { - \\ return null; - \\} - }); - - cases.addC("comma operator", - \\int foo(void) { - \\ return 1, 2; - \\} - , &[_][]const u8{ - \\pub export fn foo() c_int { - \\ return x: { - \\ _ = 1; - \\ break :x 2; - \\ }; - \\} - }); - cases.addC("statement expression", \\int foo(void) { \\ return ({ @@ -2292,4 +2231,159 @@ pub fn addCases(cases: *tests.TranslateCContext) void { \\ } \\} }); + + cases.add("for loop with var init but empty body", + \\void foo(void) { + \\ for (int x = 0; x < 10; x++); + \\} + , &[_][]const u8{ + \\pub fn foo() void { + \\ { + \\ var x: c_int = 0; + \\ while (x < 10) : (x += 1) {} + \\ } + \\} + }); + + cases.add("do while with empty body", + \\void foo(void) { + \\ do ; while (1); + \\} + , &[_][]const u8{ // TODO this should be if (1 != 0) break + \\pub fn foo() void { + \\ while (true) { + \\ {} + \\ if (!1) break; + \\ } + \\} + }); + + cases.add("for with empty body", + \\void foo(void) { + \\ for (;;); + \\} + , &[_][]const u8{ + \\pub fn foo() void { + \\ while (true) {} + \\} + }); + + cases.add("while with empty body", + \\void foo(void) { + \\ while (1); + \\} + , &[_][]const u8{ + \\pub fn foo() void { + \\ while (1 != 0) {} + \\} + }); + + cases.add("undefined array global", + \\int array[100]; + , &[_][]const u8{ + \\pub var array: [100]c_int = undefined; + }); + + cases.add("qualified struct and enum", + \\struct Foo { + \\ int x; + \\ int y; + \\}; + \\enum Bar { + \\ BarA, + \\ BarB, + \\}; + \\void func(struct Foo *a, enum Bar **b); + , &[_][]const u8{ + \\pub const struct_Foo = extern struct { + \\ x: c_int, + \\ y: c_int, + \\}; + , + \\pub const enum_Bar = extern enum { + \\ A, + \\ B, + \\}; + , + \\pub const BarA = enum_Bar.A; + , + \\pub const BarB = enum_Bar.B; + , + \\pub extern fn func(a: [*c]struct_Foo, b: [*c]([*c]enum_Bar)) void; + , + \\pub const Foo = struct_Foo; + , + \\pub const Bar = enum_Bar; + }); + + cases.add("restrict -> noalias", + \\void foo(void *restrict bar, void *restrict); + , &[_][]const u8{ + \\pub extern fn foo(noalias bar: ?*c_void, noalias arg1: ?*c_void) void; + }); + + cases.addC("assign", + \\int max(int a) { + \\ int tmp; + \\ tmp = a; + \\ a = tmp; + \\} + , &[_][]const u8{ + \\pub export fn max(_arg_a: c_int) c_int { + \\ var a = _arg_a; + \\ var tmp: c_int = undefined; + \\ tmp = a; + \\ a = tmp; + \\} + }); + + cases.addC("chaining assign", + \\void max(int a) { + \\ int b, c; + \\ c = b = a; + \\} + , &[_][]const u8{ + \\pub export fn max(a: c_int) void { + \\ var b: c_int = undefined; + \\ var c: c_int = undefined; + \\ c = (x: { + \\ const _tmp = a; + \\ b = _tmp; + \\ break :x _tmp; + \\ }); + \\} + }); + + cases.add("anonymous enum", + \\enum { + \\ One, + \\ Two, + \\}; + , &[_][]const u8{ + \\pub const One = 0; + \\pub const Two = 1; + }); + + cases.addC("c style cast", + \\int float_to_int(float a) { + \\ return (int)a; + \\} + , &[_][]const u8{ + \\pub export fn float_to_int(a: f32) c_int { + \\ return @as(c_int, a); + \\} + }); + + cases.addC("comma operator", + \\int foo(void) { + \\ return 1, 2; + \\} + , &[_][]const u8{ + \\pub export fn foo() c_int { + \\ return x: { + \\ _ = 1; + \\ break :x 2; + \\ }; + \\} + }); }