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}