zig/test/translate_c.zig

4316 lines
130 KiB
Zig
Raw Normal View History

2020-02-25 08:43:21 +00:00
const std = @import("std");
const builtin = @import("builtin");
const tests = @import("tests.zig");
// ********************************************************
// * *
// * DO NOT ADD NEW CASES HERE *
// * instead add a file to test/cases/translate_c *
// * *
// ********************************************************
2019-12-12 19:14:55 +00:00
pub fn addCases(cases: *tests.TranslateCContext) void {
const default_enum_type = if (builtin.abi == .msvc) "c_int" else "c_uint";
cases.add("do while with breaks",
\\void foo(int a) {
\\ do {
\\ if (a) break;
\\ } while (4);
\\ do {
\\ if (a) break;
\\ } while (0);
\\ do {
\\ if (a) break;
\\ } while (a);
\\ do {
\\ break;
\\ } while (3);
\\ do {
\\ break;
\\ } while (0);
\\ do {
\\ break;
\\ } while (a);
\\}
, &[_][]const u8{
\\pub export fn foo(arg_a: c_int) void {
\\ var a = arg_a;
\\ _ = &a;
\\ while (true) {
\\ if (a != 0) break;
\\ }
\\ while (true) {
\\ if (a != 0) break;
\\ if (!false) break;
\\ }
\\ while (true) {
\\ if (a != 0) break;
\\ if (!(a != 0)) break;
\\ }
\\ while (true) {
\\ break;
\\ }
\\ while (true) {
\\ break;
\\ }
\\ while (true) {
\\ break;
\\ }
\\}
});
cases.add("variables check for opaque demotion",
\\struct A {
\\ _Atomic int a;
\\} a;
\\int main(void) {
\\ struct A a;
\\}
, &[_][]const u8{
\\pub const struct_A = opaque {};
\\pub const a = @compileError("non-extern variable has opaque type");
,
\\pub extern fn main() c_int;
});
cases.add("field access is grouped if necessary",
\\unsigned long foo(unsigned long x) {
\\ return ((union{unsigned long _x}){x})._x;
\\}
, &[_][]const u8{
\\pub export fn foo(arg_x: c_ulong) c_ulong {
\\ var x = arg_x;
\\ _ = &x;
\\ const union_unnamed_1 = extern union {
\\ _x: c_ulong,
\\ };
\\ _ = &union_unnamed_1;
\\ return (union_unnamed_1{
\\ ._x = x,
\\ })._x;
\\}
});
cases.add("unnamed child types of typedef receive typedef's name",
\\typedef enum {
\\ FooA,
\\ FooB,
\\} Foo;
\\typedef struct {
\\ int a, b;
\\} Bar;
, &[_][]const u8{
\\pub const FooA: c_int = 0;
\\pub const FooB: c_int = 1;
\\pub const Foo =
++ " " ++ default_enum_type ++
\\;
\\pub const Bar = extern struct {
\\ a: c_int = @import("std").mem.zeroes(c_int),
\\ b: c_int = @import("std").mem.zeroes(c_int),
\\};
});
cases.add("if as while stmt has semicolon",
\\void foo() {
\\ while (1) if (1) {
\\ int a = 1;
\\ } else {
\\ int b = 2;
\\ }
\\ if (1) if (1) {}
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ while (true) if (true) {
\\ var a: c_int = 1;
\\ _ = &a;
\\ } else {
\\ var b: c_int = 2;
\\ _ = &b;
\\ };
\\ if (true) if (true) {};
\\}
});
cases.add("conditional operator cast to void",
\\int bar();
\\void foo() {
\\ int a;
\\ a ? a = 2 : bar();
\\}
, &[_][]const u8{
\\pub extern fn bar(...) c_int;
\\pub export fn foo() void {
\\ var a: c_int = undefined;
\\ _ = &a;
\\ if (a != 0) a = 2 else _ = bar();
\\}
});
cases.add("struct in struct init to zero",
\\struct Foo {
\\ int a;
\\ struct Bar {
\\ int a;
\\ } b;
\\} a = {};
\\#define PTR void *
, &[_][]const u8{
\\pub const struct_Bar_1 = extern struct {
\\ a: c_int = @import("std").mem.zeroes(c_int),
\\};
\\pub const struct_Foo = extern struct {
\\ a: c_int = @import("std").mem.zeroes(c_int),
\\ b: struct_Bar_1 = @import("std").mem.zeroes(struct_Bar_1),
\\};
\\pub export var a: struct_Foo = struct_Foo{
\\ .a = 0,
\\ .b = @import("std").mem.zeroes(struct_Bar_1),
\\};
,
\\pub const PTR = ?*anyopaque;
});
cases.add("scoped record",
\\void foo() {
\\ struct Foo {
\\ int A;
\\ int B;
\\ int C;
\\ };
\\ struct Foo a = {0};
\\ {
\\ struct Foo {
\\ int A;
\\ int B;
\\ int C;
\\ };
\\ struct Foo a = {0};
\\ }
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ const struct_Foo = extern struct {
\\ A: c_int = @import("std").mem.zeroes(c_int),
\\ B: c_int = @import("std").mem.zeroes(c_int),
\\ C: c_int = @import("std").mem.zeroes(c_int),
\\ };
\\ _ = &struct_Foo;
\\ var a: struct_Foo = struct_Foo{
\\ .A = @as(c_int, 0),
\\ .B = 0,
\\ .C = 0,
\\ };
\\ _ = &a;
\\ {
\\ const struct_Foo_1 = extern struct {
\\ A: c_int = @import("std").mem.zeroes(c_int),
\\ B: c_int = @import("std").mem.zeroes(c_int),
\\ C: c_int = @import("std").mem.zeroes(c_int),
\\ };
\\ _ = &struct_Foo_1;
\\ var a_2: struct_Foo_1 = struct_Foo_1{
\\ .A = @as(c_int, 0),
\\ .B = 0,
\\ .C = 0,
\\ };
\\ _ = &a_2;
\\ }
\\}
});
cases.add("scoped typedef",
\\void foo() {
\\ typedef union {
\\ int A;
\\ int B;
\\ int C;
\\ } Foo;
\\ Foo a = {0};
\\ {
\\ typedef union {
\\ int A;
\\ int B;
\\ int C;
\\ } Foo;
\\ Foo a = {0};
\\ }
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ const union_unnamed_1 = extern union {
\\ A: c_int,
\\ B: c_int,
\\ C: c_int,
\\ };
\\ _ = &union_unnamed_1;
\\ const Foo = union_unnamed_1;
\\ _ = &Foo;
\\ var a: Foo = Foo{
\\ .A = @as(c_int, 0),
\\ };
\\ _ = &a;
\\ {
\\ const union_unnamed_2 = extern union {
\\ A: c_int,
\\ B: c_int,
\\ C: c_int,
\\ };
\\ _ = &union_unnamed_2;
\\ const Foo_1 = union_unnamed_2;
\\ _ = &Foo_1;
\\ var a_2: Foo_1 = Foo_1{
\\ .A = @as(c_int, 0),
\\ };
\\ _ = &a_2;
\\ }
\\}
});
cases.add("use cast param as macro fn return type",
2021-06-12 14:54:39 +01:00
\\#include <stdint.h>
\\#define SYS_BASE_CACHED 0
2021-06-12 14:54:39 +01:00
\\#define MEM_PHYSICAL_TO_K0(x) (void*)((uint32_t)(x) + SYS_BASE_CACHED)
, &[_][]const u8{
\\pub inline fn MEM_PHYSICAL_TO_K0(x: anytype) ?*anyopaque {
\\ _ = &x;
\\ return @import("std").zig.c_translation.cast(?*anyopaque, @import("std").zig.c_translation.cast(u32, x) + SYS_BASE_CACHED);
\\}
});
cases.add("variadic function demoted to extern",
\\int foo(int bar, ...) {
\\ return 1;
\\}
, &[_][]const u8{
\\warning: TODO unable to translate variadic function, demoted to extern
\\pub extern fn foo(bar: c_int, ...) c_int;
});
cases.add("pointer to opaque demoted struct",
\\typedef struct {
\\ _Atomic int foo;
\\} Foo;
\\
\\typedef struct {
\\ Foo *bar;
\\} Bar;
, &[_][]const u8{
\\source.h:1:9: warning: struct demoted to opaque type - unable to translate type of field foo
\\pub const Foo = opaque {};
\\pub const Bar = extern struct {
\\ bar: ?*Foo = @import("std").mem.zeroes(?*Foo),
\\};
});
cases.add("macro expressions respect C operator precedence",
\\int *foo = 0;
\\#define FOO *((foo) + 2)
\\#define VALUE (1 + 2 * 3 + 4 * 5 + 6 << 7 | 8 == 9)
\\#define _AL_READ3BYTES(p) ((*(unsigned char *)(p)) \
\\ | (*((unsigned char *)(p) + 1) << 8) \
\\ | (*((unsigned char *)(p) + 2) << 16))
, &[_][]const u8{
2021-03-05 18:42:21 +00:00
\\pub const FOO = (foo + @as(c_int, 2)).*;
2020-10-31 10:21:49 +00:00
,
\\pub const VALUE = ((((@as(c_int, 1) + (@as(c_int, 2) * @as(c_int, 3))) + (@as(c_int, 4) * @as(c_int, 5))) + @as(c_int, 6)) << @as(c_int, 7)) | @intFromBool(@as(c_int, 8) == @as(c_int, 9));
2020-10-31 10:21:49 +00:00
,
\\pub inline fn _AL_READ3BYTES(p: anytype) @TypeOf((@import("std").zig.c_translation.cast([*c]u8, p).* | ((@import("std").zig.c_translation.cast([*c]u8, p) + @as(c_int, 1)).* << @as(c_int, 8))) | ((@import("std").zig.c_translation.cast([*c]u8, p) + @as(c_int, 2)).* << @as(c_int, 16))) {
\\ _ = &p;
\\ return (@import("std").zig.c_translation.cast([*c]u8, p).* | ((@import("std").zig.c_translation.cast([*c]u8, p) + @as(c_int, 1)).* << @as(c_int, 8))) | ((@import("std").zig.c_translation.cast([*c]u8, p) + @as(c_int, 2)).* << @as(c_int, 16));
\\}
});
cases.add("static variable in block scope",
\\float bar;
\\int foo() {
\\ _Thread_local static int bar = 2;
\\}
, &[_][]const u8{
\\pub export var bar: f32 = @import("std").mem.zeroes(f32);
\\pub export fn foo() c_int {
\\ const bar_1 = struct {
\\ threadlocal var static: c_int = 2;
\\ };
\\ _ = &bar_1;
\\ return 0;
\\}
});
cases.add("missing return stmt",
\\int foo() {}
\\int bar() {
\\ int a = 2;
\\}
\\int baz() {
\\ return 0;
\\}
, &[_][]const u8{
\\pub export fn foo() c_int {
\\ return 0;
\\}
\\pub export fn bar() c_int {
\\ var a: c_int = 2;
\\ _ = &a;
\\ return 0;
\\}
\\pub export fn baz() c_int {
\\ return 0;
\\}
});
cases.add("alignof",
\\void main() {
\\ int a = _Alignof(int);
\\}
, &[_][]const u8{
\\pub export fn main() void {
\\ var a: c_int = @as(c_int, @bitCast(@as(c_uint, @truncate(@alignOf(c_int)))));
\\ _ = &a;
\\}
});
cases.add("initializer list macro",
\\typedef struct Color {
\\ unsigned char r;
\\ unsigned char g;
\\ unsigned char b;
\\ unsigned char a;
\\} Color;
\\#define CLITERAL(type) (type)
\\#define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 } // Light Gray
\\typedef struct boom_t
\\{
\\ int i1;
\\} boom_t;
\\#define FOO ((boom_t){1})
\\typedef struct { float x; } MyCStruct;
\\#define A(_x) (MyCStruct) { .x = (_x) }
\\#define B A(0.f)
, &[_][]const u8{
\\pub const struct_Color = extern struct {
\\ r: u8 = @import("std").mem.zeroes(u8),
\\ g: u8 = @import("std").mem.zeroes(u8),
\\ b: u8 = @import("std").mem.zeroes(u8),
\\ a: u8 = @import("std").mem.zeroes(u8),
\\};
\\pub const Color = struct_Color;
2020-10-31 10:21:49 +00:00
,
\\pub inline fn CLITERAL(@"type": anytype) @TypeOf(@"type") {
\\ _ = &@"type";
\\ return @"type";
\\}
2020-10-31 10:21:49 +00:00
,
2021-03-05 18:42:21 +00:00
\\pub const LIGHTGRAY = @import("std").mem.zeroInit(CLITERAL(Color), .{ @as(c_int, 200), @as(c_int, 200), @as(c_int, 200), @as(c_int, 255) });
,
\\pub const struct_boom_t = extern struct {
\\ i1: c_int = @import("std").mem.zeroes(c_int),
\\};
\\pub const boom_t = struct_boom_t;
,
2021-03-05 18:42:21 +00:00
\\pub const FOO = @import("std").mem.zeroInit(boom_t, .{@as(c_int, 1)});
,
\\pub const MyCStruct = extern struct {
\\ x: f32 = @import("std").mem.zeroes(f32),
\\};
,
\\pub inline fn A(_x: anytype) MyCStruct {
\\ _ = &_x;
\\ return @import("std").mem.zeroInit(MyCStruct, .{
\\ .x = _x,
\\ });
\\}
,
2023-11-24 18:11:11 +00:00
\\pub const B = A(@as(f32, 0));
});
cases.add("complex switch",
\\int main() {
\\ int i = 2;
\\ switch (i) {
\\ case 0: {
\\ case 2:{
\\ i += 2;}
\\ i += 1;
\\ }
\\ }
\\}
, &[_][]const u8{ // TODO properly translate this
\\source.h:5:13: warning: TODO complex switch
,
\\source.h:1:5: warning: unable to translate function, demoted to extern
\\pub extern fn main() c_int;
});
2020-03-12 15:14:01 +00:00
cases.add("correct semicolon after infixop",
\\#define _IO_ERR_SEEN 0
2020-03-12 15:14:01 +00:00
\\#define __ferror_unlocked_body(_fp) (((_fp)->_flags & _IO_ERR_SEEN) != 0)
, &[_][]const u8{
\\pub inline fn __ferror_unlocked_body(_fp: anytype) @TypeOf((_fp.*._flags & _IO_ERR_SEEN) != @as(c_int, 0)) {
\\ _ = &_fp;
2021-03-05 18:42:21 +00:00
\\ return (_fp.*._flags & _IO_ERR_SEEN) != @as(c_int, 0);
2020-03-12 15:14:01 +00:00
\\}
});
2020-03-12 12:18:41 +00:00
cases.add("c booleans are just ints",
\\#define FOO(x) ((x >= 0) + (x >= 0))
\\#define BAR 1 && 2 > 4
, &[_][]const u8{
\\pub inline fn FOO(x: anytype) @TypeOf(@intFromBool(x >= @as(c_int, 0)) + @intFromBool(x >= @as(c_int, 0))) {
\\ _ = &x;
\\ return @intFromBool(x >= @as(c_int, 0)) + @intFromBool(x >= @as(c_int, 0));
2020-03-12 12:18:41 +00:00
\\}
2020-10-31 10:21:49 +00:00
,
2021-03-05 18:42:21 +00:00
\\pub const BAR = (@as(c_int, 1) != 0) and (@as(c_int, 2) > @as(c_int, 4));
2020-03-12 12:18:41 +00:00
});
cases.add("struct with aligned fields",
\\struct foo {
\\ __attribute__((aligned(1))) short bar;
\\};
, &[_][]const u8{
\\pub const struct_foo = extern struct {
\\ bar: c_short align(1) = @import("std").mem.zeroes(c_short),
\\};
});
cases.add("struct with flexible array",
2020-03-08 10:07:26 +00:00
\\struct foo { int x; int y[]; };
\\struct bar { int x; int y[0]; };
, &[_][]const u8{
\\pub const struct_foo = extern struct {
\\ x: c_int align(4) = @import("std").mem.zeroes(c_int),
\\ pub fn y(self: anytype) @import("std").zig.c_translation.FlexibleArrayType(@TypeOf(self), c_int) {
\\ const Intermediate = @import("std").zig.c_translation.FlexibleArrayType(@TypeOf(self), u8);
\\ const ReturnType = @import("std").zig.c_translation.FlexibleArrayType(@TypeOf(self), c_int);
\\ return @as(ReturnType, @ptrCast(@alignCast(@as(Intermediate, @ptrCast(self)) + 4)));
\\ }
\\};
\\pub const struct_bar = extern struct {
\\ x: c_int align(4) = @import("std").mem.zeroes(c_int),
\\ pub fn y(self: anytype) @import("std").zig.c_translation.FlexibleArrayType(@TypeOf(self), c_int) {
\\ const Intermediate = @import("std").zig.c_translation.FlexibleArrayType(@TypeOf(self), u8);
\\ const ReturnType = @import("std").zig.c_translation.FlexibleArrayType(@TypeOf(self), c_int);
\\ return @as(ReturnType, @ptrCast(@alignCast(@as(Intermediate, @ptrCast(self)) + 4)));
\\ }
\\};
2020-03-08 10:07:26 +00:00
});
cases.add("nested loops without blocks",
\\void foo() {
\\ while (0) while (0) {}
\\ for (;;) while (0);
\\ for (;;) do {} while (0);
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ while (false) while (false) {};
\\ while (true) while (false) {};
\\ while (true) while (true) {
\\ if (!false) break;
\\ };
\\}
});
2020-03-05 09:22:50 +00:00
cases.add("macro comma operator",
\\#define foo (foo, bar)
\\int baz(int x, int y) { return 0; }
2020-03-06 09:03:56 +00:00
\\#define bar(x) (&x, +3, 4 == 4, 5 * 6, baz(1, 2), 2 % 2, baz(1,2))
2020-03-05 09:22:50 +00:00
, &[_][]const u8{
\\pub const foo = blk_1: {
\\ _ = &foo;
\\ break :blk_1 bar;
2020-03-05 09:22:50 +00:00
\\};
2020-10-31 10:21:49 +00:00
,
\\pub inline fn bar(x: anytype) @TypeOf(baz(@as(c_int, 1), @as(c_int, 2))) {
\\ _ = &x;
\\ return blk_1: {
\\ _ = &x;
2021-03-05 18:42:21 +00:00
\\ _ = @as(c_int, 3);
\\ _ = @as(c_int, 4) == @as(c_int, 4);
\\ _ = @as(c_int, 5) * @as(c_int, 6);
\\ _ = baz(@as(c_int, 1), @as(c_int, 2));
2022-11-03 12:07:00 +00:00
\\ _ = @import("std").zig.c_translation.MacroArithmetic.rem(@as(c_int, 2), @as(c_int, 2));
\\ break :blk_1 baz(@as(c_int, 1), @as(c_int, 2));
2020-03-05 09:22:50 +00:00
\\ };
\\}
});
cases.add("macro keyword define",
\\#define foo 1
\\#define inline 2
, &[_][]const u8{
2021-03-05 18:42:21 +00:00
\\pub const foo = @as(c_int, 1);
2020-10-31 10:21:49 +00:00
,
2021-03-05 18:42:21 +00:00
\\pub const @"inline" = @as(c_int, 2);
});
2020-02-14 21:41:18 +00:00
cases.add("macro line continuation",
\\int BAR = 0;
2020-02-14 21:41:18 +00:00
\\#define FOO -\
\\BAR
, &[_][]const u8{
\\pub const FOO = -BAR;
});
cases.add("struct with atomic field",
\\struct arcan_shmif_cont {
\\ struct arcan_shmif_page* addr;
\\};
\\struct arcan_shmif_page {
\\ volatile _Atomic int abufused[12];
\\};
, &[_][]const u8{
\\source.h:4:8: warning: struct demoted to opaque type - unable to translate type of field abufused
\\pub const struct_arcan_shmif_page = opaque {};
\\pub const struct_arcan_shmif_cont = extern struct {
\\ addr: ?*struct_arcan_shmif_page = @import("std").mem.zeroes(?*struct_arcan_shmif_page),
\\};
});
cases.add("function prototype translated as optional",
\\typedef void (*fnptr_ty)(void);
\\typedef __attribute__((cdecl)) void (*fnptr_attr_ty)(void);
\\struct foo {
\\ __attribute__((cdecl)) void (*foo)(void);
\\ void (*bar)(void);
\\ fnptr_ty baz;
\\ fnptr_attr_ty qux;
\\};
, &[_][]const u8{
\\pub const fnptr_ty = ?*const fn () callconv(.C) void;
\\pub const fnptr_attr_ty = ?*const fn () callconv(.C) void;
\\pub const struct_foo = extern struct {
\\ foo: ?*const fn () callconv(.C) void = @import("std").mem.zeroes(?*const fn () callconv(.C) void),
\\ bar: ?*const fn () callconv(.C) void = @import("std").mem.zeroes(?*const fn () callconv(.C) void),
\\ baz: fnptr_ty = @import("std").mem.zeroes(fnptr_ty),
\\ qux: fnptr_attr_ty = @import("std").mem.zeroes(fnptr_attr_ty),
\\};
});
cases.add("function prototype with parenthesis",
\\void (f0) (void *L);
\\void ((f1)) (void *L);
\\void (((f2))) (void *L);
, &[_][]const u8{
\\pub extern fn f0(L: ?*anyopaque) void;
\\pub extern fn f1(L: ?*anyopaque) void;
\\pub extern fn f2(L: ?*anyopaque) void;
});
cases.add("array initializer w/ typedef",
\\typedef unsigned char uuid_t[16];
\\static const uuid_t UUID_NULL __attribute__ ((unused)) = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
, &[_][]const u8{
\\pub const uuid_t = [16]u8;
2020-01-30 18:53:35 +00:00
\\pub const UUID_NULL: uuid_t = [16]u8{
\\ 0,
\\ 0,
\\ 0,
\\ 0,
\\ 0,
\\ 0,
\\ 0,
\\ 0,
\\ 0,
\\ 0,
\\ 0,
\\ 0,
\\ 0,
\\ 0,
\\ 0,
\\ 0,
\\};
});
cases.add("empty declaration",
\\;
, &[_][]const u8{""});
cases.add("#define hex literal with capital X",
\\#define VAL 0XF00D
, &[_][]const u8{
2023-11-24 18:11:11 +00:00
\\pub const VAL = @import("std").zig.c_translation.promoteIntLiteral(c_int, 0xF00D, .hex);
});
2020-01-05 16:47:29 +00:00
cases.add("anonymous struct & unions",
\\typedef struct {
\\ union {
\\ char x;
\\ struct { int y; };
\\ };
\\} outer;
\\void foo(outer *x) { x->y = x->x; }
, &[_][]const u8{
\\const struct_unnamed_2 = extern struct {
\\ y: c_int = @import("std").mem.zeroes(c_int),
2020-01-05 16:47:29 +00:00
\\};
\\const union_unnamed_1 = extern union {
2020-01-05 16:47:29 +00:00
\\ x: u8,
\\ unnamed_0: struct_unnamed_2,
2020-01-05 16:47:29 +00:00
\\};
\\pub const outer = extern struct {
\\ unnamed_0: union_unnamed_1 = @import("std").mem.zeroes(union_unnamed_1),
2020-01-05 16:47:29 +00:00
\\};
\\pub export fn foo(arg_x: [*c]outer) void {
\\ var x = arg_x;
\\ _ = &x;
\\ x.*.unnamed_0.unnamed_0.y = @as(c_int, @bitCast(@as(c_uint, x.*.unnamed_0.x)));
2020-01-05 16:47:29 +00:00
\\}
});
cases.add("union initializer",
\\union { int x; char c[4]; }
\\ ua = {1},
\\ ub = {.c={'a','b','b','a'}};
, &[_][]const u8{
\\const union_unnamed_1 = extern union {
\\ x: c_int,
\\ c: [4]u8,
\\};
\\pub export var ua: union_unnamed_1 = union_unnamed_1{
\\ .x = @as(c_int, 1),
\\};
\\pub export var ub: union_unnamed_1 = union_unnamed_1{
2020-01-30 18:53:35 +00:00
\\ .c = [4]u8{
\\ 'a',
\\ 'b',
\\ 'b',
\\ 'a',
\\ },
\\};
});
cases.add("struct initializer - simple",
2020-01-05 10:39:01 +00:00
\\typedef struct { int x; } foo;
\\struct {double x,y,z;} s0 = {1.2, 1.3};
\\struct {int sec,min,hour,day,mon,year;} s1 = {.day=31,12,2014,.sec=30,15,17};
\\struct {int x,y;} s2 = {.y = 2, .x=1};
2020-01-05 10:39:01 +00:00
\\foo s3 = { 123 };
, &[_][]const u8{
\\pub const foo = extern struct {
\\ x: c_int = @import("std").mem.zeroes(c_int),
2020-01-05 10:39:01 +00:00
\\};
\\const struct_unnamed_1 = extern struct {
\\ x: f64 = @import("std").mem.zeroes(f64),
\\ y: f64 = @import("std").mem.zeroes(f64),
\\ z: f64 = @import("std").mem.zeroes(f64),
\\};
\\pub export var s0: struct_unnamed_1 = struct_unnamed_1{
\\ .x = 1.2,
\\ .y = 1.3,
\\ .z = 0,
\\};
\\const struct_unnamed_2 = extern struct {
\\ sec: c_int = @import("std").mem.zeroes(c_int),
\\ min: c_int = @import("std").mem.zeroes(c_int),
\\ hour: c_int = @import("std").mem.zeroes(c_int),
\\ day: c_int = @import("std").mem.zeroes(c_int),
\\ mon: c_int = @import("std").mem.zeroes(c_int),
\\ year: c_int = @import("std").mem.zeroes(c_int),
\\};
\\pub export var s1: struct_unnamed_2 = struct_unnamed_2{
\\ .sec = @as(c_int, 30),
\\ .min = @as(c_int, 15),
\\ .hour = @as(c_int, 17),
\\ .day = @as(c_int, 31),
\\ .mon = @as(c_int, 12),
\\ .year = @as(c_int, 2014),
\\};
\\const struct_unnamed_3 = extern struct {
\\ x: c_int = @import("std").mem.zeroes(c_int),
\\ y: c_int = @import("std").mem.zeroes(c_int),
\\};
\\pub export var s2: struct_unnamed_3 = struct_unnamed_3{
\\ .x = @as(c_int, 1),
\\ .y = @as(c_int, 2),
\\};
2020-01-05 10:39:01 +00:00
\\pub export var s3: foo = foo{
\\ .x = @as(c_int, 123),
\\};
});
cases.add("simple ptrCast for casts between opaque types",
\\struct opaque;
\\struct opaque_2;
\\void function(struct opaque *opaque) {
\\ struct opaque_2 *cast = (struct opaque_2 *)opaque;
\\}
, &[_][]const u8{
\\pub const struct_opaque = opaque {};
\\pub const struct_opaque_2 = opaque {};
\\pub export fn function(arg_opaque_1: ?*struct_opaque) void {
\\ var opaque_1 = arg_opaque_1;
\\ _ = &opaque_1;
\\ var cast: ?*struct_opaque_2 = @as(?*struct_opaque_2, @ptrCast(opaque_1));
\\ _ = &cast;
\\}
});
translate-c: packed struct implies align(1) on every field Superceeds PR #12735 (now supporting all packed structs in GNU C) Fixes issue #12733 This stops translating C packed struct as a Zig packed struct. Instead use a regular `extern struct` with `align(1)`. This is because (as @Vexu explained) Zig packed structs are really just integers (not structs). Alignment issue is more complicated. I think @ifreund was the first to notice it in his comment on PR #12735 Justification of my interpretion of the C(lang) behavior comes from a careful reading of the GCC docs for type & variable attributes: (clang emulates gnu's packed attribute here) The final line of the documentation for __attribute__ ((aligned)) [on types] says: > When used on a struct, or struct member, *the aligned attribute can only increase the alignment*; in order to decrease it, the packed attribute must be specified as well. This implies that GCC uses the `packed` attribute for alignment purposes in addition to eliminating padding. The documentation for __attribute__((packed)) [on types], states: > This attribute, attached to a struct, union, or C++ class type definition, specifies that each of its members (other than zero-width bit-fields) is placed to minimize the memory required. **This is equivalent to specifying the packed attribute on each of the members**. The key is resolving this indirection, and looking at the documentation for __attribute__((packed)) [on fields (wierdly under "variables" section)]: > The packed attribute specifies that a **structure member should have the smallest possible alignment** — one bit for a bit-field and one byte otherwise, unless a larger value is specified with the aligned attribute. The attribute does not apply to non-member objects. Furthermore, alignment is the only effect of the packed attribute mentioned in the GCC docs (for "common" architecture). Based on this, it seems safe to completely substitute C 'packed' with Zig 'align(1)'. Target-specific or undocumented behavior potentially changes this. Unfortunately, the current implementation of `translate-c` translates as `packed struct` without alignment info. Because Zig packed structs are really integers (as mentioned above), they are the wrong interpretation and we should be using 'extern struct'. Running `translate-c` on the following code: ```c struct foo { char a; int b; } __attribute__((packed)); struct bar { char a; int b; short c; __attribute__((aligned(8))) long d; } __attribute__((packed)); ``` Previously used a 'packed struct' (which was not FFI-safe on stage1). After applying this change, the translated structures have align(1) explicitly applied to all of their fields AS EXPECTED (unless explicitly overriden). This makes Zig behavior for `tranlsate-c` consistent with clang/GCC. Here is the newly produced (correct) output for the above example: ```zig pub const struct_foo = extern struct { a: u8 align(1), b: c_int align(1), }; pub const struct_bar = extern struct { a: u8 align(1), b: c_int align(1), c: c_short align(1), d: c_long align(8), }; ``` Also note for reference: Since the last stable release (0.9.1), there was a change in the language spec related to the alignment of packed structures. The docs for Zig 0.9.1 read: > Packed structs have 1-byte alignment. So the old behavior of translate-c (not specifying any alignment) was possibly correct back then. However the current docs read: > Packed structs have the same alignment as their backing integer Suggsestive both to the change to an integer-backed representation which is incompatible with C's notation.
2022-09-05 01:16:26 +01:00
cases.add("struct initializer - packed",
\\struct {int x,y,z;} __attribute__((packed)) s0 = {1, 2};
, &[_][]const u8{
\\const struct_unnamed_1 = extern struct {
\\ x: c_int align(1) = @import("std").mem.zeroes(c_int),
\\ y: c_int align(1) = @import("std").mem.zeroes(c_int),
\\ z: c_int align(1) = @import("std").mem.zeroes(c_int),
translate-c: packed struct implies align(1) on every field Superceeds PR #12735 (now supporting all packed structs in GNU C) Fixes issue #12733 This stops translating C packed struct as a Zig packed struct. Instead use a regular `extern struct` with `align(1)`. This is because (as @Vexu explained) Zig packed structs are really just integers (not structs). Alignment issue is more complicated. I think @ifreund was the first to notice it in his comment on PR #12735 Justification of my interpretion of the C(lang) behavior comes from a careful reading of the GCC docs for type & variable attributes: (clang emulates gnu's packed attribute here) The final line of the documentation for __attribute__ ((aligned)) [on types] says: > When used on a struct, or struct member, *the aligned attribute can only increase the alignment*; in order to decrease it, the packed attribute must be specified as well. This implies that GCC uses the `packed` attribute for alignment purposes in addition to eliminating padding. The documentation for __attribute__((packed)) [on types], states: > This attribute, attached to a struct, union, or C++ class type definition, specifies that each of its members (other than zero-width bit-fields) is placed to minimize the memory required. **This is equivalent to specifying the packed attribute on each of the members**. The key is resolving this indirection, and looking at the documentation for __attribute__((packed)) [on fields (wierdly under "variables" section)]: > The packed attribute specifies that a **structure member should have the smallest possible alignment** — one bit for a bit-field and one byte otherwise, unless a larger value is specified with the aligned attribute. The attribute does not apply to non-member objects. Furthermore, alignment is the only effect of the packed attribute mentioned in the GCC docs (for "common" architecture). Based on this, it seems safe to completely substitute C 'packed' with Zig 'align(1)'. Target-specific or undocumented behavior potentially changes this. Unfortunately, the current implementation of `translate-c` translates as `packed struct` without alignment info. Because Zig packed structs are really integers (as mentioned above), they are the wrong interpretation and we should be using 'extern struct'. Running `translate-c` on the following code: ```c struct foo { char a; int b; } __attribute__((packed)); struct bar { char a; int b; short c; __attribute__((aligned(8))) long d; } __attribute__((packed)); ``` Previously used a 'packed struct' (which was not FFI-safe on stage1). After applying this change, the translated structures have align(1) explicitly applied to all of their fields AS EXPECTED (unless explicitly overriden). This makes Zig behavior for `tranlsate-c` consistent with clang/GCC. Here is the newly produced (correct) output for the above example: ```zig pub const struct_foo = extern struct { a: u8 align(1), b: c_int align(1), }; pub const struct_bar = extern struct { a: u8 align(1), b: c_int align(1), c: c_short align(1), d: c_long align(8), }; ``` Also note for reference: Since the last stable release (0.9.1), there was a change in the language spec related to the alignment of packed structures. The docs for Zig 0.9.1 read: > Packed structs have 1-byte alignment. So the old behavior of translate-c (not specifying any alignment) was possibly correct back then. However the current docs read: > Packed structs have the same alignment as their backing integer Suggsestive both to the change to an integer-backed representation which is incompatible with C's notation.
2022-09-05 01:16:26 +01:00
\\};
\\pub export var s0: struct_unnamed_1 = struct_unnamed_1{
\\ .x = @as(c_int, 1),
\\ .y = @as(c_int, 2),
\\ .z = 0,
\\};
});
2020-01-02 10:33:26 +00:00
cases.add("linksection() attribute",
2020-01-02 11:13:05 +00:00
\\// Use the "segment,section" format to make this test pass when
\\// targeting the mach-o binary format
\\__attribute__ ((__section__("NEAR,.data")))
2020-01-02 10:33:26 +00:00
\\extern char my_array[16];
2020-01-02 11:13:05 +00:00
\\__attribute__ ((__section__("NEAR,.data")))
2020-01-02 10:33:26 +00:00
\\void my_fn(void) { }
, &[_][]const u8{
2020-01-02 11:13:05 +00:00
\\pub extern var my_array: [16]u8 linksection("NEAR,.data");
\\pub export fn my_fn() linksection("NEAR,.data") void {}
2020-01-02 10:33:26 +00:00
});
cases.add("simple function prototypes",
\\void __attribute__((noreturn)) foo(void);
\\int bar(void);
, &[_][]const u8{
\\pub extern fn foo() noreturn;
\\pub extern fn bar() c_int;
});
cases.add("simple var decls",
\\void foo(void) {
\\ int a;
\\ char b = 123;
\\ const int c;
\\ const unsigned d = 440;
\\ int e = 10;
\\ unsigned int f = 10u;
\\}
, &[_][]const u8{
2019-12-12 14:56:21 +00:00
\\pub export fn foo() void {
\\ var a: c_int = undefined;
\\ _ = &a;
\\ var b: u8 = 123;
\\ _ = &b;
\\ const c: c_int = undefined;
\\ _ = &c;
\\ const d: c_uint = @as(c_uint, @bitCast(@as(c_int, 440)));
\\ _ = &d;
\\ var e: c_int = 10;
\\ _ = &e;
\\ var f: c_uint = 10;
\\ _ = &f;
\\}
});
cases.add("ignore result, explicit function arguments",
\\void foo(void) {
\\ int a;
\\ 1;
\\ "hey";
\\ 1 + 1;
\\ 1 - 1;
\\ a = 1;
\\}
, &[_][]const u8{
2019-12-12 14:56:21 +00:00
\\pub export fn foo() void {
\\ var a: c_int = undefined;
\\ _ = &a;
\\ _ = @as(c_int, 1);
\\ _ = "hey";
\\ _ = @as(c_int, 1) + @as(c_int, 1);
\\ _ = @as(c_int, 1) - @as(c_int, 1);
\\ a = 1;
\\}
});
cases.add("function with no prototype",
\\int foo() {
\\ return 5;
\\}
, &[_][]const u8{
\\pub export fn foo() c_int {
\\ return 5;
\\}
});
cases.add("variables",
2019-12-12 14:56:21 +00:00
\\extern int extern_var;
\\static const int int_var = 13;
\\int foo;
2019-12-12 14:56:21 +00:00
, &[_][]const u8{
\\pub extern var extern_var: c_int;
\\pub const int_var: c_int = 13;
\\pub export var foo: c_int = @import("std").mem.zeroes(c_int);
2019-12-12 14:56:21 +00:00
});
cases.add("const ptr initializer",
2019-12-12 14:56:21 +00:00
\\static const char *v0 = "0.0.0";
, &[_][]const u8{
\\pub var v0: [*c]const u8 = "0.0.0";
});
cases.add("static incomplete array inside function",
2019-12-12 19:14:55 +00:00
\\void foo(void) {
\\ static const char v2[] = "2.2.2";
\\}
2019-12-12 14:56:21 +00:00
, &[_][]const u8{
\\pub export fn foo() void {
\\ const v2 = struct {
\\ const static: [5:0]u8 = "2.2.2".*;
\\ };
\\ _ = &v2;
\\}
2019-12-12 14:56:21 +00:00
});
cases.add("simple function definition",
2019-12-12 14:56:21 +00:00
\\void foo(void) {}
\\static void bar(void) {}
, &[_][]const u8{
\\pub export fn foo() void {}
2019-12-28 20:28:18 +00:00
\\pub fn bar() callconv(.C) void {}
2019-12-12 14:56:21 +00:00
});
cases.add("typedef void",
2019-12-13 15:36:18 +00:00
\\typedef void Foo;
\\Foo fun(Foo *a);
, &[_][]const u8{
\\pub const Foo = anyopaque;
2020-10-31 10:21:49 +00:00
,
\\pub extern fn fun(a: ?*Foo) void;
2019-12-13 15:36:18 +00:00
});
cases.add("duplicate typedef",
2019-12-13 15:36:18 +00:00
\\typedef long foo;
\\typedef int bar;
\\typedef long foo;
\\typedef int baz;
, &[_][]const u8{
\\pub const foo = c_long;
\\pub const bar = c_int;
\\pub const baz = c_int;
});
cases.add("casting pointers to ints and ints to pointers",
\\void foo(void);
\\void bar(void) {
\\ void *func_ptr = foo;
\\ void (*typed_func_ptr)(void) = (void (*)(void)) (unsigned long) func_ptr;
\\}
, &[_][]const u8{
\\pub extern fn foo() void;
\\pub export fn bar() void {
\\ var func_ptr: ?*anyopaque = @as(?*anyopaque, @ptrCast(&foo));
\\ _ = &func_ptr;
\\ var typed_func_ptr: ?*const fn () callconv(.C) void = @as(?*const fn () callconv(.C) void, @ptrFromInt(@as(c_ulong, @intCast(@intFromPtr(func_ptr)))));
\\ _ = &typed_func_ptr;
\\}
});
2019-12-13 15:36:18 +00:00
cases.add("noreturn attribute",
2019-12-13 15:36:18 +00:00
\\void foo(void) __attribute__((noreturn));
, &[_][]const u8{
\\pub extern fn foo() noreturn;
});
cases.add("always_inline attribute",
\\__attribute__((always_inline)) int foo() {
\\ return 5;
\\}
, &[_][]const u8{
\\pub inline fn foo() c_int {
\\ return 5;
\\}
});
cases.add("add, sub, mul, div, rem",
2019-12-19 22:10:25 +00:00
\\int s() {
\\ int a, b, c;
2019-12-13 15:36:18 +00:00
\\ c = a + b;
\\ c = a - b;
\\ c = a * b;
\\ c = a / b;
\\ c = a % b;
\\}
2019-12-19 22:10:25 +00:00
\\unsigned u() {
\\ unsigned a, b, c;
2019-12-13 15:36:18 +00:00
\\ c = a + b;
\\ c = a - b;
\\ c = a * b;
\\ c = a / b;
\\ c = a % b;
\\}
, &[_][]const u8{
2019-12-19 22:10:25 +00:00
\\pub export fn s() c_int {
\\ var a: c_int = undefined;
\\ _ = &a;
2019-12-19 22:10:25 +00:00
\\ var b: c_int = undefined;
\\ _ = &b;
2019-12-13 15:36:18 +00:00
\\ var c: c_int = undefined;
\\ _ = &c;
\\ c = a + b;
\\ c = a - b;
\\ c = a * b;
2019-12-13 15:36:18 +00:00
\\ c = @divTrunc(a, b);
\\ c = @import("std").zig.c_translation.signedRemainder(a, b);
\\ return 0;
2019-12-13 15:36:18 +00:00
\\}
2019-12-19 22:10:25 +00:00
\\pub export fn u() c_uint {
\\ var a: c_uint = undefined;
\\ _ = &a;
2019-12-19 22:10:25 +00:00
\\ var b: c_uint = undefined;
\\ _ = &b;
2019-12-13 15:36:18 +00:00
\\ var c: c_uint = undefined;
\\ _ = &c;
\\ c = a +% b;
\\ c = a -% b;
\\ c = a *% b;
\\ c = a / b;
\\ c = a % b;
\\ return 0;
2019-12-13 15:36:18 +00:00
\\}
});
cases.add("typedef of function in struct field",
\\typedef void lws_callback_function(void);
\\struct Foo {
\\ void (*func)(void);
\\ lws_callback_function *callback_http;
\\};
, &[_][]const u8{
\\pub const lws_callback_function = fn () callconv(.C) void;
\\pub const struct_Foo = extern struct {
\\ func: ?*const fn () callconv(.C) void = @import("std").mem.zeroes(?*const fn () callconv(.C) void),
\\ callback_http: ?*const lws_callback_function = @import("std").mem.zeroes(?*const lws_callback_function),
\\};
});
2019-12-16 05:43:18 +00:00
cases.add("pointer to struct demoted to opaque due to bit fields",
\\struct Foo {
\\ unsigned int: 1;
\\};
\\struct Bar {
\\ struct Foo *foo;
\\};
, &[_][]const u8{
\\pub const struct_Foo = opaque {};
,
\\pub const struct_Bar = extern struct {
\\ foo: ?*struct_Foo = @import("std").mem.zeroes(?*struct_Foo),
\\};
});
cases.add("macro with left shift",
2019-12-16 05:43:18 +00:00
\\#define REDISMODULE_READ (1<<0)
, &[_][]const u8{
2021-03-05 18:42:21 +00:00
\\pub const REDISMODULE_READ = @as(c_int, 1) << @as(c_int, 0);
2019-12-16 05:43:18 +00:00
});
2019-12-27 14:37:32 +00:00
cases.add("macro with right shift",
\\#define FLASH_SIZE 0x200000UL /* 2 MB */
\\#define FLASH_BANK_SIZE (FLASH_SIZE >> 1) /* 1 MB */
, &[_][]const u8{
\\pub const FLASH_SIZE = @as(c_ulong, 0x200000);
2020-10-31 10:21:49 +00:00
,
2021-03-05 18:42:21 +00:00
\\pub const FLASH_BANK_SIZE = FLASH_SIZE >> @as(c_int, 1);
2019-12-27 14:37:32 +00:00
});
cases.add("double define struct",
2019-12-16 05:43:18 +00:00
\\typedef struct Bar Bar;
\\typedef struct Foo Foo;
\\
\\struct Foo {
\\ Foo *a;
\\};
\\
\\struct Bar {
\\ Foo *a;
\\};
, &[_][]const u8{
\\pub const struct_Foo = extern struct {
\\ a: [*c]Foo = @import("std").mem.zeroes([*c]Foo),
2019-12-16 05:43:18 +00:00
\\};
2020-10-31 10:21:49 +00:00
,
2019-12-16 05:43:18 +00:00
\\pub const Foo = struct_Foo;
2020-10-31 10:21:49 +00:00
,
2019-12-16 05:43:18 +00:00
\\pub const struct_Bar = extern struct {
\\ a: [*c]Foo = @import("std").mem.zeroes([*c]Foo),
2019-12-16 05:43:18 +00:00
\\};
2020-10-31 10:21:49 +00:00
,
2019-12-16 05:43:18 +00:00
\\pub const Bar = struct_Bar;
});
cases.add("simple struct",
2019-12-16 05:43:18 +00:00
\\struct Foo {
\\ int x;
\\ char *y;
\\};
, &[_][]const u8{
\\const struct_Foo = extern struct {
\\ x: c_int = @import("std").mem.zeroes(c_int),
\\ y: [*c]u8 = @import("std").mem.zeroes([*c]u8),
2019-12-16 05:43:18 +00:00
\\};
2020-10-31 10:21:49 +00:00
,
2019-12-16 05:43:18 +00:00
\\pub const Foo = struct_Foo;
});
cases.add("self referential struct with function pointer",
\\struct Foo {
\\ void (*derp)(struct Foo *foo);
\\};
, &[_][]const u8{
\\pub const struct_Foo = extern struct {
\\ derp: ?*const fn ([*c]struct_Foo) callconv(.C) void = @import("std").mem.zeroes(?*const fn ([*c]struct_Foo) callconv(.C) void),
\\};
,
\\pub const Foo = struct_Foo;
});
2019-12-16 05:43:18 +00:00
cases.add("struct prototype used in func",
2019-12-16 05:43:18 +00:00
\\struct Foo;
\\struct Foo *some_func(struct Foo *foo, int x);
, &[_][]const u8{
\\pub const struct_Foo = opaque {};
2020-10-31 10:21:49 +00:00
,
2019-12-16 05:43:18 +00:00
\\pub extern fn some_func(foo: ?*struct_Foo, x: c_int) ?*struct_Foo;
2020-10-31 10:21:49 +00:00
,
2019-12-16 05:43:18 +00:00
\\pub const Foo = struct_Foo;
});
cases.add("#define an unsigned integer literal",
2019-12-16 05:43:18 +00:00
\\#define CHANNEL_COUNT 24
, &[_][]const u8{
2021-03-05 18:42:21 +00:00
\\pub const CHANNEL_COUNT = @as(c_int, 24);
2019-12-16 05:43:18 +00:00
});
cases.add("#define referencing another #define",
2019-12-16 05:43:18 +00:00
\\#define THING2 THING1
\\#define THING1 1234
, &[_][]const u8{
2021-03-05 18:42:21 +00:00
\\pub const THING1 = @as(c_int, 1234);
2020-10-31 10:21:49 +00:00
,
2019-12-16 05:43:18 +00:00
\\pub const THING2 = THING1;
});
cases.add("circular struct definitions",
2019-12-16 05:43:18 +00:00
\\struct Bar;
\\
\\struct Foo {
\\ struct Bar *next;
\\};
\\
\\struct Bar {
\\ struct Foo *next;
\\};
, &[_][]const u8{
\\pub const struct_Bar = extern struct {
\\ next: [*c]struct_Foo = @import("std").mem.zeroes([*c]struct_Foo),
2019-12-16 05:43:18 +00:00
\\};
2020-10-31 10:21:49 +00:00
,
2019-12-16 05:43:18 +00:00
\\pub const struct_Foo = extern struct {
\\ next: [*c]struct_Bar = @import("std").mem.zeroes([*c]struct_Bar),
2019-12-16 05:43:18 +00:00
\\};
});
cases.add("#define string",
2019-12-16 05:43:18 +00:00
\\#define foo "a string"
, &[_][]const u8{
\\pub const foo = "a string";
});
cases.add("zig keywords in C code",
2019-12-16 05:43:18 +00:00
\\struct comptime {
\\ int defer;
\\};
, &[_][]const u8{
\\pub const struct_comptime = extern struct {
\\ @"defer": c_int = @import("std").mem.zeroes(c_int),
2019-12-16 05:43:18 +00:00
\\};
2020-10-31 10:21:49 +00:00
,
2019-12-16 05:43:18 +00:00
\\pub const @"comptime" = struct_comptime;
});
cases.add("macro with parens around negative number",
2019-12-16 05:43:18 +00:00
\\#define LUA_GLOBALSINDEX (-10002)
, &[_][]const u8{
2021-03-05 18:42:21 +00:00
\\pub const LUA_GLOBALSINDEX = -@as(c_int, 10002);
2019-12-16 05:43:18 +00:00
});
cases.add(
2019-12-16 05:43:18 +00:00
"u integer suffix after 0 (zero) in macro definition",
"#define ZERO 0U",
&[_][]const u8{
"pub const ZERO = @as(c_uint, 0);",
},
);
cases.add(
2019-12-16 05:43:18 +00:00
"l integer suffix after 0 (zero) in macro definition",
"#define ZERO 0L",
&[_][]const u8{
"pub const ZERO = @as(c_long, 0);",
},
);
cases.add(
2019-12-16 05:43:18 +00:00
"ul integer suffix after 0 (zero) in macro definition",
"#define ZERO 0UL",
&[_][]const u8{
"pub const ZERO = @as(c_ulong, 0);",
},
);
cases.add(
2019-12-16 05:43:18 +00:00
"lu integer suffix after 0 (zero) in macro definition",
"#define ZERO 0LU",
&[_][]const u8{
"pub const ZERO = @as(c_ulong, 0);",
},
);
cases.add(
2019-12-16 05:43:18 +00:00
"ll integer suffix after 0 (zero) in macro definition",
"#define ZERO 0LL",
&[_][]const u8{
"pub const ZERO = @as(c_longlong, 0);",
},
);
cases.add(
2019-12-16 05:43:18 +00:00
"ull integer suffix after 0 (zero) in macro definition",
"#define ZERO 0ULL",
&[_][]const u8{
"pub const ZERO = @as(c_ulonglong, 0);",
},
);
cases.add(
2019-12-16 05:43:18 +00:00
"llu integer suffix after 0 (zero) in macro definition",
"#define ZERO 0LLU",
&[_][]const u8{
"pub const ZERO = @as(c_ulonglong, 0);",
},
);
cases.add(
2019-12-16 05:43:18 +00:00
"bitwise not on u-suffixed 0 (zero) in macro definition",
"#define NOT_ZERO (~0U)",
&[_][]const u8{
"pub const NOT_ZERO = ~@as(c_uint, 0);",
},
);
2020-02-02 08:49:51 +00:00
cases.add("float suffixes",
\\#define foo 3.14f
\\#define bar 16.e-2l
2020-02-10 14:37:37 +00:00
\\#define FOO 0.12345
\\#define BAR .12345
\\#define baz 1e1
\\#define BAZ 42e-3f
\\#define foobar -73.L
\\extern const float my_float = 1.0f;
\\extern const double my_double = 1.0;
\\extern const long double my_longdouble = 1.0l;
\\extern const long double my_extended_precision_longdouble = 1.0000000000000003l;
, &([_][]const u8{
2020-02-02 08:49:51 +00:00
"pub const foo = @as(f32, 3.14);",
2023-11-24 18:11:11 +00:00
"pub const bar = @as(c_longdouble, 16.e-2);",
"pub const FOO = @as(f64, 0.12345);",
"pub const BAR = @as(f64, 0.12345);",
"pub const baz = @as(f64, 1e1);",
"pub const BAZ = @as(f32, 42e-3);",
2023-11-24 18:11:11 +00:00
"pub const foobar = -@as(c_longdouble, 73);",
"pub export const my_float: f32 = 1.0;",
"pub export const my_double: f64 = 1.0;",
"pub export const my_longdouble: c_longdouble = 1.0;",
switch (@bitSizeOf(c_longdouble)) {
// TODO implement decimal format for f128 <https://github.com/ziglang/zig/issues/1181>
// (so that f80/f128 values not exactly representable as f64 can be emitted in decimal form)
80 => "pub export const my_extended_precision_longdouble: c_longdouble = 0x1.000000000000159ep0;",
128 => "pub export const my_extended_precision_longdouble: c_longdouble = 0x1.000000000000159e05f1e2674d21p0;",
else => "pub export const my_extended_precision_longdouble: c_longdouble = 1.0000000000000002;",
},
}));
cases.add("macro defines hexadecimal float",
\\#define FOO 0xf7p38
\\#define BAR -0X8F.BP5F
\\#define FOOBAR 0X0P+0
\\#define BAZ -0x.0a5dp+12
\\#define FOOBAZ 0xfE.P-1l
, &[_][]const u8{
"pub const FOO = @as(f64, 0xf7p38);",
"pub const BAR = -@as(f32, 0x8F.BP5);",
"pub const FOOBAR = @as(f64, 0x0P+0);",
"pub const BAZ = -@as(f64, 0x0.0a5dp+12);",
2023-11-24 18:11:11 +00:00
"pub const FOOBAZ = @as(c_longdouble, 0xfE.P-1);",
2020-02-02 08:49:51 +00:00
});
cases.add("comments",
\\#define foo 1 //foo
\\#define bar /* bar */ 2
, &[_][]const u8{
2021-03-05 18:42:21 +00:00
"pub const foo = @as(c_int, 1);",
"pub const bar = @as(c_int, 2);",
});
cases.add("string prefix",
\\#define foo L"hello"
, &[_][]const u8{
"pub const foo = \"hello\";",
});
cases.add("null statements",
\\void foo(void) {
\\ ;;;;;
\\}
, &[_][]const u8{
\\pub export fn foo() void {}
});
if (builtin.os.tag != .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 VAL01: c_int = 0;
\\pub const VAL02: c_int = 1;
\\pub const VAL03: c_int = 2;
\\pub const VAL04: c_int = 3;
\\pub const VAL05: c_int = -1;
\\pub const VAL06: c_int = -2;
\\pub const VAL07: c_int = -3;
\\pub const VAL08: c_int = -4;
\\pub const VAL09: c_int = -3;
\\pub const VAL10: c_int = -1000012000;
\\pub const VAL11: c_int = -1000161000;
\\pub const VAL12: c_int = -1000174001;
\\pub const VAL13: c_int = -3;
\\pub const VAL14: c_int = -1000012000;
\\pub const VAL15: c_int = -1000161000;
\\pub const VAL16: c_int = -3;
\\pub const VAL17: c_int = 1000011998;
\\pub const VAL18: c_longlong = 1152921504606846976;
\\pub const VAL19: c_longlong = 3458764513820540927;
\\pub const VAL20: c_longlong = 6917529027641081854;
\\pub const VAL21: c_longlong = 6917529027641081853;
\\pub const VAL22: c_int = 0;
\\pub const VAL23: c_longlong = -1;
\\pub const enum_EnumWithInits = c_longlong;
});
}
cases.add("predefined expressions",
\\void foo(void) {
\\ __func__;
\\ __FUNCTION__;
\\ __PRETTY_FUNCTION__;
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ _ = "foo";
\\ _ = "foo";
\\ _ = "void foo(void)";
\\}
});
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: ?*const fn () callconv(.C) void) void;
});
cases.add("void cast",
2019-12-19 22:10:25 +00:00
\\void foo() {
\\ int a;
\\ (void) a;
\\}
, &[_][]const u8{
2019-12-19 22:10:25 +00:00
\\pub export fn foo() void {
\\ var a: c_int = undefined;
\\ _ = &a;
\\ _ = &a;
\\}
});
cases.add("implicit cast to void *",
2019-12-19 22:10:25 +00:00
\\void *foo() {
\\ unsigned short *x;
\\ return x;
\\}
, &[_][]const u8{
\\pub export fn foo() ?*anyopaque {
2019-12-19 22:10:25 +00:00
\\ var x: [*c]c_ushort = undefined;
\\ _ = &x;
\\ return @as(?*anyopaque, @ptrCast(x));
\\}
});
cases.add("null pointer implicit cast",
\\int* foo(void) {
\\ return 0;
\\}
, &[_][]const u8{
\\pub export fn foo() [*c]c_int {
\\ return null;
\\}
});
cases.add("simple union",
\\union Foo {
\\ int x;
\\ double y;
\\};
, &[_][]const u8{
\\pub const union_Foo = extern union {
\\ x: c_int,
\\ y: f64,
\\};
2020-10-31 10:21:49 +00:00
,
\\pub const Foo = union_Foo;
});
cases.add("packed union - simple",
\\union Foo {
\\ char x;
\\ double y;
\\} __attribute__((packed));
, &[_][]const u8{
\\pub const union_Foo = extern union {
\\ x: u8 align(1),
\\ y: f64 align(1),
\\};
,
\\pub const Foo = union_Foo;
});
cases.add("packed union - nested unpacked",
\\union Foo{
\\ char x;
\\ double y;
\\ struct {
\\ char a;
\\ int b;
\\ } z;
\\} __attribute__((packed));
, &[_][]const u8{
// NOTE: The nested struct is *not* packed/aligned,
// even though the parent struct is
// this is consistent with GCC docs
\\const struct_unnamed_1 = extern struct {
\\ a: u8 = @import("std").mem.zeroes(u8),
\\ b: c_int = @import("std").mem.zeroes(c_int),
\\};
,
\\pub const union_Foo = extern union {
\\ x: u8 align(1),
\\ y: f64 align(1),
\\ z: struct_unnamed_1 align(1),
\\};
,
\\pub const Foo = union_Foo;
});
cases.add("packed union - nested packed",
\\union Foo{
\\ char x;
\\ double y;
\\ struct {
\\ char a;
\\ int b;
\\ } __attribute__((packed)) z;
\\} __attribute__((packed));
, &[_][]const u8{
// in order for the nested struct to be packed, it must
// have an independent packed declaration on
// the nested type (see GCC docs for details)
\\const struct_unnamed_1 = extern struct {
\\ a: u8 align(1) = @import("std").mem.zeroes(u8),
\\ b: c_int align(1) = @import("std").mem.zeroes(c_int),
\\};
,
\\pub const union_Foo = extern union {
\\ x: u8 align(1),
\\ y: f64 align(1),
\\ z: struct_unnamed_1 align(1),
\\};
,
\\pub const Foo = union_Foo;
});
cases.add("string literal",
\\const char *foo(void) {
\\ return "bar";
\\}
, &[_][]const u8{
\\pub export fn foo() [*c]const u8 {
\\ return "bar";
\\}
});
cases.add("return void",
\\void foo(void) {
\\ return;
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ return;
\\}
});
cases.add("for loop",
\\void foo(void) {
\\ for (int i = 0; i; i++) { }
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ {
\\ var i: c_int = 0;
\\ _ = &i;
\\ while (i != 0) : (i += 1) {}
\\ }
\\}
});
cases.add("empty for loop",
\\void foo(void) {
\\ for (;;) { }
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ while (true) {}
\\}
});
cases.add("for loop with simple init expression",
\\void foo(void) {
\\ int i;
\\ for (i = 3; i; i--) { }
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var i: c_int = undefined;
\\ _ = &i;
\\ {
\\ i = 3;
\\ while (i != 0) : (i -= 1) {}
\\ }
\\}
});
cases.add("break statement",
\\void foo(void) {
\\ for (;;) {
\\ break;
\\ }
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ while (true) {
\\ break;
\\ }
\\}
});
cases.add("continue statement",
\\void foo(void) {
\\ for (;;) {
\\ continue;
\\ }
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ while (true) {
\\ continue;
\\ }
\\}
});
cases.add("pointer casting",
2019-12-19 22:10:25 +00:00
\\float *ptrcast() {
\\ int *a;
\\ return (float *)a;
\\}
, &[_][]const u8{
2019-12-19 22:10:25 +00:00
\\pub export fn ptrcast() [*c]f32 {
\\ var a: [*c]c_int = undefined;
\\ _ = &a;
\\ return @as([*c]f32, @ptrCast(@alignCast(a)));
\\}
});
cases.add("casting pointer to pointer",
\\float **ptrptrcast() {
\\ int **a;
\\ return (float **)a;
\\}
, &[_][]const u8{
\\pub export fn ptrptrcast() [*c][*c]f32 {
\\ var a: [*c][*c]c_int = undefined;
\\ _ = &a;
\\ return @as([*c][*c]f32, @ptrCast(@alignCast(a)));
\\}
});
cases.add("pointer conversion with different alignment",
\\void test_ptr_cast() {
\\ void *p;
\\ {
\\ char *to_char = (char *)p;
\\ short *to_short = (short *)p;
\\ int *to_int = (int *)p;
\\ long long *to_longlong = (long long *)p;
\\ }
\\ {
\\ char *to_char = p;
\\ short *to_short = p;
\\ int *to_int = p;
\\ long long *to_longlong = p;
\\ }
\\}
, &[_][]const u8{
\\pub export fn test_ptr_cast() void {
\\ var p: ?*anyopaque = undefined;
\\ _ = &p;
\\ {
\\ var to_char: [*c]u8 = @as([*c]u8, @ptrCast(@alignCast(p)));
\\ _ = &to_char;
\\ var to_short: [*c]c_short = @as([*c]c_short, @ptrCast(@alignCast(p)));
\\ _ = &to_short;
\\ var to_int: [*c]c_int = @as([*c]c_int, @ptrCast(@alignCast(p)));
\\ _ = &to_int;
\\ var to_longlong: [*c]c_longlong = @as([*c]c_longlong, @ptrCast(@alignCast(p)));
\\ _ = &to_longlong;
\\ }
\\ {
\\ var to_char: [*c]u8 = @as([*c]u8, @ptrCast(@alignCast(p)));
\\ _ = &to_char;
\\ var to_short: [*c]c_short = @as([*c]c_short, @ptrCast(@alignCast(p)));
\\ _ = &to_short;
\\ var to_int: [*c]c_int = @as([*c]c_int, @ptrCast(@alignCast(p)));
\\ _ = &to_int;
\\ var to_longlong: [*c]c_longlong = @as([*c]c_longlong, @ptrCast(@alignCast(p)));
\\ _ = &to_longlong;
\\ }
\\}
});
cases.add("while on non-bool",
2019-12-19 22:10:25 +00:00
\\int while_none_bool() {
\\ int a;
\\ float b;
\\ void *c;
2019-12-18 23:38:42 +00:00
\\ while (a) return 0;
\\ while (b) return 1;
\\ while (c) return 2;
\\ return 3;
\\}
, &[_][]const u8{
2019-12-19 22:10:25 +00:00
\\pub export fn while_none_bool() c_int {
\\ var a: c_int = undefined;
\\ _ = &a;
2019-12-19 22:10:25 +00:00
\\ var b: f32 = undefined;
\\ _ = &b;
\\ var c: ?*anyopaque = undefined;
\\ _ = &c;
2019-12-18 23:38:42 +00:00
\\ while (a != 0) return 0;
\\ while (b != 0) return 1;
\\ while (c != null) return 2;
\\ return 3;
\\}
});
cases.add("for on non-bool",
2019-12-19 22:10:25 +00:00
\\int for_none_bool() {
\\ int a;
\\ float b;
\\ void *c;
2019-12-18 23:38:42 +00:00
\\ for (;a;) return 0;
\\ for (;b;) return 1;
\\ for (;c;) return 2;
\\ return 3;
\\}
, &[_][]const u8{
2019-12-19 22:10:25 +00:00
\\pub export fn for_none_bool() c_int {
\\ var a: c_int = undefined;
\\ _ = &a;
2019-12-19 22:10:25 +00:00
\\ var b: f32 = undefined;
\\ _ = &b;
\\ var c: ?*anyopaque = undefined;
\\ _ = &c;
2019-12-18 23:38:42 +00:00
\\ while (a != 0) return 0;
\\ while (b != 0) return 1;
\\ while (c != null) return 2;
\\ return 3;
\\}
});
cases.add("bitshift",
\\int foo(void) {
\\ return (1 << 2) >> 1;
\\}
, &[_][]const u8{
\\pub export fn foo() c_int {
\\ return (@as(c_int, 1) << @intCast(2)) >> @intCast(1);
\\}
});
cases.add("sizeof",
\\#include <stddef.h>
\\size_t size_of(void) {
\\ return sizeof(int);
\\}
, &[_][]const u8{
\\pub export fn size_of() usize {
\\ return @sizeOf(c_int);
\\}
});
cases.add("normal deref",
2019-12-19 22:10:25 +00:00
\\void foo() {
\\ int *x;
\\ *x = 1;
\\}
, &[_][]const u8{
2019-12-19 22:10:25 +00:00
\\pub export fn foo() void {
\\ var x: [*c]c_int = undefined;
\\ _ = &x;
\\ x.* = 1;
\\}
});
cases.add("address of operator",
\\int foo(void) {
\\ int x = 1234;
\\ int *ptr = &x;
\\ return *ptr;
\\}
, &[_][]const u8{
\\pub export fn foo() c_int {
\\ var x: c_int = 1234;
\\ _ = &x;
\\ var ptr: [*c]c_int = &x;
\\ _ = &ptr;
\\ return ptr.*;
\\}
});
cases.add("bin not",
2019-12-19 22:10:25 +00:00
\\int foo() {
\\ int x;
\\ return ~x;
\\}
, &[_][]const u8{
2019-12-19 22:10:25 +00:00
\\pub export fn foo() c_int {
\\ var x: c_int = undefined;
\\ _ = &x;
\\ return ~x;
\\}
});
cases.add("bool not",
2019-12-19 22:10:25 +00:00
\\int foo() {
\\ int a;
\\ float b;
\\ void *c;
\\ return !(a == 0);
\\ return !a;
\\ return !b;
\\ return !c;
\\}
, &[_][]const u8{
2019-12-19 22:10:25 +00:00
\\pub export fn foo() c_int {
\\ var a: c_int = undefined;
\\ _ = &a;
2019-12-19 22:10:25 +00:00
\\ var b: f32 = undefined;
\\ _ = &b;
\\ var c: ?*anyopaque = undefined;
\\ _ = &c;
\\ return @intFromBool(!(a == @as(c_int, 0)));
\\ return @intFromBool(!(a != 0));
\\ return @intFromBool(!(b != 0));
\\ return @intFromBool(!(c != null));
\\}
});
cases.add("__extension__ cast",
2019-12-19 19:30:51 +00:00
\\int foo(void) {
\\ return __extension__ 1;
\\}
, &[_][]const u8{
\\pub export fn foo() c_int {
\\ return 1;
\\}
});
if (builtin.os.tag != .windows) {
2019-12-19 22:10:25 +00:00
// sysv_abi not currently supported on windows
cases.add("Macro qualified functions",
2019-12-19 22:10:25 +00:00
\\void __attribute__((sysv_abi)) foo(void);
, &[_][]const u8{
\\pub extern fn foo() void;
});
}
cases.add("Forward-declared enum",
\\extern enum enum_ty my_enum;
\\enum enum_ty { FOO };
, &[_][]const u8{
\\pub const FOO: c_int = 0;
\\pub const enum_enum_ty = c_int;
\\pub extern var my_enum: enum_enum_ty;
});
cases.add("Parameterless function pointers",
\\typedef void (*fn0)();
\\typedef void (*fn1)(char);
, &[_][]const u8{
\\pub const fn0 = ?*const fn (...) callconv(.C) void;
\\pub const fn1 = ?*const fn (u8) callconv(.C) void;
});
2020-02-25 08:43:21 +00:00
cases.addWithTarget("Calling convention", .{
2022-10-07 18:54:44 +01:00
.cpu_arch = .x86,
separate std.Target and std.zig.CrossTarget Zig now supports a more fine-grained sense of what is native and what is not. Some examples: This is now allowed: -target native Different OS but native CPU, default Windows C ABI: -target native-windows This could be useful for example when running in Wine. Different CPU but native OS, native C ABI. -target x86_64-native -mcpu=skylake Different C ABI but otherwise native target: -target native-native-musl -target native-native-gnu Lots of breaking changes to related std lib APIs. Calls to getOs() will need to be changed to getOsTag(). Calls to getArch() will need to be changed to getCpuArch(). Usage of Target.Cross and Target.Native need to be updated to use CrossTarget API. `std.build.Builder.standardTargetOptions` is changed to accept its parameters as a struct with default values. It now has the ability to specify a whitelist of targets allowed, as well as the default target. Rather than two different ways of collecting the target, it's now always a string that is validated, and prints helpful diagnostics for invalid targets. This feature should now be actually useful, and contributions welcome to further improve the user experience. `std.build.LibExeObjStep.setTheTarget` is removed. `std.build.LibExeObjStep.setTarget` is updated to take a CrossTarget parameter. `std.build.LibExeObjStep.setTargetGLibC` is removed. glibc versions are handled in the CrossTarget API and can be specified with the `-target` triple. `std.builtin.Version` gains a `format` method.
2020-02-26 06:18:23 +00:00
.os_tag = .linux,
.abi = .none,
2019-12-28 20:28:18 +00:00
},
\\void __attribute__((fastcall)) foo1(float *a);
\\void __attribute__((stdcall)) foo2(float *a);
\\void __attribute__((vectorcall)) foo3(float *a);
\\void __attribute__((cdecl)) foo4(float *a);
2019-12-29 18:34:54 +00:00
\\void __attribute__((thiscall)) foo5(float *a);
2019-12-28 20:28:18 +00:00
, &[_][]const u8{
\\pub extern fn foo1(a: [*c]f32) callconv(.Fastcall) void;
\\pub extern fn foo2(a: [*c]f32) callconv(.Stdcall) void;
\\pub extern fn foo3(a: [*c]f32) callconv(.Vectorcall) void;
2019-12-28 20:28:18 +00:00
\\pub extern fn foo4(a: [*c]f32) void;
\\pub extern fn foo5(a: [*c]f32) callconv(.Thiscall) void;
2019-12-28 20:28:18 +00:00
});
cases.addWithTarget("Calling convention", std.Target.Query.parse(.{
separate std.Target and std.zig.CrossTarget Zig now supports a more fine-grained sense of what is native and what is not. Some examples: This is now allowed: -target native Different OS but native CPU, default Windows C ABI: -target native-windows This could be useful for example when running in Wine. Different CPU but native OS, native C ABI. -target x86_64-native -mcpu=skylake Different C ABI but otherwise native target: -target native-native-musl -target native-native-gnu Lots of breaking changes to related std lib APIs. Calls to getOs() will need to be changed to getOsTag(). Calls to getArch() will need to be changed to getCpuArch(). Usage of Target.Cross and Target.Native need to be updated to use CrossTarget API. `std.build.Builder.standardTargetOptions` is changed to accept its parameters as a struct with default values. It now has the ability to specify a whitelist of targets allowed, as well as the default target. Rather than two different ways of collecting the target, it's now always a string that is validated, and prints helpful diagnostics for invalid targets. This feature should now be actually useful, and contributions welcome to further improve the user experience. `std.build.LibExeObjStep.setTheTarget` is removed. `std.build.LibExeObjStep.setTarget` is updated to take a CrossTarget parameter. `std.build.LibExeObjStep.setTargetGLibC` is removed. glibc versions are handled in the CrossTarget API and can be specified with the `-target` triple. `std.builtin.Version` gains a `format` method.
2020-02-26 06:18:23 +00:00
.arch_os_abi = "arm-linux-none",
.cpu_features = "generic+v8_5a",
}) catch unreachable,
2019-12-28 20:28:18 +00:00
\\void __attribute__((pcs("aapcs"))) foo1(float *a);
\\void __attribute__((pcs("aapcs-vfp"))) foo2(float *a);
, &[_][]const u8{
\\pub extern fn foo1(a: [*c]f32) callconv(.AAPCS) void;
\\pub extern fn foo2(a: [*c]f32) callconv(.AAPCSVFP) void;
2019-12-28 20:28:18 +00:00
});
cases.addWithTarget("Calling convention", std.Target.Query.parse(.{
separate std.Target and std.zig.CrossTarget Zig now supports a more fine-grained sense of what is native and what is not. Some examples: This is now allowed: -target native Different OS but native CPU, default Windows C ABI: -target native-windows This could be useful for example when running in Wine. Different CPU but native OS, native C ABI. -target x86_64-native -mcpu=skylake Different C ABI but otherwise native target: -target native-native-musl -target native-native-gnu Lots of breaking changes to related std lib APIs. Calls to getOs() will need to be changed to getOsTag(). Calls to getArch() will need to be changed to getCpuArch(). Usage of Target.Cross and Target.Native need to be updated to use CrossTarget API. `std.build.Builder.standardTargetOptions` is changed to accept its parameters as a struct with default values. It now has the ability to specify a whitelist of targets allowed, as well as the default target. Rather than two different ways of collecting the target, it's now always a string that is validated, and prints helpful diagnostics for invalid targets. This feature should now be actually useful, and contributions welcome to further improve the user experience. `std.build.LibExeObjStep.setTheTarget` is removed. `std.build.LibExeObjStep.setTarget` is updated to take a CrossTarget parameter. `std.build.LibExeObjStep.setTargetGLibC` is removed. glibc versions are handled in the CrossTarget API and can be specified with the `-target` triple. `std.builtin.Version` gains a `format` method.
2020-02-26 06:18:23 +00:00
.arch_os_abi = "aarch64-linux-none",
.cpu_features = "generic+v8_5a",
}) catch unreachable,
2019-12-28 20:28:18 +00:00
\\void __attribute__((aarch64_vector_pcs)) foo1(float *a);
, &[_][]const u8{
\\pub extern fn foo1(a: [*c]f32) callconv(.Vectorcall) void;
2019-12-28 20:28:18 +00:00
});
cases.add("Parameterless function prototypes",
\\void a() {}
\\void b(void) {}
\\void c();
\\void d(void);
\\static void e() {}
\\static void f(void) {}
\\static void g();
\\static void h(void);
, &[_][]const u8{
2019-12-12 14:56:21 +00:00
\\pub export fn a() void {}
\\pub export fn b() void {}
\\pub extern fn c(...) void;
\\pub extern fn d() void;
\\pub fn e() callconv(.C) void {}
\\pub fn f() callconv(.C) void {}
\\pub extern fn g() void;
\\pub extern fn h() void;
});
cases.add("variable declarations",
2019-12-13 12:55:36 +00:00
\\extern char arr0[] = "hello";
\\static char arr1[] = "hello";
\\char arr2[] = "hello";
, &[_][]const u8{
\\pub export var arr0: [5:0]u8 = "hello".*;
\\pub var arr1: [5:0]u8 = "hello".*;
\\pub export var arr2: [5:0]u8 = "hello".*;
2019-12-13 12:55:36 +00:00
});
cases.add("array initializer expr",
2019-12-14 08:41:00 +00:00
\\static void foo(void){
\\ char arr[10] ={1};
\\ char *arr1[10] ={0};
\\}
, &[_][]const u8{
2019-12-28 20:28:18 +00:00
\\pub fn foo() callconv(.C) void {
2020-01-30 18:53:35 +00:00
\\ var arr: [10]u8 = [1]u8{
\\ 1,
2020-01-30 18:53:35 +00:00
\\ } ++ [1]u8{0} ** 9;
\\ _ = &arr;
2020-01-30 18:53:35 +00:00
\\ var arr1: [10][*c]u8 = [1][*c]u8{
2019-12-14 08:41:00 +00:00
\\ null,
2020-01-30 18:53:35 +00:00
\\ } ++ [1][*c]u8{null} ** 9;
\\ _ = &arr1;
2019-12-14 08:41:00 +00:00
\\}
});
cases.add("enums",
2019-12-14 19:46:54 +00:00
\\typedef enum {
\\ a,
\\ b,
\\ c,
\\} d;
\\enum {
\\ e,
\\ f = 4,
\\ g,
\\} h = e;
\\struct Baz {
\\ enum {
\\ i,
\\ j,
\\ k,
\\ } l;
\\ d m;
\\};
\\enum i {
\\ n,
\\ o,
\\ p,
\\};
, &[_][]const u8{
\\pub const a: c_int = 0;
\\pub const b: c_int = 1;
\\pub const c: c_int = 2;
\\pub const d =
++ " " ++ default_enum_type ++
\\;
\\pub const e: c_int = 0;
\\pub const f: c_int = 4;
\\pub const g: c_int = 5;
\\const enum_unnamed_1 =
++ " " ++ default_enum_type ++
\\;
\\pub export var h: enum_unnamed_1 = @as(c_uint, @bitCast(e));
\\pub const i: c_int = 0;
\\pub const j: c_int = 1;
\\pub const k: c_int = 2;
\\const enum_unnamed_2 =
++ " " ++ default_enum_type ++
\\;
\\pub const struct_Baz = extern struct {
\\ l: enum_unnamed_2 = @import("std").mem.zeroes(enum_unnamed_2),
\\ m: d = @import("std").mem.zeroes(d),
\\};
\\pub const n: c_int = 0;
\\pub const o: c_int = 1;
\\pub const p: c_int = 2;
\\pub const enum_i =
++ " " ++ default_enum_type ++
\\;
2020-10-31 10:21:49 +00:00
,
"pub const Baz = struct_Baz;",
2019-12-14 19:46:54 +00:00
});
cases.add("#define a char literal",
2019-12-15 12:44:11 +00:00
\\#define A_CHAR 'a'
, &[_][]const u8{
\\pub const A_CHAR = 'a';
});
cases.add("comment after integer literal",
2019-12-15 12:44:11 +00:00
\\#define SDL_INIT_VIDEO 0x00000020 /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
, &[_][]const u8{
2021-03-05 18:42:21 +00:00
\\pub const SDL_INIT_VIDEO = @as(c_int, 0x00000020);
2019-12-15 12:44:11 +00:00
});
cases.add("u integer suffix after hex literal",
2019-12-15 12:44:11 +00:00
\\#define SDL_INIT_VIDEO 0x00000020u /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
, &[_][]const u8{
\\pub const SDL_INIT_VIDEO = @as(c_uint, 0x00000020);
});
cases.add("l integer suffix after hex literal",
2019-12-15 12:44:11 +00:00
\\#define SDL_INIT_VIDEO 0x00000020l /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
, &[_][]const u8{
\\pub const SDL_INIT_VIDEO = @as(c_long, 0x00000020);
});
cases.add("ul integer suffix after hex literal",
2019-12-15 12:44:11 +00:00
\\#define SDL_INIT_VIDEO 0x00000020ul /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
, &[_][]const u8{
\\pub const SDL_INIT_VIDEO = @as(c_ulong, 0x00000020);
});
cases.add("lu integer suffix after hex literal",
2019-12-15 12:44:11 +00:00
\\#define SDL_INIT_VIDEO 0x00000020lu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
, &[_][]const u8{
\\pub const SDL_INIT_VIDEO = @as(c_ulong, 0x00000020);
});
cases.add("ll integer suffix after hex literal",
2019-12-15 12:44:11 +00:00
\\#define SDL_INIT_VIDEO 0x00000020ll /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
, &[_][]const u8{
\\pub const SDL_INIT_VIDEO = @as(c_longlong, 0x00000020);
});
cases.add("ull integer suffix after hex literal",
2019-12-15 12:44:11 +00:00
\\#define SDL_INIT_VIDEO 0x00000020ull /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
, &[_][]const u8{
\\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 0x00000020);
});
cases.add("llu integer suffix after hex literal",
2019-12-15 12:44:11 +00:00
\\#define SDL_INIT_VIDEO 0x00000020llu /**< SDL_INIT_VIDEO implies SDL_INIT_EVENTS */
, &[_][]const u8{
\\pub const SDL_INIT_VIDEO = @as(c_ulonglong, 0x00000020);
});
cases.add("generate inline func for #define global extern fn",
\\extern void (*fn_ptr)(void);
\\#define foo fn_ptr
\\
\\extern char (*fn_ptr2)(int, float);
\\#define bar fn_ptr2
, &[_][]const u8{
\\pub extern var fn_ptr: ?*const fn () callconv(.C) void;
,
\\pub inline fn foo() void {
\\ return fn_ptr.?();
\\}
,
\\pub extern var fn_ptr2: ?*const fn (c_int, f32) callconv(.C) u8;
,
\\pub inline fn bar(arg_1: c_int, arg_2: f32) u8 {
\\ return fn_ptr2.?(arg_1, arg_2);
\\}
});
2019-12-15 14:50:20 +00:00
cases.add("macros with field targets",
\\typedef unsigned int GLbitfield;
\\typedef void (*PFNGLCLEARPROC) (GLbitfield mask);
\\typedef void(*OpenGLProc)(void);
\\union OpenGLProcs {
\\ OpenGLProc ptr[1];
\\ struct {
\\ PFNGLCLEARPROC Clear;
\\ } gl;
\\};
\\extern union OpenGLProcs glProcs;
\\#define glClearUnion glProcs.gl.Clear
\\#define glClearPFN PFNGLCLEARPROC
, &[_][]const u8{
\\pub const GLbitfield = c_uint;
\\pub const PFNGLCLEARPROC = ?*const fn (GLbitfield) callconv(.C) void;
\\pub const OpenGLProc = ?*const fn () callconv(.C) void;
\\const struct_unnamed_1 = extern struct {
\\ Clear: PFNGLCLEARPROC = @import("std").mem.zeroes(PFNGLCLEARPROC),
\\};
\\pub const union_OpenGLProcs = extern union {
\\ ptr: [1]OpenGLProc,
\\ gl: struct_unnamed_1,
\\};
\\pub extern var glProcs: union_OpenGLProcs;
,
\\pub const glClearPFN = PFNGLCLEARPROC;
,
\\pub inline fn glClearUnion(arg_2: GLbitfield) void {
\\ return glProcs.gl.Clear.?(arg_2);
\\}
,
\\pub const OpenGLProcs = union_OpenGLProcs;
});
2019-12-15 14:50:20 +00:00
cases.add("macro pointer cast",
\\#define NRF_GPIO_BASE 0
2021-06-12 14:54:39 +01:00
\\typedef struct { int dummy; } NRF_GPIO_Type;
2019-12-15 19:32:30 +00:00
\\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE)
, &[_][]const u8{
\\pub const NRF_GPIO = @import("std").zig.c_translation.cast([*c]NRF_GPIO_Type, NRF_GPIO_BASE);
2019-12-15 19:32:30 +00:00
});
cases.add("basic macro function",
2019-12-16 07:55:37 +00:00
\\extern int c;
2019-12-15 21:46:35 +00:00
\\#define BASIC(c) (c*2)
2020-02-13 07:09:49 +00:00
\\#define FOO(L,b) (L + b)
\\#define BAR() (c*c)
2019-12-15 21:46:35 +00:00
, &[_][]const u8{
2019-12-16 07:55:37 +00:00
\\pub extern var c: c_int;
2020-10-31 10:21:49 +00:00
,
\\pub inline fn BASIC(c_1: anytype) @TypeOf(c_1 * @as(c_int, 2)) {
\\ _ = &c_1;
2021-03-05 18:42:21 +00:00
\\ return c_1 * @as(c_int, 2);
2019-12-16 05:43:18 +00:00
\\}
2020-10-31 10:21:49 +00:00
,
\\pub inline fn FOO(L: anytype, b: anytype) @TypeOf(L + b) {
\\ _ = &L;
\\ _ = &b;
2020-02-13 07:09:49 +00:00
\\ return L + b;
\\}
,
\\pub inline fn BAR() @TypeOf(c * c) {
\\ return c * c;
\\}
});
cases.add("macro defines string literal with hex",
2019-12-16 05:43:18 +00:00
\\#define FOO "aoeu\xab derp"
\\#define FOO2 "aoeu\x0007a derp"
\\#define FOO_CHAR '\xfF'
, &[_][]const u8{
2019-12-16 05:43:18 +00:00
\\pub const FOO = "aoeu\xab derp";
2020-10-31 10:21:49 +00:00
,
\\pub const FOO2 = "aoeu\x7a derp";
2020-10-31 10:21:49 +00:00
,
\\pub const FOO_CHAR = '\xff';
});
2019-12-27 14:37:32 +00:00
cases.add("macro add",
\\#define D3_AHB1PERIPH_BASE 0
2019-12-27 14:37:32 +00:00
\\#define PERIPH_BASE (0x40000000UL) /*!< Base address of : AHB/APB Peripherals */
\\#define D3_APB1PERIPH_BASE (PERIPH_BASE + 0x18000000UL)
\\#define RCC_BASE (D3_AHB1PERIPH_BASE + 0x4400UL)
, &[_][]const u8{
\\pub const PERIPH_BASE = @as(c_ulong, 0x40000000);
2020-10-31 10:21:49 +00:00
,
2019-12-27 14:37:32 +00:00
\\pub const D3_APB1PERIPH_BASE = PERIPH_BASE + @as(c_ulong, 0x18000000);
2020-10-31 10:21:49 +00:00
,
2019-12-27 14:37:32 +00:00
\\pub const RCC_BASE = D3_AHB1PERIPH_BASE + @as(c_ulong, 0x4400);
});
cases.add("variable aliasing",
2019-12-16 10:18:56 +00:00
\\static long a = 2;
\\static long b = 2;
\\static int c = 4;
\\void foo(char c) {
\\ int a;
\\ char b = 123;
\\ b = (char) a;
\\ {
\\ int d = 5;
\\ }
\\ unsigned d = 440;
\\}
, &[_][]const u8{
\\pub var a: c_long = 2;
\\pub var b: c_long = 2;
2019-12-16 10:18:56 +00:00
\\pub var c: c_int = 4;
\\pub export fn foo(arg_c_1: u8) void {
\\ var c_1 = arg_c_1;
\\ _ = &c_1;
2019-12-16 10:18:56 +00:00
\\ var a_2: c_int = undefined;
\\ _ = &a_2;
\\ var b_3: u8 = 123;
\\ _ = &b_3;
\\ b_3 = @as(u8, @bitCast(@as(i8, @truncate(a_2))));
2019-12-16 10:18:56 +00:00
\\ {
\\ var d: c_int = 5;
\\ _ = &d;
2019-12-16 10:18:56 +00:00
\\ }
\\ var d: c_uint = @as(c_uint, @bitCast(@as(c_int, 440)));
\\ _ = &d;
2019-12-16 10:18:56 +00:00
\\}
});
cases.add("comma operator",
\\int foo() {
2019-12-16 13:22:37 +00:00
\\ 2, 4;
\\ return 2, 4, 6;
\\}
, &[_][]const u8{
\\pub export fn foo() c_int {
\\ _ = blk: {
\\ _ = @as(c_int, 2);
\\ break :blk @as(c_int, 4);
\\ };
\\ return blk: {
\\ _ = blk_1: {
\\ _ = @as(c_int, 2);
\\ break :blk_1 @as(c_int, 4);
\\ };
\\ break :blk @as(c_int, 6);
\\ };
2019-12-16 13:22:37 +00:00
\\}
});
cases.add("worst-case assign",
\\void foo() {
2019-12-16 13:59:35 +00:00
\\ int a;
\\ int b;
\\ a = b = 2;
\\}
, &[_][]const u8{
\\pub export fn foo() void {
2019-12-16 13:59:35 +00:00
\\ var a: c_int = undefined;
\\ _ = &a;
2019-12-16 13:59:35 +00:00
\\ var b: c_int = undefined;
\\ _ = &b;
\\ a = blk: {
\\ const tmp = @as(c_int, 2);
\\ b = tmp;
\\ break :blk tmp;
2019-12-16 13:59:35 +00:00
\\ };
\\}
});
cases.add("while loops",
2019-12-16 19:45:38 +00:00
\\int foo() {
\\ int a = 5;
\\ while (2)
\\ a = 2;
\\ while (4) {
\\ int a = 4;
\\ a = 9;
\\ return 6, a;
\\ }
\\ do {
\\ int a = 2;
\\ a = 12;
\\ } while (4);
\\ do
\\ a = 7;
\\ while (4);
\\}
, &[_][]const u8{
\\pub export fn foo() c_int {
\\ var a: c_int = 5;
\\ _ = &a;
\\ while (true) {
\\ a = 2;
\\ }
\\ while (true) {
\\ var a_1: c_int = 4;
\\ _ = &a_1;
\\ a_1 = 9;
\\ return blk: {
\\ _ = @as(c_int, 6);
\\ break :blk a_1;
\\ };
2019-12-16 19:45:38 +00:00
\\ }
\\ while (true) {
\\ var a_1: c_int = 2;
\\ _ = &a_1;
\\ a_1 = 12;
2019-12-16 19:45:38 +00:00
\\ }
\\ while (true) {
\\ a = 7;
\\ }
\\ return 0;
2019-12-16 19:45:38 +00:00
\\}
});
cases.add("for loops",
\\void foo() {
2019-12-16 23:08:08 +00:00
\\ for (int i = 2, b = 4; i + 2; i = 2) {
\\ int a = 2;
\\ a = 6, 5, 7;
\\ }
\\ char i = 2;
\\}
, &[_][]const u8{
\\pub export fn foo() void {
2019-12-16 23:08:08 +00:00
\\ {
\\ var i: c_int = 2;
\\ _ = &i;
2019-12-16 23:08:08 +00:00
\\ var b: c_int = 4;
\\ _ = &b;
\\ while ((i + @as(c_int, 2)) != 0) : (i = 2) {
2019-12-16 23:08:08 +00:00
\\ var a: c_int = 2;
\\ _ = &a;
\\ _ = blk: {
\\ _ = blk_1: {
\\ a = 6;
\\ break :blk_1 @as(c_int, 5);
\\ };
\\ break :blk @as(c_int, 7);
\\ };
2019-12-16 23:08:08 +00:00
\\ }
\\ }
\\ var i: u8 = 2;
\\ _ = &i;
2019-12-16 23:08:08 +00:00
\\}
});
cases.add("shadowing primitive types",
\\unsigned anyerror = 2;
\\#define noreturn _Noreturn
\\typedef enum {
\\ f32,
\\ u32,
\\} BadEnum;
, &[_][]const u8{
\\pub export var @"anyerror": c_uint = 2;
,
\\pub const @"noreturn" = @compileError("unable to translate C expr: unexpected token '_Noreturn'");
,
\\pub const @"f32": c_int = 0;
\\pub const @"u32": c_int = 1;
\\pub const BadEnum = c_uint;
});
cases.add("floats",
2019-12-17 09:15:41 +00:00
\\float a = 3.1415;
\\double b = 3.1415;
\\int c = 3.1415;
\\double d = 3;
, &[_][]const u8{
\\pub export var a: f32 = @as(f32, @floatCast(3.1415));
2019-12-17 09:15:41 +00:00
\\pub export var b: f64 = 3.1415;
\\pub export var c: c_int = @as(c_int, @intFromFloat(3.1415));
\\pub export var d: f64 = 3;
2019-12-17 09:15:41 +00:00
});
cases.add("conditional operator",
2019-12-17 10:06:28 +00:00
\\int bar(void) {
\\ if (2 ? 5 : 5 ? 4 : 6) 2;
\\ return 2 ? 5 : 5 ? 4 : 6;
\\}
, &[_][]const u8{
\\pub export fn bar() c_int {
\\ if ((if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6)) != 0) {
\\ _ = @as(c_int, 2);
\\ }
\\ return if (true) @as(c_int, 5) else if (true) @as(c_int, 4) else @as(c_int, 6);
2019-12-17 10:06:28 +00:00
\\}
});
cases.add("switch on int",
\\void switch_fn(int i) {
2019-12-17 15:19:28 +00:00
\\ int res = 0;
\\ switch (i) {
\\ case 0:
\\ res = 1;
2019-12-20 09:35:21 +00:00
\\ case 1 ... 3:
2019-12-17 15:19:28 +00:00
\\ res = 2;
\\ default:
\\ res = 3 * i;
\\ break;
\\ break;
\\ case 7: {
\\ res = 7;
\\ break;
\\ }
2019-12-20 09:35:21 +00:00
\\ case 4:
\\ case 5:
\\ res = 69;
\\ {
2019-12-17 15:19:28 +00:00
\\ res = 5;
\\ return;
\\ }
\\ case 6:
\\ switch (res) {
\\ case 9: break;
\\ }
\\ res = 1;
\\ return;
2019-12-17 15:19:28 +00:00
\\ }
\\}
, &[_][]const u8{
\\pub export fn switch_fn(arg_i: c_int) void {
\\ var i = arg_i;
\\ _ = &i;
2019-12-17 15:19:28 +00:00
\\ var res: c_int = 0;
\\ _ = &res;
\\ while (true) {
\\ switch (i) {
\\ @as(c_int, 0) => {
\\ res = 1;
\\ res = 2;
\\ res = @as(c_int, 3) * i;
\\ break;
\\ },
\\ @as(c_int, 1)...@as(c_int, 3) => {
\\ res = 2;
\\ res = @as(c_int, 3) * i;
\\ break;
\\ },
\\ else => {
\\ res = @as(c_int, 3) * i;
\\ break;
\\ },
\\ @as(c_int, 7) => {
\\ {
\\ res = 7;
\\ break;
\\ }
\\ },
\\ @as(c_int, 4), @as(c_int, 5) => {
\\ res = 69;
\\ {
\\ res = 5;
\\ return;
\\ }
\\ },
\\ @as(c_int, 6) => {
\\ while (true) {
\\ switch (res) {
\\ @as(c_int, 9) => break,
\\ else => {},
\\ }
\\ break;
\\ }
\\ res = 1;
\\ return;
\\ },
\\ }
\\ break;
2019-12-17 15:19:28 +00:00
\\ }
\\}
});
if (builtin.os.tag != .windows) {
// When clang uses the <arch>-windows-none triple it behaves as MSVC and
// interprets the inner `struct Bar` as an anonymous structure
cases.add("type referenced struct",
\\struct Foo {
\\ struct Bar{
\\ int b;
\\ };
\\ struct Bar c;
\\};
, &[_][]const u8{
\\pub const struct_Bar_1 = extern struct {
\\ b: c_int = @import("std").mem.zeroes(c_int),
\\};
\\pub const struct_Foo = extern struct {
\\ c: struct_Bar_1 = @import("std").mem.zeroes(struct_Bar_1),
\\};
});
}
cases.add("undefined array global",
\\int array[100] = {};
, &[_][]const u8{
2020-01-30 18:53:35 +00:00
\\pub export var array: [100]c_int = [1]c_int{0} ** 100;
});
cases.add("restrict -> noalias",
\\void foo(void *restrict bar, void *restrict);
, &[_][]const u8{
\\pub extern fn foo(noalias bar: ?*anyopaque, noalias ?*anyopaque) void;
});
cases.add("assign",
\\void max(int a) {
\\ int tmp;
\\ tmp = a;
\\ a = tmp;
\\}
, &[_][]const u8{
\\pub export fn max(arg_a: c_int) void {
\\ var a = arg_a;
\\ _ = &a;
\\ var tmp: c_int = undefined;
\\ _ = &tmp;
\\ tmp = a;
\\ a = tmp;
\\}
});
cases.add("chaining assign",
\\void max(int a) {
\\ int b, c;
\\ c = b = a;
\\}
, &[_][]const u8{
\\pub export fn max(arg_a: c_int) void {
\\ var a = arg_a;
\\ _ = &a;
\\ var b: c_int = undefined;
\\ _ = &b;
\\ var c: c_int = undefined;
\\ _ = &c;
\\ c = blk: {
\\ const tmp = a;
\\ b = tmp;
\\ break :blk tmp;
\\ };
\\}
});
cases.add("anonymous enum",
\\enum {
\\ One,
\\ Two,
\\};
, &[_][]const u8{
\\pub const One: c_int = 0;
\\pub const Two: c_int = 1;
\\const enum_unnamed_1 =
++ " " ++ default_enum_type ++
\\;
});
cases.add("c style cast",
\\int int_from_float(float a) {
\\ return (int)a;
\\}
, &[_][]const u8{
\\pub export fn int_from_float(arg_a: f32) c_int {
\\ var a = arg_a;
\\ _ = &a;
\\ return @as(c_int, @intFromFloat(a));
\\}
});
cases.add("escape sequences",
\\const char *escapes() {
\\char a = '\'',
\\ b = '\\',
\\ c = '\a',
\\ d = '\b',
\\ e = '\f',
\\ f = '\n',
\\ g = '\r',
\\ h = '\t',
\\ i = '\v',
\\ j = '\0',
\\ k = '\"';
\\ return "\'\\\a\b\f\n\r\t\v\0\"";
\\}
\\
, &[_][]const u8{
\\pub export fn escapes() [*c]const u8 {
\\ var a: u8 = '\'';
\\ _ = &a;
\\ var b: u8 = '\\';
\\ _ = &b;
\\ var c: u8 = '\x07';
\\ _ = &c;
\\ var d: u8 = '\x08';
\\ _ = &d;
\\ var e: u8 = '\x0c';
\\ _ = &e;
\\ var f: u8 = '\n';
\\ _ = &f;
\\ var g: u8 = '\r';
\\ _ = &g;
\\ var h: u8 = '\t';
\\ _ = &h;
\\ var i: u8 = '\x0b';
\\ _ = &i;
\\ var j: u8 = '\x00';
\\ _ = &j;
\\ var k: u8 = '"';
\\ _ = &k;
\\ return "'\\\x07\x08\x0c\n\r\t\x0b\x00\"";
\\}
});
cases.add("do loop",
\\void foo(void) {
\\ int a = 2;
\\ do {
\\ a = a - 1;
\\ } while (a);
\\
\\ int b = 2;
\\ do
\\ b = b -1;
\\ while (b);
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var a: c_int = 2;
\\ _ = &a;
\\ while (true) {
\\ a = a - @as(c_int, 1);
\\ if (!(a != 0)) break;
\\ }
\\ var b: c_int = 2;
\\ _ = &b;
\\ while (true) {
\\ b = b - @as(c_int, 1);
\\ if (!(b != 0)) break;
\\ }
\\}
});
cases.add("logical and, logical or, on non-bool values, extra parens",
\\enum Foo {
\\ FooA,
\\ FooB,
\\ FooC,
\\};
\\typedef int SomeTypedef;
\\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);
Support casting enums to all int types. In C, enums are represented as signed integers, so casting from an enum to an integer should use the "cast integer to integer" translation code path. Previously it used the "cast enum to generic non-enum" code path, because enums were not being treated as integers. Ultimately this can produce zig code that fails to compile if the destination type does not support the full range of enum values (e.g. translated C code that casts an enum value to an unsigned integer would fail to compile since enums are signed integers, and unsigned integers cannot represent the full range of values that signed ones can). One interesting thing that came up during testing is that the implicit enum-to-int cast that occurs when an enum is used in a boolean expression was parsed as an (int) by some versions of the zig compiler, and an (unsigned int) cast by others. Specifically, the following code: ```c enum Foo {Bar, Baz}; // ... enum Foo foo = Bar; if (0 || foo) { // do something } ``` When tested on MacOS, Linux, and Windows using a compiler built from the Windows Zig Compiler Dev Kit, the above code would emit a cast to c_uint: `if (false or (@bitCast(c_uint, @enumToInt(foo)) != 0)) {}` However when tested on Windows with a Zig compiler built using MSVC, it produces: `if (false or (@bitCast(c_int, @enumToInt(foo)) != 0)) {}` In this particular case I don't think it matters, since a c_int and c_uint will have the same representation for zero, but I'm not sure if this is ultimately the result of implementation-defined behavior or something else. Because of this, I added explicit casts in the `translate_c.zig` tests, to ensure that the emitted zig source exactly matches across platforms. I also added a behavior test in `run_translated_c.zig` that uses the old implicit casts from `translate_c.zig` to ensure that the emitted Zig code behaves the same as the C code regardless of what cast is used.
2020-12-07 22:20:45 +00:00
\\ int k = (a || (int)d);
\\ int l = ((int)d && b);
\\ int m = (c || (unsigned int)d);
\\ SomeTypedef td = 44;
\\ int o = (td || b);
\\ int p = (c && td);
\\ return ((((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p);
\\}
, &[_][]const u8{
\\pub const FooA: c_int = 0;
\\pub const FooB: c_int = 1;
\\pub const FooC: c_int = 2;
\\pub const enum_Foo =
++ " " ++ default_enum_type ++
\\;
\\pub const SomeTypedef = c_int;
\\pub export fn and_or_non_bool(arg_a: c_int, arg_b: f32, arg_c: ?*anyopaque) c_int {
\\ var a = arg_a;
\\ _ = &a;
\\ var b = arg_b;
\\ _ = &b;
\\ var c = arg_c;
\\ _ = &c;
\\ var d: enum_Foo = @as(c_uint, @bitCast(FooA));
\\ _ = &d;
\\ var e: c_int = @intFromBool((a != 0) and (b != 0));
\\ _ = &e;
\\ var f: c_int = @intFromBool((b != 0) and (c != null));
\\ _ = &f;
\\ var g: c_int = @intFromBool((a != 0) and (c != null));
\\ _ = &g;
\\ var h: c_int = @intFromBool((a != 0) or (b != 0));
\\ _ = &h;
\\ var i: c_int = @intFromBool((b != 0) or (c != null));
\\ _ = &i;
\\ var j: c_int = @intFromBool((a != 0) or (c != null));
\\ _ = &j;
\\ var k: c_int = @intFromBool((a != 0) or (@as(c_int, @bitCast(d)) != 0));
\\ _ = &k;
\\ var l: c_int = @intFromBool((@as(c_int, @bitCast(d)) != 0) and (b != 0));
\\ _ = &l;
\\ var m: c_int = @intFromBool((c != null) or (d != 0));
\\ _ = &m;
\\ var td: SomeTypedef = 44;
\\ _ = &td;
\\ var o: c_int = @intFromBool((td != 0) or (b != 0));
\\ _ = &o;
\\ var p: c_int = @intFromBool((c != null) and (td != 0));
\\ _ = &p;
\\ return (((((((((e + f) + g) + h) + i) + j) + k) + l) + m) + o) + p;
\\}
2020-10-31 10:21:49 +00:00
,
\\pub const Foo = enum_Foo;
});
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 = @import("std").mem.zeroes(c_int),
\\ y: c_int = @import("std").mem.zeroes(c_int),
\\};
\\pub const BarA: c_int = 0;
\\pub const BarB: c_int = 1;
\\pub const enum_Bar =
++ " " ++ default_enum_type ++
\\;
\\pub extern fn func(a: [*c]struct_Foo, b: [*c][*c]enum_Bar) void;
2020-10-31 10:21:49 +00:00
,
\\pub const Foo = struct_Foo;
\\pub const Bar = enum_Bar;
});
cases.add("bitwise binary operators, simpler parens",
\\int max(int a, int b) {
\\ return (a & b) ^ (a | b);
\\}
, &[_][]const u8{
\\pub export fn max(arg_a: c_int, arg_b: c_int) c_int {
\\ var a = arg_a;
\\ _ = &a;
\\ var b = arg_b;
\\ _ = &b;
\\ return (a & b) ^ (a | b);
\\}
});
cases.add("comparison operators (no if)", // TODO Come up with less contrived tests? Make sure to cover all these comparisons.
\\int test_comparisons(int a, int b) {
\\ int c = (a < b);
\\ int d = (a > b);
\\ int e = (a <= b);
\\ int f = (a >= b);
\\ int g = (c < d);
\\ int h = (e < f);
\\ int i = (g < h);
\\ return i;
\\}
, &[_][]const u8{
\\pub export fn test_comparisons(arg_a: c_int, arg_b: c_int) c_int {
\\ var a = arg_a;
\\ _ = &a;
\\ var b = arg_b;
\\ _ = &b;
\\ var c: c_int = @intFromBool(a < b);
\\ _ = &c;
\\ var d: c_int = @intFromBool(a > b);
\\ _ = &d;
\\ var e: c_int = @intFromBool(a <= b);
\\ _ = &e;
\\ var f: c_int = @intFromBool(a >= b);
\\ _ = &f;
\\ var g: c_int = @intFromBool(c < d);
\\ _ = &g;
\\ var h: c_int = @intFromBool(e < f);
\\ _ = &h;
\\ var i: c_int = @intFromBool(g < h);
\\ _ = &i;
\\ return i;
\\}
});
cases.add("==, !=",
\\int max(int a, int b) {
\\ if (a == b)
\\ return a;
\\ if (a != b)
\\ return b;
\\ return a;
\\}
, &[_][]const u8{
\\pub export fn max(arg_a: c_int, arg_b: c_int) c_int {
\\ var a = arg_a;
\\ _ = &a;
\\ var b = arg_b;
\\ _ = &b;
\\ if (a == b) return a;
\\ if (a != b) return b;
\\ return a;
\\}
});
cases.add("typedeffed bool expression",
\\typedef char* yes;
\\void foo(void) {
\\ yes a;
\\ if (a) 2;
\\}
, &[_][]const u8{
\\pub const yes = [*c]u8;
\\pub export fn foo() void {
\\ var a: yes = undefined;
\\ _ = &a;
\\ if (a != null) {
\\ _ = @as(c_int, 2);
\\ }
\\}
});
cases.add("statement expression",
2019-12-18 19:20:38 +00:00
\\int foo(void) {
\\ return ({
\\ int a = 1;
\\ a;
\\ a;
\\ });
\\}
, &[_][]const u8{
\\pub export fn foo() c_int {
\\ return blk: {
2019-12-18 19:20:38 +00:00
\\ var a: c_int = 1;
\\ _ = &a;
\\ _ = &a;
2019-12-18 19:20:38 +00:00
\\ break :blk a;
\\ };
2019-12-18 19:20:38 +00:00
\\}
});
cases.add("field access expression",
2019-12-18 20:29:42 +00:00
\\#define ARROW a->b
\\#define DOT a.b
\\extern struct Foo {
\\ int b;
\\}a;
\\float b = 2.0f;
\\void foo(void) {
2019-12-18 20:29:42 +00:00
\\ struct Foo *c;
\\ a.b;
\\ c->b;
\\}
, &[_][]const u8{
\\pub const struct_Foo = extern struct {
\\ b: c_int = @import("std").mem.zeroes(c_int),
2019-12-18 20:29:42 +00:00
\\};
\\pub extern var a: struct_Foo;
\\pub export var b: f32 = 2.0;
\\pub export fn foo() void {
2019-12-18 20:29:42 +00:00
\\ var c: [*c]struct_Foo = undefined;
\\ _ = &c;
2019-12-18 20:29:42 +00:00
\\ _ = a.b;
\\ _ = c.*.b;
\\}
2020-10-31 10:21:49 +00:00
,
2019-12-18 20:29:42 +00:00
\\pub const DOT = a.b;
2020-10-31 10:21:49 +00:00
,
\\pub const ARROW = a.*.b;
2019-12-18 20:29:42 +00:00
});
cases.add("array access",
2019-12-18 20:57:53 +00:00
\\#define ACCESS array[2]
\\int array[100] = {};
\\int foo(int index) {
\\ return array[index];
\\}
, &[_][]const u8{
2020-01-30 18:53:35 +00:00
\\pub export var array: [100]c_int = [1]c_int{0} ** 100;
\\pub export fn foo(arg_index: c_int) c_int {
\\ var index = arg_index;
\\ _ = &index;
\\ return array[@as(c_uint, @intCast(index))];
2019-12-18 20:57:53 +00:00
\\}
2020-10-31 10:21:49 +00:00
,
\\pub const ACCESS = array[@as(usize, @intCast(@as(c_int, 2)))];
2019-12-18 20:57:53 +00:00
});
cases.add("cast signed array index to unsigned",
\\void foo() {
\\ int a[10], i = 0;
\\ a[i] = 0;
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var a: [10]c_int = undefined;
\\ _ = &a;
\\ var i: c_int = 0;
\\ _ = &i;
\\ a[@as(c_uint, @intCast(i))] = 0;
\\}
});
cases.add("long long array index cast to usize",
\\void foo() {
\\ long long a[10], i = 0;
\\ a[i] = 0;
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var a: [10]c_longlong = undefined;
\\ _ = &a;
\\ var i: c_longlong = 0;
\\ _ = &i;
\\ a[@as(usize, @intCast(i))] = 0;
\\}
});
cases.add("unsigned array index skips cast",
\\void foo() {
\\ unsigned int a[10], i = 0;
\\ a[i] = 0;
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var a: [10]c_uint = undefined;
\\ _ = &a;
\\ var i: c_uint = 0;
\\ _ = &i;
\\ a[i] = 0;
\\}
});
cases.add("macro call",
2019-12-18 21:56:39 +00:00
\\#define CALL(arg) bar(arg)
\\int bar(int x) { return x; }
2019-12-18 21:56:39 +00:00
, &[_][]const u8{
\\pub inline fn CALL(arg: anytype) @TypeOf(bar(arg)) {
\\ _ = &arg;
2019-12-18 21:56:39 +00:00
\\ return bar(arg);
\\}
});
cases.add("macro call with no args",
\\#define CALL(arg) bar()
\\int bar(void) { return 0; }
, &[_][]const u8{
\\pub inline fn CALL(arg: anytype) @TypeOf(bar()) {
\\ _ = &arg;
\\ return bar();
\\}
});
cases.add("logical and, logical or",
2019-12-18 23:38:42 +00:00
\\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(arg_a: c_int, arg_b: c_int) c_int {
\\ var a = arg_a;
\\ _ = &a;
\\ var b = arg_b;
\\ _ = &b;
\\ if ((a < b) or (a == b)) return b;
\\ if ((a >= b) and (a == b)) return a;
\\ return a;
2019-12-18 23:38:42 +00:00
\\}
});
cases.add("simple if statement",
2019-12-18 23:38:42 +00:00
\\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(arg_a: c_int, arg_b: c_int) c_int {
\\ var a = arg_a;
\\ _ = &a;
\\ var b = arg_b;
\\ _ = &b;
\\ if (a < b) return b;
\\ if (a < b) return b else return a;
\\ if (a < b) {} else {}
\\ return 0;
2019-12-18 23:38:42 +00:00
\\}
});
cases.add("if statements",
\\void foo() {
\\ if (2) {
\\ int a = 2;
\\ }
\\ if (2, 5) {
\\ int a = 2;
\\ }
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ if (true) {
\\ var a: c_int = 2;
\\ _ = &a;
\\ }
\\ if ((blk: {
\\ _ = @as(c_int, 2);
\\ break :blk @as(c_int, 5);
\\ }) != 0) {
\\ var a: c_int = 2;
\\ _ = &a;
\\ }
\\}
});
cases.add("if on non-bool",
2019-12-18 23:38:42 +00:00
\\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: c_int = 0;
\\pub const B: c_int = 1;
\\pub const C: c_int = 2;
\\pub const enum_SomeEnum =
++ " " ++ default_enum_type ++
\\;
\\pub export fn if_none_bool(arg_a: c_int, arg_b: f32, arg_c: ?*anyopaque, arg_d: enum_SomeEnum) c_int {
\\ var a = arg_a;
\\ _ = &a;
\\ var b = arg_b;
\\ _ = &b;
\\ var c = arg_c;
\\ _ = &c;
\\ var d = arg_d;
\\ _ = &d;
\\ if (a != 0) return 0;
\\ if (b != 0) return 1;
\\ if (c != null) return 2;
\\ if (d != 0) return 3;
\\ return 4;
\\}
2019-12-18 23:38:42 +00:00
});
cases.add("simple data types",
\\#include <stdint.h>
\\int foo(char a, unsigned char b, signed char c);
\\int foo(char a, unsigned char b, signed char c); // test a duplicate prototype
\\void bar(uint8_t a, uint16_t b, uint32_t c, uint64_t d);
\\void baz(int8_t a, int16_t b, int32_t c, int64_t d);
, &[_][]const u8{
\\pub extern fn foo(a: u8, b: u8, c: i8) c_int;
\\pub extern fn bar(a: u8, b: u16, c: u32, d: u64) void;
\\pub extern fn baz(a: i8, b: i16, c: i32, d: i64) void;
});
cases.add("simple function",
\\int abs(int a) {
\\ return a < 0 ? -a : a;
\\}
, &[_][]const u8{
\\pub export fn abs(arg_a: c_int) c_int {
\\ var a = arg_a;
\\ _ = &a;
\\ return if (a < @as(c_int, 0)) -a else a;
\\}
});
cases.add("post increment",
\\unsigned foo1(unsigned a) {
\\ a++;
\\ return a;
\\}
\\int foo2(int a) {
\\ a++;
\\ return a;
\\}
\\int *foo3(int *a) {
\\ a++;
\\ return a;
\\}
, &[_][]const u8{
\\pub export fn foo1(arg_a: c_uint) c_uint {
\\ var a = arg_a;
\\ _ = &a;
\\ a +%= 1;
\\ return a;
\\}
\\pub export fn foo2(arg_a: c_int) c_int {
\\ var a = arg_a;
\\ _ = &a;
\\ a += 1;
\\ return a;
\\}
\\pub export fn foo3(arg_a: [*c]c_int) [*c]c_int {
\\ var a = arg_a;
\\ _ = &a;
\\ a += 1;
\\ return a;
\\}
});
cases.add("deref function pointer",
\\void foo(void) {}
\\int baz(void) { return 0; }
\\void bar(void) {
\\ void(*f)(void) = foo;
\\ int(*b)(void) = baz;
\\ f();
\\ (*(f))();
\\ foo();
\\ b();
\\ (*(b))();
\\ baz();
\\}
, &[_][]const u8{
\\pub export fn foo() void {}
\\pub export fn baz() c_int {
\\ return 0;
\\}
\\pub export fn bar() void {
\\ var f: ?*const fn () callconv(.C) void = &foo;
\\ _ = &f;
\\ var b: ?*const fn () callconv(.C) c_int = &baz;
\\ _ = &b;
\\ f.?();
\\ f.?();
\\ foo();
\\ _ = b.?();
\\ _ = b.?();
\\ _ = baz();
\\}
});
cases.add("pre increment/decrement",
2019-12-19 08:48:32 +00:00
\\void foo(void) {
\\ int i = 0;
\\ unsigned u = 0;
\\ ++i;
\\ --i;
\\ ++u;
\\ --u;
\\ i = ++i;
\\ i = --i;
\\ u = ++u;
\\ u = --u;
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var i: c_int = 0;
\\ _ = &i;
\\ var u: c_uint = 0;
\\ _ = &u;
2019-12-19 08:48:32 +00:00
\\ i += 1;
\\ i -= 1;
\\ u +%= 1;
\\ u -%= 1;
\\ i = blk: {
\\ const ref = &i;
\\ ref.* += 1;
\\ break :blk ref.*;
\\ };
\\ i = blk: {
\\ const ref = &i;
\\ ref.* -= 1;
\\ break :blk ref.*;
\\ };
\\ u = blk: {
\\ const ref = &u;
\\ ref.* +%= 1;
\\ break :blk ref.*;
\\ };
\\ u = blk: {
\\ const ref = &u;
\\ ref.* -%= 1;
\\ break :blk ref.*;
\\ };
2019-12-19 08:48:32 +00:00
\\}
});
cases.add("shift right assign",
2017-09-21 02:16:26 +01:00
\\int log2(unsigned a) {
\\ int i = 0;
\\ while (a > 0) {
2017-09-21 03:12:57 +01:00
\\ a >>= 1;
2017-09-21 02:16:26 +01:00
\\ }
\\ return i;
\\}
2019-12-19 22:10:25 +00:00
, &[_][]const u8{
\\pub export fn log2(arg_a: c_uint) c_int {
\\ var a = arg_a;
\\ _ = &a;
2017-09-21 02:16:26 +01:00
\\ var i: c_int = 0;
\\ _ = &i;
\\ while (a > @as(c_uint, @bitCast(@as(c_int, 0)))) {
\\ a >>= @intCast(@as(c_int, 1));
\\ }
2017-09-21 02:16:26 +01:00
\\ return i;
\\}
});
cases.add("shift right assign with a fixed size type",
\\#include <stdint.h>
\\int log2(uint32_t a) {
\\ int i = 0;
\\ while (a > 0) {
\\ a >>= 1;
\\ }
\\ return i;
\\}
, &[_][]const u8{
\\pub export fn log2(arg_a: u32) c_int {
\\ var a = arg_a;
\\ _ = &a;
\\ var i: c_int = 0;
\\ _ = &i;
\\ while (a > @as(u32, @bitCast(@as(c_int, 0)))) {
\\ a >>= @intCast(@as(c_int, 1));
\\ }
\\ return i;
\\}
});
2017-09-21 04:16:44 +01:00
cases.add("compound assignment operators",
\\void foo(void) {
\\ int a = 0;
\\ unsigned b = 0;
\\ a += (a += 1);
\\ a -= (a -= 1);
\\ a *= (a *= 1);
\\ a &= (a &= 1);
\\ a |= (a |= 1);
\\ a ^= (a ^= 1);
\\ a >>= (a >>= 1);
\\ a <<= (a <<= 1);
\\ a /= (a /= 1);
\\ a %= (a %= 1);
\\ b /= (b /= 1);
\\ b %= (b %= 1);
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var a: c_int = 0;
\\ _ = &a;
\\ var b: c_uint = 0;
\\ _ = &b;
\\ a += blk: {
\\ const ref = &a;
\\ ref.* += @as(c_int, 1);
\\ break :blk ref.*;
\\ };
\\ a -= blk: {
\\ const ref = &a;
\\ ref.* -= @as(c_int, 1);
\\ break :blk ref.*;
\\ };
\\ a *= blk: {
\\ const ref = &a;
\\ ref.* *= @as(c_int, 1);
\\ break :blk ref.*;
\\ };
\\ a &= blk: {
\\ const ref = &a;
\\ ref.* &= @as(c_int, 1);
\\ break :blk ref.*;
\\ };
\\ a |= blk: {
\\ const ref = &a;
\\ ref.* |= @as(c_int, 1);
\\ break :blk ref.*;
\\ };
\\ a ^= blk: {
\\ const ref = &a;
\\ ref.* ^= @as(c_int, 1);
\\ break :blk ref.*;
\\ };
\\ a >>= @intCast(blk: {
\\ const ref = &a;
\\ ref.* >>= @intCast(@as(c_int, 1));
\\ break :blk ref.*;
\\ });
\\ a <<= @intCast(blk: {
\\ const ref = &a;
\\ ref.* <<= @intCast(@as(c_int, 1));
\\ break :blk ref.*;
\\ });
\\ a = @divTrunc(a, blk: {
\\ const ref = &a;
\\ ref.* = @divTrunc(ref.*, @as(c_int, 1));
\\ break :blk ref.*;
\\ });
\\ a = @import("std").zig.c_translation.signedRemainder(a, blk: {
\\ const ref = &a;
\\ ref.* = @import("std").zig.c_translation.signedRemainder(ref.*, @as(c_int, 1));
\\ break :blk ref.*;
\\ });
\\ b /= blk: {
\\ const ref = &b;
\\ ref.* /= @as(c_uint, @bitCast(@as(c_int, 1)));
\\ break :blk ref.*;
\\ };
\\ b %= blk: {
\\ const ref = &b;
\\ ref.* %= @as(c_uint, @bitCast(@as(c_int, 1)));
\\ break :blk ref.*;
\\ };
\\}
});
cases.add("compound assignment operators unsigned",
\\void foo(void) {
\\ unsigned a = 0;
\\ a += (a += 1);
\\ a -= (a -= 1);
\\ a *= (a *= 1);
\\ a &= (a &= 1);
\\ a |= (a |= 1);
\\ a ^= (a ^= 1);
\\ a >>= (a >>= 1);
\\ a <<= (a <<= 1);
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var a: c_uint = 0;
\\ _ = &a;
\\ a +%= blk: {
\\ const ref = &a;
\\ ref.* +%= @as(c_uint, @bitCast(@as(c_int, 1)));
\\ break :blk ref.*;
\\ };
\\ a -%= blk: {
\\ const ref = &a;
\\ ref.* -%= @as(c_uint, @bitCast(@as(c_int, 1)));
\\ break :blk ref.*;
\\ };
\\ a *%= blk: {
\\ const ref = &a;
\\ ref.* *%= @as(c_uint, @bitCast(@as(c_int, 1)));
\\ break :blk ref.*;
\\ };
\\ a &= blk: {
\\ const ref = &a;
\\ ref.* &= @as(c_uint, @bitCast(@as(c_int, 1)));
\\ break :blk ref.*;
\\ };
\\ a |= blk: {
\\ const ref = &a;
\\ ref.* |= @as(c_uint, @bitCast(@as(c_int, 1)));
\\ break :blk ref.*;
\\ };
\\ a ^= blk: {
\\ const ref = &a;
\\ ref.* ^= @as(c_uint, @bitCast(@as(c_int, 1)));
\\ break :blk ref.*;
\\ };
\\ a >>= @intCast(blk: {
\\ const ref = &a;
\\ ref.* >>= @intCast(@as(c_int, 1));
\\ break :blk ref.*;
\\ });
\\ a <<= @intCast(blk: {
\\ const ref = &a;
\\ ref.* <<= @intCast(@as(c_int, 1));
\\ break :blk ref.*;
\\ });
\\}
});
cases.add("post increment/decrement",
\\void foo(void) {
\\ int i = 0;
\\ unsigned u = 0;
\\ i++;
\\ i--;
\\ u++;
\\ u--;
\\ i = i++;
\\ i = i--;
\\ u = u++;
\\ u = u--;
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var i: c_int = 0;
\\ _ = &i;
\\ var u: c_uint = 0;
\\ _ = &u;
\\ i += 1;
\\ i -= 1;
\\ u +%= 1;
\\ u -%= 1;
\\ i = blk: {
\\ const ref = &i;
\\ const tmp = ref.*;
\\ ref.* += 1;
\\ break :blk tmp;
\\ };
\\ i = blk: {
\\ const ref = &i;
\\ const tmp = ref.*;
\\ ref.* -= 1;
\\ break :blk tmp;
\\ };
\\ u = blk: {
\\ const ref = &u;
\\ const tmp = ref.*;
\\ ref.* +%= 1;
\\ break :blk tmp;
\\ };
\\ u = blk: {
\\ const ref = &u;
\\ const tmp = ref.*;
\\ ref.* -%= 1;
\\ break :blk tmp;
\\ };
2019-12-19 14:07:33 +00:00
\\}
});
cases.add("implicit casts",
\\#include <stdbool.h>
\\
\\void fn_int(int x);
\\void fn_f32(float x);
\\void fn_f64(double x);
\\void fn_char(char x);
\\void fn_bool(bool x);
\\void fn_ptr(void *x);
\\
\\void call() {
\\ fn_int(3.0f);
\\ fn_int(3.0);
\\ fn_int('ABCD');
\\ fn_f32(3);
\\ fn_f64(3);
\\ fn_char('3');
\\ fn_char('\x1');
\\ fn_char(0);
\\ fn_f32(3.0f);
\\ fn_f64(3.0);
\\ fn_bool(123);
\\ fn_bool(0);
\\ fn_bool(&fn_int);
\\ fn_int((int)&fn_int);
\\ fn_ptr((void *)42);
\\}
, &[_][]const u8{
\\pub extern fn fn_int(x: c_int) void;
\\pub extern fn fn_f32(x: f32) void;
\\pub extern fn fn_f64(x: f64) void;
\\pub extern fn fn_char(x: u8) void;
\\pub extern fn fn_bool(x: bool) void;
\\pub extern fn fn_ptr(x: ?*anyopaque) void;
\\pub export fn call() void {
\\ fn_int(@as(c_int, @intFromFloat(3.0)));
\\ fn_int(@as(c_int, @intFromFloat(3.0)));
\\ fn_int(@as(c_int, 1094861636));
\\ fn_f32(@as(f32, @floatFromInt(@as(c_int, 3))));
\\ fn_f64(@as(f64, @floatFromInt(@as(c_int, 3))));
\\ fn_char(@as(u8, @bitCast(@as(i8, @truncate(@as(c_int, '3'))))));
\\ fn_char(@as(u8, @bitCast(@as(i8, @truncate(@as(c_int, '\x01'))))));
\\ fn_char(@as(u8, @bitCast(@as(i8, @truncate(@as(c_int, 0))))));
\\ fn_f32(3.0);
\\ fn_f64(3.0);
\\ fn_bool(@as(c_int, 123) != 0);
\\ fn_bool(@as(c_int, 0) != 0);
\\ fn_bool(@intFromPtr(&fn_int) != 0);
\\ fn_int(@as(c_int, @intCast(@intFromPtr(&fn_int))));
\\ fn_ptr(@as(?*anyopaque, @ptrFromInt(@as(c_int, 42))));
\\}
});
cases.add("function call",
\\static void bar(void) { }
\\void foo(int *(baz)(void)) {
\\ bar();
\\ baz();
\\}
, &[_][]const u8{
\\pub fn bar() callconv(.C) void {}
\\pub export fn foo(arg_baz: ?*const fn () callconv(.C) [*c]c_int) void {
\\ var baz = arg_baz;
\\ _ = &baz;
\\ bar();
\\ _ = baz.?();
\\}
});
2019-12-19 22:10:25 +00:00
cases.add("macro defines string literal with octal",
2019-12-19 19:30:51 +00:00
\\#define FOO "aoeu\023 derp"
\\#define FOO2 "aoeu\0234 derp"
\\#define FOO_CHAR '\077'
, &[_][]const u8{
\\pub const FOO = "aoeu\x13 derp";
2020-10-31 10:21:49 +00:00
,
2019-12-20 16:51:44 +00:00
\\pub const FOO2 = "aoeu\x134 derp";
2020-10-31 10:21:49 +00:00
,
\\pub const FOO_CHAR = '\x3f';
2019-12-19 19:30:51 +00:00
});
cases.add("macro cast",
2021-06-12 14:54:39 +01:00
\\#include <stdint.h>
\\int baz(void *arg) { return 0; }
\\#define FOO(bar) baz((void *)(baz))
\\#define BAR (void*) a
\\#define BAZ (uint32_t)(2)
\\#define a 2
, &[_][]const u8{
\\pub inline fn FOO(bar: anytype) @TypeOf(baz(@import("std").zig.c_translation.cast(?*anyopaque, baz))) {
\\ _ = &bar;
\\ return baz(@import("std").zig.c_translation.cast(?*anyopaque, baz));
\\}
2020-10-31 10:21:49 +00:00
,
\\pub const BAR = @import("std").zig.c_translation.cast(?*anyopaque, a);
2020-10-31 10:21:49 +00:00
,
\\pub const BAZ = @import("std").zig.c_translation.cast(u32, @as(c_int, 2));
});
cases.add("macro with cast to unsigned short, long, and long long",
\\#define CURLAUTH_BASIC_BUT_USHORT ((unsigned short) 1)
\\#define CURLAUTH_BASIC ((unsigned long) 1)
\\#define CURLAUTH_BASIC_BUT_ULONGLONG ((unsigned long long) 1)
, &[_][]const u8{
\\pub const CURLAUTH_BASIC_BUT_USHORT = @import("std").zig.c_translation.cast(c_ushort, @as(c_int, 1));
\\pub const CURLAUTH_BASIC = @import("std").zig.c_translation.cast(c_ulong, @as(c_int, 1));
\\pub const CURLAUTH_BASIC_BUT_ULONGLONG = @import("std").zig.c_translation.cast(c_ulonglong, @as(c_int, 1));
});
cases.add("macro conditional operator",
\\ int a, b, c;
\\#define FOO a ? b : c
, &[_][]const u8{
\\pub const FOO = if (a) b else c;
});
cases.add("do while as expr",
\\static void foo(void) {
\\ if (1)
\\ do {} while (0);
\\}
, &[_][]const u8{
2020-01-02 18:01:38 +00:00
\\pub fn foo() callconv(.C) void {
\\ if (true) while (true) {
\\ if (!false) break;
\\ };
\\}
});
2019-12-29 18:10:20 +00:00
cases.add("macro comparisons",
2019-12-29 18:10:20 +00:00
\\#define MIN(a, b) ((b) < (a) ? (b) : (a))
\\#define MAX(a, b) ((b) > (a) ? (b) : (a))
, &[_][]const u8{
\\pub inline fn MIN(a: anytype, b: anytype) @TypeOf(if (b < a) b else a) {
\\ _ = &a;
\\ _ = &b;
2019-12-29 18:10:20 +00:00
\\ return if (b < a) b else a;
\\}
2020-10-31 10:21:49 +00:00
,
\\pub inline fn MAX(a: anytype, b: anytype) @TypeOf(if (b > a) b else a) {
\\ _ = &a;
\\ _ = &b;
2019-12-29 18:10:20 +00:00
\\ return if (b > a) b else a;
\\}
});
cases.add("nested assignment",
\\int foo(int *p, int x) {
\\ return *p++ = x;
\\}
, &[_][]const u8{
\\pub export fn foo(arg_p: [*c]c_int, arg_x: c_int) c_int {
\\ var p = arg_p;
\\ _ = &p;
\\ var x = arg_x;
\\ _ = &x;
\\ return blk: {
\\ const tmp = x;
\\ (blk_1: {
\\ const ref = &p;
\\ const tmp_2 = ref.*;
\\ ref.* += 1;
\\ break :blk_1 tmp_2;
\\ }).* = tmp;
\\ break :blk tmp;
\\ };
\\}
});
cases.add("widening and truncating integer casting to different signedness",
\\unsigned long foo(void) {
\\ return -1;
\\}
\\unsigned short bar(long x) {
\\ return x;
\\}
, &[_][]const u8{
\\pub export fn foo() c_ulong {
\\ return @as(c_ulong, @bitCast(@as(c_long, -@as(c_int, 1))));
\\}
\\pub export fn bar(arg_x: c_long) c_ushort {
\\ var x = arg_x;
\\ _ = &x;
\\ return @as(c_ushort, @bitCast(@as(c_short, @truncate(x))));
\\}
});
cases.add("arg name aliasing decl which comes after",
\\void foo(int bar) {
\\ bar = 2;
\\}
\\int bar = 4;
, &[_][]const u8{
\\pub export fn foo(arg_bar_1: c_int) void {
\\ var bar_1 = arg_bar_1;
\\ _ = &bar_1;
\\ bar_1 = 2;
\\}
\\pub export var bar: c_int = 4;
});
cases.add("arg name aliasing macro which comes after",
\\void foo(int bar) {
\\ bar = 2;
\\}
\\#define bar 4
, &[_][]const u8{
\\pub export fn foo(arg_bar_1: c_int) void {
\\ var bar_1 = arg_bar_1;
\\ _ = &bar_1;
\\ bar_1 = 2;
\\}
2020-10-31 10:21:49 +00:00
,
2021-03-05 18:42:21 +00:00
\\pub const bar = @as(c_int, 4);
});
cases.add("don't export inline functions",
\\inline void a(void) {}
\\static void b(void) {}
\\void c(void) {}
\\static void foo() {}
, &[_][]const u8{
2020-01-02 18:01:38 +00:00
\\pub fn a() callconv(.C) void {}
\\pub fn b() callconv(.C) void {}
\\pub export fn c() void {}
\\pub fn foo() callconv(.C) void {}
});
cases.add("casting away const and volatile",
\\void foo(int *a) {}
\\void bar(const int *a) {
\\ foo((int *)a);
\\}
\\void baz(volatile int *a) {
\\ foo((int *)a);
\\}
, &[_][]const u8{
\\pub export fn foo(arg_a: [*c]c_int) void {
\\ var a = arg_a;
\\ _ = &a;
\\}
\\pub export fn bar(arg_a: [*c]const c_int) void {
\\ var a = arg_a;
\\ _ = &a;
\\ foo(@as([*c]c_int, @ptrCast(@volatileCast(@constCast(a)))));
\\}
\\pub export fn baz(arg_a: [*c]volatile c_int) void {
\\ var a = arg_a;
\\ _ = &a;
\\ foo(@as([*c]c_int, @ptrCast(@volatileCast(@constCast(a)))));
\\}
});
2020-01-05 19:02:28 +00:00
cases.add("handling of _Bool type",
\\_Bool foo(_Bool x) {
\\ _Bool a = x != 1;
\\ _Bool b = a != 0;
\\ _Bool c = foo;
\\ return foo(c != b);
\\}
, &[_][]const u8{
\\pub export fn foo(arg_x: bool) bool {
\\ var x = arg_x;
\\ _ = &x;
\\ var a: bool = @as(c_int, @intFromBool(x)) != @as(c_int, 1);
\\ _ = &a;
\\ var b: bool = @as(c_int, @intFromBool(a)) != @as(c_int, 0);
\\ _ = &b;
\\ var c: bool = @intFromPtr(&foo) != 0;
\\ _ = &c;
\\ return foo(@as(c_int, @intFromBool(c)) != @as(c_int, @intFromBool(b)));
2020-01-05 19:02:28 +00:00
\\}
});
cases.add("Don't make const parameters mutable",
\\int max(const int x, int y) {
\\ return (x > y) ? x : y;
\\}
, &[_][]const u8{
\\pub export fn max(x: c_int, arg_y: c_int) c_int {
\\ _ = &x;
\\ var y = arg_y;
\\ _ = &y;
\\ return if (x > y) x else y;
\\}
});
2020-03-29 00:40:26 +00:00
cases.add("string concatenation in macros",
\\#define FOO "hello"
\\#define BAR FOO " world"
\\#define BAZ "oh, " FOO
, &[_][]const u8{
\\pub const FOO = "hello";
2020-10-31 10:21:49 +00:00
,
2020-03-29 00:40:26 +00:00
\\pub const BAR = FOO ++ " world";
2020-10-31 10:21:49 +00:00
,
2020-03-29 00:40:26 +00:00
\\pub const BAZ = "oh, " ++ FOO;
});
cases.add("string concatenation in macros: two defines",
\\#define FOO "hello"
\\#define BAZ " world"
\\#define BAR FOO BAZ
, &[_][]const u8{
\\pub const FOO = "hello";
2020-10-31 10:21:49 +00:00
,
2020-03-29 00:40:26 +00:00
\\pub const BAZ = " world";
2020-10-31 10:21:49 +00:00
,
2020-03-29 00:40:26 +00:00
\\pub const BAR = FOO ++ BAZ;
});
cases.add("string concatenation in macros: two strings",
\\#define FOO "a" "b"
\\#define BAR FOO "c"
, &[_][]const u8{
\\pub const FOO = "a" ++ "b";
2020-10-31 10:21:49 +00:00
,
2020-03-29 00:40:26 +00:00
\\pub const BAR = FOO ++ "c";
});
cases.add("string concatenation in macros: three strings",
\\#define FOO "a" "b" "c"
, &[_][]const u8{
\\pub const FOO = "a" ++ "b" ++ "c";
2020-03-29 00:40:26 +00:00
});
2020-04-06 00:06:43 +01:00
cases.add("multibyte character literals",
\\#define FOO 'abcd'
, &[_][]const u8{
\\pub const FOO = 0x61626364;
});
2020-04-15 13:15:32 +01:00
cases.add("Make sure casts are grouped",
\\typedef struct
\\{
\\ int i;
\\}
\\*_XPrivDisplay;
\\typedef struct _XDisplay Display;
\\#define DefaultScreen(dpy) (((_XPrivDisplay)(dpy))->default_screen)
\\
, &[_][]const u8{
\\pub inline fn DefaultScreen(dpy: anytype) @TypeOf(@import("std").zig.c_translation.cast(_XPrivDisplay, dpy).*.default_screen) {
\\ _ = &dpy;
\\ return @import("std").zig.c_translation.cast(_XPrivDisplay, dpy).*.default_screen;
\\}
});
2020-04-07 20:08:46 +01:00
2020-04-15 13:14:10 +01:00
cases.add("macro integer literal casts",
\\#define NULL ((void*)0)
2020-04-15 13:14:10 +01:00
\\#define FOO ((int)0x8000)
, &[_][]const u8{
\\pub const NULL = @import("std").zig.c_translation.cast(?*anyopaque, @as(c_int, 0));
2020-10-31 10:21:49 +00:00
,
2023-11-24 18:11:11 +00:00
\\pub const FOO = @import("std").zig.c_translation.cast(c_int, @import("std").zig.c_translation.promoteIntLiteral(c_int, 0x8000, .hex));
});
2020-04-15 13:15:32 +01:00
if (builtin.abi == .msvc) {
2020-04-07 20:19:28 +01:00
cases.add("nameless struct fields",
2020-04-07 20:08:46 +01:00
\\typedef struct NAMED
\\{
\\ long name;
\\} NAMED;
\\
\\typedef struct ONENAMEWITHSTRUCT
\\{
\\ NAMED;
\\ long b;
\\} ONENAMEWITHSTRUCT;
, &[_][]const u8{
\\pub const struct_NAMED = extern struct {
\\ name: c_long = @import("std").mem.zeroes(c_long),
2020-04-07 20:08:46 +01:00
\\};
\\pub const NAMED = struct_NAMED;
\\pub const struct_ONENAMEWITHSTRUCT = extern struct {
\\ unnamed_0: struct_NAMED = = @import("std").mem.zeroes(struct_NAMED),
\\ b: c_long = @import("std").mem.zeroes(c_long),
2020-04-07 20:08:46 +01:00
\\};
});
2020-04-07 20:19:28 +01:00
} else {
cases.add("nameless struct fields",
\\typedef struct NAMED
\\{
\\ long name;
\\} NAMED;
\\
\\typedef struct ONENAMEWITHSTRUCT
\\{
\\ NAMED;
\\ long b;
\\} ONENAMEWITHSTRUCT;
, &[_][]const u8{
\\pub const struct_NAMED = extern struct {
\\ name: c_long = @import("std").mem.zeroes(c_long),
2020-04-07 20:19:28 +01:00
\\};
\\pub const NAMED = struct_NAMED;
\\pub const struct_ONENAMEWITHSTRUCT = extern struct {
\\ b: c_long = @import("std").mem.zeroes(c_long),
2020-04-07 20:19:28 +01:00
\\};
});
2020-04-07 20:08:46 +01:00
}
cases.add("unnamed fields have predictable names",
\\struct a {
\\ struct {};
\\};
\\struct b {
\\ struct {};
\\};
, &[_][]const u8{
\\const struct_unnamed_1 = extern struct {};
\\pub const struct_a = extern struct {
\\ unnamed_0: struct_unnamed_1 = @import("std").mem.zeroes(struct_unnamed_1),
\\};
\\const struct_unnamed_2 = extern struct {};
\\pub const struct_b = extern struct {
\\ unnamed_0: struct_unnamed_2 = @import("std").mem.zeroes(struct_unnamed_2),
\\};
});
2021-03-05 19:51:19 +00:00
cases.add("integer literal promotion",
\\#define GUARANTEED_TO_FIT_1 1024
\\#define GUARANTEED_TO_FIT_2 10241024L
\\#define GUARANTEED_TO_FIT_3 20482048LU
\\#define MAY_NEED_PROMOTION_1 10241024
\\#define MAY_NEED_PROMOTION_2 307230723072L
\\#define MAY_NEED_PROMOTION_3 819281928192LU
\\#define MAY_NEED_PROMOTION_HEX 0x80000000
\\#define MAY_NEED_PROMOTION_OCT 020000000000
, &[_][]const u8{
\\pub const GUARANTEED_TO_FIT_1 = @as(c_int, 1024);
\\pub const GUARANTEED_TO_FIT_2 = @as(c_long, 10241024);
\\pub const GUARANTEED_TO_FIT_3 = @as(c_ulong, 20482048);
\\pub const MAY_NEED_PROMOTION_1 = @import("std").zig.c_translation.promoteIntLiteral(c_int, 10241024, .decimal);
\\pub const MAY_NEED_PROMOTION_2 = @import("std").zig.c_translation.promoteIntLiteral(c_long, 307230723072, .decimal);
\\pub const MAY_NEED_PROMOTION_3 = @import("std").zig.c_translation.promoteIntLiteral(c_ulong, 819281928192, .decimal);
2023-11-24 18:11:11 +00:00
\\pub const MAY_NEED_PROMOTION_HEX = @import("std").zig.c_translation.promoteIntLiteral(c_int, 0x80000000, .hex);
\\pub const MAY_NEED_PROMOTION_OCT = @import("std").zig.c_translation.promoteIntLiteral(c_int, 0o20000000000, .octal);
2021-03-05 19:51:19 +00:00
});
cases.add("demote un-implemented builtins",
\\#define FOO(X) __builtin_alloca_with_align((X), 8)
, &[_][]const u8{
\\pub const FOO = @compileError("unable to translate macro: undefined identifier `__builtin_alloca_with_align`");
});
cases.add("null sentinel arrays when initialized from string literal. Issue #8256",
\\#include <stdint.h>
\\char zero[0] = "abc";
\\uint32_t zero_w[0] = U"💯💯💯";
\\char empty_incomplete[] = "";
\\uint32_t empty_incomplete_w[] = U"";
\\char empty_constant[100] = "";
\\uint32_t empty_constant_w[100] = U"";
\\char incomplete[] = "abc";
\\uint32_t incomplete_w[] = U"💯💯💯";
\\char truncated[1] = "abc";
\\uint32_t truncated_w[1] = U"💯💯💯";
\\char extend[5] = "a";
\\uint32_t extend_w[5] = U"💯";
\\char no_null[3] = "abc";
\\uint32_t no_null_w[3] = U"💯💯💯";
, &[_][]const u8{
\\pub export var zero: [0]u8 = [0]u8{};
\\pub export var zero_w: [0]u32 = [0]u32{};
\\pub export var empty_incomplete: [1]u8 = [1]u8{0} ** 1;
\\pub export var empty_incomplete_w: [1]u32 = [1]u32{0} ** 1;
\\pub export var empty_constant: [100]u8 = [1]u8{0} ** 100;
\\pub export var empty_constant_w: [100]u32 = [1]u32{0} ** 100;
\\pub export var incomplete: [3:0]u8 = "abc".*;
\\pub export var incomplete_w: [3:0]u32 = [3:0]u32{
\\ '\u{1f4af}',
\\ '\u{1f4af}',
\\ '\u{1f4af}',
\\};
\\pub export var truncated: [1]u8 = "abc"[0..1].*;
\\pub export var truncated_w: [1]u32 = [1]u32{
\\ '\u{1f4af}',
\\};
\\pub export var extend: [5]u8 = "a"[0..1].* ++ [1]u8{0} ** 4;
\\pub export var extend_w: [5]u32 = [1]u32{
\\ '\u{1f4af}',
\\} ++ [1]u32{0} ** 4;
\\pub export var no_null: [3]u8 = "abc".*;
\\pub export var no_null_w: [3]u32 = [3]u32{
\\ '\u{1f4af}',
\\ '\u{1f4af}',
\\ '\u{1f4af}',
\\};
});
cases.add("global assembly",
\\__asm__(".globl func\n\t"
\\ ".type func, @function\n\t"
\\ "func:\n\t"
\\ ".cfi_startproc\n\t"
\\ "movl $42, %eax\n\t"
\\ "ret\n\t"
\\ ".cfi_endproc");
, &[_][]const u8{
\\comptime {
\\ asm (".globl func\n\t.type func, @function\n\tfunc:\n\t.cfi_startproc\n\tmovl $42, %eax\n\tret\n\t.cfi_endproc");
\\}
});
cases.add("Demote function that initializes opaque struct",
\\struct my_struct {
\\ unsigned a: 15;
\\ unsigned: 2;
\\ unsigned b: 15;
\\};
\\void initialize(void) {
\\ struct my_struct S = {.a = 1, .b = 2};
\\}
, &[_][]const u8{
\\warning: local variable has opaque type
,
\\warning: unable to translate function, demoted to extern
\\pub extern fn initialize() void;
});
cases.add("Demote function that dereferences opaque type",
\\struct my_struct {
\\ unsigned a: 1;
\\};
\\void deref(struct my_struct *s) {
\\ *s;
\\}
, &[_][]const u8{
\\warning: cannot dereference opaque type
,
\\warning: unable to translate function, demoted to extern
\\pub extern fn deref(arg_s: ?*struct_my_struct) void;
});
cases.add("Demote function that dereference types that contain opaque type",
\\struct inner {
2022-10-10 23:43:53 +01:00
\\ _Atomic int a;
\\};
\\struct outer {
\\ int thing;
\\ struct inner sub_struct;
\\};
\\void deref(struct outer *s) {
\\ *s;
\\}
, &[_][]const u8{
\\pub const struct_inner = opaque {};
,
\\pub const struct_outer = extern struct {
\\ thing: c_int = @import("std").mem.zeroes(c_int),
\\ sub_struct: struct_inner = @import("std").mem.zeroes(struct_inner),
\\};
,
\\warning: unable to translate function, demoted to extern
,
\\pub extern fn deref(arg_s: ?*struct_outer) void;
});
cases.add("Function prototype declared within function",
\\int foo(void) {
\\ extern int bar(int, int);
\\ return bar(1, 2);
\\}
, &[_][]const u8{
\\pub extern fn bar(c_int, c_int) c_int;
\\pub export fn foo() c_int {
\\ return bar(@as(c_int, 1), @as(c_int, 2));
\\}
});
cases.add("static local variable zero-initialized if no initializer",
\\struct FOO {int x; int y;};
\\int bar(void) {
\\ static struct FOO foo;
\\ return foo.x;
\\}
, &[_][]const u8{
\\pub const struct_FOO = extern struct {
\\ x: c_int = @import("std").mem.zeroes(c_int),
\\ y: c_int = @import("std").mem.zeroes(c_int),
\\};
\\pub export fn bar() c_int {
\\ const foo = struct {
\\ var static: struct_FOO = @import("std").mem.zeroes(struct_FOO);
\\ };
\\ _ = &foo;
\\ return foo.static.x;
\\}
});
2021-06-12 14:54:39 +01:00
cases.add("macro with nontrivial cast",
\\#define MAP_FAILED ((void *) -1)
\\typedef long long LONG_PTR;
\\#define INVALID_HANDLE_VALUE ((void *)(LONG_PTR)-1)
, &[_][]const u8{
\\pub const MAP_FAILED = @import("std").zig.c_translation.cast(?*anyopaque, -@as(c_int, 1));
\\pub const INVALID_HANDLE_VALUE = @import("std").zig.c_translation.cast(?*anyopaque, @import("std").zig.c_translation.cast(LONG_PTR, -@as(c_int, 1)));
2021-06-12 14:54:39 +01:00
});
cases.add("discard unused local variables and function parameters",
\\#define FOO(A, B) (A)
\\int bar(int x, int y) {
\\ return x;
\\}
, &[_][]const u8{
\\pub export fn bar(arg_x: c_int, arg_y: c_int) c_int {
\\ var x = arg_x;
\\ _ = &x;
\\ var y = arg_y;
\\ _ = &y;
\\ return x;
\\}
,
\\pub inline fn FOO(A: anytype, B: anytype) @TypeOf(A) {
\\ _ = &A;
\\ _ = &B;
\\ return A;
\\}
});
cases.add("Use @ syntax for bare underscore identifier in macro or public symbol",
\\#define FOO _
\\int _ = 42;
, &[_][]const u8{
\\pub const FOO = @"_";
,
\\pub export var @"_": c_int = 42;
});
cases.add("Macro matching",
\\#define FOO(X) (X ## U)
, &[_][]const u8{
\\pub const FOO = @import("std").zig.c_translation.Macros.U_SUFFIX;
});
cases.add("Simple array access of pointer with non-negative integer constant",
\\void foo(int *p) {
\\ p[0];
\\ p[1];
\\}
, &[_][]const u8{
\\_ = p[@as(c_uint, @intCast(@as(c_int, 0)))];
,
\\_ = p[@as(c_uint, @intCast(@as(c_int, 1)))];
});
cases.add("Undefined macro identifier",
\\#define FOO BAR
, &[_][]const u8{
\\pub const FOO = @compileError("unable to translate macro: undefined identifier `BAR`");
});
cases.add("Macro redefines builtin",
\\#define FOO __builtin_popcount
, &[_][]const u8{
\\pub const FOO = __builtin_popcount;
});
cases.add("Only consider public decls in `isBuiltinDefined`",
\\#define FOO std
, &[_][]const u8{
\\pub const FOO = @compileError("unable to translate macro: undefined identifier `std`");
});
cases.add("Macro without a value",
\\#define FOO
, &[_][]const u8{
\\pub const FOO = "";
});
2022-08-19 11:03:14 +01:00
cases.add("leading zeroes",
\\#define O_RDONLY 00
\\#define HELLO 000
\\#define ZERO 0
\\#define WORLD 00000123
, &[_][]const u8{
\\pub const O_RDONLY = @as(c_int, 0o0);
\\pub const HELLO = @as(c_int, 0o00);
\\pub const ZERO = @as(c_int, 0);
\\pub const WORLD = @as(c_int, 0o0000123);
});
cases.add("Assign expression from bool to int",
\\void foo(void) {
\\ int a;
\\ if (a = 1 > 0) {}
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var a: c_int = undefined;
\\ _ = &a;
\\ if ((blk: {
\\ const tmp = @intFromBool(@as(c_int, 1) > @as(c_int, 0));
\\ a = tmp;
\\ break :blk tmp;
\\ }) != 0) {}
\\}
});
if (builtin.os.tag == .windows) {
cases.add("Pointer subtraction with typedef",
\\typedef char* S;
\\void foo() {
\\ S a, b;
\\ long long c = a - b;
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var a: S = undefined;
\\ _ = &a;
\\ var b: S = undefined;
\\ _ = &b;
\\ var c: c_longlong = @divExact(@as(c_longlong, @bitCast(@intFromPtr(a) -% @intFromPtr(b))), @sizeOf(u8));
\\ _ = &c;
\\}
});
} else {
cases.add("Pointer subtraction with typedef",
\\typedef char* S;
\\void foo() {
\\ S a, b;
\\ long c = a - b;
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var a: S = undefined;
\\ _ = &a;
\\ var b: S = undefined;
\\ _ = &b;
\\ var c: c_long = @divExact(@as(c_long, @bitCast(@intFromPtr(a) -% @intFromPtr(b))), @sizeOf(u8));
\\ _ = &c;
\\}
});
}
cases.add("extern array of unknown length",
\\extern int foo[];
, &[_][]const u8{
\\const foo: [*c]c_int = @extern([*c]c_int, .{
\\ .name = "foo",
\\});
});
cases.add("string array initializer",
\\static const char foo[] = {"bar"};
, &[_][]const u8{
\\pub const foo: [3:0]u8 = "bar";
});
cases.add("worst-case assign from mangle prefix",
\\void foo() {
\\ int n, tmp = 1;
\\ if (n = tmp) {}
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var n: c_int = undefined;
\\ _ = &n;
\\ var tmp: c_int = 1;
\\ _ = &tmp;
\\ if ((blk: {
\\ const tmp_1 = tmp;
\\ n = tmp_1;
\\ break :blk tmp_1;
\\ }) != 0) {}
\\}
});
cases.add("worst-case assign to mangle prefix",
\\void foo() {
\\ int tmp, n = 1;
\\ if (tmp = n) {}
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var tmp: c_int = undefined;
\\ _ = &tmp;
\\ var n: c_int = 1;
\\ _ = &n;
\\ if ((blk: {
\\ const tmp_1 = n;
\\ tmp = tmp_1;
\\ break :blk tmp_1;
\\ }) != 0) {}
\\}
});
cases.add("worst-case precrement mangle prefix",
\\void foo() {
\\ int n, ref = 1;
\\ if (n = ++ref) {}
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var n: c_int = undefined;
\\ _ = &n;
\\ var ref: c_int = 1;
\\ _ = &ref;
\\ if ((blk: {
\\ const tmp = blk_1: {
\\ const ref_2 = &ref;
\\ ref_2.* += 1;
\\ break :blk_1 ref_2.*;
\\ };
\\ n = tmp;
\\ break :blk tmp;
\\ }) != 0) {}
\\}
});
cases.add("worst-case postcrement mangle prefix",
\\void foo() {
\\ int n, ref = 1;
\\ if (n = ref++) {}
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var n: c_int = undefined;
\\ _ = &n;
\\ var ref: c_int = 1;
\\ _ = &ref;
\\ if ((blk: {
\\ const tmp = blk_1: {
\\ const ref_2 = &ref;
\\ const tmp_3 = ref_2.*;
\\ ref_2.* += 1;
\\ break :blk_1 tmp_3;
\\ };
\\ n = tmp;
\\ break :blk tmp;
\\ }) != 0) {}
\\}
});
cases.add("worst-case compound assign from mangle prefix",
\\void foo() {
\\ int n, ref = 1;
\\ if (n += ref) {}
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var n: c_int = undefined;
\\ _ = &n;
\\ var ref: c_int = 1;
\\ _ = &ref;
\\ if ((blk: {
\\ const ref_1 = &n;
\\ ref_1.* += ref;
\\ break :blk ref_1.*;
\\ }) != 0) {}
\\}
});
cases.add("worst-case compound assign to mangle prefix",
\\void foo() {
\\ int ref, n = 1;
\\ if (ref += n) {}
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var ref: c_int = undefined;
\\ _ = &ref;
\\ var n: c_int = 1;
\\ _ = &n;
\\ if ((blk: {
\\ const ref_1 = &ref;
\\ ref_1.* += n;
\\ break :blk ref_1.*;
\\ }) != 0) {}
\\}
});
cases.add("binary conditional operator where condition is the mangle prefix",
\\void foo() {
\\ int f = 1;
\\ int n, cond_temp = 1;
\\ if (n = (cond_temp)?:(f)) {}
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var f: c_int = 1;
\\ _ = &f;
\\ var n: c_int = undefined;
\\ _ = &n;
\\ var cond_temp: c_int = 1;
\\ _ = &cond_temp;
\\ if ((blk: {
\\ const tmp = blk_1: {
\\ const cond_temp_2 = cond_temp;
\\ break :blk_1 if (cond_temp_2 != 0) cond_temp_2 else f;
\\ };
\\ n = tmp;
\\ break :blk tmp;
\\ }) != 0) {}
\\}
});
cases.add("binary conditional operator where false_expr is the mangle prefix",
\\void foo() {
\\ int cond_temp = 1;
\\ int n, f = 1;
\\ if (n = (f)?:(cond_temp)) {}
\\}
, &[_][]const u8{
\\pub export fn foo() void {
\\ var cond_temp: c_int = 1;
\\ _ = &cond_temp;
\\ var n: c_int = undefined;
\\ _ = &n;
\\ var f: c_int = 1;
\\ _ = &f;
\\ if ((blk: {
\\ const tmp = blk_1: {
\\ const cond_temp_2 = f;
\\ break :blk_1 if (cond_temp_2 != 0) cond_temp_2 else cond_temp;
\\ };
\\ n = tmp;
\\ break :blk tmp;
\\ }) != 0) {}
\\}
});
cases.add("macro using argument as struct name is not translated",
\\#define FOO(x) struct x
, &[_][]const u8{
\\pub const FOO = @compileError("unable to translate macro: untranslatable usage of arg `x`");
});
cases.add("global struct whose default name conflicts with global is mangled",
\\struct foo {
\\ int x;
\\};
\\const char *struct_foo = "hello world";
, &[_][]const u8{
\\pub const struct_foo_1 = extern struct {
\\ x: c_int = @import("std").mem.zeroes(c_int),
\\};
,
\\pub const foo = struct_foo_1;
,
\\pub export var struct_foo: [*c]const u8 = "hello world";
});
cases.add("unsupport declare statement at the last of a compound statement which belongs to a statement expr",
\\void somefunc(void) {
\\ int y;
\\ (void)({y=1; _Static_assert(1);});
\\}
, &[_][]const u8{
\\pub export fn somefunc() void {
\\ var y: c_int = undefined;
\\ _ = &y;
\\ _ = blk: {
\\ y = 1;
\\ };
\\}
});
}