authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-07-16 19:03:03+00:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-07-16 19:03:03+00:00
logcc3bceea3de67f24e6c17d4c04a34a2301e097f8
tree6a05a16e80652903162ac3ccbe37ce8e06a73530
parent3cdc0f104ee375a669d1a322da877df64255976b
parent5e88a7a42724d7ccf9db58e1524beedebb1efd93
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #5889 from Vexu/translate-c

Translate-c support initializer lists in macros

8 files changed, 134 insertions(+), 3 deletions(-)

CONTRIBUTING.md+5
......@@ -152,6 +152,11 @@ The relevant tests for this feature are:
152152 same, and that the program exits cleanly. This kind of test coverage is preferred, when
153153 possible, because it makes sure that the resulting Zig code is actually viable.
154154
155 * `test/stage1/behavior/translate_c_macros.zig` - each test case consists of a Zig test
156 which checks that the relevant macros in `test/stage1/behavior/translate_c_macros.h`.
157 have the correct values. Macros have to be tested separately since they are expanded by
158 Clang in `run_translated_c` tests.
159
155160 * `test/translate_c.zig` - each test case is C code, with a list of expected strings which
156161 must be found in the resulting Zig code. This kind of test is more precise in what it
157162 measures, but does not provide test coverage of whether the resulting Zig code is valid.
lib/std/mem.zig+26-3
......@@ -552,7 +552,7 @@ pub fn zeroes(comptime T: type) T {
552552 if (@sizeOf(T) == 0) return T{};
553553 if (comptime meta.containerLayout(T) == .Extern) {
554554 var item: T = undefined;
555 @memset(@ptrCast([*]u8, &item), 0, @sizeOf(T));
555 set(u8, asBytes(&item), 0);
556556 return item;
557557 } else {
558558 var structure: T = undefined;
......@@ -709,6 +709,14 @@ pub fn zeroInit(comptime T: type, init: anytype) T {
709709 .Struct => |init_info| {
710710 var value = std.mem.zeroes(T);
711711
712 // typeInfo won't tell us if this is a tuple
713 if (comptime eql(u8, init_info.fields[0].name, "0")) {
714 inline for (init_info.fields) |field, i| {
715 @field(value, struct_info.fields[i].name) = @field(init, field.name);
716 }
717 return value;
718 }
719
712720 inline for (init_info.fields) |field| {
713721 if (!@hasField(T, field.name)) {
714722 @compileError("Encountered an initializer for `" ++ field.name ++ "`, but it is not a field of " ++ @typeName(T));
......@@ -760,7 +768,7 @@ test "zeroInit" {
760768 .a = 42,
761769 });
762770
763 testing.expectEqual(s, S{
771 testing.expectEqual(S{
764772 .a = 42,
765773 .b = null,
766774 .c = .{
......@@ -768,7 +776,22 @@ test "zeroInit" {
768776 },
769777 .e = [3]u8{ 0, 0, 0 },
770778 .f = -1,
771 });
779 }, s);
780
781 const Color = struct {
782 r: u8,
783 g: u8,
784 b: u8,
785 a: u8,
786 };
787
788 const c = zeroInit(Color, .{255, 255});
789 testing.expectEqual(Color{
790 .r = 255,
791 .g = 255,
792 .b = 0,
793 .a = 0,
794 }, c);
772795}
773796
774797/// Compares two slices of numbers lexicographically. O(n).
src-self-hosted/translate_c.zig+55
......@@ -6061,6 +6061,61 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
60616061 node = &call_node.base;
60626062 continue;
60636063 },
6064 .LBrace => {
6065 // must come immediately after `node`
6066 _ = try appendToken(c, .Comma, ",");
6067
6068 const dot = try appendToken(c, .Period, ".");
6069 _ = try appendToken(c, .LBrace, "{");
6070
6071 var init_vals = std.ArrayList(*ast.Node).init(c.gpa);
6072 defer init_vals.deinit();
6073
6074 while (true) {
6075 const val = try parseCPrefixOpExpr(c, it, source, source_loc, scope);
6076 try init_vals.append(val);
6077 const next = it.next().?;
6078 if (next.id == .Comma)
6079 _ = try appendToken(c, .Comma, ",")
6080 else if (next.id == .RBrace)
6081 break
6082 else {
6083 const first_tok = it.list.at(0);
6084 try failDecl(
6085 c,
6086 source_loc,
6087 source[first_tok.start..first_tok.end],
6088 "unable to translate C expr: expected ',' or '}}'",
6089 .{},
6090 );
6091 return error.ParseError;
6092 }
6093 }
6094 const tuple_node = try ast.Node.StructInitializerDot.alloc(c.arena, init_vals.items.len);
6095 tuple_node.* = .{
6096 .dot = dot,
6097 .list_len = init_vals.items.len,
6098 .rtoken = try appendToken(c, .RBrace, "}"),
6099 };
6100 mem.copy(*ast.Node, tuple_node.list(), init_vals.items);
6101
6102
6103 //(@import("std").mem.zeroInit(T, .{x}))
6104 const import_fn_call = try c.createBuiltinCall("@import", 1);
6105 const std_node = try transCreateNodeStringLiteral(c, "\"std\"");
6106 import_fn_call.params()[0] = std_node;
6107 import_fn_call.rparen_token = try appendToken(c, .RParen, ")");
6108 const inner_field_access = try transCreateNodeFieldAccess(c, &import_fn_call.base, "mem");
6109 const outer_field_access = try transCreateNodeFieldAccess(c, inner_field_access, "zeroInit");
6110
6111 const zero_init_call = try c.createCall(outer_field_access, 2);
6112 zero_init_call.params()[0] = node;
6113 zero_init_call.params()[1] = &tuple_node.base;
6114 zero_init_call.rtoken = try appendToken(c, .RParen, ")");
6115
6116 node = &zero_init_call.base;
6117 continue;
6118 },
60646119 .BangEqual => {
60656120 op_token = try appendToken(c, .BangEqual, "!=");
60666121 op_id = .BangEqual;
test/stage1/behavior.zig+1
......@@ -133,4 +133,5 @@ comptime {
133133 _ = @import("behavior/while.zig");
134134 _ = @import("behavior/widening.zig");
135135 _ = @import("behavior/src.zig");
136 _ = @import("behavior/translate_c_macros.zig");
136137}
test/stage1/behavior/translate_c_macros.h created+9
......@@ -0,0 +1,9 @@
1// initializer list expression
2typedef struct Color {
3 unsigned char r;
4 unsigned char g;
5 unsigned char b;
6 unsigned char a;
7} Color;
8#define CLITERAL(type) (type)
9#define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 } // Light Gray
\ No newline at end of file
test/stage1/behavior/translate_c_macros.zig created+12
......@@ -0,0 +1,12 @@
1const expect = @import("std").testing.expect;
2
3const h = @cImport(@cInclude("stage1/behavior/translate_c_macros.h"));
4
5test "initializer list expression" {
6 @import("std").testing.expectEqual(h.Color{
7 .r = 200,
8 .g = 200,
9 .b = 200,
10 .a = 255,
11 }, h.LIGHTGRAY);
12}
test/tests.zig+1
......@@ -537,6 +537,7 @@ pub fn addPkgTests(
537537 these_tests.enable_qemu = is_qemu_enabled;
538538 these_tests.enable_wasmtime = is_wasmtime_enabled;
539539 these_tests.glibc_multi_install_dir = glibc_dir;
540 these_tests.addIncludeDir("test");
540541
541542 step.dependOn(&these_tests.step);
542543 }
test/translate_c.zig+25
......@@ -3,6 +3,31 @@ const std = @import("std");
33const CrossTarget = std.zig.CrossTarget;
44
55pub fn addCases(cases: *tests.TranslateCContext) void {
6 cases.add("initializer list macro",
7 \\typedef struct Color {
8 \\ unsigned char r;
9 \\ unsigned char g;
10 \\ unsigned char b;
11 \\ unsigned char a;
12 \\} Color;
13 \\#define CLITERAL(type) (type)
14 \\#define LIGHTGRAY CLITERAL(Color){ 200, 200, 200, 255 } // Light Gray
15 , &[_][]const u8{ // TODO properly translate this
16 \\pub const struct_Color = extern struct {
17 \\ r: u8,
18 \\ g: u8,
19 \\ b: u8,
20 \\ a: u8,
21 \\};
22 \\pub const Color = struct_Color;
23 ,
24 \\pub inline fn CLITERAL(type_1: anytype) @TypeOf(type_1) {
25 \\ return type_1;
26 \\}
27 ,
28 \\pub const LIGHTGRAY = @import("std").mem.zeroInit(CLITERAL(Color), .{ 200, 200, 200, 255 });
29 });
30
631 cases.add("complex switch",
732 \\int main() {
833 \\ int i = 2;