| author | |
| committer | |
| log | cc3bceea3de67f24e6c17d4c04a34a2301e097f8 |
| tree | 6a05a16e80652903162ac3ccbe37ce8e06a73530 |
| parent | 3cdc0f104ee375a669d1a322da877df64255976b |
| parent | 5e88a7a42724d7ccf9db58e1524beedebb1efd93 |
| signature |
Translate-c support initializer lists in macros8 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, when | 152 | 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. |
| 154 | 154 | ||
| 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 which | 160 | * `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 it | 161 | 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); |
| 711 | 711 | ||
| 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 | }); |
| 762 | 770 | ||
| 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 | } |
| 773 | 796 | ||
| 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 | ||
| 2 | typedef 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 @@ | ||
| 1 | const expect = @import("std").testing.expect; | ||
| 2 | |||
| 3 | const h = @cImport(@cInclude("stage1/behavior/translate_c_macros.h")); | ||
| 4 | |||
| 5 | test "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"); | ||
| 540 | 541 | ||
| 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"); |
| 3 | const CrossTarget = std.zig.CrossTarget; | 3 | const CrossTarget = std.zig.CrossTarget; |
| 4 | 4 | ||
| 5 | pub fn addCases(cases: *tests.TranslateCContext) void { | 5 | pub 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; |