1const std = @import("std");
2const mem = std.mem;
3const assert = std.debug.assert;
4const CallingConvention = std.builtin.CallingConvention;
5
6const aro = @import("aro");
7const CToken = aro.Tokenizer.Token;
8const Tree = aro.Tree;
9const Node = Tree.Node;
10const TokenIndex = Tree.TokenIndex;
11const QualType = aro.QualType;
12
13const ast = @import("ast.zig");
14const ZigNode = ast.Node;
15const ZigTag = ZigNode.Tag;
16const builtins = @import("builtins.zig");
17const helpers = @import("helpers.zig");
18const MacroTranslator = @import("MacroTranslator.zig");
19const PatternList = @import("PatternList.zig");
20const Scope = @import("Scope.zig");
21
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
62pub const Error = std.mem.Allocator.Error;
63pub const MacroProcessingError = Error || error{UnexpectedMacroToken};
64pub const TypeError = Error || error{UnsupportedType};
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};
79
80const Translator = @This();
81
82/// The C AST to be translated.
83tree: *const Tree,
84/// The compilation corresponding to the AST.
85comp: *aro.Compilation,
86/// The Preprocessor that produced the source for `tree`.
87pp: *const aro.Preprocessor,
88
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
100gpa: mem.Allocator,
101arena: mem.Allocator,
102
103alias_list: Scope.AliasList,
104global_scope: *Scope.Root,
105/// Running number used for creating new unique identifiers.
106mangle_count: u32 = 0,
107
108/// Table of declarations for enum, struct, union and typedef types.
109type_decls: std.array_hash_map.Auto(Node.Index, []const u8) = .empty,
110/// Table of record decls that have been demoted to opaques.
111opaque_demotes: std.HashMapUnmanaged(QualType, void, QualTypeHashContext, std.hash_map.default_max_load_percentage) = .empty,
112/// Table of unnamed enums and records that are child types of typedefs.
113unnamed_typedefs: std.HashMapUnmanaged(QualType, []const u8, QualTypeHashContext, std.hash_map.default_max_load_percentage) = .empty,
114/// Table of anonymous record to generated field names.
115anonymous_record_field_names: std.HashMapUnmanaged(
116 AnonymousRecordFieldNames.Key,
117 []const u8,
118 AnonymousRecordFieldNames.Context,
119 std.hash_map.default_max_load_percentage,
120) = .empty,
121
122/// This one is different than the root scope's name table. This contains
123/// a list of names that we found by visiting all the top level decls without
124/// translating them. The other maps are updated as we translate; this one is updated
125/// up front in a pre-processing step.
126global_names: std.array_hash_map.String(void) = .empty,
127
128/// This is similar to `global_names`, but contains names which we would
129/// *like* to use, but do not strictly *have* to if they are unavailable.
130/// These are relevant to types, which ideally we would name like
131/// 'struct_foo' with an alias 'foo', but if either of those names is taken,
132/// may be mangled.
133/// This is distinct from `global_names` so we can detect at a type
134/// declaration whether or not the name is available.
135weak_global_names: std.array_hash_map.String(void) = .empty,
136
137/// Set of identifiers known to refer to typedef declarations.
138/// Used when parsing macros.
139typedefs: std.array_hash_map.String(void) = .empty,
140
141/// The lhs lval of a compound assignment expression.
142compound_assign_dummy: ?ZigNode = null,
143
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
148pub fn getMangle(t: *Translator) u32 {
149 t.mangle_count += 1;
150 return t.mangle_count;
151}
152
153/// Convert an `aro.Source.Location` to a 'file:line:column' string.
154pub fn locStr(t: *Translator, loc: aro.Source.Location) ![]const u8 {
155 const expanded = loc.expand(t.comp);
156 const filename = expanded.path;
157
158 const line = expanded.line_no;
159 const col = expanded.col;
160
161 return std.fmt.allocPrint(t.arena, "{s}:{d}:{d}", .{ filename, line, col });
162}
163
164fn maybeSuppressResult(t: *Translator, used: ResultUsed, result: ZigNode) TransError!ZigNode {
165 if (used == .used) return result;
166 return ZigTag.discard.create(t.arena, .{ .should_skip = false, .value = result });
167}
168
169pub fn addTopLevelDecl(t: *Translator, name: []const u8, decl_node: ZigNode) !void {
170 const gop = try t.global_scope.sym_table.getOrPut(t.gpa, name);
171 if (gop.found_existing) return; // Any duplicate decls are equivalent
172 gop.value_ptr.* = decl_node;
173 try t.global_scope.nodes.append(t.gpa, decl_node);
174}
175
176fn fail(
177 t: *Translator,
178 err: anytype,
179 source_loc: TokenIndex,
180 comptime format: []const u8,
181 args: anytype,
182) (@TypeOf(err) || error{OutOfMemory}) {
183 try t.warn(&t.global_scope.base, source_loc, format, args);
184 return err;
185}
186
187pub fn failDecl(
188 t: *Translator,
189 scope: *Scope,
190 tok_idx: TokenIndex,
191 name: []const u8,
192 comptime format: []const u8,
193 args: anytype,
194) Error!void {
195 const loc = t.tree.tokens.items(.loc)[tok_idx];
196 return t.failDeclExtra(scope, loc, name, format, args);
197}
198
199pub fn failDeclExtra(
200 t: *Translator,
201 scope: *Scope,
202 loc: aro.Source.Location,
203 name: []const u8,
204 comptime format: []const u8,
205 args: anytype,
206) Error!void {
207 // location
208 // pub const name = @compileError(msg);
209 const fail_msg = try std.fmt.allocPrint(t.arena, format, args);
210 const fail_decl = try ZigTag.fail_decl.create(t.arena, .{
211 .actual = name,
212 .mangled = fail_msg,
213 .local = scope.id != .root,
214 });
215
216 const str = try t.locStr(loc);
217 const location_comment = try std.fmt.allocPrint(t.arena, "// {s}", .{str});
218 const loc_node = try ZigTag.warning.create(t.arena, location_comment);
219
220 if (scope.id == .root) {
221 try t.addTopLevelDecl(name, fail_decl);
222 try scope.appendNode(loc_node);
223 } else {
224 try scope.appendNode(fail_decl);
225 try scope.appendNode(loc_node);
226
227 const bs = try scope.findBlockScope(t);
228 try bs.discardVariable(name);
229 }
230}
231
232fn warn(t: *Translator, scope: *Scope, tok_idx: TokenIndex, comptime format: []const u8, args: anytype) !void {
233 const loc = t.tree.tokens.items(.loc)[tok_idx];
234 const str = try t.locStr(loc);
235 const value = try std.fmt.allocPrint(t.arena, "// {s}: warning: " ++ format, .{str} ++ args);
236 try scope.appendNode(try ZigTag.warning.create(t.arena, value));
237}
238
239pub const Options = struct {
240 gpa: mem.Allocator,
241 comp: *aro.Compilation,
242 pp: *const aro.Preprocessor,
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,
250};
251
252pub fn translate(options: Options) mem.Allocator.Error![]u8 {
253 const gpa = options.gpa;
254 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
255 defer arena_allocator.deinit();
256 const arena = arena_allocator.allocator();
257
258 var translator: Translator = .{
259 .gpa = gpa,
260 .arena = arena,
261 .alias_list = .empty,
262 .global_scope = try arena.create(Scope.Root),
263 .comp = options.comp,
264 .pp = options.pp,
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,
271 };
272 translator.global_scope.* = Scope.Root.init(&translator);
273 defer {
274 translator.type_decls.deinit(gpa);
275 translator.alias_list.deinit(gpa);
276 translator.global_names.deinit(gpa);
277 translator.weak_global_names.deinit(gpa);
278 translator.opaque_demotes.deinit(gpa);
279 translator.unnamed_typedefs.deinit(gpa);
280 translator.anonymous_record_field_names.deinit(gpa);
281 translator.typedefs.deinit(gpa);
282 translator.global_scope.deinit();
283 translator.wip_var_inits.deinit(gpa);
284 }
285
286 try translator.prepopulateGlobalNameTable();
287 try translator.transTopLevelDecls();
288
289 // Insert empty line before macros.
290 try translator.global_scope.nodes.append(gpa, try ZigTag.warning.create(arena, "\n"));
291
292 try translator.transMacros();
293
294 for (translator.alias_list.items) |alias| {
295 if (!translator.global_scope.sym_table.contains(alias.alias)) {
296 const node = try ZigTag.alias.create(arena, .{ .actual = alias.alias, .mangled = alias.name });
297 try translator.addTopLevelDecl(alias.alias, node);
298 }
299 }
300
301 try translator.global_scope.processContainerMemberFns();
302
303 var allocating: std.Io.Writer.Allocating = .init(gpa);
304 defer allocating.deinit();
305
306 allocating.writer.writeAll(
307 \\const __root = @This();
308 \\pub const __builtin = @import("std").zig.c_translation.builtins;
309 \\pub const __helpers = @import("std").zig.c_translation.helpers;
310 \\
311 ) catch return error.OutOfMemory;
312
313 var zig_ast = try ast.render(gpa, translator.global_scope.nodes.items);
314 defer {
315 gpa.free(zig_ast.source);
316 zig_ast.deinit(gpa);
317 }
318 zig_ast.render(gpa, &allocating.writer, .{}) catch return error.OutOfMemory;
319 return allocating.toOwnedSlice();
320}
321
322fn prepopulateGlobalNameTable(t: *Translator) !void {
323 for (t.tree.root_decls.items) |decl| {
324 switch (decl.get(t.tree)) {
325 .typedef => |typedef_decl| {
326 const decl_name = t.tree.tokSlice(typedef_decl.name_tok);
327 try t.global_names.put(t.gpa, decl_name, {});
328
329 // Check for typedefs with unnamed enum/record child types.
330 const base = typedef_decl.qt.base(t.comp);
331 switch (base.type) {
332 .@"enum" => |enum_ty| {
333 if (enum_ty.name.lookup(t.comp)[0] != '(') continue;
334 },
335 .@"struct", .@"union" => |record_ty| {
336 if (record_ty.name.lookup(t.comp)[0] != '(') continue;
337 },
338 else => continue,
339 }
340
341 const gop = try t.unnamed_typedefs.getOrPut(t.gpa, base.qt);
342 if (gop.found_existing) {
343 // One typedef can declare multiple names.
344 // Don't put this one in `decl_table` so it's processed later.
345 continue;
346 }
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, {});
350 },
351
352 .struct_decl,
353 .union_decl,
354 .struct_forward_decl,
355 .union_forward_decl,
356 .enum_decl,
357 .enum_forward_decl,
358 => {
359 const decl_qt = decl.qt(t.tree);
360 const prefix, const name = switch (decl_qt.base(t.comp).type) {
361 .@"struct" => |struct_ty| .{ "struct", struct_ty.name.lookup(t.comp) },
362 .@"union" => |union_ty| .{ "union", union_ty.name.lookup(t.comp) },
363 .@"enum" => |enum_ty| .{ "enum", enum_ty.name.lookup(t.comp) },
364 else => unreachable,
365 };
366 const prefixed_name = try std.fmt.allocPrint(t.arena, "{s}_{s}", .{ prefix, name });
367 // `name` and `prefixed_name` are the preferred names for this type.
368 // However, we can name it anything else if necessary, so these are "weak names".
369 try t.weak_global_names.ensureUnusedCapacity(t.gpa, 2);
370 t.weak_global_names.putAssumeCapacity(name, {});
371 t.weak_global_names.putAssumeCapacity(prefixed_name, {});
372 },
373
374 .function, .variable => {
375 const decl_name = t.tree.tokSlice(decl.tok(t.tree));
376 try t.global_names.put(t.gpa, decl_name, {});
377 },
378 .static_assert => {},
379 .empty_decl => {},
380 .global_asm => {},
381 else => unreachable,
382 }
383 }
384
385 for (t.pp.defines.keys(), t.pp.defines.values()) |name, macro| {
386 if (macro.isBuiltin()) continue;
387 if (!t.isSelfDefinedMacro(name, macro)) {
388 try t.global_names.put(t.gpa, name, {});
389 }
390 }
391}
392
393/// Determines whether macro is of the form: `#define FOO FOO` (Possibly with trailing tokens)
394/// Macros of this form will not be translated.
395fn isSelfDefinedMacro(t: *Translator, name: []const u8, macro: aro.Preprocessor.Macro) bool {
396 if (macro.is_func) return false;
397
398 if (macro.tokens.len < 1) return false;
399 const first_tok = macro.tokens[0];
400
401 const source = t.comp.getSource(macro.loc.id);
402 const slice = source.buf[first_tok.start..first_tok.end];
403
404 return std.mem.eql(u8, name, slice);
405}
406
407// =======================
408// Declaration translation
409// =======================
410
411fn transTopLevelDecls(t: *Translator) !void {
412 for (t.tree.root_decls.items) |decl| {
413 try t.transDecl(&t.global_scope.base, decl);
414 }
415}
416
417fn transDecl(t: *Translator, scope: *Scope, decl: Node.Index) !void {
418 switch (decl.get(t.tree)) {
419 .typedef => |typedef_decl| {
420 // Implicit typedefs are translated only if referenced.
421 if (typedef_decl.implicit) return;
422 try t.transTypeDef(scope, decl);
423 },
424
425 .struct_decl, .union_decl => |record_decl| {
426 try t.transRecordDecl(scope, record_decl.container_qt);
427 },
428
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
436 .enum_decl => |enum_decl| {
437 try t.transEnumDecl(scope, enum_decl.container_qt);
438 },
439
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
447 .enum_field,
448 .record_field,
449 => return,
450
451 .function => |function| {
452 if (function.definition) |definition| {
453 return t.transFnDecl(scope, definition.get(t.tree).function);
454 }
455 try t.transFnDecl(scope, function);
456 },
457
458 .variable => |variable| {
459 if (variable.definition != null) return;
460 try t.transVarDecl(scope, variable, decl);
461 },
462 .static_assert => |static_assert| {
463 try t.transStaticAssert(&t.global_scope.base, static_assert);
464 },
465 .global_asm => |global_asm| {
466 try t.transGlobalAsm(&t.global_scope.base, global_asm);
467 },
468 .empty_decl => {},
469 else => unreachable,
470 }
471}
472
473pub const builtin_typedef_map = std.StaticStringMap([]const u8).initComptime(.{
474 .{ "uint8_t", "u8" },
475 .{ "int8_t", "i8" },
476 .{ "uint16_t", "u16" },
477 .{ "int16_t", "i16" },
478 .{ "uint24_t", "u24" },
479 .{ "int24_t", "i24" },
480 .{ "uint32_t", "u32" },
481 .{ "int32_t", "i32" },
482 .{ "uint48_t", "u48" },
483 .{ "int48_t", "i48" },
484 .{ "uint64_t", "u64" },
485 .{ "int64_t", "i64" },
486 .{ "intptr_t", "isize" },
487 .{ "uintptr_t", "usize" },
488 .{ "ssize_t", "isize" },
489 .{ "size_t", "usize" },
490});
491
492fn transTypeDef(t: *Translator, scope: *Scope, typedef_node: Node.Index) Error!void {
493 const typedef_decl = typedef_node.get(t.tree).typedef;
494 if (t.type_decls.get(typedef_node)) |_|
495 return; // Avoid processing this decl twice
496
497 const toplevel = scope.id == .root;
498 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined;
499
500 var name: []const u8 = t.tree.tokSlice(typedef_decl.name_tok);
501 try t.typedefs.put(t.gpa, name, {});
502
503 if (builtin_typedef_map.get(name)) |builtin| {
504 return t.type_decls.putNoClobber(t.gpa, typedef_node, builtin);
505 }
506 if (!toplevel) name = try bs.makeMangledName(name);
507 try t.type_decls.putNoClobber(t.gpa, typedef_node, name);
508
509 const typedef_loc = typedef_decl.name_tok;
510 const init_node = t.transType(scope, typedef_decl.qt, typedef_loc) catch |err| switch (err) {
511 error.UnsupportedType => {
512 return t.failDecl(scope, typedef_loc, name, "unable to resolve typedef child type", .{});
513 },
514 error.OutOfMemory => |e| return e,
515 };
516
517 const payload = try t.arena.create(ast.Payload.SimpleVarDecl);
518 payload.* = .{
519 .base = .{ .tag = if (toplevel) .pub_var_simple else .var_simple },
520 .data = .{
521 .name = name,
522 .init = init_node,
523 },
524 };
525 const node = ZigNode.initPayload(&payload.base);
526
527 if (toplevel) {
528 try t.addTopLevelDecl(name, node);
529 } else {
530 try scope.appendNode(node);
531 try bs.discardVariable(name);
532 }
533}
534
535fn mangleWeakGlobalName(t: *Translator, want_name: []const u8) Error![]const u8 {
536 var cur_name = want_name;
537
538 if (!t.weak_global_names.contains(want_name)) {
539 // This type wasn't noticed by the name detection pass, so nothing has been treating this as
540 // a weak global name. We must mangle it to avoid conflicts with locals.
541 cur_name = try std.fmt.allocPrint(t.arena, "{s}_{d}", .{ want_name, t.getMangle() });
542 }
543
544 while (t.global_names.contains(cur_name)) {
545 cur_name = try std.fmt.allocPrint(t.arena, "{s}_{d}", .{ want_name, t.getMangle() });
546 }
547 return cur_name;
548}
549
550fn transRecordDecl(t: *Translator, scope: *Scope, record_qt: QualType) Error!void {
551 const base = record_qt.base(t.comp);
552 const record_ty = switch (base.type) {
553 .@"struct", .@"union" => |record_ty| record_ty,
554 else => unreachable,
555 };
556
557 if (t.type_decls.get(record_ty.decl_node)) |_|
558 return; // Avoid processing this decl twice
559
560 const toplevel = scope.id == .root;
561 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined;
562
563 const container_kind: ZigTag = if (base.type == .@"union") .@"union" else .@"struct";
564 const container_kind_name = @tagName(container_kind);
565
566 var bare_name = record_ty.name.lookup(t.comp);
567 var is_unnamed = false;
568 var name = bare_name;
569
570 if (t.unnamed_typedefs.get(base.qt)) |typedef_name| {
571 bare_name = typedef_name;
572 name = typedef_name;
573 } else {
574 if (record_ty.isAnonymous(t.comp)) {
575 bare_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{t.getMangle()});
576 is_unnamed = true;
577 }
578 name = try std.fmt.allocPrint(t.arena, "{s}_{s}", .{ container_kind_name, bare_name });
579 if (toplevel and !is_unnamed) {
580 name = try t.mangleWeakGlobalName(name);
581 }
582 }
583 if (!toplevel) name = try bs.makeMangledName(name);
584 try t.type_decls.putNoClobber(t.gpa, record_ty.decl_node, name);
585
586 const is_pub = toplevel and !is_unnamed;
587 const init_node = init: {
588 if (record_ty.layout == null) {
589 try t.opaque_demotes.put(t.gpa, base.qt, {});
590 break :init ZigTag.opaque_literal.init();
591 }
592
593 var fields: std.ArrayList(ast.Payload.Container.Field) = .empty;
594 defer fields.deinit(t.gpa);
595 try fields.ensureUnusedCapacity(t.gpa, record_ty.fields.len);
596
597 var functions: std.ArrayList(ZigNode) = .empty;
598 defer functions.deinit(t.gpa);
599
600 var unnamed_field_count: u32 = 0;
601
602 // If a record doesn't have any attributes that would affect the alignment and
603 // layout, then we can just use a simple `extern` type. If it does have attributes,
604 // then we need to inspect the layout and assign an `align` value for each field.
605 const has_alignment_attributes = aligned: {
606 if (record_qt.hasAttribute(t.comp, .@"packed")) break :aligned true;
607 if (record_qt.hasAttribute(t.comp, .aligned)) break :aligned true;
608 for (record_ty.fields) |field| {
609 const field_attrs = field.attributes(t.comp);
610 for (field_attrs) |field_attr| {
611 switch (field_attr.tag) {
612 .@"packed", .aligned => break :aligned true,
613 else => {},
614 }
615 }
616 }
617 break :aligned false;
618 };
619 const head_field_alignment: ?c_uint = if (has_alignment_attributes) t.headFieldAlignment(record_ty) else null;
620
621 for (record_ty.fields, 0..) |field, field_index| {
622 const field_loc = field.name_tok;
623
624 // Demote record to opaque if it contains a bitfield
625 if (field.bit_width != .null) {
626 try t.opaque_demotes.put(t.gpa, base.qt, {});
627 try t.warn(scope, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name});
628 break :init ZigTag.opaque_literal.init();
629 }
630
631 var field_name = field.name.lookup(t.comp);
632 if (field.name_tok == 0) {
633 field_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{unnamed_field_count});
634 unnamed_field_count += 1;
635 try t.anonymous_record_field_names.put(t.gpa, .{
636 .parent = base.qt,
637 .field = field.qt,
638 }, field_name);
639 }
640
641 const field_type = field_type: {
642 // Check if this is a flexible array member.
643 flexible: {
644 if (field_index != record_ty.fields.len - 1 and container_kind != .@"union") break :flexible;
645 const array_ty = field.qt.get(t.comp, .array) orelse break :flexible;
646 if (!t.isFlexibleArrayLen(array_ty.len)) break :flexible;
647
648 const elem_type = t.transType(scope, array_ty.elem, field_loc) catch |err| switch (err) {
649 error.UnsupportedType => break :flexible,
650 else => |e| return e,
651 };
652 const backing_array_len: usize = switch (array_ty.len) {
653 .fixed => |n| @intCast(n),
654 else => 0,
655 };
656 const backing_array = try ZigTag.array_type.create(t.arena, .{ .len = backing_array_len, .elem_type = elem_type });
657
658 const member_name = field_name;
659 field_name = try std.fmt.allocPrint(t.arena, "_{s}", .{field_name});
660
661 const member = try t.createFlexibleMemberFn(member_name, field_name);
662 try functions.append(t.gpa, member);
663
664 break :field_type backing_array;
665 }
666
667 break :field_type t.transType(scope, field.qt, field_loc) catch |err| switch (err) {
668 error.UnsupportedType => {
669 try t.opaque_demotes.put(t.gpa, base.qt, {});
670 try t.warn(scope, field.name_tok, "{s} demoted to opaque type - unable to translate type of field {s}", .{
671 container_kind_name,
672 field_name,
673 });
674 break :init ZigTag.opaque_literal.init();
675 },
676 else => |e| return e,
677 };
678 };
679
680 // Demote record to opaque if it contains an opaque field
681 if (t.typeWasDemotedToOpaque(field.qt)) {
682 try t.opaque_demotes.put(t.gpa, base.qt, {});
683 try t.warn(scope, field_loc, "{s} demoted to opaque type - has opaque field", .{container_kind_name});
684 break :init ZigTag.opaque_literal.init();
685 }
686
687 const field_alignment = if (has_alignment_attributes)
688 t.alignmentForField(record_ty, head_field_alignment, field_index)
689 else
690 null;
691
692 // C99 introduced designated initializers for structs. Omitted fields are implicitly
693 // initialized to zero. Some C APIs are designed with this in mind. Defaulting to zero
694 // values for translated struct fields permits Zig code to comfortably use such an API.
695 const default_value = if (t.default_init and container_kind == .@"struct")
696 try t.createZeroValueNode(field.qt, field_type, .no_as)
697 else
698 null;
699
700 fields.appendAssumeCapacity(.{
701 .name = field_name,
702 .type = field_type,
703 .alignment = field_alignment,
704 .default_value = default_value,
705 });
706 }
707
708 // A record is empty if it has no fields or only flexible array fields.
709 if (record_ty.fields.len == functions.items.len and
710 t.comp.target.os.tag == .windows and t.comp.target.abi == .msvc)
711 {
712 // In MSVC empty records have the same size as their alignment.
713 const padding_bits = record_ty.layout.?.size_bits;
714 const alignment_bits = record_ty.layout.?.field_alignment_bits;
715
716 try fields.append(t.gpa, .{
717 .name = "_padding",
718 .type = try ZigTag.type.create(t.arena, try std.fmt.allocPrint(t.arena, "u{d}", .{padding_bits})),
719 .alignment = @divExact(alignment_bits, 8),
720 .default_value = if (t.default_init and container_kind == .@"struct")
721 ZigTag.zero_literal.init()
722 else
723 null,
724 });
725 }
726
727 const container_payload = try t.arena.create(ast.Payload.Container);
728 container_payload.* = .{
729 .base = .{ .tag = container_kind },
730 .data = .{
731 .layout = .@"extern",
732 .fields = try t.arena.dupe(ast.Payload.Container.Field, fields.items),
733 .decls = try t.arena.dupe(ZigNode, functions.items),
734 },
735 };
736 break :init ZigNode.initPayload(&container_payload.base);
737 };
738
739 const payload = try t.arena.create(ast.Payload.SimpleVarDecl);
740 payload.* = .{
741 .base = .{ .tag = if (is_pub) .pub_var_simple else .var_simple },
742 .data = .{
743 .name = name,
744 .init = init_node,
745 },
746 };
747 const node = ZigNode.initPayload(&payload.base);
748 if (toplevel) {
749 try t.addTopLevelDecl(name, node);
750 // Only add the alias if the name is available *and* it was caught by
751 // name detection. Don't bother performing a weak mangle, since a
752 // mangled name is of no real use here.
753 if (!is_unnamed and !t.global_names.contains(bare_name) and t.weak_global_names.contains(bare_name))
754 try t.alias_list.append(t.gpa, .{ .alias = bare_name, .name = name });
755 try t.global_scope.container_member_fns_map.put(t.gpa, record_qt, .{
756 .container_decl_ptr = &payload.data.init,
757 });
758 } else {
759 try scope.appendNode(node);
760 try bs.discardVariable(name);
761 }
762}
763
764fn transFnDecl(t: *Translator, scope: *Scope, function: Node.Function) Error!void {
765 const func_ty = function.qt.get(t.comp, .func).?;
766
767 const fn_name = t.tree.tokSlice(function.name_tok);
768 if (scope.getAlias(fn_name) != null or t.global_scope.containsNow(fn_name))
769 return; // Avoid processing this decl twice
770
771 const fn_decl_loc = function.name_tok;
772 const has_body = function.body != null and func_ty.kind != .variadic and t.func_bodies;
773 if (function.body != null and func_ty.kind == .variadic) {
774 try t.warn(scope, function.name_tok, "TODO unable to translate variadic function, demoted to extern", .{});
775 }
776
777 const is_always_inline = has_body and function.qt.getAttribute(t.comp, .always_inline) != null;
778 const proto_ctx: FnProtoContext = .{
779 .fn_name = fn_name,
780 .is_always_inline = is_always_inline,
781 .is_extern = !has_body,
782 .is_export = !function.static and has_body and !is_always_inline and !function.@"inline",
783 .is_pub = scope.id == .root and (!function.static or t.pub_static),
784 .has_body = has_body,
785 .cc = if (function.qt.getAttribute(t.comp, .calling_convention)) |some| switch (some.cc) {
786 .c => .c,
787 .stdcall => .x86_stdcall,
788 .thiscall => .x86_thiscall,
789 .fastcall => .x86_fastcall,
790 .regcall => .x86_regcall,
791 .riscv_vector => .riscv_vector,
792 .aarch64_sve_pcs => .aarch64_sve_pcs,
793 .aarch64_vector_pcs => .aarch64_vfabi,
794 .arm_aapcs => .arm_aapcs,
795 .arm_aapcs_vfp => .arm_aapcs_vfp,
796 .vectorcall => switch (t.comp.target.cpu.arch) {
797 .x86 => .x86_vectorcall,
798 .aarch64, .aarch64_be => .aarch64_vfabi,
799 else => .c,
800 },
801 .x86_64_sysv => .x86_64_sysv,
802 .x86_64_win => .x86_64_win,
803 } else .c,
804 };
805
806 const proto_node = t.transFnType(&t.global_scope.base, function.qt, func_ty, fn_decl_loc, proto_ctx) catch |err| switch (err) {
807 error.UnsupportedType => {
808 return t.failDecl(scope, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});
809 },
810 error.OutOfMemory => |e| return e,
811 };
812
813 const proto_payload = proto_node.castTag(.func).?;
814 if (!has_body) {
815 if (scope.id != .root) {
816 const bs: *Scope.Block = try scope.findBlockScope(t);
817 const mangled_name = try bs.createMangledName(fn_name, false, Scope.Block.extern_local_prefix);
818 const wrapped = try ZigTag.wrapped_local.create(t.arena, .{ .name = mangled_name, .init = proto_node });
819 try scope.appendNode(wrapped);
820 try bs.discardVariable(mangled_name);
821 return;
822 }
823 try t.global_scope.addMemberFunction(func_ty, proto_payload);
824 return t.addTopLevelDecl(fn_name, proto_node);
825 }
826
827 // actual function definition with body
828 const body_stmt = function.body.?.get(t.tree).compound_stmt;
829 var block_scope = try Scope.Block.init(t, &t.global_scope.base, false);
830 block_scope.return_type = func_ty.return_type;
831 defer block_scope.deinit();
832
833 var param_id: c_uint = 0;
834 for (proto_payload.data.params, func_ty.params) |*param, param_info| {
835 const param_name = param.name orelse {
836 proto_payload.data.is_extern = true;
837 proto_payload.data.is_export = false;
838 proto_payload.data.is_inline = false;
839 try t.warn(&t.global_scope.base, fn_decl_loc, "function {s} parameter has no name, demoted to extern", .{fn_name});
840 return t.addTopLevelDecl(fn_name, proto_node);
841 };
842
843 const is_const = param_info.qt.@"const";
844
845 const mangled_param_name = try block_scope.makeMangledName(param_name);
846 param.name = mangled_param_name;
847
848 if (!is_const) {
849 const bare_arg_name = try std.fmt.allocPrint(t.arena, "arg_{s}", .{mangled_param_name});
850 const arg_name = try block_scope.makeMangledName(bare_arg_name);
851 param.name = arg_name;
852
853 const redecl_node = try ZigTag.arg_redecl.create(t.arena, .{ .actual = mangled_param_name, .mangled = arg_name });
854 try block_scope.statements.append(t.gpa, redecl_node);
855 }
856 try block_scope.discardVariable(mangled_param_name);
857
858 param_id += 1;
859 }
860
861 t.transCompoundStmtInline(body_stmt, &block_scope) catch |err| switch (err) {
862 error.OutOfMemory => |e| return e,
863 error.SelfReferential => unreachable,
864 error.UnsupportedTranslation,
865 error.UnsupportedType,
866 => {
867 proto_payload.data.is_extern = true;
868 proto_payload.data.is_export = false;
869 proto_payload.data.is_inline = false;
870 try t.warn(&t.global_scope.base, fn_decl_loc, "unable to translate function, demoted to extern", .{});
871 return t.addTopLevelDecl(fn_name, proto_node);
872 },
873 };
874
875 try t.global_scope.addMemberFunction(func_ty, proto_payload);
876 proto_payload.data.body = try block_scope.complete();
877 return t.addTopLevelDecl(fn_name, proto_node);
878}
879
880fn transVarDecl(t: *Translator, scope: *Scope, variable: Node.Variable, decl_node: Node.Index) Error!void {
881 const base_name = t.tree.tokSlice(variable.name_tok);
882 const toplevel = scope.id == .root;
883 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined;
884 const name, const use_base_name = blk: {
885 if (toplevel) break :blk .{ base_name, false };
886
887 // Local extern and static variables are wrapped in a struct.
888 const prefix: ?[]const u8 = switch (variable.storage_class) {
889 .@"extern" => Scope.Block.extern_local_prefix,
890 .static => Scope.Block.static_local_prefix,
891 else => null,
892 };
893 break :blk .{ try bs.createMangledName(base_name, false, prefix), prefix != null };
894 };
895
896 if (t.typeWasDemotedToOpaque(variable.qt)) {
897 if (variable.storage_class != .@"extern" and scope.id == .root) {
898 return t.failDecl(scope, variable.name_tok, name, "non-extern variable has opaque type", .{});
899 } else {
900 return t.failDecl(scope, variable.name_tok, name, "local variable has opaque type", .{});
901 }
902 }
903
904 const type_node = (if (variable.initializer) |init|
905 t.transTypeInit(scope, variable.qt, init, variable.name_tok)
906 else
907 t.transType(scope, variable.qt, variable.name_tok)) catch |err| switch (err) {
908 error.UnsupportedType => {
909 return t.failDecl(scope, variable.name_tok, name, "unable to translate variable declaration type", .{});
910 },
911 else => |e| return e,
912 };
913
914 const array_ty = variable.qt.get(t.comp, .array);
915 var is_const = variable.qt.@"const" or (array_ty != null and array_ty.?.elem.@"const");
916 var is_extern = variable.storage_class == .@"extern";
917
918 var self_referential = false;
919 const init_node = init: {
920 if (variable.initializer) |init| {
921 const maybe_literal = init.get(t.tree);
922 if (!toplevel) try t.wip_var_inits.putNoClobber(t.gpa, decl_node, {});
923 defer _ = t.wip_var_inits.remove(decl_node);
924
925 const init_node = (if (maybe_literal == .string_literal_expr)
926 t.transStringLiteralInitializer(init, maybe_literal.string_literal_expr, type_node)
927 else
928 t.transExprCoercing(scope, init, .used)) catch |err| switch (err) {
929 error.SelfReferential => {
930 self_referential = true;
931 break :init ZigTag.undefined_literal.init();
932 },
933 error.UnsupportedTranslation, error.UnsupportedType => {
934 return t.failDecl(scope, variable.name_tok, name, "unable to resolve var init expr", .{});
935 },
936 else => |e| return e,
937 };
938
939 break :init try t.toNonBool(init_node, variable.qt);
940 }
941 if (variable.storage_class == .@"extern") {
942 if (array_ty != null and array_ty.?.len == .incomplete) {
943 // Oh no, an extern array of unknown size! These are really fun because there's no
944 // direct equivalent in Zig. To translate correctly, we'll have to create a C-pointer
945 // to the data initialized via @extern.
946
947 // Since this is really a pointer to the underlying data, we tweak a few properties.
948 is_extern = false;
949 is_const = true;
950
951 const name_str = try std.fmt.allocPrint(t.arena, "\"{s}\"", .{base_name});
952 break :init try ZigTag.builtin_extern.create(t.arena, .{
953 .type = type_node,
954 .name = try ZigTag.string_literal.create(t.arena, name_str),
955 });
956 }
957 break :init null;
958 }
959 if (toplevel or variable.storage_class == .static or variable.thread_local) {
960 // The C language specification states that variables with static or threadlocal
961 // storage without an initializer are initialized to a zero value.
962 break :init try t.createZeroValueNode(variable.qt, type_node, .no_as);
963 }
964 break :init ZigTag.undefined_literal.init();
965 };
966
967 const linksection_string = blk: {
968 if (variable.qt.getAttribute(t.comp, .section)) |section| {
969 break :blk t.comp.interner.get(section.name.ref()).bytes;
970 }
971 break :blk null;
972 };
973
974 // TODO actually set with @export/@extern
975 const linkage = variable.qt.linkage(t.comp);
976 if (linkage != .strong) {
977 try t.warn(scope, variable.name_tok, "TODO {s} linkage ignored", .{@tagName(linkage)});
978 }
979
980 const alignment: ?c_uint = variable.qt.requestedAlignment(t.comp) orelse null;
981 var node = try ZigTag.var_decl.create(t.arena, .{
982 .is_pub = toplevel,
983 .is_const = is_const and !self_referential,
984 .is_extern = is_extern,
985 .is_export = toplevel and variable.storage_class == .auto and linkage == .strong,
986 .is_threadlocal = variable.thread_local,
987 .linksection_string = linksection_string,
988 .alignment = alignment,
989 .name = if (use_base_name) base_name else name,
990 .type = type_node,
991 .init = init_node,
992 });
993
994 if (toplevel) {
995 try t.addTopLevelDecl(name, node);
996 } else {
997 if (use_base_name) {
998 node = try ZigTag.wrapped_local.create(t.arena, .{ .name = name, .init = node });
999 }
1000 try scope.appendNode(node);
1001 if (self_referential) {
1002 const deferred_init = t.transExprCoercing(scope, variable.initializer.?, .used) catch |err| switch (err) {
1003 error.SelfReferential => unreachable,
1004 error.UnsupportedTranslation, error.UnsupportedType => {
1005 return t.failDecl(scope, variable.name_tok, name, "unable to resolve var init expr", .{});
1006 },
1007 else => |e| return e,
1008 };
1009
1010 const assign = try ZigTag.assign.create(t.arena, .{
1011 .lhs = try ZigTag.identifier.create(t.arena, name),
1012 .rhs = try t.toNonBool(deferred_init, variable.qt),
1013 });
1014 try scope.appendNode(assign);
1015 }
1016 try bs.discardVariable(name);
1017
1018 if (variable.qt.getAttribute(t.comp, .cleanup)) |cleanup_attr| {
1019 const cleanup_fn_name = t.tree.tokSlice(cleanup_attr.function.tok);
1020 const mangled_fn_name = scope.getAlias(cleanup_fn_name) orelse cleanup_fn_name;
1021 const fn_id = try ZigTag.identifier.create(t.arena, mangled_fn_name);
1022
1023 const varname = try ZigTag.identifier.create(t.arena, name);
1024 const args = try t.arena.alloc(ZigNode, 1);
1025 args[0] = try ZigTag.address_of.create(t.arena, varname);
1026
1027 const cleanup_call = try ZigTag.call.create(t.arena, .{ .lhs = fn_id, .args = args });
1028 const discard = try ZigTag.discard.create(t.arena, .{ .should_skip = false, .value = cleanup_call });
1029 const deferred_cleanup = try ZigTag.@"defer".create(t.arena, discard);
1030
1031 try bs.statements.append(t.gpa, deferred_cleanup);
1032 }
1033 }
1034}
1035
1036fn transEnumDecl(t: *Translator, scope: *Scope, enum_qt: QualType) Error!void {
1037 const base = enum_qt.base(t.comp);
1038 const enum_ty = base.type.@"enum";
1039
1040 if (t.type_decls.get(enum_ty.decl_node)) |_|
1041 return; // Avoid processing this decl twice
1042
1043 const toplevel = scope.id == .root;
1044 const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(t) else undefined;
1045
1046 var bare_name = enum_ty.name.lookup(t.comp);
1047 var is_unnamed = false;
1048 var name = bare_name;
1049 if (t.unnamed_typedefs.get(base.qt)) |typedef_name| {
1050 bare_name = typedef_name;
1051 name = typedef_name;
1052 } else {
1053 if (enum_ty.isAnonymous(t.comp)) {
1054 bare_name = try std.fmt.allocPrint(t.arena, "unnamed_{d}", .{t.getMangle()});
1055 is_unnamed = true;
1056 }
1057 name = try std.fmt.allocPrint(t.arena, "enum_{s}", .{bare_name});
1058 }
1059 if (!toplevel) name = try bs.makeMangledName(name);
1060 try t.type_decls.putNoClobber(t.gpa, enum_ty.decl_node, name);
1061
1062 const enum_type_node = if (!base.qt.hasIncompleteSize(t.comp)) blk: {
1063 const enum_decl = enum_ty.decl_node.get(t.tree).enum_decl;
1064 for (enum_ty.fields, enum_decl.fields) |field, field_node| {
1065 var enum_val_name = field.name.lookup(t.comp);
1066 if (!toplevel) {
1067 enum_val_name = try bs.makeMangledName(enum_val_name);
1068 }
1069
1070 const enum_const_type_node: ?ZigNode = t.transType(scope, field.qt, field.name_tok) catch |err| switch (err) {
1071 error.UnsupportedType => null,
1072 else => |e| return e,
1073 };
1074
1075 const val = t.tree.value_map.get(field_node).?;
1076 const enum_const_def = try ZigTag.enum_constant.create(t.arena, .{
1077 .name = enum_val_name,
1078 .is_public = toplevel,
1079 .type = enum_const_type_node,
1080 .value = try t.createIntNode(val),
1081 });
1082 if (toplevel)
1083 try t.addTopLevelDecl(enum_val_name, enum_const_def)
1084 else {
1085 try scope.appendNode(enum_const_def);
1086 try bs.discardVariable(enum_val_name);
1087 }
1088 }
1089
1090 break :blk t.transType(scope, enum_ty.tag.?, enum_decl.name_or_kind_tok) catch |err| switch (err) {
1091 error.UnsupportedType => {
1092 return t.failDecl(scope, enum_decl.name_or_kind_tok, name, "unable to translate enum integer type", .{});
1093 },
1094 else => |e| return e,
1095 };
1096 } else blk: {
1097 try t.opaque_demotes.put(t.gpa, base.qt, {});
1098 break :blk ZigTag.opaque_literal.init();
1099 };
1100
1101 const is_pub = toplevel and !is_unnamed;
1102 const payload = try t.arena.create(ast.Payload.SimpleVarDecl);
1103 payload.* = .{
1104 .base = .{ .tag = if (is_pub) .pub_var_simple else .var_simple },
1105 .data = .{
1106 .init = enum_type_node,
1107 .name = name,
1108 },
1109 };
1110 const node = ZigNode.initPayload(&payload.base);
1111 if (toplevel) {
1112 try t.addTopLevelDecl(name, node);
1113 if (!is_unnamed)
1114 try t.alias_list.append(t.gpa, .{ .alias = bare_name, .name = name });
1115 } else {
1116 try scope.appendNode(node);
1117 try bs.discardVariable(name);
1118 }
1119}
1120
1121fn transStaticAssert(t: *Translator, scope: *Scope, static_assert: Node.StaticAssert) Error!void {
1122 const condition = t.transExpr(scope, static_assert.cond, .used) catch |err| switch (err) {
1123 error.SelfReferential => unreachable,
1124 error.UnsupportedTranslation, error.UnsupportedType => {
1125 return try t.warn(&t.global_scope.base, static_assert.cond.tok(t.tree), "unable to translate _Static_assert condition", .{});
1126 },
1127 error.OutOfMemory => |e| return e,
1128 };
1129
1130 // generate @compileError message that matches C compiler output
1131 const diagnostic = if (static_assert.message) |message| str: {
1132 // Aro guarantees this to be a string literal.
1133 const str_val = t.tree.value_map.get(message).?;
1134 const str_qt = message.qt(t.tree);
1135
1136 const bytes = t.comp.interner.get(str_val.ref()).bytes;
1137 var allocating: std.Io.Writer.Allocating = .init(t.gpa);
1138 defer allocating.deinit();
1139
1140 allocating.writer.writeAll("\"static assertion failed \\") catch return error.OutOfMemory;
1141
1142 aro.Value.printString(bytes, str_qt, t.comp, &allocating.writer) catch return error.OutOfMemory;
1143 allocating.writer.end -= 1; // printString adds a terminating " so we need to remove it
1144 allocating.writer.writeAll("\\\"\"") catch return error.OutOfMemory;
1145
1146 break :str try ZigTag.string_literal.create(t.arena, try t.arena.dupe(u8, allocating.written()));
1147 } else try ZigTag.string_literal.create(t.arena, "\"static assertion failed\"");
1148
1149 const assert_node = try ZigTag.static_assert.create(t.arena, .{ .lhs = condition, .rhs = diagnostic });
1150 try scope.appendNode(assert_node);
1151}
1152
1153fn transGlobalAsm(t: *Translator, scope: *Scope, global_asm: Node.GlobalAsm) Error!void {
1154 const asm_string = t.tree.value_map.get(global_asm.asm_str).?;
1155 const bytes = t.comp.interner.get(asm_string.ref()).bytes;
1156
1157 var allocating: std.Io.Writer.Allocating = try .initCapacity(t.gpa, bytes.len);
1158 defer allocating.deinit();
1159 aro.Value.printString(bytes, global_asm.asm_str.qt(t.tree), t.comp, &allocating.writer) catch return error.OutOfMemory;
1160
1161 const str_node = try ZigTag.string_literal.create(t.arena, try t.arena.dupe(u8, allocating.written()));
1162
1163 const asm_node = try ZigTag.asm_simple.create(t.arena, str_node);
1164 const block = try ZigTag.block_single.create(t.arena, asm_node);
1165 const comptime_node = try ZigTag.@"comptime".create(t.arena, block);
1166
1167 try scope.appendNode(comptime_node);
1168}
1169
1170// ================
1171// Type translation
1172// ================
1173
1174fn getTypeStr(t: *Translator, qt: QualType) ![]const u8 {
1175 var allocating: std.Io.Writer.Allocating = .init(t.gpa);
1176 defer allocating.deinit();
1177 qt.print(t.comp, &allocating.writer) catch return error.OutOfMemory;
1178 return t.arena.dupe(u8, allocating.written());
1179}
1180
1181fn transType(t: *Translator, scope: *Scope, qt: QualType, source_loc: TokenIndex) TypeError!ZigNode {
1182 loop: switch (qt.type(t.comp)) {
1183 .atomic => {
1184 const type_name = try t.getTypeStr(qt);
1185 return t.fail(error.UnsupportedType, source_loc, "TODO support atomic type: '{s}'", .{type_name});
1186 },
1187 .void => return ZigTag.type.create(t.arena, "anyopaque"),
1188 .bool => return ZigTag.type.create(t.arena, "bool"),
1189 .int => |int_ty| switch (int_ty) {
1190 //.char => return ZigTag.type.create(t.arena, "c_char"), // TODO: this is the preferred translation
1191 .char => return ZigTag.type.create(t.arena, "u8"),
1192 .schar => return ZigTag.type.create(t.arena, "i8"),
1193 .uchar => return ZigTag.type.create(t.arena, "u8"),
1194 .short => return ZigTag.type.create(t.arena, "c_short"),
1195 .ushort => return ZigTag.type.create(t.arena, "c_ushort"),
1196 .int => return ZigTag.type.create(t.arena, "c_int"),
1197 .uint => return ZigTag.type.create(t.arena, "c_uint"),
1198 .long => return ZigTag.type.create(t.arena, "c_long"),
1199 .ulong => return ZigTag.type.create(t.arena, "c_ulong"),
1200 .long_long => return ZigTag.type.create(t.arena, "c_longlong"),
1201 .ulong_long => return ZigTag.type.create(t.arena, "c_ulonglong"),
1202 .int128 => return ZigTag.type.create(t.arena, "i128"),
1203 .uint128 => return ZigTag.type.create(t.arena, "u128"),
1204 },
1205 .float => |float_ty| switch (float_ty) {
1206 .fp16, .float16 => return ZigTag.type.create(t.arena, "f16"),
1207 .float, .float32 => return ZigTag.type.create(t.arena, "f32"),
1208 .double, .float64, .float32x => return ZigTag.type.create(t.arena, "f64"),
1209 .long_double, .float64x => return ZigTag.type.create(t.arena, "c_longdouble"),
1210 .float128 => return ZigTag.type.create(t.arena, "f128"),
1211 .bf16 => return t.fail(error.UnsupportedType, source_loc, "TODO support bfloat16", .{}),
1212 .dfloat32,
1213 .dfloat64,
1214 .dfloat128,
1215 .dfloat64x,
1216 => return t.fail(error.UnsupportedType, source_loc, "TODO support decimal float type: '{s}'", .{try t.getTypeStr(qt)}),
1217 .float128x => unreachable, // Unsupported on all targets
1218 },
1219 .pointer => |pointer_ty| {
1220 const child_qt = pointer_ty.child;
1221
1222 const is_fn_proto = child_qt.is(t.comp, .func);
1223 const is_const = is_fn_proto or child_qt.@"const";
1224 const is_volatile = child_qt.@"volatile";
1225 const elem_type = try t.transType(scope, child_qt, source_loc);
1226 const ptr_info: @FieldType(ast.Payload.Pointer, "data") = .{
1227 .is_const = is_const,
1228 .is_volatile = is_volatile,
1229 .elem_type = elem_type,
1230 .is_allowzero = false,
1231 };
1232 if (is_fn_proto or
1233 t.typeIsOpaque(child_qt) or
1234 t.typeWasDemotedToOpaque(child_qt))
1235 {
1236 const ptr = try ZigTag.single_pointer.create(t.arena, ptr_info);
1237 return ZigTag.optional_type.create(t.arena, ptr);
1238 }
1239
1240 return ZigTag.c_pointer.create(t.arena, ptr_info);
1241 },
1242 .array => |array_ty| {
1243 const elem_qt = array_ty.elem;
1244 switch (array_ty.len) {
1245 .incomplete, .unspecified_variable => {
1246 const elem_type = try t.transType(scope, elem_qt, source_loc);
1247 return ZigTag.c_pointer.create(t.arena, .{
1248 .is_const = elem_qt.@"const",
1249 .is_volatile = elem_qt.@"volatile",
1250 .is_allowzero = false,
1251 .elem_type = elem_type,
1252 });
1253 },
1254 .fixed, .static => |len| {
1255 const elem_type = try t.transType(scope, elem_qt, source_loc);
1256 return ZigTag.array_type.create(t.arena, .{ .len = len, .elem_type = elem_type });
1257 },
1258 .variable => return t.fail(error.UnsupportedType, source_loc, "VLA unsupported '{s}'", .{try t.getTypeStr(qt)}),
1259 }
1260 },
1261 .func => |func_ty| return t.transFnType(scope, qt, func_ty, source_loc, .{}),
1262 .@"struct", .@"union" => |record_ty| {
1263 var trans_scope = scope;
1264 if (!record_ty.isAnonymous(t.comp)) {
1265 if (t.weak_global_names.contains(record_ty.name.lookup(t.comp))) trans_scope = &t.global_scope.base;
1266 }
1267 try t.transRecordDecl(trans_scope, qt);
1268 const name = t.type_decls.get(record_ty.decl_node).?;
1269 return ZigTag.identifier.create(t.arena, name);
1270 },
1271 .@"enum" => |enum_ty| {
1272 var trans_scope = scope;
1273 const is_anonymous = enum_ty.isAnonymous(t.comp);
1274 if (!is_anonymous) {
1275 if (t.weak_global_names.contains(enum_ty.name.lookup(t.comp))) trans_scope = &t.global_scope.base;
1276 }
1277 try t.transEnumDecl(trans_scope, qt);
1278 const name = t.type_decls.get(enum_ty.decl_node).?;
1279 return ZigTag.identifier.create(t.arena, name);
1280 },
1281 .typedef => |typedef_ty| {
1282 var trans_scope = scope;
1283 const typedef_name = typedef_ty.name.lookup(t.comp);
1284 if (builtin_typedef_map.get(typedef_name)) |builtin| return ZigTag.type.create(t.arena, builtin);
1285 if (t.global_names.contains(typedef_name)) trans_scope = &t.global_scope.base;
1286
1287 try t.transTypeDef(trans_scope, typedef_ty.decl_node);
1288 const name = t.type_decls.get(typedef_ty.decl_node).?;
1289 return ZigTag.identifier.create(t.arena, name);
1290 },
1291 .attributed => |attributed_ty| continue :loop attributed_ty.base.type(t.comp),
1292 .typeof => |typeof_ty| {
1293 if (typeof_ty.expr) |expr| {
1294 if (t.transExpr(scope, expr, .used)) |node| {
1295 return ZigTag.typeof.create(t.arena, node);
1296 } else |err| switch (err) {
1297 error.SelfReferential => {},
1298 error.UnsupportedTranslation => {},
1299 error.UnsupportedType => {},
1300 error.OutOfMemory => |e| return e,
1301 }
1302 }
1303 continue :loop typeof_ty.base.type(t.comp);
1304 },
1305 .vector => |vector_ty| {
1306 const len = try t.createNumberNode(vector_ty.len);
1307 const elem_type = try t.transType(scope, vector_ty.elem, source_loc);
1308 return ZigTag.vector.create(t.arena, .{ .lhs = len, .rhs = elem_type });
1309 },
1310 else => return t.fail(error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{try t.getTypeStr(qt)}),
1311 }
1312}
1313
1314/// Look ahead through the fields of the record to determine what the alignment of the record
1315/// would be without any align/packed/etc. attributes. This helps us determine whether or not
1316/// the fields with 0 offset need an `align` qualifier. Strictly speaking, we could just
1317/// pedantically assign those fields the same alignment as the parent's pointer alignment,
1318/// but this helps the generated code to be a little less verbose.
1319fn headFieldAlignment(t: *Translator, record_decl: aro.Type.Record) ?c_uint {
1320 const bits_per_byte = 8;
1321 const parent_ptr_alignment_bits = record_decl.layout.?.pointer_alignment_bits;
1322 const parent_ptr_alignment = parent_ptr_alignment_bits / bits_per_byte;
1323 var max_field_alignment_bits: u64 = 0;
1324 for (record_decl.fields) |field|
1325 max_field_alignment_bits = @max(max_field_alignment_bits, bits_per_byte * field.qt.alignof(t.comp));
1326 if (max_field_alignment_bits != parent_ptr_alignment_bits) {
1327 return parent_ptr_alignment;
1328 } else {
1329 return null;
1330 }
1331}
1332
1333/// This function inspects the generated layout of a record to determine the alignment for a
1334/// particular field. This approach is necessary because unlike Zig, a C compiler is not
1335/// required to fulfill the requested alignment, which means we'd risk generating different code
1336/// if we only look at the user-requested alignment.
1337///
1338/// Returns a ?c_uint to match Clang's behavior of using c_uint. The return type can be changed
1339/// after the Clang frontend for translate-c is removed. A null value indicates that a field is
1340/// 'naturally aligned'.
1341fn alignmentForField(
1342 t: *Translator,
1343 record_decl: aro.Type.Record,
1344 head_field_alignment: ?c_uint,
1345 field_index: usize,
1346) ?c_uint {
1347 const fields = record_decl.fields;
1348 assert(fields.len != 0);
1349 const field = fields[field_index];
1350
1351 const bits_per_byte = 8;
1352 const parent_ptr_alignment_bits = record_decl.layout.?.pointer_alignment_bits;
1353 const parent_ptr_alignment = parent_ptr_alignment_bits / bits_per_byte;
1354
1355 // bitfields aren't supported yet. Until support is added, records with bitfields
1356 // should be demoted to opaque, and this function shouldn't be called for them.
1357 if (field.bit_width != .null) {
1358 @panic("TODO: add bitfield support for records");
1359 }
1360
1361 const field_offset_bits: u64 = field.layout.offset_bits;
1362 const field_size_bits: u64 = field.layout.size_bits;
1363
1364 // Fields with zero width always have an alignment of 1
1365 if (field_size_bits == 0) {
1366 return 1;
1367 }
1368
1369 // Fields with 0 offset inherit the parent's pointer alignment.
1370 if (field_offset_bits == 0) {
1371 return head_field_alignment;
1372 }
1373
1374 // Records have a natural alignment when used as a field, and their size is
1375 // a multiple of this alignment value. For all other types, the natural alignment
1376 // is their size.
1377 const field_natural_alignment_bits: u64 = bits_per_byte * field.qt.alignof(t.comp);
1378 const rem_bits = field_offset_bits % field_natural_alignment_bits;
1379
1380 // If there's a remainder, then the alignment is smaller than the field's
1381 // natural alignment
1382 if (rem_bits > 0) {
1383 const rem_alignment = rem_bits / bits_per_byte;
1384 if (rem_alignment > 0 and std.math.isPowerOfTwo(rem_alignment)) {
1385 const actual_alignment = @min(rem_alignment, parent_ptr_alignment);
1386 return @as(c_uint, @truncate(actual_alignment));
1387 } else {
1388 return 1;
1389 }
1390 }
1391
1392 // A field may have an offset which positions it to be naturally aligned, but the
1393 // parent's pointer alignment determines if this is actually true, so we take the minimum
1394 // value.
1395 // For example, a float field (4 bytes wide) with a 4 byte offset is positioned to have natural
1396 // alignment, but if the parent pointer alignment is 2, then the actual alignment of the
1397 // float is 2.
1398 const field_natural_alignment: u64 = field_natural_alignment_bits / bits_per_byte;
1399 const offset_alignment = field_offset_bits / bits_per_byte;
1400 const possible_alignment = @min(parent_ptr_alignment, offset_alignment);
1401 if (possible_alignment == field_natural_alignment) {
1402 return null;
1403 } else if (possible_alignment < field_natural_alignment) {
1404 if (std.math.isPowerOfTwo(possible_alignment)) {
1405 return possible_alignment;
1406 } else {
1407 return 1;
1408 }
1409 } else { // possible_alignment > field_natural_alignment
1410 // Here, the field is positioned be at a higher alignment than it's natural alignment. This means we
1411 // need to determine whether it's a specified alignment. We can determine that from the padding preceding
1412 // the field.
1413 const padding_from_prev_field: u64 = blk: {
1414 if (field_offset_bits != 0) {
1415 const previous_field = fields[field_index - 1];
1416 break :blk (field_offset_bits - previous_field.layout.offset_bits) - previous_field.layout.size_bits;
1417 } else {
1418 break :blk 0;
1419 }
1420 };
1421 if (padding_from_prev_field < field_natural_alignment_bits) {
1422 return null;
1423 } else {
1424 return possible_alignment;
1425 }
1426 }
1427}
1428
1429const FnProtoContext = struct {
1430 is_pub: bool = false,
1431 is_export: bool = false,
1432 is_extern: bool = false,
1433 is_always_inline: bool = false,
1434 fn_name: ?[]const u8 = null,
1435 has_body: bool = false,
1436 cc: ast.Payload.Func.CallingConvention = .c,
1437};
1438
1439fn transFnType(
1440 t: *Translator,
1441 scope: *Scope,
1442 func_qt: QualType,
1443 func_ty: aro.Type.Func,
1444 source_loc: TokenIndex,
1445 ctx: FnProtoContext,
1446) !ZigNode {
1447 const param_count: usize = func_ty.params.len;
1448 const fn_params = try t.arena.alloc(ast.Payload.Param, param_count);
1449
1450 for (func_ty.params, fn_params) |param_info, *param_node| {
1451 const param_qt = param_info.qt;
1452 const is_noalias = param_qt.restrict;
1453
1454 const param_name: ?[]const u8 = if (param_info.name == .empty)
1455 null
1456 else
1457 param_info.name.lookup(t.comp);
1458
1459 const type_node = try t.transType(scope, param_qt, param_info.name_tok);
1460 param_node.* = .{
1461 .is_noalias = is_noalias,
1462 .name = param_name,
1463 .type = type_node,
1464 };
1465 }
1466
1467 const linksection_string = blk: {
1468 if (func_qt.getAttribute(t.comp, .section)) |section| {
1469 break :blk t.comp.interner.get(section.name.ref()).bytes;
1470 }
1471 break :blk null;
1472 };
1473
1474 const alignment: ?c_uint = func_qt.requestedAlignment(t.comp) orelse null;
1475
1476 const explicit_callconv = if ((ctx.is_always_inline or ctx.is_export or ctx.is_extern) and ctx.cc == .c) null else ctx.cc;
1477
1478 const return_type_node = blk: {
1479 if (func_qt.getAttribute(t.comp, .noreturn) != null) {
1480 break :blk ZigTag.noreturn_type.init();
1481 } else {
1482 const return_qt = func_ty.return_type;
1483 if (return_qt.is(t.comp, .void)) {
1484 // convert primitive anyopaque to actual void (only for return type)
1485 break :blk ZigTag.void_type.init();
1486 } else {
1487 break :blk t.transType(scope, return_qt, source_loc) catch |err| switch (err) {
1488 error.UnsupportedType => {
1489 try t.warn(scope, source_loc, "unsupported function proto return type", .{});
1490 return err;
1491 },
1492 error.OutOfMemory => |e| return e,
1493 };
1494 }
1495 }
1496 };
1497
1498 // TODO actually set with @export/@extern
1499 const linkage = func_qt.linkage(t.comp);
1500 if (linkage != .strong) {
1501 try t.warn(scope, source_loc, "TODO {s} linkage ignored", .{@tagName(linkage)});
1502 }
1503
1504 const payload = try t.arena.create(ast.Payload.Func);
1505 payload.* = .{
1506 .base = .{ .tag = .func },
1507 .data = .{
1508 .is_pub = ctx.is_pub,
1509 .is_extern = ctx.is_extern,
1510 .is_export = ctx.is_export and linkage == .strong,
1511 .is_inline = ctx.is_always_inline,
1512 .is_var_args = switch (func_ty.kind) {
1513 .normal => false,
1514 .variadic => true,
1515 .old_style => if (t.comp.target.cpu.arch.isWasm())
1516 false
1517 else
1518 !ctx.is_export and !ctx.is_always_inline and !ctx.has_body,
1519 },
1520 .name = ctx.fn_name,
1521 .linksection_string = linksection_string,
1522 .explicit_callconv = explicit_callconv,
1523 .params = fn_params,
1524 .return_type = return_type_node,
1525 .body = null,
1526 .alignment = alignment,
1527 },
1528 };
1529 return ZigNode.initPayload(&payload.base);
1530}
1531
1532/// Produces a Zig AST node by translating a Type, respecting the width, but modifying the signed-ness.
1533/// Asserts the type is an integer.
1534fn transTypeIntWidthOf(t: *Translator, qt: QualType, is_signed: bool) TypeError!ZigNode {
1535 return ZigTag.type.create(t.arena, loop: switch (qt.base(t.comp).type) {
1536 .int => |int_ty| switch (int_ty) {
1537 .char, .schar, .uchar => if (is_signed) "i8" else "u8",
1538 .short, .ushort => if (is_signed) "c_short" else "c_ushort",
1539 .int, .uint => if (is_signed) "c_int" else "c_uint",
1540 .long, .ulong => if (is_signed) "c_long" else "c_ulong",
1541 .long_long, .ulong_long => if (is_signed) "c_longlong" else "c_ulonglong",
1542 .int128, .uint128 => if (is_signed) "i128" else "u128",
1543 },
1544 .bit_int => |bit_int_ty| try std.fmt.allocPrint(t.arena, "{s}{d}", .{
1545 if (is_signed) "i" else "u",
1546 bit_int_ty.bits,
1547 }),
1548 .@"enum" => |enum_ty| blk: {
1549 const tag_ty = enum_ty.tag orelse
1550 break :blk if (is_signed) "c_int" else "c_uint";
1551
1552 continue :loop tag_ty.base(t.comp).type;
1553 },
1554 else => unreachable, // only call this function when it has already been determined the type is int
1555 });
1556}
1557
1558fn transTypeInit(
1559 t: *Translator,
1560 scope: *Scope,
1561 qt: QualType,
1562 init: Node.Index,
1563 source_loc: TokenIndex,
1564) TypeError!ZigNode {
1565 switch (init.get(t.tree)) {
1566 .string_literal_expr => |literal| {
1567 const elem_ty = try t.transType(scope, qt.childType(t.comp), source_loc);
1568
1569 const string_lit_size = literal.qt.arrayLen(t.comp).?;
1570 const array_size = qt.arrayLen(t.comp).?;
1571
1572 if (array_size == string_lit_size) {
1573 return ZigTag.null_sentinel_array_type.create(t.arena, .{ .len = array_size - 1, .elem_type = elem_ty });
1574 } else {
1575 return ZigTag.array_type.create(t.arena, .{ .len = array_size, .elem_type = elem_ty });
1576 }
1577 },
1578 else => {},
1579 }
1580 return t.transType(scope, qt, source_loc);
1581}
1582
1583// ============
1584// Type helpers
1585// ============
1586
1587fn typeIsOpaque(t: *Translator, qt: QualType) bool {
1588 return switch (qt.base(t.comp).type) {
1589 .void => true,
1590 .@"struct", .@"union" => |record_ty| {
1591 if (record_ty.layout == null) return true;
1592 for (record_ty.fields) |field| {
1593 if (field.bit_width != .null) return true;
1594 }
1595 return false;
1596 },
1597 else => false,
1598 };
1599}
1600
1601fn typeWasDemotedToOpaque(t: *Translator, qt: QualType) bool {
1602 return t.opaque_demotes.contains(qt.base(t.comp).qt);
1603}
1604
1605fn typeHasWrappingOverflow(t: *Translator, qt: QualType) bool {
1606 if (t.signedness(qt) == .unsigned) {
1607 // unsigned integer overflow wraps around.
1608 return true;
1609 } else {
1610 // float, signed integer, and pointer overflow is undefined behavior.
1611 return false;
1612 }
1613}
1614
1615/// Signedness of type when translated to Zig.
1616/// Different from `QualType.signedness()` for `char` and enums.
1617/// Returns null for non-int types.
1618fn signedness(t: *Translator, qt: QualType) ?std.builtin.Signedness {
1619 return loop: switch (qt.base(t.comp).type) {
1620 .bool => .unsigned,
1621 .bit_int => |bit_int| bit_int.signedness,
1622 .int => |int_ty| switch (int_ty) {
1623 .char => .unsigned, // Always translated as u8
1624 .schar, .short, .int, .long, .long_long, .int128 => .signed,
1625 .uchar, .ushort, .uint, .ulong, .ulong_long, .uint128 => .unsigned,
1626 },
1627 .@"enum" => |enum_ty| {
1628 const tag_qt = enum_ty.tag orelse return .signed;
1629 continue :loop tag_qt.base(t.comp).type;
1630 },
1631 else => return null,
1632 };
1633}
1634
1635// =====================
1636// Statement translation
1637// =====================
1638
1639fn transStmt(t: *Translator, scope: *Scope, stmt: Node.Index) TransError!ZigNode {
1640 switch (stmt.get(t.tree)) {
1641 .compound_stmt => |compound| {
1642 return t.transCompoundStmt(scope, compound);
1643 },
1644 .static_assert => |static_assert| {
1645 try t.transStaticAssert(scope, static_assert);
1646 return ZigTag.declaration.init();
1647 },
1648 .return_stmt => |return_stmt| return t.transReturnStmt(scope, return_stmt),
1649 .null_stmt => return ZigTag.empty_block.init(),
1650 .if_stmt => |if_stmt| return t.transIfStmt(scope, if_stmt),
1651 .while_stmt => |while_stmt| return t.transWhileStmt(scope, while_stmt),
1652 .do_while_stmt => |do_while_stmt| return t.transDoWhileStmt(scope, do_while_stmt),
1653 .for_stmt => |for_stmt| return t.transForStmt(scope, for_stmt),
1654 .continue_stmt => return ZigTag.@"continue".init(),
1655 .break_stmt => return ZigTag.@"break".init(),
1656 .typedef => |typedef_decl| {
1657 assert(!typedef_decl.implicit);
1658 try t.transTypeDef(scope, stmt);
1659 return ZigTag.declaration.init();
1660 },
1661 .struct_decl, .union_decl => |record_decl| {
1662 try t.transRecordDecl(scope, record_decl.container_qt);
1663 return ZigTag.declaration.init();
1664 },
1665 .struct_forward_decl, .union_forward_decl => |record_decl| {
1666 if (record_decl.definition) |some| {
1667 return t.transStmt(scope, some);
1668 }
1669 try t.transRecordDecl(scope, record_decl.container_qt);
1670 return ZigTag.declaration.init();
1671 },
1672 .enum_decl => |enum_decl| {
1673 try t.transEnumDecl(scope, enum_decl.container_qt);
1674 return ZigTag.declaration.init();
1675 },
1676 .enum_forward_decl => |enum_decl| {
1677 if (enum_decl.definition) |some| {
1678 return t.transStmt(scope, some);
1679 }
1680 try t.transEnumDecl(scope, enum_decl.container_qt);
1681 return ZigTag.declaration.init();
1682 },
1683 .function => |function| {
1684 try t.transFnDecl(scope, function);
1685 return ZigTag.declaration.init();
1686 },
1687 .variable => |variable| {
1688 try t.transVarDecl(scope, variable, stmt);
1689 return ZigTag.declaration.init();
1690 },
1691 .switch_stmt => |switch_stmt| return t.transSwitch(scope, switch_stmt),
1692 .case_stmt, .default_stmt => {
1693 return t.fail(error.UnsupportedTranslation, stmt.tok(t.tree), "TODO complex switch", .{});
1694 },
1695 .goto_stmt, .computed_goto_stmt, .labeled_stmt => {
1696 return t.fail(error.UnsupportedTranslation, stmt.tok(t.tree), "TODO goto", .{});
1697 },
1698 .asm_stmt => {
1699 return t.fail(error.UnsupportedTranslation, stmt.tok(t.tree), "TODO asm stmt", .{});
1700 },
1701 else => return t.transExprCoercing(scope, stmt, .unused),
1702 }
1703}
1704
1705fn transCompoundStmtInline(t: *Translator, compound: Node.CompoundStmt, block: *Scope.Block) TransError!void {
1706 for (compound.body) |stmt| {
1707 const result = try t.transStmt(&block.base, stmt);
1708 switch (result.tag()) {
1709 .declaration, .empty_block => {},
1710 else => {
1711 try block.statements.append(t.gpa, result);
1712 if (result.isNoreturn()) return;
1713 },
1714 }
1715 }
1716}
1717
1718fn transCompoundStmt(t: *Translator, scope: *Scope, compound: Node.CompoundStmt) TransError!ZigNode {
1719 var block_scope = try Scope.Block.init(t, scope, false);
1720 defer block_scope.deinit();
1721 try t.transCompoundStmtInline(compound, &block_scope);
1722 return try block_scope.complete();
1723}
1724
1725fn transReturnStmt(t: *Translator, scope: *Scope, return_stmt: Node.ReturnStmt) TransError!ZigNode {
1726 switch (return_stmt.operand) {
1727 .none => return ZigTag.return_void.init(),
1728 .expr => |operand| {
1729 const rhs = try t.transExprCoercing(scope, operand, .used);
1730 const return_qt = scope.findBlockReturnType();
1731 return ZigTag.@"return".create(t.arena, try t.toNonBool(rhs, return_qt));
1732 },
1733 .implicit => |zero| {
1734 if (zero) return ZigTag.@"return".create(t.arena, ZigTag.zero_literal.init());
1735
1736 const return_qt = scope.findBlockReturnType();
1737 if (return_qt.is(t.comp, .void)) return ZigTag.empty_block.init();
1738
1739 return ZigTag.@"return".create(t.arena, ZigTag.undefined_literal.init());
1740 },
1741 }
1742}
1743
1744/// If a statement can possibly translate to a Zig assignment (either directly because it's
1745/// an assignment in C or indirectly via result assignment to `_`) AND it's the sole statement
1746/// in the body of an if statement or loop, then we need to put the statement into its own block.
1747/// The `else` case here corresponds to statements that could result in an assignment. If a statement
1748/// class never needs a block, add its enum to the top prong.
1749fn maybeBlockify(t: *Translator, scope: *Scope, stmt: Node.Index) TransError!ZigNode {
1750 switch (stmt.get(t.tree)) {
1751 .break_stmt,
1752 .continue_stmt,
1753 .compound_stmt,
1754 .decl_ref_expr,
1755 .enumeration_ref,
1756 .do_while_stmt,
1757 .for_stmt,
1758 .if_stmt,
1759 .return_stmt,
1760 .null_stmt,
1761 .while_stmt,
1762 => return t.transStmt(scope, stmt),
1763 else => return t.blockify(scope, stmt),
1764 }
1765}
1766
1767/// Translate statement and place it in its own block.
1768fn blockify(t: *Translator, scope: *Scope, stmt: Node.Index) TransError!ZigNode {
1769 var block_scope = try Scope.Block.init(t, scope, false);
1770 defer block_scope.deinit();
1771 const result = try t.transStmt(&block_scope.base, stmt);
1772 try block_scope.statements.append(t.gpa, result);
1773 return block_scope.complete();
1774}
1775
1776fn transIfStmt(t: *Translator, scope: *Scope, if_stmt: Node.IfStmt) TransError!ZigNode {
1777 var cond_scope: Scope.Condition = .{
1778 .base = .{
1779 .parent = scope,
1780 .id = .condition,
1781 },
1782 };
1783 defer cond_scope.deinit();
1784 const cond = try t.transBoolExpr(&cond_scope.base, if_stmt.cond);
1785
1786 // block needed to keep else statement from attaching to inner while
1787 const must_blockify = (if_stmt.else_body != null) and switch (if_stmt.then_body.get(t.tree)) {
1788 .while_stmt, .do_while_stmt, .for_stmt => true,
1789 else => false,
1790 };
1791
1792 const then_node = if (must_blockify)
1793 try t.blockify(scope, if_stmt.then_body)
1794 else
1795 try t.maybeBlockify(scope, if_stmt.then_body);
1796
1797 const else_node = if (if_stmt.else_body) |stmt|
1798 try t.maybeBlockify(scope, stmt)
1799 else
1800 null;
1801 return ZigTag.@"if".create(t.arena, .{ .cond = cond, .then = then_node, .@"else" = else_node });
1802}
1803
1804fn transWhileStmt(t: *Translator, scope: *Scope, while_stmt: Node.WhileStmt) TransError!ZigNode {
1805 var cond_scope: Scope.Condition = .{
1806 .base = .{
1807 .parent = scope,
1808 .id = .condition,
1809 },
1810 };
1811 defer cond_scope.deinit();
1812 const cond = try t.transBoolExpr(&cond_scope.base, while_stmt.cond);
1813
1814 var loop_scope: Scope = .{
1815 .parent = scope,
1816 .id = .loop,
1817 };
1818 const body = try t.maybeBlockify(&loop_scope, while_stmt.body);
1819 return ZigTag.@"while".create(t.arena, .{ .cond = cond, .body = body, .cont_expr = null });
1820}
1821
1822fn transDoWhileStmt(t: *Translator, scope: *Scope, do_stmt: Node.DoWhileStmt) TransError!ZigNode {
1823 var loop_scope: Scope = .{
1824 .parent = scope,
1825 .id = .do_loop,
1826 };
1827
1828 // if (!cond) break;
1829 var cond_scope: Scope.Condition = .{
1830 .base = .{
1831 .parent = scope,
1832 .id = .condition,
1833 },
1834 };
1835 defer cond_scope.deinit();
1836 const cond = try t.transBoolExpr(&cond_scope.base, do_stmt.cond);
1837 const if_not_break = switch (cond.tag()) {
1838 .true_literal => {
1839 const body_node = try t.maybeBlockify(scope, do_stmt.body);
1840 return ZigTag.while_true.create(t.arena, body_node);
1841 },
1842 else => try ZigTag.if_not_break.create(t.arena, cond),
1843 };
1844
1845 var body_node = try t.transStmt(&loop_scope, do_stmt.body);
1846 if (body_node.isNoreturn()) {
1847 // The body node ends in a noreturn statement. Simply put it in a while (true)
1848 // in case it contains breaks or continues.
1849 } else if (do_stmt.body.get(t.tree) == .compound_stmt) {
1850 // there's already a block in C, so we'll append our condition to it.
1851 // c: do {
1852 // c: a;
1853 // c: b;
1854 // c: } while(c);
1855 // zig: while (true) {
1856 // zig: a;
1857 // zig: b;
1858 // zig: if (!cond) break;
1859 // zig: }
1860 const block = body_node.castTag(.block).?;
1861 block.data.stmts.len += 1; // This is safe since we reserve one extra space in Scope.Block.complete.
1862 block.data.stmts[block.data.stmts.len - 1] = if_not_break;
1863 } else {
1864 // the C statement is without a block, so we need to create a block to contain it.
1865 // c: do
1866 // c: a;
1867 // c: while(c);
1868 // zig: while (true) {
1869 // zig: a;
1870 // zig: if (!cond) break;
1871 // zig: }
1872 const statements = try t.arena.alloc(ZigNode, 2);
1873 statements[0] = body_node;
1874 statements[1] = if_not_break;
1875 body_node = try ZigTag.block.create(t.arena, .{ .label = null, .stmts = statements });
1876 }
1877 return ZigTag.while_true.create(t.arena, body_node);
1878}
1879
1880fn transForStmt(t: *Translator, scope: *Scope, for_stmt: Node.ForStmt) TransError!ZigNode {
1881 var loop_scope: Scope = .{
1882 .parent = scope,
1883 .id = .loop,
1884 };
1885
1886 var block_scope: ?Scope.Block = null;
1887 defer if (block_scope) |*bs| bs.deinit();
1888
1889 switch (for_stmt.init) {
1890 .decls => |decls| {
1891 block_scope = try Scope.Block.init(t, scope, false);
1892 loop_scope.parent = &block_scope.?.base;
1893 for (decls) |decl| {
1894 try t.transDecl(&block_scope.?.base, decl);
1895 }
1896 },
1897 .expr => |maybe_init| if (maybe_init) |init| {
1898 block_scope = try Scope.Block.init(t, scope, false);
1899 loop_scope.parent = &block_scope.?.base;
1900 const init_node = try t.transStmt(&block_scope.?.base, init);
1901 try loop_scope.appendNode(init_node);
1902 },
1903 }
1904 var cond_scope: Scope.Condition = .{
1905 .base = .{
1906 .parent = &loop_scope,
1907 .id = .condition,
1908 },
1909 };
1910 defer cond_scope.deinit();
1911
1912 const cond = if (for_stmt.cond) |cond|
1913 try t.transBoolExpr(&cond_scope.base, cond)
1914 else
1915 ZigTag.true_literal.init();
1916
1917 const cont_expr = if (for_stmt.incr) |incr|
1918 try t.transExpr(&cond_scope.base, incr, .unused)
1919 else
1920 null;
1921
1922 const body = try t.maybeBlockify(&loop_scope, for_stmt.body);
1923 const while_node = try ZigTag.@"while".create(t.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr });
1924 if (block_scope) |*bs| {
1925 try bs.statements.append(t.gpa, while_node);
1926 return try bs.complete();
1927 } else {
1928 return while_node;
1929 }
1930}
1931
1932fn transSwitch(t: *Translator, scope: *Scope, switch_stmt: Node.SwitchStmt) TransError!ZigNode {
1933 var loop_scope: Scope = .{
1934 .parent = scope,
1935 .id = .loop,
1936 };
1937
1938 var block_scope = try Scope.Block.init(t, &loop_scope, false);
1939 defer block_scope.deinit();
1940
1941 const base_scope = &block_scope.base;
1942
1943 var cond_scope: Scope.Condition = .{
1944 .base = .{
1945 .parent = base_scope,
1946 .id = .condition,
1947 },
1948 };
1949 defer cond_scope.deinit();
1950 const switch_expr = try t.transExpr(&cond_scope.base, switch_stmt.cond, .used);
1951
1952 var cases: std.ArrayList(ZigNode) = .empty;
1953 defer cases.deinit(t.gpa);
1954 var has_default = false;
1955
1956 const body_node = switch_stmt.body.get(t.tree);
1957 if (body_node != .compound_stmt) {
1958 return t.fail(error.UnsupportedTranslation, switch_stmt.switch_tok, "TODO complex switch", .{});
1959 }
1960 const body = body_node.compound_stmt.body;
1961 // Iterate over switch body and collect all cases.
1962 // Fallthrough is handled by duplicating statements.
1963 for (body, 0..) |stmt, i| {
1964 switch (stmt.get(t.tree)) {
1965 .case_stmt => {
1966 var items: std.ArrayList(ZigNode) = .empty;
1967 defer items.deinit(t.gpa);
1968 const sub = try t.transCaseStmt(base_scope, stmt, &items);
1969 const res = try t.transSwitchProngStmt(base_scope, sub, body[i..]);
1970
1971 if (items.items.len == 0) {
1972 has_default = true;
1973 const switch_else = try ZigTag.switch_else.create(t.arena, res);
1974 try cases.append(t.gpa, switch_else);
1975 } else {
1976 const switch_prong = try ZigTag.switch_prong.create(t.arena, .{
1977 .cases = try t.arena.dupe(ZigNode, items.items),
1978 .cond = res,
1979 });
1980 try cases.append(t.gpa, switch_prong);
1981 }
1982 },
1983 .default_stmt => |default_stmt| {
1984 has_default = true;
1985
1986 var sub = default_stmt.body;
1987 while (true) switch (sub.get(t.tree)) {
1988 .case_stmt => |sub_case| sub = sub_case.body,
1989 .default_stmt => |sub_default| sub = sub_default.body,
1990 else => break,
1991 };
1992
1993 const res = try t.transSwitchProngStmt(base_scope, sub, body[i..]);
1994
1995 const switch_else = try ZigTag.switch_else.create(t.arena, res);
1996 try cases.append(t.gpa, switch_else);
1997 },
1998 else => {}, // collected in transSwitchProngStmt
1999 }
2000 }
2001
2002 if (!has_default) {
2003 const else_prong = try ZigTag.switch_else.create(t.arena, ZigTag.empty_block.init());
2004 try cases.append(t.gpa, else_prong);
2005 }
2006
2007 const switch_node = try ZigTag.@"switch".create(t.arena, .{
2008 .cond = switch_expr,
2009 .cases = try t.arena.dupe(ZigNode, cases.items),
2010 });
2011 try block_scope.statements.append(t.gpa, switch_node);
2012 try block_scope.statements.append(t.gpa, ZigTag.@"break".init());
2013 const while_body = try block_scope.complete();
2014
2015 return ZigTag.while_true.create(t.arena, while_body);
2016}
2017
2018/// Collects all items for this case, returns the first statement after the labels.
2019/// If items ends up empty, the prong should be translated as an else.
2020fn transCaseStmt(
2021 t: *Translator,
2022 scope: *Scope,
2023 stmt: Node.Index,
2024 items: *std.ArrayList(ZigNode),
2025) TransError!Node.Index {
2026 var sub = stmt;
2027 var seen_default = false;
2028 while (true) {
2029 switch (sub.get(t.tree)) {
2030 .default_stmt => |default_stmt| {
2031 seen_default = true;
2032 items.items.len = 0;
2033 sub = default_stmt.body;
2034 },
2035 .case_stmt => |case_stmt| {
2036 if (seen_default) {
2037 items.items.len = 0;
2038 sub = case_stmt.body;
2039 continue;
2040 }
2041
2042 const expr = if (case_stmt.end) |end| blk: {
2043 const start_node = try t.transExpr(scope, case_stmt.start, .used);
2044 const end_node = try t.transExpr(scope, end, .used);
2045
2046 break :blk try ZigTag.ellipsis3.create(t.arena, .{ .lhs = start_node, .rhs = end_node });
2047 } else try t.transExpr(scope, case_stmt.start, .used);
2048
2049 try items.append(t.gpa, expr);
2050 sub = case_stmt.body;
2051 },
2052 else => return sub,
2053 }
2054 }
2055}
2056
2057/// Collects all statements seen by this case into a block.
2058/// Avoids creating a block if the first statement is a break or return.
2059fn transSwitchProngStmt(
2060 t: *Translator,
2061 scope: *Scope,
2062 stmt: Node.Index,
2063 body: []const Node.Index,
2064) TransError!ZigNode {
2065 switch (stmt.get(t.tree)) {
2066 .case_stmt, .default_stmt => unreachable,
2067 else => {
2068 var block_scope = try Scope.Block.init(t, scope, false);
2069 defer block_scope.deinit();
2070
2071 // we do not need to translate `stmt` since it is the first stmt of `body`
2072 try t.transSwitchProngStmtInline(&block_scope, body);
2073 return try block_scope.complete();
2074 },
2075 }
2076}
2077
2078/// Collects all statements seen by this case into a block.
2079fn transSwitchProngStmtInline(
2080 t: *Translator,
2081 block: *Scope.Block,
2082 body: []const Node.Index,
2083) TransError!void {
2084 for (body) |stmt| {
2085 switch (stmt.get(t.tree)) {
2086 .case_stmt => |case_stmt| {
2087 var sub = case_stmt.body;
2088 while (true) switch (sub.get(t.tree)) {
2089 .case_stmt => |sub_case| sub = sub_case.body,
2090 .default_stmt => |sub_default| sub = sub_default.body,
2091 else => break,
2092 };
2093 const result = try t.transStmt(&block.base, sub);
2094 assert(result.tag() != .declaration);
2095 try block.statements.append(t.gpa, result);
2096 if (result.isNoreturn()) return;
2097 },
2098 .default_stmt => |default_stmt| {
2099 var sub = default_stmt.body;
2100 while (true) switch (sub.get(t.tree)) {
2101 .case_stmt => |sub_case| sub = sub_case.body,
2102 .default_stmt => |sub_default| sub = sub_default.body,
2103 else => break,
2104 };
2105 const result = try t.transStmt(&block.base, sub);
2106 assert(result.tag() != .declaration);
2107 try block.statements.append(t.gpa, result);
2108 if (result.isNoreturn()) return;
2109 },
2110 else => {
2111 const result = try t.transStmt(&block.base, stmt);
2112 switch (result.tag()) {
2113 .declaration, .empty_block => {},
2114 else => {
2115 try block.statements.append(t.gpa, result);
2116 if (result.isNoreturn()) return;
2117 },
2118 }
2119 },
2120 }
2121 }
2122}
2123
2124// ======================
2125// Expression translation
2126// ======================
2127
2128const ResultUsed = enum { used, unused };
2129
2130fn transExpr(t: *Translator, scope: *Scope, expr: Node.Index, used: ResultUsed) TransError!ZigNode {
2131 const qt = expr.qt(t.tree);
2132 return t.maybeSuppressResult(used, switch (expr.get(t.tree)) {
2133 .paren_expr => |paren_expr| {
2134 return t.transExpr(scope, paren_expr.operand, used);
2135 },
2136 .cast => |cast| return t.transCastExpr(scope, cast, cast.qt, used, .with_as),
2137 .decl_ref_expr => |decl_ref| try t.transDeclRefExpr(scope, decl_ref),
2138 .enumeration_ref => |enum_ref| try t.transDeclRefExpr(scope, enum_ref),
2139 .addr_of_expr => |addr_of_expr| try ZigTag.address_of.create(t.arena, try t.transExpr(scope, addr_of_expr.operand, .used)),
2140 .deref_expr => |deref_expr| res: {
2141 if (t.typeWasDemotedToOpaque(qt))
2142 return t.fail(error.UnsupportedTranslation, deref_expr.op_tok, "cannot dereference opaque type", .{});
2143
2144 // Dereferencing a function pointer is a no-op.
2145 if (qt.is(t.comp, .func)) return t.transExpr(scope, deref_expr.operand, used);
2146
2147 break :res try ZigTag.deref.create(t.arena, try t.transExpr(scope, deref_expr.operand, .used));
2148 },
2149 .bool_not_expr => |bool_not_expr| try ZigTag.not.create(t.arena, try t.transBoolExpr(scope, bool_not_expr.operand)),
2150 .bit_not_expr => |bit_not_expr| try ZigTag.bit_not.create(t.arena, op: {
2151 const operand = try t.transExpr(scope, bit_not_expr.operand, .used);
2152 if (!operand.isBoolRes()) break :op operand;
2153
2154 const casted = try ZigTag.int_from_bool.create(t.arena, operand);
2155 const ty = try t.transType(scope, bit_not_expr.qt, bit_not_expr.op_tok);
2156 break :op try ZigTag.as.create(t.arena, .{ .lhs = ty, .rhs = casted });
2157 }),
2158 .plus_expr => |plus_expr| return t.transExpr(scope, plus_expr.operand, used),
2159 .negate_expr => |negate_expr| res: {
2160 const operand_qt = negate_expr.operand.qt(t.tree);
2161 if (!t.typeHasWrappingOverflow(operand_qt)) {
2162 const sub_expr_node = try t.transExpr(scope, negate_expr.operand, .used);
2163 const to_negate = if (sub_expr_node.isBoolRes()) blk: {
2164 const ty_node = try ZigTag.type.create(t.arena, "c_int");
2165 const int_node = try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
2166 break :blk try ZigTag.as.create(t.arena, .{ .lhs = ty_node, .rhs = int_node });
2167 } else sub_expr_node;
2168
2169 break :res try ZigTag.negate.create(t.arena, to_negate);
2170 } else if (t.signedness(operand_qt) == .unsigned) {
2171 // use -% x for unsigned integers
2172 break :res try ZigTag.negate_wrap.create(t.arena, try t.transExpr(scope, negate_expr.operand, .used));
2173 } else return t.fail(error.UnsupportedTranslation, negate_expr.op_tok, "C negation with non float non integer", .{});
2174 },
2175 .div_expr => |div_expr| res: {
2176 if (qt.isInt(t.comp) and t.signedness(qt) == .signed) {
2177 // signed integer division uses @divTrunc
2178 const lhs = try t.transExpr(scope, div_expr.lhs, .used);
2179 const rhs = try t.transExpr(scope, div_expr.rhs, .used);
2180 break :res try ZigTag.div_trunc.create(t.arena, .{ .lhs = lhs, .rhs = rhs });
2181 }
2182 // unsigned/float division uses the operator
2183 break :res try t.transBinExpr(scope, div_expr, .div);
2184 },
2185 .mod_expr => |mod_expr| res: {
2186 if (qt.isInt(t.comp) and t.signedness(qt) == .signed) {
2187 // signed integer remainder uses __helpers.signedRemainder
2188 const lhs = try t.transExpr(scope, mod_expr.lhs, .used);
2189 const rhs = try t.transExpr(scope, mod_expr.rhs, .used);
2190 break :res try t.createHelperCallNode(.signedRemainder, &.{ lhs, rhs });
2191 }
2192 // unsigned/float division uses the operator
2193 break :res try t.transBinExpr(scope, mod_expr, .mod);
2194 },
2195 .add_expr => |add_expr| res: {
2196 // `ptr + idx` and `idx + ptr` -> ptr + @as(usize, @bitCast(@as(isize, @intCast(idx))))
2197 const lhs_qt = add_expr.lhs.qt(t.tree);
2198 const rhs_qt = add_expr.rhs.qt(t.tree);
2199 if (qt.isPointer(t.comp) and (t.signedness(lhs_qt) == .signed or
2200 t.signedness(rhs_qt) == .signed))
2201 {
2202 break :res try t.transPointerArithmeticSignedOp(scope, add_expr, .add);
2203 }
2204
2205 if (t.signedness(qt) == .unsigned) {
2206 break :res try t.transBinExpr(scope, add_expr, .add_wrap);
2207 } else {
2208 break :res try t.transBinExpr(scope, add_expr, .add);
2209 }
2210 },
2211 .sub_expr => |sub_expr| res: {
2212 // `ptr - idx` -> ptr - @as(usize, @bitCast(@as(isize, @intCast(idx))))
2213 const lhs_qt = sub_expr.lhs.qt(t.tree);
2214 const rhs_qt = sub_expr.rhs.qt(t.tree);
2215 if (qt.isPointer(t.comp) and (t.signedness(lhs_qt) == .signed or
2216 t.signedness(rhs_qt) == .signed))
2217 {
2218 break :res try t.transPointerArithmeticSignedOp(scope, sub_expr, .sub);
2219 }
2220
2221 if (sub_expr.lhs.qt(t.tree).isPointer(t.comp) and sub_expr.rhs.qt(t.tree).isPointer(t.comp)) {
2222 break :res try t.transPtrDiffExpr(scope, sub_expr);
2223 } else if (t.signedness(qt) == .unsigned) {
2224 break :res try t.transBinExpr(scope, sub_expr, .sub_wrap);
2225 } else {
2226 break :res try t.transBinExpr(scope, sub_expr, .sub);
2227 }
2228 },
2229 .mul_expr => |mul_expr| if (t.signedness(qt) == .unsigned)
2230 try t.transBinExpr(scope, mul_expr, .mul_wrap)
2231 else
2232 try t.transBinExpr(scope, mul_expr, .mul),
2233
2234 .less_than_expr => |lt| try t.transBinExpr(scope, lt, .less_than),
2235 .greater_than_expr => |gt| try t.transBinExpr(scope, gt, .greater_than),
2236 .less_than_equal_expr => |lte| try t.transBinExpr(scope, lte, .less_than_equal),
2237 .greater_than_equal_expr => |gte| try t.transBinExpr(scope, gte, .greater_than_equal),
2238 .equal_expr => |equal_expr| try t.transBinExpr(scope, equal_expr, .equal),
2239 .not_equal_expr => |not_equal_expr| try t.transBinExpr(scope, not_equal_expr, .not_equal),
2240
2241 .bool_and_expr => |bool_and_expr| try t.transBoolBinExpr(scope, bool_and_expr, .@"and"),
2242 .bool_or_expr => |bool_or_expr| try t.transBoolBinExpr(scope, bool_or_expr, .@"or"),
2243
2244 .bit_and_expr => |bit_and_expr| try t.transBinExpr(scope, bit_and_expr, .bit_and),
2245 .bit_or_expr => |bit_or_expr| try t.transBinExpr(scope, bit_or_expr, .bit_or),
2246 .bit_xor_expr => |bit_xor_expr| try t.transBinExpr(scope, bit_xor_expr, .bit_xor),
2247
2248 .shl_expr => |shl_expr| try t.transShiftExpr(scope, shl_expr, .shl),
2249 .shr_expr => |shr_expr| try t.transShiftExpr(scope, shr_expr, .shr),
2250
2251 .member_access_expr => |member_access| try t.transMemberAccess(scope, .normal, member_access, null, .accessor),
2252 .member_access_ptr_expr => |member_access| try t.transMemberAccess(scope, .ptr, member_access, null, .accessor),
2253 .array_access_expr => |array_access| try t.transArrayAccess(scope, array_access, null),
2254
2255 .builtin_ref => unreachable,
2256 .builtin_call_expr => |call| return t.transBuiltinCall(scope, call, used),
2257 .call_expr => |call| return t.transCall(scope, call, used),
2258
2259 .builtin_types_compatible_p => |compatible| blk: {
2260 const lhs = try t.transType(scope, compatible.lhs, compatible.builtin_tok);
2261 const rhs = try t.transType(scope, compatible.rhs, compatible.builtin_tok);
2262
2263 break :blk try ZigTag.equal.create(t.arena, .{
2264 .lhs = lhs,
2265 .rhs = rhs,
2266 });
2267 },
2268 .builtin_choose_expr => |choose| return t.transCondExpr(scope, choose, used),
2269 .cond_expr => |cond_expr| return t.transCondExpr(scope, cond_expr, used),
2270 .binary_cond_expr => |conditional| return t.transBinaryCondExpr(scope, conditional, used),
2271 .cond_dummy_expr => unreachable,
2272
2273 .assign_expr => |assign| return t.transAssignExpr(scope, assign, used),
2274 .add_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2275 .sub_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2276 .mul_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2277 .div_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2278 .mod_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2279 .shl_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2280 .shr_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2281 .bit_and_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2282 .bit_xor_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2283 .bit_or_assign_expr => |assign| return t.transCompoundAssign(scope, assign, used),
2284 .compound_assign_dummy_expr => {
2285 assert(used == .used);
2286 return t.compound_assign_dummy.?;
2287 },
2288
2289 .comma_expr => |comma_expr| return t.transCommaExpr(scope, comma_expr, used),
2290 .pre_inc_expr => |un| return t.transIncDecExpr(scope, un, .pre, .inc, used),
2291 .pre_dec_expr => |un| return t.transIncDecExpr(scope, un, .pre, .dec, used),
2292 .post_inc_expr => |un| return t.transIncDecExpr(scope, un, .post, .inc, used),
2293 .post_dec_expr => |un| return t.transIncDecExpr(scope, un, .post, .dec, used),
2294
2295 .int_literal => return t.transIntLiteral(scope, expr, used, .with_as),
2296 .char_literal => return t.transCharLiteral(scope, expr, used, .with_as),
2297 .float_literal => return t.transFloatLiteral(scope, expr, used, .with_as),
2298 .string_literal_expr => |literal| try t.transStringLiteral(scope, expr, literal),
2299 .bool_literal => res: {
2300 const val = t.tree.value_map.get(expr).?;
2301 break :res if (val.toBool(t.comp))
2302 ZigTag.true_literal.init()
2303 else
2304 ZigTag.false_literal.init();
2305 },
2306 .nullptr_literal => ZigTag.null_literal.init(),
2307 .imaginary_literal => |literal| {
2308 return t.fail(error.UnsupportedTranslation, literal.op_tok, "TODO complex numbers", .{});
2309 },
2310 .compound_literal_expr => |literal| return t.transCompoundLiteral(scope, literal, used),
2311
2312 .default_init_expr => |default_init| return t.transDefaultInit(scope, default_init, used, .with_as),
2313 .array_init_expr => |array_init| return t.transArrayInit(scope, array_init, used),
2314 .union_init_expr => |union_init| return t.transUnionInit(scope, union_init, used),
2315 .struct_init_expr => |struct_init| return t.transStructInit(scope, struct_init, used),
2316 .array_filler_expr => unreachable,
2317
2318 .sizeof_expr => |sizeof| try t.transTypeInfo(scope, .sizeof, sizeof),
2319 .alignof_expr => |alignof| try t.transTypeInfo(scope, .alignof, alignof),
2320
2321 .imag_expr, .real_expr => |un| {
2322 return t.fail(error.UnsupportedTranslation, un.op_tok, "TODO complex numbers", .{});
2323 },
2324 .addr_of_label => |addr_of_label| {
2325 return t.fail(error.UnsupportedTranslation, addr_of_label.label_tok, "TODO computed goto", .{});
2326 },
2327
2328 .generic_expr => |generic| return t.transExpr(scope, generic.chosen, used),
2329 .generic_association_expr => |generic| return t.transExpr(scope, generic.expr, used),
2330 .generic_default_expr => |generic| return t.transExpr(scope, generic.expr, used),
2331
2332 .stmt_expr => |stmt_expr| return t.transStmtExpr(scope, stmt_expr, used),
2333
2334 .builtin_convertvector => |convertvector| try t.transConvertvectorExpr(scope, convertvector),
2335 .builtin_shufflevector => |shufflevector| try t.transShufflevectorExpr(scope, shufflevector),
2336
2337 .builtin_va_arg_pack, .builtin_va_arg_pack_len => |va_arg_pack| {
2338 return t.fail(error.UnsupportedTranslation, va_arg_pack.builtin_tok, "TODO va arg pack", .{});
2339 },
2340
2341 .compound_stmt,
2342 .static_assert,
2343 .return_stmt,
2344 .null_stmt,
2345 .if_stmt,
2346 .while_stmt,
2347 .do_while_stmt,
2348 .for_stmt,
2349 .continue_stmt,
2350 .break_stmt,
2351 .labeled_stmt,
2352 .switch_stmt,
2353 .case_stmt,
2354 .default_stmt,
2355 .goto_stmt,
2356 .computed_goto_stmt,
2357 .asm_stmt,
2358 .global_asm,
2359 .typedef,
2360 .struct_decl,
2361 .union_decl,
2362 .enum_decl,
2363 .function,
2364 .param,
2365 .variable,
2366 .enum_field,
2367 .record_field,
2368 .struct_forward_decl,
2369 .union_forward_decl,
2370 .enum_forward_decl,
2371 .empty_decl,
2372 => unreachable, // not an expression
2373 });
2374}
2375
2376/// Same as `transExpr` but with the knowledge that the operand will be type coerced, and therefore
2377/// an `@as` would be redundant. This is used to prevent redundant `@as` in integer literals.
2378fn transExprCoercing(t: *Translator, scope: *Scope, expr: Node.Index, used: ResultUsed) TransError!ZigNode {
2379 switch (expr.get(t.tree)) {
2380 .int_literal => return t.transIntLiteral(scope, expr, used, .no_as),
2381 .char_literal => return t.transCharLiteral(scope, expr, used, .no_as),
2382 .float_literal => return t.transFloatLiteral(scope, expr, used, .no_as),
2383 .cast => |cast| switch (cast.kind) {
2384 .no_op => {
2385 const operand = cast.operand.get(t.tree);
2386 if (operand == .cast) {
2387 return t.transCastExpr(scope, operand.cast, cast.qt, used, .no_as);
2388 }
2389 return t.transExprCoercing(scope, cast.operand, used);
2390 },
2391 .lval_to_rval => return t.transExprCoercing(scope, cast.operand, used),
2392 else => return t.transCastExpr(scope, cast, cast.qt, used, .no_as),
2393 },
2394 .default_init_expr => |default_init| return try t.transDefaultInit(scope, default_init, used, .no_as),
2395 .compound_literal_expr => |literal| {
2396 if (!literal.thread_local and literal.storage_class != .static) {
2397 return t.transExprCoercing(scope, literal.initializer, used);
2398 }
2399 },
2400 else => {},
2401 }
2402
2403 return t.transExpr(scope, expr, used);
2404}
2405
2406fn transBoolExpr(t: *Translator, scope: *Scope, expr: Node.Index) TransError!ZigNode {
2407 switch (expr.get(t.tree)) {
2408 .int_literal => {
2409 const int_val = t.tree.value_map.get(expr).?;
2410 return if (int_val.isZero(t.comp))
2411 ZigTag.false_literal.init()
2412 else
2413 ZigTag.true_literal.init();
2414 },
2415 .cast => |cast| switch (cast.kind) {
2416 .bool_to_int => return t.transExpr(scope, cast.operand, .used),
2417 .array_to_pointer => {
2418 const operand = cast.operand.get(t.tree);
2419 if (operand == .string_literal_expr) {
2420 // @intFromPtr("foo") != 0, always true
2421 const str = try t.transStringLiteral(scope, cast.operand, operand.string_literal_expr);
2422 const int_from_ptr = try ZigTag.int_from_ptr.create(t.arena, str);
2423 return ZigTag.not_equal.create(t.arena, .{ .lhs = int_from_ptr, .rhs = ZigTag.zero_literal.init() });
2424 }
2425 },
2426 else => {},
2427 },
2428 else => {},
2429 }
2430
2431 const maybe_bool_res = try t.transExpr(scope, expr, .used);
2432 if (maybe_bool_res.isBoolRes()) {
2433 return maybe_bool_res;
2434 }
2435
2436 return t.finishBoolExpr(expr.qt(t.tree), maybe_bool_res);
2437}
2438
2439fn toNonBool(t: *Translator, node: ZigNode, qt: QualType) Error!ZigNode {
2440 if (!node.isBoolRes()) return node;
2441 if (qt.is(t.comp, .bool)) return node;
2442 return ZigTag.int_from_bool.create(t.arena, node);
2443}
2444
2445fn finishBoolExpr(t: *Translator, qt: QualType, node: ZigNode) TransError!ZigNode {
2446 const sk = qt.scalarKind(t.comp);
2447 if (sk == .bool) return node;
2448 if (sk == .nullptr_t) {
2449 // node == null, always true
2450 return ZigTag.equal.create(t.arena, .{ .lhs = node, .rhs = ZigTag.null_literal.init() });
2451 }
2452 if (sk.isPointer()) {
2453 // node != null
2454 return ZigTag.not_equal.create(t.arena, .{ .lhs = node, .rhs = ZigTag.null_literal.init() });
2455 }
2456 if (sk != .none) {
2457 // node != 0
2458 return ZigTag.not_equal.create(t.arena, .{ .lhs = node, .rhs = ZigTag.zero_literal.init() });
2459 }
2460 unreachable; // Unexpected bool expression type
2461}
2462
2463fn transCastExpr(
2464 t: *Translator,
2465 scope: *Scope,
2466 cast: Node.Cast,
2467 dest_qt: QualType,
2468 used: ResultUsed,
2469 suppress_as: SuppressCast,
2470) TransError!ZigNode {
2471 const operand = switch (cast.kind) {
2472 .no_op => {
2473 const operand = cast.operand.get(t.tree);
2474 if (operand == .cast) {
2475 return t.transCastExpr(scope, operand.cast, cast.qt, used, suppress_as);
2476 }
2477 return t.transExpr(scope, cast.operand, used);
2478 },
2479 .lval_to_rval, .function_to_pointer => {
2480 return t.transExpr(scope, cast.operand, used);
2481 },
2482 .int_cast => int_cast: {
2483 const src_qt = cast.operand.qt(t.tree);
2484
2485 if (cast.implicit) {
2486 if (t.tree.value_map.get(cast.operand)) |val| {
2487 const max_int = try aro.Value.maxInt(dest_qt, t.comp);
2488 const min_int = try aro.Value.minInt(dest_qt, t.comp);
2489
2490 if (val.compare(.lte, max_int, t.comp) and val.compare(.gte, min_int, t.comp)) {
2491 break :int_cast try t.transExprCoercing(scope, cast.operand, .used);
2492 }
2493 }
2494 }
2495 const operand = try t.transExpr(scope, cast.operand, .used);
2496 break :int_cast try t.transIntCast(operand, src_qt, dest_qt);
2497 },
2498 .to_void => {
2499 assert(used == .unused);
2500 return try t.transExpr(scope, cast.operand, .unused);
2501 },
2502 .null_to_pointer => ZigTag.null_literal.init(),
2503 .array_to_pointer => array_to_pointer: {
2504 const child_qt = dest_qt.childType(t.comp);
2505
2506 loop: switch (cast.operand.get(t.tree)) {
2507 .string_literal_expr => |literal| {
2508 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2509
2510 const ref = if (literal.kind == .utf8 or literal.kind == .ascii)
2511 sub_expr_node
2512 else
2513 try ZigTag.address_of.create(t.arena, sub_expr_node);
2514
2515 const casted = if (child_qt.@"const")
2516 ref
2517 else
2518 try ZigTag.const_cast.create(t.arena, sub_expr_node);
2519
2520 return t.maybeSuppressResult(used, casted);
2521 },
2522 .paren_expr => |paren_expr| {
2523 continue :loop paren_expr.operand.get(t.tree);
2524 },
2525 .generic_expr => |generic| {
2526 continue :loop generic.chosen.get(t.tree);
2527 },
2528 .generic_association_expr => |generic| {
2529 continue :loop generic.expr.get(t.tree);
2530 },
2531 .generic_default_expr => |generic| {
2532 continue :loop generic.expr.get(t.tree);
2533 },
2534 else => {},
2535 }
2536
2537 // Flexible array members are translated as member functions returning
2538 // [*c]T, so no address-of + @ptrCast wrapping is needed.
2539 flexible: {
2540 if (cast.operand.qt(t.tree).arrayLen(t.comp) == null) {
2541 return try t.transExpr(scope, cast.operand, used);
2542 }
2543
2544 const member_index, const base_qt = switch (cast.operand.get(t.tree)) {
2545 .member_access_expr => |ma| .{ ma.member_index, ma.base.qt(t.tree) },
2546 .member_access_ptr_expr => |ma| .{ ma.member_index, ma.base.qt(t.tree).childType(t.comp) },
2547 else => break :flexible,
2548 };
2549 const record = base_qt.getRecord(t.comp) orelse break :flexible;
2550 if (member_index != record.fields.len - 1 and base_qt.base(t.comp).type != .@"union") break :flexible;
2551 const array_ty = record.fields[member_index].qt.get(t.comp, .array) orelse break :flexible;
2552 if (t.isFlexibleArrayLen(array_ty.len)) return try t.transExpr(scope, cast.operand, used);
2553 }
2554
2555 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2556 const ref = try ZigTag.address_of.create(t.arena, sub_expr_node);
2557 const align_cast = try ZigTag.align_cast.create(t.arena, ref);
2558 break :array_to_pointer try ZigTag.ptr_cast.create(t.arena, align_cast);
2559 },
2560 .int_to_pointer => int_to_pointer: {
2561 var sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2562 const operand_qt = cast.operand.qt(t.tree);
2563 if (t.signedness(operand_qt) == .signed or operand_qt.bitSizeof(t.comp) > t.comp.target.ptrBitWidth()) {
2564 sub_expr_node = try ZigTag.as.create(t.arena, .{
2565 .lhs = try ZigTag.type.create(t.arena, "usize"),
2566 .rhs = try ZigTag.int_cast.create(t.arena, sub_expr_node),
2567 });
2568 } else if (sub_expr_node.isBoolRes()) {
2569 sub_expr_node = try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
2570 }
2571 break :int_to_pointer try ZigTag.ptr_from_int.create(t.arena, sub_expr_node);
2572 },
2573 .int_to_bool => {
2574 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2575 if (sub_expr_node.isBoolRes()) return sub_expr_node;
2576 if (cast.operand.qt(t.tree).is(t.comp, .bool)) return sub_expr_node;
2577 const cmp_node = try ZigTag.not_equal.create(t.arena, .{ .lhs = sub_expr_node, .rhs = ZigTag.zero_literal.init() });
2578 return t.maybeSuppressResult(used, cmp_node);
2579 },
2580 .float_to_bool => {
2581 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2582 const cmp_node = try ZigTag.not_equal.create(t.arena, .{ .lhs = sub_expr_node, .rhs = ZigTag.zero_literal.init() });
2583 return t.maybeSuppressResult(used, cmp_node);
2584 },
2585 .pointer_to_bool => {
2586 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2587
2588 // Special case function pointers as @intFromPtr(expr) != 0
2589 if (cast.operand.qt(t.tree).get(t.comp, .pointer)) |ptr_ty| if (ptr_ty.child.is(t.comp, .func)) {
2590 const ptr_node = if (sub_expr_node.tag() == .identifier)
2591 try ZigTag.address_of.create(t.arena, sub_expr_node)
2592 else
2593 sub_expr_node;
2594 const int_from_ptr = try ZigTag.int_from_ptr.create(t.arena, ptr_node);
2595 const cmp_node = try ZigTag.not_equal.create(t.arena, .{ .lhs = int_from_ptr, .rhs = ZigTag.zero_literal.init() });
2596 return t.maybeSuppressResult(used, cmp_node);
2597 };
2598
2599 const cmp_node = try ZigTag.not_equal.create(t.arena, .{ .lhs = sub_expr_node, .rhs = ZigTag.null_literal.init() });
2600 return t.maybeSuppressResult(used, cmp_node);
2601 },
2602 .bool_to_int => bool_to_int: {
2603 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2604 break :bool_to_int try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
2605 },
2606 .bool_to_float => bool_to_float: {
2607 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2608 const int_from_bool = try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
2609 break :bool_to_float try ZigTag.float_from_int.create(t.arena, int_from_bool);
2610 },
2611 .bool_to_pointer => bool_to_pointer: {
2612 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2613 const int_from_bool = try ZigTag.int_from_bool.create(t.arena, sub_expr_node);
2614 break :bool_to_pointer try ZigTag.ptr_from_int.create(t.arena, int_from_bool);
2615 },
2616 .float_cast => float_cast: {
2617 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2618 break :float_cast try ZigTag.float_cast.create(t.arena, sub_expr_node);
2619 },
2620 .int_to_float => int_to_float: {
2621 const sub_expr_node = try t.transExpr(scope, cast.operand, used);
2622 const int_node = if (sub_expr_node.isBoolRes())
2623 try ZigTag.int_from_bool.create(t.arena, sub_expr_node)
2624 else
2625 sub_expr_node;
2626 break :int_to_float try ZigTag.float_from_int.create(t.arena, int_node);
2627 },
2628 .float_to_int => float_to_int: {
2629 const sub_expr_node = try t.transExpr(scope, cast.operand, .used);
2630 break :float_to_int try ZigTag.int_from_float.create(t.arena, sub_expr_node);
2631 },
2632 .pointer_to_int => pointer_to_int: {
2633 const sub_expr_node = try t.transPointerCastExpr(scope, cast.operand);
2634 const ptr_node = try ZigTag.int_from_ptr.create(t.arena, sub_expr_node);
2635 break :pointer_to_int try ZigTag.int_cast.create(t.arena, ptr_node);
2636 },
2637 .bitcast => bitcast: {
2638 const sub_expr_node = try t.transPointerCastExpr(scope, cast.operand);
2639 const operand_qt = cast.operand.qt(t.tree);
2640 if (dest_qt.isPointer(t.comp) and operand_qt.isPointer(t.comp)) {
2641 var casted = try ZigTag.align_cast.create(t.arena, sub_expr_node);
2642 casted = try ZigTag.ptr_cast.create(t.arena, casted);
2643
2644 const src_elem = operand_qt.childType(t.comp);
2645 const dest_elem = dest_qt.childType(t.comp);
2646 if ((src_elem.@"const" or src_elem.is(t.comp, .func)) and !dest_elem.@"const") {
2647 casted = try ZigTag.const_cast.create(t.arena, casted);
2648 }
2649 if (src_elem.@"volatile" and !dest_elem.@"volatile") {
2650 casted = try ZigTag.volatile_cast.create(t.arena, casted);
2651 }
2652 break :bitcast casted;
2653 }
2654
2655 break :bitcast try ZigTag.bit_cast.create(t.arena, sub_expr_node);
2656 },
2657 .union_cast => union_cast: {
2658 const union_type = try t.transType(scope, dest_qt, cast.l_paren);
2659
2660 const operand_qt = cast.operand.qt(t.tree);
2661 const union_base = dest_qt.base(t.comp);
2662 const field = for (union_base.type.@"union".fields) |field| {
2663 if (field.qt.eql(operand_qt, t.comp)) break field;
2664 } else unreachable;
2665 const field_name = if (field.name_tok == 0) t.anonymous_record_field_names.get(.{
2666 .parent = union_base.qt,
2667 .field = field.qt,
2668 }).? else field.name.lookup(t.comp);
2669
2670 const field_init = try t.arena.create(ast.Payload.ContainerInit.Initializer);
2671 field_init.* = .{
2672 .name = field_name,
2673 .value = try t.transExpr(scope, cast.operand, .used),
2674 };
2675 break :union_cast try ZigTag.container_init.create(t.arena, .{
2676 .lhs = union_type,
2677 .inits = field_init[0..1],
2678 });
2679 },
2680 else => return t.fail(error.UnsupportedTranslation, cast.l_paren, "TODO translate {s} cast", .{@tagName(cast.kind)}),
2681 };
2682 if (suppress_as == .no_as) return t.maybeSuppressResult(used, operand);
2683 if (used == .unused) return t.maybeSuppressResult(used, operand);
2684 const as = try ZigTag.as.create(t.arena, .{
2685 .lhs = try t.transType(scope, dest_qt, cast.l_paren),
2686 .rhs = operand,
2687 });
2688 return as;
2689}
2690
2691fn transIntCast(t: *Translator, operand: ZigNode, src_qt: QualType, dest_qt: QualType) !ZigNode {
2692 const src_dest_order = src_qt.intRankOrder(dest_qt, t.comp);
2693 const different_sign = t.signedness(src_qt) != t.signedness(dest_qt);
2694 const needs_bitcast = different_sign and !(t.signedness(src_qt) == .unsigned and src_dest_order == .lt);
2695
2696 var casted = operand;
2697 if (casted.isBoolRes()) {
2698 casted = try ZigTag.int_from_bool.create(t.arena, casted);
2699 } else if (src_dest_order == .gt) {
2700 // No C type is smaller than the 1 bit from @intFromBool
2701 casted = try ZigTag.truncate.create(t.arena, casted);
2702 }
2703 if (needs_bitcast) {
2704 if (src_dest_order != .eq) {
2705 casted = try ZigTag.as.create(t.arena, .{
2706 .lhs = try t.transTypeIntWidthOf(dest_qt, t.signedness(src_qt) == .signed),
2707 .rhs = casted,
2708 });
2709 }
2710 return ZigTag.bit_cast.create(t.arena, casted);
2711 }
2712 return casted;
2713}
2714
2715/// Same as `transExpr` but adds a `&` if the expression is an identifier referencing a function type.
2716fn transPointerCastExpr(t: *Translator, scope: *Scope, expr: Node.Index) TransError!ZigNode {
2717 const sub_expr_node = try t.transExpr(scope, expr, .used);
2718 switch (expr.get(t.tree)) {
2719 .cast => |cast| if (cast.kind == .function_to_pointer and sub_expr_node.tag() == .identifier) {
2720 return ZigTag.address_of.create(t.arena, sub_expr_node);
2721 },
2722 else => {},
2723 }
2724 return sub_expr_node;
2725}
2726
2727fn transDeclRefExpr(t: *Translator, scope: *Scope, decl_ref: Node.DeclRef) TransError!ZigNode {
2728 if (t.wip_var_inits.contains(decl_ref.decl)) return error.SelfReferential;
2729
2730 const name = t.tree.tokSlice(decl_ref.name_tok);
2731 const maybe_alias = scope.getAlias(name);
2732 const mangled_name = maybe_alias orelse name;
2733
2734 switch (decl_ref.decl.get(t.tree)) {
2735 .function => |function| if (function.definition == null and function.body == null) {
2736 // Try translating the decl again in case of out of scope declaration.
2737 try t.transFnDecl(scope, function);
2738 },
2739 else => {},
2740 }
2741
2742 const decl = decl_ref.decl.get(t.tree);
2743 const ref_expr = blk: {
2744 const identifier = try ZigTag.identifier.create(t.arena, mangled_name);
2745 if (decl_ref.qt.is(t.comp, .func) and maybe_alias != null) {
2746 break :blk try ZigTag.field_access.create(t.arena, .{
2747 .lhs = identifier,
2748 .field_name = name,
2749 });
2750 }
2751 if (decl == .variable and maybe_alias != null) {
2752 switch (decl.variable.storage_class) {
2753 .@"extern", .static => {
2754 break :blk try ZigTag.field_access.create(t.arena, .{
2755 .lhs = identifier,
2756 .field_name = name,
2757 });
2758 },
2759 else => {},
2760 }
2761 }
2762 break :blk identifier;
2763 };
2764
2765 scope.skipVariableDiscard(mangled_name);
2766 return ref_expr;
2767}
2768
2769fn transBinExpr(t: *Translator, scope: *Scope, bin: Node.Binary, op_id: ZigTag) TransError!ZigNode {
2770 const lhs_uncasted = try t.transExpr(scope, bin.lhs, .used);
2771 const rhs_uncasted = try t.transExpr(scope, bin.rhs, .used);
2772
2773 const lhs = if (lhs_uncasted.isBoolRes())
2774 try ZigTag.int_from_bool.create(t.arena, lhs_uncasted)
2775 else
2776 lhs_uncasted;
2777
2778 const rhs = if (rhs_uncasted.isBoolRes())
2779 try ZigTag.int_from_bool.create(t.arena, rhs_uncasted)
2780 else
2781 rhs_uncasted;
2782
2783 return t.createBinOpNode(op_id, lhs, rhs);
2784}
2785
2786fn transBoolBinExpr(t: *Translator, scope: *Scope, bin: Node.Binary, op: ZigTag) !ZigNode {
2787 std.debug.assert(op == .@"and" or op == .@"or");
2788
2789 const lhs = try t.transBoolExpr(scope, bin.lhs);
2790 const rhs = try t.transBoolExpr(scope, bin.rhs);
2791
2792 return t.createBinOpNode(op, lhs, rhs);
2793}
2794
2795fn transShiftExpr(t: *Translator, scope: *Scope, bin: Node.Binary, op_id: ZigTag) !ZigNode {
2796 std.debug.assert(op_id == .shl or op_id == .shr);
2797
2798 // lhs >> @intCast(rh)
2799 const lhs = try t.transExpr(scope, bin.lhs, .used);
2800
2801 const rhs = try t.transExpr(scope, bin.rhs, .used);
2802 const rhs_casted = try ZigTag.int_cast.create(t.arena, rhs);
2803
2804 return t.createBinOpNode(op_id, lhs, rhs_casted);
2805}
2806
2807fn transCondExpr(
2808 t: *Translator,
2809 scope: *Scope,
2810 conditional: Node.Conditional,
2811 used: ResultUsed,
2812) TransError!ZigNode {
2813 var cond_scope: Scope.Condition = .{
2814 .base = .{
2815 .parent = scope,
2816 .id = .condition,
2817 },
2818 };
2819 defer cond_scope.deinit();
2820
2821 const res_is_bool = conditional.qt.is(t.comp, .bool);
2822 const cond = try t.transBoolExpr(&cond_scope.base, conditional.cond);
2823
2824 var then_body = try t.transExpr(scope, conditional.then_expr, used);
2825 if (!res_is_bool and then_body.isBoolRes()) {
2826 then_body = try ZigTag.int_from_bool.create(t.arena, then_body);
2827 }
2828
2829 var else_body = try t.transExpr(scope, conditional.else_expr, used);
2830 if (!res_is_bool and else_body.isBoolRes()) {
2831 else_body = try ZigTag.int_from_bool.create(t.arena, else_body);
2832 }
2833
2834 // The `ResultUsed` is forwarded to both branches so no need to suppress the result here.
2835 return ZigTag.@"if".create(t.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body });
2836}
2837
2838fn transBinaryCondExpr(
2839 t: *Translator,
2840 scope: *Scope,
2841 conditional: Node.Conditional,
2842 used: ResultUsed,
2843) TransError!ZigNode {
2844 // GNU extension of the ternary operator where the middle expression is
2845 // omitted, the condition itself is returned if it evaluates to true.
2846
2847 if (used == .unused) {
2848 // Result unused so this can be translated as
2849 // if (condition) else_expr;
2850 var cond_scope: Scope.Condition = .{
2851 .base = .{
2852 .parent = scope,
2853 .id = .condition,
2854 },
2855 };
2856 defer cond_scope.deinit();
2857
2858 return ZigTag.@"if".create(t.arena, .{
2859 .cond = try t.transBoolExpr(&cond_scope.base, conditional.cond),
2860 .then = try t.transExpr(scope, conditional.else_expr, .unused),
2861 .@"else" = null,
2862 });
2863 }
2864
2865 const res_is_bool = conditional.qt.is(t.comp, .bool);
2866 // c: (condition)?:(else_expr)
2867 // zig: (blk: {
2868 // const _cond_temp = (condition);
2869 // break :blk if (_cond_temp) _cond_temp else (else_expr);
2870 // })
2871 var block_scope = try Scope.Block.init(t, scope, true);
2872 defer block_scope.deinit();
2873
2874 const cond_temp = try block_scope.reserveMangledName("cond_temp");
2875 const init_node = try t.transExpr(&block_scope.base, conditional.cond, .used);
2876 const temp_decl = try ZigTag.var_simple.create(t.arena, .{ .name = cond_temp, .init = init_node });
2877 try block_scope.statements.append(t.gpa, temp_decl);
2878
2879 var cond_scope: Scope.Condition = .{
2880 .base = .{
2881 .parent = &block_scope.base,
2882 .id = .condition,
2883 },
2884 };
2885 defer cond_scope.deinit();
2886
2887 const cond_ident = try ZigTag.identifier.create(t.arena, cond_temp);
2888 const cond_node = try t.finishBoolExpr(conditional.cond.qt(t.tree), cond_ident);
2889 var then_body = cond_ident;
2890 if (!res_is_bool and init_node.isBoolRes()) {
2891 then_body = try ZigTag.int_from_bool.create(t.arena, then_body);
2892 }
2893
2894 var else_body = try t.transExpr(&block_scope.base, conditional.else_expr, .used);
2895 if (!res_is_bool and else_body.isBoolRes()) {
2896 else_body = try ZigTag.int_from_bool.create(t.arena, else_body);
2897 }
2898 const if_node = try ZigTag.@"if".create(t.arena, .{
2899 .cond = cond_node,
2900 .then = then_body,
2901 .@"else" = else_body,
2902 });
2903 const break_node = try ZigTag.break_val.create(t.arena, .{
2904 .label = block_scope.label,
2905 .val = if_node,
2906 });
2907 try block_scope.statements.append(t.gpa, break_node);
2908 return block_scope.complete();
2909}
2910
2911fn transCommaExpr(t: *Translator, scope: *Scope, bin: Node.Binary, used: ResultUsed) TransError!ZigNode {
2912 if (used == .unused) {
2913 const lhs = try t.transExprCoercing(scope, bin.lhs, .unused);
2914 try scope.appendNode(lhs);
2915 const rhs = try t.transExprCoercing(scope, bin.rhs, .unused);
2916 return rhs;
2917 }
2918
2919 var block_scope = try Scope.Block.init(t, scope, true);
2920 defer block_scope.deinit();
2921
2922 const lhs = try t.transExprCoercing(&block_scope.base, bin.lhs, .unused);
2923 try block_scope.statements.append(t.gpa, lhs);
2924
2925 const rhs = try t.transExprCoercing(&block_scope.base, bin.rhs, .used);
2926 const break_node = try ZigTag.break_val.create(t.arena, .{
2927 .label = block_scope.label,
2928 .val = try t.toNonBool(rhs, bin.qt),
2929 });
2930 try block_scope.statements.append(t.gpa, break_node);
2931
2932 return try block_scope.complete();
2933}
2934
2935fn transAssignExpr(t: *Translator, scope: *Scope, bin: Node.Binary, used: ResultUsed) !ZigNode {
2936 if (used == .unused) {
2937 const lhs = try t.transExpr(scope, bin.lhs, .used);
2938 const rhs = try t.transExprCoercing(scope, bin.rhs, .used);
2939
2940 const lhs_qt = bin.lhs.qt(t.tree);
2941 return t.createBinOpNode(.assign, lhs, try t.toNonBool(rhs, lhs_qt));
2942 }
2943
2944 var block_scope = try Scope.Block.init(t, scope, true);
2945 defer block_scope.deinit();
2946
2947 const tmp = try block_scope.reserveMangledName("tmp");
2948
2949 const rhs = try t.transExpr(&block_scope.base, bin.rhs, .used);
2950 const lhs_qt = bin.lhs.qt(t.tree);
2951 const tmp_decl = try ZigTag.var_simple.create(t.arena, .{
2952 .name = tmp,
2953 .init = try t.toNonBool(rhs, lhs_qt),
2954 });
2955 try block_scope.statements.append(t.gpa, tmp_decl);
2956
2957 const lhs = try t.transExprCoercing(&block_scope.base, bin.lhs, .used);
2958 const tmp_ident = try ZigTag.identifier.create(t.arena, tmp);
2959
2960 const assign = try t.createBinOpNode(.assign, lhs, tmp_ident);
2961 try block_scope.statements.append(t.gpa, assign);
2962
2963 const break_node = try ZigTag.break_val.create(t.arena, .{
2964 .label = block_scope.label,
2965 .val = tmp_ident,
2966 });
2967 try block_scope.statements.append(t.gpa, break_node);
2968
2969 return try block_scope.complete();
2970}
2971
2972fn transCompoundAssign(
2973 t: *Translator,
2974 scope: *Scope,
2975 assign: Node.Binary,
2976 used: ResultUsed,
2977) !ZigNode {
2978 // If the result is unused we can try using the equivalent Zig operator
2979 // without a block
2980 if (used == .unused) {
2981 if (try t.transCompoundAssignSimple(scope, null, assign)) |some| {
2982 return some;
2983 }
2984 }
2985
2986 // Otherwise we need to wrap the the compound assignment in a block.
2987 var block_scope = try Scope.Block.init(t, scope, used == .used);
2988 defer block_scope.deinit();
2989 const ref = try block_scope.reserveMangledName("ref");
2990
2991 const lhs_expr = try t.transExpr(&block_scope.base, assign.lhs, .used);
2992 const addr_of = try ZigTag.address_of.create(t.arena, lhs_expr);
2993 const ref_decl = try ZigTag.var_simple.create(t.arena, .{ .name = ref, .init = addr_of });
2994 try block_scope.statements.append(t.gpa, ref_decl);
2995
2996 const lhs_node = try ZigTag.identifier.create(t.arena, ref);
2997 const ref_node = try ZigTag.deref.create(t.arena, lhs_node);
2998
2999 // Use the equivalent Zig operator if possible.
3000 if (try t.transCompoundAssignSimple(scope, ref_node, assign)) |some| {
3001 try block_scope.statements.append(t.gpa, some);
3002 } else {
3003 const old_dummy = t.compound_assign_dummy;
3004 defer t.compound_assign_dummy = old_dummy;
3005 t.compound_assign_dummy = ref_node;
3006
3007 // Otherwise do the operation and assignment separately.
3008 const rhs_node = try t.transExprCoercing(&block_scope.base, assign.rhs, .used);
3009 const assign_node = try t.createBinOpNode(.assign, ref_node, rhs_node);
3010 try block_scope.statements.append(t.gpa, assign_node);
3011 }
3012
3013 if (used == .used) {
3014 const break_node = try ZigTag.break_val.create(t.arena, .{
3015 .label = block_scope.label,
3016 .val = ref_node,
3017 });
3018 try block_scope.statements.append(t.gpa, break_node);
3019 }
3020 return block_scope.complete();
3021}
3022
3023/// Translates compound assignment using the equivalent Zig operator if possible.
3024fn transCompoundAssignSimple(t: *Translator, scope: *Scope, lhs_dummy_opt: ?ZigNode, assign: Node.Binary) TransError!?ZigNode {
3025 const assign_rhs = assign.rhs.get(t.tree);
3026 if (assign_rhs == .cast) return null;
3027
3028 const is_signed = t.signedness(assign.qt) == .signed;
3029 switch (assign_rhs) {
3030 .div_expr, .mod_expr => if (is_signed) return null,
3031 else => {},
3032 }
3033 const lhs_ptr = assign.qt.isPointer(t.comp);
3034
3035 const bin, const op: ZigTag, const cast: enum { none, shift, usize } = switch (assign_rhs) {
3036 .add_expr => |bin| .{
3037 bin,
3038 if (t.typeHasWrappingOverflow(bin.qt)) .add_wrap_assign else .add_assign,
3039 if (lhs_ptr and t.signedness(bin.rhs.qt(t.tree)) == .signed) .usize else .none,
3040 },
3041 .sub_expr => |bin| .{
3042 bin,
3043 if (t.typeHasWrappingOverflow(bin.qt)) .sub_wrap_assign else .sub_assign,
3044 if (lhs_ptr and t.signedness(bin.rhs.qt(t.tree)) == .signed) .usize else .none,
3045 },
3046 .mul_expr => |bin| .{
3047 bin,
3048 if (t.typeHasWrappingOverflow(bin.qt)) .mul_wrap_assign else .mul_assign,
3049 .none,
3050 },
3051 .mod_expr => |bin| .{ bin, .mod_assign, .none },
3052 .div_expr => |bin| .{ bin, .div_assign, .none },
3053 .shl_expr => |bin| .{ bin, .shl_assign, .shift },
3054 .shr_expr => |bin| .{ bin, .shr_assign, .shift },
3055 .bit_and_expr => |bin| .{ bin, .bit_and_assign, .none },
3056 .bit_xor_expr => |bin| .{ bin, .bit_xor_assign, .none },
3057 .bit_or_expr => |bin| .{ bin, .bit_or_assign, .none },
3058 else => unreachable,
3059 };
3060
3061 const lhs_node = blk: {
3062 const old_dummy = t.compound_assign_dummy;
3063 defer t.compound_assign_dummy = old_dummy;
3064 t.compound_assign_dummy = lhs_dummy_opt orelse try t.transExpr(scope, assign.lhs, .used);
3065
3066 break :blk try t.transExpr(scope, bin.lhs, .used);
3067 };
3068
3069 const rhs_node = try t.transExprCoercing(scope, bin.rhs, .used);
3070 const casted_rhs = switch (cast) {
3071 .none => rhs_node,
3072 .shift => try ZigTag.int_cast.create(t.arena, rhs_node),
3073 .usize => try t.usizeCastForWrappingPtrArithmetic(rhs_node),
3074 };
3075 return try t.createBinOpNode(op, lhs_node, casted_rhs);
3076}
3077
3078fn transIncDecExpr(
3079 t: *Translator,
3080 scope: *Scope,
3081 un: Node.Unary,
3082 position: enum { pre, post },
3083 kind: enum { inc, dec },
3084 used: ResultUsed,
3085) !ZigNode {
3086 const is_wrapping = t.typeHasWrappingOverflow(un.qt);
3087 const op: ZigTag = switch (kind) {
3088 .inc => if (is_wrapping) .add_wrap_assign else .add_assign,
3089 .dec => if (is_wrapping) .sub_wrap_assign else .sub_assign,
3090 };
3091
3092 const one_literal = ZigTag.one_literal.init();
3093 if (used == .unused) {
3094 const operand = try t.transExpr(scope, un.operand, .used);
3095 return try t.createBinOpNode(op, operand, one_literal);
3096 }
3097
3098 var block_scope = try Scope.Block.init(t, scope, true);
3099 defer block_scope.deinit();
3100
3101 const ref = try block_scope.reserveMangledName("ref");
3102 const operand = try t.transExprCoercing(&block_scope.base, un.operand, .used);
3103 const operand_ref = try ZigTag.address_of.create(t.arena, operand);
3104 const ref_decl = try ZigTag.var_simple.create(t.arena, .{ .name = ref, .init = operand_ref });
3105 try block_scope.statements.append(t.gpa, ref_decl);
3106
3107 const ref_ident = try ZigTag.identifier.create(t.arena, ref);
3108 const ref_deref = try ZigTag.deref.create(t.arena, ref_ident);
3109 const effect = try t.createBinOpNode(op, ref_deref, one_literal);
3110
3111 switch (position) {
3112 .pre => {
3113 try block_scope.statements.append(t.gpa, effect);
3114
3115 const break_node = try ZigTag.break_val.create(t.arena, .{
3116 .label = block_scope.label,
3117 .val = ref_deref,
3118 });
3119 try block_scope.statements.append(t.gpa, break_node);
3120 },
3121 .post => {
3122 const tmp = try block_scope.reserveMangledName("tmp");
3123 const tmp_decl = try ZigTag.var_simple.create(t.arena, .{ .name = tmp, .init = ref_deref });
3124 try block_scope.statements.append(t.gpa, tmp_decl);
3125
3126 try block_scope.statements.append(t.gpa, effect);
3127
3128 const tmp_ident = try ZigTag.identifier.create(t.arena, tmp);
3129 const break_node = try ZigTag.break_val.create(t.arena, .{
3130 .label = block_scope.label,
3131 .val = tmp_ident,
3132 });
3133 try block_scope.statements.append(t.gpa, break_node);
3134 },
3135 }
3136
3137 return try block_scope.complete();
3138}
3139
3140fn transPtrDiffExpr(t: *Translator, scope: *Scope, bin: Node.Binary) TransError!ZigNode {
3141 const lhs_uncasted = try t.transExpr(scope, bin.lhs, .used);
3142 const rhs_uncasted = try t.transExpr(scope, bin.rhs, .used);
3143
3144 const lhs = try ZigTag.int_from_ptr.create(t.arena, lhs_uncasted);
3145 const rhs = try ZigTag.int_from_ptr.create(t.arena, rhs_uncasted);
3146
3147 const sub_res = try t.createBinOpNode(.sub_wrap, lhs, rhs);
3148
3149 // @divExact(@as(<platform-ptrdiff_t>, @bitCast(@intFromPtr(lhs)) -% @intFromPtr(rhs)), @sizeOf(<lhs target type>))
3150 const ptrdiff_type = try t.transTypeIntWidthOf(bin.qt, true);
3151
3152 const bitcast = try ZigTag.as.create(t.arena, .{
3153 .lhs = ptrdiff_type,
3154 .rhs = try ZigTag.bit_cast.create(t.arena, sub_res),
3155 });
3156
3157 // C standard requires that pointer subtraction operands are of the same type,
3158 // otherwise it is undefined behavior. So we can assume the left and right
3159 // sides are the same Type and arbitrarily choose left.
3160 const lhs_ty = try t.transType(scope, bin.lhs.qt(t.tree), bin.lhs.tok(t.tree));
3161 const c_pointer = t.getContainer(lhs_ty).?;
3162
3163 if (c_pointer.castTag(.c_pointer)) |c_pointer_payload| {
3164 const sizeof = try ZigTag.sizeof.create(t.arena, c_pointer_payload.data.elem_type);
3165 return ZigTag.div_exact.create(t.arena, .{
3166 .lhs = bitcast,
3167 .rhs = sizeof,
3168 });
3169 } else {
3170 // This is an opaque/incomplete type. This subtraction exhibits Undefined Behavior by the C99 spec.
3171 // However, allowing subtraction on `void *` and function pointers is a commonly used extension.
3172 // So, just return the value in byte units, mirroring the behavior of this language extension as implemented by GCC and Clang.
3173 return bitcast;
3174 }
3175}
3176
3177/// Translate an arithmetic expression with a pointer operand and a signed-integer operand.
3178/// Zig requires a usize argument for pointer arithmetic, so we intCast to isize and then
3179/// bitcast to usize; pointer wraparound makes the math work.
3180/// Zig pointer addition is not commutative (unlike C); the pointer operand needs to be on the left.
3181/// The + operator in C is not a sequence point so it should be safe to switch the order if necessary.
3182fn transPointerArithmeticSignedOp(t: *Translator, scope: *Scope, bin: Node.Binary, op_id: ZigTag) TransError!ZigNode {
3183 std.debug.assert(op_id == .add or op_id == .sub);
3184
3185 const lhs_qt = bin.lhs.qt(t.tree);
3186 const swap_operands = op_id == .add and t.signedness(lhs_qt) == .signed;
3187
3188 const swizzled_lhs = if (swap_operands) bin.rhs else bin.lhs;
3189 const swizzled_rhs = if (swap_operands) bin.lhs else bin.rhs;
3190
3191 const lhs_node = try t.transExpr(scope, swizzled_lhs, .used);
3192 const rhs_node = try t.transExpr(scope, swizzled_rhs, .used);
3193
3194 const bitcast_node = try t.usizeCastForWrappingPtrArithmetic(rhs_node);
3195
3196 return t.createBinOpNode(op_id, lhs_node, bitcast_node);
3197}
3198
3199fn transMemberAccess(
3200 t: *Translator,
3201 scope: *Scope,
3202 kind: enum { normal, ptr },
3203 member_access: Node.MemberAccess,
3204 opt_base: ?ZigNode,
3205 flex_array_mode: enum { accessor, backing },
3206) TransError!ZigNode {
3207 const base_info = switch (kind) {
3208 .normal => member_access.base.qt(t.tree),
3209 .ptr => member_access.base.qt(t.tree).childType(t.comp),
3210 };
3211 if (t.typeWasDemotedToOpaque(base_info)) {
3212 return t.fail(error.UnsupportedTranslation, member_access.access_tok, "member access of demoted record", .{});
3213 }
3214
3215 const record = base_info.getRecord(t.comp).?;
3216 const field = record.fields[member_access.member_index];
3217 const field_name = if (field.name_tok == 0) t.anonymous_record_field_names.get(.{
3218 .parent = base_info.base(t.comp).qt,
3219 .field = field.qt,
3220 }).? else field.name.lookup(t.comp);
3221 const base_node = opt_base orelse try t.transExpr(scope, member_access.base, .used);
3222 const lhs = switch (kind) {
3223 .normal => base_node,
3224 .ptr => try ZigTag.deref.create(t.arena, base_node),
3225 };
3226 const field_access = try ZigTag.field_access.create(t.arena, .{
3227 .lhs = lhs,
3228 .field_name = field_name,
3229 });
3230
3231 // Flexible array members are translated as member functions.
3232 if (member_access.member_index == record.fields.len - 1 or base_info.base(t.comp).type == .@"union") {
3233 if (field.qt.get(t.comp, .array)) |array_ty| {
3234 if (t.isFlexibleArrayLen(array_ty.len)) {
3235 switch (flex_array_mode) {
3236 .accessor => return ZigTag.call.create(t.arena, .{ .lhs = field_access, .args = &.{} }),
3237 .backing => {
3238 const backing_name = try std.fmt.allocPrint(t.arena, "_{s}", .{field_name});
3239 return ZigTag.field_access.create(t.arena, .{ .lhs = lhs, .field_name = backing_name });
3240 },
3241 }
3242 }
3243 }
3244 }
3245
3246 return field_access;
3247}
3248
3249fn transArrayAccess(t: *Translator, scope: *Scope, array_access: Node.ArrayAccess, opt_base: ?ZigNode) TransError!ZigNode {
3250 // Unwrap the base statement if it's an array decayed to a bare pointer type
3251 // so that we index the array itself
3252 const base = base: {
3253 const base = array_access.base.get(t.tree);
3254 if (base != .cast) break :base array_access.base;
3255 if (base.cast.kind != .array_to_pointer) break :base array_access.base;
3256 break :base base.cast.operand;
3257 };
3258
3259 const base_node = opt_base orelse try t.transExpr(scope, base, .used);
3260 const index = index: {
3261 const index = try t.transExpr(scope, array_access.index, .used);
3262 const index_qt = array_access.index.qt(t.tree);
3263 const maybe_bigger_than_usize = type: switch (index_qt.base(t.comp).type) {
3264 .bool => {
3265 break :index try ZigTag.int_from_bool.create(t.arena, index);
3266 },
3267 .int => |int| switch (int) {
3268 .long_long, .ulong_long, .int128, .uint128 => true,
3269 else => false,
3270 },
3271 .bit_int => |bit_int| bit_int.bits > t.comp.target.ptrBitWidth(),
3272 .@"enum" => |e| if (e.tag) |tag| continue :type tag.base(t.comp).type else false,
3273 else => unreachable,
3274 };
3275
3276 const is_nonnegative_int_literal = if (t.tree.value_map.get(array_access.index)) |val|
3277 val.compare(.gte, .zero, t.comp)
3278 else
3279 false;
3280 const is_signed = t.signedness(index_qt) == .signed;
3281
3282 if (is_signed and !is_nonnegative_int_literal) {
3283 // First cast to `isize` to get proper sign extension and
3284 // then @bitCast to `usize` to satisfy the compiler.
3285 const index_isize = try ZigTag.as.create(t.arena, .{
3286 .lhs = try ZigTag.type.create(t.arena, "isize"),
3287 .rhs = try ZigTag.int_cast.create(t.arena, index),
3288 });
3289 break :index try ZigTag.bit_cast.create(t.arena, index_isize);
3290 }
3291
3292 if (maybe_bigger_than_usize) {
3293 break :index try ZigTag.int_cast.create(t.arena, index);
3294 }
3295 break :index index;
3296 };
3297
3298 return ZigTag.array_access.create(t.arena, .{
3299 .lhs = base_node,
3300 .rhs = index,
3301 });
3302}
3303
3304fn transOffsetof(t: *Translator, scope: *Scope, arg: Node.Index) TransError!ZigNode {
3305 // Translate __builtin_offsetof(T, designator) as
3306 // @intFromPtr(&(@as(*allowzero T, @ptrFromInt(0)).designator))
3307 const member = try t.transMemberDesignator(scope, arg);
3308 const address = try ZigTag.address_of.create(t.arena, member);
3309 return ZigTag.int_from_ptr.create(t.arena, address);
3310}
3311
3312fn transMemberDesignator(t: *Translator, scope: *Scope, arg: Node.Index) TransError!ZigNode {
3313 switch (arg.get(t.tree)) {
3314 .default_init_expr => |default| {
3315 const elem_node = try t.transType(scope, default.qt, default.last_tok);
3316 const ptr_ty = try ZigTag.single_pointer.create(t.arena, .{
3317 .elem_type = elem_node,
3318 .is_allowzero = true,
3319 .is_const = false,
3320 .is_volatile = false,
3321 });
3322 const zero = try ZigTag.ptr_from_int.create(t.arena, ZigTag.zero_literal.init());
3323 return ZigTag.as.create(t.arena, .{ .lhs = ptr_ty, .rhs = zero });
3324 },
3325 .array_access_expr => |access| {
3326 const base = try t.transMemberDesignator(scope, access.base);
3327 return t.transArrayAccess(scope, access, base);
3328 },
3329 .member_access_expr => |access| {
3330 const base = try t.transMemberDesignator(scope, access.base);
3331 // In offsetof context, flexible array members must be accessed via
3332 // the backing field (`_name`) rather than the accessor function,
3333 // because you can't take the address of a function call result.
3334 return t.transMemberAccess(scope, .normal, access, base, .backing);
3335 },
3336 .cast => |cast| {
3337 assert(cast.kind == .array_to_pointer);
3338 return t.transMemberDesignator(scope, cast.operand);
3339 },
3340 else => unreachable,
3341 }
3342}
3343
3344fn transBuiltinCall(
3345 t: *Translator,
3346 scope: *Scope,
3347 call: Node.BuiltinCall,
3348 used: ResultUsed,
3349) TransError!ZigNode {
3350 const builtin_name = t.tree.tokSlice(call.builtin_tok);
3351 if (std.mem.eql(u8, builtin_name, "__builtin_offsetof")) {
3352 const res = try t.transOffsetof(scope, call.args[0]);
3353 return t.maybeSuppressResult(used, res);
3354 }
3355
3356 const builtin = builtins.map.get(builtin_name) orelse
3357 return t.fail(error.UnsupportedTranslation, call.builtin_tok, "TODO implement function '{s}' in std.zig.c_builtins", .{builtin_name});
3358
3359 if (builtin.tag) |tag| switch (tag) {
3360 .byte_swap, .ceil, .cos, .sin, .exp, .exp2, .exp10, .abs, .log, .log2, .log10, .round, .sqrt, .trunc, .floor => {
3361 assert(call.args.len == 1);
3362 const arg = try t.transExprCoercing(scope, call.args[0], .used);
3363 const arg_ty = try t.transType(scope, call.args[0].qt(t.tree), call.args[0].tok(t.tree));
3364 const coerced = try ZigTag.as.create(t.arena, .{ .lhs = arg_ty, .rhs = arg });
3365
3366 const ptr = try t.arena.create(ast.Payload.UnOp);
3367 ptr.* = .{ .base = .{ .tag = tag }, .data = coerced };
3368 return t.maybeSuppressResult(used, ZigNode.initPayload(&ptr.base));
3369 },
3370 .@"unreachable" => return ZigTag.@"unreachable".init(),
3371 else => unreachable,
3372 };
3373
3374 const arg_nodes = try t.arena.alloc(ZigNode, call.args.len);
3375 for (call.args, arg_nodes) |c_arg, *zig_arg| {
3376 zig_arg.* = try t.transExprCoercing(scope, c_arg, .used);
3377 }
3378
3379 const builtin_identifier = try ZigTag.identifier.create(t.arena, "__builtin");
3380 const field_access = try ZigTag.field_access.create(t.arena, .{
3381 .lhs = builtin_identifier,
3382 .field_name = builtin.name,
3383 });
3384
3385 const res = try ZigTag.call.create(t.arena, .{
3386 .lhs = field_access,
3387 .args = arg_nodes,
3388 });
3389 if (call.qt.is(t.comp, .void)) return res;
3390 return t.maybeSuppressResult(used, res);
3391}
3392
3393fn transCall(
3394 t: *Translator,
3395 scope: *Scope,
3396 call: Node.Call,
3397 used: ResultUsed,
3398) TransError!ZigNode {
3399 const raw_fn_expr = try t.transExpr(scope, call.callee, .used);
3400 const fn_expr = blk: {
3401 loop: switch (call.callee.get(t.tree)) {
3402 .paren_expr => |paren_expr| {
3403 continue :loop paren_expr.operand.get(t.tree);
3404 },
3405 .decl_ref_expr => |decl_ref| {
3406 if (decl_ref.qt.is(t.comp, .func)) break :blk raw_fn_expr;
3407 },
3408 .cast => |cast| {
3409 if (cast.kind == .function_to_pointer) {
3410 continue :loop cast.operand.get(t.tree);
3411 }
3412 },
3413 .deref_expr, .addr_of_expr => |un| {
3414 continue :loop un.operand.get(t.tree);
3415 },
3416 .generic_expr => |generic| {
3417 continue :loop generic.chosen.get(t.tree);
3418 },
3419 .generic_association_expr => |generic| {
3420 continue :loop generic.expr.get(t.tree);
3421 },
3422 .generic_default_expr => |generic| {
3423 continue :loop generic.expr.get(t.tree);
3424 },
3425 else => {},
3426 }
3427 break :blk try ZigTag.unwrap.create(t.arena, raw_fn_expr);
3428 };
3429
3430 const callee_qt = call.callee.qt(t.tree);
3431 const maybe_ptr_ty = callee_qt.get(t.comp, .pointer);
3432 const func_qt = if (maybe_ptr_ty) |ptr| ptr.child else callee_qt;
3433 const func_ty = func_qt.get(t.comp, .func).?;
3434
3435 const arg_nodes = try t.arena.alloc(ZigNode, call.args.len);
3436 for (call.args, arg_nodes, 0..) |c_arg, *zig_arg, i| {
3437 if (i < func_ty.params.len) {
3438 zig_arg.* = try t.transExprCoercing(scope, c_arg, .used);
3439
3440 if (zig_arg.isBoolRes() and !func_ty.params[i].qt.is(t.comp, .bool)) {
3441 // In C the result type of a boolean expression is int. If this result is passed as
3442 // an argument to a function whose parameter is also int, there is no cast. Therefore
3443 // in Zig we'll need to cast it from bool to u1 (which will safely coerce to c_int).
3444 zig_arg.* = try ZigTag.int_from_bool.create(t.arena, zig_arg.*);
3445 }
3446 } else {
3447 zig_arg.* = try t.transExpr(scope, c_arg, .used);
3448
3449 if (zig_arg.isBoolRes()) {
3450 // Same as above but now we don't have a result type.
3451 const u1_node = try ZigTag.int_from_bool.create(t.arena, zig_arg.*);
3452 const c_int_node = try ZigTag.type.create(t.arena, "c_int");
3453 zig_arg.* = try ZigTag.as.create(t.arena, .{ .lhs = c_int_node, .rhs = u1_node });
3454 }
3455 }
3456 }
3457
3458 const res = try ZigTag.call.create(t.arena, .{
3459 .lhs = fn_expr,
3460 .args = arg_nodes,
3461 });
3462 if (call.qt.is(t.comp, .void)) return res;
3463 return t.maybeSuppressResult(used, res);
3464}
3465
3466const SuppressCast = enum { with_as, no_as };
3467
3468/// Attempt to translate literal as the name of the simple macro
3469/// it was expanded from.
3470fn checkLiteralMacro(t: *Translator, tok: TokenIndex, used: ResultUsed) !?ZigNode {
3471 if (!t.keep_macro_literals) return null;
3472 const expansion_locs = t.pp.expansionSlice(tok);
3473 if (expansion_locs.len == 0) return null;
3474
3475 const last_expand = expansion_locs[0];
3476 const source = t.comp.getSource(last_expand.id);
3477 var tokenizer: aro.Tokenizer = .{
3478 .buf = source.buf,
3479 .langopts = t.comp.langopts,
3480 .source = last_expand.id,
3481 .index = last_expand.byte_offset,
3482 .splice_locs = &.{},
3483 };
3484 const name_tok = tokenizer.next();
3485 if (!name_tok.id.isMacroIdentifier()) return null;
3486
3487 const name = t.pp.tokSlice(name_tok);
3488 if (t.global_scope.containsNow(name)) return null;
3489 const macro = t.pp.defines.get(name) orelse return null;
3490 if (macro.is_func) return null;
3491 if (macro.isBuiltin()) return null;
3492
3493 var tok_count: u8 = 0;
3494 for (macro.tokens) |macro_tok| {
3495 switch (macro_tok.id) {
3496 .invalid => continue,
3497 .whitespace => continue,
3498 .comment => continue,
3499 .macro_ws => continue,
3500 else => {
3501 if (tok_count != 0) return null;
3502 tok_count += 1;
3503 },
3504 }
3505 }
3506
3507 if (t.checkTranslatableMacro(macro.tokens, macro.params) != null) return null;
3508
3509 const ident = try ZigTag.identifier.create(t.arena, name);
3510 return try t.maybeSuppressResult(used, ident);
3511}
3512
3513fn transIntLiteral(
3514 t: *Translator,
3515 scope: *Scope,
3516 literal_index: Node.Index,
3517 used: ResultUsed,
3518 suppress_as: SuppressCast,
3519) TransError!ZigNode {
3520 if (try t.checkLiteralMacro(literal_index.tok(t.tree), used)) |node| return node;
3521 const val = t.tree.value_map.get(literal_index).?;
3522 const int_lit_node = try t.createIntNode(val);
3523 if (suppress_as == .no_as) {
3524 return t.maybeSuppressResult(used, int_lit_node);
3525 }
3526
3527 // Integer literals in C have types, and this can matter for several reasons.
3528 // For example, this is valid C:
3529 // unsigned char y = 256;
3530 // How this gets evaluated is the 256 is an integer, which gets truncated to signed char, then bit-casted
3531 // to unsigned char, resulting in 0. In order for this to work, we have to emit this zig code:
3532 // var y = @as(u8, @bitCast(@as(i8, @truncate(@as(c_int, 256)))));
3533
3534 // @as(T, x)
3535 const ty_node = try t.transType(scope, literal_index.qt(t.tree), literal_index.tok(t.tree));
3536 const as = try ZigTag.as.create(t.arena, .{ .lhs = ty_node, .rhs = int_lit_node });
3537 return t.maybeSuppressResult(used, as);
3538}
3539
3540fn transCharLiteral(
3541 t: *Translator,
3542 scope: *Scope,
3543 literal_index: Node.Index,
3544 used: ResultUsed,
3545 suppress_as: SuppressCast,
3546) TransError!ZigNode {
3547 if (try t.checkLiteralMacro(literal_index.tok(t.tree), used)) |node| return node;
3548 const val = t.tree.value_map.get(literal_index).?;
3549 const char_literal = literal_index.get(t.tree).char_literal;
3550 const narrow = char_literal.kind == .ascii or char_literal.kind == .utf8;
3551
3552 // C has a somewhat obscure feature called multi-character character constant
3553 // e.g. 'abcd'
3554 const int_value = val.toInt(u32, t.comp).?;
3555 const int_lit_node = if (char_literal.kind == .ascii and int_value > 255)
3556 try t.createNumberNode(int_value)
3557 else
3558 try t.createCharLiteralNode(narrow, int_value);
3559
3560 if (suppress_as == .no_as) {
3561 return t.maybeSuppressResult(used, int_lit_node);
3562 }
3563
3564 // See comment in `transIntLiteral` for why this code is here.
3565 // @as(T, x)
3566 const as_node = try ZigTag.as.create(t.arena, .{
3567 .lhs = try t.transType(scope, char_literal.qt, char_literal.literal_tok),
3568 .rhs = int_lit_node,
3569 });
3570 return t.maybeSuppressResult(used, as_node);
3571}
3572
3573fn transFloatLiteral(
3574 t: *Translator,
3575 scope: *Scope,
3576 literal_index: Node.Index,
3577 used: ResultUsed,
3578 suppress_as: SuppressCast,
3579) TransError!ZigNode {
3580 if (try t.checkLiteralMacro(literal_index.tok(t.tree), used)) |node| return node;
3581 const val = t.tree.value_map.get(literal_index).?;
3582 const float_literal = literal_index.get(t.tree).float_literal;
3583
3584 var allocating: std.Io.Writer.Allocating = .init(t.gpa);
3585 defer allocating.deinit();
3586 _ = val.print(float_literal.qt, t.comp, &allocating.writer) catch return error.OutOfMemory;
3587 if (mem.findScalar(u8, allocating.written(), '.') == null) {
3588 allocating.writer.writeAll(".0") catch return error.OutOfMemory;
3589 }
3590
3591 const float_lit_node = try ZigTag.float_literal.create(t.arena, try t.arena.dupe(u8, allocating.written()));
3592 if (suppress_as == .no_as) {
3593 return t.maybeSuppressResult(used, float_lit_node);
3594 }
3595
3596 const as_node = try ZigTag.as.create(t.arena, .{
3597 .lhs = try t.transType(scope, float_literal.qt, float_literal.literal_tok),
3598 .rhs = float_lit_node,
3599 });
3600 return t.maybeSuppressResult(used, as_node);
3601}
3602
3603fn transStringLiteral(
3604 t: *Translator,
3605 scope: *Scope,
3606 expr: Node.Index,
3607 literal: Node.CharLiteral,
3608) TransError!ZigNode {
3609 switch (literal.kind) {
3610 .ascii, .utf8 => return t.transNarrowStringLiteral(expr, literal),
3611 .utf16, .utf32, .wide => {
3612 const name = try std.fmt.allocPrint(t.arena, "{s}_string_{d}", .{ @tagName(literal.kind), t.getMangle() });
3613
3614 const array_type = try t.transTypeInit(scope, literal.qt, expr, literal.literal_tok);
3615 const lit_array = try t.transStringLiteralInitializer(expr, literal, array_type);
3616 const decl = try ZigTag.var_simple.create(t.arena, .{ .name = name, .init = lit_array });
3617 try scope.appendNode(decl);
3618 return ZigTag.identifier.create(t.arena, name);
3619 },
3620 }
3621}
3622
3623fn transNarrowStringLiteral(
3624 t: *Translator,
3625 expr: Node.Index,
3626 literal: Node.CharLiteral,
3627) TransError!ZigNode {
3628 const val = t.tree.value_map.get(expr).?;
3629
3630 const bytes = t.comp.interner.get(val.ref()).bytes;
3631 var allocating: std.Io.Writer.Allocating = try .initCapacity(t.gpa, bytes.len);
3632 defer allocating.deinit();
3633
3634 aro.Value.printString(bytes, literal.qt, t.comp, &allocating.writer) catch return error.OutOfMemory;
3635
3636 return ZigTag.string_literal.create(t.arena, try t.arena.dupe(u8, allocating.written()));
3637}
3638
3639/// Translate a string literal that is initializing an array. In general narrow string
3640/// literals become `"<string>".*` or `"<string>"[0..<size>].*` if they need truncation.
3641/// Wide string literals become an array of integers. zero-fillers pad out the array to
3642/// the appropriate length, if necessary.
3643fn transStringLiteralInitializer(
3644 t: *Translator,
3645 expr: Node.Index,
3646 literal: Node.CharLiteral,
3647 array_type: ZigNode,
3648) TransError!ZigNode {
3649 assert(array_type.tag() == .array_type or array_type.tag() == .null_sentinel_array_type);
3650
3651 const is_narrow = literal.kind == .ascii or literal.kind == .utf8;
3652
3653 // The length of the string literal excluding the sentinel.
3654 const str_length = literal.qt.arrayLen(t.comp).? - 1;
3655
3656 const payload = (array_type.castTag(.array_type) orelse array_type.castTag(.null_sentinel_array_type).?).data;
3657 const array_size = payload.len;
3658 const elem_type = payload.elem_type;
3659
3660 if (array_size == 0) return ZigTag.empty_array.create(t.arena, array_type);
3661
3662 const num_inits = @min(str_length, array_size);
3663 if (num_inits == 0) {
3664 return ZigTag.array_filler.create(t.arena, .{
3665 .type = elem_type,
3666 .filler = ZigTag.zero_literal.init(),
3667 .count = array_size,
3668 });
3669 }
3670
3671 const init_node = if (is_narrow) blk: {
3672 // "string literal".* or string literal"[0..num_inits].*
3673 var str = try t.transNarrowStringLiteral(expr, literal);
3674 if (str_length != array_size) str = try ZigTag.string_slice.create(t.arena, .{ .string = str, .end = num_inits });
3675 break :blk try ZigTag.deref.create(t.arena, str);
3676 } else blk: {
3677 const size = literal.qt.childType(t.comp).sizeof(t.comp);
3678
3679 const val = t.tree.value_map.get(expr).?;
3680 const bytes = t.comp.interner.get(val.ref()).bytes;
3681
3682 const init_list = try t.arena.alloc(ZigNode, @intCast(num_inits));
3683 for (init_list, 0..) |*item, i| {
3684 const codepoint = switch (size) {
3685 2 => @as(*const u16, @ptrCast(@alignCast(bytes.ptr + i * 2))).*,
3686 4 => @as(*const u32, @ptrCast(@alignCast(bytes.ptr + i * 4))).*,
3687 else => unreachable,
3688 };
3689 item.* = try t.createCharLiteralNode(false, codepoint);
3690 }
3691 const init_args: ast.Payload.Array.ArrayTypeInfo = .{ .len = num_inits, .elem_type = elem_type };
3692 const init_array_type = if (array_type.tag() == .array_type)
3693 try ZigTag.array_type.create(t.arena, init_args)
3694 else
3695 try ZigTag.null_sentinel_array_type.create(t.arena, init_args);
3696 break :blk try ZigTag.array_init.create(t.arena, .{
3697 .cond = init_array_type,
3698 .cases = init_list,
3699 });
3700 };
3701
3702 if (num_inits == array_size) return init_node;
3703 assert(array_size > str_length); // If array_size <= str_length, `num_inits == array_size` and we've already returned.
3704
3705 const filler_node = try ZigTag.array_filler.create(t.arena, .{
3706 .type = elem_type,
3707 .filler = ZigTag.zero_literal.init(),
3708 .count = array_size - str_length,
3709 });
3710 return ZigTag.array_cat.create(t.arena, .{ .lhs = init_node, .rhs = filler_node });
3711}
3712
3713fn transCompoundLiteral(
3714 t: *Translator,
3715 scope: *Scope,
3716 literal: Node.CompoundLiteral,
3717 used: ResultUsed,
3718) TransError!ZigNode {
3719 if (used == .unused) {
3720 return t.transExpr(scope, literal.initializer, .unused);
3721 }
3722
3723 // TODO taking a reference to a compound literal should result in a mutable
3724 // pointer (unless the literal is const).
3725
3726 const initializer = try t.transExprCoercing(scope, literal.initializer, .used);
3727 const ty = try t.transType(scope, literal.qt, literal.l_paren_tok);
3728 if (!literal.thread_local and literal.storage_class != .static) {
3729 // In the simple case a compound literal can be translated
3730 // simply as `@as(type, initializer)`.
3731 return ZigTag.as.create(t.arena, .{ .lhs = ty, .rhs = initializer });
3732 }
3733
3734 // Otherwise static or thread local compound literals are translated as
3735 // a reference to a variable wrapped in a struct.
3736
3737 var block_scope = try Scope.Block.init(t, scope, true);
3738 defer block_scope.deinit();
3739
3740 const tmp = try block_scope.reserveMangledName("tmp");
3741 const wrapped_name = "compound_literal";
3742
3743 // const tmp = struct { var compound_literal = initializer };
3744 const temp_decl = try ZigTag.var_decl.create(t.arena, .{
3745 .is_pub = false,
3746 .is_const = literal.qt.@"const",
3747 .is_extern = false,
3748 .is_export = false,
3749 .is_threadlocal = literal.thread_local,
3750 .linksection_string = null,
3751 .alignment = null,
3752 .name = wrapped_name,
3753 .type = ty,
3754 .init = initializer,
3755 });
3756 const wrapped = try ZigTag.wrapped_local.create(t.arena, .{ .name = tmp, .init = temp_decl });
3757 try block_scope.statements.append(t.gpa, wrapped);
3758
3759 // break :blk tmp.compound_literal
3760 const static_tmp_ident = try ZigTag.identifier.create(t.arena, tmp);
3761 const field_access = try ZigTag.field_access.create(t.arena, .{
3762 .lhs = static_tmp_ident,
3763 .field_name = wrapped_name,
3764 });
3765 const break_node = try ZigTag.break_val.create(t.arena, .{
3766 .label = block_scope.label,
3767 .val = field_access,
3768 });
3769 try block_scope.statements.append(t.gpa, break_node);
3770
3771 return block_scope.complete();
3772}
3773
3774fn transDefaultInit(
3775 t: *Translator,
3776 scope: *Scope,
3777 default_init: Node.DefaultInit,
3778 used: ResultUsed,
3779 suppress_as: SuppressCast,
3780) TransError!ZigNode {
3781 assert(used == .used);
3782 const type_node = try t.transType(scope, default_init.qt, default_init.last_tok);
3783 return try t.createZeroValueNode(default_init.qt, type_node, suppress_as);
3784}
3785
3786fn transArrayInit(
3787 t: *Translator,
3788 scope: *Scope,
3789 array_init: Node.ContainerInit,
3790 used: ResultUsed,
3791) TransError!ZigNode {
3792 assert(used == .used);
3793 const array_item_qt = array_init.container_qt.childType(t.comp);
3794 const array_item_type = try t.transType(scope, array_item_qt, array_init.l_brace_tok);
3795 var maybe_lhs: ?ZigNode = null;
3796 var val_list: std.ArrayList(ZigNode) = .empty;
3797 defer val_list.deinit(t.gpa);
3798 var i: usize = 0;
3799 while (i < array_init.items.len) {
3800 const rhs = switch (array_init.items[i].get(t.tree)) {
3801 .array_filler_expr => |array_filler| blk: {
3802 const node = try ZigTag.array_filler.create(t.arena, .{
3803 .type = array_item_type,
3804 .filler = try t.createZeroValueNode(array_item_qt, array_item_type, .no_as),
3805 .count = @intCast(array_filler.count),
3806 });
3807 i += 1;
3808 break :blk node;
3809 },
3810 else => blk: {
3811 defer val_list.clearRetainingCapacity();
3812 while (i < array_init.items.len) : (i += 1) {
3813 if (array_init.items[i].get(t.tree) == .array_filler_expr) break;
3814 const expr = try t.transExprCoercing(scope, array_init.items[i], .used);
3815 try val_list.append(t.gpa, try t.toNonBool(expr, array_item_qt));
3816 }
3817 const array_type = try ZigTag.array_type.create(t.arena, .{
3818 .elem_type = array_item_type,
3819 .len = val_list.items.len,
3820 });
3821 const array_init_node = try ZigTag.array_init.create(t.arena, .{
3822 .cond = array_type,
3823 .cases = try t.arena.dupe(ZigNode, val_list.items),
3824 });
3825 break :blk array_init_node;
3826 },
3827 };
3828 maybe_lhs = if (maybe_lhs) |lhs| blk: {
3829 const cat = try ZigTag.array_cat.create(t.arena, .{
3830 .lhs = lhs,
3831 .rhs = rhs,
3832 });
3833 break :blk cat;
3834 } else rhs;
3835 }
3836 return maybe_lhs orelse try ZigTag.container_init_dot.create(t.arena, &.{});
3837}
3838
3839fn transUnionInit(
3840 t: *Translator,
3841 scope: *Scope,
3842 union_init: Node.UnionInit,
3843 used: ResultUsed,
3844) TransError!ZigNode {
3845 assert(used == .used);
3846 const init_expr = union_init.initializer orelse
3847 return ZigTag.undefined_literal.init();
3848
3849 if (init_expr.get(t.tree) == .default_init_expr) {
3850 return try t.transExpr(scope, init_expr, used);
3851 }
3852
3853 const union_type = try t.transType(scope, union_init.union_qt, union_init.l_brace_tok);
3854
3855 const union_base = union_init.union_qt.base(t.comp);
3856 const field = union_base.type.@"union".fields[union_init.field_index];
3857 const field_name = if (field.name_tok == 0) t.anonymous_record_field_names.get(.{
3858 .parent = union_base.qt,
3859 .field = field.qt,
3860 }).? else field.name.lookup(t.comp);
3861
3862 const field_init = try t.arena.create(ast.Payload.ContainerInit.Initializer);
3863 field_init.* = .{
3864 .name = field_name,
3865 .value = try t.toNonBool(try t.transExprCoercing(scope, init_expr, .used), field.qt),
3866 };
3867 const container_init = try ZigTag.container_init.create(t.arena, .{
3868 .lhs = union_type,
3869 .inits = field_init[0..1],
3870 });
3871 return container_init;
3872}
3873
3874fn transStructInit(
3875 t: *Translator,
3876 scope: *Scope,
3877 struct_init: Node.ContainerInit,
3878 used: ResultUsed,
3879) TransError!ZigNode {
3880 assert(used == .used);
3881 const struct_type = try t.transType(scope, struct_init.container_qt, struct_init.l_brace_tok);
3882 const field_inits = try t.arena.alloc(ast.Payload.ContainerInit.Initializer, struct_init.items.len);
3883
3884 const struct_base = struct_init.container_qt.base(t.comp);
3885 for (
3886 field_inits,
3887 struct_init.items,
3888 struct_base.type.@"struct".fields,
3889 ) |*init, field_expr, field| {
3890 const field_name = if (field.name_tok == 0) t.anonymous_record_field_names.get(.{
3891 .parent = struct_base.qt,
3892 .field = field.qt,
3893 }).? else field.name.lookup(t.comp);
3894 init.* = .{
3895 .name = field_name,
3896 .value = try t.toNonBool(try t.transExprCoercing(scope, field_expr, .used), field.qt),
3897 };
3898 }
3899
3900 const container_init = try ZigTag.container_init.create(t.arena, .{
3901 .lhs = struct_type,
3902 .inits = field_inits,
3903 });
3904 return container_init;
3905}
3906
3907fn transTypeInfo(
3908 t: *Translator,
3909 scope: *Scope,
3910 op: ZigTag,
3911 typeinfo: Node.TypeInfo,
3912) TransError!ZigNode {
3913 const operand = operand: {
3914 if (typeinfo.expr) |expr| {
3915 const operand = try t.transExpr(scope, expr, .used);
3916 if (operand.tag() == .string_literal) {
3917 const deref = try ZigTag.deref.create(t.arena, operand);
3918 break :operand try ZigTag.typeof.create(t.arena, deref);
3919 }
3920 break :operand try ZigTag.typeof.create(t.arena, operand);
3921 }
3922 break :operand try t.transType(scope, typeinfo.operand_qt, typeinfo.op_tok);
3923 };
3924
3925 const payload = try t.arena.create(ast.Payload.UnOp);
3926 payload.* = .{
3927 .base = .{ .tag = op },
3928 .data = operand,
3929 };
3930 return ZigNode.initPayload(&payload.base);
3931}
3932
3933fn transStmtExpr(
3934 t: *Translator,
3935 scope: *Scope,
3936 stmt_expr: Node.Unary,
3937 used: ResultUsed,
3938) TransError!ZigNode {
3939 const compound_stmt = stmt_expr.operand.get(t.tree).compound_stmt;
3940 if (used == .unused) {
3941 return t.transCompoundStmt(scope, compound_stmt);
3942 }
3943 var block_scope = try Scope.Block.init(t, scope, true);
3944 defer block_scope.deinit();
3945
3946 for (compound_stmt.body[0 .. compound_stmt.body.len - 1]) |stmt| {
3947 const result = try t.transStmt(&block_scope.base, stmt);
3948 switch (result.tag()) {
3949 .declaration, .empty_block => {},
3950 else => try block_scope.statements.append(t.gpa, result),
3951 }
3952 }
3953
3954 const last_result = try t.transExpr(&block_scope.base, compound_stmt.body[compound_stmt.body.len - 1], .used);
3955 switch (last_result.tag()) {
3956 .declaration, .empty_block => {},
3957 else => {
3958 const break_node = try ZigTag.break_val.create(t.arena, .{
3959 .label = block_scope.label,
3960 .val = last_result,
3961 });
3962 try block_scope.statements.append(t.gpa, break_node);
3963 },
3964 }
3965 return block_scope.complete();
3966}
3967
3968fn transConvertvectorExpr(
3969 t: *Translator,
3970 scope: *Scope,
3971 convertvector: Node.Convertvector,
3972) TransError!ZigNode {
3973 var block_scope = try Scope.Block.init(t, scope, true);
3974 defer block_scope.deinit();
3975
3976 const src_expr_node = try t.transExpr(&block_scope.base, convertvector.operand, .used);
3977 const tmp = try block_scope.reserveMangledName("tmp");
3978 const tmp_decl = try ZigTag.var_simple.create(t.arena, .{ .name = tmp, .init = src_expr_node });
3979 try block_scope.statements.append(t.gpa, tmp_decl);
3980 const tmp_ident = try ZigTag.identifier.create(t.arena, tmp);
3981
3982 const dest_type_node = try t.transType(&block_scope.base, convertvector.dest_qt, convertvector.builtin_tok);
3983 const dest_vec_ty = convertvector.dest_qt.get(t.comp, .vector).?;
3984 const src_vec_ty = convertvector.operand.qt(t.tree).get(t.comp, .vector).?;
3985
3986 const src_elem_sk = src_vec_ty.elem.scalarKind(t.comp);
3987 const dest_elem_sk = convertvector.dest_qt.childType(t.comp).scalarKind(t.comp);
3988
3989 const items = try t.arena.alloc(ZigNode, dest_vec_ty.len);
3990 for (items, 0..dest_vec_ty.len) |*item, i| {
3991 const value = try ZigTag.array_access.create(t.arena, .{
3992 .lhs = tmp_ident,
3993 .rhs = try t.createNumberNode(i),
3994 });
3995
3996 if (src_elem_sk == .float and dest_elem_sk == .float) {
3997 item.* = try ZigTag.float_cast.create(t.arena, value);
3998 } else if (src_elem_sk == .float) {
3999 item.* = try ZigTag.int_from_float.create(t.arena, value);
4000 } else if (dest_elem_sk == .float) {
4001 item.* = try ZigTag.float_from_int.create(t.arena, value);
4002 } else {
4003 item.* = try t.transIntCast(value, src_vec_ty.elem, dest_vec_ty.elem);
4004 }
4005 }
4006
4007 const vec_init = try ZigTag.array_init.create(t.arena, .{
4008 .cond = dest_type_node,
4009 .cases = items,
4010 });
4011 const break_node = try ZigTag.break_val.create(t.arena, .{
4012 .label = block_scope.label,
4013 .val = vec_init,
4014 });
4015 try block_scope.statements.append(t.gpa, break_node);
4016
4017 return block_scope.complete();
4018}
4019
4020fn transShufflevectorExpr(
4021 t: *Translator,
4022 scope: *Scope,
4023 shufflevector: Node.Shufflevector,
4024) TransError!ZigNode {
4025 if (shufflevector.indexes.len == 0) {
4026 return t.fail(error.UnsupportedTranslation, shufflevector.builtin_tok, "@shuffle needs at least 1 index", .{});
4027 }
4028
4029 const a = try t.transExpr(scope, shufflevector.lhs, .used);
4030 const b = try t.transExpr(scope, shufflevector.rhs, .used);
4031
4032 // First two arguments to __builtin_shufflevector must be the same type
4033 const vector_child_type = try t.vectorTypeInfo(a, "child");
4034 const vector_len = try t.vectorTypeInfo(a, "len");
4035 const shuffle_mask = blk: {
4036 const mask_len = shufflevector.indexes.len;
4037
4038 const mask_type = try ZigTag.vector.create(t.arena, .{
4039 .lhs = try t.createNumberNode(mask_len),
4040 .rhs = try ZigTag.type.create(t.arena, "i32"),
4041 });
4042
4043 const init_list = try t.arena.alloc(ZigNode, mask_len);
4044 for (init_list, shufflevector.indexes) |*init, index| {
4045 const index_expr = try t.transExprCoercing(scope, index, .used);
4046 const converted_index = try t.createHelperCallNode(.shuffleVectorIndex, &.{ index_expr, vector_len });
4047 init.* = converted_index;
4048 }
4049
4050 break :blk try ZigTag.array_init.create(t.arena, .{
4051 .cond = mask_type,
4052 .cases = init_list,
4053 });
4054 };
4055
4056 return ZigTag.shuffle.create(t.arena, .{
4057 .element_type = vector_child_type,
4058 .a = a,
4059 .b = b,
4060 .mask_vector = shuffle_mask,
4061 });
4062}
4063
4064// =====================
4065// Node creation helpers
4066// =====================
4067
4068fn createZeroValueNode(
4069 t: *Translator,
4070 qt: QualType,
4071 type_node: ZigNode,
4072 suppress_as: SuppressCast,
4073) !ZigNode {
4074 switch (qt.base(t.comp).type) {
4075 .bool => return ZigTag.false_literal.init(),
4076 .int, .bit_int, .float => {
4077 const zero_literal = ZigTag.zero_literal.init();
4078 return switch (suppress_as) {
4079 .with_as => try t.createBinOpNode(.as, type_node, zero_literal),
4080 .no_as => zero_literal,
4081 };
4082 },
4083 .pointer => {
4084 const null_literal = ZigTag.null_literal.init();
4085 return switch (suppress_as) {
4086 .with_as => try t.createBinOpNode(.as, type_node, null_literal),
4087 .no_as => null_literal,
4088 };
4089 },
4090 else => {},
4091 }
4092 return try ZigTag.std_mem_zeroes.create(t.arena, type_node);
4093}
4094
4095fn createIntNode(t: *Translator, int: aro.Value) !ZigNode {
4096 var space: aro.Interner.Tag.Int.BigIntSpace = undefined;
4097 var big = t.comp.interner.get(int.ref()).toBigInt(&space);
4098 const is_negative = !big.positive;
4099 big.positive = true;
4100
4101 const str = big.toStringAlloc(t.arena, 10, .lower) catch |err| switch (err) {
4102 error.OutOfMemory => |e| return e,
4103 };
4104 const res = try ZigTag.integer_literal.create(t.arena, str);
4105 if (is_negative) return ZigTag.negate.create(t.arena, res);
4106 return res;
4107}
4108
4109fn createNumberNode(t: *Translator, num: anytype) !ZigNode {
4110 const str = try std.fmt.allocPrint(t.arena, "{d}", .{num});
4111 return ZigTag.integer_literal.create(t.arena, str);
4112}
4113
4114fn createCharLiteralNode(t: *Translator, narrow: bool, val: u32) TransError!ZigNode {
4115 return ZigTag.char_literal.create(t.arena, if (narrow)
4116 try std.fmt.allocPrint(t.arena, "'{f}'", .{std.zig.fmtChar(@as(u8, @intCast(val)))})
4117 else
4118 try std.fmt.allocPrint(t.arena, "'\\u{{{x}}}'", .{val}));
4119}
4120
4121fn createBinOpNode(
4122 t: *Translator,
4123 op: ZigTag,
4124 lhs: ZigNode,
4125 rhs: ZigNode,
4126) !ZigNode {
4127 const payload = try t.arena.create(ast.Payload.BinOp);
4128 payload.* = .{
4129 .base = .{ .tag = op },
4130 .data = .{
4131 .lhs = lhs,
4132 .rhs = rhs,
4133 },
4134 };
4135 return ZigNode.initPayload(&payload.base);
4136}
4137
4138pub fn createHelperCallNode(t: *Translator, name: std.meta.DeclEnum(std.zig.c_translation.helpers), args_opt: ?[]const ZigNode) !ZigNode {
4139 if (args_opt) |args| {
4140 return ZigTag.helper_call.create(t.arena, .{
4141 .name = @tagName(name),
4142 .args = try t.arena.dupe(ZigNode, args),
4143 });
4144 } else {
4145 return ZigTag.helper_ref.create(t.arena, @tagName(name));
4146 }
4147}
4148
4149/// Cast a signed integer node to a usize, for use in pointer arithmetic. Negative numbers
4150/// will become very large positive numbers but that is ok since we only use this in
4151/// pointer arithmetic expressions, where wraparound will ensure we get the correct value.
4152/// node -> @as(usize, @bitCast(@as(isize, @intCast(node))))
4153fn usizeCastForWrappingPtrArithmetic(t: *Translator, node: ZigNode) TransError!ZigNode {
4154 const intcast_node = try ZigTag.as.create(t.arena, .{
4155 .lhs = try ZigTag.type.create(t.arena, "isize"),
4156 .rhs = try ZigTag.int_cast.create(t.arena, node),
4157 });
4158
4159 return ZigTag.as.create(t.arena, .{
4160 .lhs = try ZigTag.type.create(t.arena, "usize"),
4161 .rhs = try ZigTag.bit_cast.create(t.arena, intcast_node),
4162 });
4163}
4164
4165/// @typeInfo(@TypeOf(vec_node)).vector.<field>
4166fn vectorTypeInfo(t: *Translator, vec_node: ZigNode, field: []const u8) TransError!ZigNode {
4167 const typeof_call = try ZigTag.typeof.create(t.arena, vec_node);
4168 const typeinfo_call = try ZigTag.typeinfo.create(t.arena, typeof_call);
4169 const vector_type_info = try ZigTag.field_access.create(t.arena, .{ .lhs = typeinfo_call, .field_name = "vector" });
4170 return ZigTag.field_access.create(t.arena, .{ .lhs = vector_type_info, .field_name = field });
4171}
4172
4173/// Returns true if the given array length qualifies as a flexible array member
4174/// under the current -fstrict-flex-arrays level.
4175fn isFlexibleArrayLen(t: *const Translator, len: anytype) bool {
4176 return switch (t.strict_flex_arrays) {
4177 .@"0" => true,
4178 .@"1" => switch (len) {
4179 .incomplete => true,
4180 .fixed => |n| n <= 1,
4181 else => false,
4182 },
4183 .@"2" => switch (len) {
4184 .incomplete => true,
4185 .fixed => |n| n == 0,
4186 else => false,
4187 },
4188 .@"3" => len == .incomplete,
4189 };
4190}
4191
4192/// Build a getter function for a flexible array field in a C record
4193/// e.g. `T items[]` or `T items[0]`. The generated function returns a [*c] pointer
4194/// to the flexible array with the correct const and volatile qualifiers
4195fn createFlexibleMemberFn(
4196 t: *Translator,
4197 member_name: []const u8,
4198 field_name: []const u8,
4199) Error!ZigNode {
4200 // Use `_self` instead of the conventional `self` to avoid the Zig error
4201 // "function parameter shadows declaration of 'self'".
4202 // `processContainerMemberFns` merges C functions matching a struct's name
4203 // prefix into the struct as `pub const` aliases (e.g. `foo_self()` becomes
4204 // `pub const self = __root.foo_self`). A parameter also named `self` would
4205 // then shadow that declaration, which Zig rejects.
4206 const self_param_name = "_self";
4207 const self_param = try ZigTag.identifier.create(t.arena, self_param_name);
4208 const self_type = try ZigTag.typeof.create(t.arena, self_param);
4209
4210 const fn_params = try t.arena.alloc(ast.Payload.Param, 1);
4211 fn_params[0] = .{
4212 .name = self_param_name,
4213 .type = ZigTag.@"anytype".init(),
4214 .is_noalias = false,
4215 };
4216
4217 // @typeInfo(@TypeOf(self.*.<field_name>)).pointer.child
4218 const dereffed = try ZigTag.deref.create(t.arena, self_param);
4219 const field_access = try ZigTag.field_access.create(t.arena, .{ .lhs = dereffed, .field_name = field_name });
4220 const type_of = try ZigTag.typeof.create(t.arena, field_access);
4221 const type_info = try ZigTag.typeinfo.create(t.arena, type_of);
4222 const array_info = try ZigTag.field_access.create(t.arena, .{ .lhs = type_info, .field_name = "array" });
4223 const child_info = try ZigTag.field_access.create(t.arena, .{ .lhs = array_info, .field_name = "child" });
4224
4225 const return_type = try t.createHelperCallNode(.FlexibleArrayType, &.{ self_type, child_info });
4226
4227 // return @ptrCast(&self.*.<field_name>);
4228 const address_of = try ZigTag.address_of.create(t.arena, field_access);
4229 const aligned = try ZigTag.align_cast.create(t.arena, address_of);
4230 const casted = try ZigTag.ptr_cast.create(t.arena, aligned);
4231 const return_stmt = try ZigTag.@"return".create(t.arena, casted);
4232 const body = try ZigTag.block_single.create(t.arena, return_stmt);
4233
4234 return ZigTag.func.create(t.arena, .{
4235 .is_pub = true,
4236 .is_extern = false,
4237 .is_export = false,
4238 .is_inline = false,
4239 .is_var_args = false,
4240 .name = member_name,
4241 .linksection_string = null,
4242 .explicit_callconv = null,
4243 .params = fn_params,
4244 .return_type = return_type,
4245 .body = body,
4246 .alignment = null,
4247 });
4248}
4249
4250// =================
4251// Macro translation
4252// =================
4253
4254fn transMacros(t: *Translator) !void {
4255 var tok_list: std.ArrayList(CToken) = .empty;
4256 defer tok_list.deinit(t.gpa);
4257
4258 var pattern_list = try PatternList.init(t.gpa);
4259 defer pattern_list.deinit(t.gpa);
4260
4261 for (t.pp.defines.keys(), t.pp.defines.values()) |name, macro| {
4262 if (macro.isBuiltin()) continue;
4263 if (t.global_scope.containsNow(name)) {
4264 continue;
4265 }
4266
4267 tok_list.items.len = 0;
4268 try tok_list.ensureUnusedCapacity(t.gpa, macro.tokens.len);
4269 for (macro.tokens) |tok| {
4270 switch (tok.id) {
4271 .invalid => continue,
4272 .whitespace => continue,
4273 .comment => continue,
4274 .macro_ws => continue,
4275 else => {},
4276 }
4277 tok_list.appendAssumeCapacity(tok);
4278 }
4279
4280 if (macro.is_func) {
4281 const ms: PatternList.MacroSlicer = .{
4282 .tokens = tok_list.items,
4283 .source = t.comp.getSource(macro.loc.id).buf,
4284 .params = @intCast(macro.params.len),
4285 };
4286 if (try pattern_list.match(ms)) |impl| {
4287 const decl = try ZigTag.pub_var_simple.create(t.arena, .{
4288 .name = name,
4289 .init = try t.createHelperCallNode(impl, null),
4290 });
4291 try t.addTopLevelDecl(name, decl);
4292 continue;
4293 }
4294 }
4295
4296 if (t.checkTranslatableMacro(tok_list.items, macro.params)) |err| {
4297 switch (err) {
4298 .undefined_identifier => |ident| try t.failDeclExtra(&t.global_scope.base, macro.loc, name, "unable to translate macro: undefined identifier `{s}`", .{ident}),
4299 .invalid_arg_usage => |ident| try t.failDeclExtra(&t.global_scope.base, macro.loc, name, "unable to translate macro: untranslatable usage of arg `{s}`", .{ident}),
4300 }
4301 continue;
4302 }
4303
4304 var macro_translator: MacroTranslator = .{
4305 .t = t,
4306 .tokens = tok_list.items,
4307 .source = t.comp.getSource(macro.loc.id).buf,
4308 .name = name,
4309 .macro = macro,
4310 };
4311
4312 const res = if (macro.is_func)
4313 macro_translator.transFnMacro()
4314 else
4315 macro_translator.transMacro();
4316 res catch |err| switch (err) {
4317 error.ParseError => continue,
4318 error.OutOfMemory => |e| return e,
4319 };
4320 }
4321}
4322
4323const MacroTranslateError = union(enum) {
4324 undefined_identifier: []const u8,
4325 invalid_arg_usage: []const u8,
4326};
4327
4328fn checkTranslatableMacro(t: *Translator, tokens: []const CToken, params: []const []const u8) ?MacroTranslateError {
4329 var last_is_type_kw = false;
4330 var i: usize = 0;
4331 while (i < tokens.len) : (i += 1) {
4332 const token = tokens[i];
4333 switch (token.id) {
4334 .period, .arrow => i += 1, // skip next token since field identifiers can be unknown
4335 .keyword_struct, .keyword_union, .keyword_enum => if (!last_is_type_kw) {
4336 last_is_type_kw = true;
4337 continue;
4338 },
4339 .macro_param, .macro_param_no_expand => {
4340 if (last_is_type_kw) {
4341 return .{ .invalid_arg_usage = params[token.end] };
4342 }
4343 },
4344 .identifier, .extended_identifier => {
4345 const identifier = t.pp.tokSlice(token);
4346 if (!t.global_scope.contains(identifier) and !builtins.map.has(identifier)) {
4347 return .{ .undefined_identifier = identifier };
4348 }
4349 },
4350 else => {},
4351 }
4352 last_is_type_kw = false;
4353 }
4354 return null;
4355}
4356
4357fn getContainer(t: *Translator, node: ZigNode) ?ZigNode {
4358 switch (node.tag()) {
4359 .@"union",
4360 .@"struct",
4361 .address_of,
4362 .bit_not,
4363 .not,
4364 .optional_type,
4365 .negate,
4366 .negate_wrap,
4367 .array_type,
4368 .c_pointer,
4369 .single_pointer,
4370 => return node,
4371
4372 .identifier => {
4373 const ident = node.castTag(.identifier).?;
4374 if (t.global_scope.sym_table.get(ident.data)) |value| {
4375 if (value.castTag(.var_decl)) |var_decl|
4376 return t.getContainer(var_decl.data.init.?);
4377 if (value.castTag(.var_simple) orelse value.castTag(.pub_var_simple)) |var_decl|
4378 return t.getContainer(var_decl.data.init);
4379 }
4380 },
4381
4382 .field_access => {
4383 const field_access = node.castTag(.field_access).?;
4384
4385 if (t.getContainerTypeOf(field_access.data.lhs)) |ty_node| {
4386 if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| {
4387 for (container.data.fields) |field| {
4388 if (mem.eql(u8, field.name, field_access.data.field_name)) {
4389 return t.getContainer(field.type);
4390 }
4391 }
4392 }
4393 }
4394 },
4395
4396 else => {},
4397 }
4398 return null;
4399}
4400
4401fn getContainerTypeOf(t: *Translator, ref: ZigNode) ?ZigNode {
4402 if (ref.castTag(.identifier)) |ident| {
4403 if (t.global_scope.sym_table.get(ident.data)) |value| {
4404 if (value.castTag(.var_decl)) |var_decl| {
4405 return t.getContainer(var_decl.data.type);
4406 }
4407 }
4408 } else if (ref.castTag(.field_access)) |field_access| {
4409 if (t.getContainerTypeOf(field_access.data.lhs)) |ty_node| {
4410 if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| {
4411 for (container.data.fields) |field| {
4412 if (mem.eql(u8, field.name, field_access.data.field_name)) {
4413 return t.getContainer(field.type);
4414 }
4415 }
4416 } else return ty_node;
4417 }
4418 }
4419 return null;
4420}
4421
4422pub fn getFnProto(t: *Translator, ref: ZigNode) ?*ast.Payload.Func {
4423 const init = if (ref.castTag(.var_decl)) |v|
4424 v.data.init orelse return null
4425 else if (ref.castTag(.var_simple) orelse ref.castTag(.pub_var_simple)) |v|
4426 v.data.init
4427 else
4428 return null;
4429 if (t.getContainerTypeOf(init)) |ty_node| {
4430 if (ty_node.castTag(.optional_type)) |prefix| {
4431 if (prefix.data.castTag(.single_pointer)) |sp| {
4432 if (sp.data.elem_type.castTag(.func)) |fn_proto| {
4433 return fn_proto;
4434 }
4435 }
4436 }
4437 }
4438 return null;
4439}