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:...@@ -152,6 +152,11 @@ The relevant tests for this feature are:
152 same, and that the program exits cleanly. This kind of test coverage is preferred, when152 same, and that the program exits cleanly. This kind of test coverage is preferred, when
153 possible, because it makes sure that the resulting Zig code is actually viable.153 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
155 * `test/translate_c.zig` - each test case is C code, with a list of expected strings which160 * `test/translate_c.zig` - each test case is C code, with a list of expected strings which
156 must be found in the resulting Zig code. This kind of test is more precise in what it161 must be found in the resulting Zig code. This kind of test is more precise in what it
157 measures, but does not provide test coverage of whether the resulting Zig code is valid.162 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 {...@@ -552,7 +552,7 @@ pub fn zeroes(comptime T: type) T {
552 if (@sizeOf(T) == 0) return T{};552 if (@sizeOf(T) == 0) return T{};
553 if (comptime meta.containerLayout(T) == .Extern) {553 if (comptime meta.containerLayout(T) == .Extern) {
554 var item: T = undefined;554 var item: T = undefined;
555 @memset(@ptrCast([*]u8, &item), 0, @sizeOf(T));555 set(u8, asBytes(&item), 0);
556 return item;556 return item;
557 } else {557 } else {
558 var structure: T = undefined;558 var structure: T = undefined;
...@@ -709,6 +709,14 @@ pub fn zeroInit(comptime T: type, init: anytype) T {...@@ -709,6 +709,14 @@ pub fn zeroInit(comptime T: type, init: anytype) T {
709 .Struct => |init_info| {709 .Struct => |init_info| {
710 var value = std.mem.zeroes(T);710 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
712 inline for (init_info.fields) |field| {720 inline for (init_info.fields) |field| {
713 if (!@hasField(T, field.name)) {721 if (!@hasField(T, field.name)) {
714 @compileError("Encountered an initializer for `" ++ field.name ++ "`, but it is not a field of " ++ @typeName(T));722 @compileError("Encountered an initializer for `" ++ field.name ++ "`, but it is not a field of " ++ @typeName(T));
...@@ -760,7 +768,7 @@ test "zeroInit" {...@@ -760,7 +768,7 @@ test "zeroInit" {
760 .a = 42,768 .a = 42,
761 });769 });
762770
763 testing.expectEqual(s, S{771 testing.expectEqual(S{
764 .a = 42,772 .a = 42,
765 .b = null,773 .b = null,
766 .c = .{774 .c = .{
...@@ -768,7 +776,22 @@ test "zeroInit" {...@@ -768,7 +776,22 @@ test "zeroInit" {
768 },776 },
769 .e = [3]u8{ 0, 0, 0 },777 .e = [3]u8{ 0, 0, 0 },
770 .f = -1,778 .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);
772}795}
773796
774/// Compares two slices of numbers lexicographically. O(n).797/// 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,...@@ -6061,6 +6061,61 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
6061 node = &call_node.base;6061 node = &call_node.base;
6062 continue;6062 continue;
6063 },6063 },
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 },
6064 .BangEqual => {6119 .BangEqual => {
6065 op_token = try appendToken(c, .BangEqual, "!=");6120 op_token = try appendToken(c, .BangEqual, "!=");
6066 op_id = .BangEqual;6121 op_id = .BangEqual;
test/stage1/behavior.zig+1
...@@ -133,4 +133,5 @@ comptime {...@@ -133,4 +133,5 @@ comptime {
133 _ = @import("behavior/while.zig");133 _ = @import("behavior/while.zig");
134 _ = @import("behavior/widening.zig");134 _ = @import("behavior/widening.zig");
135 _ = @import("behavior/src.zig");135 _ = @import("behavior/src.zig");
136 _ = @import("behavior/translate_c_macros.zig");
136}137}
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(...@@ -537,6 +537,7 @@ pub fn addPkgTests(
537 these_tests.enable_qemu = is_qemu_enabled;537 these_tests.enable_qemu = is_qemu_enabled;
538 these_tests.enable_wasmtime = is_wasmtime_enabled;538 these_tests.enable_wasmtime = is_wasmtime_enabled;
539 these_tests.glibc_multi_install_dir = glibc_dir;539 these_tests.glibc_multi_install_dir = glibc_dir;
540 these_tests.addIncludeDir("test");
540541
541 step.dependOn(&these_tests.step);542 step.dependOn(&these_tests.step);
542 }543 }
test/translate_c.zig+25
...@@ -3,6 +3,31 @@ const std = @import("std");...@@ -3,6 +3,31 @@ const std = @import("std");
3const CrossTarget = std.zig.CrossTarget;3const CrossTarget = std.zig.CrossTarget;
44
5pub fn addCases(cases: *tests.TranslateCContext) void {5pub 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
6 cases.add("complex switch",31 cases.add("complex switch",
7 \\int main() {32 \\int main() {
8 \\ int i = 2;33 \\ int i = 2;