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