translate-c: support designated initializers in macros

This commit is contained in:
Veikka Tuominen 2021-05-31 21:06:12 +03:00 committed by Andrew Kelley
parent 99b6305aa8
commit c6a0a4e728
3 changed files with 112 additions and 13 deletions

View File

@ -4813,8 +4813,11 @@ fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
const br = blk_last.castTag(.break_val).?;
break :blk br.data.val;
} else expr;
const return_type = if (typeof_arg.castTag(.std_meta_cast)) |some|
const return_type = if (typeof_arg.castTag(.std_meta_cast) orelse typeof_arg.castTag(.std_mem_zeroinit)) |some|
some.data.lhs
else if (typeof_arg.castTag(.std_mem_zeroes)) |some|
some.data
else
try Tag.typeof.create(c.arena, typeof_arg);
@ -4932,6 +4935,7 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node {
}
},
.FloatLiteral => |suffix| {
if (suffix != .none) lit_bytes = lit_bytes[0 .. lit_bytes.len - 1];
const dot_index = mem.indexOfScalar(u8, lit_bytes, '.').?;
if (dot_index == 0) {
lit_bytes = try std.fmt.allocPrint(c.arena, "0{s}", .{lit_bytes});
@ -4943,15 +4947,16 @@ fn parseCNumLit(c: *Context, m: *MacroCtx) ParseError!Node {
lit_bytes[dot_index + 1 ..],
});
}
if (suffix == .none) {
if (suffix == .none)
return transCreateNodeNumber(c, lit_bytes, .float);
}
const type_node = try Tag.type.create(c.arena, switch (suffix) {
.f => "f32",
.l => "c_longdouble",
else => unreachable,
});
const rhs = try transCreateNodeNumber(c, lit_bytes[0 .. lit_bytes.len - 1], .float);
const rhs = try transCreateNodeNumber(c, lit_bytes, .float);
return Tag.as.create(c.arena, .{ .lhs = type_node, .rhs = rhs });
},
else => unreachable,
@ -5540,6 +5545,42 @@ fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
}
},
.LBrace => {
// Check for designated field initializers
if (m.peek().? == .Period) {
var init_vals = std.ArrayList(ast.Payload.ContainerInitDot.Initializer).init(c.gpa);
defer init_vals.deinit();
while (true) {
if (m.next().? != .Period) {
try m.fail(c, "unable to translate C expr: expected '.'", .{});
return error.ParseError;
}
if (m.next().? != .Identifier) {
try m.fail(c, "unable to translate C expr: expected identifier", .{});
return error.ParseError;
}
const name = m.slice();
if (m.next().? != .Equal) {
try m.fail(c, "unable to translate C expr: expected '='", .{});
return error.ParseError;
}
const val = try parseCCondExpr(c, m, scope);
try init_vals.append(.{ .name = name, .value = val });
switch (m.next().?) {
.Comma => {},
.RBrace => break,
else => {
try m.fail(c, "unable to translate C expr: expected ',' or '}}'", .{});
return error.ParseError;
},
}
}
const tuple_node = try Tag.container_init_dot.create(c.arena, try c.arena.dupe(ast.Payload.ContainerInitDot.Initializer, init_vals.items));
node = try Tag.std_mem_zeroinit.create(c.arena, .{ .lhs = node, .rhs = tuple_node });
continue;
}
var init_vals = std.ArrayList(Node).init(c.gpa);
defer init_vals.deinit();

View File

@ -71,6 +71,7 @@ pub const Node = extern union {
array_init,
tuple,
container_init,
container_init_dot,
std_meta_cast,
/// _ = operand;
discard,
@ -332,6 +333,7 @@ pub const Node = extern union {
.ptr_cast,
.div_exact,
.byte_offset_of,
.std_meta_cast,
=> Payload.BinOp,
.integer_literal,
@ -354,7 +356,7 @@ pub const Node = extern union {
.@"struct", .@"union" => Payload.Record,
.tuple => Payload.TupleInit,
.container_init => Payload.ContainerInit,
.std_meta_cast => Payload.Infix,
.container_init_dot => Payload.ContainerInitDot,
.std_meta_promoteIntLiteral => Payload.PromoteIntLiteral,
.block => Payload.Block,
.c_pointer, .single_pointer => Payload.Pointer,
@ -448,14 +450,6 @@ pub const Node = extern union {
pub const Payload = struct {
tag: Node.Tag,
pub const Infix = struct {
base: Payload,
data: struct {
lhs: Node,
rhs: Node,
},
};
pub const Value = struct {
base: Payload,
data: []const u8,
@ -600,6 +594,16 @@ pub const Payload = struct {
};
};
pub const ContainerInitDot = struct {
base: Payload,
data: []Initializer,
pub const Initializer = struct {
name: []const u8,
value: Node,
};
};
pub const Block = struct {
base: Payload,
data: struct {
@ -1893,6 +1897,44 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
});
}
},
.container_init_dot => {
const payload = node.castTag(.container_init_dot).?.data;
_ = try c.addToken(.period, ".");
const l_brace = try c.addToken(.l_brace, "{");
var inits = try c.gpa.alloc(NodeIndex, std.math.max(payload.len, 2));
defer c.gpa.free(inits);
inits[0] = 0;
inits[1] = 0;
for (payload) |init, i| {
_ = try c.addToken(.period, ".");
_ = try c.addIdentifier(init.name);
_ = try c.addToken(.equal, "=");
inits[i] = try renderNode(c, init.value);
_ = try c.addToken(.comma, ",");
}
_ = try c.addToken(.r_brace, "}");
if (payload.len < 3) {
return c.addNode(.{
.tag = .struct_init_dot_two_comma,
.main_token = l_brace,
.data = .{
.lhs = inits[0],
.rhs = inits[1],
},
});
} else {
const span = try c.listToSpan(inits);
return c.addNode(.{
.tag = .struct_init_dot_comma,
.main_token = l_brace,
.data = .{
.lhs = span.start,
.rhs = span.end,
},
});
}
},
.container_init => {
const payload = node.castTag(.container_init).?.data;
const lhs = try renderNode(c, payload.lhs);
@ -2257,6 +2299,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
.array_init,
.tuple,
.container_init,
.container_init_dot,
.block,
=> return c.addNode(.{
.tag = .grouped_expression,

View File

@ -336,6 +336,9 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\ int i1;
\\} boom_t;
\\#define FOO ((boom_t){1})
\\typedef struct { float x; } MyCStruct;
\\#define A(_x) (MyCStruct) { .x = (_x) }
\\#define B A(0.f)
, &[_][]const u8{
\\pub const struct_Color = extern struct {
\\ r: u8,
@ -357,6 +360,18 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
\\pub const boom_t = struct_boom_t;
,
\\pub const FOO = @import("std").mem.zeroInit(boom_t, .{@as(c_int, 1)});
,
\\pub const MyCStruct = extern struct {
\\ x: f32,
\\};
,
\\pub inline fn A(_x: anytype) MyCStruct {
\\ return @import("std").mem.zeroInit(MyCStruct, .{
\\ .x = _x,
\\ });
\\}
,
\\pub const B = A(@as(f32, 0.0));
});
cases.add("complex switch",