Merge pull request #5889 from Vexu/translate-c
Translate-c support initializer lists in macros
This commit is contained in:
commit
cc3bceea3d
@ -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
|
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.
|
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
|
* `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
|
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.
|
measures, but does not provide test coverage of whether the resulting Zig code is valid.
|
||||||
|
@ -552,7 +552,7 @@ pub fn zeroes(comptime T: type) T {
|
|||||||
if (@sizeOf(T) == 0) return T{};
|
if (@sizeOf(T) == 0) return T{};
|
||||||
if (comptime meta.containerLayout(T) == .Extern) {
|
if (comptime meta.containerLayout(T) == .Extern) {
|
||||||
var item: T = undefined;
|
var item: T = undefined;
|
||||||
@memset(@ptrCast([*]u8, &item), 0, @sizeOf(T));
|
set(u8, asBytes(&item), 0);
|
||||||
return item;
|
return item;
|
||||||
} else {
|
} else {
|
||||||
var structure: T = undefined;
|
var structure: T = undefined;
|
||||||
@ -709,6 +709,14 @@ pub fn zeroInit(comptime T: type, init: anytype) T {
|
|||||||
.Struct => |init_info| {
|
.Struct => |init_info| {
|
||||||
var value = std.mem.zeroes(T);
|
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| {
|
inline for (init_info.fields) |field| {
|
||||||
if (!@hasField(T, field.name)) {
|
if (!@hasField(T, field.name)) {
|
||||||
@compileError("Encountered an initializer for `" ++ field.name ++ "`, but it is not a field of " ++ @typeName(T));
|
@compileError("Encountered an initializer for `" ++ field.name ++ "`, but it is not a field of " ++ @typeName(T));
|
||||||
@ -760,7 +768,7 @@ test "zeroInit" {
|
|||||||
.a = 42,
|
.a = 42,
|
||||||
});
|
});
|
||||||
|
|
||||||
testing.expectEqual(s, S{
|
testing.expectEqual(S{
|
||||||
.a = 42,
|
.a = 42,
|
||||||
.b = null,
|
.b = null,
|
||||||
.c = .{
|
.c = .{
|
||||||
@ -768,7 +776,22 @@ test "zeroInit" {
|
|||||||
},
|
},
|
||||||
.e = [3]u8{ 0, 0, 0 },
|
.e = [3]u8{ 0, 0, 0 },
|
||||||
.f = -1,
|
.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).
|
/// Compares two slices of numbers lexicographically. O(n).
|
||||||
|
@ -6061,6 +6061,61 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8,
|
|||||||
node = &call_node.base;
|
node = &call_node.base;
|
||||||
continue;
|
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 => {
|
.BangEqual => {
|
||||||
op_token = try appendToken(c, .BangEqual, "!=");
|
op_token = try appendToken(c, .BangEqual, "!=");
|
||||||
op_id = .BangEqual;
|
op_id = .BangEqual;
|
||||||
|
@ -133,4 +133,5 @@ comptime {
|
|||||||
_ = @import("behavior/while.zig");
|
_ = @import("behavior/while.zig");
|
||||||
_ = @import("behavior/widening.zig");
|
_ = @import("behavior/widening.zig");
|
||||||
_ = @import("behavior/src.zig");
|
_ = @import("behavior/src.zig");
|
||||||
|
_ = @import("behavior/translate_c_macros.zig");
|
||||||
}
|
}
|
||||||
|
9
test/stage1/behavior/translate_c_macros.h
Normal file
9
test/stage1/behavior/translate_c_macros.h
Normal file
@ -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
|
12
test/stage1/behavior/translate_c_macros.zig
Normal file
12
test/stage1/behavior/translate_c_macros.zig
Normal file
@ -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);
|
||||||
|
}
|
@ -537,6 +537,7 @@ pub fn addPkgTests(
|
|||||||
these_tests.enable_qemu = is_qemu_enabled;
|
these_tests.enable_qemu = is_qemu_enabled;
|
||||||
these_tests.enable_wasmtime = is_wasmtime_enabled;
|
these_tests.enable_wasmtime = is_wasmtime_enabled;
|
||||||
these_tests.glibc_multi_install_dir = glibc_dir;
|
these_tests.glibc_multi_install_dir = glibc_dir;
|
||||||
|
these_tests.addIncludeDir("test");
|
||||||
|
|
||||||
step.dependOn(&these_tests.step);
|
step.dependOn(&these_tests.step);
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,31 @@ const std = @import("std");
|
|||||||
const CrossTarget = std.zig.CrossTarget;
|
const CrossTarget = std.zig.CrossTarget;
|
||||||
|
|
||||||
pub fn addCases(cases: *tests.TranslateCContext) void {
|
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",
|
cases.add("complex switch",
|
||||||
\\int main() {
|
\\int main() {
|
||||||
\\ int i = 2;
|
\\ int i = 2;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user