From 8fe076daaf182863d116f2be7b13e3743bbcaae3 Mon Sep 17 00:00:00 2001 From: Vexu Date: Thu, 16 Jul 2020 16:00:42 +0300 Subject: [PATCH 1/4] std.mem.zeroInit support initiating with tuples --- lib/std/mem.zig | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 0fbe96942eedd03a3af3f610b0856475d7f77992..1a330667ed8227f5d37183760cecaa1cf1c46ef1 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -709,6 +709,14 @@ pub fn zeroInit(comptime T: type, init: anytype) T { .Struct => |init_info| { var value = std.mem.zeroes(T); + // typeInfo won't tell us if this is a tuple + if (comptime eql(u8, init_info.fields[0].name, "0")) { + inline for (init_info.fields) |field, i| { + @field(value, struct_info.fields[i].name) = @field(init, field.name); + } + return value; + } + inline for (init_info.fields) |field| { if (!@hasField(T, field.name)) { @compileError("Encountered an initializer for `" ++ field.name ++ "`, but it is not a field of " ++ @typeName(T)); @@ -760,7 +768,7 @@ test "zeroInit" { .a = 42, }); - testing.expectEqual(s, S{ + testing.expectEqual(S{ .a = 42, .b = null, .c = .{ @@ -768,7 +776,22 @@ test "zeroInit" { }, .e = [3]u8{ 0, 0, 0 }, .f = -1, - }); + }, s); + + const Color = struct { + r: u8, + g: u8, + b: u8, + a: u8, + }; + + const c = zeroInit(Color, .{255, 255}); + testing.expectEqual(Color{ + .r = 255, + .g = 255, + .b = 0, + .a = 0, + }, c); } /// Compares two slices of numbers lexicographically. O(n). -- 2.54.0 From 37647375dcb817f88895ba1a17d648184727e4cc Mon Sep 17 00:00:00 2001 From: Vexu Date: Thu, 16 Jul 2020 16:20:47 +0300 Subject: [PATCH 2/4] translate-c: support initializer list expr macros --- src-self-hosted/translate_c.zig | 55 +++++++++++++++++++++++++++++++++ test/translate_c.zig | 25 +++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 1da52cda969f21dd3f2040290986b6068808b8cc..46f33dd0ff7963ff477658835cbc9add20bb22ef 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -6061,6 +6061,61 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, node = &call_node.base; continue; }, + .LBrace => { + // must come immediately after `node` + _ = try appendToken(c, .Comma, ","); + + const dot = try appendToken(c, .Period, "."); + _ = try appendToken(c, .LBrace, "{"); + + var init_vals = std.ArrayList(*ast.Node).init(c.gpa); + defer init_vals.deinit(); + + while (true) { + const val = try parseCPrefixOpExpr(c, it, source, source_loc, scope); + try init_vals.append(val); + const next = it.next().?; + if (next.id == .Comma) + _ = try appendToken(c, .Comma, ",") + else if (next.id == .RBrace) + break + else { + const first_tok = it.list.at(0); + try failDecl( + c, + source_loc, + source[first_tok.start..first_tok.end], + "unable to translate C expr: expected ',' or '}}'", + .{}, + ); + return error.ParseError; + } + } + const tuple_node = try ast.Node.StructInitializerDot.alloc(c.arena, init_vals.items.len); + tuple_node.* = .{ + .dot = dot, + .list_len = init_vals.items.len, + .rtoken = try appendToken(c, .RBrace, "}"), + }; + mem.copy(*ast.Node, tuple_node.list(), init_vals.items); + + + //(@import("std").mem.zeroInit(T, .{x})) + const import_fn_call = try c.createBuiltinCall("@import", 1); + const std_node = try transCreateNodeStringLiteral(c, "\"std\""); + import_fn_call.params()[0] = std_node; + import_fn_call.rparen_token = try appendToken(c, .RParen, ")"); + const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "mem"); + const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "zeroInit"); + + const zero_init_call = try c.createCall(outer_field_access, 2); + zero_init_call.params()[0] = node; + zero_init_call.params()[1] = &tuple_node.base; + zero_init_call.rtoken = try appendToken(c, .RParen, ")"); + + node = &zero_init_call.base; + continue; + }, .BangEqual => { op_token = try appendToken(c, .BangEqual, "!="); op_id = .BangEqual; diff --git a/test/translate_c.zig b/test/translate_c.zig index 905c74bca2de451acab4edde2017a40051f9dc7e..d12b2ab860cd401c5ab1b0479a7de78dd79e3759 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -3,6 +3,31 @@ const std = @import("std"); const CrossTarget = std.zig.CrossTarget; pub fn addCases(cases: *tests.TranslateCContext) void { + 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 + , &[_][]const u8{ // TODO properly translate this + \\pub const struct_Color = extern struct { + \\ r: u8, + \\ g: u8, + \\ b: u8, + \\ a: u8, + \\}; + \\pub const Color = struct_Color; + , + \\pub inline fn CLITERAL(type_1: anytype) @TypeOf(type_1) { + \\ return type_1; + \\} + , + \\pub const LIGHTGRAY = @import("std").mem.zeroInit(CLITERAL(Color), .{ 200, 200, 200, 255 }); + }); + cases.add("complex switch", \\int main() { \\ int i = 2; -- 2.54.0 From 06c08e5219f03328f483cded319c7ba75efb5188 Mon Sep 17 00:00:00 2001 From: Vexu Date: Thu, 16 Jul 2020 17:05:14 +0300 Subject: [PATCH 3/4] std.mem.zeroes use std.mem.set instead of `@memset` stage1 comptime is not smart enough to remeber the size of the casted item which leads to out of bounds errors. --- lib/std/mem.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/mem.zig b/lib/std/mem.zig index 1a330667ed8227f5d37183760cecaa1cf1c46ef1..f4489a4867c10eed0b6b854f13069ef76585b58a 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -552,7 +552,7 @@ pub fn zeroes(comptime T: type) T { if (@sizeOf(T) == 0) return T{}; if (comptime meta.containerLayout(T) == .Extern) { var item: T = undefined; - @memset(@ptrCast([*]u8, &item), 0, @sizeOf(T)); + set(u8, asBytes(&item), 0); return item; } else { var structure: T = undefined; -- 2.54.0 From 5e88a7a42724d7ccf9db58e1524beedebb1efd93 Mon Sep 17 00:00:00 2001 From: Vexu Date: Thu, 16 Jul 2020 17:10:52 +0300 Subject: [PATCH 4/4] add behavior tests fro macro translations --- CONTRIBUTING.md | 5 +++++ test/stage1/behavior.zig | 1 + test/stage1/behavior/translate_c_macros.h | 9 +++++++++ test/stage1/behavior/translate_c_macros.zig | 12 ++++++++++++ test/tests.zig | 1 + 5 files changed, 28 insertions(+) create mode 100644 test/stage1/behavior/translate_c_macros.h create mode 100644 test/stage1/behavior/translate_c_macros.zig diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 62b8083222eb781c8a5579d39d292bae581201ff..a8c77875e4c9b3a3f32a4908bdafb893033ff56a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -152,6 +152,11 @@ The relevant tests for this feature are: same, and that the program exits cleanly. This kind of test coverage is preferred, when possible, because it makes sure that the resulting Zig code is actually viable. + * `test/stage1/behavior/translate_c_macros.zig` - each test case consists of a Zig test + which checks that the relevant macros in `test/stage1/behavior/translate_c_macros.h`. + have the correct values. Macros have to be tested separately since they are expanded by + Clang in `run_translated_c` tests. + * `test/translate_c.zig` - each test case is C code, with a list of expected strings which must be found in the resulting Zig code. This kind of test is more precise in what it measures, but does not provide test coverage of whether the resulting Zig code is valid. diff --git a/test/stage1/behavior.zig b/test/stage1/behavior.zig index a15f1f26b9127e2d30c29f38cb2139e3e7690e0f..eb5bd9907f92dee0e25d112e065a70542e27c7a2 100644 --- a/test/stage1/behavior.zig +++ b/test/stage1/behavior.zig @@ -133,4 +133,5 @@ comptime { _ = @import("behavior/while.zig"); _ = @import("behavior/widening.zig"); _ = @import("behavior/src.zig"); + _ = @import("behavior/translate_c_macros.zig"); } diff --git a/test/stage1/behavior/translate_c_macros.h b/test/stage1/behavior/translate_c_macros.h new file mode 100644 index 0000000000000000000000000000000000000000..abc6c1e3cf078a89c9d2152a7a139b820ef845c6 --- /dev/null +++ b/test/stage1/behavior/translate_c_macros.h @@ -0,0 +1,9 @@ +// initializer list expression +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 \ No newline at end of file diff --git a/test/stage1/behavior/translate_c_macros.zig b/test/stage1/behavior/translate_c_macros.zig new file mode 100644 index 0000000000000000000000000000000000000000..ea42016e9bb6db57f16aeaf89d7b89b78845eafb --- /dev/null +++ b/test/stage1/behavior/translate_c_macros.zig @@ -0,0 +1,12 @@ +const expect = @import("std").testing.expect; + +const h = @cImport(@cInclude("stage1/behavior/translate_c_macros.h")); + +test "initializer list expression" { + @import("std").testing.expectEqual(h.Color{ + .r = 200, + .g = 200, + .b = 200, + .a = 255, + }, h.LIGHTGRAY); +} diff --git a/test/tests.zig b/test/tests.zig index 0cf5ec28dd1ed072b3c00d8e67fa3d825e8571be..6598a05ea79c70f13b307c59d85eb15266a6ab96 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -537,6 +537,7 @@ pub fn addPkgTests( these_tests.enable_qemu = is_qemu_enabled; these_tests.enable_wasmtime = is_wasmtime_enabled; these_tests.glibc_multi_install_dir = glibc_dir; + these_tests.addIncludeDir("test"); step.dependOn(&these_tests.step); } -- 2.54.0