authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-04-11 15:05:16-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-04-11 15:17:46-07:00
log5149128d228458993ccb212eb875fd43c23595b2
tree077f6ff6cb900fc103f3827a52be057515309515
parent67c6ac947a2945974c375977c237eac073d81f56

update translate-c to latest

upstream commit 46b5609b5ac4c0a896217d1d984f3ae50e4810b5

7 files changed, 596 insertions(+), 188 deletions(-)

lib/compiler/translate-c/MacroTranslator.zig+80-11
...@@ -266,7 +266,8 @@ fn parseCNumLit(mt: *MacroTranslator) ParseError!ZigNode {...@@ -266,7 +266,8 @@ fn parseCNumLit(mt: *MacroTranslator) ParseError!ZigNode {
266 const lit_bytes = mt.tokSlice();266 const lit_bytes = mt.tokSlice();
267 mt.i += 1;267 mt.i += 1;
268268
269 var bytes = try std.ArrayList(u8).initCapacity(arena, lit_bytes.len + 3);269 // +3 for prefix and +2 for suffix
270 var bytes = try std.ArrayList(u8).initCapacity(arena, lit_bytes.len + 3 + 2);
270271
271 const prefix = aro.Tree.Token.NumberPrefix.fromString(lit_bytes);272 const prefix = aro.Tree.Token.NumberPrefix.fromString(lit_bytes);
272 switch (prefix) {273 switch (prefix) {
...@@ -350,13 +351,21 @@ fn parseCNumLit(mt: *MacroTranslator) ParseError!ZigNode {...@@ -350,13 +351,21 @@ fn parseCNumLit(mt: *MacroTranslator) ParseError!ZigNode {
350 if (is_float) {351 if (is_float) {
351 const type_node = try ZigTag.type.create(arena, switch (suffix) {352 const type_node = try ZigTag.type.create(arena, switch (suffix) {
352 .F16 => "f16",353 .F16 => "f16",
353 .F => "f32",354 .F, .F32 => "f32",
354 .None => "f64",355 .None, .F32x, .F64 => "f64",
355 .L => "c_longdouble",356 .L, .F64x => "c_longdouble",
356 .W => "f80",357 .W => "f80",
357 .Q, .F128 => "f128",358 .Q, .F128 => "f128",
358 else => unreachable,359 else => {
360 try mt.fail("TODO: float literal suffix: '{s}'", .{suffix_str});
361 return error.ParseError;
362 },
359 });363 });
364 if (bytes.getLast() == '.') {
365 bytes.appendAssumeCapacity('0');
366 } else if (mem.findAny(u8, bytes.items, ".eEpP") == null) {
367 bytes.appendSliceAssumeCapacity(".0");
368 }
360 const rhs = try ZigTag.float_literal.create(arena, bytes.items);369 const rhs = try ZigTag.float_literal.create(arena, bytes.items);
361 return ZigTag.as.create(arena, .{ .lhs = type_node, .rhs = rhs });370 return ZigTag.as.create(arena, .{ .lhs = type_node, .rhs = rhs });
362 } else {371 } else {
...@@ -582,6 +591,7 @@ fn escapeUnprintables(mt: *MacroTranslator) ![]const u8 {...@@ -582,6 +591,7 @@ fn escapeUnprintables(mt: *MacroTranslator) ![]const u8 {
582591
583fn parseCPrimaryExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {592fn parseCPrimaryExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
584 const arena = mt.t.arena;593 const arena = mt.t.arena;
594 const gpa = mt.t.gpa;
585 const tok = mt.peek();595 const tok = mt.peek();
586 switch (tok) {596 switch (tok) {
587 .char_literal,597 .char_literal,
...@@ -646,6 +656,51 @@ fn parseCPrimaryExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {...@@ -646,6 +656,51 @@ fn parseCPrimaryExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
646 }656 }
647 return identifier;657 return identifier;
648 },658 },
659 .keyword_generic => {
660 mt.i += 1;
661
662 try mt.expect(.l_paren);
663 const param = try mt.parseCCondExpr(scope);
664 const typeof_param = try ZigTag.typeof.create(arena, param);
665 try mt.expect(.comma);
666
667 var cases: std.ArrayList(ZigNode) = .empty;
668 defer cases.deinit(gpa);
669 var has_default = false;
670 while (true) {
671 const case = if (mt.eat(.keyword_default)) blk: {
672 has_default = true;
673 try mt.expect(.colon);
674 const expr = try mt.parseCCondExpr(scope);
675 break :blk try ZigTag.switch_else.create(arena, expr);
676 } else blk: {
677 const case_type = try mt.parseCTypeName(scope) orelse {
678 try mt.fail("unable to translate C expr: expected type instead got '{s}'", .{mt.peek().symbol()});
679 return error.ParseError;
680 };
681 try mt.expect(.colon);
682 const expr = try mt.parseCCondExpr(scope);
683 break :blk try ZigTag.switch_prong.create(arena, .{
684 .cases = try arena.dupe(ZigNode, &.{case_type}),
685 .cond = expr,
686 });
687 };
688 try cases.append(gpa, case);
689 if (!mt.eat(.comma)) break;
690 }
691 try mt.expect(.r_paren);
692
693 if (!has_default) try cases.append(gpa, try ZigTag.switch_else.create(
694 arena,
695 try ZigTag.@"comptime".create(arena, ZigTag.@"unreachable".init()),
696 ));
697
698 const sw = try ZigTag.@"switch".create(arena, .{
699 .cond = typeof_param,
700 .cases = try arena.dupe(ZigNode, cases.items),
701 });
702 return sw;
703 },
649 else => {},704 else => {},
650 }705 }
651706
...@@ -678,8 +733,10 @@ fn macroIntToBool(mt: *MacroTranslator, node: ZigNode) !ZigNode {...@@ -678,8 +733,10 @@ fn macroIntToBool(mt: *MacroTranslator, node: ZigNode) !ZigNode {
678}733}
679734
680fn parseCCondExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {735fn parseCCondExpr(mt: *MacroTranslator, scope: *Scope) ParseError!ZigNode {
681 const node = try mt.parseCOrExpr(scope);736 const condition = try mt.parseCOrExpr(scope);
682 if (!mt.eat(.question_mark)) return node;737 if (!mt.eat(.question_mark)) return condition;
738 const bool_ty = try ZigTag.type.create(mt.t.arena, "bool");
739 const node = try mt.t.createHelperCallNode(.cast, &.{ bool_ty, condition });
683740
684 const then_body = try mt.parseCOrExpr(scope);741 const then_body = try mt.parseCOrExpr(scope);
685 try mt.expect(.colon);742 try mt.expect(.colon);
...@@ -1135,6 +1192,8 @@ fn parseCPostfixExpr(mt: *MacroTranslator, scope: *Scope, type_name: ?ZigNode) P...@@ -1135,6 +1192,8 @@ fn parseCPostfixExpr(mt: *MacroTranslator, scope: *Scope, type_name: ?ZigNode) P
1135 .string_literal_utf_8,1192 .string_literal_utf_8,
1136 .string_literal_utf_32,1193 .string_literal_utf_32,
1137 .string_literal_wide,1194 .string_literal_wide,
1195 .macro_param,
1196 .macro_param_no_expand,
1138 => {},1197 => {},
1139 .identifier, .extended_identifier => {1198 .identifier, .extended_identifier => {
1140 if (mt.t.global_scope.blank_macros.contains(mt.tokSlice())) {1199 if (mt.t.global_scope.blank_macros.contains(mt.tokSlice())) {
...@@ -1160,8 +1219,13 @@ fn parseCPostfixExprInner(mt: *MacroTranslator, scope: *Scope, type_name: ?ZigNo...@@ -1160,8 +1219,13 @@ fn parseCPostfixExprInner(mt: *MacroTranslator, scope: *Scope, type_name: ?ZigNo
1160 mt.i += 1;1219 mt.i += 1;
1161 const tok = mt.tokens[mt.i];1220 const tok = mt.tokens[mt.i];
1162 if (tok.id == .macro_param or tok.id == .macro_param_no_expand) {1221 if (tok.id == .macro_param or tok.id == .macro_param_no_expand) {
1163 try mt.fail("unable to translate C expr: field access using macro parameter", .{});1222 const param = mt.macro.params[tok.end];
1164 return error.ParseError;1223 mt.i += 1;
1224
1225 const mangled_name = scope.getAlias(param) orelse param;
1226 const field_name = try ZigTag.identifier.create(arena, mangled_name);
1227 node = try ZigTag.field_builtin.create(arena, .{ .lhs = node, .rhs = field_name });
1228 continue;
1165 }1229 }
1166 const field_name = mt.tokSlice();1230 const field_name = mt.tokSlice();
1167 try mt.expect(.identifier);1231 try mt.expect(.identifier);
...@@ -1172,8 +1236,13 @@ fn parseCPostfixExprInner(mt: *MacroTranslator, scope: *Scope, type_name: ?ZigNo...@@ -1172,8 +1236,13 @@ fn parseCPostfixExprInner(mt: *MacroTranslator, scope: *Scope, type_name: ?ZigNo
1172 mt.i += 1;1236 mt.i += 1;
1173 const tok = mt.tokens[mt.i];1237 const tok = mt.tokens[mt.i];
1174 if (tok.id == .macro_param or tok.id == .macro_param_no_expand) {1238 if (tok.id == .macro_param or tok.id == .macro_param_no_expand) {
1175 try mt.fail("unable to translate C expr: field access using macro parameter", .{});1239 const param = mt.macro.params[tok.end];
1176 return error.ParseError;1240 mt.i += 1;
1241
1242 const mangled_name = scope.getAlias(param) orelse param;
1243 const field_name = try ZigTag.identifier.create(arena, mangled_name);
1244 node = try ZigTag.field_builtin.create(arena, .{ .lhs = node, .rhs = field_name });
1245 continue;
1177 }1246 }
1178 const field_name = mt.tokSlice();1247 const field_name = mt.tokSlice();
1179 try mt.expect(.identifier);1248 try mt.expect(.identifier);
lib/compiler/translate-c/PatternList.zig+1-9
...@@ -65,10 +65,7 @@ const templates = [_]Template{...@@ -65,10 +65,7 @@ const templates = [_]Template{
65 .{ "CAST_OR_CALL(X, Y) ((X)(Y))", .CAST_OR_CALL },65 .{ "CAST_OR_CALL(X, Y) ((X)(Y))", .CAST_OR_CALL },
6666
67 .{67 .{
68 \\wl_container_of(ptr, sample, member) \68 "wl_container_of(ptr, sample, member) (__typeof__(sample))((char *)(ptr) - offsetof(__typeof__(*sample), member))",
69 \\(__typeof__(sample))((char *)(ptr) - \
70 \\ offsetof(__typeof__(*sample), member))
71 ,
72 .WL_CONTAINER_OF,69 .WL_CONTAINER_OF,
73 },70 },
7471
...@@ -267,11 +264,6 @@ test "Macro matching" {...@@ -267,11 +264,6 @@ test "Macro matching" {
267 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## LL)", .LL_SUFFIX);264 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## LL)", .LL_SUFFIX);
268 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## UL)", .UL_SUFFIX);265 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## UL)", .UL_SUFFIX);
269 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## ULL)", .ULL_SUFFIX);266 try helper.checkMacro(allocator, pattern_list, "BAR(Z) (Z ## ULL)", .ULL_SUFFIX);
270 try helper.checkMacro(allocator, pattern_list,
271 \\container_of(a, b, c) \
272 \\(__typeof__(b))((char *)(a) - \
273 \\ offsetof(__typeof__(*b), c))
274 , .WL_CONTAINER_OF);
275267
276 try helper.checkMacro(allocator, pattern_list, "NO_MATCH(X, Y) (X + Y)", null);268 try helper.checkMacro(allocator, pattern_list, "NO_MATCH(X, Y) (X + Y)", null);
277 try helper.checkMacro(allocator, pattern_list, "CAST_OR_CALL(X, Y) (X)(Y)", .CAST_OR_CALL);269 try helper.checkMacro(allocator, pattern_list, "CAST_OR_CALL(X, Y) (X)(Y)", .CAST_OR_CALL);
lib/compiler/translate-c/Scope.zig+43-6
...@@ -18,7 +18,22 @@ pub const ContainerMemberFns = struct {...@@ -18,7 +18,22 @@ pub const ContainerMemberFns = struct {
18 container_decl_ptr: *ast.Node,18 container_decl_ptr: *ast.Node,
19 member_fns: std.ArrayList(*ast.Payload.Func) = .empty,19 member_fns: std.ArrayList(*ast.Payload.Func) = .empty,
20};20};
21pub const ContainerMemberFnsHashMap = std.AutoArrayHashMapUnmanaged(aro.QualType, ContainerMemberFns);21pub const ContainerMemberFnsHashMap = std.ArrayHashMapUnmanaged(
22 aro.QualType,
23 ContainerMemberFns,
24 struct {
25 pub fn hash(self: @This(), key: aro.QualType) u32 {
26 const auto_hash = std.array_hash_map.getAutoHashFn(aro.QualType, @This());
27 return auto_hash(self, key.unqualified());
28 }
29
30 pub fn eql(self: @This(), a: aro.QualType, b: aro.QualType, b_index: usize) bool {
31 const auto_eql = std.array_hash_map.getAutoEqlFn(aro.QualType, @This());
32 return auto_eql(self, a.unqualified(), b.unqualified(), b_index);
33 }
34 },
35 false,
36);
2237
23id: Id,38id: Id,
24parent: ?*Scope,39parent: ?*Scope,
...@@ -254,7 +269,12 @@ pub const Root = struct {...@@ -254,7 +269,12 @@ pub const Root = struct {
254269
255 var member_names: std.StringArrayHashMapUnmanaged(void) = .empty;270 var member_names: std.StringArrayHashMapUnmanaged(void) = .empty;
256 defer member_names.deinit(gpa);271 defer member_names.deinit(gpa);
257 for (root.container_member_fns_map.values()) |members| {272 for (root.container_member_fns_map.keys(), root.container_member_fns_map.values()) |container_qt, members| {
273 // Get the container name
274 const container_name = root.translator.unnamed_typedefs.get(container_qt) orelse
275 container_qt.getRecord(root.translator.comp).?.name.lookup(root.translator.comp);
276 std.debug.assert(container_name.len > 0);
277
258 member_names.clearRetainingCapacity();278 member_names.clearRetainingCapacity();
259 const decls_ptr = switch (members.container_decl_ptr.tag()) {279 const decls_ptr = switch (members.container_decl_ptr.tag()) {
260 .@"struct", .@"union" => blk_record: {280 .@"struct", .@"union" => blk_record: {
...@@ -274,7 +294,7 @@ pub const Root = struct {...@@ -274,7 +294,7 @@ pub const Root = struct {
274 members.container_decl_ptr.* = container_decl;294 members.container_decl_ptr.* = container_decl;
275 break :blk_opaque &container_decl.castTag(.@"opaque").?.data.decls;295 break :blk_opaque &container_decl.castTag(.@"opaque").?.data.decls;
276 },296 },
277 else => return,297 else => continue,
278 };298 };
279299
280 const old_decls = decls_ptr.*;300 const old_decls = decls_ptr.*;
...@@ -299,9 +319,26 @@ pub const Root = struct {...@@ -299,9 +319,26 @@ pub const Root = struct {
299319
300 for (members.member_fns.items) |func| {320 for (members.member_fns.items) |func| {
301 const func_name = func.data.name.?;321 const func_name = func.data.name.?;
302 const func_name_trimmed = std.mem.trimEnd(u8, func_name, "_");322 const func_name_alias = blk: {
303 const last_idx = std.mem.findLast(u8, func_name_trimmed, "_") orelse continue;323 // Try multiple candidate prefixes to extract the alias
304 const func_name_alias = func_name[last_idx + 1 ..];324 // 1. typedef struct { ... } foo; -> foo_get_bar() extracts "get_bar"
325 // 2. typedef struct _foo foo; -> foo_get_bar() extracts "get_bar"
326 const container_name_trimmed = std.mem.trimStart(u8, container_name, "_");
327 const suffix = std.mem.cutPrefix(u8, func_name, container_name_trimmed);
328 // Check suffix starts with '_' to avoid invalid aliases like "1_get_bar" from foo1_get_bar()
329 if (suffix) |alias| if (alias.len > 0 and alias[0] == '_') {
330 const alias_trimmed = std.mem.trimStart(u8, alias, "_");
331 if (alias_trimmed.len > 0) break :blk alias_trimmed;
332 };
333
334 // Doesn't match any prefix - fallback to trimming trailing underscores and using last segment
335 const func_name_trimmed = std.mem.trimEnd(u8, func_name, "_");
336 const last_idx = std.mem.findLast(u8, func_name_trimmed, "_") orelse continue;
337 break :blk func_name[last_idx + 1 ..];
338 };
339
340 // Skip if the alias conflicts with an existing type
341 if (root.contains(func_name_alias)) continue;
305 const member_name_slot = try member_names.getOrPutValue(gpa, func_name_alias, {});342 const member_name_slot = try member_names.getOrPutValue(gpa, func_name_alias, {});
306 if (member_name_slot.found_existing) continue;343 if (member_name_slot.found_existing) continue;
307 func_ref_vars[count] = try ast.Node.Tag.pub_var_simple.create(arena, .{344 func_ref_vars[count] = try ast.Node.Tag.pub_var_simple.create(arena, .{
lib/compiler/translate-c/Translator.zig+364-126
...@@ -19,10 +19,63 @@ const MacroTranslator = @import("MacroTranslator.zig");...@@ -19,10 +19,63 @@ const MacroTranslator = @import("MacroTranslator.zig");
19const PatternList = @import("PatternList.zig");19const PatternList = @import("PatternList.zig");
20const Scope = @import("Scope.zig");20const Scope = @import("Scope.zig");
2121
22const AnonymousRecordFieldNames = struct {
23 pub const Key = struct {
24 parent: QualType,
25 field: QualType,
26 };
27
28 pub const Context = struct {
29 pub fn hash(ctx: Context, key: Key) u64 {
30 const auto_hash = std.hash_map.getAutoHashFn(Key, Context);
31 return auto_hash(ctx, .{
32 .parent = key.parent.unqualified(),
33 .field = key.field.unqualified(),
34 });
35 }
36
37 pub fn eql(ctx: Context, a: Key, b: Key) bool {
38 const auto_eql = std.hash_map.getAutoEqlFn(Key, Context);
39 return auto_eql(ctx, .{
40 .parent = a.parent.unqualified(),
41 .field = a.field.unqualified(),
42 }, .{
43 .parent = b.parent.unqualified(),
44 .field = b.field.unqualified(),
45 });
46 }
47 };
48};
49
50pub const QualTypeHashContext = struct {
51 pub fn hash(ctx: QualTypeHashContext, key: QualType) u64 {
52 const auto_hash = std.hash_map.getAutoHashFn(QualType, QualTypeHashContext);
53 return auto_hash(ctx, key.unqualified());
54 }
55
56 pub fn eql(ctx: QualTypeHashContext, a: QualType, b: QualType) bool {
57 const auto_eql = std.hash_map.getAutoEqlFn(QualType, QualTypeHashContext);
58 return auto_eql(ctx, a.unqualified(), b.unqualified());
59 }
60};
61
22pub const Error = std.mem.Allocator.Error;62pub const Error = std.mem.Allocator.Error;
23pub const MacroProcessingError = Error || error{UnexpectedMacroToken};63pub const MacroProcessingError = Error || error{UnexpectedMacroToken};
24pub const TypeError = Error || error{UnsupportedType};64pub const TypeError = Error || error{UnsupportedType};
25pub const TransError = TypeError || error{UnsupportedTranslation};65pub const TransError = TypeError || error{ UnsupportedTranslation, SelfReferential };
66
67/// Control when to treat a trailing array as a flexible array member.
68/// Mirrors the -fstrict-flex-arrays=<n> compiler flag.
69pub const StrictFlexArraysLevel = enum {
70 /// Any trailing array member is a flexible array.
71 @"0",
72 /// Trailing arrays of size 0, 1, or undefined are flexible.
73 @"1",
74 /// Trailing arrays of size 0 or undefined are flexible (default).
75 @"2",
76 /// Only trailing arrays of undefined size are flexible.
77 @"3",
78};
2679
27const Translator = @This();80const Translator = @This();
2881
...@@ -33,6 +86,17 @@ comp: *aro.Compilation,...@@ -33,6 +86,17 @@ comp: *aro.Compilation,
33/// The Preprocessor that produced the source for `tree`.86/// The Preprocessor that produced the source for `tree`.
34pp: *const aro.Preprocessor,87pp: *const aro.Preprocessor,
3588
89/// Should static functions be translated as `pub`.
90pub_static: bool,
91/// Should function bodies be translated.
92func_bodies: bool,
93/// Should macro names of literals be preserved.
94keep_macro_literals: bool,
95/// Should struct fields be default initialized.
96default_init: bool,
97/// Control when to treat a trailing array as a flexible array member.
98strict_flex_arrays: StrictFlexArraysLevel,
99
36gpa: mem.Allocator,100gpa: mem.Allocator,
37arena: mem.Allocator,101arena: mem.Allocator,
38102
...@@ -44,14 +108,16 @@ mangle_count: u32 = 0,...@@ -44,14 +108,16 @@ mangle_count: u32 = 0,
44/// Table of declarations for enum, struct, union and typedef types.108/// Table of declarations for enum, struct, union and typedef types.
45type_decls: std.AutoArrayHashMapUnmanaged(Node.Index, []const u8) = .empty,109type_decls: std.AutoArrayHashMapUnmanaged(Node.Index, []const u8) = .empty,
46/// Table of record decls that have been demoted to opaques.110/// Table of record decls that have been demoted to opaques.
47opaque_demotes: std.AutoHashMapUnmanaged(QualType, void) = .empty,111opaque_demotes: std.HashMapUnmanaged(QualType, void, QualTypeHashContext, std.hash_map.default_max_load_percentage) = .empty,
48/// Table of unnamed enums and records that are child types of typedefs.112/// Table of unnamed enums and records that are child types of typedefs.
49unnamed_typedefs: std.AutoHashMapUnmanaged(QualType, []const u8) = .empty,113unnamed_typedefs: std.HashMapUnmanaged(QualType, []const u8, QualTypeHashContext, std.hash_map.default_max_load_percentage) = .empty,
50/// Table of anonymous record to generated field names.114/// Table of anonymous record to generated field names.
51anonymous_record_field_names: std.AutoHashMapUnmanaged(struct {115anonymous_record_field_names: std.HashMapUnmanaged(
52 parent: QualType,116 AnonymousRecordFieldNames.Key,
53 field: QualType,117 []const u8,
54}, []const u8) = .empty,118 AnonymousRecordFieldNames.Context,
119 std.hash_map.default_max_load_percentage,
120) = .empty,
55121
56/// This one is different than the root scope's name table. This contains122/// This one is different than the root scope's name table. This contains
57/// a list of names that we found by visiting all the top level decls without123/// a list of names that we found by visiting all the top level decls without
...@@ -75,6 +141,10 @@ typedefs: std.StringArrayHashMapUnmanaged(void) = .empty,...@@ -75,6 +141,10 @@ typedefs: std.StringArrayHashMapUnmanaged(void) = .empty,
75/// The lhs lval of a compound assignment expression.141/// The lhs lval of a compound assignment expression.
76compound_assign_dummy: ?ZigNode = null,142compound_assign_dummy: ?ZigNode = null,
77143
144/// Set of variables whose initializers are currently being translated.
145/// Used to detect self-referential initializers.
146wip_var_inits: std.AutoHashMapUnmanaged(Node.Index, void) = .empty,
147
78pub fn getMangle(t: *Translator) u32 {148pub fn getMangle(t: *Translator) u32 {
79 t.mangle_count += 1;149 t.mangle_count += 1;
80 return t.mangle_count;150 return t.mangle_count;
...@@ -98,10 +168,9 @@ fn maybeSuppressResult(t: *Translator, used: ResultUsed, result: ZigNode) TransE...@@ -98,10 +168,9 @@ fn maybeSuppressResult(t: *Translator, used: ResultUsed, result: ZigNode) TransE
98168
99pub fn addTopLevelDecl(t: *Translator, name: []const u8, decl_node: ZigNode) !void {169pub fn addTopLevelDecl(t: *Translator, name: []const u8, decl_node: ZigNode) !void {
100 const gop = try t.global_scope.sym_table.getOrPut(t.gpa, name);170 const gop = try t.global_scope.sym_table.getOrPut(t.gpa, name);
101 if (!gop.found_existing) {171 if (gop.found_existing) return; // Any duplicate decls are equivalent
102 gop.value_ptr.* = decl_node;172 gop.value_ptr.* = decl_node;
103 try t.global_scope.nodes.append(t.gpa, decl_node);173 try t.global_scope.nodes.append(t.gpa, decl_node);
104 }
105}174}
106175
107fn fail(176fn fail(
...@@ -172,6 +241,12 @@ pub const Options = struct {...@@ -172,6 +241,12 @@ pub const Options = struct {
172 comp: *aro.Compilation,241 comp: *aro.Compilation,
173 pp: *const aro.Preprocessor,242 pp: *const aro.Preprocessor,
174 tree: *const aro.Tree,243 tree: *const aro.Tree,
244 module_libs: bool,
245 pub_static: bool,
246 func_bodies: bool,
247 keep_macro_literals: bool,
248 default_init: bool,
249 strict_flex_arrays: StrictFlexArraysLevel,
175};250};
176251
177pub fn translate(options: Options) mem.Allocator.Error![]u8 {252pub fn translate(options: Options) mem.Allocator.Error![]u8 {
...@@ -188,6 +263,11 @@ pub fn translate(options: Options) mem.Allocator.Error![]u8 {...@@ -188,6 +263,11 @@ pub fn translate(options: Options) mem.Allocator.Error![]u8 {
188 .comp = options.comp,263 .comp = options.comp,
189 .pp = options.pp,264 .pp = options.pp,
190 .tree = options.tree,265 .tree = options.tree,
266 .pub_static = options.pub_static,
267 .func_bodies = options.func_bodies,
268 .keep_macro_literals = options.keep_macro_literals,
269 .default_init = options.default_init,
270 .strict_flex_arrays = options.strict_flex_arrays,
191 };271 };
192 translator.global_scope.* = Scope.Root.init(&translator);272 translator.global_scope.* = Scope.Root.init(&translator);
193 defer {273 defer {
...@@ -200,6 +280,7 @@ pub fn translate(options: Options) mem.Allocator.Error![]u8 {...@@ -200,6 +280,7 @@ pub fn translate(options: Options) mem.Allocator.Error![]u8 {
200 translator.anonymous_record_field_names.deinit(gpa);280 translator.anonymous_record_field_names.deinit(gpa);
201 translator.typedefs.deinit(gpa);281 translator.typedefs.deinit(gpa);
202 translator.global_scope.deinit();282 translator.global_scope.deinit();
283 translator.wip_var_inits.deinit(gpa);
203 }284 }
204285
205 try translator.prepopulateGlobalNameTable();286 try translator.prepopulateGlobalNameTable();
...@@ -227,7 +308,6 @@ pub fn translate(options: Options) mem.Allocator.Error![]u8 {...@@ -227,7 +308,6 @@ pub fn translate(options: Options) mem.Allocator.Error![]u8 {
227 \\pub const __builtin = @import("std").zig.c_translation.builtins;308 \\pub const __builtin = @import("std").zig.c_translation.builtins;
228 \\pub const __helpers = @import("std").zig.c_translation.helpers;309 \\pub const __helpers = @import("std").zig.c_translation.helpers;
229 \\310 \\
230 \\
231 ) catch return error.OutOfMemory;311 ) catch return error.OutOfMemory;
232312
233 var zig_ast = try ast.render(gpa, translator.global_scope.nodes.items);313 var zig_ast = try ast.render(gpa, translator.global_scope.nodes.items);
...@@ -261,10 +341,12 @@ fn prepopulateGlobalNameTable(t: *Translator) !void {...@@ -261,10 +341,12 @@ fn prepopulateGlobalNameTable(t: *Translator) !void {
261 const gop = try t.unnamed_typedefs.getOrPut(t.gpa, base.qt);341 const gop = try t.unnamed_typedefs.getOrPut(t.gpa, base.qt);
262 if (gop.found_existing) {342 if (gop.found_existing) {
263 // One typedef can declare multiple names.343 // One typedef can declare multiple names.
264 // TODO Don't put this one in `decl_table` so it's processed later.344 // Don't put this one in `decl_table` so it's processed later.
265 continue;345 continue;
266 }346 }
267 gop.value_ptr.* = decl_name;347 gop.value_ptr.* = decl_name;
348 try t.type_decls.put(t.gpa, decl, decl_name);
349 try t.typedefs.put(t.gpa, decl_name, {});
268 },350 },
269351
270 .struct_decl,352 .struct_decl,
...@@ -344,15 +426,26 @@ fn transDecl(t: *Translator, scope: *Scope, decl: Node.Index) !void {...@@ -344,15 +426,26 @@ fn transDecl(t: *Translator, scope: *Scope, decl: Node.Index) !void {
344 try t.transRecordDecl(scope, record_decl.container_qt);426 try t.transRecordDecl(scope, record_decl.container_qt);
345 },427 },
346428
429 .struct_forward_decl, .union_forward_decl => |record_decl| {
430 if (record_decl.definition) |some| {
431 return t.transDecl(scope, some);
432 }
433 try t.transRecordDecl(scope, record_decl.container_qt);
434 },
435
347 .enum_decl => |enum_decl| {436 .enum_decl => |enum_decl| {
348 try t.transEnumDecl(scope, enum_decl.container_qt);437 try t.transEnumDecl(scope, enum_decl.container_qt);
349 },438 },
350439
440 .enum_forward_decl => |enum_decl| {
441 if (enum_decl.definition) |some| {
442 return t.transDecl(scope, some);
443 }
444 try t.transEnumDecl(scope, enum_decl.container_qt);
445 },
446
351 .enum_field,447 .enum_field,
352 .record_field,448 .record_field,
353 .struct_forward_decl,
354 .union_forward_decl,
355 .enum_forward_decl,
356 => return,449 => return,
357450
358 .function => |function| {451 .function => |function| {
...@@ -364,7 +457,7 @@ fn transDecl(t: *Translator, scope: *Scope, decl: Node.Index) !void {...@@ -364,7 +457,7 @@ fn transDecl(t: *Translator, scope: *Scope, decl: Node.Index) !void {
364457
365 .variable => |variable| {458 .variable => |variable| {
366 if (variable.definition != null) return;459 if (variable.definition != null) return;
367 try t.transVarDecl(scope, variable);460 try t.transVarDecl(scope, variable, decl);
368 },461 },
369 .static_assert => |static_assert| {462 .static_assert => |static_assert| {
370 try t.transStaticAssert(&t.global_scope.base, static_assert);463 try t.transStaticAssert(&t.global_scope.base, static_assert);
...@@ -531,13 +624,6 @@ fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!voi...@@ -531,13 +624,6 @@ fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!voi
531 break :init ZigTag.opaque_literal.init();624 break :init ZigTag.opaque_literal.init();
532 }625 }
533626
534 // Demote record to opaque if it contains an opaque field
535 if (t.typeWasDemotedToOpaque(field.qt)) {
536 try t.opaque_demotes.put(t.gpa, base.qt, {});
537 try t.warn(scope, field_loc, "{s} demoted to opaque type - has opaque field", .{container_kind_name});
538 break :init ZigTag.opaque_literal.init();
539 }
540
541 var field_name = field.name.lookup(t.comp);627 var field_name = field.name.lookup(t.comp);
542 if (field.name_tok == 0) {628 if (field.name_tok == 0) {
543 field_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{unnamed_field_count});629 field_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{unnamed_field_count});
...@@ -548,23 +634,22 @@ fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!voi...@@ -548,23 +634,22 @@ fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!voi
548 }, field_name);634 }, field_name);
549 }635 }
550636
551 const field_alignment = if (has_alignment_attributes)
552 t.alignmentForField(record_ty, head_field_alignment, field_index)
553 else
554 null;
555
556 const field_type = field_type: {637 const field_type = field_type: {
557 // Check if this is a flexible array member.638 // Check if this is a flexible array member.
558 flexible: {639 flexible: {
559 if (field_index != record_ty.fields.len - 1 and container_kind != .@"union") break :flexible;640 if (field_index != record_ty.fields.len - 1 and container_kind != .@"union") break :flexible;
560 const array_ty = field.qt.get(t.comp, .array) orelse break :flexible;641 const array_ty = field.qt.get(t.comp, .array) orelse break :flexible;
561 if (array_ty.len != .incomplete and (array_ty.len != .fixed or array_ty.len.fixed != 0)) break :flexible;642 if (!t.isFlexibleArrayLen(array_ty.len)) break :flexible;
562643
563 const elem_type = t.transType(scope, array_ty.elem, field_loc) catch |err| switch (err) {644 const elem_type = t.transType(scope, array_ty.elem, field_loc) catch |err| switch (err) {
564 error.UnsupportedType => break :flexible,645 error.UnsupportedType => break :flexible,
565 else => |e| return e,646 else => |e| return e,
566 };647 };
567 const zero_array = try ZigTag.array_type.create(t.arena, .{ .len = 0, .elem_type = elem_type });648 const backing_array_len: usize = switch (array_ty.len) {
649 .fixed => |n| @intCast(n),
650 else => 0,
651 };
652 const backing_array = try ZigTag.array_type.create(t.arena, .{ .len = backing_array_len, .elem_type = elem_type });
568653
569 const member_name = field_name;654 const member_name = field_name;
570 field_name = try std.fmt.allocPrint(t.arena, "_{s}", .{field_name});655 field_name = try std.fmt.allocPrint(t.arena, "_{s}", .{field_name});
...@@ -572,7 +657,7 @@ fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!voi...@@ -572,7 +657,7 @@ fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!voi
572 const member = try t.createFlexibleMemberFn(member_name, field_name);657 const member = try t.createFlexibleMemberFn(member_name, field_name);
573 try functions.append(t.gpa, member);658 try functions.append(t.gpa, member);
574659
575 break :field_type zero_array;660 break :field_type backing_array;
576 }661 }
577662
578 break :field_type t.transType(scope, field.qt, field_loc) catch |err| switch (err) {663 break :field_type t.transType(scope, field.qt, field_loc) catch |err| switch (err) {
...@@ -588,10 +673,22 @@ fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!voi...@@ -588,10 +673,22 @@ fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!voi
588 };673 };
589 };674 };
590675
676 // Demote record to opaque if it contains an opaque field
677 if (t.typeWasDemotedToOpaque(field.qt)) {
678 try t.opaque_demotes.put(t.gpa, base.qt, {});
679 try t.warn(scope, field_loc, "{s} demoted to opaque type - has opaque field", .{container_kind_name});
680 break :init ZigTag.opaque_literal.init();
681 }
682
683 const field_alignment = if (has_alignment_attributes)
684 t.alignmentForField(record_ty, head_field_alignment, field_index)
685 else
686 null;
687
591 // C99 introduced designated initializers for structs. Omitted fields are implicitly688 // C99 introduced designated initializers for structs. Omitted fields are implicitly
592 // initialized to zero. Some C APIs are designed with this in mind. Defaulting to zero689 // initialized to zero. Some C APIs are designed with this in mind. Defaulting to zero
593 // values for translated struct fields permits Zig code to comfortably use such an API.690 // values for translated struct fields permits Zig code to comfortably use such an API.
594 const default_value = if (container_kind == .@"struct")691 const default_value = if (t.default_init and container_kind == .@"struct")
595 try t.createZeroValueNode(field.qt, field_type, .no_as)692 try t.createZeroValueNode(field.qt, field_type, .no_as)
596 else693 else
597 null;694 null;
...@@ -616,7 +713,7 @@ fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!voi...@@ -616,7 +713,7 @@ fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!voi
616 .name = "_padding",713 .name = "_padding",
617 .type = try ZigTag.type.create(t.arena, try std.fmt.allocPrint(t.arena, "u{d}", .{padding_bits})),714 .type = try ZigTag.type.create(t.arena, try std.fmt.allocPrint(t.arena, "u{d}", .{padding_bits})),
618 .alignment = @divExact(alignment_bits, 8),715 .alignment = @divExact(alignment_bits, 8),
619 .default_value = if (container_kind == .@"struct")716 .default_value = if (t.default_init and container_kind == .@"struct")
620 ZigTag.zero_literal.init()717 ZigTag.zero_literal.init()
621 else718 else
622 null,719 null,
...@@ -663,14 +760,12 @@ fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!voi...@@ -663,14 +760,12 @@ fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!voi
663fn transFnDecl(t: *Translator, scope: *Scope, function: Node.Function) Error!void {760fn transFnDecl(t: *Translator, scope: *Scope, function: Node.Function) Error!void {
664 const func_ty = function.qt.get(t.comp, .func).?;761 const func_ty = function.qt.get(t.comp, .func).?;
665762
666 const is_pub = scope.id == .root;
667
668 const fn_name = t.tree.tokSlice(function.name_tok);763 const fn_name = t.tree.tokSlice(function.name_tok);
669 if (scope.getAlias(fn_name) != null or t.global_scope.containsNow(fn_name))764 if (scope.getAlias(fn_name) != null or t.global_scope.containsNow(fn_name))
670 return; // Avoid processing this decl twice765 return; // Avoid processing this decl twice
671766
672 const fn_decl_loc = function.name_tok;767 const fn_decl_loc = function.name_tok;
673 const has_body = function.body != null and func_ty.kind != .variadic;768 const has_body = function.body != null and func_ty.kind != .variadic and t.func_bodies;
674 if (function.body != null and func_ty.kind == .variadic) {769 if (function.body != null and func_ty.kind == .variadic) {
675 try t.warn(scope, function.name_tok, "TODO unable to translate variadic function, demoted to extern", .{});770 try t.warn(scope, function.name_tok, "TODO unable to translate variadic function, demoted to extern", .{});
676 }771 }
...@@ -681,7 +776,7 @@ fn transFnDecl(t: *Translator, scope: *Scope, function: Node.Function) Error!voi...@@ -681,7 +776,7 @@ fn transFnDecl(t: *Translator, scope: *Scope, function: Node.Function) Error!voi
681 .is_always_inline = is_always_inline,776 .is_always_inline = is_always_inline,
682 .is_extern = !has_body,777 .is_extern = !has_body,
683 .is_export = !function.static and has_body and !is_always_inline and !function.@"inline",778 .is_export = !function.static and has_body and !is_always_inline and !function.@"inline",
684 .is_pub = is_pub,779 .is_pub = scope.id == .root and (!function.static or t.pub_static),
685 .has_body = has_body,780 .has_body = has_body,
686 .cc = if (function.qt.getAttribute(t.comp, .calling_convention)) |some| switch (some.cc) {781 .cc = if (function.qt.getAttribute(t.comp, .calling_convention)) |some| switch (some.cc) {
687 .c => .c,782 .c => .c,
...@@ -761,6 +856,7 @@ fn transFnDecl(t: *Translator, scope: *Scope, function: Node.Function) Error!voi...@@ -761,6 +856,7 @@ fn transFnDecl(t: *Translator, scope: *Scope, function: Node.Function) Error!voi
761856
762 t.transCompoundStmtInline(body_stmt, &block_scope) catch |err| switch (err) {857 t.transCompoundStmtInline(body_stmt, &block_scope) catch |err| switch (err) {
763 error.OutOfMemory => |e| return e,858 error.OutOfMemory => |e| return e,
859 error.SelfReferential => unreachable,
764 error.UnsupportedTranslation,860 error.UnsupportedTranslation,
765 error.UnsupportedType,861 error.UnsupportedType,
766 => {862 => {
...@@ -777,7 +873,7 @@ fn transFnDecl(t: *Translator, scope: *Scope, function: Node.Function) Error!voi...@@ -777,7 +873,7 @@ fn transFnDecl(t: *Translator, scope: *Scope, function: Node.Function) Error!voi
777 return t.addTopLevelDecl(fn_name, proto_node);873 return t.addTopLevelDecl(fn_name, proto_node);
778}874}
779875
780fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable) Error!void {876fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable, decl_node: Node.Index) Error!void {
781 const base_name = t.tree.tokSlice(variable.name_tok);877 const base_name = t.tree.tokSlice(variable.name_tok);
782 const toplevel = scope.id == .root;878 const toplevel = scope.id == .root;
783 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined;879 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined;
...@@ -815,24 +911,28 @@ fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable) Error!vo...@@ -815,24 +911,28 @@ fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable) Error!vo
815 var is_const = variable.qt.@"const" or (array_ty != null and array_ty.?.elem.@"const");911 var is_const = variable.qt.@"const" or (array_ty != null and array_ty.?.elem.@"const");
816 var is_extern = variable.storage_class == .@"extern";912 var is_extern = variable.storage_class == .@"extern";
817913
914 var self_referential = false;
818 const init_node = init: {915 const init_node = init: {
819 if (variable.initializer) |init| {916 if (variable.initializer) |init| {
820 const maybe_literal = init.get(t.tree);917 const maybe_literal = init.get(t.tree);
918 if (!toplevel) try t.wip_var_inits.putNoClobber(t.gpa, decl_node, {});
919 defer _ = t.wip_var_inits.remove(decl_node);
920
821 const init_node = (if (maybe_literal == .string_literal_expr)921 const init_node = (if (maybe_literal == .string_literal_expr)
822 t.transStringLiteralInitializer(init, maybe_literal.string_literal_expr, type_node)922 t.transStringLiteralInitializer(init, maybe_literal.string_literal_expr, type_node)
823 else923 else
824 t.transExprCoercing(scope, init, .used)) catch |err| switch (err) {924 t.transExprCoercing(scope, init, .used)) catch |err| switch (err) {
925 error.SelfReferential => {
926 self_referential = true;
927 break :init ZigTag.undefined_literal.init();
928 },
825 error.UnsupportedTranslation, error.UnsupportedType => {929 error.UnsupportedTranslation, error.UnsupportedType => {
826 return t.failDecl(scope, variable.name_tok, name, "unable to resolve var init expr", .{});930 return t.failDecl(scope, variable.name_tok, name, "unable to resolve var init expr", .{});
827 },931 },
828 else => |e| return e,932 else => |e| return e,
829 };933 };
830934
831 if (!variable.qt.is(t.comp, .bool) and init_node.isBoolRes()) {935 break :init try t.toNonBool(init_node, variable.qt);
832 break :init try ZigTag.int_from_bool.create(t.arena, init_node);
833 } else {
834 break :init init_node;
835 }
836 }936 }
837 if (variable.storage_class == .@"extern") {937 if (variable.storage_class == .@"extern") {
838 if (array_ty != null and array_ty.?.len == .incomplete) {938 if (array_ty != null and array_ty.?.len == .incomplete) {
...@@ -876,7 +976,7 @@ fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable) Error!vo...@@ -876,7 +976,7 @@ fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable) Error!vo
876 const alignment: ?c_uint = variable.qt.requestedAlignment(t.comp) orelse null;976 const alignment: ?c_uint = variable.qt.requestedAlignment(t.comp) orelse null;
877 var node = try ZigTag.var_decl.create(t.arena, .{977 var node = try ZigTag.var_decl.create(t.arena, .{
878 .is_pub = toplevel,978 .is_pub = toplevel,
879 .is_const = is_const,979 .is_const = is_const and !self_referential,
880 .is_extern = is_extern,980 .is_extern = is_extern,
881 .is_export = toplevel and variable.storage_class == .auto and linkage == .strong,981 .is_export = toplevel and variable.storage_class == .auto and linkage == .strong,
882 .is_threadlocal = variable.thread_local,982 .is_threadlocal = variable.thread_local,
...@@ -894,6 +994,21 @@ fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable) Error!vo...@@ -894,6 +994,21 @@ fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable) Error!vo
894 node = try ZigTag.wrapped_local.create(t.arena, .{ .name = name, .init = node });994 node = try ZigTag.wrapped_local.create(t.arena, .{ .name = name, .init = node });
895 }995 }
896 try scope.appendNode(node);996 try scope.appendNode(node);
997 if (self_referential) {
998 const deferred_init = t.transExprCoercing(scope, variable.initializer.?, .used) catch |err| switch (err) {
999 error.SelfReferential => unreachable,
1000 error.UnsupportedTranslation, error.UnsupportedType => {
1001 return t.failDecl(scope, variable.name_tok, name, "unable to resolve var init expr", .{});
1002 },
1003 else => |e| return e,
1004 };
1005
1006 const assign = try ZigTag.assign.create(t.arena, .{
1007 .lhs = try ZigTag.identifier.create(t.arena, name),
1008 .rhs = try t.toNonBool(deferred_init, variable.qt),
1009 });
1010 try scope.appendNode(assign);
1011 }
897 try bs.discardVariable(name);1012 try bs.discardVariable(name);
8981013
899 if (variable.qt.getAttribute(t.comp, .cleanup)) |cleanup_attr| {1014 if (variable.qt.getAttribute(t.comp, .cleanup)) |cleanup_attr| {
...@@ -1001,6 +1116,7 @@ fn transEnumDecl(t: *Translator, scope: *Scope, enum_qt: QualType) Error!void {...@@ -1001,6 +1116,7 @@ fn transEnumDecl(t: *Translator, scope: *Scope, enum_qt: QualType) Error!void {
10011116
1002fn transStaticAssert(t: *Translator, scope: *Scope, static_assert: Node.StaticAssert) Error!void {1117fn transStaticAssert(t: *Translator, scope: *Scope, static_assert: Node.StaticAssert) Error!void {
1003 const condition = t.transExpr(scope, static_assert.cond, .used) catch |err| switch (err) {1118 const condition = t.transExpr(scope, static_assert.cond, .used) catch |err| switch (err) {
1119 error.SelfReferential => unreachable,
1004 error.UnsupportedTranslation, error.UnsupportedType => {1120 error.UnsupportedTranslation, error.UnsupportedType => {
1005 return try t.warn(&t.global_scope.base, static_assert.cond.tok(t.tree), "unable to translate _Static_assert condition", .{});1121 return try t.warn(&t.global_scope.base, static_assert.cond.tok(t.tree), "unable to translate _Static_assert condition", .{});
1006 },1122 },
...@@ -1084,21 +1200,17 @@ fn transType(t: *Translator, scope: *Scope, qt: QualType, source_loc: TokenIndex...@@ -1084,21 +1200,17 @@ fn transType(t: *Translator, scope: *Scope, qt: QualType, source_loc: TokenIndex
1084 },1200 },
1085 .float => |float_ty| switch (float_ty) {1201 .float => |float_ty| switch (float_ty) {
1086 .fp16, .float16 => return ZigTag.type.create(t.arena, "f16"),1202 .fp16, .float16 => return ZigTag.type.create(t.arena, "f16"),
1087 .float => return ZigTag.type.create(t.arena, "f32"),1203 .float, .float32 => return ZigTag.type.create(t.arena, "f32"),
1088 .double => return ZigTag.type.create(t.arena, "f64"),1204 .double, .float64, .float32x => return ZigTag.type.create(t.arena, "f64"),
1089 .long_double => return ZigTag.type.create(t.arena, "c_longdouble"),1205 .long_double, .float64x => return ZigTag.type.create(t.arena, "c_longdouble"),
1090 .float128 => return ZigTag.type.create(t.arena, "f128"),1206 .float128 => return ZigTag.type.create(t.arena, "f128"),
1091 .bf16,1207 .bf16 => return t.fail(error.UnsupportedType, source_loc, "TODO support bfloat16", .{}),
1092 .float32,
1093 .float64,
1094 .float32x,
1095 .float64x,
1096 .float128x,
1097 .dfloat32,1208 .dfloat32,
1098 .dfloat64,1209 .dfloat64,
1099 .dfloat128,1210 .dfloat128,
1100 .dfloat64x,1211 .dfloat64x,
1101 => return t.fail(error.UnsupportedType, source_loc, "TODO support float type: '{s}'", .{try t.getTypeStr(qt)}),1212 => return t.fail(error.UnsupportedType, source_loc, "TODO support decimal float type: '{s}'", .{try t.getTypeStr(qt)}),
1213 .float128x => unreachable, // Unsupported on all targets
1102 },1214 },
1103 .pointer => |pointer_ty| {1215 .pointer => |pointer_ty| {
1104 const child_qt = pointer_ty.child;1216 const child_qt = pointer_ty.child;
...@@ -1173,9 +1285,21 @@ fn transType(t: *Translator, scope: *Scope, qt: QualType, source_loc: TokenIndex...@@ -1173,9 +1285,21 @@ fn transType(t: *Translator, scope: *Scope, qt: QualType, source_loc: TokenIndex
1173 return ZigTag.identifier.create(t.arena, name);1285 return ZigTag.identifier.create(t.arena, name);
1174 },1286 },
1175 .attributed => |attributed_ty| continue :loop attributed_ty.base.type(t.comp),1287 .attributed => |attributed_ty| continue :loop attributed_ty.base.type(t.comp),
1176 .typeof => |typeof_ty| continue :loop typeof_ty.base.type(t.comp),1288 .typeof => |typeof_ty| {
1289 if (typeof_ty.expr) |expr| {
1290 if (t.transExpr(scope, expr, .used)) |node| {
1291 return ZigTag.typeof.create(t.arena, node);
1292 } else |err| switch (err) {
1293 error.SelfReferential => {},
1294 error.UnsupportedTranslation => {},
1295 error.UnsupportedType => {},
1296 error.OutOfMemory => return error.OutOfMemory,
1297 }
1298 }
1299 continue :loop typeof_ty.base.type(t.comp);
1300 },
1177 .vector => |vector_ty| {1301 .vector => |vector_ty| {
1178 const len = try t.createNumberNode(vector_ty.len, .int);1302 const len = try t.createNumberNode(vector_ty.len);
1179 const elem_type = try t.transType(scope, vector_ty.elem, source_loc);1303 const elem_type = try t.transType(scope, vector_ty.elem, source_loc);
1180 return ZigTag.vector.create(t.arena, .{ .lhs = len, .rhs = elem_type });1304 return ZigTag.vector.create(t.arena, .{ .lhs = len, .rhs = elem_type });
1181 },1305 },
...@@ -1384,7 +1508,10 @@ fn transFnType(...@@ -1384,7 +1508,10 @@ fn transFnType(
1384 .is_var_args = switch (func_ty.kind) {1508 .is_var_args = switch (func_ty.kind) {
1385 .normal => false,1509 .normal => false,
1386 .variadic => true,1510 .variadic => true,
1387 .old_style => !ctx.is_export and !ctx.is_always_inline and !ctx.has_body,1511 .old_style => if (t.comp.target.cpu.arch.isWasm())
1512 false
1513 else
1514 !ctx.is_export and !ctx.is_always_inline and !ctx.has_body,
1388 },1515 },
1389 .name = ctx.fn_name,1516 .name = ctx.fn_name,
1390 .linksection_string = linksection_string,1517 .linksection_string = linksection_string,
...@@ -1468,7 +1595,7 @@ fn typeIsOpaque(t: *Translator, qt: QualType) bool {...@@ -1468,7 +1595,7 @@ fn typeIsOpaque(t: *Translator, qt: QualType) bool {
1468}1595}
14691596
1470fn typeWasDemotedToOpaque(t: *Translator, qt: QualType) bool {1597fn typeWasDemotedToOpaque(t: *Translator, qt: QualType) bool {
1471 return t.opaque_demotes.contains(qt);1598 return t.opaque_demotes.contains(qt.base(t.comp).qt);
1472}1599}
14731600
1474fn typeHasWrappingOverflow(t: *Translator, qt: QualType) bool {1601fn typeHasWrappingOverflow(t: *Translator, qt: QualType) bool {
...@@ -1531,16 +1658,30 @@ fn transStmt(t: *Translator, scope: *Scope, stmt: Node.Index) TransError!ZigNode...@@ -1531,16 +1658,30 @@ fn transStmt(t: *Translator, scope: *Scope, stmt: Node.Index) TransError!ZigNode
1531 try t.transRecordDecl(scope, record_decl.container_qt);1658 try t.transRecordDecl(scope, record_decl.container_qt);
1532 return ZigTag.declaration.init();1659 return ZigTag.declaration.init();
1533 },1660 },
1661 .struct_forward_decl, .union_forward_decl => |record_decl| {
1662 if (record_decl.definition) |some| {
1663 return t.transStmt(scope, some);
1664 }
1665 try t.transRecordDecl(scope, record_decl.container_qt);
1666 return ZigTag.declaration.init();
1667 },
1534 .enum_decl => |enum_decl| {1668 .enum_decl => |enum_decl| {
1535 try t.transEnumDecl(scope, enum_decl.container_qt);1669 try t.transEnumDecl(scope, enum_decl.container_qt);
1536 return ZigTag.declaration.init();1670 return ZigTag.declaration.init();
1537 },1671 },
1672 .enum_forward_decl => |enum_decl| {
1673 if (enum_decl.definition) |some| {
1674 return t.transStmt(scope, some);
1675 }
1676 try t.transEnumDecl(scope, enum_decl.container_qt);
1677 return ZigTag.declaration.init();
1678 },
1538 .function => |function| {1679 .function => |function| {
1539 try t.transFnDecl(scope, function);1680 try t.transFnDecl(scope, function);
1540 return ZigTag.declaration.init();1681 return ZigTag.declaration.init();
1541 },1682 },
1542 .variable => |variable| {1683 .variable => |variable| {
1543 try t.transVarDecl(scope, variable);1684 try t.transVarDecl(scope, variable, stmt);
1544 return ZigTag.declaration.init();1685 return ZigTag.declaration.init();
1545 },1686 },
1546 .switch_stmt => |switch_stmt| return t.transSwitch(scope, switch_stmt),1687 .switch_stmt => |switch_stmt| return t.transSwitch(scope, switch_stmt),
...@@ -1562,7 +1703,10 @@ fn transCompoundStmtInline(t: *Translator, compound: Node.CompoundStmt, block: *...@@ -1562,7 +1703,10 @@ fn transCompoundStmtInline(t: *Translator, compound: Node.CompoundStmt, block: *
1562 const result = try t.transStmt(&block.base, stmt);1703 const result = try t.transStmt(&block.base, stmt);
1563 switch (result.tag()) {1704 switch (result.tag()) {
1564 .declaration, .empty_block => {},1705 .declaration, .empty_block => {},
1565 else => try block.statements.append(t.gpa, result),1706 else => {
1707 try block.statements.append(t.gpa, result);
1708 if (result.isNoreturn()) return;
1709 },
1566 }1710 }
1567 }1711 }
1568}1712}
...@@ -1578,12 +1722,9 @@ fn transReturnStmt(t: *Translator, scope: *Scope, return_stmt: Node.ReturnStmt)...@@ -1578,12 +1722,9 @@ fn transReturnStmt(t: *Translator, scope: *Scope, return_stmt: Node.ReturnStmt)
1578 switch (return_stmt.operand) {1722 switch (return_stmt.operand) {
1579 .none => return ZigTag.return_void.init(),1723 .none => return ZigTag.return_void.init(),
1580 .expr => |operand| {1724 .expr => |operand| {
1581 var rhs = try t.transExprCoercing(scope, operand, .used);1725 const rhs = try t.transExprCoercing(scope, operand, .used);
1582 const return_qt = scope.findBlockReturnType();1726 const return_qt = scope.findBlockReturnType();
1583 if (rhs.isBoolRes() and !return_qt.is(t.comp, .bool)) {1727 return ZigTag.@"return".create(t.arena, try t.toNonBool(rhs, return_qt));
1584 rhs = try ZigTag.int_from_bool.create(t.arena, rhs);
1585 }
1586 return ZigTag.@"return".create(t.arena, rhs);
1587 },1728 },
1588 .implicit => |zero| {1729 .implicit => |zero| {
1589 if (zero) return ZigTag.@"return".create(t.arena, ZigTag.zero_literal.init());1730 if (zero) return ZigTag.@"return".create(t.arena, ZigTag.zero_literal.init());
...@@ -1698,7 +1839,7 @@ fn transDoWhileStmt(t: *Translator, scope: *Scope, do_stmt: Node.DoWhileStmt) Tr...@@ -1698,7 +1839,7 @@ fn transDoWhileStmt(t: *Translator, scope: *Scope, do_stmt: Node.DoWhileStmt) Tr
1698 };1839 };
16991840
1700 var body_node = try t.transStmt(&loop_scope, do_stmt.body);1841 var body_node = try t.transStmt(&loop_scope, do_stmt.body);
1701 if (body_node.isNoreturn(true)) {1842 if (body_node.isNoreturn()) {
1702 // The body node ends in a noreturn statement. Simply put it in a while (true)1843 // The body node ends in a noreturn statement. Simply put it in a while (true)
1703 // in case it contains breaks or continues.1844 // in case it contains breaks or continues.
1704 } else if (do_stmt.body.get(t.tree) == .compound_stmt) {1845 } else if (do_stmt.body.get(t.tree) == .compound_stmt) {
...@@ -1918,8 +2059,6 @@ fn transSwitchProngStmt(...@@ -1918,8 +2059,6 @@ fn transSwitchProngStmt(
1918 body: []const Node.Index,2059 body: []const Node.Index,
1919) TransError!ZigNode {2060) TransError!ZigNode {
1920 switch (stmt.get(t.tree)) {2061 switch (stmt.get(t.tree)) {
1921 .break_stmt => return ZigTag.@"break".init(),
1922 .return_stmt => return t.transStmt(scope, stmt),
1923 .case_stmt, .default_stmt => unreachable,2062 .case_stmt, .default_stmt => unreachable,
1924 else => {2063 else => {
1925 var block_scope = try Scope.Block.init(t, scope, false);2064 var block_scope = try Scope.Block.init(t, scope, false);
...@@ -1940,15 +2079,6 @@ fn transSwitchProngStmtInline(...@@ -1940,15 +2079,6 @@ fn transSwitchProngStmtInline(
1940) TransError!void {2079) TransError!void {
1941 for (body) |stmt| {2080 for (body) |stmt| {
1942 switch (stmt.get(t.tree)) {2081 switch (stmt.get(t.tree)) {
1943 .return_stmt => {
1944 const result = try t.transStmt(&block.base, stmt);
1945 try block.statements.append(t.gpa, result);
1946 return;
1947 },
1948 .break_stmt => {
1949 try block.statements.append(t.gpa, ZigTag.@"break".init());
1950 return;
1951 },
1952 .case_stmt => |case_stmt| {2082 .case_stmt => |case_stmt| {
1953 var sub = case_stmt.body;2083 var sub = case_stmt.body;
1954 while (true) switch (sub.get(t.tree)) {2084 while (true) switch (sub.get(t.tree)) {
...@@ -1959,7 +2089,7 @@ fn transSwitchProngStmtInline(...@@ -1959,7 +2089,7 @@ fn transSwitchProngStmtInline(
1959 const result = try t.transStmt(&block.base, sub);2089 const result = try t.transStmt(&block.base, sub);
1960 assert(result.tag() != .declaration);2090 assert(result.tag() != .declaration);
1961 try block.statements.append(t.gpa, result);2091 try block.statements.append(t.gpa, result);
1962 if (result.isNoreturn(true)) return;2092 if (result.isNoreturn()) return;
1963 },2093 },
1964 .default_stmt => |default_stmt| {2094 .default_stmt => |default_stmt| {
1965 var sub = default_stmt.body;2095 var sub = default_stmt.body;
...@@ -1971,18 +2101,16 @@ fn transSwitchProngStmtInline(...@@ -1971,18 +2101,16 @@ fn transSwitchProngStmtInline(
1971 const result = try t.transStmt(&block.base, sub);2101 const result = try t.transStmt(&block.base, sub);
1972 assert(result.tag() != .declaration);2102 assert(result.tag() != .declaration);
1973 try block.statements.append(t.gpa, result);2103 try block.statements.append(t.gpa, result);
1974 if (result.isNoreturn(true)) return;2104 if (result.isNoreturn()) return;
1975 },
1976 .compound_stmt => |compound_stmt| {
1977 const result = try t.transCompoundStmt(&block.base, compound_stmt);
1978 try block.statements.append(t.gpa, result);
1979 if (result.isNoreturn(true)) return;
1980 },2105 },
1981 else => {2106 else => {
1982 const result = try t.transStmt(&block.base, stmt);2107 const result = try t.transStmt(&block.base, stmt);
1983 switch (result.tag()) {2108 switch (result.tag()) {
1984 .declaration, .empty_block => {},2109 .declaration, .empty_block => {},
1985 else => try block.statements.append(t.gpa, result),2110 else => {
2111 try block.statements.append(t.gpa, result);
2112 if (result.isNoreturn()) return;
2113 },
1986 }2114 }
1987 },2115 },
1988 }2116 }
...@@ -2015,7 +2143,14 @@ fn transExpr(t: *Translator, scope: *Scope, expr: Node.Index, used: ResultUsed)...@@ -2015,7 +2143,14 @@ fn transExpr(t: *Translator, scope: *Scope, expr: Node.Index, used: ResultUsed)
2015 break :res try ZigTag.deref.create(t.arena, try t.transExpr(scope, deref_expr.operand, .used));2143 break :res try ZigTag.deref.create(t.arena, try t.transExpr(scope, deref_expr.operand, .used));
2016 },2144 },
2017 .bool_not_expr => |bool_not_expr| try ZigTag.not.create(t.arena, try t.transBoolExpr(scope, bool_not_expr.operand)),2145 .bool_not_expr => |bool_not_expr| try ZigTag.not.create(t.arena, try t.transBoolExpr(scope, bool_not_expr.operand)),
2018 .bit_not_expr => |bit_not_expr| try ZigTag.bit_not.create(t.arena, try t.transExpr(scope, bit_not_expr.operand, .used)),2146 .bit_not_expr => |bit_not_expr| try ZigTag.bit_not.create(t.arena, op: {
2147 const operand = try t.transExpr(scope, bit_not_expr.operand, .used);
2148 if (!operand.isBoolRes()) break :op operand;
2149
2150 const casted = try ZigTag.int_from_bool.create(t.arena, operand);
2151 const ty = try t.transType(scope, bit_not_expr.qt, bit_not_expr.op_tok);
2152 break :op try ZigTag.as.create(t.arena, .{ .lhs = ty, .rhs = casted });
2153 }),
2019 .plus_expr => |plus_expr| return t.transExpr(scope, plus_expr.operand, used),2154 .plus_expr => |plus_expr| return t.transExpr(scope, plus_expr.operand, used),
2020 .negate_expr => |negate_expr| res: {2155 .negate_expr => |negate_expr| res: {
2021 const operand_qt = negate_expr.operand.qt(t.tree);2156 const operand_qt = negate_expr.operand.qt(t.tree);
...@@ -2109,8 +2244,8 @@ fn transExpr(t: *Translator, scope: *Scope, expr: Node.Index, used: ResultUsed)...@@ -2109,8 +2244,8 @@ fn transExpr(t: *Translator, scope: *Scope, expr: Node.Index, used: ResultUsed)
2109 .shl_expr => |shl_expr| try t.transShiftExpr(scope, shl_expr, .shl),2244 .shl_expr => |shl_expr| try t.transShiftExpr(scope, shl_expr, .shl),
2110 .shr_expr => |shr_expr| try t.transShiftExpr(scope, shr_expr, .shr),2245 .shr_expr => |shr_expr| try t.transShiftExpr(scope, shr_expr, .shr),
21112246
2112 .member_access_expr => |member_access| try t.transMemberAccess(scope, .normal, member_access, null),2247 .member_access_expr => |member_access| try t.transMemberAccess(scope, .normal, member_access, null, .accessor),
2113 .member_access_ptr_expr => |member_access| try t.transMemberAccess(scope, .ptr, member_access, null),2248 .member_access_ptr_expr => |member_access| try t.transMemberAccess(scope, .ptr, member_access, null, .accessor),
2114 .array_access_expr => |array_access| try t.transArrayAccess(scope, array_access, null),2249 .array_access_expr => |array_access| try t.transArrayAccess(scope, array_access, null),
21152250
2116 .builtin_ref => unreachable,2251 .builtin_ref => unreachable,
...@@ -2195,6 +2330,10 @@ fn transExpr(t: *Translator, scope: *Scope, expr: Node.Index, used: ResultUsed)...@@ -2195,6 +2330,10 @@ fn transExpr(t: *Translator, scope: *Scope, expr: Node.Index, used: ResultUsed)
2195 .builtin_convertvector => |convertvector| try t.transConvertvectorExpr(scope, convertvector),2330 .builtin_convertvector => |convertvector| try t.transConvertvectorExpr(scope, convertvector),
2196 .builtin_shufflevector => |shufflevector| try t.transShufflevectorExpr(scope, shufflevector),2331 .builtin_shufflevector => |shufflevector| try t.transShufflevectorExpr(scope, shufflevector),
21972332
2333 .builtin_va_arg_pack, .builtin_va_arg_pack_len => |va_arg_pack| {
2334 return t.fail(error.UnsupportedTranslation, va_arg_pack.builtin_tok, "TODO va arg pack", .{});
2335 },
2336
2198 .compound_stmt,2337 .compound_stmt,
2199 .static_assert,2338 .static_assert,
2200 .return_stmt,2339 .return_stmt,
...@@ -2293,6 +2432,12 @@ fn transBoolExpr(t: *Translator, scope: *Scope, expr: Node.Index) TransError!Zig...@@ -2293,6 +2432,12 @@ fn transBoolExpr(t: *Translator, scope: *Scope, expr: Node.Index) TransError!Zig
2293 return t.finishBoolExpr(expr.qt(t.tree), maybe_bool_res);2432 return t.finishBoolExpr(expr.qt(t.tree), maybe_bool_res);
2294}2433}
22952434
2435fn toNonBool(t: *Translator, node: ZigNode, qt: QualType) Error!ZigNode {
2436 if (!node.isBoolRes()) return node;
2437 if (qt.is(t.comp, .bool)) return node;
2438 return ZigTag.int_from_bool.create(t.arena, node);
2439}
2440
2296fn finishBoolExpr(t: *Translator, qt: QualType, node: ZigNode) TransError!ZigNode {2441fn finishBoolExpr(t: *Translator, qt: QualType, node: ZigNode) TransError!ZigNode {
2297 const sk = qt.scalarKind(t.comp);2442 const sk = qt.scalarKind(t.comp);
2298 if (sk == .bool) return node;2443 if (sk == .bool) return node;
...@@ -2385,8 +2530,22 @@ fn transCastExpr(...@@ -2385,8 +2530,22 @@ fn transCastExpr(
2385 else => {},2530 else => {},
2386 }2531 }
23872532
2388 if (cast.operand.qt(t.tree).arrayLen(t.comp) == null) {2533 // Flexible array members are translated as member functions returning
2389 return try t.transExpr(scope, cast.operand, used);2534 // [*c]T, so no address-of + @ptrCast wrapping is needed.
2535 flexible: {
2536 if (cast.operand.qt(t.tree).arrayLen(t.comp) == null) {
2537 return try t.transExpr(scope, cast.operand, used);
2538 }
2539
2540 const member_index, const base_qt = switch (cast.operand.get(t.tree)) {
2541 .member_access_expr => |ma| .{ ma.member_index, ma.base.qt(t.tree) },
2542 .member_access_ptr_expr => |ma| .{ ma.member_index, ma.base.qt(t.tree).childType(t.comp) },
2543 else => break :flexible,
2544 };
2545 const record = base_qt.getRecord(t.comp) orelse break :flexible;
2546 if (member_index != record.fields.len - 1 and base_qt.base(t.comp).type != .@"union") break :flexible;
2547 const array_ty = record.fields[member_index].qt.get(t.comp, .array) orelse break :flexible;
2548 if (t.isFlexibleArrayLen(array_ty.len)) return try t.transExpr(scope, cast.operand, used);
2390 }2549 }
23912550
2392 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);2551 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
...@@ -2402,6 +2561,8 @@ fn transCastExpr(...@@ -2402,6 +2561,8 @@ fn transCastExpr(
2402 .lhs = try ZigTag.type.create(t.arena, "usize"),2561 .lhs = try ZigTag.type.create(t.arena, "usize"),
2403 .rhs = try ZigTag.int_cast.create(t.arena, sub_expr_node),2562 .rhs = try ZigTag.int_cast.create(t.arena, sub_expr_node),
2404 });2563 });
2564 } else if (sub_expr_node.isBoolRes()) {
2565 sub_expr_node = try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
2405 }2566 }
2406 break :int_to_pointer try ZigTag.ptr_from_int.create(t.arena, sub_expr_node);2567 break :int_to_pointer try ZigTag.ptr_from_int.create(t.arena, sub_expr_node);
2407 },2568 },
...@@ -2560,6 +2721,8 @@ fn transPointerCastExpr(t: *Translator, scope: *Scope, expr: Node.Index) TransEr...@@ -2560,6 +2721,8 @@ fn transPointerCastExpr(t: *Translator, scope: *Scope, expr: Node.Index) TransEr
2560}2721}
25612722
2562fn transDeclRefExpr(t: *Translator, scope: *Scope, decl_ref: Node.DeclRef) TransError!ZigNode {2723fn transDeclRefExpr(t: *Translator, scope: *Scope, decl_ref: Node.DeclRef) TransError!ZigNode {
2724 if (t.wip_var_inits.contains(decl_ref.decl)) return error.SelfReferential;
2725
2563 const name = t.tree.tokSlice(decl_ref.name_tok);2726 const name = t.tree.tokSlice(decl_ref.name_tok);
2564 const maybe_alias = scope.getAlias(name);2727 const maybe_alias = scope.getAlias(name);
2565 const mangled_name = maybe_alias orelse name;2728 const mangled_name = maybe_alias orelse name;
...@@ -2631,7 +2794,7 @@ fn transShiftExpr(t: *Translator, scope: *Scope, bin: Node.Binary, op_id: ZigTag...@@ -2631,7 +2794,7 @@ fn transShiftExpr(t: *Translator, scope: *Scope, bin: Node.Binary, op_id: ZigTag
2631 // lhs >> @intCast(rh)2794 // lhs >> @intCast(rh)
2632 const lhs = try t.transExpr(scope, bin.lhs, .used);2795 const lhs = try t.transExpr(scope, bin.lhs, .used);
26332796
2634 const rhs = try t.transExprCoercing(scope, bin.rhs, .used);2797 const rhs = try t.transExpr(scope, bin.rhs, .used);
2635 const rhs_casted = try ZigTag.int_cast.create(t.arena, rhs);2798 const rhs_casted = try ZigTag.int_cast.create(t.arena, rhs);
26362799
2637 return t.createBinOpNode(op_id, lhs, rhs_casted);2800 return t.createBinOpNode(op_id, lhs, rhs_casted);
...@@ -2758,7 +2921,7 @@ fn transCommaExpr(t: *Translator, scope: *Scope, bin: Node.Binary, used: ResultU...@@ -2758,7 +2921,7 @@ fn transCommaExpr(t: *Translator, scope: *Scope, bin: Node.Binary, used: ResultU
2758 const rhs = try t.transExprCoercing(&block_scope.base, bin.rhs, .used);2921 const rhs = try t.transExprCoercing(&block_scope.base, bin.rhs, .used);
2759 const break_node = try ZigTag.break_val.create(t.arena, .{2922 const break_node = try ZigTag.break_val.create(t.arena, .{
2760 .label = block_scope.label,2923 .label = block_scope.label,
2761 .val = rhs,2924 .val = try t.toNonBool(rhs, bin.qt),
2762 });2925 });
2763 try block_scope.statements.append(t.gpa, break_node);2926 try block_scope.statements.append(t.gpa, break_node);
27642927
...@@ -2768,14 +2931,10 @@ fn transCommaExpr(t: *Translator, scope: *Scope, bin: Node.Binary, used: ResultU...@@ -2768,14 +2931,10 @@ fn transCommaExpr(t: *Translator, scope: *Scope, bin: Node.Binary, used: ResultU
2768fn transAssignExpr(t: *Translator, scope: *Scope, bin: Node.Binary, used: ResultUsed) !ZigNode {2931fn transAssignExpr(t: *Translator, scope: *Scope, bin: Node.Binary, used: ResultUsed) !ZigNode {
2769 if (used == .unused) {2932 if (used == .unused) {
2770 const lhs = try t.transExpr(scope, bin.lhs, .used);2933 const lhs = try t.transExpr(scope, bin.lhs, .used);
2771 var rhs = try t.transExprCoercing(scope, bin.rhs, .used);2934 const rhs = try t.transExprCoercing(scope, bin.rhs, .used);
27722935
2773 const lhs_qt = bin.lhs.qt(t.tree);2936 const lhs_qt = bin.lhs.qt(t.tree);
2774 if (rhs.isBoolRes() and !lhs_qt.is(t.comp, .bool)) {2937 return t.createBinOpNode(.assign, lhs, try t.toNonBool(rhs, lhs_qt));
2775 rhs = try ZigTag.int_from_bool.create(t.arena, rhs);
2776 }
2777
2778 return t.createBinOpNode(.assign, lhs, rhs);
2779 }2938 }
27802939
2781 var block_scope = try Scope.Block.init(t, scope, true);2940 var block_scope = try Scope.Block.init(t, scope, true);
...@@ -2783,13 +2942,12 @@ fn transAssignExpr(t: *Translator, scope: *Scope, bin: Node.Binary, used: Result...@@ -2783,13 +2942,12 @@ fn transAssignExpr(t: *Translator, scope: *Scope, bin: Node.Binary, used: Result
27832942
2784 const tmp = try block_scope.reserveMangledName("tmp");2943 const tmp = try block_scope.reserveMangledName("tmp");
27852944
2786 var rhs = try t.transExpr(&block_scope.base, bin.rhs, .used);2945 const rhs = try t.transExpr(&block_scope.base, bin.rhs, .used);
2787 const lhs_qt = bin.lhs.qt(t.tree);2946 const lhs_qt = bin.lhs.qt(t.tree);
2788 if (rhs.isBoolRes() and !lhs_qt.is(t.comp, .bool)) {2947 const tmp_decl = try ZigTag.var_simple.create(t.arena, .{
2789 rhs = try ZigTag.int_from_bool.create(t.arena, rhs);2948 .name = tmp,
2790 }2949 .init = try t.toNonBool(rhs, lhs_qt),
27912950 });
2792 const tmp_decl = try ZigTag.var_simple.create(t.arena, .{ .name = tmp, .init = rhs });
2793 try block_scope.statements.append(t.gpa, tmp_decl);2951 try block_scope.statements.append(t.gpa, tmp_decl);
27942952
2795 const lhs = try t.transExprCoercing(&block_scope.base, bin.lhs, .used);2953 const lhs = try t.transExprCoercing(&block_scope.base, bin.lhs, .used);
...@@ -3040,6 +3198,7 @@ fn transMemberAccess(...@@ -3040,6 +3198,7 @@ fn transMemberAccess(
3040 kind: enum { normal, ptr },3198 kind: enum { normal, ptr },
3041 member_access: Node.MemberAccess,3199 member_access: Node.MemberAccess,
3042 opt_base: ?ZigNode,3200 opt_base: ?ZigNode,
3201 flex_array_mode: enum { accessor, backing },
3043) TransError!ZigNode {3202) TransError!ZigNode {
3044 const base_info = switch (kind) {3203 const base_info = switch (kind) {
3045 .normal => member_access.base.qt(t.tree),3204 .normal => member_access.base.qt(t.tree),
...@@ -3068,8 +3227,14 @@ fn transMemberAccess(...@@ -3068,8 +3227,14 @@ fn transMemberAccess(
3068 // Flexible array members are translated as member functions.3227 // Flexible array members are translated as member functions.
3069 if (member_access.member_index == record.fields.len - 1 or base_info.base(t.comp).type == .@"union") {3228 if (member_access.member_index == record.fields.len - 1 or base_info.base(t.comp).type == .@"union") {
3070 if (field.qt.get(t.comp, .array)) |array_ty| {3229 if (field.qt.get(t.comp, .array)) |array_ty| {
3071 if (array_ty.len == .incomplete or (array_ty.len == .fixed and array_ty.len.fixed == 0)) {3230 if (t.isFlexibleArrayLen(array_ty.len)) {
3072 return ZigTag.call.create(t.arena, .{ .lhs = field_access, .args = &.{} });3231 switch (flex_array_mode) {
3232 .accessor => return ZigTag.call.create(t.arena, .{ .lhs = field_access, .args = &.{} }),
3233 .backing => {
3234 const backing_name = try std.fmt.allocPrint(t.arena, "_{s}", .{field_name});
3235 return ZigTag.field_access.create(t.arena, .{ .lhs = lhs, .field_name = backing_name });
3236 },
3237 }
3073 }3238 }
3074 }3239 }
3075 }3240 }
...@@ -3091,7 +3256,7 @@ fn transArrayAccess(t: *Translator, scope: *Scope, array_access: Node.ArrayAcces...@@ -3091,7 +3256,7 @@ fn transArrayAccess(t: *Translator, scope: *Scope, array_access: Node.ArrayAcces
3091 const index = index: {3256 const index = index: {
3092 const index = try t.transExpr(scope, array_access.index, .used);3257 const index = try t.transExpr(scope, array_access.index, .used);
3093 const index_qt = array_access.index.qt(t.tree);3258 const index_qt = array_access.index.qt(t.tree);
3094 const maybe_bigger_than_usize = switch (index_qt.base(t.comp).type) {3259 const maybe_bigger_than_usize = type: switch (index_qt.base(t.comp).type) {
3095 .bool => {3260 .bool => {
3096 break :index try ZigTag.int_from_bool.create(t.arena, index);3261 break :index try ZigTag.int_from_bool.create(t.arena, index);
3097 },3262 },
...@@ -3100,6 +3265,7 @@ fn transArrayAccess(t: *Translator, scope: *Scope, array_access: Node.ArrayAcces...@@ -3100,6 +3265,7 @@ fn transArrayAccess(t: *Translator, scope: *Scope, array_access: Node.ArrayAcces
3100 else => false,3265 else => false,
3101 },3266 },
3102 .bit_int => |bit_int| bit_int.bits > t.comp.target.ptrBitWidth(),3267 .bit_int => |bit_int| bit_int.bits > t.comp.target.ptrBitWidth(),
3268 .@"enum" => |e| if (e.tag) |tag| continue :type tag.base(t.comp).type else false,
3103 else => unreachable,3269 else => unreachable,
3104 };3270 };
31053271
...@@ -3158,7 +3324,10 @@ fn transMemberDesignator(t: *Translator, scope: *Scope, arg: Node.Index) TransEr...@@ -3158,7 +3324,10 @@ fn transMemberDesignator(t: *Translator, scope: *Scope, arg: Node.Index) TransEr
3158 },3324 },
3159 .member_access_expr => |access| {3325 .member_access_expr => |access| {
3160 const base = try t.transMemberDesignator(scope, access.base);3326 const base = try t.transMemberDesignator(scope, access.base);
3161 return t.transMemberAccess(scope, .normal, access, base);3327 // In offsetof context, flexible array members must be accessed via
3328 // the backing field (`_name`) rather than the accessor function,
3329 // because you can't take the address of a function call result.
3330 return t.transMemberAccess(scope, .normal, access, base, .backing);
3162 },3331 },
3163 .cast => |cast| {3332 .cast => |cast| {
3164 assert(cast.kind == .array_to_pointer);3333 assert(cast.kind == .array_to_pointer);
...@@ -3292,6 +3461,51 @@ fn transCall(...@@ -3292,6 +3461,51 @@ fn transCall(
32923461
3293const SuppressCast = enum { with_as, no_as };3462const SuppressCast = enum { with_as, no_as };
32943463
3464/// Attempt to translate literal as the name of the simple macro
3465/// it was expanded from.
3466fn checkLiteralMacro(t: *Translator, tok: TokenIndex, used: ResultUsed) !?ZigNode {
3467 if (!t.keep_macro_literals) return null;
3468 const expansion_locs = t.pp.expansionSlice(tok);
3469 if (expansion_locs.len == 0) return null;
3470
3471 const last_expand = expansion_locs[0];
3472 const source = t.comp.getSource(last_expand.id);
3473 var tokenizer: aro.Tokenizer = .{
3474 .buf = source.buf,
3475 .langopts = t.comp.langopts,
3476 .source = last_expand.id,
3477 .index = last_expand.byte_offset,
3478 .splice_locs = &.{},
3479 };
3480 const name_tok = tokenizer.next();
3481 if (!name_tok.id.isMacroIdentifier()) return null;
3482
3483 const name = t.pp.tokSlice(name_tok);
3484 if (t.global_scope.containsNow(name)) return null;
3485 const macro = t.pp.defines.get(name) orelse return null;
3486 if (macro.is_func) return null;
3487 if (macro.isBuiltin()) return null;
3488
3489 var tok_count: u8 = 0;
3490 for (macro.tokens) |macro_tok| {
3491 switch (macro_tok.id) {
3492 .invalid => continue,
3493 .whitespace => continue,
3494 .comment => continue,
3495 .macro_ws => continue,
3496 else => {
3497 if (tok_count != 0) return null;
3498 tok_count += 1;
3499 },
3500 }
3501 }
3502
3503 if (t.checkTranslatableMacro(macro.tokens, macro.params) != null) return null;
3504
3505 const ident = try ZigTag.identifier.create(t.arena, name);
3506 return try t.maybeSuppressResult(used, ident);
3507}
3508
3295fn transIntLiteral(3509fn transIntLiteral(
3296 t: *Translator,3510 t: *Translator,
3297 scope: *Scope,3511 scope: *Scope,
...@@ -3299,6 +3513,7 @@ fn transIntLiteral(...@@ -3299,6 +3513,7 @@ fn transIntLiteral(
3299 used: ResultUsed,3513 used: ResultUsed,
3300 suppress_as: SuppressCast,3514 suppress_as: SuppressCast,
3301) TransError!ZigNode {3515) TransError!ZigNode {
3516 if (try t.checkLiteralMacro(literal_index.tok(t.tree), used)) |node| return node;
3302 const val = t.tree.value_map.get(literal_index).?;3517 const val = t.tree.value_map.get(literal_index).?;
3303 const int_lit_node = try t.createIntNode(val);3518 const int_lit_node = try t.createIntNode(val);
3304 if (suppress_as == .no_as) {3519 if (suppress_as == .no_as) {
...@@ -3325,6 +3540,7 @@ fn transCharLiteral(...@@ -3325,6 +3540,7 @@ fn transCharLiteral(
3325 used: ResultUsed,3540 used: ResultUsed,
3326 suppress_as: SuppressCast,3541 suppress_as: SuppressCast,
3327) TransError!ZigNode {3542) TransError!ZigNode {
3543 if (try t.checkLiteralMacro(literal_index.tok(t.tree), used)) |node| return node;
3328 const val = t.tree.value_map.get(literal_index).?;3544 const val = t.tree.value_map.get(literal_index).?;
3329 const char_literal = literal_index.get(t.tree).char_literal;3545 const char_literal = literal_index.get(t.tree).char_literal;
3330 const narrow = char_literal.kind == .ascii or char_literal.kind == .utf8;3546 const narrow = char_literal.kind == .ascii or char_literal.kind == .utf8;
...@@ -3333,7 +3549,7 @@ fn transCharLiteral(...@@ -3333,7 +3549,7 @@ fn transCharLiteral(
3333 // e.g. 'abcd'3549 // e.g. 'abcd'
3334 const int_value = val.toInt(u32, t.comp).?;3550 const int_value = val.toInt(u32, t.comp).?;
3335 const int_lit_node = if (char_literal.kind == .ascii and int_value > 255)3551 const int_lit_node = if (char_literal.kind == .ascii and int_value > 255)
3336 try t.createNumberNode(int_value, .int)3552 try t.createNumberNode(int_value)
3337 else3553 else
3338 try t.createCharLiteralNode(narrow, int_value);3554 try t.createCharLiteralNode(narrow, int_value);
33393555
...@@ -3357,12 +3573,16 @@ fn transFloatLiteral(...@@ -3357,12 +3573,16 @@ fn transFloatLiteral(
3357 used: ResultUsed,3573 used: ResultUsed,
3358 suppress_as: SuppressCast,3574 suppress_as: SuppressCast,
3359) TransError!ZigNode {3575) TransError!ZigNode {
3576 if (try t.checkLiteralMacro(literal_index.tok(t.tree), used)) |node| return node;
3360 const val = t.tree.value_map.get(literal_index).?;3577 const val = t.tree.value_map.get(literal_index).?;
3361 const float_literal = literal_index.get(t.tree).float_literal;3578 const float_literal = literal_index.get(t.tree).float_literal;
33623579
3363 var allocating: std.Io.Writer.Allocating = .init(t.gpa);3580 var allocating: std.Io.Writer.Allocating = .init(t.gpa);
3364 defer allocating.deinit();3581 defer allocating.deinit();
3365 _ = val.print(float_literal.qt, t.comp, &allocating.writer) catch return error.OutOfMemory;3582 _ = val.print(float_literal.qt, t.comp, &allocating.writer) catch return error.OutOfMemory;
3583 if (mem.findScalar(u8, allocating.written(), '.') == null) {
3584 allocating.writer.writeAll(".0") catch return error.OutOfMemory;
3585 }
33663586
3367 const float_lit_node = try ZigTag.float_literal.create(t.arena, try t.arena.dupe(u8, allocating.written()));3587 const float_lit_node = try ZigTag.float_literal.create(t.arena, try t.arena.dupe(u8, allocating.written()));
3368 if (suppress_as == .no_as) {3588 if (suppress_as == .no_as) {
...@@ -3588,7 +3808,7 @@ fn transArrayInit(...@@ -3588,7 +3808,7 @@ fn transArrayInit(
3588 while (i < array_init.items.len) : (i += 1) {3808 while (i < array_init.items.len) : (i += 1) {
3589 if (array_init.items[i].get(t.tree) == .array_filler_expr) break;3809 if (array_init.items[i].get(t.tree) == .array_filler_expr) break;
3590 const expr = try t.transExprCoercing(scope, array_init.items[i], .used);3810 const expr = try t.transExprCoercing(scope, array_init.items[i], .used);
3591 try val_list.append(t.gpa, expr);3811 try val_list.append(t.gpa, try t.toNonBool(expr, array_item_qt));
3592 }3812 }
3593 const array_type = try ZigTag.array_type.create(t.arena, .{3813 const array_type = try ZigTag.array_type.create(t.arena, .{
3594 .elem_type = array_item_type,3814 .elem_type = array_item_type,
...@@ -3638,7 +3858,7 @@ fn transUnionInit(...@@ -3638,7 +3858,7 @@ fn transUnionInit(
3638 const field_init = try t.arena.create(ast.Payload.ContainerInit.Initializer);3858 const field_init = try t.arena.create(ast.Payload.ContainerInit.Initializer);
3639 field_init.* = .{3859 field_init.* = .{
3640 .name = field_name,3860 .name = field_name,
3641 .value = try t.transExprCoercing(scope, init_expr, .used),3861 .value = try t.toNonBool(try t.transExprCoercing(scope, init_expr, .used), field.qt),
3642 };3862 };
3643 const container_init = try ZigTag.container_init.create(t.arena, .{3863 const container_init = try ZigTag.container_init.create(t.arena, .{
3644 .lhs = union_type,3864 .lhs = union_type,
...@@ -3669,7 +3889,7 @@ fn transStructInit(...@@ -3669,7 +3889,7 @@ fn transStructInit(
3669 }).? else field.name.lookup(t.comp);3889 }).? else field.name.lookup(t.comp);
3670 init.* = .{3890 init.* = .{
3671 .name = field_name,3891 .name = field_name,
3672 .value = try t.transExprCoercing(scope, field_expr, .used),3892 .value = try t.toNonBool(try t.transExprCoercing(scope, field_expr, .used), field.qt),
3673 };3893 };
3674 }3894 }
36753895
...@@ -3766,7 +3986,7 @@ fn transConvertvectorExpr(...@@ -3766,7 +3986,7 @@ fn transConvertvectorExpr(
3766 for (items, 0..dest_vec_ty.len) |*item, i| {3986 for (items, 0..dest_vec_ty.len) |*item, i| {
3767 const value = try ZigTag.array_access.create(t.arena, .{3987 const value = try ZigTag.array_access.create(t.arena, .{
3768 .lhs = tmp_ident,3988 .lhs = tmp_ident,
3769 .rhs = try t.createNumberNode(i, .int),3989 .rhs = try t.createNumberNode(i),
3770 });3990 });
37713991
3772 if (src_elem_sk == .float and dest_elem_sk == .float) {3992 if (src_elem_sk == .float and dest_elem_sk == .float) {
...@@ -3812,7 +4032,7 @@ fn transShufflevectorExpr(...@@ -3812,7 +4032,7 @@ fn transShufflevectorExpr(
3812 const mask_len = shufflevector.indexes.len;4032 const mask_len = shufflevector.indexes.len;
38134033
3814 const mask_type = try ZigTag.vector.create(t.arena, .{4034 const mask_type = try ZigTag.vector.create(t.arena, .{
3815 .lhs = try t.createNumberNode(mask_len, .int),4035 .lhs = try t.createNumberNode(mask_len),
3816 .rhs = try ZigTag.type.create(t.arena, "i32"),4036 .rhs = try ZigTag.type.create(t.arena, "i32"),
3817 });4037 });
38184038
...@@ -3882,16 +4102,9 @@ fn createIntNode(t: *Translator, int: aro.Value) !ZigNode {...@@ -3882,16 +4102,9 @@ fn createIntNode(t: *Translator, int: aro.Value) !ZigNode {
3882 return res;4102 return res;
3883}4103}
38844104
3885fn createNumberNode(t: *Translator, num: anytype, num_kind: enum { int, float }) !ZigNode {4105fn createNumberNode(t: *Translator, num: anytype) !ZigNode {
3886 const fmt_s = switch (@typeInfo(@TypeOf(num))) {4106 const str = try std.fmt.allocPrint(t.arena, "{d}", .{num});
3887 .int, .comptime_int => "{d}",4107 return ZigTag.integer_literal.create(t.arena, str);
3888 else => "{s}",
3889 };
3890 const str = try std.fmt.allocPrint(t.arena, fmt_s, .{num});
3891 if (num_kind == .float)
3892 return ZigTag.float_literal.create(t.arena, str)
3893 else
3894 return ZigTag.integer_literal.create(t.arena, str);
3895}4108}
38964109
3897fn createCharLiteralNode(t: *Translator, narrow: bool, val: u32) TransError!ZigNode {4110fn createCharLiteralNode(t: *Translator, narrow: bool, val: u32) TransError!ZigNode {
...@@ -3953,6 +4166,25 @@ fn vectorTypeInfo(t: *Translator, vec_node: ZigNode, field: []const u8) TransErr...@@ -3953,6 +4166,25 @@ fn vectorTypeInfo(t: *Translator, vec_node: ZigNode, field: []const u8) TransErr
3953 return ZigTag.field_access.create(t.arena, .{ .lhs = vector_type_info, .field_name = field });4166 return ZigTag.field_access.create(t.arena, .{ .lhs = vector_type_info, .field_name = field });
3954}4167}
39554168
4169/// Returns true if the given array length qualifies as a flexible array member
4170/// under the current -fstrict-flex-arrays level.
4171fn isFlexibleArrayLen(t: *const Translator, len: anytype) bool {
4172 return switch (t.strict_flex_arrays) {
4173 .@"0" => true,
4174 .@"1" => switch (len) {
4175 .incomplete => true,
4176 .fixed => |n| n <= 1,
4177 else => false,
4178 },
4179 .@"2" => switch (len) {
4180 .incomplete => true,
4181 .fixed => |n| n == 0,
4182 else => false,
4183 },
4184 .@"3" => len == .incomplete,
4185 };
4186}
4187
3956/// Build a getter function for a flexible array field in a C record4188/// Build a getter function for a flexible array field in a C record
3957/// e.g. `T items[]` or `T items[0]`. The generated function returns a [*c] pointer4189/// e.g. `T items[]` or `T items[0]`. The generated function returns a [*c] pointer
3958/// to the flexible array with the correct const and volatile qualifiers4190/// to the flexible array with the correct const and volatile qualifiers
...@@ -3961,7 +4193,13 @@ fn createFlexibleMemberFn(...@@ -3961,7 +4193,13 @@ fn createFlexibleMemberFn(
3961 member_name: []const u8,4193 member_name: []const u8,
3962 field_name: []const u8,4194 field_name: []const u8,
3963) Error!ZigNode {4195) Error!ZigNode {
3964 const self_param_name = "self";4196 // Use `_self` instead of the conventional `self` to avoid the Zig error
4197 // "function parameter shadows declaration of 'self'".
4198 // `processContainerMemberFns` merges C functions matching a struct's name
4199 // prefix into the struct as `pub const` aliases (e.g. `foo_self()` becomes
4200 // `pub const self = __root.foo_self`). A parameter also named `self` would
4201 // then shadow that declaration, which Zig rejects.
4202 const self_param_name = "_self";
3965 const self_param = try ZigTag.identifier.create(t.arena, self_param_name);4203 const self_param = try ZigTag.identifier.create(t.arena, self_param_name);
3966 const self_type = try ZigTag.typeof.create(t.arena, self_param);4204 const self_type = try ZigTag.typeof.create(t.arena, self_param);
39674205
lib/compiler/translate-c/ast.zig+19-11
...@@ -50,6 +50,7 @@ pub const Node = extern union {...@@ -50,6 +50,7 @@ pub const Node = extern union {
50 break_val,50 break_val,
51 @"return",51 @"return",
52 field_access,52 field_access,
53 field_builtin,
53 array_access,54 array_access,
54 call,55 call,
55 var_decl,56 var_decl,
...@@ -371,6 +372,7 @@ pub const Node = extern union {...@@ -371,6 +372,7 @@ pub const Node = extern union {
371 .div_exact,372 .div_exact,
372 .offset_of,373 .offset_of,
373 .static_assert,374 .static_assert,
375 .field_builtin,
374 => Payload.BinOp,376 => Payload.BinOp,
375377
376 .integer_literal,378 .integer_literal,
...@@ -455,14 +457,14 @@ pub const Node = extern union {...@@ -455,14 +457,14 @@ pub const Node = extern union {
455 return .{ .ptr_otherwise = payload };457 return .{ .ptr_otherwise = payload };
456 }458 }
457459
458 pub fn isNoreturn(node: Node, break_counts: bool) bool {460 pub fn isNoreturn(node: Node) bool {
459 switch (node.tag()) {461 return switch (node.tag()) {
460 .block => {462 .block => {
461 const block_node = node.castTag(.block).?;463 const block_node = node.castTag(.block).?;
462 if (block_node.data.stmts.len == 0) return false;464 if (block_node.data.stmts.len == 0) return false;
463465
464 const last = block_node.data.stmts[block_node.data.stmts.len - 1];466 const last = block_node.data.stmts[block_node.data.stmts.len - 1];
465 return last.isNoreturn(break_counts);467 return last.isNoreturn();
466 },468 },
467 .@"switch" => {469 .@"switch" => {
468 const switch_node = node.castTag(.@"switch").?;470 const switch_node = node.castTag(.@"switch").?;
...@@ -475,15 +477,16 @@ pub const Node = extern union {...@@ -475,15 +477,16 @@ pub const Node = extern union {
475 else477 else
476 unreachable;478 unreachable;
477479
478 if (!body.isNoreturn(break_counts)) return false;480 if (!body.isNoreturn()) return false;
479 }481 }
480 return true;482 return true;
481 },483 },
482 .@"return", .return_void => return true,484 .@"return", .return_void => true,
483 .@"break" => if (break_counts) return true,485 .@"break" => true,
484 else => {},486 .@"continue" => true,
485 }487 .@"unreachable" => true,
486 return false;488 else => false,
489 };
487 }490 }
488491
489 pub fn isBoolRes(res: Node) bool {492 pub fn isBoolRes(res: Node) bool {
...@@ -2015,6 +2018,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {...@@ -2015,6 +2018,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
2015 const lhs = try renderNodeGrouped(c, payload.lhs);2018 const lhs = try renderNodeGrouped(c, payload.lhs);
2016 return renderFieldAccess(c, lhs, payload.field_name);2019 return renderFieldAccess(c, lhs, payload.field_name);
2017 },2020 },
2021 .field_builtin => {
2022 const payload = node.castTag(.field_builtin).?.data;
2023 return renderBuiltinCall(c, "@field", &.{ payload.lhs, payload.rhs });
2024 },
2018 .@"struct", .@"union", .@"opaque" => return renderContainer(c, node),2025 .@"struct", .@"union", .@"opaque" => return renderContainer(c, node),
2019 .enum_constant => {2026 .enum_constant => {
2020 const payload = node.castTag(.enum_constant).?.data;2027 const payload = node.castTag(.enum_constant).?.data;
...@@ -2424,7 +2431,7 @@ fn renderNullSentinelArrayType(c: *Context, len: u64, elem_type: Node) !NodeInde...@@ -2424,7 +2431,7 @@ fn renderNullSentinelArrayType(c: *Context, len: u64, elem_type: Node) !NodeInde
2424fn addSemicolonIfNeeded(c: *Context, node: Node) !void {2431fn addSemicolonIfNeeded(c: *Context, node: Node) !void {
2425 switch (node.tag()) {2432 switch (node.tag()) {
2426 .warning => unreachable,2433 .warning => unreachable,
2427 .var_decl, .var_simple, .arg_redecl, .alias, .block, .empty_block, .block_single, .@"switch", .wrapped_local, .mut_str => {},2434 .static_assert, .var_decl, .var_simple, .arg_redecl, .alias, .block, .empty_block, .block_single, .@"switch", .wrapped_local, .mut_str => {},
2428 .while_true => {2435 .while_true => {
2429 const payload = node.castTag(.while_true).?.data;2436 const payload = node.castTag(.while_true).?.data;
2430 return addSemicolonIfNotBlock(c, payload);2437 return addSemicolonIfNotBlock(c, payload);
...@@ -2532,6 +2539,8 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {...@@ -2532,6 +2539,8 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
2532 .trunc,2539 .trunc,
2533 .floor,2540 .floor,
2534 .root_ref,2541 .root_ref,
2542 .field_builtin,
2543 .@"switch",
2535 => {2544 => {
2536 // no grouping needed2545 // no grouping needed
2537 return renderNode(c, node);2546 return renderNode(c, node);
...@@ -2594,7 +2603,6 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {...@@ -2594,7 +2603,6 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
2594 .pub_var_simple,2603 .pub_var_simple,
2595 .enum_constant,2604 .enum_constant,
2596 .@"while",2605 .@"while",
2597 .@"switch",
2598 .@"break",2606 .@"break",
2599 .break_val,2607 .break_val,
2600 .pub_inline_fn,2608 .pub_inline_fn,
lib/compiler/translate-c/main.zig+75-16
...@@ -1,15 +1,17 @@...@@ -1,15 +1,17 @@
1const std = @import("std");1const std = @import("std");
2const Io = std.Io;
3const assert = std.debug.assert;2const assert = std.debug.assert;
4const mem = std.mem;3const mem = std.mem;
5const process = std.process;4const process = std.process;
5const Io = std.Io;
6
6const aro = @import("aro");7const aro = @import("aro");
7const compiler_util = @import("../util.zig");8const compiler_util = @import("../util.zig");
9
8const Translator = @import("Translator.zig");10const Translator = @import("Translator.zig");
911
10const fast_exit = @import("builtin").mode != .Debug;12const fast_exit = @import("builtin").mode != .Debug;
1113
12pub fn main(init: std.process.Init) u8 {14pub fn main(init: process.Init) u8 {
13 const gpa = init.gpa;15 const gpa = init.gpa;
14 const arena = init.arena.allocator();16 const arena = init.arena.allocator();
15 const io = init.io;17 const io = init.io;
...@@ -33,16 +35,20 @@ pub fn main(init: std.process.Init) u8 {...@@ -33,16 +35,20 @@ pub fn main(init: std.process.Init) u8 {
33 var stderr = Io.File.stderr().writer(io, &stderr_buf);35 var stderr = Io.File.stderr().writer(io, &stderr_buf);
34 var diagnostics: aro.Diagnostics = switch (zig_integration) {36 var diagnostics: aro.Diagnostics = switch (zig_integration) {
35 false => .{ .output = .{ .to_writer = .{37 false => .{ .output = .{ .to_writer = .{
36 .mode = Io.Terminal.Mode.detect(io, stderr.file, NO_COLOR, CLICOLOR_FORCE) catch unreachable,38 .mode = Io.Terminal.Mode.detect(io, stderr.file, NO_COLOR, CLICOLOR_FORCE) catch .no_color,
37 .writer = &stderr.interface,39 .writer = &stderr.interface,
38 } } },40 } } },
39 true => .{ .output = .{ .to_list = .{41 true => .{ .output = .{ .to_list = .{ .arena = .init(gpa) } } },
40 .arena = .init(gpa),
41 } } },
42 };42 };
43 defer diagnostics.deinit();43 defer diagnostics.deinit();
4444
45 var comp = aro.Compilation.initDefault(gpa, arena, io, &diagnostics, .cwd(), environ_map) catch |err| switch (err) {45 var comp = aro.Compilation.init(.{
46 .gpa = gpa,
47 .arena = arena,
48 .io = io,
49 .diagnostics = &diagnostics,
50 .environ_map = environ_map,
51 }) catch |err| switch (err) {
46 error.OutOfMemory => {52 error.OutOfMemory => {
47 std.debug.print("ran out of memory initializing C compilation\n", .{});53 std.debug.print("ran out of memory initializing C compilation\n", .{});
48 if (fast_exit) process.exit(1);54 if (fast_exit) process.exit(1);
...@@ -82,7 +88,6 @@ pub fn main(init: std.process.Init) u8 {...@@ -82,7 +88,6 @@ pub fn main(init: std.process.Init) u8 {
82 return 1;88 return 1;
83 },89 },
84 };90 };
85
86 assert(comp.diagnostics.errors == 0 or !zig_integration);91 assert(comp.diagnostics.errors == 0 or !zig_integration);
87 if (fast_exit) process.exit(@intFromBool(comp.diagnostics.errors != 0));92 if (fast_exit) process.exit(@intFromBool(comp.diagnostics.errors != 0));
88 return @intFromBool(comp.diagnostics.errors != 0);93 return @intFromBool(comp.diagnostics.errors != 0);
...@@ -107,10 +112,23 @@ pub const usage =...@@ -107,10 +112,23 @@ pub const usage =
107 \\Usage {s}: [options] file [CC options]112 \\Usage {s}: [options] file [CC options]
108 \\113 \\
109 \\Options:114 \\Options:
110 \\ --help Print this message115 \\ --help Print this message
111 \\ --version Print translate-c version116 \\ --version Print translate-c version
112 \\ -fmodule-libs Import libraries as modules117 \\ -fmodule-libs Import libraries as modules
113 \\ -fno-module-libs (default) Install libraries next to output file118 \\ -fno-module-libs (default) Install libraries next to output file
119 \\ -fpub-static (default) Translate static functions as pub
120 \\ -fno-pub-static Do not translate static functions as pub
121 \\ -ffunc-bodies (default) Translate function bodies
122 \\ -fno-func-bodies Do not translate function bodies
123 \\ -fkeep-macro-literals (default) Preserve macro names for literals
124 \\ -fno-keep-macro-literals Do not preserve macro names for literals
125 \\ -fdefault-init Default initialize struct fields
126 \\ -fno-default-init (default) Do not default initialize struct fields
127 \\ -fstrict-flex-arrays=<n> Control when to treat a trailing array as a flexible array member (default: 2)
128 \\ 0: any trailing array
129 \\ 1: size [0]/[1]/[]
130 \\ 2: size [0]/[]
131 \\ 3: [] only
114 \\132 \\
115 \\133 \\
116;134;
...@@ -119,7 +137,14 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: []const [:0]const u8, zig...@@ -119,7 +137,14 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: []const [:0]const u8, zig
119 const gpa = d.comp.gpa;137 const gpa = d.comp.gpa;
120 const io = d.comp.io;138 const io = d.comp.io;
121139
122 var aro_args: std.ArrayList([:0]const u8) = .empty;140 var module_libs = true;
141 var pub_static = true;
142 var func_bodies = true;
143 var keep_macro_literals = true;
144 var default_init = true;
145 var strict_flex_arrays: Translator.StrictFlexArraysLevel = .@"2";
146
147 var aro_args: std.ArrayList([:0]const u8) = try .initCapacity(gpa, args.len);
123 defer aro_args.deinit(gpa);148 defer aro_args.deinit(gpa);
124149
125 for (args, 0..) |arg, i| {150 for (args, 0..) |arg, i| {
...@@ -139,8 +164,34 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: []const [:0]const u8, zig...@@ -139,8 +164,34 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: []const [:0]const u8, zig
139 } else if (mem.eql(u8, arg, "--zig-integration")) {164 } else if (mem.eql(u8, arg, "--zig-integration")) {
140 if (i != 1 or !zig_integration)165 if (i != 1 or !zig_integration)
141 return d.fatal("--zig-integration must be the first argument", .{});166 return d.fatal("--zig-integration must be the first argument", .{});
167 } else if (mem.eql(u8, arg, "-fmodule-libs")) {
168 module_libs = true;
169 } else if (mem.eql(u8, arg, "-fno-module-libs")) {
170 module_libs = false;
171 } else if (mem.eql(u8, arg, "-fpub-static")) {
172 pub_static = true;
173 } else if (mem.eql(u8, arg, "-fno-pub-static")) {
174 pub_static = false;
175 } else if (mem.eql(u8, arg, "-ffunc-bodies")) {
176 func_bodies = true;
177 } else if (mem.eql(u8, arg, "-fno-func-bodies")) {
178 func_bodies = false;
179 } else if (mem.eql(u8, arg, "-fkeep-macro-literals")) {
180 keep_macro_literals = true;
181 } else if (mem.eql(u8, arg, "-fno-keep-macro-literals")) {
182 keep_macro_literals = false;
183 } else if (mem.eql(u8, arg, "-fdefault-init")) {
184 default_init = true;
185 } else if (mem.eql(u8, arg, "-fno-default-init")) {
186 default_init = false;
187 } else if (mem.startsWith(u8, arg, "-fstrict-flex-arrays=")) {
188 const val_str = arg["-fstrict-flex-arrays=".len..];
189 if (val_str.len != 1 or val_str[0] < '0' or val_str[0] > '3') {
190 return d.fatal("-fstrict-flex-arrays= requires a value of '0', '1', '2', or '3'", .{});
191 }
192 strict_flex_arrays = @enumFromInt(val_str[0] - '0');
142 } else {193 } else {
143 try aro_args.append(gpa, arg);194 aro_args.appendAssumeCapacity(arg);
144 }195 }
145 }196 }
146 const user_macros = macros: {197 const user_macros = macros: {
...@@ -148,7 +199,7 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: []const [:0]const u8, zig...@@ -148,7 +199,7 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: []const [:0]const u8, zig
148 defer macro_buf.deinit(gpa);199 defer macro_buf.deinit(gpa);
149200
150 var discard_buf: [256]u8 = undefined;201 var discard_buf: [256]u8 = undefined;
151 var discarding: std.Io.Writer.Discarding = .init(&discard_buf);202 var discarding: Io.Writer.Discarding = .init(&discard_buf);
152 assert(!try d.parseArgs(&discarding.writer, &macro_buf, aro_args.items));203 assert(!try d.parseArgs(&discarding.writer, &macro_buf, aro_args.items));
153 if (macro_buf.items.len > std.math.maxInt(u32)) {204 if (macro_buf.items.len > std.math.maxInt(u32)) {
154 return d.fatal("user provided macro source exceeded max size", .{});205 return d.fatal("user provided macro source exceeded max size", .{});
...@@ -185,7 +236,9 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: []const [:0]const u8, zig...@@ -185,7 +236,9 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: []const [:0]const u8, zig
185 else => |e| return e,236 else => |e| return e,
186 };237 };
187238
188 var pp = try aro.Preprocessor.initDefault(d.comp);239 var pp = try aro.Preprocessor.init(d.comp, .{
240 .base_file = source.id,
241 });
189 defer pp.deinit();242 defer pp.deinit();
190243
191 var name_buf: [std.fs.max_name_bytes]u8 = undefined;244 var name_buf: [std.fs.max_name_bytes]u8 = undefined;
...@@ -235,6 +288,12 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: []const [:0]const u8, zig...@@ -235,6 +288,12 @@ fn translate(d: *aro.Driver, tc: *aro.Toolchain, args: []const [:0]const u8, zig
235 .comp = d.comp,288 .comp = d.comp,
236 .pp = &pp,289 .pp = &pp,
237 .tree = &c_tree,290 .tree = &c_tree,
291 .module_libs = module_libs,
292 .pub_static = pub_static,
293 .func_bodies = func_bodies,
294 .keep_macro_literals = keep_macro_literals,
295 .default_init = default_init,
296 .strict_flex_arrays = strict_flex_arrays,
238 });297 });
239 defer gpa.free(rendered_zig);298 defer gpa.free(rendered_zig);
240299
lib/std/zig/c_translation/helpers.zig+14-9
...@@ -81,15 +81,20 @@ fn ToUnsigned(comptime T: type) type {...@@ -81,15 +81,20 @@ fn ToUnsigned(comptime T: type) type {
81}81}
8282
83/// Constructs a [*c] pointer with the const and volatile annotations83/// Constructs a [*c] pointer with the const and volatile annotations
84/// from Self for pointing to a C flexible array of Element.84/// from SelfType for pointing to a C flexible array of ElementType.
85pub fn FlexibleArrayType(comptime Self: type, comptime Element: type) type {85pub fn FlexibleArrayType(comptime SelfType: type, comptime ElementType: type) type {
86 return switch (@typeInfo(Self)) {86 switch (@typeInfo(SelfType)) {
87 .pointer => |ptr| @Pointer(.c, .{87 .pointer => |ptr| {
88 .@"const" = ptr.is_const,88 return @Pointer(.c, .{
89 .@"volatile" = ptr.is_volatile,89 .@"const" = ptr.is_const,
90 }, Element, null),90 .@"volatile" = ptr.is_volatile,
91 else => |info| @compileError("Invalid self type \"" ++ @tagName(info) ++ "\" for flexible array getter: " ++ @typeName(Self)),91 .@"allowzero" = true,
92 };92 .@"addrspace" = .generic,
93 .@"align" = null,
94 }, ElementType, null);
95 },
96 else => |info| @compileError("Invalid self type \"" ++ @tagName(info) ++ "\" for flexible array getter: " ++ @typeName(SelfType)),
97 }
93}98}
9499
95/// Promote the type of an integer literal until it fits as C would.100/// Promote the type of an integer literal until it fits as C would.