zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

blob 2fbcf72c (261581B) - Raw


      1 const std = @import("std");
      2 const testing = std.testing;
      3 const assert = std.debug.assert;
      4 const mem = std.mem;
      5 const math = std.math;
      6 const meta = std.meta;
      7 const CallingConvention = std.builtin.CallingConvention;
      8 const clang = @import("clang.zig");
      9 const aro = @import("aro");
     10 const CToken = aro.Tokenizer.Token;
     11 const Node = ast.Node;
     12 const Tag = Node.Tag;
     13 const common = @import("aro_translate_c");
     14 const ast = common.ast;
     15 const Error = common.Error;
     16 const MacroProcessingError = common.MacroProcessingError;
     17 const TypeError = common.TypeError;
     18 const TransError = common.TransError;
     19 const SymbolTable = common.SymbolTable;
     20 const AliasList = common.AliasList;
     21 const ResultUsed = common.ResultUsed;
     22 const Scope = common.ScopeExtra(Context, clang.QualType);
     23 const PatternList = common.PatternList;
     24 const MacroSlicer = common.MacroSlicer;
     25 
     26 pub const Context = struct {
     27     gpa: mem.Allocator,
     28     arena: mem.Allocator,
     29     source_manager: *clang.SourceManager,
     30     decl_table: std.AutoArrayHashMapUnmanaged(usize, []const u8) = .{},
     31     alias_list: AliasList,
     32     global_scope: *Scope.Root,
     33     clang_context: *clang.ASTContext,
     34     mangle_count: u32 = 0,
     35     /// Table of record decls that have been demoted to opaques.
     36     opaque_demotes: std.AutoHashMapUnmanaged(usize, void) = .{},
     37     /// Table of unnamed enums and records that are child types of typedefs.
     38     unnamed_typedefs: std.AutoHashMapUnmanaged(usize, []const u8) = .{},
     39     /// Needed to decide if we are parsing a typename
     40     typedefs: std.StringArrayHashMapUnmanaged(void) = .{},
     41 
     42     /// This one is different than the root scope's name table. This contains
     43     /// a list of names that we found by visiting all the top level decls without
     44     /// translating them. The other maps are updated as we translate; this one is updated
     45     /// up front in a pre-processing step.
     46     global_names: std.StringArrayHashMapUnmanaged(void) = .{},
     47 
     48     /// This is similar to `global_names`, but contains names which we would
     49     /// *like* to use, but do not strictly *have* to if they are unavailable.
     50     /// These are relevant to types, which ideally we would name like
     51     /// 'struct_foo' with an alias 'foo', but if either of those names is taken,
     52     /// may be mangled.
     53     /// This is distinct from `global_names` so we can detect at a type
     54     /// declaration whether or not the name is available.
     55     weak_global_names: std.StringArrayHashMapUnmanaged(void) = .{},
     56 
     57     pattern_list: PatternList,
     58 
     59     fn getMangle(c: *Context) u32 {
     60         c.mangle_count += 1;
     61         return c.mangle_count;
     62     }
     63 
     64     /// Convert a null-terminated C string to a slice allocated in the arena
     65     fn str(c: *Context, s: [*:0]const u8) ![]u8 {
     66         return c.arena.dupe(u8, mem.sliceTo(s, 0));
     67     }
     68 
     69     /// Convert a clang source location to a file:line:column string
     70     fn locStr(c: *Context, loc: clang.SourceLocation) ![]u8 {
     71         const spelling_loc = c.source_manager.getSpellingLoc(loc);
     72         const filename_c = c.source_manager.getFilename(spelling_loc);
     73         const filename = if (filename_c) |s| try c.str(s) else @as([]const u8, "(no file)");
     74 
     75         const line = c.source_manager.getSpellingLineNumber(spelling_loc);
     76         const column = c.source_manager.getSpellingColumnNumber(spelling_loc);
     77         return std.fmt.allocPrint(c.arena, "{s}:{d}:{d}", .{ filename, line, column });
     78     }
     79 };
     80 
     81 pub fn translate(
     82     gpa: mem.Allocator,
     83     args_begin: [*]?[*]const u8,
     84     args_end: [*]?[*]const u8,
     85     errors: *std.zig.ErrorBundle,
     86     resources_path: [*:0]const u8,
     87 ) !std.zig.Ast {
     88     var clang_errors: []clang.ErrorMsg = &.{};
     89 
     90     const ast_unit = clang.LoadFromCommandLine(
     91         args_begin,
     92         args_end,
     93         &clang_errors.ptr,
     94         &clang_errors.len,
     95         resources_path,
     96     ) orelse {
     97         defer clang.ErrorMsg.delete(clang_errors.ptr, clang_errors.len);
     98 
     99         var bundle: std.zig.ErrorBundle.Wip = undefined;
    100         try bundle.init(gpa);
    101         defer bundle.deinit();
    102 
    103         for (clang_errors) |c_error| {
    104             const line = line: {
    105                 const source = c_error.source orelse break :line 0;
    106                 var start = c_error.offset;
    107                 while (start > 0) : (start -= 1) {
    108                     if (source[start - 1] == '\n') break;
    109                 }
    110                 var end = c_error.offset;
    111                 while (true) : (end += 1) {
    112                     if (source[end] == 0) break;
    113                     if (source[end] == '\n') break;
    114                 }
    115                 break :line try bundle.addString(source[start..end]);
    116             };
    117 
    118             try bundle.addRootErrorMessage(.{
    119                 .msg = try bundle.addString(c_error.msg_ptr[0..c_error.msg_len]),
    120                 .src_loc = if (c_error.filename_ptr) |filename_ptr| try bundle.addSourceLocation(.{
    121                     .src_path = try bundle.addString(filename_ptr[0..c_error.filename_len]),
    122                     .span_start = c_error.offset,
    123                     .span_main = c_error.offset,
    124                     .span_end = c_error.offset + 1,
    125                     .line = c_error.line,
    126                     .column = c_error.column,
    127                     .source_line = line,
    128                 }) else .none,
    129             });
    130         }
    131         errors.* = try bundle.toOwnedBundle("");
    132 
    133         return error.SemanticAnalyzeFail;
    134     };
    135     defer ast_unit.delete();
    136 
    137     // For memory that has the same lifetime as the Ast that we return
    138     // from this function.
    139     var arena_allocator = std.heap.ArenaAllocator.init(gpa);
    140     defer arena_allocator.deinit();
    141     const arena = arena_allocator.allocator();
    142 
    143     var context = Context{
    144         .gpa = gpa,
    145         .arena = arena,
    146         .source_manager = ast_unit.getSourceManager(),
    147         .alias_list = AliasList.init(gpa),
    148         .global_scope = try arena.create(Scope.Root),
    149         .clang_context = ast_unit.getASTContext(),
    150         .pattern_list = try PatternList.init(gpa),
    151     };
    152     context.global_scope.* = Scope.Root.init(&context);
    153     defer {
    154         context.decl_table.deinit(gpa);
    155         context.alias_list.deinit();
    156         context.global_names.deinit(gpa);
    157         context.opaque_demotes.deinit(gpa);
    158         context.unnamed_typedefs.deinit(gpa);
    159         context.typedefs.deinit(gpa);
    160         context.global_scope.deinit();
    161         context.pattern_list.deinit(gpa);
    162     }
    163 
    164     inline for (@typeInfo(std.zig.c_builtins).Struct.decls) |decl| {
    165         const builtin = try Tag.pub_var_simple.create(arena, .{
    166             .name = decl.name,
    167             .init = try Tag.import_c_builtin.create(arena, decl.name),
    168         });
    169         try addTopLevelDecl(&context, decl.name, builtin);
    170     }
    171 
    172     try prepopulateGlobalNameTable(ast_unit, &context);
    173 
    174     if (!ast_unit.visitLocalTopLevelDecls(&context, declVisitorC)) {
    175         return error.OutOfMemory;
    176     }
    177 
    178     try transPreprocessorEntities(&context, ast_unit);
    179 
    180     try addMacros(&context);
    181     for (context.alias_list.items) |alias| {
    182         const node = try Tag.alias.create(arena, .{ .actual = alias.alias, .mangled = alias.name });
    183         try addTopLevelDecl(&context, alias.alias, node);
    184     }
    185 
    186     return ast.render(gpa, context.global_scope.nodes.items);
    187 }
    188 
    189 /// Determines whether macro is of the form: `#define FOO FOO` (Possibly with trailing tokens)
    190 /// Macros of this form will not be translated.
    191 fn isSelfDefinedMacro(unit: *const clang.ASTUnit, c: *const Context, macro: *const clang.MacroDefinitionRecord) !bool {
    192     const source = try getMacroText(unit, c, macro);
    193     var tokenizer: aro.Tokenizer = .{
    194         .buf = source,
    195         .source = .unused,
    196         .langopts = .{},
    197     };
    198     const name_tok = tokenizer.nextNoWS();
    199     const name = source[name_tok.start..name_tok.end];
    200 
    201     const first_tok = tokenizer.nextNoWS();
    202     // We do not just check for `.Identifier` below because keyword tokens are preferentially matched first by
    203     // the tokenizer.
    204     // In other words we would miss `#define inline inline` (`inline` is a valid c89 identifier)
    205     if (first_tok.id == .eof) return false;
    206     return mem.eql(u8, name, source[first_tok.start..first_tok.end]);
    207 }
    208 
    209 fn prepopulateGlobalNameTable(ast_unit: *clang.ASTUnit, c: *Context) !void {
    210     if (!ast_unit.visitLocalTopLevelDecls(c, declVisitorNamesOnlyC)) {
    211         return error.OutOfMemory;
    212     }
    213 
    214     // TODO if we see #undef, delete it from the table
    215     var it = ast_unit.getLocalPreprocessingEntities_begin();
    216     const it_end = ast_unit.getLocalPreprocessingEntities_end();
    217 
    218     while (it.I != it_end.I) : (it.I += 1) {
    219         const entity = it.deref();
    220         switch (entity.getKind()) {
    221             .MacroDefinitionKind => {
    222                 const macro = @as(*clang.MacroDefinitionRecord, @ptrCast(entity));
    223                 const raw_name = macro.getName_getNameStart();
    224                 const name = try c.str(raw_name);
    225 
    226                 if (!try isSelfDefinedMacro(ast_unit, c, macro)) {
    227                     try c.global_names.put(c.gpa, name, {});
    228                 }
    229             },
    230             else => {},
    231         }
    232     }
    233 }
    234 
    235 fn declVisitorNamesOnlyC(context: ?*anyopaque, decl: *const clang.Decl) callconv(.C) bool {
    236     const c: *Context = @ptrCast(@alignCast(context));
    237     declVisitorNamesOnly(c, decl) catch return false;
    238     return true;
    239 }
    240 
    241 fn declVisitorC(context: ?*anyopaque, decl: *const clang.Decl) callconv(.C) bool {
    242     const c: *Context = @ptrCast(@alignCast(context));
    243     declVisitor(c, decl) catch return false;
    244     return true;
    245 }
    246 
    247 fn declVisitorNamesOnly(c: *Context, decl: *const clang.Decl) Error!void {
    248     if (decl.castToNamedDecl()) |named_decl| {
    249         const decl_name = try c.str(named_decl.getName_bytes_begin());
    250 
    251         switch (decl.getKind()) {
    252             .Record, .Enum => {
    253                 // These types are prefixed with the container kind.
    254                 const container_prefix = if (decl.getKind() == .Record) prefix: {
    255                     const record_decl: *const clang.RecordDecl = @ptrCast(decl);
    256                     if (record_decl.isUnion()) {
    257                         break :prefix "union";
    258                     } else {
    259                         break :prefix "struct";
    260                     }
    261                 } else "enum";
    262                 const prefixed_name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_prefix, decl_name });
    263                 // `decl_name` and `prefixed_name` are the preferred names for this type.
    264                 // However, we can name it anything else if necessary, so these are "weak names".
    265                 try c.weak_global_names.ensureUnusedCapacity(c.gpa, 2);
    266                 c.weak_global_names.putAssumeCapacity(decl_name, {});
    267                 c.weak_global_names.putAssumeCapacity(prefixed_name, {});
    268             },
    269             else => {
    270                 try c.global_names.put(c.gpa, decl_name, {});
    271             },
    272         }
    273 
    274         // Check for typedefs with unnamed enum/record child types.
    275         if (decl.getKind() == .Typedef) {
    276             const typedef_decl = @as(*const clang.TypedefNameDecl, @ptrCast(decl));
    277             var child_ty = typedef_decl.getUnderlyingType().getTypePtr();
    278             const addr: usize = while (true) switch (child_ty.getTypeClass()) {
    279                 .Enum => {
    280                     const enum_ty = @as(*const clang.EnumType, @ptrCast(child_ty));
    281                     const enum_decl = enum_ty.getDecl();
    282                     // check if this decl is unnamed
    283                     if (@as(*const clang.NamedDecl, @ptrCast(enum_decl)).getName_bytes_begin()[0] != 0) return;
    284                     break @intFromPtr(enum_decl.getCanonicalDecl());
    285                 },
    286                 .Record => {
    287                     const record_ty = @as(*const clang.RecordType, @ptrCast(child_ty));
    288                     const record_decl = record_ty.getDecl();
    289                     // check if this decl is unnamed
    290                     if (@as(*const clang.NamedDecl, @ptrCast(record_decl)).getName_bytes_begin()[0] != 0) return;
    291                     break @intFromPtr(record_decl.getCanonicalDecl());
    292                 },
    293                 .Elaborated => {
    294                     const elaborated_ty = @as(*const clang.ElaboratedType, @ptrCast(child_ty));
    295                     child_ty = elaborated_ty.getNamedType().getTypePtr();
    296                 },
    297                 .Decayed => {
    298                     const decayed_ty = @as(*const clang.DecayedType, @ptrCast(child_ty));
    299                     child_ty = decayed_ty.getDecayedType().getTypePtr();
    300                 },
    301                 .Attributed => {
    302                     const attributed_ty = @as(*const clang.AttributedType, @ptrCast(child_ty));
    303                     child_ty = attributed_ty.getEquivalentType().getTypePtr();
    304                 },
    305                 .MacroQualified => {
    306                     const macroqualified_ty = @as(*const clang.MacroQualifiedType, @ptrCast(child_ty));
    307                     child_ty = macroqualified_ty.getModifiedType().getTypePtr();
    308                 },
    309                 else => return,
    310             };
    311 
    312             const result = try c.unnamed_typedefs.getOrPut(c.gpa, addr);
    313             if (result.found_existing) {
    314                 // One typedef can declare multiple names.
    315                 // Don't put this one in `decl_table` so it's processed later.
    316                 return;
    317             }
    318             result.value_ptr.* = decl_name;
    319             // Put this typedef in the decl_table to avoid redefinitions.
    320             try c.decl_table.putNoClobber(c.gpa, @intFromPtr(typedef_decl.getCanonicalDecl()), decl_name);
    321             try c.typedefs.put(c.gpa, decl_name, {});
    322         }
    323     }
    324 }
    325 
    326 fn declVisitor(c: *Context, decl: *const clang.Decl) Error!void {
    327     switch (decl.getKind()) {
    328         .Function => {
    329             return visitFnDecl(c, @as(*const clang.FunctionDecl, @ptrCast(decl)));
    330         },
    331         .Typedef => {
    332             try transTypeDef(c, &c.global_scope.base, @as(*const clang.TypedefNameDecl, @ptrCast(decl)));
    333         },
    334         .Enum => {
    335             try transEnumDecl(c, &c.global_scope.base, @as(*const clang.EnumDecl, @ptrCast(decl)));
    336         },
    337         .Record => {
    338             try transRecordDecl(c, &c.global_scope.base, @as(*const clang.RecordDecl, @ptrCast(decl)));
    339         },
    340         .Var => {
    341             return visitVarDecl(c, @as(*const clang.VarDecl, @ptrCast(decl)), null);
    342         },
    343         .Empty => {
    344             // Do nothing
    345         },
    346         .FileScopeAsm => {
    347             try transFileScopeAsm(c, &c.global_scope.base, @as(*const clang.FileScopeAsmDecl, @ptrCast(decl)));
    348         },
    349         else => {
    350             const decl_name = try c.str(decl.getDeclKindName());
    351             try warn(c, &c.global_scope.base, decl.getLocation(), "ignoring {s} declaration", .{decl_name});
    352         },
    353     }
    354 }
    355 
    356 fn transFileScopeAsm(c: *Context, scope: *Scope, file_scope_asm: *const clang.FileScopeAsmDecl) Error!void {
    357     const asm_string = file_scope_asm.getAsmString();
    358     var len: usize = undefined;
    359     const bytes_ptr = asm_string.getString_bytes_begin_size(&len);
    360 
    361     const str = try std.fmt.allocPrint(c.arena, "\"{}\"", .{std.zig.fmtEscapes(bytes_ptr[0..len])});
    362     const str_node = try Tag.string_literal.create(c.arena, str);
    363 
    364     const asm_node = try Tag.asm_simple.create(c.arena, str_node);
    365     const block = try Tag.block_single.create(c.arena, asm_node);
    366     const comptime_node = try Tag.@"comptime".create(c.arena, block);
    367 
    368     try scope.appendNode(comptime_node);
    369 }
    370 
    371 fn visitFnDecl(c: *Context, fn_decl: *const clang.FunctionDecl) Error!void {
    372     const fn_name = try c.str(@as(*const clang.NamedDecl, @ptrCast(fn_decl)).getName_bytes_begin());
    373     if (c.global_scope.sym_table.contains(fn_name))
    374         return; // Avoid processing this decl twice
    375 
    376     // Skip this declaration if a proper definition exists
    377     if (!fn_decl.isThisDeclarationADefinition()) {
    378         if (fn_decl.getDefinition()) |def|
    379             return visitFnDecl(c, def);
    380     }
    381 
    382     const fn_decl_loc = fn_decl.getLocation();
    383     const has_body = fn_decl.hasBody();
    384     const storage_class = fn_decl.getStorageClass();
    385     const is_always_inline = has_body and fn_decl.hasAlwaysInlineAttr();
    386     var decl_ctx = FnDeclContext{
    387         .fn_name = fn_name,
    388         .has_body = has_body,
    389         .storage_class = storage_class,
    390         .is_always_inline = is_always_inline,
    391         .is_export = switch (storage_class) {
    392             .None => has_body and !is_always_inline and !fn_decl.isInlineSpecified(),
    393             .Extern, .Static => false,
    394             .PrivateExtern => return failDecl(c, fn_decl_loc, fn_name, "unsupported storage class: private extern", .{}),
    395             .Auto => unreachable, // Not legal on functions
    396             .Register => unreachable, // Not legal on functions
    397         },
    398     };
    399 
    400     var fn_qt = fn_decl.getType();
    401 
    402     const fn_type = while (true) {
    403         const fn_type = fn_qt.getTypePtr();
    404 
    405         switch (fn_type.getTypeClass()) {
    406             .Attributed => {
    407                 const attr_type = @as(*const clang.AttributedType, @ptrCast(fn_type));
    408                 fn_qt = attr_type.getEquivalentType();
    409             },
    410             .Paren => {
    411                 const paren_type = @as(*const clang.ParenType, @ptrCast(fn_type));
    412                 fn_qt = paren_type.getInnerType();
    413             },
    414             else => break fn_type,
    415         }
    416     };
    417     const fn_ty = @as(*const clang.FunctionType, @ptrCast(fn_type));
    418     const return_qt = fn_ty.getReturnType();
    419 
    420     const proto_node = switch (fn_type.getTypeClass()) {
    421         .FunctionProto => blk: {
    422             const fn_proto_type = @as(*const clang.FunctionProtoType, @ptrCast(fn_type));
    423             if (has_body and fn_proto_type.isVariadic()) {
    424                 decl_ctx.has_body = false;
    425                 decl_ctx.storage_class = .Extern;
    426                 decl_ctx.is_export = false;
    427                 decl_ctx.is_always_inline = false;
    428                 try warn(c, &c.global_scope.base, fn_decl_loc, "TODO unable to translate variadic function, demoted to extern", .{});
    429             }
    430             break :blk transFnProto(c, fn_decl, fn_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) {
    431                 error.UnsupportedType => {
    432                     return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});
    433                 },
    434                 error.OutOfMemory => |e| return e,
    435             };
    436         },
    437         .FunctionNoProto => blk: {
    438             const fn_no_proto_type = @as(*const clang.FunctionType, @ptrCast(fn_type));
    439             break :blk transFnNoProto(c, fn_no_proto_type, fn_decl_loc, decl_ctx, true) catch |err| switch (err) {
    440                 error.UnsupportedType => {
    441                     return failDecl(c, fn_decl_loc, fn_name, "unable to resolve prototype of function", .{});
    442                 },
    443                 error.OutOfMemory => |e| return e,
    444             };
    445         },
    446         else => return failDecl(c, fn_decl_loc, fn_name, "unable to resolve function type {}", .{fn_type.getTypeClass()}),
    447     };
    448 
    449     if (!decl_ctx.has_body) {
    450         return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base));
    451     }
    452 
    453     // actual function definition with body
    454     const body_stmt = fn_decl.getBody();
    455     var block_scope = try Scope.Block.init(c, &c.global_scope.base, false);
    456     block_scope.return_type = return_qt;
    457     defer block_scope.deinit();
    458 
    459     const scope = &block_scope.base;
    460 
    461     var param_id: c_uint = 0;
    462     for (proto_node.data.params) |*param| {
    463         const param_name = param.name orelse {
    464             proto_node.data.is_extern = true;
    465             proto_node.data.is_export = false;
    466             proto_node.data.is_inline = false;
    467             try warn(c, &c.global_scope.base, fn_decl_loc, "function {s} parameter has no name, demoted to extern", .{fn_name});
    468             return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base));
    469         };
    470 
    471         const c_param = fn_decl.getParamDecl(param_id);
    472         const qual_type = c_param.getOriginalType();
    473         const is_const = qual_type.isConstQualified();
    474 
    475         const mangled_param_name = try block_scope.makeMangledName(c, param_name);
    476         param.name = mangled_param_name;
    477 
    478         if (!is_const) {
    479             const bare_arg_name = try std.fmt.allocPrint(c.arena, "arg_{s}", .{mangled_param_name});
    480             const arg_name = try block_scope.makeMangledName(c, bare_arg_name);
    481             param.name = arg_name;
    482 
    483             const redecl_node = try Tag.arg_redecl.create(c.arena, .{ .actual = mangled_param_name, .mangled = arg_name });
    484             try block_scope.statements.append(redecl_node);
    485         }
    486         try block_scope.discardVariable(c, mangled_param_name);
    487 
    488         param_id += 1;
    489     }
    490 
    491     const casted_body = @as(*const clang.CompoundStmt, @ptrCast(body_stmt));
    492     transCompoundStmtInline(c, casted_body, &block_scope) catch |err| switch (err) {
    493         error.OutOfMemory => |e| return e,
    494         error.UnsupportedTranslation,
    495         error.UnsupportedType,
    496         => {
    497             proto_node.data.is_extern = true;
    498             proto_node.data.is_export = false;
    499             proto_node.data.is_inline = false;
    500             try warn(c, &c.global_scope.base, fn_decl_loc, "unable to translate function, demoted to extern", .{});
    501             return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base));
    502         },
    503     };
    504     // add return statement if the function didn't have one
    505     blk: {
    506         const maybe_body = try block_scope.complete(c);
    507         if (fn_ty.getNoReturnAttr() or isAnyopaque(return_qt) or maybe_body.isNoreturn(false)) {
    508             proto_node.data.body = maybe_body;
    509             break :blk;
    510         }
    511 
    512         const rhs = transZeroInitExpr(c, scope, fn_decl_loc, return_qt.getTypePtr()) catch |err| switch (err) {
    513             error.OutOfMemory => |e| return e,
    514             error.UnsupportedTranslation,
    515             error.UnsupportedType,
    516             => {
    517                 proto_node.data.is_extern = true;
    518                 proto_node.data.is_export = false;
    519                 proto_node.data.is_inline = false;
    520                 try warn(c, &c.global_scope.base, fn_decl_loc, "unable to create a return value for function, demoted to extern", .{});
    521                 return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base));
    522             },
    523         };
    524         const ret = try Tag.@"return".create(c.arena, rhs);
    525         try block_scope.statements.append(ret);
    526         proto_node.data.body = try block_scope.complete(c);
    527     }
    528 
    529     return addTopLevelDecl(c, fn_name, Node.initPayload(&proto_node.base));
    530 }
    531 
    532 fn transQualTypeMaybeInitialized(c: *Context, scope: *Scope, qt: clang.QualType, decl_init: ?*const clang.Expr, loc: clang.SourceLocation) TransError!Node {
    533     return if (decl_init) |init_expr|
    534         transQualTypeInitialized(c, scope, qt, init_expr, loc)
    535     else
    536         transQualType(c, scope, qt, loc);
    537 }
    538 
    539 /// This is used in global scope to convert a string literal `S` to [*c]u8:
    540 /// &(struct {
    541 ///     var static = S.*;
    542 /// }).static;
    543 fn stringLiteralToCharStar(c: *Context, str: Node) Error!Node {
    544     const var_name = Scope.Block.static_inner_name;
    545 
    546     const variables = try c.arena.alloc(Node, 1);
    547     variables[0] = try Tag.mut_str.create(c.arena, .{ .name = var_name, .init = str });
    548 
    549     const anon_struct = try Tag.@"struct".create(c.arena, .{
    550         .layout = .none,
    551         .fields = &.{},
    552         .functions = &.{},
    553         .variables = variables,
    554     });
    555 
    556     const member_access = try Tag.field_access.create(c.arena, .{
    557         .lhs = anon_struct,
    558         .field_name = var_name,
    559     });
    560     return Tag.address_of.create(c.arena, member_access);
    561 }
    562 
    563 /// if mangled_name is not null, this var decl was declared in a block scope.
    564 fn visitVarDecl(c: *Context, var_decl: *const clang.VarDecl, mangled_name: ?[]const u8) Error!void {
    565     const var_name = mangled_name orelse try c.str(@as(*const clang.NamedDecl, @ptrCast(var_decl)).getName_bytes_begin());
    566     if (c.global_scope.sym_table.contains(var_name))
    567         return; // Avoid processing this decl twice
    568 
    569     const is_pub = mangled_name == null;
    570     const is_threadlocal = var_decl.getTLSKind() != .None;
    571     const scope = &c.global_scope.base;
    572     const var_decl_loc = var_decl.getLocation();
    573 
    574     const qual_type = var_decl.getTypeSourceInfo_getType();
    575     const storage_class = var_decl.getStorageClass();
    576     const has_init = var_decl.hasInit();
    577     const decl_init = var_decl.getInit();
    578     var is_const = qual_type.isConstQualified();
    579 
    580     // In C extern variables with initializers behave like Zig exports.
    581     // extern int foo = 2;
    582     // does the same as:
    583     // extern int foo;
    584     // int foo = 2;
    585     var is_extern = storage_class == .Extern and !has_init;
    586     var is_export = !is_extern and storage_class != .Static;
    587 
    588     if (!is_extern and qualTypeWasDemotedToOpaque(c, qual_type)) {
    589         return failDecl(c, var_decl_loc, var_name, "non-extern variable has opaque type", .{});
    590     }
    591 
    592     const type_node = transQualTypeMaybeInitialized(c, scope, qual_type, decl_init, var_decl_loc) catch |err| switch (err) {
    593         error.UnsupportedTranslation, error.UnsupportedType => {
    594             return failDecl(c, var_decl_loc, var_name, "unable to resolve variable type", .{});
    595         },
    596         error.OutOfMemory => |e| return e,
    597     };
    598 
    599     var init_node: ?Node = null;
    600 
    601     // If the initialization expression is not present, initialize with undefined.
    602     // If it is an integer literal, we can skip the @as since it will be redundant
    603     // with the variable type.
    604     if (has_init) trans_init: {
    605         if (decl_init) |expr| {
    606             const node_or_error = if (expr.getStmtClass() == .StringLiteralClass)
    607                 transStringLiteralInitializer(c, @as(*const clang.StringLiteral, @ptrCast(expr)), type_node)
    608             else
    609                 transExprCoercing(c, scope, expr, .used);
    610             init_node = node_or_error catch |err| switch (err) {
    611                 error.UnsupportedTranslation,
    612                 error.UnsupportedType,
    613                 => {
    614                     is_extern = true;
    615                     is_export = false;
    616                     try warn(c, scope, var_decl_loc, "unable to translate variable initializer, demoted to extern", .{});
    617                     break :trans_init;
    618                 },
    619                 error.OutOfMemory => |e| return e,
    620             };
    621             if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node.?)) {
    622                 init_node = try Tag.int_from_bool.create(c.arena, init_node.?);
    623             } else if (init_node.?.tag() == .string_literal and qualTypeIsCharStar(qual_type)) {
    624                 init_node = try stringLiteralToCharStar(c, init_node.?);
    625             }
    626         } else {
    627             init_node = Tag.undefined_literal.init();
    628         }
    629     } else if (storage_class != .Extern) {
    630         // The C language specification states that variables with static or threadlocal
    631         // storage without an initializer are initialized to a zero value.
    632 
    633         // std.mem.zeroes(T)
    634         init_node = try Tag.std_mem_zeroes.create(c.arena, type_node);
    635     } else if (qual_type.getTypeClass() == .IncompleteArray) {
    636         // Oh no, an extern array of unknown size! These are really fun because there's no
    637         // direct equivalent in Zig. To translate correctly, we'll have to create a C-pointer
    638         // to the data initialized via @extern.
    639 
    640         const name_str = try std.fmt.allocPrint(c.arena, "\"{s}\"", .{var_name});
    641         init_node = try Tag.builtin_extern.create(c.arena, .{
    642             .type = type_node,
    643             .name = try Tag.string_literal.create(c.arena, name_str),
    644         });
    645 
    646         // Since this is really a pointer to the underlying data, we tweak a few properties.
    647         is_extern = false;
    648         is_const = true;
    649     }
    650 
    651     const linksection_string = blk: {
    652         var str_len: usize = undefined;
    653         if (var_decl.getSectionAttribute(&str_len)) |str_ptr| {
    654             break :blk str_ptr[0..str_len];
    655         }
    656         break :blk null;
    657     };
    658 
    659     const node = try Tag.var_decl.create(c.arena, .{
    660         .is_pub = is_pub,
    661         .is_const = is_const,
    662         .is_extern = is_extern,
    663         .is_export = is_export,
    664         .is_threadlocal = is_threadlocal,
    665         .linksection_string = linksection_string,
    666         .alignment = ClangAlignment.forVar(c, var_decl).zigAlignment(),
    667         .name = var_name,
    668         .type = type_node,
    669         .init = init_node,
    670     });
    671     return addTopLevelDecl(c, var_name, node);
    672 }
    673 
    674 const builtin_typedef_map = std.StaticStringMap([]const u8).initComptime(.{
    675     .{ "uint8_t", "u8" },
    676     .{ "int8_t", "i8" },
    677     .{ "uint16_t", "u16" },
    678     .{ "int16_t", "i16" },
    679     .{ "uint32_t", "u32" },
    680     .{ "int32_t", "i32" },
    681     .{ "uint64_t", "u64" },
    682     .{ "int64_t", "i64" },
    683     .{ "intptr_t", "isize" },
    684     .{ "uintptr_t", "usize" },
    685     .{ "ssize_t", "isize" },
    686     .{ "size_t", "usize" },
    687 });
    688 
    689 fn transTypeDef(c: *Context, scope: *Scope, typedef_decl: *const clang.TypedefNameDecl) Error!void {
    690     if (c.decl_table.get(@intFromPtr(typedef_decl.getCanonicalDecl()))) |_|
    691         return; // Avoid processing this decl twice
    692     const toplevel = scope.id == .root;
    693     const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;
    694 
    695     var name: []const u8 = try c.str(@as(*const clang.NamedDecl, @ptrCast(typedef_decl)).getName_bytes_begin());
    696     try c.typedefs.put(c.gpa, name, {});
    697 
    698     if (builtin_typedef_map.get(name)) |builtin| {
    699         return c.decl_table.putNoClobber(c.gpa, @intFromPtr(typedef_decl.getCanonicalDecl()), builtin);
    700     }
    701     if (!toplevel) name = try bs.makeMangledName(c, name);
    702     try c.decl_table.putNoClobber(c.gpa, @intFromPtr(typedef_decl.getCanonicalDecl()), name);
    703 
    704     const child_qt = typedef_decl.getUnderlyingType();
    705     const typedef_loc = typedef_decl.getLocation();
    706     const init_node = transQualType(c, scope, child_qt, typedef_loc) catch |err| switch (err) {
    707         error.UnsupportedType => {
    708             return failDecl(c, typedef_loc, name, "unable to resolve typedef child type", .{});
    709         },
    710         error.OutOfMemory => |e| return e,
    711     };
    712 
    713     const payload = try c.arena.create(ast.Payload.SimpleVarDecl);
    714     payload.* = .{
    715         .base = .{ .tag = ([2]Tag{ .var_simple, .pub_var_simple })[@intFromBool(toplevel)] },
    716         .data = .{
    717             .name = name,
    718             .init = init_node,
    719         },
    720     };
    721     const node = Node.initPayload(&payload.base);
    722 
    723     if (toplevel) {
    724         try addTopLevelDecl(c, name, node);
    725     } else {
    726         try scope.appendNode(node);
    727         if (node.tag() != .pub_var_simple) {
    728             try bs.discardVariable(c, name);
    729         }
    730     }
    731 }
    732 
    733 /// Build a getter function for a flexible array member at the end of a C struct
    734 /// e.g. `T items[]` or `T items[0]`. The generated function returns a [*c] pointer
    735 /// to the flexible array with the correct const and volatile qualifiers
    736 fn buildFlexibleArrayFn(
    737     c: *Context,
    738     scope: *Scope,
    739     layout: *const clang.ASTRecordLayout,
    740     field_name: []const u8,
    741     field_decl: *const clang.FieldDecl,
    742 ) TypeError!Node {
    743     const field_qt = field_decl.getType();
    744     const field_qt_canon = qualTypeCanon(field_qt);
    745 
    746     const u8_type = try Tag.type.create(c.arena, "u8");
    747     const self_param_name = "self";
    748     const self_param = try Tag.identifier.create(c.arena, self_param_name);
    749     const self_type = try Tag.typeof.create(c.arena, self_param);
    750 
    751     const fn_params = try c.arena.alloc(ast.Payload.Param, 1);
    752 
    753     fn_params[0] = .{
    754         .name = self_param_name,
    755         .type = Tag.@"anytype".init(),
    756         .is_noalias = false,
    757     };
    758 
    759     const array_type = @as(*const clang.ArrayType, @ptrCast(field_qt_canon));
    760     const element_qt = array_type.getElementType();
    761     const element_type = try transQualType(c, scope, element_qt, field_decl.getLocation());
    762 
    763     var block_scope = try Scope.Block.init(c, scope, false);
    764     defer block_scope.deinit();
    765 
    766     const intermediate_type_name = try block_scope.makeMangledName(c, "Intermediate");
    767     const intermediate_type = try Tag.helpers_flexible_array_type.create(c.arena, .{ .lhs = self_type, .rhs = u8_type });
    768     const intermediate_type_decl = try Tag.var_simple.create(c.arena, .{
    769         .name = intermediate_type_name,
    770         .init = intermediate_type,
    771     });
    772     try block_scope.statements.append(intermediate_type_decl);
    773     const intermediate_type_ident = try Tag.identifier.create(c.arena, intermediate_type_name);
    774 
    775     const return_type_name = try block_scope.makeMangledName(c, "ReturnType");
    776     const return_type = try Tag.helpers_flexible_array_type.create(c.arena, .{ .lhs = self_type, .rhs = element_type });
    777     const return_type_decl = try Tag.var_simple.create(c.arena, .{
    778         .name = return_type_name,
    779         .init = return_type,
    780     });
    781     try block_scope.statements.append(return_type_decl);
    782     const return_type_ident = try Tag.identifier.create(c.arena, return_type_name);
    783 
    784     const field_index = field_decl.getFieldIndex();
    785     const bit_offset = layout.getFieldOffset(field_index); // this is a target-specific constant based on the struct layout
    786     const byte_offset = bit_offset / 8;
    787 
    788     const casted_self = try Tag.as.create(c.arena, .{
    789         .lhs = intermediate_type_ident,
    790         .rhs = try Tag.ptr_cast.create(c.arena, self_param),
    791     });
    792     const field_offset = try transCreateNodeNumber(c, byte_offset, .int);
    793     const field_ptr = try Tag.add.create(c.arena, .{ .lhs = casted_self, .rhs = field_offset });
    794 
    795     const ptr_cast = try Tag.as.create(c.arena, .{
    796         .lhs = return_type_ident,
    797         .rhs = try Tag.ptr_cast.create(
    798             c.arena,
    799             try Tag.align_cast.create(
    800                 c.arena,
    801                 field_ptr,
    802             ),
    803         ),
    804     });
    805     const return_stmt = try Tag.@"return".create(c.arena, ptr_cast);
    806     try block_scope.statements.append(return_stmt);
    807 
    808     const payload = try c.arena.create(ast.Payload.Func);
    809     payload.* = .{
    810         .base = .{ .tag = .func },
    811         .data = .{
    812             .is_pub = true,
    813             .is_extern = false,
    814             .is_export = false,
    815             .is_inline = false,
    816             .is_var_args = false,
    817             .name = field_name,
    818             .linksection_string = null,
    819             .explicit_callconv = null,
    820             .params = fn_params,
    821             .return_type = return_type,
    822             .body = try block_scope.complete(c),
    823             .alignment = null,
    824         },
    825     };
    826     return Node.initPayload(&payload.base);
    827 }
    828 
    829 /// Return true if `field_decl` is the flexible array field for its parent record
    830 fn isFlexibleArrayFieldDecl(c: *Context, field_decl: *const clang.FieldDecl) bool {
    831     const record_decl = field_decl.getParent() orelse return false;
    832     const record_flexible_field = flexibleArrayField(c, record_decl) orelse return false;
    833     return field_decl == record_flexible_field;
    834 }
    835 
    836 /// Find the flexible array field for a record if any. A flexible array field is an
    837 /// incomplete or zero-length array that occurs as the last field of a record.
    838 /// clang's RecordDecl::hasFlexibleArrayMember is not suitable for determining
    839 /// this because it returns false for a record that ends with a zero-length
    840 /// array, but we consider those to be flexible arrays
    841 fn flexibleArrayField(c: *Context, record_def: *const clang.RecordDecl) ?*const clang.FieldDecl {
    842     var it = record_def.field_begin();
    843     const end_it = record_def.field_end();
    844     var flexible_field: ?*const clang.FieldDecl = null;
    845     while (it.neq(end_it)) : (it = it.next()) {
    846         const field_decl = it.deref();
    847         const ty = qualTypeCanon(field_decl.getType());
    848         const incomplete_or_zero_size = ty.isIncompleteOrZeroLengthArrayType(c.clang_context);
    849         if (incomplete_or_zero_size) {
    850             flexible_field = field_decl;
    851         } else {
    852             flexible_field = null;
    853         }
    854     }
    855     return flexible_field;
    856 }
    857 
    858 fn mangleWeakGlobalName(c: *Context, want_name: []const u8) ![]const u8 {
    859     var cur_name = want_name;
    860 
    861     if (!c.weak_global_names.contains(want_name)) {
    862         // This type wasn't noticed by the name detection pass, so nothing has been treating this as
    863         // a weak global name. We must mangle it to avoid conflicts with locals.
    864         cur_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ want_name, c.getMangle() });
    865     }
    866 
    867     while (c.global_names.contains(cur_name)) {
    868         cur_name = try std.fmt.allocPrint(c.arena, "{s}_{d}", .{ want_name, c.getMangle() });
    869     }
    870     return cur_name;
    871 }
    872 
    873 fn transRecordDecl(c: *Context, scope: *Scope, record_decl: *const clang.RecordDecl) Error!void {
    874     if (c.decl_table.get(@intFromPtr(record_decl.getCanonicalDecl()))) |_|
    875         return; // Avoid processing this decl twice
    876     const record_loc = record_decl.getLocation();
    877     const toplevel = scope.id == .root;
    878     const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;
    879 
    880     var is_union = false;
    881     var container_kind_name: []const u8 = undefined;
    882     var bare_name: []const u8 = try c.str(@as(*const clang.NamedDecl, @ptrCast(record_decl)).getName_bytes_begin());
    883 
    884     if (record_decl.isUnion()) {
    885         container_kind_name = "union";
    886         is_union = true;
    887     } else if (record_decl.isStruct()) {
    888         container_kind_name = "struct";
    889     } else {
    890         try c.decl_table.putNoClobber(c.gpa, @intFromPtr(record_decl.getCanonicalDecl()), bare_name);
    891         return failDecl(c, record_loc, bare_name, "record {s} is not a struct or union", .{bare_name});
    892     }
    893 
    894     var is_unnamed = false;
    895     var name = bare_name;
    896     if (c.unnamed_typedefs.get(@intFromPtr(record_decl.getCanonicalDecl()))) |typedef_name| {
    897         bare_name = typedef_name;
    898         name = typedef_name;
    899     } else {
    900         // Record declarations such as `struct {...} x` have no name but they're not
    901         // anonymous hence here isAnonymousStructOrUnion is not needed
    902         if (bare_name.len == 0) {
    903             bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()});
    904             is_unnamed = true;
    905         }
    906         name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ container_kind_name, bare_name });
    907         if (toplevel and !is_unnamed) {
    908             name = try mangleWeakGlobalName(c, name);
    909         }
    910     }
    911     if (!toplevel) name = try bs.makeMangledName(c, name);
    912     try c.decl_table.putNoClobber(c.gpa, @intFromPtr(record_decl.getCanonicalDecl()), name);
    913 
    914     const is_pub = toplevel and !is_unnamed;
    915     const init_node = blk: {
    916         const record_def = record_decl.getDefinition() orelse {
    917             try c.opaque_demotes.put(c.gpa, @intFromPtr(record_decl.getCanonicalDecl()), {});
    918             break :blk Tag.opaque_literal.init();
    919         };
    920 
    921         var fields = std.ArrayList(ast.Payload.Record.Field).init(c.gpa);
    922         defer fields.deinit();
    923 
    924         var functions = std.ArrayList(Node).init(c.gpa);
    925         defer functions.deinit();
    926 
    927         const flexible_field = flexibleArrayField(c, record_def);
    928         var unnamed_field_count: u32 = 0;
    929         var it = record_def.field_begin();
    930         const end_it = record_def.field_end();
    931         const layout = record_def.getASTRecordLayout(c.clang_context);
    932         const record_alignment = layout.getAlignment();
    933 
    934         while (it.neq(end_it)) : (it = it.next()) {
    935             const field_decl = it.deref();
    936             const field_loc = field_decl.getLocation();
    937             const field_qt = field_decl.getType();
    938 
    939             if (field_decl.isBitField()) {
    940                 try c.opaque_demotes.put(c.gpa, @intFromPtr(record_decl.getCanonicalDecl()), {});
    941                 try warn(c, scope, field_loc, "{s} demoted to opaque type - has bitfield", .{container_kind_name});
    942                 break :blk Tag.opaque_literal.init();
    943             }
    944 
    945             var is_anon = false;
    946             var field_name = try c.str(@as(*const clang.NamedDecl, @ptrCast(field_decl)).getName_bytes_begin());
    947             if (field_decl.isAnonymousStructOrUnion() or field_name.len == 0) {
    948                 // Context.getMangle() is not used here because doing so causes unpredictable field names for anonymous fields.
    949                 field_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{unnamed_field_count});
    950                 unnamed_field_count += 1;
    951                 is_anon = true;
    952             }
    953             if (flexible_field == field_decl) {
    954                 const flexible_array_fn = buildFlexibleArrayFn(c, scope, layout, field_name, field_decl) catch |err| switch (err) {
    955                     error.UnsupportedType => {
    956                         try c.opaque_demotes.put(c.gpa, @intFromPtr(record_decl.getCanonicalDecl()), {});
    957                         try warn(c, scope, record_loc, "{s} demoted to opaque type - unable to translate type of flexible array field {s}", .{ container_kind_name, field_name });
    958                         break :blk Tag.opaque_literal.init();
    959                     },
    960                     else => |e| return e,
    961                 };
    962                 try functions.append(flexible_array_fn);
    963                 continue;
    964             }
    965             const field_type = transQualType(c, scope, field_qt, field_loc) catch |err| switch (err) {
    966                 error.UnsupportedType => {
    967                     try c.opaque_demotes.put(c.gpa, @intFromPtr(record_decl.getCanonicalDecl()), {});
    968                     try warn(c, scope, record_loc, "{s} demoted to opaque type - unable to translate type of field {s}", .{ container_kind_name, field_name });
    969                     break :blk Tag.opaque_literal.init();
    970                 },
    971                 else => |e| return e,
    972             };
    973 
    974             const alignment = if (flexible_field != null and field_decl.getFieldIndex() == 0)
    975                 @as(c_uint, @intCast(record_alignment))
    976             else
    977                 ClangAlignment.forField(c, field_decl, record_def).zigAlignment();
    978 
    979             // C99 introduced designated initializers for structs. Omitted fields are implicitly
    980             // initialized to zero. Some C APIs are designed with this in mind. Defaulting to zero
    981             // values for translated struct fields permits Zig code to comfortably use such an API.
    982             const default_value = if (record_decl.isStruct())
    983                 try Tag.std_mem_zeroes.create(c.arena, field_type)
    984             else
    985                 null;
    986 
    987             if (is_anon) {
    988                 try c.decl_table.putNoClobber(c.gpa, @intFromPtr(field_decl.getCanonicalDecl()), field_name);
    989             }
    990 
    991             try fields.append(.{
    992                 .name = field_name,
    993                 .type = field_type,
    994                 .alignment = alignment,
    995                 .default_value = default_value,
    996             });
    997         }
    998 
    999         const record_payload = try c.arena.create(ast.Payload.Record);
   1000         record_payload.* = .{
   1001             .base = .{ .tag = ([2]Tag{ .@"struct", .@"union" })[@intFromBool(is_union)] },
   1002             .data = .{
   1003                 .layout = .@"extern",
   1004                 .fields = try c.arena.dupe(ast.Payload.Record.Field, fields.items),
   1005                 .functions = try c.arena.dupe(Node, functions.items),
   1006                 .variables = &.{},
   1007             },
   1008         };
   1009         break :blk Node.initPayload(&record_payload.base);
   1010     };
   1011 
   1012     const payload = try c.arena.create(ast.Payload.SimpleVarDecl);
   1013     payload.* = .{
   1014         .base = .{ .tag = ([2]Tag{ .var_simple, .pub_var_simple })[@intFromBool(is_pub)] },
   1015         .data = .{
   1016             .name = name,
   1017             .init = init_node,
   1018         },
   1019     };
   1020     const node = Node.initPayload(&payload.base);
   1021     if (toplevel) {
   1022         try addTopLevelDecl(c, name, node);
   1023         // Only add the alias if the name is available *and* it was caught by
   1024         // name detection. Don't bother performing a weak mangle, since a
   1025         // mangled name is of no real use here.
   1026         if (!is_unnamed and !c.global_names.contains(bare_name) and c.weak_global_names.contains(bare_name))
   1027             try c.alias_list.append(.{ .alias = bare_name, .name = name });
   1028     } else {
   1029         try scope.appendNode(node);
   1030         if (node.tag() != .pub_var_simple) {
   1031             try bs.discardVariable(c, name);
   1032         }
   1033     }
   1034 }
   1035 
   1036 fn transEnumDecl(c: *Context, scope: *Scope, enum_decl: *const clang.EnumDecl) Error!void {
   1037     if (c.decl_table.get(@intFromPtr(enum_decl.getCanonicalDecl()))) |_|
   1038         return; // Avoid processing this decl twice
   1039     const enum_loc = enum_decl.getLocation();
   1040     const toplevel = scope.id == .root;
   1041     const bs: *Scope.Block = if (!toplevel) try scope.findBlockScope(c) else undefined;
   1042 
   1043     var is_unnamed = false;
   1044     var bare_name: []const u8 = try c.str(@as(*const clang.NamedDecl, @ptrCast(enum_decl)).getName_bytes_begin());
   1045     var name = bare_name;
   1046     if (c.unnamed_typedefs.get(@intFromPtr(enum_decl.getCanonicalDecl()))) |typedef_name| {
   1047         bare_name = typedef_name;
   1048         name = typedef_name;
   1049     } else {
   1050         if (bare_name.len == 0) {
   1051             bare_name = try std.fmt.allocPrint(c.arena, "unnamed_{d}", .{c.getMangle()});
   1052             is_unnamed = true;
   1053         }
   1054         name = try std.fmt.allocPrint(c.arena, "enum_{s}", .{bare_name});
   1055         if (toplevel and !is_unnamed) {
   1056             name = try mangleWeakGlobalName(c, name);
   1057         }
   1058     }
   1059     if (!toplevel) name = try bs.makeMangledName(c, name);
   1060     try c.decl_table.putNoClobber(c.gpa, @intFromPtr(enum_decl.getCanonicalDecl()), name);
   1061 
   1062     const enum_type_node = if (enum_decl.getDefinition()) |enum_def| blk: {
   1063         var it = enum_def.enumerator_begin();
   1064         const end_it = enum_def.enumerator_end();
   1065         while (it.neq(end_it)) : (it = it.next()) {
   1066             const enum_const = it.deref();
   1067             var enum_val_name: []const u8 = try c.str(@as(*const clang.NamedDecl, @ptrCast(enum_const)).getName_bytes_begin());
   1068             if (!toplevel) {
   1069                 enum_val_name = try bs.makeMangledName(c, enum_val_name);
   1070             }
   1071 
   1072             const enum_const_qt = @as(*const clang.ValueDecl, @ptrCast(enum_const)).getType();
   1073             const enum_const_loc = @as(*const clang.Decl, @ptrCast(enum_const)).getLocation();
   1074             const enum_const_type_node: ?Node = transQualType(c, scope, enum_const_qt, enum_const_loc) catch |err| switch (err) {
   1075                 error.UnsupportedType => null,
   1076                 else => |e| return e,
   1077             };
   1078 
   1079             const enum_const_def = try Tag.enum_constant.create(c.arena, .{
   1080                 .name = enum_val_name,
   1081                 .is_public = toplevel,
   1082                 .type = enum_const_type_node,
   1083                 // TODO: as of LLVM 18, the return value from `enum_const.getInitVal` here needs
   1084                 // to be freed with a call to its free() method.
   1085                 .value = try transCreateNodeAPInt(c, enum_const.getInitVal()),
   1086             });
   1087             if (toplevel)
   1088                 try addTopLevelDecl(c, enum_val_name, enum_const_def)
   1089             else {
   1090                 try scope.appendNode(enum_const_def);
   1091                 try bs.discardVariable(c, enum_val_name);
   1092             }
   1093         }
   1094 
   1095         const int_type = enum_decl.getIntegerType();
   1096         // The underlying type may be null in case of forward-declared enum
   1097         // types, while that's not ISO-C compliant many compilers allow this and
   1098         // default to the usual integer type used for all the enums.
   1099 
   1100         // default to c_int since msvc and gcc default to different types
   1101         break :blk if (int_type.ptr != null)
   1102             transQualType(c, scope, int_type, enum_loc) catch |err| switch (err) {
   1103                 error.UnsupportedType => {
   1104                     return failDecl(c, enum_loc, name, "unable to translate enum integer type", .{});
   1105                 },
   1106                 else => |e| return e,
   1107             }
   1108         else
   1109             try Tag.type.create(c.arena, "c_int");
   1110     } else blk: {
   1111         try c.opaque_demotes.put(c.gpa, @intFromPtr(enum_decl.getCanonicalDecl()), {});
   1112         break :blk Tag.opaque_literal.init();
   1113     };
   1114 
   1115     const is_pub = toplevel and !is_unnamed;
   1116     const payload = try c.arena.create(ast.Payload.SimpleVarDecl);
   1117     payload.* = .{
   1118         .base = .{ .tag = ([2]Tag{ .var_simple, .pub_var_simple })[@intFromBool(is_pub)] },
   1119         .data = .{
   1120             .init = enum_type_node,
   1121             .name = name,
   1122         },
   1123     };
   1124     const node = Node.initPayload(&payload.base);
   1125     if (toplevel) {
   1126         try addTopLevelDecl(c, name, node);
   1127         // Only add the alias if the name is available *and* it was caught by
   1128         // name detection. Don't bother performing a weak mangle, since a
   1129         // mangled name is of no real use here.
   1130         if (!is_unnamed and !c.global_names.contains(bare_name) and c.weak_global_names.contains(bare_name))
   1131             try c.alias_list.append(.{ .alias = bare_name, .name = name });
   1132     } else {
   1133         try scope.appendNode(node);
   1134         if (node.tag() != .pub_var_simple) {
   1135             try bs.discardVariable(c, name);
   1136         }
   1137     }
   1138 }
   1139 
   1140 fn transStmt(
   1141     c: *Context,
   1142     scope: *Scope,
   1143     stmt: *const clang.Stmt,
   1144     result_used: ResultUsed,
   1145 ) TransError!Node {
   1146     const sc = stmt.getStmtClass();
   1147     switch (sc) {
   1148         .BinaryOperatorClass => return transBinaryOperator(c, scope, @as(*const clang.BinaryOperator, @ptrCast(stmt)), result_used),
   1149         .CompoundStmtClass => return transCompoundStmt(c, scope, @as(*const clang.CompoundStmt, @ptrCast(stmt))),
   1150         .CStyleCastExprClass => return transCStyleCastExprClass(c, scope, @as(*const clang.CStyleCastExpr, @ptrCast(stmt)), result_used),
   1151         .DeclStmtClass => return transDeclStmt(c, scope, @as(*const clang.DeclStmt, @ptrCast(stmt))),
   1152         .DeclRefExprClass => return transDeclRefExpr(c, scope, @as(*const clang.DeclRefExpr, @ptrCast(stmt))),
   1153         .ImplicitCastExprClass => return transImplicitCastExpr(c, scope, @as(*const clang.ImplicitCastExpr, @ptrCast(stmt)), result_used),
   1154         .IntegerLiteralClass => return transIntegerLiteral(c, scope, @as(*const clang.IntegerLiteral, @ptrCast(stmt)), result_used, .with_as),
   1155         .ReturnStmtClass => return transReturnStmt(c, scope, @as(*const clang.ReturnStmt, @ptrCast(stmt))),
   1156         .StringLiteralClass => return transStringLiteral(c, scope, @as(*const clang.StringLiteral, @ptrCast(stmt)), result_used),
   1157         .ParenExprClass => {
   1158             const expr = try transExpr(c, scope, @as(*const clang.ParenExpr, @ptrCast(stmt)).getSubExpr(), .used);
   1159             return maybeSuppressResult(c, result_used, expr);
   1160         },
   1161         .InitListExprClass => return transInitListExpr(c, scope, @as(*const clang.InitListExpr, @ptrCast(stmt)), result_used),
   1162         .ImplicitValueInitExprClass => return transImplicitValueInitExpr(c, scope, @as(*const clang.Expr, @ptrCast(stmt))),
   1163         .IfStmtClass => return transIfStmt(c, scope, @as(*const clang.IfStmt, @ptrCast(stmt))),
   1164         .WhileStmtClass => return transWhileLoop(c, scope, @as(*const clang.WhileStmt, @ptrCast(stmt))),
   1165         .DoStmtClass => return transDoWhileLoop(c, scope, @as(*const clang.DoStmt, @ptrCast(stmt))),
   1166         .NullStmtClass => {
   1167             return Tag.empty_block.init();
   1168         },
   1169         .ContinueStmtClass => return Tag.@"continue".init(),
   1170         .BreakStmtClass => return Tag.@"break".init(),
   1171         .ForStmtClass => return transForLoop(c, scope, @as(*const clang.ForStmt, @ptrCast(stmt))),
   1172         .FloatingLiteralClass => return transFloatingLiteral(c, @as(*const clang.FloatingLiteral, @ptrCast(stmt)), result_used),
   1173         .ConditionalOperatorClass => {
   1174             return transConditionalOperator(c, scope, @as(*const clang.ConditionalOperator, @ptrCast(stmt)), result_used);
   1175         },
   1176         .BinaryConditionalOperatorClass => {
   1177             return transBinaryConditionalOperator(c, scope, @as(*const clang.BinaryConditionalOperator, @ptrCast(stmt)), result_used);
   1178         },
   1179         .SwitchStmtClass => return transSwitch(c, scope, @as(*const clang.SwitchStmt, @ptrCast(stmt))),
   1180         .CaseStmtClass, .DefaultStmtClass => {
   1181             return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO complex switch", .{});
   1182         },
   1183         .ConstantExprClass => return transConstantExpr(c, scope, @as(*const clang.Expr, @ptrCast(stmt)), result_used),
   1184         .PredefinedExprClass => return transPredefinedExpr(c, scope, @as(*const clang.PredefinedExpr, @ptrCast(stmt)), result_used),
   1185         .CharacterLiteralClass => return transCharLiteral(c, scope, @as(*const clang.CharacterLiteral, @ptrCast(stmt)), result_used, .with_as),
   1186         .StmtExprClass => return transStmtExpr(c, scope, @as(*const clang.StmtExpr, @ptrCast(stmt)), result_used),
   1187         .MemberExprClass => return transMemberExpr(c, scope, @as(*const clang.MemberExpr, @ptrCast(stmt)), result_used),
   1188         .ArraySubscriptExprClass => return transArrayAccess(c, scope, @as(*const clang.ArraySubscriptExpr, @ptrCast(stmt)), result_used),
   1189         .CallExprClass => return transCallExpr(c, scope, @as(*const clang.CallExpr, @ptrCast(stmt)), result_used),
   1190         .UnaryExprOrTypeTraitExprClass => return transUnaryExprOrTypeTraitExpr(c, scope, @as(*const clang.UnaryExprOrTypeTraitExpr, @ptrCast(stmt)), result_used),
   1191         .UnaryOperatorClass => return transUnaryOperator(c, scope, @as(*const clang.UnaryOperator, @ptrCast(stmt)), result_used),
   1192         .CompoundAssignOperatorClass => return transCompoundAssignOperator(c, scope, @as(*const clang.CompoundAssignOperator, @ptrCast(stmt)), result_used),
   1193         .OpaqueValueExprClass => {
   1194             const source_expr = @as(*const clang.OpaqueValueExpr, @ptrCast(stmt)).getSourceExpr().?;
   1195             const expr = try transExpr(c, scope, source_expr, .used);
   1196             return maybeSuppressResult(c, result_used, expr);
   1197         },
   1198         .OffsetOfExprClass => return transOffsetOfExpr(c, @as(*const clang.OffsetOfExpr, @ptrCast(stmt)), result_used),
   1199         .CompoundLiteralExprClass => {
   1200             const compound_literal = @as(*const clang.CompoundLiteralExpr, @ptrCast(stmt));
   1201             return transExpr(c, scope, compound_literal.getInitializer(), result_used);
   1202         },
   1203         .GenericSelectionExprClass => {
   1204             const gen_sel = @as(*const clang.GenericSelectionExpr, @ptrCast(stmt));
   1205             return transExpr(c, scope, gen_sel.getResultExpr(), result_used);
   1206         },
   1207         .ConvertVectorExprClass => {
   1208             const conv_vec = @as(*const clang.ConvertVectorExpr, @ptrCast(stmt));
   1209             const conv_vec_node = try transConvertVectorExpr(c, scope, conv_vec);
   1210             return maybeSuppressResult(c, result_used, conv_vec_node);
   1211         },
   1212         .ShuffleVectorExprClass => {
   1213             const shuffle_vec_expr = @as(*const clang.ShuffleVectorExpr, @ptrCast(stmt));
   1214             const shuffle_vec_node = try transShuffleVectorExpr(c, scope, shuffle_vec_expr);
   1215             return maybeSuppressResult(c, result_used, shuffle_vec_node);
   1216         },
   1217         .ChooseExprClass => {
   1218             const choose_expr = @as(*const clang.ChooseExpr, @ptrCast(stmt));
   1219             return transExpr(c, scope, choose_expr.getChosenSubExpr(), result_used);
   1220         },
   1221         // When adding new cases here, see comment for maybeBlockify()
   1222         .GCCAsmStmtClass,
   1223         .GotoStmtClass,
   1224         .IndirectGotoStmtClass,
   1225         .AttributedStmtClass,
   1226         .AddrLabelExprClass,
   1227         .AtomicExprClass,
   1228         .BlockExprClass,
   1229         .UserDefinedLiteralClass,
   1230         .BuiltinBitCastExprClass,
   1231         .DesignatedInitExprClass,
   1232         .LabelStmtClass,
   1233         => return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "TODO implement translation of stmt class {s}", .{@tagName(sc)}),
   1234         else => return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "unsupported stmt class {s}", .{@tagName(sc)}),
   1235     }
   1236 }
   1237 
   1238 /// See https://clang.llvm.org/docs/LanguageExtensions.html#langext-builtin-convertvector
   1239 fn transConvertVectorExpr(
   1240     c: *Context,
   1241     scope: *Scope,
   1242     expr: *const clang.ConvertVectorExpr,
   1243 ) TransError!Node {
   1244     const base_stmt = @as(*const clang.Stmt, @ptrCast(expr));
   1245 
   1246     var block_scope = try Scope.Block.init(c, scope, true);
   1247     defer block_scope.deinit();
   1248 
   1249     const src_expr = expr.getSrcExpr();
   1250     const src_type = qualTypeCanon(src_expr.getType());
   1251     const src_vector_ty = @as(*const clang.VectorType, @ptrCast(src_type));
   1252     const src_element_qt = src_vector_ty.getElementType();
   1253 
   1254     const src_expr_node = try transExpr(c, &block_scope.base, src_expr, .used);
   1255 
   1256     const dst_qt = expr.getTypeSourceInfo_getType();
   1257     const dst_type_node = try transQualType(c, &block_scope.base, dst_qt, base_stmt.getBeginLoc());
   1258     const dst_vector_ty = @as(*const clang.VectorType, @ptrCast(qualTypeCanon(dst_qt)));
   1259     const num_elements = dst_vector_ty.getNumElements();
   1260     const dst_element_qt = dst_vector_ty.getElementType();
   1261 
   1262     // workaround for https://github.com/ziglang/zig/issues/8322
   1263     // we store the casted results into temp variables and use those
   1264     // to initialize the vector. Eventually we can just directly
   1265     // construct the init_list from casted source members
   1266     var i: usize = 0;
   1267     while (i < num_elements) : (i += 1) {
   1268         const mangled_name = try block_scope.makeMangledName(c, "tmp");
   1269         const value = try Tag.array_access.create(c.arena, .{
   1270             .lhs = src_expr_node,
   1271             .rhs = try transCreateNodeNumber(c, i, .int),
   1272         });
   1273         const tmp_decl_node = try Tag.var_simple.create(c.arena, .{
   1274             .name = mangled_name,
   1275             .init = try transCCast(c, &block_scope.base, base_stmt.getBeginLoc(), dst_element_qt, src_element_qt, value),
   1276         });
   1277         try block_scope.statements.append(tmp_decl_node);
   1278     }
   1279 
   1280     const init_list = try c.arena.alloc(Node, num_elements);
   1281     for (init_list, 0..) |*init, init_index| {
   1282         const tmp_decl = block_scope.statements.items[init_index];
   1283         const name = tmp_decl.castTag(.var_simple).?.data.name;
   1284         init.* = try Tag.identifier.create(c.arena, name);
   1285     }
   1286 
   1287     const vec_init = try Tag.array_init.create(c.arena, .{
   1288         .cond = dst_type_node,
   1289         .cases = init_list,
   1290     });
   1291 
   1292     const break_node = try Tag.break_val.create(c.arena, .{
   1293         .label = block_scope.label,
   1294         .val = vec_init,
   1295     });
   1296     try block_scope.statements.append(break_node);
   1297     return block_scope.complete(c);
   1298 }
   1299 
   1300 fn makeShuffleMask(c: *Context, scope: *Scope, expr: *const clang.ShuffleVectorExpr, vector_len: Node) TransError!Node {
   1301     const num_subexprs = expr.getNumSubExprs();
   1302     assert(num_subexprs >= 3); // two source vectors + at least 1 index expression
   1303     const mask_len = num_subexprs - 2;
   1304 
   1305     const mask_type = try Tag.vector.create(c.arena, .{
   1306         .lhs = try transCreateNodeNumber(c, mask_len, .int),
   1307         .rhs = try Tag.type.create(c.arena, "i32"),
   1308     });
   1309 
   1310     const init_list = try c.arena.alloc(Node, mask_len);
   1311 
   1312     for (init_list, 0..) |*init, i| {
   1313         const index_expr = try transExprCoercing(c, scope, expr.getExpr(@as(c_uint, @intCast(i + 2))), .used);
   1314         const converted_index = try Tag.helpers_shuffle_vector_index.create(c.arena, .{ .lhs = index_expr, .rhs = vector_len });
   1315         init.* = converted_index;
   1316     }
   1317 
   1318     return Tag.array_init.create(c.arena, .{
   1319         .cond = mask_type,
   1320         .cases = init_list,
   1321     });
   1322 }
   1323 
   1324 /// @typeInfo(@TypeOf(vec_node)).Vector.<field>
   1325 fn vectorTypeInfo(arena: mem.Allocator, vec_node: Node, field: []const u8) TransError!Node {
   1326     const typeof_call = try Tag.typeof.create(arena, vec_node);
   1327     const typeinfo_call = try Tag.typeinfo.create(arena, typeof_call);
   1328     const vector_type_info = try Tag.field_access.create(arena, .{ .lhs = typeinfo_call, .field_name = "Vector" });
   1329     return Tag.field_access.create(arena, .{ .lhs = vector_type_info, .field_name = field });
   1330 }
   1331 
   1332 fn transShuffleVectorExpr(
   1333     c: *Context,
   1334     scope: *Scope,
   1335     expr: *const clang.ShuffleVectorExpr,
   1336 ) TransError!Node {
   1337     const base_expr = @as(*const clang.Expr, @ptrCast(expr));
   1338     const num_subexprs = expr.getNumSubExprs();
   1339     if (num_subexprs < 3) return fail(c, error.UnsupportedTranslation, base_expr.getBeginLoc(), "ShuffleVector needs at least 1 index", .{});
   1340 
   1341     const a = try transExpr(c, scope, expr.getExpr(0), .used);
   1342     const b = try transExpr(c, scope, expr.getExpr(1), .used);
   1343 
   1344     // clang requires first two arguments to __builtin_shufflevector to be same type
   1345     const vector_child_type = try vectorTypeInfo(c.arena, a, "child");
   1346     const vector_len = try vectorTypeInfo(c.arena, a, "len");
   1347     const shuffle_mask = try makeShuffleMask(c, scope, expr, vector_len);
   1348 
   1349     return Tag.shuffle.create(c.arena, .{
   1350         .element_type = vector_child_type,
   1351         .a = a,
   1352         .b = b,
   1353         .mask_vector = shuffle_mask,
   1354     });
   1355 }
   1356 
   1357 /// Translate a "simple" offsetof expression containing exactly one component,
   1358 /// when that component is of kind .Field - e.g. offsetof(mytype, myfield)
   1359 fn transSimpleOffsetOfExpr(c: *Context, expr: *const clang.OffsetOfExpr) TransError!Node {
   1360     assert(expr.getNumComponents() == 1);
   1361     const component = expr.getComponent(0);
   1362     if (component.getKind() == .Field) {
   1363         const field_decl = component.getField();
   1364         if (field_decl.getParent()) |record_decl| {
   1365             if (c.decl_table.get(@intFromPtr(record_decl.getCanonicalDecl()))) |type_name| {
   1366                 const type_node = try Tag.type.create(c.arena, type_name);
   1367 
   1368                 const raw_field_name = try c.str(@as(*const clang.NamedDecl, @ptrCast(field_decl)).getName_bytes_begin());
   1369                 const quoted_field_name = try std.fmt.allocPrint(c.arena, "\"{s}\"", .{raw_field_name});
   1370                 const field_name_node = try Tag.string_literal.create(c.arena, quoted_field_name);
   1371 
   1372                 return Tag.offset_of.create(c.arena, .{
   1373                     .lhs = type_node,
   1374                     .rhs = field_name_node,
   1375                 });
   1376             }
   1377         }
   1378     }
   1379     return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "failed to translate simple OffsetOfExpr", .{});
   1380 }
   1381 
   1382 fn transOffsetOfExpr(
   1383     c: *Context,
   1384     expr: *const clang.OffsetOfExpr,
   1385     result_used: ResultUsed,
   1386 ) TransError!Node {
   1387     if (expr.getNumComponents() == 1) {
   1388         const offsetof_expr = try transSimpleOffsetOfExpr(c, expr);
   1389         return maybeSuppressResult(c, result_used, offsetof_expr);
   1390     }
   1391 
   1392     // TODO implement OffsetOfExpr with more than 1 component
   1393     // OffsetOfExpr API:
   1394     //     call expr.getComponent(idx) while idx < expr.getNumComponents()
   1395     //     component.getKind() will be either .Array or .Field (other kinds are C++-only)
   1396     //     if .Field, use component.getField() to retrieve *clang.FieldDecl
   1397     //     if .Array, use component.getArrayExprIndex() to get a c_uint which
   1398     //         can be passed to expr.getIndexExpr(expr_index) to get the *clang.Expr for the array index
   1399 
   1400     return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "TODO: implement complex OffsetOfExpr translation", .{});
   1401 }
   1402 
   1403 /// Cast a signed integer node to a usize, for use in pointer arithmetic. Negative numbers
   1404 /// will become very large positive numbers but that is ok since we only use this in
   1405 /// pointer arithmetic expressions, where wraparound will ensure we get the correct value.
   1406 /// node -> @bitCast(usize, @intCast(isize, node))
   1407 fn usizeCastForWrappingPtrArithmetic(gpa: mem.Allocator, node: Node) TransError!Node {
   1408     const intcast_node = try Tag.as.create(gpa, .{
   1409         .lhs = try Tag.type.create(gpa, "isize"),
   1410         .rhs = try Tag.int_cast.create(gpa, node),
   1411     });
   1412 
   1413     return Tag.as.create(gpa, .{
   1414         .lhs = try Tag.type.create(gpa, "usize"),
   1415         .rhs = try Tag.bit_cast.create(gpa, intcast_node),
   1416     });
   1417 }
   1418 
   1419 /// Translate an arithmetic expression with a pointer operand and a signed-integer operand.
   1420 /// Zig requires a usize argument for pointer arithmetic, so we intCast to isize and then
   1421 /// bitcast to usize; pointer wraparound make the math work.
   1422 /// Zig pointer addition is not commutative (unlike C); the pointer operand needs to be on the left.
   1423 /// The + operator in C is not a sequence point so it should be safe to switch the order if necessary.
   1424 fn transCreatePointerArithmeticSignedOp(
   1425     c: *Context,
   1426     scope: *Scope,
   1427     stmt: *const clang.BinaryOperator,
   1428     result_used: ResultUsed,
   1429 ) TransError!Node {
   1430     const is_add = stmt.getOpcode() == .Add;
   1431     const lhs = stmt.getLHS();
   1432     const rhs = stmt.getRHS();
   1433     const swap_operands = is_add and cIsSignedInteger(getExprQualType(c, lhs));
   1434 
   1435     const swizzled_lhs = if (swap_operands) rhs else lhs;
   1436     const swizzled_rhs = if (swap_operands) lhs else rhs;
   1437 
   1438     const lhs_node = try transExpr(c, scope, swizzled_lhs, .used);
   1439     const rhs_node = try transExpr(c, scope, swizzled_rhs, .used);
   1440 
   1441     const bitcast_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node);
   1442 
   1443     return transCreateNodeInfixOp(
   1444         c,
   1445         if (is_add) .add else .sub,
   1446         lhs_node,
   1447         bitcast_node,
   1448         result_used,
   1449     );
   1450 }
   1451 
   1452 fn transBinaryOperator(
   1453     c: *Context,
   1454     scope: *Scope,
   1455     stmt: *const clang.BinaryOperator,
   1456     result_used: ResultUsed,
   1457 ) TransError!Node {
   1458     const op = stmt.getOpcode();
   1459     const qt = stmt.getType();
   1460     const isPointerDiffExpr = cIsPointerDiffExpr(stmt);
   1461     switch (op) {
   1462         .Assign => return try transCreateNodeAssign(c, scope, result_used, stmt.getLHS(), stmt.getRHS()),
   1463         .Comma => {
   1464             var block_scope = try Scope.Block.init(c, scope, true);
   1465             defer block_scope.deinit();
   1466 
   1467             const lhs = try transExpr(c, &block_scope.base, stmt.getLHS(), .unused);
   1468             try block_scope.statements.append(lhs);
   1469 
   1470             const rhs = try transExpr(c, &block_scope.base, stmt.getRHS(), .used);
   1471             const break_node = try Tag.break_val.create(c.arena, .{
   1472                 .label = block_scope.label,
   1473                 .val = rhs,
   1474             });
   1475             try block_scope.statements.append(break_node);
   1476             const block_node = try block_scope.complete(c);
   1477             return maybeSuppressResult(c, result_used, block_node);
   1478         },
   1479         .Div => {
   1480             if (cIsSignedInteger(qt)) {
   1481                 // signed integer division uses @divTrunc
   1482                 const lhs = try transExpr(c, scope, stmt.getLHS(), .used);
   1483                 const rhs = try transExpr(c, scope, stmt.getRHS(), .used);
   1484                 const div_trunc = try Tag.div_trunc.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
   1485                 return maybeSuppressResult(c, result_used, div_trunc);
   1486             }
   1487         },
   1488         .Rem => {
   1489             if (cIsSignedInteger(qt)) {
   1490                 // signed integer remainder uses std.zig.c_translation.signedRemainder
   1491                 const lhs = try transExpr(c, scope, stmt.getLHS(), .used);
   1492                 const rhs = try transExpr(c, scope, stmt.getRHS(), .used);
   1493                 const rem = try Tag.signed_remainder.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
   1494                 return maybeSuppressResult(c, result_used, rem);
   1495             }
   1496         },
   1497         .Shl => {
   1498             return transCreateNodeShiftOp(c, scope, stmt, .shl, result_used);
   1499         },
   1500         .Shr => {
   1501             return transCreateNodeShiftOp(c, scope, stmt, .shr, result_used);
   1502         },
   1503         .LAnd => {
   1504             return transCreateNodeBoolInfixOp(c, scope, stmt, .@"and", result_used);
   1505         },
   1506         .LOr => {
   1507             return transCreateNodeBoolInfixOp(c, scope, stmt, .@"or", result_used);
   1508         },
   1509         .Add, .Sub => {
   1510             // `ptr + idx` and `idx + ptr` -> ptr + @bitCast(usize, @intCast(isize, idx))
   1511             // `ptr - idx` -> ptr - @bitCast(usize, @intCast(isize, idx))
   1512             if (qualTypeIsPtr(qt) and (cIsSignedInteger(getExprQualType(c, stmt.getLHS())) or
   1513                 cIsSignedInteger(getExprQualType(c, stmt.getRHS())))) return transCreatePointerArithmeticSignedOp(c, scope, stmt, result_used);
   1514         },
   1515         else => {},
   1516     }
   1517     var op_id: Tag = undefined;
   1518     switch (op) {
   1519         .Add => {
   1520             if (cIsUnsignedInteger(qt)) {
   1521                 op_id = .add_wrap;
   1522             } else {
   1523                 op_id = .add;
   1524             }
   1525         },
   1526         .Sub => {
   1527             if (cIsUnsignedInteger(qt) or isPointerDiffExpr) {
   1528                 op_id = .sub_wrap;
   1529             } else {
   1530                 op_id = .sub;
   1531             }
   1532         },
   1533         .Mul => {
   1534             if (cIsUnsignedInteger(qt)) {
   1535                 op_id = .mul_wrap;
   1536             } else {
   1537                 op_id = .mul;
   1538             }
   1539         },
   1540         .Div => {
   1541             // unsigned/float division uses the operator
   1542             op_id = .div;
   1543         },
   1544         .Rem => {
   1545             // unsigned/float division uses the operator
   1546             op_id = .mod;
   1547         },
   1548         .LT => {
   1549             op_id = .less_than;
   1550         },
   1551         .GT => {
   1552             op_id = .greater_than;
   1553         },
   1554         .LE => {
   1555             op_id = .less_than_equal;
   1556         },
   1557         .GE => {
   1558             op_id = .greater_than_equal;
   1559         },
   1560         .EQ => {
   1561             op_id = .equal;
   1562         },
   1563         .NE => {
   1564             op_id = .not_equal;
   1565         },
   1566         .And => {
   1567             op_id = .bit_and;
   1568         },
   1569         .Xor => {
   1570             op_id = .bit_xor;
   1571         },
   1572         .Or => {
   1573             op_id = .bit_or;
   1574         },
   1575         else => unreachable,
   1576     }
   1577 
   1578     const lhs_uncasted = try transExpr(c, scope, stmt.getLHS(), .used);
   1579     const rhs_uncasted = try transExpr(c, scope, stmt.getRHS(), .used);
   1580 
   1581     const lhs = if (isBoolRes(lhs_uncasted))
   1582         try Tag.int_from_bool.create(c.arena, lhs_uncasted)
   1583     else if (isPointerDiffExpr)
   1584         try Tag.int_from_ptr.create(c.arena, lhs_uncasted)
   1585     else
   1586         lhs_uncasted;
   1587 
   1588     const rhs = if (isBoolRes(rhs_uncasted))
   1589         try Tag.int_from_bool.create(c.arena, rhs_uncasted)
   1590     else if (isPointerDiffExpr)
   1591         try Tag.int_from_ptr.create(c.arena, rhs_uncasted)
   1592     else
   1593         rhs_uncasted;
   1594 
   1595     const infixOpNode = try transCreateNodeInfixOp(c, op_id, lhs, rhs, result_used);
   1596     if (isPointerDiffExpr) {
   1597         // @divExact(@bitCast(<platform-ptrdiff_t>, @intFromPtr(lhs) -% @intFromPtr(rhs)), @sizeOf(<lhs target type>))
   1598         const ptrdiff_type = try transQualTypeIntWidthOf(c, qt, true);
   1599 
   1600         // C standard requires that pointer subtraction operands are of the same type,
   1601         // otherwise it is undefined behavior. So we can assume the left and right
   1602         // sides are the same QualType and arbitrarily choose left.
   1603         const lhs_expr = stmt.getLHS();
   1604         const lhs_qt = getExprQualType(c, lhs_expr);
   1605         const lhs_qt_translated = try transQualType(c, scope, lhs_qt, lhs_expr.getBeginLoc());
   1606         const c_pointer = getContainer(c, lhs_qt_translated).?;
   1607         const elem_type = c_pointer.castTag(.c_pointer).?.data.elem_type;
   1608         const sizeof = try Tag.sizeof.create(c.arena, elem_type);
   1609 
   1610         const bitcast = try Tag.as.create(c.arena, .{
   1611             .lhs = ptrdiff_type,
   1612             .rhs = try Tag.bit_cast.create(c.arena, infixOpNode),
   1613         });
   1614 
   1615         return Tag.div_exact.create(c.arena, .{
   1616             .lhs = bitcast,
   1617             .rhs = sizeof,
   1618         });
   1619     }
   1620     return infixOpNode;
   1621 }
   1622 
   1623 fn transCompoundStmtInline(
   1624     c: *Context,
   1625     stmt: *const clang.CompoundStmt,
   1626     block: *Scope.Block,
   1627 ) TransError!void {
   1628     var it = stmt.body_begin();
   1629     const end_it = stmt.body_end();
   1630     while (it != end_it) : (it += 1) {
   1631         const result = try transStmt(c, &block.base, it[0], .unused);
   1632         switch (result.tag()) {
   1633             .declaration, .empty_block => {},
   1634             else => try block.statements.append(result),
   1635         }
   1636     }
   1637 }
   1638 
   1639 fn transCompoundStmt(c: *Context, scope: *Scope, stmt: *const clang.CompoundStmt) TransError!Node {
   1640     var block_scope = try Scope.Block.init(c, scope, false);
   1641     defer block_scope.deinit();
   1642     try transCompoundStmtInline(c, stmt, &block_scope);
   1643     return try block_scope.complete(c);
   1644 }
   1645 
   1646 fn transCStyleCastExprClass(
   1647     c: *Context,
   1648     scope: *Scope,
   1649     stmt: *const clang.CStyleCastExpr,
   1650     result_used: ResultUsed,
   1651 ) TransError!Node {
   1652     const cast_expr = @as(*const clang.CastExpr, @ptrCast(stmt));
   1653     const sub_expr = stmt.getSubExpr();
   1654     const dst_type = stmt.getType();
   1655     const src_type = sub_expr.getType();
   1656     const sub_expr_node = try transExpr(c, scope, sub_expr, .used);
   1657     const loc = stmt.getBeginLoc();
   1658 
   1659     const cast_node = if (cast_expr.getCastKind() == .ToUnion) blk: {
   1660         const field_decl = cast_expr.getTargetFieldForToUnionCast(dst_type, src_type).?; // C syntax error if target field is null
   1661         const field_name = try c.str(@as(*const clang.NamedDecl, @ptrCast(field_decl)).getName_bytes_begin());
   1662 
   1663         const union_ty = try transQualType(c, scope, dst_type, loc);
   1664 
   1665         const inits = [1]ast.Payload.ContainerInit.Initializer{.{ .name = field_name, .value = sub_expr_node }};
   1666         break :blk try Tag.container_init.create(c.arena, .{
   1667             .lhs = union_ty,
   1668             .inits = try c.arena.dupe(ast.Payload.ContainerInit.Initializer, &inits),
   1669         });
   1670     } else (try transCCast(
   1671         c,
   1672         scope,
   1673         loc,
   1674         dst_type,
   1675         src_type,
   1676         sub_expr_node,
   1677     ));
   1678     return maybeSuppressResult(c, result_used, cast_node);
   1679 }
   1680 
   1681 /// The alignment of a variable or field
   1682 const ClangAlignment = struct {
   1683     /// Clang reports the alignment in bits, we use bytes
   1684     /// Clang uses 0 for "no alignment specified", we use null
   1685     bit_alignment: c_uint,
   1686     /// If the field or variable is marked as 'packed'
   1687     ///
   1688     /// According to the GCC variable attribute docs, this impacts alignment
   1689     /// https://gcc.gnu.org/onlinedocs/gcc/Common-Variable-Attributes.html
   1690     ///
   1691     /// > The packed attribute specifies that a structure member
   1692     /// > should have the smallest possible alignment
   1693     ///
   1694     /// Note also that specifying the 'packed' attribute on a structure
   1695     /// implicitly packs all its fields (making their alignment 1).
   1696     ///
   1697     /// This will be null if the AST node doesn't support packing (functions)
   1698     is_packed: ?bool,
   1699 
   1700     /// Get the alignment for a field, optionally taking into account the parent record
   1701     pub fn forField(c: *const Context, field: *const clang.FieldDecl, parent: ?*const clang.RecordDecl) ClangAlignment {
   1702         const parent_packed = if (parent) |record| record.getPackedAttribute() else false;
   1703         // NOTE: According to GCC docs, parent attribute packed implies child attribute packed
   1704         return ClangAlignment{
   1705             .bit_alignment = field.getAlignedAttribute(c.clang_context),
   1706             .is_packed = field.getPackedAttribute() or parent_packed,
   1707         };
   1708     }
   1709 
   1710     pub fn forVar(c: *const Context, var_decl: *const clang.VarDecl) ClangAlignment {
   1711         return ClangAlignment{
   1712             .bit_alignment = var_decl.getAlignedAttribute(c.clang_context),
   1713             .is_packed = var_decl.getPackedAttribute(),
   1714         };
   1715     }
   1716 
   1717     pub fn forFunc(c: *const Context, fun: *const clang.FunctionDecl) ClangAlignment {
   1718         return ClangAlignment{
   1719             .bit_alignment = fun.getAlignedAttribute(c.clang_context),
   1720             .is_packed = null, // not supported by GCC/clang (or meaningful),
   1721         };
   1722     }
   1723 
   1724     /// Translate the clang alignment info into a zig alignment
   1725     ///
   1726     /// Returns null if there is no special alignment info
   1727     pub fn zigAlignment(self: ClangAlignment) ?c_uint {
   1728         if (self.bit_alignment != 0) {
   1729             return self.bit_alignment / 8;
   1730         } else if (self.is_packed orelse false) {
   1731             return 1;
   1732         } else {
   1733             return null;
   1734         }
   1735     }
   1736 };
   1737 
   1738 fn transDeclStmtOne(
   1739     c: *Context,
   1740     scope: *Scope,
   1741     decl: *const clang.Decl,
   1742     block_scope: *Scope.Block,
   1743 ) TransError!void {
   1744     switch (decl.getKind()) {
   1745         .Var => {
   1746             const var_decl = @as(*const clang.VarDecl, @ptrCast(decl));
   1747             const decl_init = var_decl.getInit();
   1748             const loc = decl.getLocation();
   1749 
   1750             const qual_type = var_decl.getTypeSourceInfo_getType();
   1751             const name = try c.str(@as(*const clang.NamedDecl, @ptrCast(var_decl)).getName_bytes_begin());
   1752             const mangled_name = try block_scope.makeMangledName(c, name);
   1753 
   1754             if (var_decl.getStorageClass() == .Extern) {
   1755                 // This is actually a global variable, put it in the global scope and reference it.
   1756                 // `_ = mangled_name;`
   1757                 return visitVarDecl(c, var_decl, mangled_name);
   1758             } else if (qualTypeWasDemotedToOpaque(c, qual_type)) {
   1759                 return fail(c, error.UnsupportedTranslation, loc, "local variable has opaque type", .{});
   1760             }
   1761 
   1762             const is_static_local = var_decl.isStaticLocal();
   1763             const is_const = qual_type.isConstQualified();
   1764             const type_node = try transQualTypeMaybeInitialized(c, scope, qual_type, decl_init, loc);
   1765 
   1766             var init_node = if (decl_init) |expr|
   1767                 if (expr.getStmtClass() == .StringLiteralClass)
   1768                     try transStringLiteralInitializer(c, @as(*const clang.StringLiteral, @ptrCast(expr)), type_node)
   1769                 else
   1770                     try transExprCoercing(c, scope, expr, .used)
   1771             else if (is_static_local)
   1772                 try Tag.std_mem_zeroes.create(c.arena, type_node)
   1773             else
   1774                 Tag.undefined_literal.init();
   1775             if (!qualTypeIsBoolean(qual_type) and isBoolRes(init_node)) {
   1776                 init_node = try Tag.int_from_bool.create(c.arena, init_node);
   1777             } else if (init_node.tag() == .string_literal and qualTypeIsCharStar(qual_type)) {
   1778                 const dst_type_node = try transQualType(c, scope, qual_type, loc);
   1779                 init_node = try removeCVQualifiers(c, dst_type_node, init_node);
   1780             }
   1781 
   1782             const var_name: []const u8 = if (is_static_local) Scope.Block.static_inner_name else mangled_name;
   1783             var node = try Tag.var_decl.create(c.arena, .{
   1784                 .is_pub = false,
   1785                 .is_const = is_const,
   1786                 .is_extern = false,
   1787                 .is_export = false,
   1788                 .is_threadlocal = var_decl.getTLSKind() != .None,
   1789                 .linksection_string = null,
   1790                 .alignment = ClangAlignment.forVar(c, var_decl).zigAlignment(),
   1791                 .name = var_name,
   1792                 .type = type_node,
   1793                 .init = init_node,
   1794             });
   1795             if (is_static_local) {
   1796                 node = try Tag.static_local_var.create(c.arena, .{ .name = mangled_name, .init = node });
   1797             }
   1798             try block_scope.statements.append(node);
   1799             try block_scope.discardVariable(c, mangled_name);
   1800 
   1801             const cleanup_attr = var_decl.getCleanupAttribute();
   1802             if (cleanup_attr) |fn_decl| {
   1803                 const cleanup_fn_name = try c.str(@as(*const clang.NamedDecl, @ptrCast(fn_decl)).getName_bytes_begin());
   1804                 const fn_id = try Tag.identifier.create(c.arena, cleanup_fn_name);
   1805 
   1806                 const varname = try Tag.identifier.create(c.arena, mangled_name);
   1807                 const args = try c.arena.alloc(Node, 1);
   1808                 args[0] = try Tag.address_of.create(c.arena, varname);
   1809 
   1810                 const cleanup_call = try Tag.call.create(c.arena, .{ .lhs = fn_id, .args = args });
   1811                 const discard = try Tag.discard.create(c.arena, .{ .should_skip = false, .value = cleanup_call });
   1812                 const deferred_cleanup = try Tag.@"defer".create(c.arena, discard);
   1813 
   1814                 try block_scope.statements.append(deferred_cleanup);
   1815             }
   1816         },
   1817         .Typedef => {
   1818             try transTypeDef(c, scope, @as(*const clang.TypedefNameDecl, @ptrCast(decl)));
   1819         },
   1820         .Record => {
   1821             try transRecordDecl(c, scope, @as(*const clang.RecordDecl, @ptrCast(decl)));
   1822         },
   1823         .Enum => {
   1824             try transEnumDecl(c, scope, @as(*const clang.EnumDecl, @ptrCast(decl)));
   1825         },
   1826         .Function => {
   1827             try visitFnDecl(c, @as(*const clang.FunctionDecl, @ptrCast(decl)));
   1828         },
   1829         else => {
   1830             const decl_name = try c.str(decl.getDeclKindName());
   1831             try warn(c, &c.global_scope.base, decl.getLocation(), "ignoring {s} declaration", .{decl_name});
   1832         },
   1833     }
   1834 }
   1835 
   1836 fn transDeclStmt(c: *Context, scope: *Scope, stmt: *const clang.DeclStmt) TransError!Node {
   1837     const block_scope = try scope.findBlockScope(c);
   1838 
   1839     var it = stmt.decl_begin();
   1840     const end_it = stmt.decl_end();
   1841     while (it != end_it) : (it += 1) {
   1842         try transDeclStmtOne(c, scope, it[0], block_scope);
   1843     }
   1844     return Tag.declaration.init();
   1845 }
   1846 
   1847 fn transDeclRefExpr(
   1848     c: *Context,
   1849     scope: *Scope,
   1850     expr: *const clang.DeclRefExpr,
   1851 ) TransError!Node {
   1852     const value_decl = expr.getDecl();
   1853     const name = try c.str(@as(*const clang.NamedDecl, @ptrCast(value_decl)).getName_bytes_begin());
   1854     const mangled_name = scope.getAlias(name);
   1855     var ref_expr = if (cIsFunctionDeclRef(@as(*const clang.Expr, @ptrCast(expr))))
   1856         try Tag.fn_identifier.create(c.arena, mangled_name)
   1857     else
   1858         try Tag.identifier.create(c.arena, mangled_name);
   1859 
   1860     if (@as(*const clang.Decl, @ptrCast(value_decl)).getKind() == .Var) {
   1861         const var_decl = @as(*const clang.VarDecl, @ptrCast(value_decl));
   1862         if (var_decl.isStaticLocal()) {
   1863             ref_expr = try Tag.field_access.create(c.arena, .{
   1864                 .lhs = ref_expr,
   1865                 .field_name = Scope.Block.static_inner_name,
   1866             });
   1867         }
   1868     }
   1869     scope.skipVariableDiscard(mangled_name);
   1870     return ref_expr;
   1871 }
   1872 
   1873 fn transImplicitCastExpr(
   1874     c: *Context,
   1875     scope: *Scope,
   1876     expr: *const clang.ImplicitCastExpr,
   1877     result_used: ResultUsed,
   1878 ) TransError!Node {
   1879     const sub_expr = expr.getSubExpr();
   1880     const dest_type = getExprQualType(c, @as(*const clang.Expr, @ptrCast(expr)));
   1881     const src_type = getExprQualType(c, sub_expr);
   1882     switch (expr.getCastKind()) {
   1883         .BitCast, .FloatingCast, .FloatingToIntegral, .IntegralToFloating, .IntegralCast, .PointerToIntegral, .IntegralToPointer => {
   1884             const sub_expr_node = try transExpr(c, scope, sub_expr, .used);
   1885             const casted = try transCCast(c, scope, expr.getBeginLoc(), dest_type, src_type, sub_expr_node);
   1886             return maybeSuppressResult(c, result_used, casted);
   1887         },
   1888         .LValueToRValue, .NoOp, .FunctionToPointerDecay => {
   1889             const sub_expr_node = try transExpr(c, scope, sub_expr, .used);
   1890             return maybeSuppressResult(c, result_used, sub_expr_node);
   1891         },
   1892         .ArrayToPointerDecay => {
   1893             const sub_expr_node = try transExpr(c, scope, sub_expr, .used);
   1894             if (exprIsNarrowStringLiteral(sub_expr) or exprIsFlexibleArrayRef(c, sub_expr)) {
   1895                 return maybeSuppressResult(c, result_used, sub_expr_node);
   1896             }
   1897 
   1898             const addr = try Tag.address_of.create(c.arena, sub_expr_node);
   1899             const casted = try transCPtrCast(c, scope, expr.getBeginLoc(), dest_type, src_type, addr);
   1900             return maybeSuppressResult(c, result_used, casted);
   1901         },
   1902         .NullToPointer => {
   1903             return Tag.null_literal.init();
   1904         },
   1905         .PointerToBoolean => {
   1906             // @intFromPtr(val) != 0
   1907             const ptr_node = try transExpr(c, scope, sub_expr, .used);
   1908             const int_from_ptr = try Tag.int_from_ptr.create(c.arena, ptr_node);
   1909 
   1910             const ne = try Tag.not_equal.create(c.arena, .{ .lhs = int_from_ptr, .rhs = Tag.zero_literal.init() });
   1911             return maybeSuppressResult(c, result_used, ne);
   1912         },
   1913         .IntegralToBoolean, .FloatingToBoolean => {
   1914             const sub_expr_node = try transExpr(c, scope, sub_expr, .used);
   1915 
   1916             // The expression is already a boolean one, return it as-is
   1917             if (isBoolRes(sub_expr_node))
   1918                 return maybeSuppressResult(c, result_used, sub_expr_node);
   1919 
   1920             // val != 0
   1921             const ne = try Tag.not_equal.create(c.arena, .{ .lhs = sub_expr_node, .rhs = Tag.zero_literal.init() });
   1922             return maybeSuppressResult(c, result_used, ne);
   1923         },
   1924         .BuiltinFnToFnPtr => {
   1925             return transBuiltinFnExpr(c, scope, sub_expr, result_used);
   1926         },
   1927         .ToVoid => {
   1928             // Should only appear in the rhs and lhs of a ConditionalOperator
   1929             return transExpr(c, scope, sub_expr, .unused);
   1930         },
   1931         else => |kind| return fail(
   1932             c,
   1933             error.UnsupportedTranslation,
   1934             @as(*const clang.Stmt, @ptrCast(expr)).getBeginLoc(),
   1935             "unsupported CastKind {s}",
   1936             .{@tagName(kind)},
   1937         ),
   1938     }
   1939 }
   1940 
   1941 fn isBuiltinDefined(name: []const u8) bool {
   1942     inline for (@typeInfo(std.zig.c_builtins).Struct.decls) |decl| {
   1943         if (std.mem.eql(u8, name, decl.name)) return true;
   1944     }
   1945     return false;
   1946 }
   1947 
   1948 fn transBuiltinFnExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node {
   1949     const node = try transExpr(c, scope, expr, used);
   1950     if (node.castTag(.fn_identifier)) |ident| {
   1951         const name = ident.data;
   1952         if (!isBuiltinDefined(name)) return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "TODO implement function '{s}' in std.zig.c_builtins", .{name});
   1953     }
   1954     return node;
   1955 }
   1956 
   1957 fn transBoolExpr(
   1958     c: *Context,
   1959     scope: *Scope,
   1960     expr: *const clang.Expr,
   1961     used: ResultUsed,
   1962 ) TransError!Node {
   1963     if (@as(*const clang.Stmt, @ptrCast(expr)).getStmtClass() == .IntegerLiteralClass) {
   1964         var signum: c_int = undefined;
   1965         if (!(@as(*const clang.IntegerLiteral, @ptrCast(expr)).getSignum(&signum, c.clang_context))) {
   1966             return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid integer literal", .{});
   1967         }
   1968         const is_zero = signum == 0;
   1969         return Node{ .tag_if_small_enough = @intFromEnum(([2]Tag{ .true_literal, .false_literal })[@intFromBool(is_zero)]) };
   1970     }
   1971 
   1972     const res = try transExpr(c, scope, expr, used);
   1973     if (isBoolRes(res)) {
   1974         return maybeSuppressResult(c, used, res);
   1975     }
   1976 
   1977     const ty = getExprQualType(c, expr).getTypePtr();
   1978     const node = try finishBoolExpr(c, scope, expr.getBeginLoc(), ty, res, used);
   1979 
   1980     return maybeSuppressResult(c, used, node);
   1981 }
   1982 
   1983 fn exprIsBooleanType(expr: *const clang.Expr) bool {
   1984     return qualTypeIsBoolean(expr.getType());
   1985 }
   1986 
   1987 fn exprIsNarrowStringLiteral(expr: *const clang.Expr) bool {
   1988     switch (expr.getStmtClass()) {
   1989         .StringLiteralClass => {
   1990             const string_lit = @as(*const clang.StringLiteral, @ptrCast(expr));
   1991             return string_lit.getCharByteWidth() == 1;
   1992         },
   1993         .PredefinedExprClass => return true,
   1994         .UnaryOperatorClass => {
   1995             const op_expr = @as(*const clang.UnaryOperator, @ptrCast(expr)).getSubExpr();
   1996             return exprIsNarrowStringLiteral(op_expr);
   1997         },
   1998         .ParenExprClass => {
   1999             const op_expr = @as(*const clang.ParenExpr, @ptrCast(expr)).getSubExpr();
   2000             return exprIsNarrowStringLiteral(op_expr);
   2001         },
   2002         .GenericSelectionExprClass => {
   2003             const gen_sel = @as(*const clang.GenericSelectionExpr, @ptrCast(expr));
   2004             return exprIsNarrowStringLiteral(gen_sel.getResultExpr());
   2005         },
   2006         else => return false,
   2007     }
   2008 }
   2009 
   2010 fn exprIsFlexibleArrayRef(c: *Context, expr: *const clang.Expr) bool {
   2011     if (expr.getStmtClass() == .MemberExprClass) {
   2012         const member_expr = @as(*const clang.MemberExpr, @ptrCast(expr));
   2013         const member_decl = member_expr.getMemberDecl();
   2014         const decl_kind = @as(*const clang.Decl, @ptrCast(member_decl)).getKind();
   2015         if (decl_kind == .Field) {
   2016             const field_decl = @as(*const clang.FieldDecl, @ptrCast(member_decl));
   2017             return isFlexibleArrayFieldDecl(c, field_decl);
   2018         }
   2019     }
   2020     return false;
   2021 }
   2022 
   2023 fn isBoolRes(res: Node) bool {
   2024     switch (res.tag()) {
   2025         .@"or",
   2026         .@"and",
   2027         .equal,
   2028         .not_equal,
   2029         .less_than,
   2030         .less_than_equal,
   2031         .greater_than,
   2032         .greater_than_equal,
   2033         .not,
   2034         .false_literal,
   2035         .true_literal,
   2036         => return true,
   2037         else => return false,
   2038     }
   2039 }
   2040 
   2041 fn finishBoolExpr(
   2042     c: *Context,
   2043     scope: *Scope,
   2044     loc: clang.SourceLocation,
   2045     ty: *const clang.Type,
   2046     node: Node,
   2047     used: ResultUsed,
   2048 ) TransError!Node {
   2049     switch (ty.getTypeClass()) {
   2050         .Builtin => {
   2051             const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(ty));
   2052 
   2053             switch (builtin_ty.getKind()) {
   2054                 .Bool => return node,
   2055                 .Char_U,
   2056                 .UChar,
   2057                 .Char_S,
   2058                 .SChar,
   2059                 .UShort,
   2060                 .UInt,
   2061                 .ULong,
   2062                 .ULongLong,
   2063                 .Short,
   2064                 .Int,
   2065                 .Long,
   2066                 .LongLong,
   2067                 .UInt128,
   2068                 .Int128,
   2069                 .Float,
   2070                 .Double,
   2071                 .Float128,
   2072                 .LongDouble,
   2073                 .WChar_U,
   2074                 .Char8,
   2075                 .Char16,
   2076                 .Char32,
   2077                 .WChar_S,
   2078                 .Float16,
   2079                 => {
   2080                     // node != 0
   2081                     return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.zero_literal.init() });
   2082                 },
   2083                 .NullPtr => {
   2084                     // node == null
   2085                     return Tag.equal.create(c.arena, .{ .lhs = node, .rhs = Tag.null_literal.init() });
   2086                 },
   2087                 else => {},
   2088             }
   2089         },
   2090         .Pointer => {
   2091             if (node.tag() == .string_literal) {
   2092                 // @intFromPtr(node) != 0
   2093                 const int_from_ptr = try Tag.int_from_ptr.create(c.arena, node);
   2094                 return Tag.not_equal.create(c.arena, .{ .lhs = int_from_ptr, .rhs = Tag.zero_literal.init() });
   2095             }
   2096             // node != null
   2097             return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.null_literal.init() });
   2098         },
   2099         .Typedef => {
   2100             const typedef_ty = @as(*const clang.TypedefType, @ptrCast(ty));
   2101             const typedef_decl = typedef_ty.getDecl();
   2102             const underlying_type = typedef_decl.getUnderlyingType();
   2103             return finishBoolExpr(c, scope, loc, underlying_type.getTypePtr(), node, used);
   2104         },
   2105         .Enum => {
   2106             // node != 0
   2107             return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.zero_literal.init() });
   2108         },
   2109         .Elaborated => {
   2110             const elaborated_ty = @as(*const clang.ElaboratedType, @ptrCast(ty));
   2111             const named_type = elaborated_ty.getNamedType();
   2112             return finishBoolExpr(c, scope, loc, named_type.getTypePtr(), node, used);
   2113         },
   2114         else => {},
   2115     }
   2116     return fail(c, error.UnsupportedType, loc, "unsupported bool expression type", .{});
   2117 }
   2118 
   2119 const SuppressCast = enum {
   2120     with_as,
   2121     no_as,
   2122 };
   2123 fn transIntegerLiteral(
   2124     c: *Context,
   2125     scope: *Scope,
   2126     expr: *const clang.IntegerLiteral,
   2127     result_used: ResultUsed,
   2128     suppress_as: SuppressCast,
   2129 ) TransError!Node {
   2130     var eval_result: clang.ExprEvalResult = undefined;
   2131     if (!expr.EvaluateAsInt(&eval_result, c.clang_context)) {
   2132         const loc = expr.getBeginLoc();
   2133         return fail(c, error.UnsupportedTranslation, loc, "invalid integer literal", .{});
   2134     }
   2135 
   2136     if (suppress_as == .no_as) {
   2137         const int_lit_node = try transCreateNodeAPInt(c, eval_result.Val.getInt());
   2138         return maybeSuppressResult(c, result_used, int_lit_node);
   2139     }
   2140 
   2141     // Integer literals in C have types, and this can matter for several reasons.
   2142     // For example, this is valid C:
   2143     //     unsigned char y = 256;
   2144     // How this gets evaluated is the 256 is an integer, which gets truncated to signed char, then bit-casted
   2145     // to unsigned char, resulting in 0. In order for this to work, we have to emit this zig code:
   2146     //     var y = @as(u8, @bitCast(@as(i8, @truncate(@as(c_int, 256)))));
   2147     // Ideally in translate-c we could flatten this out to simply:
   2148     //     var y: u8 = 0;
   2149     // But the first step is to be correct, and the next step is to make the output more elegant.
   2150 
   2151     // @as(T, x)
   2152     const expr_base = @as(*const clang.Expr, @ptrCast(expr));
   2153     const ty_node = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc());
   2154     const rhs = try transCreateNodeAPInt(c, eval_result.Val.getInt());
   2155     const as = try Tag.as.create(c.arena, .{ .lhs = ty_node, .rhs = rhs });
   2156     return maybeSuppressResult(c, result_used, as);
   2157 }
   2158 
   2159 fn transReturnStmt(
   2160     c: *Context,
   2161     scope: *Scope,
   2162     expr: *const clang.ReturnStmt,
   2163 ) TransError!Node {
   2164     const val_expr = expr.getRetValue() orelse
   2165         return Tag.return_void.init();
   2166 
   2167     var rhs = try transExprCoercing(c, scope, val_expr, .used);
   2168     const return_qt = scope.findBlockReturnType();
   2169     if (isBoolRes(rhs) and !qualTypeIsBoolean(return_qt)) {
   2170         rhs = try Tag.int_from_bool.create(c.arena, rhs);
   2171     }
   2172     return Tag.@"return".create(c.arena, rhs);
   2173 }
   2174 
   2175 fn transNarrowStringLiteral(
   2176     c: *Context,
   2177     stmt: *const clang.StringLiteral,
   2178     result_used: ResultUsed,
   2179 ) TransError!Node {
   2180     var len: usize = undefined;
   2181     const bytes_ptr = stmt.getString_bytes_begin_size(&len);
   2182 
   2183     const str = try std.fmt.allocPrint(c.arena, "\"{}\"", .{std.zig.fmtEscapes(bytes_ptr[0..len])});
   2184     const node = try Tag.string_literal.create(c.arena, str);
   2185     return maybeSuppressResult(c, result_used, node);
   2186 }
   2187 
   2188 fn transStringLiteral(
   2189     c: *Context,
   2190     scope: *Scope,
   2191     stmt: *const clang.StringLiteral,
   2192     result_used: ResultUsed,
   2193 ) TransError!Node {
   2194     const kind = stmt.getKind();
   2195     switch (kind) {
   2196         .Ascii, .UTF8 => return transNarrowStringLiteral(c, stmt, result_used),
   2197         .UTF16, .UTF32, .Wide => {
   2198             const str_type = @tagName(stmt.getKind());
   2199             const name = try std.fmt.allocPrint(c.arena, "zig.{s}_string_{d}", .{ str_type, c.getMangle() });
   2200 
   2201             const expr_base = @as(*const clang.Expr, @ptrCast(stmt));
   2202             const array_type = try transQualTypeInitialized(c, scope, expr_base.getType(), expr_base, expr_base.getBeginLoc());
   2203             const lit_array = try transStringLiteralInitializer(c, stmt, array_type);
   2204             const decl = try Tag.var_simple.create(c.arena, .{ .name = name, .init = lit_array });
   2205             try scope.appendNode(decl);
   2206             const node = try Tag.identifier.create(c.arena, name);
   2207             return maybeSuppressResult(c, result_used, node);
   2208         },
   2209     }
   2210 }
   2211 
   2212 fn getArrayPayload(array_type: Node) ast.Payload.Array.ArrayTypeInfo {
   2213     return (array_type.castTag(.array_type) orelse array_type.castTag(.null_sentinel_array_type).?).data;
   2214 }
   2215 
   2216 /// Translate a string literal that is initializing an array. In general narrow string
   2217 /// literals become `"<string>".*` or `"<string>"[0..<size>].*` if they need truncation.
   2218 /// Wide string literals become an array of integers. zero-fillers pad out the array to
   2219 /// the appropriate length, if necessary.
   2220 fn transStringLiteralInitializer(
   2221     c: *Context,
   2222     stmt: *const clang.StringLiteral,
   2223     array_type: Node,
   2224 ) TransError!Node {
   2225     assert(array_type.tag() == .array_type or array_type.tag() == .null_sentinel_array_type);
   2226 
   2227     const is_narrow = stmt.getKind() == .Ascii or stmt.getKind() == .UTF8;
   2228 
   2229     const str_length = stmt.getLength();
   2230     const payload = getArrayPayload(array_type);
   2231     const array_size = payload.len;
   2232     const elem_type = payload.elem_type;
   2233 
   2234     if (array_size == 0) return Tag.empty_array.create(c.arena, elem_type);
   2235 
   2236     const num_inits = @min(str_length, array_size);
   2237     const init_node = if (num_inits > 0) blk: {
   2238         if (is_narrow) {
   2239             // "string literal".* or string literal"[0..num_inits].*
   2240             var str = try transNarrowStringLiteral(c, stmt, .used);
   2241             if (str_length != array_size) str = try Tag.string_slice.create(c.arena, .{ .string = str, .end = num_inits });
   2242             break :blk try Tag.deref.create(c.arena, str);
   2243         } else {
   2244             const init_list = try c.arena.alloc(Node, num_inits);
   2245             var i: c_uint = 0;
   2246             while (i < num_inits) : (i += 1) {
   2247                 init_list[i] = try transCreateCharLitNode(c, false, stmt.getCodeUnit(i));
   2248             }
   2249             const init_args = .{ .len = num_inits, .elem_type = elem_type };
   2250             const init_array_type = try if (array_type.tag() == .array_type) Tag.array_type.create(c.arena, init_args) else Tag.null_sentinel_array_type.create(c.arena, init_args);
   2251             break :blk try Tag.array_init.create(c.arena, .{
   2252                 .cond = init_array_type,
   2253                 .cases = init_list,
   2254             });
   2255         }
   2256     } else null;
   2257 
   2258     if (num_inits == array_size) return init_node.?; // init_node is only null if num_inits == 0; but if num_inits == array_size == 0 we've already returned
   2259     assert(array_size > str_length); // If array_size <= str_length, `num_inits == array_size` and we've already returned.
   2260 
   2261     const filler_node = try Tag.array_filler.create(c.arena, .{
   2262         .type = elem_type,
   2263         .filler = Tag.zero_literal.init(),
   2264         .count = array_size - str_length,
   2265     });
   2266 
   2267     if (init_node) |some| {
   2268         return Tag.array_cat.create(c.arena, .{ .lhs = some, .rhs = filler_node });
   2269     } else {
   2270         return filler_node;
   2271     }
   2272 }
   2273 
   2274 /// determine whether `stmt` is a "pointer subtraction expression" - a subtraction where
   2275 /// both operands resolve to addresses. The C standard requires that both operands
   2276 /// point to elements of the same array object, but we do not verify that here.
   2277 fn cIsPointerDiffExpr(stmt: *const clang.BinaryOperator) bool {
   2278     const lhs = @as(*const clang.Stmt, @ptrCast(stmt.getLHS()));
   2279     const rhs = @as(*const clang.Stmt, @ptrCast(stmt.getRHS()));
   2280     return stmt.getOpcode() == .Sub and
   2281         qualTypeIsPtr(@as(*const clang.Expr, @ptrCast(lhs)).getType()) and
   2282         qualTypeIsPtr(@as(*const clang.Expr, @ptrCast(rhs)).getType());
   2283 }
   2284 
   2285 fn cIsEnum(qt: clang.QualType) bool {
   2286     return qt.getCanonicalType().getTypeClass() == .Enum;
   2287 }
   2288 
   2289 fn cIsVector(qt: clang.QualType) bool {
   2290     return qt.getCanonicalType().getTypeClass() == .Vector;
   2291 }
   2292 
   2293 /// Get the underlying int type of an enum. The C compiler chooses a signed int
   2294 /// type that is large enough to hold all of the enum's values. It is not required
   2295 /// to be the smallest possible type that can hold all the values.
   2296 fn cIntTypeForEnum(enum_qt: clang.QualType) clang.QualType {
   2297     assert(cIsEnum(enum_qt));
   2298     const ty = enum_qt.getCanonicalType().getTypePtr();
   2299     const enum_ty = @as(*const clang.EnumType, @ptrCast(ty));
   2300     const enum_decl = enum_ty.getDecl();
   2301     return enum_decl.getIntegerType();
   2302 }
   2303 
   2304 // when modifying this function, make sure to also update std.zig.c_translation.cast
   2305 fn transCCast(
   2306     c: *Context,
   2307     scope: *Scope,
   2308     loc: clang.SourceLocation,
   2309     dst_type: clang.QualType,
   2310     src_type: clang.QualType,
   2311     expr: Node,
   2312 ) !Node {
   2313     if (qualTypeCanon(dst_type).isVoidType()) return expr;
   2314     if (dst_type.eq(src_type)) return expr;
   2315     if (qualTypeIsPtr(dst_type) and qualTypeIsPtr(src_type))
   2316         return transCPtrCast(c, scope, loc, dst_type, src_type, expr);
   2317     if (cIsEnum(dst_type)) return transCCast(c, scope, loc, cIntTypeForEnum(dst_type), src_type, expr);
   2318     if (cIsEnum(src_type)) return transCCast(c, scope, loc, dst_type, cIntTypeForEnum(src_type), expr);
   2319 
   2320     const dst_node = try transQualType(c, scope, dst_type, loc);
   2321     if (cIsInteger(dst_type) and cIsInteger(src_type)) {
   2322         // 1. If src_type is an enum, determine the underlying signed int type
   2323         // 2. Extend or truncate without changing signed-ness.
   2324         // 3. Bit-cast to correct signed-ness
   2325         const src_type_is_signed = cIsSignedInteger(src_type);
   2326         var src_int_expr = expr;
   2327 
   2328         if (isBoolRes(src_int_expr)) {
   2329             src_int_expr = try Tag.int_from_bool.create(c.arena, src_int_expr);
   2330             return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = src_int_expr });
   2331         }
   2332 
   2333         switch (cIntTypeCmp(dst_type, src_type)) {
   2334             .lt => {
   2335                 // @truncate(SameSignSmallerInt, src_int_expr)
   2336                 const ty_node = try transQualTypeIntWidthOf(c, dst_type, src_type_is_signed);
   2337                 src_int_expr = try Tag.as.create(c.arena, .{
   2338                     .lhs = ty_node,
   2339                     .rhs = try Tag.truncate.create(c.arena, src_int_expr),
   2340                 });
   2341             },
   2342             .gt => {
   2343                 // @as(SameSignBiggerInt, src_int_expr)
   2344                 const ty_node = try transQualTypeIntWidthOf(c, dst_type, src_type_is_signed);
   2345                 src_int_expr = try Tag.as.create(c.arena, .{ .lhs = ty_node, .rhs = src_int_expr });
   2346             },
   2347             .eq => {
   2348                 // src_int_expr = src_int_expr
   2349             },
   2350         }
   2351         // @as(dest_type, @bitCast(intermediate_value))
   2352         return Tag.as.create(c.arena, .{
   2353             .lhs = dst_node,
   2354             .rhs = try Tag.bit_cast.create(c.arena, src_int_expr),
   2355         });
   2356     }
   2357     if (cIsVector(src_type) or cIsVector(dst_type)) {
   2358         // C cast where at least 1 operand is a vector requires them to be same size
   2359         // @as(dest_type, @bitCast(val))
   2360         return Tag.as.create(c.arena, .{
   2361             .lhs = dst_node,
   2362             .rhs = try Tag.bit_cast.create(c.arena, expr),
   2363         });
   2364     }
   2365     if (cIsInteger(dst_type) and qualTypeIsPtr(src_type)) {
   2366         // @intCast(dest_type, @intFromPtr(val))
   2367         const int_from_ptr = try Tag.int_from_ptr.create(c.arena, expr);
   2368         return Tag.as.create(c.arena, .{
   2369             .lhs = dst_node,
   2370             .rhs = try Tag.int_cast.create(c.arena, int_from_ptr),
   2371         });
   2372     }
   2373     if (cIsInteger(src_type) and qualTypeIsPtr(dst_type)) {
   2374         // @as(dest_type, @ptrFromInt(val))
   2375         return Tag.as.create(c.arena, .{
   2376             .lhs = dst_node,
   2377             .rhs = try Tag.ptr_from_int.create(c.arena, expr),
   2378         });
   2379     }
   2380     if (cIsFloating(src_type) and cIsFloating(dst_type)) {
   2381         // @as(dest_type, @floatCast(val))
   2382         return Tag.as.create(c.arena, .{
   2383             .lhs = dst_node,
   2384             .rhs = try Tag.float_cast.create(c.arena, expr),
   2385         });
   2386     }
   2387     if (cIsFloating(src_type) and !cIsFloating(dst_type)) {
   2388         // bool expression: floating val != 0
   2389         if (qualTypeIsBoolean(dst_type)) {
   2390             return Tag.not_equal.create(c.arena, .{
   2391                 .lhs = expr,
   2392                 .rhs = Tag.zero_literal.init(),
   2393             });
   2394         }
   2395 
   2396         // @as(dest_type, @intFromFloat(val))
   2397         return Tag.as.create(c.arena, .{
   2398             .lhs = dst_node,
   2399             .rhs = try Tag.int_from_float.create(c.arena, expr),
   2400         });
   2401     }
   2402     if (!cIsFloating(src_type) and cIsFloating(dst_type)) {
   2403         var rhs = expr;
   2404         if (qualTypeIsBoolean(src_type) or isBoolRes(rhs)) rhs = try Tag.int_from_bool.create(c.arena, expr);
   2405         // @as(dest_type, @floatFromInt(val))
   2406         return Tag.as.create(c.arena, .{
   2407             .lhs = dst_node,
   2408             .rhs = try Tag.float_from_int.create(c.arena, rhs),
   2409         });
   2410     }
   2411     if (qualTypeIsBoolean(src_type) and !qualTypeIsBoolean(dst_type)) {
   2412         // @intFromBool returns a u1
   2413         // TODO: if dst_type is 1 bit & signed (bitfield) we need @bitCast
   2414         // instead of @as
   2415         const int_from_bool = try Tag.int_from_bool.create(c.arena, expr);
   2416         return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = int_from_bool });
   2417     }
   2418     // @as(dest_type, val)
   2419     return Tag.as.create(c.arena, .{ .lhs = dst_node, .rhs = expr });
   2420 }
   2421 
   2422 fn transExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node {
   2423     return transStmt(c, scope, @as(*const clang.Stmt, @ptrCast(expr)), used);
   2424 }
   2425 
   2426 /// Same as `transExpr` but with the knowledge that the operand will be type coerced, and therefore
   2427 /// an `@as` would be redundant. This is used to prevent redundant `@as` in integer literals.
   2428 fn transExprCoercing(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node {
   2429     switch (@as(*const clang.Stmt, @ptrCast(expr)).getStmtClass()) {
   2430         .IntegerLiteralClass => {
   2431             return transIntegerLiteral(c, scope, @as(*const clang.IntegerLiteral, @ptrCast(expr)), .used, .no_as);
   2432         },
   2433         .CharacterLiteralClass => {
   2434             return transCharLiteral(c, scope, @as(*const clang.CharacterLiteral, @ptrCast(expr)), .used, .no_as);
   2435         },
   2436         .UnaryOperatorClass => {
   2437             const un_expr = @as(*const clang.UnaryOperator, @ptrCast(expr));
   2438             if (un_expr.getOpcode() == .Extension) {
   2439                 return transExprCoercing(c, scope, un_expr.getSubExpr(), used);
   2440             }
   2441         },
   2442         .ImplicitCastExprClass => {
   2443             const cast_expr = @as(*const clang.ImplicitCastExpr, @ptrCast(expr));
   2444             const sub_expr = cast_expr.getSubExpr();
   2445             switch (@as(*const clang.Stmt, @ptrCast(sub_expr)).getStmtClass()) {
   2446                 .IntegerLiteralClass, .CharacterLiteralClass => switch (cast_expr.getCastKind()) {
   2447                     .IntegralToFloating => return transExprCoercing(c, scope, sub_expr, used),
   2448                     .IntegralCast => {
   2449                         const dest_type = getExprQualType(c, expr);
   2450                         if (literalFitsInType(c, sub_expr, dest_type))
   2451                             return transExprCoercing(c, scope, sub_expr, used);
   2452                     },
   2453                     else => {},
   2454                 },
   2455                 else => {},
   2456             }
   2457         },
   2458         else => {},
   2459     }
   2460     return transExpr(c, scope, expr, .used);
   2461 }
   2462 
   2463 fn literalFitsInType(c: *Context, expr: *const clang.Expr, qt: clang.QualType) bool {
   2464     var width = qualTypeIntBitWidth(c, qt) catch 8;
   2465     if (width == 0) width = 8; // Byte is the smallest type.
   2466     const is_signed = cIsSignedInteger(qt);
   2467     const width_max_int = (@as(u64, 1) << math.lossyCast(u6, width - @intFromBool(is_signed))) - 1;
   2468 
   2469     switch (@as(*const clang.Stmt, @ptrCast(expr)).getStmtClass()) {
   2470         .CharacterLiteralClass => {
   2471             const char_lit = @as(*const clang.CharacterLiteral, @ptrCast(expr));
   2472             const val = char_lit.getValue();
   2473             // If the val is less than the max int then it fits.
   2474             return val <= width_max_int;
   2475         },
   2476         .IntegerLiteralClass => {
   2477             const int_lit = @as(*const clang.IntegerLiteral, @ptrCast(expr));
   2478             var eval_result: clang.ExprEvalResult = undefined;
   2479             if (!int_lit.EvaluateAsInt(&eval_result, c.clang_context)) {
   2480                 return false;
   2481             }
   2482 
   2483             const int = eval_result.Val.getInt();
   2484             return int.lessThanEqual(width_max_int);
   2485         },
   2486         else => unreachable,
   2487     }
   2488 }
   2489 
   2490 fn transInitListExprRecord(
   2491     c: *Context,
   2492     scope: *Scope,
   2493     loc: clang.SourceLocation,
   2494     expr: *const clang.InitListExpr,
   2495     ty: *const clang.Type,
   2496 ) TransError!Node {
   2497     var is_union_type = false;
   2498     // Unions and Structs are both represented as RecordDecl
   2499     const record_ty = ty.getAsRecordType() orelse
   2500         blk: {
   2501         is_union_type = true;
   2502         break :blk ty.getAsUnionType();
   2503     } orelse unreachable;
   2504     const record_decl = record_ty.getDecl();
   2505     const record_def = record_decl.getDefinition() orelse
   2506         unreachable;
   2507 
   2508     const ty_node = try transType(c, scope, ty, loc);
   2509     const init_count = expr.getNumInits();
   2510     var field_inits = std.ArrayList(ast.Payload.ContainerInit.Initializer).init(c.gpa);
   2511     defer field_inits.deinit();
   2512 
   2513     if (init_count == 0) {
   2514         const source_loc = @as(*const clang.Expr, @ptrCast(expr)).getBeginLoc();
   2515         return transZeroInitExpr(c, scope, source_loc, ty);
   2516     }
   2517 
   2518     var init_i: c_uint = 0;
   2519     var it = record_def.field_begin();
   2520     const end_it = record_def.field_end();
   2521     while (it.neq(end_it)) : (it = it.next()) {
   2522         const field_decl = it.deref();
   2523 
   2524         // The initializer for a union type has a single entry only
   2525         if (is_union_type and field_decl != expr.getInitializedFieldInUnion()) {
   2526             continue;
   2527         }
   2528 
   2529         assert(init_i < init_count);
   2530         const elem_expr = expr.getInit(init_i);
   2531         init_i += 1;
   2532 
   2533         // Generate the field assignment expression:
   2534         //     .field_name = expr
   2535         var raw_name = try c.str(@as(*const clang.NamedDecl, @ptrCast(field_decl)).getName_bytes_begin());
   2536         if (field_decl.isAnonymousStructOrUnion()) {
   2537             const name = c.decl_table.get(@intFromPtr(field_decl.getCanonicalDecl())).?;
   2538             raw_name = try c.arena.dupe(u8, name);
   2539         }
   2540 
   2541         var init_expr = try transExpr(c, scope, elem_expr, .used);
   2542         const field_qt = field_decl.getType();
   2543         if (init_expr.tag() == .string_literal and qualTypeIsCharStar(field_qt)) {
   2544             if (scope.id == .root) {
   2545                 init_expr = try stringLiteralToCharStar(c, init_expr);
   2546             } else {
   2547                 const dst_type_node = try transQualType(c, scope, field_qt, loc);
   2548                 init_expr = try removeCVQualifiers(c, dst_type_node, init_expr);
   2549             }
   2550         }
   2551         try field_inits.append(.{
   2552             .name = raw_name,
   2553             .value = init_expr,
   2554         });
   2555     }
   2556     if (ty_node.castTag(.identifier)) |ident_node| {
   2557         scope.skipVariableDiscard(ident_node.data);
   2558     }
   2559     return Tag.container_init.create(c.arena, .{
   2560         .lhs = ty_node,
   2561         .inits = try c.arena.dupe(ast.Payload.ContainerInit.Initializer, field_inits.items),
   2562     });
   2563 }
   2564 
   2565 fn transInitListExprArray(
   2566     c: *Context,
   2567     scope: *Scope,
   2568     loc: clang.SourceLocation,
   2569     expr: *const clang.InitListExpr,
   2570     ty: *const clang.Type,
   2571 ) TransError!Node {
   2572     const arr_type = ty.getAsArrayTypeUnsafe();
   2573     const child_qt = arr_type.getElementType();
   2574     const child_type = try transQualType(c, scope, child_qt, loc);
   2575     const init_count = expr.getNumInits();
   2576     assert(@as(*const clang.Type, @ptrCast(arr_type)).isConstantArrayType());
   2577     const const_arr_ty = @as(*const clang.ConstantArrayType, @ptrCast(arr_type));
   2578     const size_ap_int = const_arr_ty.getSize();
   2579     const all_count = size_ap_int.getLimitedValue(usize);
   2580     const leftover_count = all_count - init_count;
   2581 
   2582     if (all_count == 0) {
   2583         return Tag.empty_array.create(c.arena, child_type);
   2584     }
   2585 
   2586     if (expr.isStringLiteralInit()) {
   2587         assert(init_count == 1);
   2588         const init_expr = expr.getInit(0);
   2589         const string_literal = init_expr.castToStringLiteral().?;
   2590         return try transStringLiteral(c, scope, string_literal, .used);
   2591     }
   2592 
   2593     const init_node = if (init_count != 0) blk: {
   2594         const init_list = try c.arena.alloc(Node, init_count);
   2595 
   2596         for (init_list, 0..) |*init, i| {
   2597             const elem_expr = expr.getInit(@as(c_uint, @intCast(i)));
   2598             init.* = try transExprCoercing(c, scope, elem_expr, .used);
   2599         }
   2600         const init_node = try Tag.array_init.create(c.arena, .{
   2601             .cond = try Tag.array_type.create(c.arena, .{ .len = init_count, .elem_type = child_type }),
   2602             .cases = init_list,
   2603         });
   2604         if (leftover_count == 0) {
   2605             return init_node;
   2606         }
   2607         break :blk init_node;
   2608     } else null;
   2609 
   2610     assert(expr.hasArrayFiller());
   2611     const filler_val_expr = expr.getArrayFiller();
   2612     const filler_node = try Tag.array_filler.create(c.arena, .{
   2613         .type = child_type,
   2614         .filler = try transExprCoercing(c, scope, filler_val_expr, .used),
   2615         .count = leftover_count,
   2616     });
   2617 
   2618     if (init_node) |some| {
   2619         return Tag.array_cat.create(c.arena, .{ .lhs = some, .rhs = filler_node });
   2620     } else {
   2621         return filler_node;
   2622     }
   2623 }
   2624 
   2625 fn transInitListExprVector(
   2626     c: *Context,
   2627     scope: *Scope,
   2628     loc: clang.SourceLocation,
   2629     expr: *const clang.InitListExpr,
   2630 ) TransError!Node {
   2631     const qt = getExprQualType(c, @as(*const clang.Expr, @ptrCast(expr)));
   2632     const vector_ty = @as(*const clang.VectorType, @ptrCast(qualTypeCanon(qt)));
   2633 
   2634     const init_count = expr.getNumInits();
   2635     const num_elements = vector_ty.getNumElements();
   2636     const element_qt = vector_ty.getElementType();
   2637 
   2638     if (init_count == 0) {
   2639         const vec_node = try Tag.vector.create(c.arena, .{
   2640             .lhs = try transCreateNodeNumber(c, num_elements, .int),
   2641             .rhs = try transQualType(c, scope, element_qt, loc),
   2642         });
   2643 
   2644         return Tag.as.create(c.arena, .{
   2645             .lhs = vec_node,
   2646             .rhs = try Tag.vector_zero_init.create(c.arena, Tag.zero_literal.init()),
   2647         });
   2648     }
   2649 
   2650     const vector_type = try transQualType(c, scope, qt, loc);
   2651 
   2652     var block_scope = try Scope.Block.init(c, scope, true);
   2653     defer block_scope.deinit();
   2654 
   2655     // workaround for https://github.com/ziglang/zig/issues/8322
   2656     // we store the initializers in temp variables and use those
   2657     // to initialize the vector. Eventually we can just directly
   2658     // construct the init_list from casted source members
   2659     var i: usize = 0;
   2660     while (i < init_count) : (i += 1) {
   2661         const mangled_name = try block_scope.makeMangledName(c, "tmp");
   2662         const init_expr = expr.getInit(@as(c_uint, @intCast(i)));
   2663         const tmp_decl_node = try Tag.var_simple.create(c.arena, .{
   2664             .name = mangled_name,
   2665             .init = try transExpr(c, &block_scope.base, init_expr, .used),
   2666         });
   2667         try block_scope.statements.append(tmp_decl_node);
   2668     }
   2669 
   2670     const init_list = try c.arena.alloc(Node, num_elements);
   2671     for (init_list, 0..) |*init, init_index| {
   2672         if (init_index < init_count) {
   2673             const tmp_decl = block_scope.statements.items[init_index];
   2674             const name = tmp_decl.castTag(.var_simple).?.data.name;
   2675             init.* = try Tag.identifier.create(c.arena, name);
   2676         } else {
   2677             init.* = Tag.undefined_literal.init();
   2678         }
   2679     }
   2680 
   2681     const array_init = try Tag.array_init.create(c.arena, .{
   2682         .cond = vector_type,
   2683         .cases = init_list,
   2684     });
   2685     const break_node = try Tag.break_val.create(c.arena, .{
   2686         .label = block_scope.label,
   2687         .val = array_init,
   2688     });
   2689     try block_scope.statements.append(break_node);
   2690 
   2691     return block_scope.complete(c);
   2692 }
   2693 
   2694 fn transInitListExpr(
   2695     c: *Context,
   2696     scope: *Scope,
   2697     expr: *const clang.InitListExpr,
   2698     used: ResultUsed,
   2699 ) TransError!Node {
   2700     const qt = getExprQualType(c, @as(*const clang.Expr, @ptrCast(expr)));
   2701     var qual_type = qt.getTypePtr();
   2702     const source_loc = @as(*const clang.Expr, @ptrCast(expr)).getBeginLoc();
   2703 
   2704     if (qualTypeWasDemotedToOpaque(c, qt)) {
   2705         return fail(c, error.UnsupportedTranslation, source_loc, "cannot initialize opaque type", .{});
   2706     }
   2707 
   2708     if (qual_type.isRecordType()) {
   2709         return maybeSuppressResult(c, used, try transInitListExprRecord(
   2710             c,
   2711             scope,
   2712             source_loc,
   2713             expr,
   2714             qual_type,
   2715         ));
   2716     } else if (qual_type.isArrayType()) {
   2717         return maybeSuppressResult(c, used, try transInitListExprArray(
   2718             c,
   2719             scope,
   2720             source_loc,
   2721             expr,
   2722             qual_type,
   2723         ));
   2724     } else if (qual_type.isVectorType()) {
   2725         return maybeSuppressResult(c, used, try transInitListExprVector(c, scope, source_loc, expr));
   2726     } else {
   2727         const type_name = try c.str(qual_type.getTypeClassName());
   2728         return fail(c, error.UnsupportedType, source_loc, "unsupported initlist type: '{s}'", .{type_name});
   2729     }
   2730 }
   2731 
   2732 fn transZeroInitExpr(
   2733     c: *Context,
   2734     scope: *Scope,
   2735     source_loc: clang.SourceLocation,
   2736     ty: *const clang.Type,
   2737 ) TransError!Node {
   2738     switch (ty.getTypeClass()) {
   2739         .Builtin => {
   2740             const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(ty));
   2741             switch (builtin_ty.getKind()) {
   2742                 .Bool => return Tag.false_literal.init(),
   2743                 .Char_U,
   2744                 .UChar,
   2745                 .Char_S,
   2746                 .Char8,
   2747                 .SChar,
   2748                 .UShort,
   2749                 .UInt,
   2750                 .ULong,
   2751                 .ULongLong,
   2752                 .Short,
   2753                 .Int,
   2754                 .Long,
   2755                 .LongLong,
   2756                 .UInt128,
   2757                 .Int128,
   2758                 .Float,
   2759                 .Double,
   2760                 .Float128,
   2761                 .Float16,
   2762                 .LongDouble,
   2763                 => return Tag.zero_literal.init(),
   2764                 else => return fail(c, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),
   2765             }
   2766         },
   2767         .Pointer => return Tag.null_literal.init(),
   2768         .Typedef => {
   2769             const typedef_ty = @as(*const clang.TypedefType, @ptrCast(ty));
   2770             const typedef_decl = typedef_ty.getDecl();
   2771             return transZeroInitExpr(
   2772                 c,
   2773                 scope,
   2774                 source_loc,
   2775                 typedef_decl.getUnderlyingType().getTypePtr(),
   2776             );
   2777         },
   2778         else => return Tag.std_mem_zeroes.create(c.arena, try transType(c, scope, ty, source_loc)),
   2779     }
   2780 }
   2781 
   2782 fn transImplicitValueInitExpr(
   2783     c: *Context,
   2784     scope: *Scope,
   2785     expr: *const clang.Expr,
   2786 ) TransError!Node {
   2787     const source_loc = expr.getBeginLoc();
   2788     const qt = getExprQualType(c, expr);
   2789     const ty = qt.getTypePtr();
   2790     return transZeroInitExpr(c, scope, source_loc, ty);
   2791 }
   2792 
   2793 /// If a statement can possibly translate to a Zig assignment (either directly because it's
   2794 /// an assignment in C or indirectly via result assignment to `_`) AND it's the sole statement
   2795 /// in the body of an if statement or loop, then we need to put the statement into its own block.
   2796 /// The `else` case here corresponds to statements that could result in an assignment. If a statement
   2797 /// class never needs a block, add its enum to the top prong.
   2798 fn maybeBlockify(c: *Context, scope: *Scope, stmt: *const clang.Stmt) TransError!Node {
   2799     switch (stmt.getStmtClass()) {
   2800         .BreakStmtClass,
   2801         .CompoundStmtClass,
   2802         .ContinueStmtClass,
   2803         .DeclRefExprClass,
   2804         .DeclStmtClass,
   2805         .DoStmtClass,
   2806         .ForStmtClass,
   2807         .IfStmtClass,
   2808         .ReturnStmtClass,
   2809         .NullStmtClass,
   2810         .WhileStmtClass,
   2811         => return transStmt(c, scope, stmt, .unused),
   2812         else => return blockify(c, scope, stmt),
   2813     }
   2814 }
   2815 
   2816 fn blockify(c: *Context, scope: *Scope, stmt: *const clang.Stmt) TransError!Node {
   2817     var block_scope = try Scope.Block.init(c, scope, false);
   2818     defer block_scope.deinit();
   2819     const result = try transStmt(c, &block_scope.base, stmt, .unused);
   2820     try block_scope.statements.append(result);
   2821     return block_scope.complete(c);
   2822 }
   2823 
   2824 fn transIfStmt(
   2825     c: *Context,
   2826     scope: *Scope,
   2827     stmt: *const clang.IfStmt,
   2828 ) TransError!Node {
   2829     // if (c) t
   2830     // if (c) t else e
   2831     var cond_scope = Scope.Condition{
   2832         .base = .{
   2833             .parent = scope,
   2834             .id = .condition,
   2835         },
   2836     };
   2837     defer cond_scope.deinit();
   2838     const cond_expr = @as(*const clang.Expr, @ptrCast(stmt.getCond()));
   2839     const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used);
   2840 
   2841     const then_stmt = stmt.getThen();
   2842     const else_stmt = stmt.getElse();
   2843     const then_class = then_stmt.getStmtClass();
   2844     // block needed to keep else statement from attaching to inner while
   2845     const must_blockify = (else_stmt != null) and switch (then_class) {
   2846         .DoStmtClass, .ForStmtClass, .WhileStmtClass => true,
   2847         else => false,
   2848     };
   2849 
   2850     const then_body = if (must_blockify)
   2851         try blockify(c, scope, then_stmt)
   2852     else
   2853         try maybeBlockify(c, scope, then_stmt);
   2854 
   2855     const else_body = if (else_stmt) |expr|
   2856         try maybeBlockify(c, scope, expr)
   2857     else
   2858         null;
   2859     return Tag.@"if".create(c.arena, .{ .cond = cond, .then = then_body, .@"else" = else_body });
   2860 }
   2861 
   2862 fn transWhileLoop(
   2863     c: *Context,
   2864     scope: *Scope,
   2865     stmt: *const clang.WhileStmt,
   2866 ) TransError!Node {
   2867     var cond_scope = Scope.Condition{
   2868         .base = .{
   2869             .parent = scope,
   2870             .id = .condition,
   2871         },
   2872     };
   2873     defer cond_scope.deinit();
   2874     const cond_expr = @as(*const clang.Expr, @ptrCast(stmt.getCond()));
   2875     const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used);
   2876 
   2877     var loop_scope = Scope{
   2878         .parent = scope,
   2879         .id = .loop,
   2880     };
   2881     const body = try maybeBlockify(c, &loop_scope, stmt.getBody());
   2882     return Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = null });
   2883 }
   2884 
   2885 fn transDoWhileLoop(
   2886     c: *Context,
   2887     scope: *Scope,
   2888     stmt: *const clang.DoStmt,
   2889 ) TransError!Node {
   2890     var loop_scope = Scope{
   2891         .parent = scope,
   2892         .id = .do_loop,
   2893     };
   2894 
   2895     // if (!cond) break;
   2896     var cond_scope = Scope.Condition{
   2897         .base = .{
   2898             .parent = scope,
   2899             .id = .condition,
   2900         },
   2901     };
   2902     defer cond_scope.deinit();
   2903     const cond = try transBoolExpr(c, &cond_scope.base, @as(*const clang.Expr, @ptrCast(stmt.getCond())), .used);
   2904     const if_not_break = switch (cond.tag()) {
   2905         .true_literal => {
   2906             const body_node = try maybeBlockify(c, scope, stmt.getBody());
   2907             return Tag.while_true.create(c.arena, body_node);
   2908         },
   2909         else => try Tag.if_not_break.create(c.arena, cond),
   2910     };
   2911 
   2912     var body_node = try transStmt(c, &loop_scope, stmt.getBody(), .unused);
   2913     if (body_node.isNoreturn(true)) {
   2914         // The body node ends in a noreturn statement. Simply put it in a while (true)
   2915         // in case it contains breaks or continues.
   2916     } else if (stmt.getBody().getStmtClass() == .CompoundStmtClass) {
   2917         // there's already a block in C, so we'll append our condition to it.
   2918         // c: do {
   2919         // c:   a;
   2920         // c:   b;
   2921         // c: } while(c);
   2922         // zig: while (true) {
   2923         // zig:   a;
   2924         // zig:   b;
   2925         // zig:   if (!cond) break;
   2926         // zig: }
   2927         const block = body_node.castTag(.block).?;
   2928         block.data.stmts.len += 1; // This is safe since we reserve one extra space in Scope.Block.complete.
   2929         block.data.stmts[block.data.stmts.len - 1] = if_not_break;
   2930     } else {
   2931         // the C statement is without a block, so we need to create a block to contain it.
   2932         // c: do
   2933         // c:   a;
   2934         // c: while(c);
   2935         // zig: while (true) {
   2936         // zig:   a;
   2937         // zig:   if (!cond) break;
   2938         // zig: }
   2939         const statements = try c.arena.alloc(Node, 2);
   2940         statements[0] = body_node;
   2941         statements[1] = if_not_break;
   2942         body_node = try Tag.block.create(c.arena, .{ .label = null, .stmts = statements });
   2943     }
   2944     return Tag.while_true.create(c.arena, body_node);
   2945 }
   2946 
   2947 fn transForLoop(
   2948     c: *Context,
   2949     scope: *Scope,
   2950     stmt: *const clang.ForStmt,
   2951 ) TransError!Node {
   2952     var loop_scope = Scope{
   2953         .parent = scope,
   2954         .id = .loop,
   2955     };
   2956 
   2957     var block_scope: ?Scope.Block = null;
   2958     defer if (block_scope) |*bs| bs.deinit();
   2959 
   2960     if (stmt.getInit()) |init| {
   2961         block_scope = try Scope.Block.init(c, scope, false);
   2962         loop_scope.parent = &block_scope.?.base;
   2963         const init_node = try transStmt(c, &block_scope.?.base, init, .unused);
   2964         if (init_node.tag() != .declaration) try block_scope.?.statements.append(init_node);
   2965     }
   2966     var cond_scope = Scope.Condition{
   2967         .base = .{
   2968             .parent = &loop_scope,
   2969             .id = .condition,
   2970         },
   2971     };
   2972     defer cond_scope.deinit();
   2973 
   2974     const cond = if (stmt.getCond()) |cond|
   2975         try transBoolExpr(c, &cond_scope.base, cond, .used)
   2976     else
   2977         Tag.true_literal.init();
   2978 
   2979     const cont_expr = if (stmt.getInc()) |incr|
   2980         try transExpr(c, &cond_scope.base, incr, .unused)
   2981     else
   2982         null;
   2983 
   2984     const body = try maybeBlockify(c, &loop_scope, stmt.getBody());
   2985     const while_node = try Tag.@"while".create(c.arena, .{ .cond = cond, .body = body, .cont_expr = cont_expr });
   2986     if (block_scope) |*bs| {
   2987         try bs.statements.append(while_node);
   2988         return try bs.complete(c);
   2989     } else {
   2990         return while_node;
   2991     }
   2992 }
   2993 
   2994 fn transSwitch(
   2995     c: *Context,
   2996     scope: *Scope,
   2997     stmt: *const clang.SwitchStmt,
   2998 ) TransError!Node {
   2999     var loop_scope = Scope{
   3000         .parent = scope,
   3001         .id = .loop,
   3002     };
   3003 
   3004     var block_scope = try Scope.Block.init(c, &loop_scope, false);
   3005     defer block_scope.deinit();
   3006 
   3007     const base_scope = &block_scope.base;
   3008 
   3009     var cond_scope = Scope.Condition{
   3010         .base = .{
   3011             .parent = base_scope,
   3012             .id = .condition,
   3013         },
   3014     };
   3015     defer cond_scope.deinit();
   3016     const switch_expr = try transExpr(c, &cond_scope.base, stmt.getCond(), .used);
   3017 
   3018     var cases = std.ArrayList(Node).init(c.gpa);
   3019     defer cases.deinit();
   3020     var has_default = false;
   3021 
   3022     const body = stmt.getBody();
   3023     assert(body.getStmtClass() == .CompoundStmtClass);
   3024     const compound_stmt = @as(*const clang.CompoundStmt, @ptrCast(body));
   3025     var it = compound_stmt.body_begin();
   3026     const end_it = compound_stmt.body_end();
   3027     // Iterate over switch body and collect all cases.
   3028     // Fallthrough is handled by duplicating statements.
   3029     while (it != end_it) : (it += 1) {
   3030         switch (it[0].getStmtClass()) {
   3031             .CaseStmtClass => {
   3032                 var items = std.ArrayList(Node).init(c.gpa);
   3033                 defer items.deinit();
   3034                 const sub = try transCaseStmt(c, base_scope, it[0], &items);
   3035                 const res = try transSwitchProngStmt(c, base_scope, sub, it, end_it);
   3036 
   3037                 if (items.items.len == 0) {
   3038                     has_default = true;
   3039                     const switch_else = try Tag.switch_else.create(c.arena, res);
   3040                     try cases.append(switch_else);
   3041                 } else {
   3042                     const switch_prong = try Tag.switch_prong.create(c.arena, .{
   3043                         .cases = try c.arena.dupe(Node, items.items),
   3044                         .cond = res,
   3045                     });
   3046                     try cases.append(switch_prong);
   3047                 }
   3048             },
   3049             .DefaultStmtClass => {
   3050                 has_default = true;
   3051                 const default_stmt = @as(*const clang.DefaultStmt, @ptrCast(it[0]));
   3052 
   3053                 var sub = default_stmt.getSubStmt();
   3054                 while (true) switch (sub.getStmtClass()) {
   3055                     .CaseStmtClass => sub = @as(*const clang.CaseStmt, @ptrCast(sub)).getSubStmt(),
   3056                     .DefaultStmtClass => sub = @as(*const clang.DefaultStmt, @ptrCast(sub)).getSubStmt(),
   3057                     else => break,
   3058                 };
   3059 
   3060                 const res = try transSwitchProngStmt(c, base_scope, sub, it, end_it);
   3061 
   3062                 const switch_else = try Tag.switch_else.create(c.arena, res);
   3063                 try cases.append(switch_else);
   3064             },
   3065             else => {}, // collected in transSwitchProngStmt
   3066         }
   3067     }
   3068 
   3069     if (!has_default) {
   3070         const else_prong = try Tag.switch_else.create(c.arena, Tag.empty_block.init());
   3071         try cases.append(else_prong);
   3072     }
   3073 
   3074     const switch_node = try Tag.@"switch".create(c.arena, .{
   3075         .cond = switch_expr,
   3076         .cases = try c.arena.dupe(Node, cases.items),
   3077     });
   3078     try block_scope.statements.append(switch_node);
   3079     try block_scope.statements.append(Tag.@"break".init());
   3080     const while_body = try block_scope.complete(c);
   3081 
   3082     return Tag.while_true.create(c.arena, while_body);
   3083 }
   3084 
   3085 /// Collects all items for this case, returns the first statement after the labels.
   3086 /// If items ends up empty, the prong should be translated as an else.
   3087 fn transCaseStmt(c: *Context, scope: *Scope, stmt: *const clang.Stmt, items: *std.ArrayList(Node)) TransError!*const clang.Stmt {
   3088     var sub = stmt;
   3089     var seen_default = false;
   3090     while (true) {
   3091         switch (sub.getStmtClass()) {
   3092             .DefaultStmtClass => {
   3093                 seen_default = true;
   3094                 items.items.len = 0;
   3095                 const default_stmt = @as(*const clang.DefaultStmt, @ptrCast(sub));
   3096                 sub = default_stmt.getSubStmt();
   3097             },
   3098             .CaseStmtClass => {
   3099                 const case_stmt = @as(*const clang.CaseStmt, @ptrCast(sub));
   3100 
   3101                 if (seen_default) {
   3102                     items.items.len = 0;
   3103                     sub = case_stmt.getSubStmt();
   3104                     continue;
   3105                 }
   3106 
   3107                 const expr = if (case_stmt.getRHS()) |rhs| blk: {
   3108                     const lhs_node = try transExprCoercing(c, scope, case_stmt.getLHS(), .used);
   3109                     const rhs_node = try transExprCoercing(c, scope, rhs, .used);
   3110 
   3111                     break :blk try Tag.ellipsis3.create(c.arena, .{ .lhs = lhs_node, .rhs = rhs_node });
   3112                 } else try transExprCoercing(c, scope, case_stmt.getLHS(), .used);
   3113 
   3114                 try items.append(expr);
   3115                 sub = case_stmt.getSubStmt();
   3116             },
   3117             else => return sub,
   3118         }
   3119     }
   3120 }
   3121 
   3122 /// Collects all statements seen by this case into a block.
   3123 /// Avoids creating a block if the first statement is a break or return.
   3124 fn transSwitchProngStmt(
   3125     c: *Context,
   3126     scope: *Scope,
   3127     stmt: *const clang.Stmt,
   3128     parent_it: clang.CompoundStmt.ConstBodyIterator,
   3129     parent_end_it: clang.CompoundStmt.ConstBodyIterator,
   3130 ) TransError!Node {
   3131     switch (stmt.getStmtClass()) {
   3132         .BreakStmtClass => return Tag.@"break".init(),
   3133         .ReturnStmtClass => return transStmt(c, scope, stmt, .unused),
   3134         .CaseStmtClass, .DefaultStmtClass => unreachable,
   3135         else => {
   3136             var block_scope = try Scope.Block.init(c, scope, false);
   3137             defer block_scope.deinit();
   3138 
   3139             // we do not need to translate `stmt` since it is the first stmt of `parent_it`
   3140             try transSwitchProngStmtInline(c, &block_scope, parent_it, parent_end_it);
   3141             return try block_scope.complete(c);
   3142         },
   3143     }
   3144 }
   3145 
   3146 /// Collects all statements seen by this case into a block.
   3147 fn transSwitchProngStmtInline(
   3148     c: *Context,
   3149     block: *Scope.Block,
   3150     start_it: clang.CompoundStmt.ConstBodyIterator,
   3151     end_it: clang.CompoundStmt.ConstBodyIterator,
   3152 ) TransError!void {
   3153     var it = start_it;
   3154     while (it != end_it) : (it += 1) {
   3155         switch (it[0].getStmtClass()) {
   3156             .ReturnStmtClass => {
   3157                 const result = try transStmt(c, &block.base, it[0], .unused);
   3158                 try block.statements.append(result);
   3159                 return;
   3160             },
   3161             .BreakStmtClass => {
   3162                 try block.statements.append(Tag.@"break".init());
   3163                 return;
   3164             },
   3165             .CaseStmtClass => {
   3166                 var sub = @as(*const clang.CaseStmt, @ptrCast(it[0])).getSubStmt();
   3167                 while (true) switch (sub.getStmtClass()) {
   3168                     .CaseStmtClass => sub = @as(*const clang.CaseStmt, @ptrCast(sub)).getSubStmt(),
   3169                     .DefaultStmtClass => sub = @as(*const clang.DefaultStmt, @ptrCast(sub)).getSubStmt(),
   3170                     else => break,
   3171                 };
   3172                 const result = try transStmt(c, &block.base, sub, .unused);
   3173                 assert(result.tag() != .declaration);
   3174                 try block.statements.append(result);
   3175                 if (result.isNoreturn(true)) {
   3176                     return;
   3177                 }
   3178             },
   3179             .DefaultStmtClass => {
   3180                 var sub = @as(*const clang.DefaultStmt, @ptrCast(it[0])).getSubStmt();
   3181                 while (true) switch (sub.getStmtClass()) {
   3182                     .CaseStmtClass => sub = @as(*const clang.CaseStmt, @ptrCast(sub)).getSubStmt(),
   3183                     .DefaultStmtClass => sub = @as(*const clang.DefaultStmt, @ptrCast(sub)).getSubStmt(),
   3184                     else => break,
   3185                 };
   3186                 const result = try transStmt(c, &block.base, sub, .unused);
   3187                 assert(result.tag() != .declaration);
   3188                 try block.statements.append(result);
   3189                 if (result.isNoreturn(true)) {
   3190                     return;
   3191                 }
   3192             },
   3193             .CompoundStmtClass => {
   3194                 const result = try transCompoundStmt(c, &block.base, @as(*const clang.CompoundStmt, @ptrCast(it[0])));
   3195                 try block.statements.append(result);
   3196                 if (result.isNoreturn(true)) {
   3197                     return;
   3198                 }
   3199             },
   3200             else => {
   3201                 const result = try transStmt(c, &block.base, it[0], .unused);
   3202                 switch (result.tag()) {
   3203                     .declaration, .empty_block => {},
   3204                     else => try block.statements.append(result),
   3205                 }
   3206             },
   3207         }
   3208     }
   3209     return;
   3210 }
   3211 
   3212 fn transConstantExpr(c: *Context, scope: *Scope, expr: *const clang.Expr, used: ResultUsed) TransError!Node {
   3213     var result: clang.ExprEvalResult = undefined;
   3214     if (!expr.evaluateAsConstantExpr(&result, .Normal, c.clang_context))
   3215         return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "invalid constant expression", .{});
   3216 
   3217     switch (result.Val.getKind()) {
   3218         .Int => {
   3219             // See comment in `transIntegerLiteral` for why this code is here.
   3220             // @as(T, x)
   3221             const expr_base = @as(*const clang.Expr, @ptrCast(expr));
   3222             const as_node = try Tag.as.create(c.arena, .{
   3223                 .lhs = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc()),
   3224                 .rhs = try transCreateNodeAPInt(c, result.Val.getInt()),
   3225             });
   3226             return maybeSuppressResult(c, used, as_node);
   3227         },
   3228         else => |kind| {
   3229             return fail(c, error.UnsupportedTranslation, expr.getBeginLoc(), "unsupported constant expression kind '{}'", .{kind});
   3230         },
   3231     }
   3232 }
   3233 
   3234 fn transPredefinedExpr(c: *Context, scope: *Scope, expr: *const clang.PredefinedExpr, used: ResultUsed) TransError!Node {
   3235     return transStringLiteral(c, scope, expr.getFunctionName(), used);
   3236 }
   3237 
   3238 fn transCreateCharLitNode(c: *Context, narrow: bool, val: u32) TransError!Node {
   3239     return Tag.char_literal.create(c.arena, if (narrow)
   3240         try std.fmt.allocPrint(c.arena, "'{'}'", .{std.zig.fmtEscapes(&.{@as(u8, @intCast(val))})})
   3241     else
   3242         try std.fmt.allocPrint(c.arena, "'\\u{{{x}}}'", .{val}));
   3243 }
   3244 
   3245 fn transCharLiteral(
   3246     c: *Context,
   3247     scope: *Scope,
   3248     stmt: *const clang.CharacterLiteral,
   3249     result_used: ResultUsed,
   3250     suppress_as: SuppressCast,
   3251 ) TransError!Node {
   3252     const kind = stmt.getKind();
   3253     const val = stmt.getValue();
   3254     const narrow = kind == .Ascii or kind == .UTF8;
   3255     // C has a somewhat obscure feature called multi-character character constant
   3256     // e.g. 'abcd'
   3257     const int_lit_node = if (kind == .Ascii and val > 255)
   3258         try transCreateNodeNumber(c, val, .int)
   3259     else
   3260         try transCreateCharLitNode(c, narrow, val);
   3261 
   3262     if (suppress_as == .no_as) {
   3263         return maybeSuppressResult(c, result_used, int_lit_node);
   3264     }
   3265     // See comment in `transIntegerLiteral` for why this code is here.
   3266     // @as(T, x)
   3267     const expr_base = @as(*const clang.Expr, @ptrCast(stmt));
   3268     const as_node = try Tag.as.create(c.arena, .{
   3269         .lhs = try transQualType(c, scope, expr_base.getType(), expr_base.getBeginLoc()),
   3270         .rhs = int_lit_node,
   3271     });
   3272     return maybeSuppressResult(c, result_used, as_node);
   3273 }
   3274 
   3275 fn transStmtExpr(c: *Context, scope: *Scope, stmt: *const clang.StmtExpr, used: ResultUsed) TransError!Node {
   3276     const comp = stmt.getSubStmt();
   3277     if (used == .unused) {
   3278         return transCompoundStmt(c, scope, comp);
   3279     }
   3280     var block_scope = try Scope.Block.init(c, scope, true);
   3281     defer block_scope.deinit();
   3282 
   3283     var it = comp.body_begin();
   3284     const end_it = comp.body_end();
   3285     while (it != end_it - 1) : (it += 1) {
   3286         const result = try transStmt(c, &block_scope.base, it[0], .unused);
   3287         switch (result.tag()) {
   3288             .declaration, .empty_block => {},
   3289             else => try block_scope.statements.append(result),
   3290         }
   3291     }
   3292 
   3293     const last_result = try transStmt(c, &block_scope.base, it[0], .used);
   3294     switch (last_result.tag()) {
   3295         .declaration, .empty_block => {},
   3296         else => {
   3297             const break_node = try Tag.break_val.create(c.arena, .{
   3298                 .label = block_scope.label,
   3299                 .val = last_result,
   3300             });
   3301             try block_scope.statements.append(break_node);
   3302         },
   3303     }
   3304     const res = try block_scope.complete(c);
   3305     return maybeSuppressResult(c, used, res);
   3306 }
   3307 
   3308 fn transMemberExpr(c: *Context, scope: *Scope, stmt: *const clang.MemberExpr, result_used: ResultUsed) TransError!Node {
   3309     var container_node = try transExpr(c, scope, stmt.getBase(), .used);
   3310     if (stmt.isArrow()) {
   3311         container_node = try Tag.deref.create(c.arena, container_node);
   3312     }
   3313 
   3314     const member_decl = stmt.getMemberDecl();
   3315     const name = blk: {
   3316         const decl_kind = @as(*const clang.Decl, @ptrCast(member_decl)).getKind();
   3317         // If we're referring to a anonymous struct/enum find the bogus name
   3318         // we've assigned to it during the RecordDecl translation
   3319         if (decl_kind == .Field) {
   3320             const field_decl = @as(*const clang.FieldDecl, @ptrCast(member_decl));
   3321             if (field_decl.isAnonymousStructOrUnion()) {
   3322                 const name = c.decl_table.get(@intFromPtr(field_decl.getCanonicalDecl())).?;
   3323                 break :blk try c.arena.dupe(u8, name);
   3324             }
   3325         }
   3326         const decl = @as(*const clang.NamedDecl, @ptrCast(member_decl));
   3327         break :blk try c.str(decl.getName_bytes_begin());
   3328     };
   3329 
   3330     var node = try Tag.field_access.create(c.arena, .{ .lhs = container_node, .field_name = name });
   3331     if (exprIsFlexibleArrayRef(c, @as(*const clang.Expr, @ptrCast(stmt)))) {
   3332         node = try Tag.call.create(c.arena, .{ .lhs = node, .args = &.{} });
   3333     }
   3334     return maybeSuppressResult(c, result_used, node);
   3335 }
   3336 
   3337 /// ptr[subscr] (`subscr` is a signed integer expression, `ptr` a pointer) becomes:
   3338 /// (blk: {
   3339 ///     const tmp = subscr;
   3340 ///     if (tmp >= 0) break :blk ptr + @intCast(usize, tmp) else break :blk ptr - ~@bitCast(usize, @intCast(isize, tmp) +% -1);
   3341 /// }).*
   3342 /// Todo: rip this out once `[*]T + isize` becomes valid.
   3343 fn transSignedArrayAccess(
   3344     c: *Context,
   3345     scope: *Scope,
   3346     container_expr: *const clang.Expr,
   3347     subscr_expr: *const clang.Expr,
   3348     result_used: ResultUsed,
   3349 ) TransError!Node {
   3350     var block_scope = try Scope.Block.init(c, scope, true);
   3351     defer block_scope.deinit();
   3352 
   3353     const tmp = try block_scope.makeMangledName(c, "tmp");
   3354 
   3355     const subscr_node = try transExpr(c, &block_scope.base, subscr_expr, .used);
   3356     const subscr_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = subscr_node });
   3357     try block_scope.statements.append(subscr_decl);
   3358 
   3359     const tmp_ref = try Tag.identifier.create(c.arena, tmp);
   3360 
   3361     const container_node = try transExpr(c, &block_scope.base, container_expr, .used);
   3362 
   3363     const cond_node = try Tag.greater_than_equal.create(c.arena, .{ .lhs = tmp_ref, .rhs = Tag.zero_literal.init() });
   3364 
   3365     const then_value = try Tag.add.create(c.arena, .{
   3366         .lhs = container_node,
   3367         .rhs = try Tag.as.create(c.arena, .{
   3368             .lhs = try Tag.type.create(c.arena, "usize"),
   3369             .rhs = try Tag.int_cast.create(c.arena, tmp_ref),
   3370         }),
   3371     });
   3372 
   3373     const then_body = try Tag.break_val.create(c.arena, .{
   3374         .label = block_scope.label,
   3375         .val = then_value,
   3376     });
   3377 
   3378     const minuend = container_node;
   3379     const signed_size = try Tag.as.create(c.arena, .{
   3380         .lhs = try Tag.type.create(c.arena, "isize"),
   3381         .rhs = try Tag.int_cast.create(c.arena, tmp_ref),
   3382     });
   3383     const to_cast = try Tag.add_wrap.create(c.arena, .{
   3384         .lhs = signed_size,
   3385         .rhs = try Tag.negate.create(c.arena, Tag.one_literal.init()),
   3386     });
   3387     const bitcast_node = try Tag.as.create(c.arena, .{
   3388         .lhs = try Tag.type.create(c.arena, "usize"),
   3389         .rhs = try Tag.bit_cast.create(c.arena, to_cast),
   3390     });
   3391     const subtrahend = try Tag.bit_not.create(c.arena, bitcast_node);
   3392     const difference = try Tag.sub.create(c.arena, .{
   3393         .lhs = minuend,
   3394         .rhs = subtrahend,
   3395     });
   3396     const else_body = try Tag.break_val.create(c.arena, .{
   3397         .label = block_scope.label,
   3398         .val = difference,
   3399     });
   3400 
   3401     const if_node = try Tag.@"if".create(c.arena, .{
   3402         .cond = cond_node,
   3403         .then = then_body,
   3404         .@"else" = else_body,
   3405     });
   3406 
   3407     try block_scope.statements.append(if_node);
   3408     const block_node = try block_scope.complete(c);
   3409 
   3410     const derefed = try Tag.deref.create(c.arena, block_node);
   3411 
   3412     return maybeSuppressResult(c, result_used, derefed);
   3413 }
   3414 
   3415 fn transArrayAccess(c: *Context, scope: *Scope, stmt: *const clang.ArraySubscriptExpr, result_used: ResultUsed) TransError!Node {
   3416     const base_stmt = stmt.getBase();
   3417     const base_qt = getExprQualType(c, base_stmt);
   3418     const is_vector = cIsVector(base_qt);
   3419 
   3420     const subscr_expr = stmt.getIdx();
   3421     const subscr_qt = getExprQualType(c, subscr_expr);
   3422     const is_longlong = cIsLongLongInteger(subscr_qt);
   3423     const is_signed = cIsSignedInteger(subscr_qt);
   3424     const is_nonnegative_int_literal = cIsNonNegativeIntLiteral(c, subscr_expr);
   3425 
   3426     // Unwrap the base statement if it's an array decayed to a bare pointer type
   3427     // so that we index the array itself
   3428     var unwrapped_base = base_stmt;
   3429     if (@as(*const clang.Stmt, @ptrCast(base_stmt)).getStmtClass() == .ImplicitCastExprClass) {
   3430         const implicit_cast = @as(*const clang.ImplicitCastExpr, @ptrCast(base_stmt));
   3431 
   3432         if (implicit_cast.getCastKind() == .ArrayToPointerDecay) {
   3433             unwrapped_base = implicit_cast.getSubExpr();
   3434         }
   3435     }
   3436 
   3437     // Special case: actual pointer (not decayed array) and signed integer subscript
   3438     // See discussion at https://github.com/ziglang/zig/pull/8589
   3439     if (is_signed and (base_stmt == unwrapped_base) and !is_vector and !is_nonnegative_int_literal)
   3440         return transSignedArrayAccess(c, scope, base_stmt, subscr_expr, result_used);
   3441 
   3442     const container_node = try transExpr(c, scope, unwrapped_base, .used);
   3443     const rhs = if (is_longlong or is_signed) blk: {
   3444         // check if long long first so that signed long long doesn't just become unsigned long long
   3445         const typeid_node = if (is_longlong) try Tag.type.create(c.arena, "usize") else try transQualTypeIntWidthOf(c, subscr_qt, false);
   3446         break :blk try Tag.as.create(c.arena, .{
   3447             .lhs = typeid_node,
   3448             .rhs = try Tag.int_cast.create(
   3449                 c.arena,
   3450                 try transExpr(c, scope, subscr_expr, .used),
   3451             ),
   3452         });
   3453     } else try transExpr(c, scope, subscr_expr, .used);
   3454 
   3455     const node = try Tag.array_access.create(c.arena, .{
   3456         .lhs = container_node,
   3457         .rhs = rhs,
   3458     });
   3459     return maybeSuppressResult(c, result_used, node);
   3460 }
   3461 
   3462 /// Check if an expression is ultimately a reference to a function declaration
   3463 /// (which means it should not be unwrapped with `.?` in translated code)
   3464 fn cIsFunctionDeclRef(expr: *const clang.Expr) bool {
   3465     switch (expr.getStmtClass()) {
   3466         .ParenExprClass => {
   3467             const op_expr = @as(*const clang.ParenExpr, @ptrCast(expr)).getSubExpr();
   3468             return cIsFunctionDeclRef(op_expr);
   3469         },
   3470         .DeclRefExprClass => {
   3471             const decl_ref = @as(*const clang.DeclRefExpr, @ptrCast(expr));
   3472             const value_decl = decl_ref.getDecl();
   3473             const qt = value_decl.getType();
   3474             return qualTypeChildIsFnProto(qt);
   3475         },
   3476         .ImplicitCastExprClass => {
   3477             const implicit_cast = @as(*const clang.ImplicitCastExpr, @ptrCast(expr));
   3478             const cast_kind = implicit_cast.getCastKind();
   3479             if (cast_kind == .BuiltinFnToFnPtr) return true;
   3480             if (cast_kind == .FunctionToPointerDecay) {
   3481                 return cIsFunctionDeclRef(implicit_cast.getSubExpr());
   3482             }
   3483             return false;
   3484         },
   3485         .UnaryOperatorClass => {
   3486             const un_op = @as(*const clang.UnaryOperator, @ptrCast(expr));
   3487             const opcode = un_op.getOpcode();
   3488             return (opcode == .AddrOf or opcode == .Deref) and cIsFunctionDeclRef(un_op.getSubExpr());
   3489         },
   3490         .GenericSelectionExprClass => {
   3491             const gen_sel = @as(*const clang.GenericSelectionExpr, @ptrCast(expr));
   3492             return cIsFunctionDeclRef(gen_sel.getResultExpr());
   3493         },
   3494         else => return false,
   3495     }
   3496 }
   3497 
   3498 fn transCallExpr(c: *Context, scope: *Scope, stmt: *const clang.CallExpr, result_used: ResultUsed) TransError!Node {
   3499     const callee = stmt.getCallee();
   3500     const raw_fn_expr = try transExpr(c, scope, callee, .used);
   3501 
   3502     var is_ptr = false;
   3503     const fn_ty = qualTypeGetFnProto(callee.getType(), &is_ptr);
   3504 
   3505     const fn_expr = if (is_ptr and fn_ty != null and !cIsFunctionDeclRef(callee))
   3506         try Tag.unwrap.create(c.arena, raw_fn_expr)
   3507     else
   3508         raw_fn_expr;
   3509 
   3510     const num_args = stmt.getNumArgs();
   3511     const args = try c.arena.alloc(Node, num_args);
   3512 
   3513     const c_args = stmt.getArgs();
   3514     var i: usize = 0;
   3515     while (i < num_args) : (i += 1) {
   3516         var arg = try transExpr(c, scope, c_args[i], .used);
   3517 
   3518         // In C the result type of a boolean expression is int. If this result is passed as
   3519         // an argument to a function whose parameter is also int, there is no cast. Therefore
   3520         // in Zig we'll need to cast it from bool to u1 (which will safely coerce to c_int).
   3521         if (fn_ty) |ty| {
   3522             switch (ty) {
   3523                 .Proto => |fn_proto| {
   3524                     const param_count = fn_proto.getNumParams();
   3525                     if (i < param_count) {
   3526                         const param_qt = fn_proto.getParamType(@as(c_uint, @intCast(i)));
   3527                         if (isBoolRes(arg) and cIsNativeInt(param_qt)) {
   3528                             arg = try Tag.int_from_bool.create(c.arena, arg);
   3529                         } else if (arg.tag() == .string_literal and qualTypeIsCharStar(param_qt)) {
   3530                             const loc = @as(*const clang.Stmt, @ptrCast(stmt)).getBeginLoc();
   3531                             const dst_type_node = try transQualType(c, scope, param_qt, loc);
   3532                             arg = try removeCVQualifiers(c, dst_type_node, arg);
   3533                         }
   3534                     }
   3535                 },
   3536                 else => {},
   3537             }
   3538         }
   3539         args[i] = arg;
   3540     }
   3541     const node = try Tag.call.create(c.arena, .{ .lhs = fn_expr, .args = args });
   3542     if (fn_ty) |ty| {
   3543         const canon = ty.getReturnType().getCanonicalType();
   3544         const ret_ty = canon.getTypePtr();
   3545         if (ret_ty.isVoidType()) {
   3546             return node;
   3547         }
   3548     }
   3549 
   3550     return maybeSuppressResult(c, result_used, node);
   3551 }
   3552 
   3553 const ClangFunctionType = union(enum) {
   3554     Proto: *const clang.FunctionProtoType,
   3555     NoProto: *const clang.FunctionType,
   3556 
   3557     fn getReturnType(self: @This()) clang.QualType {
   3558         switch (@as(meta.Tag(@This()), self)) {
   3559             .Proto => return self.Proto.getReturnType(),
   3560             .NoProto => return self.NoProto.getReturnType(),
   3561         }
   3562     }
   3563 };
   3564 
   3565 fn qualTypeGetFnProto(qt: clang.QualType, is_ptr: *bool) ?ClangFunctionType {
   3566     const canon = qt.getCanonicalType();
   3567     var ty = canon.getTypePtr();
   3568     is_ptr.* = false;
   3569 
   3570     if (ty.getTypeClass() == .Pointer) {
   3571         is_ptr.* = true;
   3572         const child_qt = ty.getPointeeType();
   3573         ty = child_qt.getTypePtr();
   3574     }
   3575     if (ty.getTypeClass() == .FunctionProto) {
   3576         return ClangFunctionType{ .Proto = @as(*const clang.FunctionProtoType, @ptrCast(ty)) };
   3577     }
   3578     if (ty.getTypeClass() == .FunctionNoProto) {
   3579         return ClangFunctionType{ .NoProto = @as(*const clang.FunctionType, @ptrCast(ty)) };
   3580     }
   3581     return null;
   3582 }
   3583 
   3584 fn transUnaryExprOrTypeTraitExpr(
   3585     c: *Context,
   3586     scope: *Scope,
   3587     stmt: *const clang.UnaryExprOrTypeTraitExpr,
   3588     result_used: ResultUsed,
   3589 ) TransError!Node {
   3590     const loc = stmt.getBeginLoc();
   3591     const type_node = try transQualType(c, scope, stmt.getTypeOfArgument(), loc);
   3592 
   3593     const kind = stmt.getKind();
   3594     const node = switch (kind) {
   3595         .SizeOf => try Tag.sizeof.create(c.arena, type_node),
   3596         .AlignOf => try Tag.alignof.create(c.arena, type_node),
   3597         .PreferredAlignOf,
   3598         .VecStep,
   3599         .OpenMPRequiredSimdAlign,
   3600         => return fail(
   3601             c,
   3602             error.UnsupportedTranslation,
   3603             loc,
   3604             "unsupported type trait kind {}",
   3605             .{kind},
   3606         ),
   3607     };
   3608     return maybeSuppressResult(c, result_used, node);
   3609 }
   3610 
   3611 fn qualTypeHasWrappingOverflow(qt: clang.QualType) bool {
   3612     if (cIsUnsignedInteger(qt)) {
   3613         // unsigned integer overflow wraps around.
   3614         return true;
   3615     } else {
   3616         // float, signed integer, and pointer overflow is undefined behavior.
   3617         return false;
   3618     }
   3619 }
   3620 
   3621 fn transUnaryOperator(c: *Context, scope: *Scope, stmt: *const clang.UnaryOperator, used: ResultUsed) TransError!Node {
   3622     const op_expr = stmt.getSubExpr();
   3623     switch (stmt.getOpcode()) {
   3624         .PostInc => if (qualTypeHasWrappingOverflow(stmt.getType()))
   3625             return transCreatePostCrement(c, scope, stmt, .add_wrap_assign, used)
   3626         else
   3627             return transCreatePostCrement(c, scope, stmt, .add_assign, used),
   3628         .PostDec => if (qualTypeHasWrappingOverflow(stmt.getType()))
   3629             return transCreatePostCrement(c, scope, stmt, .sub_wrap_assign, used)
   3630         else
   3631             return transCreatePostCrement(c, scope, stmt, .sub_assign, used),
   3632         .PreInc => if (qualTypeHasWrappingOverflow(stmt.getType()))
   3633             return transCreatePreCrement(c, scope, stmt, .add_wrap_assign, used)
   3634         else
   3635             return transCreatePreCrement(c, scope, stmt, .add_assign, used),
   3636         .PreDec => if (qualTypeHasWrappingOverflow(stmt.getType()))
   3637             return transCreatePreCrement(c, scope, stmt, .sub_wrap_assign, used)
   3638         else
   3639             return transCreatePreCrement(c, scope, stmt, .sub_assign, used),
   3640         .AddrOf => {
   3641             return Tag.address_of.create(c.arena, try transExpr(c, scope, op_expr, used));
   3642         },
   3643         .Deref => {
   3644             if (qualTypeWasDemotedToOpaque(c, stmt.getType()))
   3645                 return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "cannot dereference opaque type", .{});
   3646 
   3647             const node = try transExpr(c, scope, op_expr, used);
   3648             var is_ptr = false;
   3649             const fn_ty = qualTypeGetFnProto(op_expr.getType(), &is_ptr);
   3650             if (fn_ty != null and is_ptr)
   3651                 return node;
   3652             return Tag.deref.create(c.arena, node);
   3653         },
   3654         .Plus => return transExpr(c, scope, op_expr, used),
   3655         .Minus => {
   3656             if (!qualTypeHasWrappingOverflow(op_expr.getType())) {
   3657                 const sub_expr_node = try transExpr(c, scope, op_expr, .used);
   3658                 const to_negate = if (isBoolRes(sub_expr_node)) blk: {
   3659                     const ty_node = try Tag.type.create(c.arena, "c_int");
   3660                     const int_node = try Tag.int_from_bool.create(c.arena, sub_expr_node);
   3661                     break :blk try Tag.as.create(c.arena, .{ .lhs = ty_node, .rhs = int_node });
   3662                 } else sub_expr_node;
   3663                 return Tag.negate.create(c.arena, to_negate);
   3664             } else if (cIsUnsignedInteger(op_expr.getType())) {
   3665                 // use -% x for unsigned integers
   3666                 return Tag.negate_wrap.create(c.arena, try transExpr(c, scope, op_expr, .used));
   3667             } else return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "C negation with non float non integer", .{});
   3668         },
   3669         .Not => {
   3670             return Tag.bit_not.create(c.arena, try transExpr(c, scope, op_expr, .used));
   3671         },
   3672         .LNot => {
   3673             return Tag.not.create(c.arena, try transBoolExpr(c, scope, op_expr, .used));
   3674         },
   3675         .Extension => {
   3676             return transExpr(c, scope, stmt.getSubExpr(), used);
   3677         },
   3678         else => return fail(c, error.UnsupportedTranslation, stmt.getBeginLoc(), "unsupported C translation {}", .{stmt.getOpcode()}),
   3679     }
   3680 }
   3681 
   3682 fn transCreatePreCrement(
   3683     c: *Context,
   3684     scope: *Scope,
   3685     stmt: *const clang.UnaryOperator,
   3686     op: Tag,
   3687     used: ResultUsed,
   3688 ) TransError!Node {
   3689     const op_expr = stmt.getSubExpr();
   3690 
   3691     if (used == .unused) {
   3692         // common case
   3693         // c: ++expr
   3694         // zig: expr += 1
   3695         const lhs = try transExpr(c, scope, op_expr, .used);
   3696         const rhs = Tag.one_literal.init();
   3697         return transCreateNodeInfixOp(c, op, lhs, rhs, .used);
   3698     }
   3699     // worst case
   3700     // c: ++expr
   3701     // zig: (blk: {
   3702     // zig:     const _ref = &expr;
   3703     // zig:     _ref.* += 1;
   3704     // zig:     break :blk _ref.*
   3705     // zig: })
   3706     var block_scope = try Scope.Block.init(c, scope, true);
   3707     defer block_scope.deinit();
   3708 
   3709     const ref = try block_scope.reserveMangledName(c, "ref");
   3710     const expr = try transExpr(c, &block_scope.base, op_expr, .used);
   3711     const addr_of = try Tag.address_of.create(c.arena, expr);
   3712     const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of });
   3713     try block_scope.statements.append(ref_decl);
   3714 
   3715     const lhs_node = try Tag.identifier.create(c.arena, ref);
   3716     const ref_node = try Tag.deref.create(c.arena, lhs_node);
   3717     const node = try transCreateNodeInfixOp(c, op, ref_node, Tag.one_literal.init(), .used);
   3718     try block_scope.statements.append(node);
   3719 
   3720     const break_node = try Tag.break_val.create(c.arena, .{
   3721         .label = block_scope.label,
   3722         .val = ref_node,
   3723     });
   3724     try block_scope.statements.append(break_node);
   3725     return block_scope.complete(c);
   3726 }
   3727 
   3728 fn transCreatePostCrement(
   3729     c: *Context,
   3730     scope: *Scope,
   3731     stmt: *const clang.UnaryOperator,
   3732     op: Tag,
   3733     used: ResultUsed,
   3734 ) TransError!Node {
   3735     const op_expr = stmt.getSubExpr();
   3736 
   3737     if (used == .unused) {
   3738         // common case
   3739         // c: expr++
   3740         // zig: expr += 1
   3741         const lhs = try transExpr(c, scope, op_expr, .used);
   3742         const rhs = Tag.one_literal.init();
   3743         return transCreateNodeInfixOp(c, op, lhs, rhs, .used);
   3744     }
   3745     // worst case
   3746     // c: expr++
   3747     // zig: (blk: {
   3748     // zig:     const _ref = &expr;
   3749     // zig:     const _tmp = _ref.*;
   3750     // zig:     _ref.* += 1;
   3751     // zig:     break :blk _tmp
   3752     // zig: })
   3753     var block_scope = try Scope.Block.init(c, scope, true);
   3754     defer block_scope.deinit();
   3755     const ref = try block_scope.reserveMangledName(c, "ref");
   3756     const tmp = try block_scope.reserveMangledName(c, "tmp");
   3757 
   3758     const expr = try transExpr(c, &block_scope.base, op_expr, .used);
   3759     const addr_of = try Tag.address_of.create(c.arena, expr);
   3760     const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of });
   3761     try block_scope.statements.append(ref_decl);
   3762 
   3763     const lhs_node = try Tag.identifier.create(c.arena, ref);
   3764     const ref_node = try Tag.deref.create(c.arena, lhs_node);
   3765 
   3766     const tmp_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = ref_node });
   3767     try block_scope.statements.append(tmp_decl);
   3768 
   3769     const node = try transCreateNodeInfixOp(c, op, ref_node, Tag.one_literal.init(), .used);
   3770     try block_scope.statements.append(node);
   3771 
   3772     const break_node = try Tag.break_val.create(c.arena, .{
   3773         .label = block_scope.label,
   3774         .val = try Tag.identifier.create(c.arena, tmp),
   3775     });
   3776     try block_scope.statements.append(break_node);
   3777     return block_scope.complete(c);
   3778 }
   3779 
   3780 fn transCompoundAssignOperator(c: *Context, scope: *Scope, stmt: *const clang.CompoundAssignOperator, used: ResultUsed) TransError!Node {
   3781     switch (stmt.getOpcode()) {
   3782         .MulAssign => if (qualTypeHasWrappingOverflow(stmt.getType()))
   3783             return transCreateCompoundAssign(c, scope, stmt, .mul_wrap_assign, used)
   3784         else
   3785             return transCreateCompoundAssign(c, scope, stmt, .mul_assign, used),
   3786         .AddAssign => if (qualTypeHasWrappingOverflow(stmt.getType()))
   3787             return transCreateCompoundAssign(c, scope, stmt, .add_wrap_assign, used)
   3788         else
   3789             return transCreateCompoundAssign(c, scope, stmt, .add_assign, used),
   3790         .SubAssign => if (qualTypeHasWrappingOverflow(stmt.getType()))
   3791             return transCreateCompoundAssign(c, scope, stmt, .sub_wrap_assign, used)
   3792         else
   3793             return transCreateCompoundAssign(c, scope, stmt, .sub_assign, used),
   3794         .DivAssign => return transCreateCompoundAssign(c, scope, stmt, .div_assign, used),
   3795         .RemAssign => return transCreateCompoundAssign(c, scope, stmt, .mod_assign, used),
   3796         .ShlAssign => return transCreateCompoundAssign(c, scope, stmt, .shl_assign, used),
   3797         .ShrAssign => return transCreateCompoundAssign(c, scope, stmt, .shr_assign, used),
   3798         .AndAssign => return transCreateCompoundAssign(c, scope, stmt, .bit_and_assign, used),
   3799         .XorAssign => return transCreateCompoundAssign(c, scope, stmt, .bit_xor_assign, used),
   3800         .OrAssign => return transCreateCompoundAssign(c, scope, stmt, .bit_or_assign, used),
   3801         else => return fail(
   3802             c,
   3803             error.UnsupportedTranslation,
   3804             stmt.getBeginLoc(),
   3805             "unsupported C translation {}",
   3806             .{stmt.getOpcode()},
   3807         ),
   3808     }
   3809 }
   3810 
   3811 fn transCreateCompoundAssign(
   3812     c: *Context,
   3813     scope: *Scope,
   3814     stmt: *const clang.CompoundAssignOperator,
   3815     op: Tag,
   3816     used: ResultUsed,
   3817 ) TransError!Node {
   3818     const is_shift = op == .shl_assign or op == .shr_assign;
   3819     const is_div = op == .div_assign;
   3820     const is_mod = op == .mod_assign;
   3821     const lhs = stmt.getLHS();
   3822     const rhs = stmt.getRHS();
   3823     const loc = stmt.getBeginLoc();
   3824     const lhs_qt = getExprQualType(c, lhs);
   3825     const rhs_qt = getExprQualType(c, rhs);
   3826     const is_signed = cIsSignedInteger(lhs_qt);
   3827     const is_ptr_op_signed = qualTypeIsPtr(lhs_qt) and cIsSignedInteger(rhs_qt);
   3828     const requires_cast = !lhs_qt.eq(rhs_qt) and !is_ptr_op_signed;
   3829 
   3830     if (used == .unused) {
   3831         // common case
   3832         // c: lhs += rhs
   3833         // zig: lhs += rhs
   3834         const lhs_node = try transExpr(c, scope, lhs, .used);
   3835         var rhs_node = try transExpr(c, scope, rhs, .used);
   3836         if (is_ptr_op_signed) rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node);
   3837 
   3838         if ((is_mod or is_div) and is_signed) {
   3839             if (requires_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node);
   3840             const operands = .{ .lhs = lhs_node, .rhs = rhs_node };
   3841             const builtin = if (is_mod)
   3842                 try Tag.signed_remainder.create(c.arena, operands)
   3843             else
   3844                 try Tag.div_trunc.create(c.arena, operands);
   3845 
   3846             return transCreateNodeInfixOp(c, .assign, lhs_node, builtin, .used);
   3847         }
   3848 
   3849         if (is_shift) {
   3850             rhs_node = try Tag.int_cast.create(c.arena, rhs_node);
   3851         } else if (requires_cast) {
   3852             rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node);
   3853         }
   3854         return transCreateNodeInfixOp(c, op, lhs_node, rhs_node, .used);
   3855     }
   3856     // worst case
   3857     // c:   lhs += rhs
   3858     // zig: (blk: {
   3859     // zig:     const _ref = &lhs;
   3860     // zig:     _ref.* += rhs;
   3861     // zig:     break :blk _ref.*
   3862     // zig: })
   3863     var block_scope = try Scope.Block.init(c, scope, true);
   3864     defer block_scope.deinit();
   3865     const ref = try block_scope.reserveMangledName(c, "ref");
   3866 
   3867     const expr = try transExpr(c, &block_scope.base, lhs, .used);
   3868     const addr_of = try Tag.address_of.create(c.arena, expr);
   3869     const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = ref, .init = addr_of });
   3870     try block_scope.statements.append(ref_decl);
   3871 
   3872     const lhs_node = try Tag.identifier.create(c.arena, ref);
   3873     const ref_node = try Tag.deref.create(c.arena, lhs_node);
   3874 
   3875     var rhs_node = try transExpr(c, &block_scope.base, rhs, .used);
   3876     if (is_ptr_op_signed) rhs_node = try usizeCastForWrappingPtrArithmetic(c.arena, rhs_node);
   3877     if ((is_mod or is_div) and is_signed) {
   3878         if (requires_cast) rhs_node = try transCCast(c, scope, loc, lhs_qt, rhs_qt, rhs_node);
   3879         const operands = .{ .lhs = ref_node, .rhs = rhs_node };
   3880         const builtin = if (is_mod)
   3881             try Tag.signed_remainder.create(c.arena, operands)
   3882         else
   3883             try Tag.div_trunc.create(c.arena, operands);
   3884 
   3885         const assign = try transCreateNodeInfixOp(c, .assign, ref_node, builtin, .used);
   3886         try block_scope.statements.append(assign);
   3887     } else {
   3888         if (is_shift) {
   3889             rhs_node = try Tag.int_cast.create(c.arena, rhs_node);
   3890         } else if (requires_cast) {
   3891             rhs_node = try transCCast(c, &block_scope.base, loc, lhs_qt, rhs_qt, rhs_node);
   3892         }
   3893 
   3894         const assign = try transCreateNodeInfixOp(c, op, ref_node, rhs_node, .used);
   3895         try block_scope.statements.append(assign);
   3896     }
   3897 
   3898     const break_node = try Tag.break_val.create(c.arena, .{
   3899         .label = block_scope.label,
   3900         .val = ref_node,
   3901     });
   3902     try block_scope.statements.append(break_node);
   3903     return block_scope.complete(c);
   3904 }
   3905 
   3906 fn removeCVQualifiers(c: *Context, dst_type_node: Node, expr: Node) Error!Node {
   3907     const const_casted = try Tag.const_cast.create(c.arena, expr);
   3908     const volatile_casted = try Tag.volatile_cast.create(c.arena, const_casted);
   3909     return Tag.as.create(c.arena, .{
   3910         .lhs = dst_type_node,
   3911         .rhs = try Tag.ptr_cast.create(c.arena, volatile_casted),
   3912     });
   3913 }
   3914 
   3915 fn transCPtrCast(
   3916     c: *Context,
   3917     scope: *Scope,
   3918     loc: clang.SourceLocation,
   3919     dst_type: clang.QualType,
   3920     src_type: clang.QualType,
   3921     expr: Node,
   3922 ) !Node {
   3923     const ty = dst_type.getTypePtr();
   3924     const child_type = ty.getPointeeType();
   3925     const src_ty = src_type.getTypePtr();
   3926     const src_child_type = src_ty.getPointeeType();
   3927     const dst_type_node = try transType(c, scope, ty, loc);
   3928 
   3929     if (!src_ty.isArrayType() and ((src_child_type.isConstQualified() and
   3930         !child_type.isConstQualified()) or
   3931         (src_child_type.isVolatileQualified() and
   3932         !child_type.isVolatileQualified())))
   3933     {
   3934         return removeCVQualifiers(c, dst_type_node, expr);
   3935     } else {
   3936         // Implicit downcasting from higher to lower alignment values is forbidden,
   3937         // use @alignCast to side-step this problem
   3938         const rhs = if (qualTypeCanon(child_type).isVoidType())
   3939             // void has 1-byte alignment, so @alignCast is not needed
   3940             expr
   3941         else if (typeIsOpaque(c, qualTypeCanon(child_type), loc))
   3942             // For opaque types a ptrCast is enough
   3943             expr
   3944         else blk: {
   3945             break :blk try Tag.align_cast.create(c.arena, expr);
   3946         };
   3947         return Tag.as.create(c.arena, .{
   3948             .lhs = dst_type_node,
   3949             .rhs = try Tag.ptr_cast.create(c.arena, rhs),
   3950         });
   3951     }
   3952 }
   3953 
   3954 fn transFloatingLiteral(c: *Context, expr: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node {
   3955     // TODO use something more accurate than widening to a larger float type and printing that result
   3956     switch (expr.getRawSemantics()) {
   3957         .IEEEhalf, // f16
   3958         .IEEEsingle, // f32
   3959         .IEEEdouble, // f64
   3960         => {
   3961             var dbl = expr.getValueAsApproximateDouble();
   3962             const is_negative = dbl < 0; // -0.0 is considered non-negative
   3963             if (is_negative) dbl = -dbl;
   3964             const str = if (dbl == @floor(dbl))
   3965                 try std.fmt.allocPrint(c.arena, "{d}.0", .{dbl})
   3966             else
   3967                 try std.fmt.allocPrint(c.arena, "{d}", .{dbl});
   3968             var node = try Tag.float_literal.create(c.arena, str);
   3969             if (is_negative) node = try Tag.negate.create(c.arena, node);
   3970             return maybeSuppressResult(c, used, node);
   3971         },
   3972         .x87DoubleExtended, // f80
   3973         .IEEEquad, // f128
   3974         => return transFloatingLiteralQuad(c, expr, used),
   3975         else => |format| return fail(
   3976             c,
   3977             error.UnsupportedTranslation,
   3978             expr.getBeginLoc(),
   3979             "unsupported floating point constant format {}",
   3980             .{format},
   3981         ),
   3982     }
   3983 }
   3984 
   3985 fn transFloatingLiteralQuad(c: *Context, expr: *const clang.FloatingLiteral, used: ResultUsed) TransError!Node {
   3986     assert(switch (expr.getRawSemantics()) {
   3987         .x87DoubleExtended, .IEEEquad => true,
   3988         else => false,
   3989     });
   3990 
   3991     var low: u64 = undefined;
   3992     var high: u64 = undefined;
   3993     expr.getValueAsApproximateQuadBits(&low, &high);
   3994     var quad: f128 = @bitCast(low | @as(u128, high) << 64);
   3995     const is_negative = quad < 0; // -0.0 is considered non-negative
   3996     if (is_negative) quad = -quad;
   3997 
   3998     // TODO implement decimal format for f128 <https://github.com/ziglang/zig/issues/1181>
   3999     // in the meantime, if the value can be roundtripped by casting it to f64, serializing it to
   4000     // the decimal format and parsing it back as the exact same f128 value, then use that serialized form
   4001     const str = fmt_decimal: {
   4002         var buf: [512]u8 = undefined; // should be large enough to print any f64 in decimal form
   4003         const dbl: f64 = @floatCast(quad);
   4004         const temp_str = if (dbl == @floor(dbl))
   4005             std.fmt.bufPrint(&buf, "{d}.0", .{dbl}) catch |err| switch (err) {
   4006                 error.NoSpaceLeft => unreachable,
   4007             }
   4008         else
   4009             std.fmt.bufPrint(&buf, "{d}", .{dbl}) catch |err| switch (err) {
   4010                 error.NoSpaceLeft => unreachable,
   4011             };
   4012         const could_roundtrip = if (std.fmt.parseFloat(f128, temp_str)) |parsed_quad|
   4013             quad == parsed_quad
   4014         else |_|
   4015             false;
   4016         break :fmt_decimal if (could_roundtrip) try c.arena.dupe(u8, temp_str) else null;
   4017     }
   4018     // otherwise, fall back to the hexadecimal format
   4019     orelse try std.fmt.allocPrint(c.arena, "{x}", .{quad});
   4020 
   4021     var node = try Tag.float_literal.create(c.arena, str);
   4022     if (is_negative) node = try Tag.negate.create(c.arena, node);
   4023     return maybeSuppressResult(c, used, node);
   4024 }
   4025 
   4026 fn transBinaryConditionalOperator(c: *Context, scope: *Scope, stmt: *const clang.BinaryConditionalOperator, used: ResultUsed) TransError!Node {
   4027     // GNU extension of the ternary operator where the middle expression is
   4028     // omitted, the condition itself is returned if it evaluates to true
   4029     const qt = @as(*const clang.Expr, @ptrCast(stmt)).getType();
   4030     const res_is_bool = qualTypeIsBoolean(qt);
   4031     const casted_stmt = @as(*const clang.AbstractConditionalOperator, @ptrCast(stmt));
   4032     const cond_expr = casted_stmt.getCond();
   4033     const false_expr = casted_stmt.getFalseExpr();
   4034 
   4035     // c:   (cond_expr)?:(false_expr)
   4036     // zig: (blk: {
   4037     //          const _cond_temp = (cond_expr);
   4038     //          break :blk if (_cond_temp) _cond_temp else (false_expr);
   4039     //      })
   4040     var block_scope = try Scope.Block.init(c, scope, true);
   4041     defer block_scope.deinit();
   4042 
   4043     const cond_temp = try block_scope.reserveMangledName(c, "cond_temp");
   4044     const init_node = try transExpr(c, &block_scope.base, cond_expr, .used);
   4045     const ref_decl = try Tag.var_simple.create(c.arena, .{ .name = cond_temp, .init = init_node });
   4046     try block_scope.statements.append(ref_decl);
   4047 
   4048     var cond_scope = Scope.Condition{
   4049         .base = .{
   4050             .parent = &block_scope.base,
   4051             .id = .condition,
   4052         },
   4053     };
   4054     defer cond_scope.deinit();
   4055 
   4056     const cond_ident = try Tag.identifier.create(c.arena, cond_temp);
   4057     const ty = getExprQualType(c, cond_expr).getTypePtr();
   4058     const cond_node = try finishBoolExpr(c, &cond_scope.base, cond_expr.getBeginLoc(), ty, cond_ident, .used);
   4059     var then_body = cond_ident;
   4060     if (!res_is_bool and isBoolRes(init_node)) {
   4061         then_body = try Tag.int_from_bool.create(c.arena, then_body);
   4062     }
   4063 
   4064     var else_body = try transExpr(c, &block_scope.base, false_expr, .used);
   4065     if (!res_is_bool and isBoolRes(else_body)) {
   4066         else_body = try Tag.int_from_bool.create(c.arena, else_body);
   4067     }
   4068     const if_node = try Tag.@"if".create(c.arena, .{
   4069         .cond = cond_node,
   4070         .then = then_body,
   4071         .@"else" = else_body,
   4072     });
   4073     const break_node = try Tag.break_val.create(c.arena, .{
   4074         .label = block_scope.label,
   4075         .val = if_node,
   4076     });
   4077     try block_scope.statements.append(break_node);
   4078     const res = try block_scope.complete(c);
   4079     return maybeSuppressResult(c, used, res);
   4080 }
   4081 
   4082 fn transConditionalOperator(c: *Context, scope: *Scope, stmt: *const clang.ConditionalOperator, used: ResultUsed) TransError!Node {
   4083     var cond_scope = Scope.Condition{
   4084         .base = .{
   4085             .parent = scope,
   4086             .id = .condition,
   4087         },
   4088     };
   4089     defer cond_scope.deinit();
   4090 
   4091     const qt = @as(*const clang.Expr, @ptrCast(stmt)).getType();
   4092     const res_is_bool = qualTypeIsBoolean(qt);
   4093     const casted_stmt = @as(*const clang.AbstractConditionalOperator, @ptrCast(stmt));
   4094     const cond_expr = casted_stmt.getCond();
   4095     const true_expr = casted_stmt.getTrueExpr();
   4096     const false_expr = casted_stmt.getFalseExpr();
   4097 
   4098     const cond = try transBoolExpr(c, &cond_scope.base, cond_expr, .used);
   4099 
   4100     var then_body = try transExpr(c, scope, true_expr, used);
   4101     if (!res_is_bool and isBoolRes(then_body)) {
   4102         then_body = try Tag.int_from_bool.create(c.arena, then_body);
   4103     }
   4104 
   4105     var else_body = try transExpr(c, scope, false_expr, used);
   4106     if (!res_is_bool and isBoolRes(else_body)) {
   4107         else_body = try Tag.int_from_bool.create(c.arena, else_body);
   4108     }
   4109 
   4110     const if_node = try Tag.@"if".create(c.arena, .{
   4111         .cond = cond,
   4112         .then = then_body,
   4113         .@"else" = else_body,
   4114     });
   4115     // Clang inserts ImplicitCast(ToVoid)'s to both rhs and lhs so we don't need to suppress the result here.
   4116     return if_node;
   4117 }
   4118 
   4119 fn maybeSuppressResult(c: *Context, used: ResultUsed, result: Node) TransError!Node {
   4120     if (used == .used) return result;
   4121     return Tag.discard.create(c.arena, .{ .should_skip = false, .value = result });
   4122 }
   4123 
   4124 fn addTopLevelDecl(c: *Context, name: []const u8, decl_node: Node) !void {
   4125     const gop = try c.global_scope.sym_table.getOrPut(name);
   4126     if (!gop.found_existing) {
   4127         gop.value_ptr.* = decl_node;
   4128         try c.global_scope.nodes.append(decl_node);
   4129     }
   4130 }
   4131 
   4132 fn transQualTypeInitializedStringLiteral(c: *Context, elem_ty: Node, string_lit: *const clang.StringLiteral) TypeError!Node {
   4133     const string_lit_size = string_lit.getLength();
   4134     const array_size = @as(usize, @intCast(string_lit_size));
   4135 
   4136     // incomplete array initialized with empty string, will be translated as [1]T{0}
   4137     // see https://github.com/ziglang/zig/issues/8256
   4138     if (array_size == 0) return Tag.array_type.create(c.arena, .{ .len = 1, .elem_type = elem_ty });
   4139 
   4140     return Tag.null_sentinel_array_type.create(c.arena, .{ .len = array_size, .elem_type = elem_ty });
   4141 }
   4142 
   4143 /// Translate a qualtype for a variable with an initializer. This only matters
   4144 /// for incomplete arrays, since the initializer determines the size of the array.
   4145 fn transQualTypeInitialized(
   4146     c: *Context,
   4147     scope: *Scope,
   4148     qt: clang.QualType,
   4149     decl_init: *const clang.Expr,
   4150     source_loc: clang.SourceLocation,
   4151 ) TypeError!Node {
   4152     const ty = qt.getTypePtr();
   4153     if (ty.getTypeClass() == .IncompleteArray) {
   4154         const incomplete_array_ty = @as(*const clang.IncompleteArrayType, @ptrCast(ty));
   4155         const elem_ty = try transType(c, scope, incomplete_array_ty.getElementType().getTypePtr(), source_loc);
   4156 
   4157         switch (decl_init.getStmtClass()) {
   4158             .StringLiteralClass => {
   4159                 const string_lit = @as(*const clang.StringLiteral, @ptrCast(decl_init));
   4160                 return transQualTypeInitializedStringLiteral(c, elem_ty, string_lit);
   4161             },
   4162             .InitListExprClass => {
   4163                 const init_expr = @as(*const clang.InitListExpr, @ptrCast(decl_init));
   4164                 const size = init_expr.getNumInits();
   4165 
   4166                 if (init_expr.isStringLiteralInit()) {
   4167                     assert(size == 1);
   4168                     const string_lit = init_expr.getInit(0).castToStringLiteral().?;
   4169                     return transQualTypeInitializedStringLiteral(c, elem_ty, string_lit);
   4170                 }
   4171 
   4172                 return Tag.array_type.create(c.arena, .{ .len = size, .elem_type = elem_ty });
   4173             },
   4174             else => {},
   4175         }
   4176     }
   4177     return transQualType(c, scope, qt, source_loc);
   4178 }
   4179 
   4180 fn transQualType(c: *Context, scope: *Scope, qt: clang.QualType, source_loc: clang.SourceLocation) TypeError!Node {
   4181     return transType(c, scope, qt.getTypePtr(), source_loc);
   4182 }
   4183 
   4184 /// Produces a Zig AST node by translating a Clang QualType, respecting the width, but modifying the signed-ness.
   4185 /// Asserts the type is an integer.
   4186 fn transQualTypeIntWidthOf(c: *Context, ty: clang.QualType, is_signed: bool) TypeError!Node {
   4187     return transTypeIntWidthOf(c, qualTypeCanon(ty), is_signed);
   4188 }
   4189 
   4190 /// Produces a Zig AST node by translating a Clang Type, respecting the width, but modifying the signed-ness.
   4191 /// Asserts the type is an integer.
   4192 fn transTypeIntWidthOf(c: *Context, ty: *const clang.Type, is_signed: bool) TypeError!Node {
   4193     assert(ty.getTypeClass() == .Builtin);
   4194     const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(ty));
   4195     return Tag.type.create(c.arena, switch (builtin_ty.getKind()) {
   4196         .Char_U, .Char_S, .UChar, .SChar, .Char8 => if (is_signed) "i8" else "u8",
   4197         .UShort, .Short => if (is_signed) "c_short" else "c_ushort",
   4198         .UInt, .Int => if (is_signed) "c_int" else "c_uint",
   4199         .ULong, .Long => if (is_signed) "c_long" else "c_ulong",
   4200         .ULongLong, .LongLong => if (is_signed) "c_longlong" else "c_ulonglong",
   4201         .UInt128, .Int128 => if (is_signed) "i128" else "u128",
   4202         .Char16 => if (is_signed) "i16" else "u16",
   4203         .Char32 => if (is_signed) "i32" else "u32",
   4204         else => unreachable, // only call this function when it has already been determined the type is int
   4205     });
   4206 }
   4207 
   4208 fn isCBuiltinType(qt: clang.QualType, kind: clang.BuiltinTypeKind) bool {
   4209     const c_type = qualTypeCanon(qt);
   4210     if (c_type.getTypeClass() != .Builtin)
   4211         return false;
   4212     const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(c_type));
   4213     return builtin_ty.getKind() == kind;
   4214 }
   4215 
   4216 fn qualTypeIsPtr(qt: clang.QualType) bool {
   4217     return qualTypeCanon(qt).getTypeClass() == .Pointer;
   4218 }
   4219 
   4220 fn qualTypeIsBoolean(qt: clang.QualType) bool {
   4221     return qualTypeCanon(qt).isBooleanType();
   4222 }
   4223 
   4224 fn qualTypeIntBitWidth(c: *Context, qt: clang.QualType) !u32 {
   4225     const ty = qt.getTypePtr();
   4226 
   4227     switch (ty.getTypeClass()) {
   4228         .Builtin => {
   4229             const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(ty));
   4230 
   4231             switch (builtin_ty.getKind()) {
   4232                 .Char_U,
   4233                 .UChar,
   4234                 .Char_S,
   4235                 .SChar,
   4236                 => return 8,
   4237                 .UInt128,
   4238                 .Int128,
   4239                 => return 128,
   4240                 else => return 0,
   4241             }
   4242 
   4243             unreachable;
   4244         },
   4245         .Typedef => {
   4246             const typedef_ty = @as(*const clang.TypedefType, @ptrCast(ty));
   4247             const typedef_decl = typedef_ty.getDecl();
   4248             const type_name = try c.str(@as(*const clang.NamedDecl, @ptrCast(typedef_decl)).getName_bytes_begin());
   4249 
   4250             if (mem.eql(u8, type_name, "uint8_t") or mem.eql(u8, type_name, "int8_t")) {
   4251                 return 8;
   4252             } else if (mem.eql(u8, type_name, "uint16_t") or mem.eql(u8, type_name, "int16_t")) {
   4253                 return 16;
   4254             } else if (mem.eql(u8, type_name, "uint32_t") or mem.eql(u8, type_name, "int32_t")) {
   4255                 return 32;
   4256             } else if (mem.eql(u8, type_name, "uint64_t") or mem.eql(u8, type_name, "int64_t")) {
   4257                 return 64;
   4258             } else {
   4259                 return 0;
   4260             }
   4261         },
   4262         else => return 0,
   4263     }
   4264 }
   4265 
   4266 fn qualTypeChildIsFnProto(qt: clang.QualType) bool {
   4267     const ty = qualTypeCanon(qt);
   4268 
   4269     switch (ty.getTypeClass()) {
   4270         .FunctionProto, .FunctionNoProto => return true,
   4271         else => return false,
   4272     }
   4273 }
   4274 
   4275 fn qualTypeCanon(qt: clang.QualType) *const clang.Type {
   4276     const canon = qt.getCanonicalType();
   4277     return canon.getTypePtr();
   4278 }
   4279 
   4280 fn getExprQualType(c: *Context, expr: *const clang.Expr) clang.QualType {
   4281     blk: {
   4282         // If this is a C `char *`, turn it into a `const char *`
   4283         if (expr.getStmtClass() != .ImplicitCastExprClass) break :blk;
   4284         const cast_expr = @as(*const clang.ImplicitCastExpr, @ptrCast(expr));
   4285         if (cast_expr.getCastKind() != .ArrayToPointerDecay) break :blk;
   4286         const sub_expr = cast_expr.getSubExpr();
   4287         if (sub_expr.getStmtClass() != .StringLiteralClass) break :blk;
   4288         const array_qt = sub_expr.getType();
   4289         const array_type = @as(*const clang.ArrayType, @ptrCast(array_qt.getTypePtr()));
   4290         var pointee_qt = array_type.getElementType();
   4291         pointee_qt.addConst();
   4292         return c.clang_context.getPointerType(pointee_qt);
   4293     }
   4294     return expr.getType();
   4295 }
   4296 
   4297 fn typeIsOpaque(c: *Context, ty: *const clang.Type, loc: clang.SourceLocation) bool {
   4298     switch (ty.getTypeClass()) {
   4299         .Builtin => {
   4300             const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(ty));
   4301             return builtin_ty.getKind() == .Void;
   4302         },
   4303         .Record => {
   4304             const record_ty = @as(*const clang.RecordType, @ptrCast(ty));
   4305             const record_decl = record_ty.getDecl();
   4306             const record_def = record_decl.getDefinition() orelse
   4307                 return true;
   4308             var it = record_def.field_begin();
   4309             const end_it = record_def.field_end();
   4310             while (it.neq(end_it)) : (it = it.next()) {
   4311                 const field_decl = it.deref();
   4312 
   4313                 if (field_decl.isBitField()) {
   4314                     return true;
   4315                 }
   4316             }
   4317             return false;
   4318         },
   4319         .Elaborated => {
   4320             const elaborated_ty = @as(*const clang.ElaboratedType, @ptrCast(ty));
   4321             const qt = elaborated_ty.getNamedType();
   4322             return typeIsOpaque(c, qt.getTypePtr(), loc);
   4323         },
   4324         .Typedef => {
   4325             const typedef_ty = @as(*const clang.TypedefType, @ptrCast(ty));
   4326             const typedef_decl = typedef_ty.getDecl();
   4327             const underlying_type = typedef_decl.getUnderlyingType();
   4328             return typeIsOpaque(c, underlying_type.getTypePtr(), loc);
   4329         },
   4330         else => return false,
   4331     }
   4332 }
   4333 
   4334 /// plain `char *` (not const; not explicitly signed or unsigned)
   4335 fn qualTypeIsCharStar(qt: clang.QualType) bool {
   4336     if (qualTypeIsPtr(qt)) {
   4337         const child_qt = qualTypeCanon(qt).getPointeeType();
   4338         return cIsUnqualifiedChar(child_qt) and !child_qt.isConstQualified();
   4339     }
   4340     return false;
   4341 }
   4342 
   4343 /// C `char` without explicit signed or unsigned qualifier
   4344 fn cIsUnqualifiedChar(qt: clang.QualType) bool {
   4345     const c_type = qualTypeCanon(qt);
   4346     if (c_type.getTypeClass() != .Builtin) return false;
   4347     const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(c_type));
   4348     return switch (builtin_ty.getKind()) {
   4349         .Char_S, .Char_U => true,
   4350         else => false,
   4351     };
   4352 }
   4353 
   4354 fn cIsInteger(qt: clang.QualType) bool {
   4355     return cIsSignedInteger(qt) or cIsUnsignedInteger(qt);
   4356 }
   4357 
   4358 fn cIsUnsignedInteger(qt: clang.QualType) bool {
   4359     const c_type = qualTypeCanon(qt);
   4360     if (c_type.getTypeClass() != .Builtin) return false;
   4361     const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(c_type));
   4362     return switch (builtin_ty.getKind()) {
   4363         .Char_U,
   4364         .UChar,
   4365         .Char_S,
   4366         .UShort,
   4367         .UInt,
   4368         .ULong,
   4369         .ULongLong,
   4370         .UInt128,
   4371         .WChar_U,
   4372         => true,
   4373         else => false,
   4374     };
   4375 }
   4376 
   4377 fn cIntTypeToIndex(qt: clang.QualType) u8 {
   4378     const c_type = qualTypeCanon(qt);
   4379     assert(c_type.getTypeClass() == .Builtin);
   4380     const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(c_type));
   4381     return switch (builtin_ty.getKind()) {
   4382         .Bool, .Char_U, .Char_S, .UChar, .SChar, .Char8 => 1,
   4383         .WChar_U, .WChar_S => 2,
   4384         .UShort, .Short, .Char16 => 3,
   4385         .UInt, .Int, .Char32 => 4,
   4386         .ULong, .Long => 5,
   4387         .ULongLong, .LongLong => 6,
   4388         .UInt128, .Int128 => 7,
   4389         else => unreachable,
   4390     };
   4391 }
   4392 
   4393 fn cIntTypeCmp(a: clang.QualType, b: clang.QualType) math.Order {
   4394     const a_index = cIntTypeToIndex(a);
   4395     const b_index = cIntTypeToIndex(b);
   4396     return math.order(a_index, b_index);
   4397 }
   4398 
   4399 /// Checks if expr is an integer literal >= 0
   4400 fn cIsNonNegativeIntLiteral(c: *Context, expr: *const clang.Expr) bool {
   4401     if (@as(*const clang.Stmt, @ptrCast(expr)).getStmtClass() == .IntegerLiteralClass) {
   4402         var signum: c_int = undefined;
   4403         if (!(@as(*const clang.IntegerLiteral, @ptrCast(expr)).getSignum(&signum, c.clang_context))) {
   4404             return false;
   4405         }
   4406         return signum >= 0;
   4407     }
   4408     return false;
   4409 }
   4410 
   4411 fn cIsSignedInteger(qt: clang.QualType) bool {
   4412     const c_type = qualTypeCanon(qt);
   4413     if (c_type.getTypeClass() != .Builtin) return false;
   4414     const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(c_type));
   4415     return switch (builtin_ty.getKind()) {
   4416         .SChar,
   4417         .Short,
   4418         .Int,
   4419         .Long,
   4420         .LongLong,
   4421         .Int128,
   4422         .WChar_S,
   4423         => true,
   4424         else => false,
   4425     };
   4426 }
   4427 
   4428 fn cIsNativeInt(qt: clang.QualType) bool {
   4429     const c_type = qualTypeCanon(qt);
   4430     if (c_type.getTypeClass() != .Builtin) return false;
   4431     const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(c_type));
   4432     return builtin_ty.getKind() == .Int;
   4433 }
   4434 
   4435 fn cIsFloating(qt: clang.QualType) bool {
   4436     const c_type = qualTypeCanon(qt);
   4437     if (c_type.getTypeClass() != .Builtin) return false;
   4438     const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(c_type));
   4439     return switch (builtin_ty.getKind()) {
   4440         .Float,
   4441         .Double,
   4442         .Float128,
   4443         .LongDouble,
   4444         => true,
   4445         else => false,
   4446     };
   4447 }
   4448 
   4449 fn cIsLongLongInteger(qt: clang.QualType) bool {
   4450     const c_type = qualTypeCanon(qt);
   4451     if (c_type.getTypeClass() != .Builtin) return false;
   4452     const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(c_type));
   4453     return switch (builtin_ty.getKind()) {
   4454         .LongLong, .ULongLong, .Int128, .UInt128 => true,
   4455         else => false,
   4456     };
   4457 }
   4458 fn transCreateNodeAssign(
   4459     c: *Context,
   4460     scope: *Scope,
   4461     result_used: ResultUsed,
   4462     lhs: *const clang.Expr,
   4463     rhs: *const clang.Expr,
   4464 ) !Node {
   4465     // common case
   4466     // c:   lhs = rhs
   4467     // zig: lhs = rhs
   4468     if (result_used == .unused) {
   4469         const lhs_node = try transExpr(c, scope, lhs, .used);
   4470         var rhs_node = try transExprCoercing(c, scope, rhs, .used);
   4471         if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) {
   4472             rhs_node = try Tag.int_from_bool.create(c.arena, rhs_node);
   4473         }
   4474         return transCreateNodeInfixOp(c, .assign, lhs_node, rhs_node, .used);
   4475     }
   4476 
   4477     // worst case
   4478     // c:   lhs = rhs
   4479     // zig: (blk: {
   4480     // zig:     const _tmp = rhs;
   4481     // zig:     lhs = _tmp;
   4482     // zig:     break :blk _tmp
   4483     // zig: })
   4484     var block_scope = try Scope.Block.init(c, scope, true);
   4485     defer block_scope.deinit();
   4486 
   4487     const tmp = try block_scope.reserveMangledName(c, "tmp");
   4488     var rhs_node = try transExpr(c, &block_scope.base, rhs, .used);
   4489     if (!exprIsBooleanType(lhs) and isBoolRes(rhs_node)) {
   4490         rhs_node = try Tag.int_from_bool.create(c.arena, rhs_node);
   4491     }
   4492 
   4493     const tmp_decl = try Tag.var_simple.create(c.arena, .{ .name = tmp, .init = rhs_node });
   4494     try block_scope.statements.append(tmp_decl);
   4495 
   4496     const lhs_node = try transExpr(c, &block_scope.base, lhs, .used);
   4497     const tmp_ident = try Tag.identifier.create(c.arena, tmp);
   4498     const assign = try transCreateNodeInfixOp(c, .assign, lhs_node, tmp_ident, .used);
   4499     try block_scope.statements.append(assign);
   4500 
   4501     const break_node = try Tag.break_val.create(c.arena, .{
   4502         .label = block_scope.label,
   4503         .val = tmp_ident,
   4504     });
   4505     try block_scope.statements.append(break_node);
   4506     return block_scope.complete(c);
   4507 }
   4508 
   4509 fn transCreateNodeInfixOp(
   4510     c: *Context,
   4511     op: Tag,
   4512     lhs: Node,
   4513     rhs: Node,
   4514     used: ResultUsed,
   4515 ) !Node {
   4516     const payload = try c.arena.create(ast.Payload.BinOp);
   4517     payload.* = .{
   4518         .base = .{ .tag = op },
   4519         .data = .{
   4520             .lhs = lhs,
   4521             .rhs = rhs,
   4522         },
   4523     };
   4524     return maybeSuppressResult(c, used, Node.initPayload(&payload.base));
   4525 }
   4526 
   4527 fn transCreateNodeBoolInfixOp(
   4528     c: *Context,
   4529     scope: *Scope,
   4530     stmt: *const clang.BinaryOperator,
   4531     op: Tag,
   4532     used: ResultUsed,
   4533 ) !Node {
   4534     std.debug.assert(op == .@"and" or op == .@"or");
   4535 
   4536     const lhs = try transBoolExpr(c, scope, stmt.getLHS(), .used);
   4537     const rhs = try transBoolExpr(c, scope, stmt.getRHS(), .used);
   4538 
   4539     return transCreateNodeInfixOp(c, op, lhs, rhs, used);
   4540 }
   4541 
   4542 fn transCreateNodeAPInt(c: *Context, int: *const clang.APSInt) !Node {
   4543     const num_limbs = math.cast(usize, int.getNumWords()) orelse return error.OutOfMemory;
   4544     var aps_int = int;
   4545     const is_negative = int.isSigned() and int.isNegative();
   4546     if (is_negative) aps_int = aps_int.negate();
   4547     defer if (is_negative) {
   4548         aps_int.free();
   4549     };
   4550 
   4551     const limbs = try c.arena.alloc(math.big.Limb, num_limbs);
   4552     defer c.arena.free(limbs);
   4553 
   4554     const data = aps_int.getRawData();
   4555     switch (@sizeOf(math.big.Limb)) {
   4556         8 => {
   4557             var i: usize = 0;
   4558             while (i < num_limbs) : (i += 1) {
   4559                 limbs[i] = data[i];
   4560             }
   4561         },
   4562         4 => {
   4563             var limb_i: usize = 0;
   4564             var data_i: usize = 0;
   4565             while (limb_i < num_limbs) : ({
   4566                 limb_i += 2;
   4567                 data_i += 1;
   4568             }) {
   4569                 limbs[limb_i] = @as(u32, @truncate(data[data_i]));
   4570                 limbs[limb_i + 1] = @as(u32, @truncate(data[data_i] >> 32));
   4571             }
   4572         },
   4573         else => @compileError("unimplemented"),
   4574     }
   4575 
   4576     const big: math.big.int.Const = .{ .limbs = limbs, .positive = true };
   4577     const str = big.toStringAlloc(c.arena, 10, .lower) catch |err| switch (err) {
   4578         error.OutOfMemory => return error.OutOfMemory,
   4579     };
   4580     const res = try Tag.integer_literal.create(c.arena, str);
   4581     if (is_negative) return Tag.negate.create(c.arena, res);
   4582     return res;
   4583 }
   4584 
   4585 fn transCreateNodeNumber(c: *Context, num: anytype, num_kind: enum { int, float }) !Node {
   4586     const fmt_s = switch (@typeInfo(@TypeOf(num))) {
   4587         .Int, .ComptimeInt => "{d}",
   4588         else => "{s}",
   4589     };
   4590     const str = try std.fmt.allocPrint(c.arena, fmt_s, .{num});
   4591     if (num_kind == .float)
   4592         return Tag.float_literal.create(c.arena, str)
   4593     else
   4594         return Tag.integer_literal.create(c.arena, str);
   4595 }
   4596 
   4597 fn transCreateNodeMacroFn(c: *Context, name: []const u8, ref: Node, proto_alias: *ast.Payload.Func) !Node {
   4598     var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa);
   4599     defer fn_params.deinit();
   4600 
   4601     for (proto_alias.data.params) |param| {
   4602         const param_name = param.name orelse
   4603             try std.fmt.allocPrint(c.arena, "arg_{d}", .{c.getMangle()});
   4604 
   4605         try fn_params.append(.{
   4606             .name = param_name,
   4607             .type = param.type,
   4608             .is_noalias = param.is_noalias,
   4609         });
   4610     }
   4611 
   4612     const init = if (ref.castTag(.var_decl)) |v|
   4613         v.data.init.?
   4614     else if (ref.castTag(.var_simple) orelse ref.castTag(.pub_var_simple)) |v|
   4615         v.data.init
   4616     else
   4617         unreachable;
   4618 
   4619     const unwrap_expr = try Tag.unwrap.create(c.arena, init);
   4620     const args = try c.arena.alloc(Node, fn_params.items.len);
   4621     for (fn_params.items, 0..) |param, i| {
   4622         args[i] = try Tag.identifier.create(c.arena, param.name.?);
   4623     }
   4624     const call_expr = try Tag.call.create(c.arena, .{
   4625         .lhs = unwrap_expr,
   4626         .args = args,
   4627     });
   4628     const return_expr = try Tag.@"return".create(c.arena, call_expr);
   4629     const block = try Tag.block_single.create(c.arena, return_expr);
   4630 
   4631     return Tag.pub_inline_fn.create(c.arena, .{
   4632         .name = name,
   4633         .params = try c.arena.dupe(ast.Payload.Param, fn_params.items),
   4634         .return_type = proto_alias.data.return_type,
   4635         .body = block,
   4636     });
   4637 }
   4638 
   4639 fn transCreateNodeShiftOp(
   4640     c: *Context,
   4641     scope: *Scope,
   4642     stmt: *const clang.BinaryOperator,
   4643     op: Tag,
   4644     used: ResultUsed,
   4645 ) !Node {
   4646     std.debug.assert(op == .shl or op == .shr);
   4647 
   4648     const lhs_expr = stmt.getLHS();
   4649     const rhs_expr = stmt.getRHS();
   4650     // lhs >> @as(u5, rh)
   4651 
   4652     const lhs = try transExpr(c, scope, lhs_expr, .used);
   4653 
   4654     const rhs = try transExprCoercing(c, scope, rhs_expr, .used);
   4655     const rhs_casted = try Tag.int_cast.create(c.arena, rhs);
   4656 
   4657     return transCreateNodeInfixOp(c, op, lhs, rhs_casted, used);
   4658 }
   4659 
   4660 fn transType(c: *Context, scope: *Scope, ty: *const clang.Type, source_loc: clang.SourceLocation) TypeError!Node {
   4661     switch (ty.getTypeClass()) {
   4662         .Builtin => {
   4663             const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(ty));
   4664             return Tag.type.create(c.arena, switch (builtin_ty.getKind()) {
   4665                 .Void => "anyopaque",
   4666                 .Bool => "bool",
   4667                 .Char_U, .UChar, .Char_S, .Char8 => "u8",
   4668                 .SChar => "i8",
   4669                 .UShort => "c_ushort",
   4670                 .UInt => "c_uint",
   4671                 .ULong => "c_ulong",
   4672                 .ULongLong => "c_ulonglong",
   4673                 .Short => "c_short",
   4674                 .Int => "c_int",
   4675                 .Long => "c_long",
   4676                 .LongLong => "c_longlong",
   4677                 .UInt128 => "u128",
   4678                 .Int128 => "i128",
   4679                 .Float => "f32",
   4680                 .Double => "f64",
   4681                 .Float128 => "f128",
   4682                 .Float16 => "f16",
   4683                 .LongDouble => "c_longdouble",
   4684                 else => return fail(c, error.UnsupportedType, source_loc, "unsupported builtin type", .{}),
   4685             });
   4686         },
   4687         .FunctionProto => {
   4688             const fn_proto_ty = @as(*const clang.FunctionProtoType, @ptrCast(ty));
   4689             const fn_proto = try transFnProto(c, null, fn_proto_ty, source_loc, null, false);
   4690             return Node.initPayload(&fn_proto.base);
   4691         },
   4692         .FunctionNoProto => {
   4693             const fn_no_proto_ty = @as(*const clang.FunctionType, @ptrCast(ty));
   4694             const fn_proto = try transFnNoProto(c, fn_no_proto_ty, source_loc, null, false);
   4695             return Node.initPayload(&fn_proto.base);
   4696         },
   4697         .Paren => {
   4698             const paren_ty = @as(*const clang.ParenType, @ptrCast(ty));
   4699             return transQualType(c, scope, paren_ty.getInnerType(), source_loc);
   4700         },
   4701         .Pointer => {
   4702             const child_qt = ty.getPointeeType();
   4703             const is_fn_proto = qualTypeChildIsFnProto(child_qt);
   4704             const is_const = is_fn_proto or child_qt.isConstQualified();
   4705             const is_volatile = child_qt.isVolatileQualified();
   4706             const elem_type = try transQualType(c, scope, child_qt, source_loc);
   4707             const ptr_info = .{
   4708                 .is_const = is_const,
   4709                 .is_volatile = is_volatile,
   4710                 .elem_type = elem_type,
   4711             };
   4712             if (is_fn_proto or
   4713                 typeIsOpaque(c, child_qt.getTypePtr(), source_loc) or
   4714                 qualTypeWasDemotedToOpaque(c, child_qt))
   4715             {
   4716                 const ptr = try Tag.single_pointer.create(c.arena, ptr_info);
   4717                 return Tag.optional_type.create(c.arena, ptr);
   4718             }
   4719 
   4720             return Tag.c_pointer.create(c.arena, ptr_info);
   4721         },
   4722         .ConstantArray => {
   4723             const const_arr_ty = @as(*const clang.ConstantArrayType, @ptrCast(ty));
   4724 
   4725             const size_ap_int = const_arr_ty.getSize();
   4726             const size = size_ap_int.getLimitedValue(usize);
   4727             const elem_type = try transType(c, scope, const_arr_ty.getElementType().getTypePtr(), source_loc);
   4728 
   4729             return Tag.array_type.create(c.arena, .{ .len = size, .elem_type = elem_type });
   4730         },
   4731         .IncompleteArray => {
   4732             const incomplete_array_ty = @as(*const clang.IncompleteArrayType, @ptrCast(ty));
   4733 
   4734             const child_qt = incomplete_array_ty.getElementType();
   4735             const is_const = child_qt.isConstQualified();
   4736             const is_volatile = child_qt.isVolatileQualified();
   4737             const elem_type = try transQualType(c, scope, child_qt, source_loc);
   4738 
   4739             return Tag.c_pointer.create(c.arena, .{ .is_const = is_const, .is_volatile = is_volatile, .elem_type = elem_type });
   4740         },
   4741         .Typedef => {
   4742             const typedef_ty = @as(*const clang.TypedefType, @ptrCast(ty));
   4743 
   4744             const typedef_decl = typedef_ty.getDecl();
   4745             var trans_scope = scope;
   4746             if (@as(*const clang.Decl, @ptrCast(typedef_decl)).castToNamedDecl()) |named_decl| {
   4747                 const decl_name = try c.str(named_decl.getName_bytes_begin());
   4748                 if (c.global_names.get(decl_name)) |_| trans_scope = &c.global_scope.base;
   4749                 if (builtin_typedef_map.get(decl_name)) |builtin| return Tag.type.create(c.arena, builtin);
   4750             }
   4751             try transTypeDef(c, trans_scope, typedef_decl);
   4752             const name = c.decl_table.get(@intFromPtr(typedef_decl.getCanonicalDecl())).?;
   4753             return Tag.identifier.create(c.arena, name);
   4754         },
   4755         .Record => {
   4756             const record_ty = @as(*const clang.RecordType, @ptrCast(ty));
   4757 
   4758             const record_decl = record_ty.getDecl();
   4759             var trans_scope = scope;
   4760             if (@as(*const clang.Decl, @ptrCast(record_decl)).castToNamedDecl()) |named_decl| {
   4761                 const decl_name = try c.str(named_decl.getName_bytes_begin());
   4762                 if (c.weak_global_names.contains(decl_name)) trans_scope = &c.global_scope.base;
   4763             }
   4764             try transRecordDecl(c, trans_scope, record_decl);
   4765             const name = c.decl_table.get(@intFromPtr(record_decl.getCanonicalDecl())).?;
   4766             return Tag.identifier.create(c.arena, name);
   4767         },
   4768         .Enum => {
   4769             const enum_ty = @as(*const clang.EnumType, @ptrCast(ty));
   4770 
   4771             const enum_decl = enum_ty.getDecl();
   4772             var trans_scope = scope;
   4773             if (@as(*const clang.Decl, @ptrCast(enum_decl)).castToNamedDecl()) |named_decl| {
   4774                 const decl_name = try c.str(named_decl.getName_bytes_begin());
   4775                 if (c.weak_global_names.contains(decl_name)) trans_scope = &c.global_scope.base;
   4776             }
   4777             try transEnumDecl(c, trans_scope, enum_decl);
   4778             const name = c.decl_table.get(@intFromPtr(enum_decl.getCanonicalDecl())).?;
   4779             return Tag.identifier.create(c.arena, name);
   4780         },
   4781         .Elaborated => {
   4782             const elaborated_ty = @as(*const clang.ElaboratedType, @ptrCast(ty));
   4783             return transQualType(c, scope, elaborated_ty.getNamedType(), source_loc);
   4784         },
   4785         .Decayed => {
   4786             const decayed_ty = @as(*const clang.DecayedType, @ptrCast(ty));
   4787             return transQualType(c, scope, decayed_ty.getDecayedType(), source_loc);
   4788         },
   4789         .Attributed => {
   4790             const attributed_ty = @as(*const clang.AttributedType, @ptrCast(ty));
   4791             return transQualType(c, scope, attributed_ty.getEquivalentType(), source_loc);
   4792         },
   4793         .MacroQualified => {
   4794             const macroqualified_ty = @as(*const clang.MacroQualifiedType, @ptrCast(ty));
   4795             return transQualType(c, scope, macroqualified_ty.getModifiedType(), source_loc);
   4796         },
   4797         .TypeOf => {
   4798             const typeof_ty = @as(*const clang.TypeOfType, @ptrCast(ty));
   4799             return transQualType(c, scope, typeof_ty.getUnmodifiedType(), source_loc);
   4800         },
   4801         .TypeOfExpr => {
   4802             const typeofexpr_ty = @as(*const clang.TypeOfExprType, @ptrCast(ty));
   4803             const underlying_expr = transExpr(c, scope, typeofexpr_ty.getUnderlyingExpr(), .used) catch |err| switch (err) {
   4804                 error.UnsupportedTranslation => {
   4805                     return fail(c, error.UnsupportedType, source_loc, "unsupported underlying expression for TypeOfExpr", .{});
   4806                 },
   4807                 else => |e| return e,
   4808             };
   4809             return Tag.typeof.create(c.arena, underlying_expr);
   4810         },
   4811         .Vector => {
   4812             const vector_ty = @as(*const clang.VectorType, @ptrCast(ty));
   4813             const num_elements = vector_ty.getNumElements();
   4814             const element_qt = vector_ty.getElementType();
   4815             return Tag.vector.create(c.arena, .{
   4816                 .lhs = try transCreateNodeNumber(c, num_elements, .int),
   4817                 .rhs = try transQualType(c, scope, element_qt, source_loc),
   4818             });
   4819         },
   4820         .BitInt, .ExtVector => {
   4821             const type_name = try c.str(ty.getTypeClassName());
   4822             return fail(c, error.UnsupportedType, source_loc, "TODO implement translation of type: '{s}'", .{type_name});
   4823         },
   4824         else => {
   4825             const type_name = try c.str(ty.getTypeClassName());
   4826             return fail(c, error.UnsupportedType, source_loc, "unsupported type: '{s}'", .{type_name});
   4827         },
   4828     }
   4829 }
   4830 
   4831 fn qualTypeWasDemotedToOpaque(c: *Context, qt: clang.QualType) bool {
   4832     const ty = qt.getTypePtr();
   4833     switch (qt.getTypeClass()) {
   4834         .Typedef => {
   4835             const typedef_ty = @as(*const clang.TypedefType, @ptrCast(ty));
   4836 
   4837             const typedef_decl = typedef_ty.getDecl();
   4838             const underlying_type = typedef_decl.getUnderlyingType();
   4839             return qualTypeWasDemotedToOpaque(c, underlying_type);
   4840         },
   4841         .Record => {
   4842             const record_ty = @as(*const clang.RecordType, @ptrCast(ty));
   4843 
   4844             const record_decl = record_ty.getDecl();
   4845             const canonical = @intFromPtr(record_decl.getCanonicalDecl());
   4846             if (c.opaque_demotes.contains(canonical)) return true;
   4847 
   4848             // check all childern for opaque types.
   4849             var it = record_decl.field_begin();
   4850             const end_it = record_decl.field_end();
   4851             while (it.neq(end_it)) : (it = it.next()) {
   4852                 const field_decl = it.deref();
   4853                 if (qualTypeWasDemotedToOpaque(c, field_decl.getType())) return true;
   4854             }
   4855             return false;
   4856         },
   4857         .Enum => {
   4858             const enum_ty = @as(*const clang.EnumType, @ptrCast(ty));
   4859 
   4860             const enum_decl = enum_ty.getDecl();
   4861             const canonical = @intFromPtr(enum_decl.getCanonicalDecl());
   4862             return c.opaque_demotes.contains(canonical);
   4863         },
   4864         .Elaborated => {
   4865             const elaborated_ty = @as(*const clang.ElaboratedType, @ptrCast(ty));
   4866             return qualTypeWasDemotedToOpaque(c, elaborated_ty.getNamedType());
   4867         },
   4868         .Decayed => {
   4869             const decayed_ty = @as(*const clang.DecayedType, @ptrCast(ty));
   4870             return qualTypeWasDemotedToOpaque(c, decayed_ty.getDecayedType());
   4871         },
   4872         .Attributed => {
   4873             const attributed_ty = @as(*const clang.AttributedType, @ptrCast(ty));
   4874             return qualTypeWasDemotedToOpaque(c, attributed_ty.getEquivalentType());
   4875         },
   4876         .MacroQualified => {
   4877             const macroqualified_ty = @as(*const clang.MacroQualifiedType, @ptrCast(ty));
   4878             return qualTypeWasDemotedToOpaque(c, macroqualified_ty.getModifiedType());
   4879         },
   4880         else => return false,
   4881     }
   4882 }
   4883 
   4884 fn isAnyopaque(qt: clang.QualType) bool {
   4885     const ty = qt.getTypePtr();
   4886     switch (ty.getTypeClass()) {
   4887         .Builtin => {
   4888             const builtin_ty = @as(*const clang.BuiltinType, @ptrCast(ty));
   4889             return builtin_ty.getKind() == .Void;
   4890         },
   4891         .Typedef => {
   4892             const typedef_ty = @as(*const clang.TypedefType, @ptrCast(ty));
   4893             const typedef_decl = typedef_ty.getDecl();
   4894             return isAnyopaque(typedef_decl.getUnderlyingType());
   4895         },
   4896         .Elaborated => {
   4897             const elaborated_ty = @as(*const clang.ElaboratedType, @ptrCast(ty));
   4898             return isAnyopaque(elaborated_ty.getNamedType().getCanonicalType());
   4899         },
   4900         .Decayed => {
   4901             const decayed_ty = @as(*const clang.DecayedType, @ptrCast(ty));
   4902             return isAnyopaque(decayed_ty.getDecayedType().getCanonicalType());
   4903         },
   4904         .Attributed => {
   4905             const attributed_ty = @as(*const clang.AttributedType, @ptrCast(ty));
   4906             return isAnyopaque(attributed_ty.getEquivalentType().getCanonicalType());
   4907         },
   4908         .MacroQualified => {
   4909             const macroqualified_ty = @as(*const clang.MacroQualifiedType, @ptrCast(ty));
   4910             return isAnyopaque(macroqualified_ty.getModifiedType().getCanonicalType());
   4911         },
   4912         else => return false,
   4913     }
   4914 }
   4915 
   4916 const FnDeclContext = struct {
   4917     fn_name: []const u8,
   4918     has_body: bool,
   4919     storage_class: clang.StorageClass,
   4920     is_always_inline: bool,
   4921     is_export: bool,
   4922 };
   4923 
   4924 fn transCC(
   4925     c: *Context,
   4926     fn_ty: *const clang.FunctionType,
   4927     source_loc: clang.SourceLocation,
   4928 ) !CallingConvention {
   4929     const clang_cc = fn_ty.getCallConv();
   4930     switch (clang_cc) {
   4931         .C => return CallingConvention.C,
   4932         .X86StdCall => return CallingConvention.Stdcall,
   4933         .X86FastCall => return CallingConvention.Fastcall,
   4934         .X86VectorCall, .AArch64VectorCall => return CallingConvention.Vectorcall,
   4935         .X86ThisCall => return CallingConvention.Thiscall,
   4936         .AAPCS => return CallingConvention.AAPCS,
   4937         .AAPCS_VFP => return CallingConvention.AAPCSVFP,
   4938         .X86_64SysV => return CallingConvention.SysV,
   4939         else => return fail(
   4940             c,
   4941             error.UnsupportedType,
   4942             source_loc,
   4943             "unsupported calling convention: {s}",
   4944             .{@tagName(clang_cc)},
   4945         ),
   4946     }
   4947 }
   4948 
   4949 fn transFnProto(
   4950     c: *Context,
   4951     fn_decl: ?*const clang.FunctionDecl,
   4952     fn_proto_ty: *const clang.FunctionProtoType,
   4953     source_loc: clang.SourceLocation,
   4954     fn_decl_context: ?FnDeclContext,
   4955     is_pub: bool,
   4956 ) !*ast.Payload.Func {
   4957     const fn_ty = @as(*const clang.FunctionType, @ptrCast(fn_proto_ty));
   4958     const cc = try transCC(c, fn_ty, source_loc);
   4959     const is_var_args = fn_proto_ty.isVariadic();
   4960     return finishTransFnProto(c, fn_decl, fn_proto_ty, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub);
   4961 }
   4962 
   4963 fn transFnNoProto(
   4964     c: *Context,
   4965     fn_ty: *const clang.FunctionType,
   4966     source_loc: clang.SourceLocation,
   4967     fn_decl_context: ?FnDeclContext,
   4968     is_pub: bool,
   4969 ) !*ast.Payload.Func {
   4970     const cc = try transCC(c, fn_ty, source_loc);
   4971     const is_var_args = if (fn_decl_context) |ctx| (!ctx.is_export and ctx.storage_class != .Static and !ctx.is_always_inline) else true;
   4972     return finishTransFnProto(c, null, null, fn_ty, source_loc, fn_decl_context, is_var_args, cc, is_pub);
   4973 }
   4974 
   4975 fn finishTransFnProto(
   4976     c: *Context,
   4977     fn_decl: ?*const clang.FunctionDecl,
   4978     fn_proto_ty: ?*const clang.FunctionProtoType,
   4979     fn_ty: *const clang.FunctionType,
   4980     source_loc: clang.SourceLocation,
   4981     fn_decl_context: ?FnDeclContext,
   4982     is_var_args: bool,
   4983     cc: CallingConvention,
   4984     is_pub: bool,
   4985 ) !*ast.Payload.Func {
   4986     const is_export = if (fn_decl_context) |ctx| ctx.is_export else false;
   4987     const is_extern = if (fn_decl_context) |ctx| !ctx.has_body else false;
   4988     const is_inline = if (fn_decl_context) |ctx| ctx.is_always_inline else false;
   4989     const scope = &c.global_scope.base;
   4990 
   4991     const param_count: usize = if (fn_proto_ty != null) fn_proto_ty.?.getNumParams() else 0;
   4992     var fn_params = try std.ArrayList(ast.Payload.Param).initCapacity(c.gpa, param_count);
   4993     defer fn_params.deinit();
   4994 
   4995     var i: usize = 0;
   4996     while (i < param_count) : (i += 1) {
   4997         const param_qt = fn_proto_ty.?.getParamType(@as(c_uint, @intCast(i)));
   4998         const is_noalias = param_qt.isRestrictQualified();
   4999 
   5000         const param_name: ?[]const u8 =
   5001             if (fn_decl) |decl|
   5002         blk: {
   5003             const param = decl.getParamDecl(@as(c_uint, @intCast(i)));
   5004             const param_name: []const u8 = try c.str(@as(*const clang.NamedDecl, @ptrCast(param)).getName_bytes_begin());
   5005             if (param_name.len < 1)
   5006                 break :blk null;
   5007 
   5008             break :blk param_name;
   5009         } else null;
   5010         const type_node = try transQualType(c, scope, param_qt, source_loc);
   5011 
   5012         fn_params.addOneAssumeCapacity().* = .{
   5013             .is_noalias = is_noalias,
   5014             .name = param_name,
   5015             .type = type_node,
   5016         };
   5017     }
   5018 
   5019     const linksection_string = blk: {
   5020         if (fn_decl) |decl| {
   5021             var str_len: usize = undefined;
   5022             if (decl.getSectionAttribute(&str_len)) |str_ptr| {
   5023                 break :blk str_ptr[0..str_len];
   5024             }
   5025         }
   5026         break :blk null;
   5027     };
   5028 
   5029     const alignment = if (fn_decl) |decl| ClangAlignment.forFunc(c, decl).zigAlignment() else null;
   5030 
   5031     const explicit_callconv = if ((is_inline or is_export or is_extern) and cc == .C) null else cc;
   5032 
   5033     const return_type_node = blk: {
   5034         if (fn_ty.getNoReturnAttr()) {
   5035             break :blk Tag.noreturn_type.init();
   5036         } else {
   5037             const return_qt = fn_ty.getReturnType();
   5038             if (isAnyopaque(return_qt)) {
   5039                 // convert primitive anyopaque to actual void (only for return type)
   5040                 break :blk Tag.void_type.init();
   5041             } else {
   5042                 break :blk transQualType(c, scope, return_qt, source_loc) catch |err| switch (err) {
   5043                     error.UnsupportedType => {
   5044                         try warn(c, scope, source_loc, "unsupported function proto return type", .{});
   5045                         return err;
   5046                     },
   5047                     error.OutOfMemory => |e| return e,
   5048                 };
   5049             }
   5050         }
   5051     };
   5052     const name: ?[]const u8 = if (fn_decl_context) |ctx| ctx.fn_name else null;
   5053     const payload = try c.arena.create(ast.Payload.Func);
   5054     payload.* = .{
   5055         .base = .{ .tag = .func },
   5056         .data = .{
   5057             .is_pub = is_pub,
   5058             .is_extern = is_extern,
   5059             .is_export = is_export,
   5060             .is_inline = is_inline,
   5061             .is_var_args = is_var_args,
   5062             .name = name,
   5063             .linksection_string = linksection_string,
   5064             .explicit_callconv = explicit_callconv,
   5065             .params = try c.arena.dupe(ast.Payload.Param, fn_params.items),
   5066             .return_type = return_type_node,
   5067             .body = null,
   5068             .alignment = alignment,
   5069         },
   5070     };
   5071     return payload;
   5072 }
   5073 
   5074 fn warn(c: *Context, scope: *Scope, loc: clang.SourceLocation, comptime format: []const u8, args: anytype) !void {
   5075     const str = try c.locStr(loc);
   5076     const value = try std.fmt.allocPrint(c.arena, "// {s}: warning: " ++ format, .{str} ++ args);
   5077     try scope.appendNode(try Tag.warning.create(c.arena, value));
   5078 }
   5079 
   5080 fn fail(
   5081     c: *Context,
   5082     err: anytype,
   5083     source_loc: clang.SourceLocation,
   5084     comptime format: []const u8,
   5085     args: anytype,
   5086 ) (@TypeOf(err) || error{OutOfMemory}) {
   5087     try warn(c, &c.global_scope.base, source_loc, format, args);
   5088     return err;
   5089 }
   5090 
   5091 pub fn failDecl(c: *Context, loc: clang.SourceLocation, name: []const u8, comptime format: []const u8, args: anytype) Error!void {
   5092     // location
   5093     // pub const name = @compileError(msg);
   5094     const fail_msg = try std.fmt.allocPrint(c.arena, format, args);
   5095     try addTopLevelDecl(c, name, try Tag.fail_decl.create(c.arena, .{ .actual = name, .mangled = fail_msg }));
   5096     const str = try c.locStr(loc);
   5097     const location_comment = try std.fmt.allocPrint(c.arena, "// {s}", .{str});
   5098     try c.global_scope.nodes.append(try Tag.warning.create(c.arena, location_comment));
   5099 }
   5100 
   5101 const MacroCtx = struct {
   5102     source: []const u8,
   5103     list: []const CToken,
   5104     i: usize = 0,
   5105     loc: clang.SourceLocation,
   5106     name: []const u8,
   5107 
   5108     fn peek(self: *MacroCtx) ?CToken.Id {
   5109         if (self.i >= self.list.len) return null;
   5110         return self.list[self.i + 1].id;
   5111     }
   5112 
   5113     fn next(self: *MacroCtx) ?CToken.Id {
   5114         if (self.i >= self.list.len) return null;
   5115         self.i += 1;
   5116         return self.list[self.i].id;
   5117     }
   5118 
   5119     fn skip(self: *MacroCtx, c: *Context, expected_id: CToken.Id) ParseError!void {
   5120         const next_id = self.next().?;
   5121         if (next_id != expected_id and !(expected_id == .identifier and next_id == .extended_identifier)) {
   5122             try self.fail(
   5123                 c,
   5124                 "unable to translate C expr: expected '{s}' instead got '{s}'",
   5125                 .{ expected_id.symbol(), next_id.symbol() },
   5126             );
   5127             return error.ParseError;
   5128         }
   5129     }
   5130 
   5131     fn slice(self: *MacroCtx) []const u8 {
   5132         const tok = self.list[self.i];
   5133         return self.source[tok.start..tok.end];
   5134     }
   5135 
   5136     fn fail(self: *MacroCtx, c: *Context, comptime fmt: []const u8, args: anytype) !void {
   5137         return failDecl(c, self.loc, self.name, fmt, args);
   5138     }
   5139 
   5140     fn makeSlicer(self: *const MacroCtx) MacroSlicer {
   5141         return .{ .source = self.source, .tokens = self.list };
   5142     }
   5143 
   5144     const MacroTranslateError = union(enum) {
   5145         undefined_identifier: []const u8,
   5146         invalid_arg_usage: []const u8,
   5147     };
   5148 
   5149     fn checkTranslatableMacro(self: *MacroCtx, scope: *Scope, params: []const ast.Payload.Param) ?MacroTranslateError {
   5150         const slicer = self.makeSlicer();
   5151         var last_is_type_kw = false;
   5152         var i: usize = 1; // index 0 is the macro name
   5153         while (i < self.list.len) : (i += 1) {
   5154             const token = self.list[i];
   5155             switch (token.id) {
   5156                 .period, .arrow => i += 1, // skip next token since field identifiers can be unknown
   5157                 .keyword_struct, .keyword_union, .keyword_enum => if (!last_is_type_kw) {
   5158                     last_is_type_kw = true;
   5159                     continue;
   5160                 },
   5161                 .identifier, .extended_identifier => {
   5162                     const identifier = slicer.slice(token);
   5163                     const is_param = for (params) |param| {
   5164                         if (param.name != null and mem.eql(u8, identifier, param.name.?)) break true;
   5165                     } else false;
   5166                     if (is_param and last_is_type_kw) {
   5167                         return .{ .invalid_arg_usage = identifier };
   5168                     }
   5169                     if (!scope.contains(identifier) and !isBuiltinDefined(identifier) and !is_param) {
   5170                         return .{ .undefined_identifier = identifier };
   5171                     }
   5172                 },
   5173                 else => {},
   5174             }
   5175             last_is_type_kw = false;
   5176         }
   5177         return null;
   5178     }
   5179 };
   5180 
   5181 fn getMacroText(unit: *const clang.ASTUnit, c: *const Context, macro: *const clang.MacroDefinitionRecord) ![]const u8 {
   5182     const begin_loc = macro.getSourceRange_getBegin();
   5183     const end_loc = clang.Lexer.getLocForEndOfToken(macro.getSourceRange_getEnd(), c.source_manager, unit);
   5184 
   5185     const begin_c = c.source_manager.getCharacterData(begin_loc);
   5186     const end_c = c.source_manager.getCharacterData(end_loc);
   5187     const slice_len = @intFromPtr(end_c) - @intFromPtr(begin_c);
   5188 
   5189     var comp = aro.Compilation.init(c.gpa);
   5190     defer comp.deinit();
   5191     const result = comp.addSourceFromBuffer("", begin_c[0..slice_len]) catch return error.OutOfMemory;
   5192 
   5193     return c.arena.dupe(u8, result.buf);
   5194 }
   5195 
   5196 fn transPreprocessorEntities(c: *Context, unit: *clang.ASTUnit) Error!void {
   5197     // TODO if we see #undef, delete it from the table
   5198     var it = unit.getLocalPreprocessingEntities_begin();
   5199     const it_end = unit.getLocalPreprocessingEntities_end();
   5200     var tok_list = std.ArrayList(CToken).init(c.gpa);
   5201     defer tok_list.deinit();
   5202     const scope = c.global_scope;
   5203 
   5204     while (it.I != it_end.I) : (it.I += 1) {
   5205         const entity = it.deref();
   5206         tok_list.items.len = 0;
   5207         switch (entity.getKind()) {
   5208             .MacroDefinitionKind => {
   5209                 const macro = @as(*clang.MacroDefinitionRecord, @ptrCast(entity));
   5210                 const raw_name = macro.getName_getNameStart();
   5211                 const begin_loc = macro.getSourceRange_getBegin();
   5212 
   5213                 const name = try c.str(raw_name);
   5214                 if (scope.containsNow(name)) {
   5215                     continue;
   5216                 }
   5217 
   5218                 const source = try getMacroText(unit, c, macro);
   5219 
   5220                 try common.tokenizeMacro(source, &tok_list);
   5221 
   5222                 var macro_ctx = MacroCtx{
   5223                     .source = source,
   5224                     .list = tok_list.items,
   5225                     .name = name,
   5226                     .loc = begin_loc,
   5227                 };
   5228                 assert(mem.eql(u8, macro_ctx.slice(), name));
   5229 
   5230                 var macro_fn = false;
   5231                 switch (macro_ctx.peek().?) {
   5232                     .identifier, .extended_identifier => {
   5233                         // if it equals itself, ignore. for example, from stdio.h:
   5234                         // #define stdin stdin
   5235                         const tok = macro_ctx.list[1];
   5236                         if (mem.eql(u8, name, source[tok.start..tok.end])) {
   5237                             assert(!c.global_names.contains(source[tok.start..tok.end]));
   5238                             continue;
   5239                         }
   5240                     },
   5241                     .nl, .eof => {
   5242                         // this means it is a macro without a value
   5243                         // We define it as an empty string so that it can still be used with ++
   5244                         const str_node = try Tag.string_literal.create(c.arena, "\"\"");
   5245                         const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = name, .init = str_node });
   5246                         try c.global_scope.macro_table.put(name, var_decl);
   5247                         try c.global_scope.blank_macros.put(name, {});
   5248                         continue;
   5249                     },
   5250                     .l_paren => {
   5251                         // if the name is immediately followed by a '(' then it is a function
   5252                         macro_fn = macro_ctx.list[0].end == macro_ctx.list[1].start;
   5253                     },
   5254                     else => {},
   5255                 }
   5256 
   5257                 (if (macro_fn)
   5258                     transMacroFnDefine(c, &macro_ctx)
   5259                 else
   5260                     transMacroDefine(c, &macro_ctx)) catch |err| switch (err) {
   5261                     error.ParseError => continue,
   5262                     error.OutOfMemory => |e| return e,
   5263                 };
   5264             },
   5265             else => {},
   5266         }
   5267     }
   5268 }
   5269 
   5270 fn transMacroDefine(c: *Context, m: *MacroCtx) ParseError!void {
   5271     const scope = &c.global_scope.base;
   5272 
   5273     if (m.checkTranslatableMacro(scope, &.{})) |err| switch (err) {
   5274         .undefined_identifier => |ident| return m.fail(c, "unable to translate macro: undefined identifier `{s}`", .{ident}),
   5275         .invalid_arg_usage => unreachable, // no args
   5276     };
   5277 
   5278     // Check if the macro only uses other blank macros.
   5279     while (true) {
   5280         switch (m.peek().?) {
   5281             .identifier, .extended_identifier => {
   5282                 const tok = m.list[m.i + 1];
   5283                 const slice = m.source[tok.start..tok.end];
   5284                 if (c.global_scope.blank_macros.contains(slice)) {
   5285                     m.i += 1;
   5286                     continue;
   5287                 }
   5288             },
   5289             .eof, .nl => {
   5290                 try c.global_scope.blank_macros.put(m.name, {});
   5291                 const init_node = try Tag.string_literal.create(c.arena, "\"\"");
   5292                 const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = m.name, .init = init_node });
   5293                 try c.global_scope.macro_table.put(m.name, var_decl);
   5294                 return;
   5295             },
   5296             else => {},
   5297         }
   5298         break;
   5299     }
   5300 
   5301     const init_node = try parseCExpr(c, m, scope);
   5302     const last = m.next().?;
   5303     if (last != .eof and last != .nl)
   5304         return m.fail(c, "unable to translate C expr: unexpected token '{s}'", .{last.symbol()});
   5305 
   5306     const var_decl = try Tag.pub_var_simple.create(c.arena, .{ .name = m.name, .init = init_node });
   5307     try c.global_scope.macro_table.put(m.name, var_decl);
   5308 }
   5309 
   5310 fn transMacroFnDefine(c: *Context, m: *MacroCtx) ParseError!void {
   5311     const macro_slicer = m.makeSlicer();
   5312     if (try c.pattern_list.match(c.gpa, macro_slicer)) |pattern| {
   5313         const decl = try Tag.pub_var_simple.create(c.arena, .{
   5314             .name = m.name,
   5315             .init = try Tag.helpers_macro.create(c.arena, pattern.impl),
   5316         });
   5317         try c.global_scope.macro_table.put(m.name, decl);
   5318         return;
   5319     }
   5320 
   5321     var block_scope = try Scope.Block.init(c, &c.global_scope.base, false);
   5322     defer block_scope.deinit();
   5323     const scope = &block_scope.base;
   5324 
   5325     try m.skip(c, .l_paren);
   5326 
   5327     var fn_params = std.ArrayList(ast.Payload.Param).init(c.gpa);
   5328     defer fn_params.deinit();
   5329 
   5330     while (true) {
   5331         switch (m.peek().?) {
   5332             .identifier, .extended_identifier => _ = m.next(),
   5333             else => break,
   5334         }
   5335 
   5336         const mangled_name = try block_scope.makeMangledName(c, m.slice());
   5337         try fn_params.append(.{
   5338             .is_noalias = false,
   5339             .name = mangled_name,
   5340             .type = Tag.@"anytype".init(),
   5341         });
   5342         try block_scope.discardVariable(c, mangled_name);
   5343         if (m.peek().? != .comma) break;
   5344         _ = m.next();
   5345     }
   5346 
   5347     try m.skip(c, .r_paren);
   5348 
   5349     if (m.checkTranslatableMacro(scope, fn_params.items)) |err| switch (err) {
   5350         .undefined_identifier => |ident| return m.fail(c, "unable to translate macro: undefined identifier `{s}`", .{ident}),
   5351         .invalid_arg_usage => |ident| return m.fail(c, "unable to translate macro: untranslatable usage of arg `{s}`", .{ident}),
   5352     };
   5353 
   5354     const expr = try parseCExpr(c, m, scope);
   5355     const last = m.next().?;
   5356     if (last != .eof and last != .nl)
   5357         return m.fail(c, "unable to translate C expr: unexpected token '{s}'", .{last.symbol()});
   5358 
   5359     const typeof_arg = if (expr.castTag(.block)) |some| blk: {
   5360         const stmts = some.data.stmts;
   5361         const blk_last = stmts[stmts.len - 1];
   5362         const br = blk_last.castTag(.break_val).?;
   5363         break :blk br.data.val;
   5364     } else expr;
   5365 
   5366     const return_type = if (typeof_arg.castTag(.helpers_cast) orelse typeof_arg.castTag(.std_mem_zeroinit)) |some|
   5367         some.data.lhs
   5368     else if (typeof_arg.castTag(.std_mem_zeroes)) |some|
   5369         some.data
   5370     else
   5371         try Tag.typeof.create(c.arena, typeof_arg);
   5372 
   5373     const return_expr = try Tag.@"return".create(c.arena, expr);
   5374     try block_scope.statements.append(return_expr);
   5375 
   5376     const fn_decl = try Tag.pub_inline_fn.create(c.arena, .{
   5377         .name = m.name,
   5378         .params = try c.arena.dupe(ast.Payload.Param, fn_params.items),
   5379         .return_type = return_type,
   5380         .body = try block_scope.complete(c),
   5381     });
   5382     try c.global_scope.macro_table.put(m.name, fn_decl);
   5383 }
   5384 
   5385 const ParseError = Error || error{ParseError};
   5386 
   5387 fn parseCExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
   5388     // TODO parseCAssignExpr here
   5389     var block_scope = try Scope.Block.init(c, scope, true);
   5390     defer block_scope.deinit();
   5391 
   5392     const node = try parseCCondExpr(c, m, &block_scope.base);
   5393     if (m.next().? != .comma) {
   5394         m.i -= 1;
   5395         return node;
   5396     }
   5397 
   5398     var last = node;
   5399     while (true) {
   5400         // suppress result
   5401         const ignore = try Tag.discard.create(c.arena, .{ .should_skip = false, .value = last });
   5402         try block_scope.statements.append(ignore);
   5403 
   5404         last = try parseCCondExpr(c, m, &block_scope.base);
   5405         if (m.next().? != .comma) {
   5406             m.i -= 1;
   5407             break;
   5408         }
   5409     }
   5410 
   5411     const break_node = try Tag.break_val.create(c.arena, .{
   5412         .label = block_scope.label,
   5413         .val = last,
   5414     });
   5415     try block_scope.statements.append(break_node);
   5416     return try block_scope.complete(c);
   5417 }
   5418 
   5419 fn parseCNumLit(ctx: *Context, m: *MacroCtx) ParseError!Node {
   5420     const lit_bytes = m.slice();
   5421     var bytes = try std.ArrayListUnmanaged(u8).initCapacity(ctx.arena, lit_bytes.len + 3);
   5422 
   5423     const prefix = aro.Tree.Token.NumberPrefix.fromString(lit_bytes);
   5424     switch (prefix) {
   5425         .binary => bytes.appendSliceAssumeCapacity("0b"),
   5426         .octal => bytes.appendSliceAssumeCapacity("0o"),
   5427         .hex => bytes.appendSliceAssumeCapacity("0x"),
   5428         .decimal => {},
   5429     }
   5430 
   5431     const after_prefix = lit_bytes[prefix.stringLen()..];
   5432     const after_int = for (after_prefix, 0..) |c, i| switch (c) {
   5433         '.' => {
   5434             if (i == 0) {
   5435                 bytes.appendAssumeCapacity('0');
   5436             }
   5437             break after_prefix[i..];
   5438         },
   5439         'e', 'E' => {
   5440             if (prefix != .hex) break after_prefix[i..];
   5441             bytes.appendAssumeCapacity(c);
   5442         },
   5443         'p', 'P' => break after_prefix[i..],
   5444         '0'...'9', 'a'...'d', 'A'...'D', 'f', 'F' => {
   5445             if (!prefix.digitAllowed(c)) break after_prefix[i..];
   5446             bytes.appendAssumeCapacity(c);
   5447         },
   5448         '\'' => {
   5449             bytes.appendAssumeCapacity('_');
   5450         },
   5451         else => break after_prefix[i..],
   5452     } else "";
   5453 
   5454     const after_frac = frac: {
   5455         if (after_int.len == 0 or after_int[0] != '.') break :frac after_int;
   5456         bytes.appendAssumeCapacity('.');
   5457         for (after_int[1..], 1..) |c, i| {
   5458             if (c == '\'') {
   5459                 bytes.appendAssumeCapacity('_');
   5460                 continue;
   5461             }
   5462             if (!prefix.digitAllowed(c)) break :frac after_int[i..];
   5463             bytes.appendAssumeCapacity(c);
   5464         }
   5465         break :frac "";
   5466     };
   5467 
   5468     const suffix_str = exponent: {
   5469         if (after_frac.len == 0) break :exponent after_frac;
   5470         switch (after_frac[0]) {
   5471             'e', 'E' => {},
   5472             'p', 'P' => if (prefix != .hex) break :exponent after_frac,
   5473             else => break :exponent after_frac,
   5474         }
   5475         bytes.appendAssumeCapacity(after_frac[0]);
   5476         for (after_frac[1..], 1..) |c, i| switch (c) {
   5477             '+', '-', '0'...'9' => {
   5478                 bytes.appendAssumeCapacity(c);
   5479             },
   5480             '\'' => {
   5481                 bytes.appendAssumeCapacity('_');
   5482             },
   5483             else => break :exponent after_frac[i..],
   5484         };
   5485         break :exponent "";
   5486     };
   5487 
   5488     const is_float = after_int.len != suffix_str.len;
   5489     const suffix = aro.Tree.Token.NumberSuffix.fromString(suffix_str, if (is_float) .float else .int) orelse {
   5490         try m.fail(ctx, "invalid number suffix: '{s}'", .{suffix_str});
   5491         return error.ParseError;
   5492     };
   5493     if (suffix.isImaginary()) {
   5494         try m.fail(ctx, "TODO: imaginary literals", .{});
   5495         return error.ParseError;
   5496     }
   5497     if (suffix.isBitInt()) {
   5498         try m.fail(ctx, "TODO: _BitInt literals", .{});
   5499         return error.ParseError;
   5500     }
   5501 
   5502     if (is_float) {
   5503         const type_node = try Tag.type.create(ctx.arena, switch (suffix) {
   5504             .F16 => "f16",
   5505             .F => "f32",
   5506             .None => "f64",
   5507             .L => "c_longdouble",
   5508             .W => "f80",
   5509             .Q, .F128 => "f128",
   5510             else => unreachable,
   5511         });
   5512         const rhs = try Tag.float_literal.create(ctx.arena, bytes.items);
   5513         return Tag.as.create(ctx.arena, .{ .lhs = type_node, .rhs = rhs });
   5514     } else {
   5515         const type_node = try Tag.type.create(ctx.arena, switch (suffix) {
   5516             .None => "c_int",
   5517             .U => "c_uint",
   5518             .L => "c_long",
   5519             .UL => "c_ulong",
   5520             .LL => "c_longlong",
   5521             .ULL => "c_ulonglong",
   5522             else => unreachable,
   5523         });
   5524         const value = std.fmt.parseInt(i128, bytes.items, 0) catch math.maxInt(i128);
   5525 
   5526         // make the output less noisy by skipping promoteIntLiteral where
   5527         // it's guaranteed to not be required because of C standard type constraints
   5528         const guaranteed_to_fit = switch (suffix) {
   5529             .None => math.cast(i16, value) != null,
   5530             .U => math.cast(u16, value) != null,
   5531             .L => math.cast(i32, value) != null,
   5532             .UL => math.cast(u32, value) != null,
   5533             .LL => math.cast(i64, value) != null,
   5534             .ULL => math.cast(u64, value) != null,
   5535             else => unreachable,
   5536         };
   5537 
   5538         const literal_node = try Tag.integer_literal.create(ctx.arena, bytes.items);
   5539         if (guaranteed_to_fit) {
   5540             return Tag.as.create(ctx.arena, .{ .lhs = type_node, .rhs = literal_node });
   5541         } else {
   5542             return Tag.helpers_promoteIntLiteral.create(ctx.arena, .{
   5543                 .type = type_node,
   5544                 .value = literal_node,
   5545                 .base = try Tag.enum_literal.create(ctx.arena, @tagName(prefix)),
   5546             });
   5547         }
   5548     }
   5549 }
   5550 
   5551 fn zigifyEscapeSequences(ctx: *Context, m: *MacroCtx) ![]const u8 {
   5552     var source = m.slice();
   5553     for (source, 0..) |c, i| {
   5554         if (c == '\"' or c == '\'') {
   5555             source = source[i..];
   5556             break;
   5557         }
   5558     }
   5559     for (source) |c| {
   5560         if (c == '\\' or c == '\t') {
   5561             break;
   5562         }
   5563     } else return source;
   5564     var bytes = try ctx.arena.alloc(u8, source.len * 2);
   5565     var state: enum {
   5566         start,
   5567         escape,
   5568         hex,
   5569         octal,
   5570     } = .start;
   5571     var i: usize = 0;
   5572     var count: u8 = 0;
   5573     var num: u8 = 0;
   5574     for (source) |c| {
   5575         switch (state) {
   5576             .escape => {
   5577                 switch (c) {
   5578                     'n', 'r', 't', '\\', '\'', '\"' => {
   5579                         bytes[i] = c;
   5580                     },
   5581                     '0'...'7' => {
   5582                         count += 1;
   5583                         num += c - '0';
   5584                         state = .octal;
   5585                         bytes[i] = 'x';
   5586                     },
   5587                     'x' => {
   5588                         state = .hex;
   5589                         bytes[i] = 'x';
   5590                     },
   5591                     'a' => {
   5592                         bytes[i] = 'x';
   5593                         i += 1;
   5594                         bytes[i] = '0';
   5595                         i += 1;
   5596                         bytes[i] = '7';
   5597                     },
   5598                     'b' => {
   5599                         bytes[i] = 'x';
   5600                         i += 1;
   5601                         bytes[i] = '0';
   5602                         i += 1;
   5603                         bytes[i] = '8';
   5604                     },
   5605                     'f' => {
   5606                         bytes[i] = 'x';
   5607                         i += 1;
   5608                         bytes[i] = '0';
   5609                         i += 1;
   5610                         bytes[i] = 'C';
   5611                     },
   5612                     'v' => {
   5613                         bytes[i] = 'x';
   5614                         i += 1;
   5615                         bytes[i] = '0';
   5616                         i += 1;
   5617                         bytes[i] = 'B';
   5618                     },
   5619                     '?' => {
   5620                         i -= 1;
   5621                         bytes[i] = '?';
   5622                     },
   5623                     'u', 'U' => {
   5624                         try m.fail(ctx, "macro tokenizing failed: TODO unicode escape sequences", .{});
   5625                         return error.ParseError;
   5626                     },
   5627                     else => {
   5628                         try m.fail(ctx, "macro tokenizing failed: unknown escape sequence", .{});
   5629                         return error.ParseError;
   5630                     },
   5631                 }
   5632                 i += 1;
   5633                 if (state == .escape)
   5634                     state = .start;
   5635             },
   5636             .start => {
   5637                 if (c == '\t') {
   5638                     bytes[i] = '\\';
   5639                     i += 1;
   5640                     bytes[i] = 't';
   5641                     i += 1;
   5642                     continue;
   5643                 }
   5644                 if (c == '\\') {
   5645                     state = .escape;
   5646                 }
   5647                 bytes[i] = c;
   5648                 i += 1;
   5649             },
   5650             .hex => {
   5651                 switch (c) {
   5652                     '0'...'9' => {
   5653                         num = std.math.mul(u8, num, 16) catch {
   5654                             try m.fail(ctx, "macro tokenizing failed: hex literal overflowed", .{});
   5655                             return error.ParseError;
   5656                         };
   5657                         num += c - '0';
   5658                     },
   5659                     'a'...'f' => {
   5660                         num = std.math.mul(u8, num, 16) catch {
   5661                             try m.fail(ctx, "macro tokenizing failed: hex literal overflowed", .{});
   5662                             return error.ParseError;
   5663                         };
   5664                         num += c - 'a' + 10;
   5665                     },
   5666                     'A'...'F' => {
   5667                         num = std.math.mul(u8, num, 16) catch {
   5668                             try m.fail(ctx, "macro tokenizing failed: hex literal overflowed", .{});
   5669                             return error.ParseError;
   5670                         };
   5671                         num += c - 'A' + 10;
   5672                     },
   5673                     else => {
   5674                         i += std.fmt.formatIntBuf(bytes[i..], num, 16, .lower, std.fmt.FormatOptions{ .fill = '0', .width = 2 });
   5675                         num = 0;
   5676                         if (c == '\\')
   5677                             state = .escape
   5678                         else
   5679                             state = .start;
   5680                         bytes[i] = c;
   5681                         i += 1;
   5682                     },
   5683                 }
   5684             },
   5685             .octal => {
   5686                 const accept_digit = switch (c) {
   5687                     // The maximum length of a octal literal is 3 digits
   5688                     '0'...'7' => count < 3,
   5689                     else => false,
   5690                 };
   5691 
   5692                 if (accept_digit) {
   5693                     count += 1;
   5694                     num = std.math.mul(u8, num, 8) catch {
   5695                         try m.fail(ctx, "macro tokenizing failed: octal literal overflowed", .{});
   5696                         return error.ParseError;
   5697                     };
   5698                     num += c - '0';
   5699                 } else {
   5700                     i += std.fmt.formatIntBuf(bytes[i..], num, 16, .lower, std.fmt.FormatOptions{ .fill = '0', .width = 2 });
   5701                     num = 0;
   5702                     count = 0;
   5703                     if (c == '\\')
   5704                         state = .escape
   5705                     else
   5706                         state = .start;
   5707                     bytes[i] = c;
   5708                     i += 1;
   5709                 }
   5710             },
   5711         }
   5712     }
   5713     if (state == .hex or state == .octal)
   5714         i += std.fmt.formatIntBuf(bytes[i..], num, 16, .lower, std.fmt.FormatOptions{ .fill = '0', .width = 2 });
   5715     return bytes[0..i];
   5716 }
   5717 
   5718 /// non-ASCII characters (c > 127) are also treated as non-printable by fmtSliceEscapeLower.
   5719 /// If a C string literal or char literal in a macro is not valid UTF-8, we need to escape
   5720 /// non-ASCII characters so that the Zig source we output will itself be UTF-8.
   5721 fn escapeUnprintables(ctx: *Context, m: *MacroCtx) ![]const u8 {
   5722     const zigified = try zigifyEscapeSequences(ctx, m);
   5723     if (std.unicode.utf8ValidateSlice(zigified)) return zigified;
   5724 
   5725     const formatter = std.fmt.fmtSliceEscapeLower(zigified);
   5726     const encoded_size = @as(usize, @intCast(std.fmt.count("{s}", .{formatter})));
   5727     const output = try ctx.arena.alloc(u8, encoded_size);
   5728     return std.fmt.bufPrint(output, "{s}", .{formatter}) catch |err| switch (err) {
   5729         error.NoSpaceLeft => unreachable,
   5730         else => |e| return e,
   5731     };
   5732 }
   5733 
   5734 fn parseCPrimaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
   5735     const tok = m.next().?;
   5736     const slice = m.slice();
   5737     switch (tok) {
   5738         .char_literal,
   5739         .char_literal_utf_8,
   5740         .char_literal_utf_16,
   5741         .char_literal_utf_32,
   5742         .char_literal_wide,
   5743         => {
   5744             if (slice[0] != '\'' or slice[1] == '\\' or slice.len == 3) {
   5745                 return Tag.char_literal.create(c.arena, try escapeUnprintables(c, m));
   5746             } else {
   5747                 const str = try std.fmt.allocPrint(c.arena, "0x{s}", .{std.fmt.fmtSliceHexLower(slice[1 .. slice.len - 1])});
   5748                 return Tag.integer_literal.create(c.arena, str);
   5749             }
   5750         },
   5751         .string_literal,
   5752         .string_literal_utf_16,
   5753         .string_literal_utf_8,
   5754         .string_literal_utf_32,
   5755         .string_literal_wide,
   5756         => {
   5757             return Tag.string_literal.create(c.arena, try escapeUnprintables(c, m));
   5758         },
   5759         .pp_num => {
   5760             return parseCNumLit(c, m);
   5761         },
   5762         .identifier, .extended_identifier => {
   5763             if (c.global_scope.blank_macros.contains(slice)) {
   5764                 return parseCPrimaryExpr(c, m, scope);
   5765             }
   5766             const mangled_name = scope.getAlias(slice);
   5767             if (builtin_typedef_map.get(mangled_name)) |ty| return Tag.type.create(c.arena, ty);
   5768             const identifier = try Tag.identifier.create(c.arena, mangled_name);
   5769             scope.skipVariableDiscard(identifier.castTag(.identifier).?.data);
   5770             return identifier;
   5771         },
   5772         .l_paren => {
   5773             const inner_node = try parseCExpr(c, m, scope);
   5774 
   5775             try m.skip(c, .r_paren);
   5776             return inner_node;
   5777         },
   5778         else => {
   5779             // for handling type macros (EVIL)
   5780             // TODO maybe detect and treat type macros as typedefs in parseCSpecifierQualifierList?
   5781             m.i -= 1;
   5782             if (try parseCTypeName(c, m, scope, true)) |type_name| {
   5783                 return type_name;
   5784             }
   5785             try m.fail(c, "unable to translate C expr: unexpected token '{s}'", .{tok.symbol()});
   5786             return error.ParseError;
   5787         },
   5788     }
   5789 }
   5790 
   5791 fn macroIntFromBool(c: *Context, node: Node) !Node {
   5792     if (!isBoolRes(node)) {
   5793         return node;
   5794     }
   5795 
   5796     return Tag.int_from_bool.create(c.arena, node);
   5797 }
   5798 
   5799 fn macroIntToBool(c: *Context, node: Node) !Node {
   5800     if (isBoolRes(node)) {
   5801         return node;
   5802     }
   5803     if (node.tag() == .string_literal) {
   5804         // @intFromPtr(node) != 0
   5805         const int_from_ptr = try Tag.int_from_ptr.create(c.arena, node);
   5806         return Tag.not_equal.create(c.arena, .{ .lhs = int_from_ptr, .rhs = Tag.zero_literal.init() });
   5807     }
   5808     // node != 0
   5809     return Tag.not_equal.create(c.arena, .{ .lhs = node, .rhs = Tag.zero_literal.init() });
   5810 }
   5811 
   5812 fn parseCCondExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
   5813     const node = try parseCOrExpr(c, m, scope);
   5814     if (m.peek().? != .question_mark) {
   5815         return node;
   5816     }
   5817     _ = m.next();
   5818 
   5819     const then_body = try parseCOrExpr(c, m, scope);
   5820     try m.skip(c, .colon);
   5821     const else_body = try parseCCondExpr(c, m, scope);
   5822     return Tag.@"if".create(c.arena, .{ .cond = node, .then = then_body, .@"else" = else_body });
   5823 }
   5824 
   5825 fn parseCOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
   5826     var node = try parseCAndExpr(c, m, scope);
   5827     while (m.next().? == .pipe_pipe) {
   5828         const lhs = try macroIntToBool(c, node);
   5829         const rhs = try macroIntToBool(c, try parseCAndExpr(c, m, scope));
   5830         node = try Tag.@"or".create(c.arena, .{ .lhs = lhs, .rhs = rhs });
   5831     }
   5832     m.i -= 1;
   5833     return node;
   5834 }
   5835 
   5836 fn parseCAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
   5837     var node = try parseCBitOrExpr(c, m, scope);
   5838     while (m.next().? == .ampersand_ampersand) {
   5839         const lhs = try macroIntToBool(c, node);
   5840         const rhs = try macroIntToBool(c, try parseCBitOrExpr(c, m, scope));
   5841         node = try Tag.@"and".create(c.arena, .{ .lhs = lhs, .rhs = rhs });
   5842     }
   5843     m.i -= 1;
   5844     return node;
   5845 }
   5846 
   5847 fn parseCBitOrExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
   5848     var node = try parseCBitXorExpr(c, m, scope);
   5849     while (m.next().? == .pipe) {
   5850         const lhs = try macroIntFromBool(c, node);
   5851         const rhs = try macroIntFromBool(c, try parseCBitXorExpr(c, m, scope));
   5852         node = try Tag.bit_or.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
   5853     }
   5854     m.i -= 1;
   5855     return node;
   5856 }
   5857 
   5858 fn parseCBitXorExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
   5859     var node = try parseCBitAndExpr(c, m, scope);
   5860     while (m.next().? == .caret) {
   5861         const lhs = try macroIntFromBool(c, node);
   5862         const rhs = try macroIntFromBool(c, try parseCBitAndExpr(c, m, scope));
   5863         node = try Tag.bit_xor.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
   5864     }
   5865     m.i -= 1;
   5866     return node;
   5867 }
   5868 
   5869 fn parseCBitAndExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
   5870     var node = try parseCEqExpr(c, m, scope);
   5871     while (m.next().? == .ampersand) {
   5872         const lhs = try macroIntFromBool(c, node);
   5873         const rhs = try macroIntFromBool(c, try parseCEqExpr(c, m, scope));
   5874         node = try Tag.bit_and.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
   5875     }
   5876     m.i -= 1;
   5877     return node;
   5878 }
   5879 
   5880 fn parseCEqExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
   5881     var node = try parseCRelExpr(c, m, scope);
   5882     while (true) {
   5883         switch (m.peek().?) {
   5884             .bang_equal => {
   5885                 _ = m.next();
   5886                 const lhs = try macroIntFromBool(c, node);
   5887                 const rhs = try macroIntFromBool(c, try parseCRelExpr(c, m, scope));
   5888                 node = try Tag.not_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
   5889             },
   5890             .equal_equal => {
   5891                 _ = m.next();
   5892                 const lhs = try macroIntFromBool(c, node);
   5893                 const rhs = try macroIntFromBool(c, try parseCRelExpr(c, m, scope));
   5894                 node = try Tag.equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
   5895             },
   5896             else => return node,
   5897         }
   5898     }
   5899 }
   5900 
   5901 fn parseCRelExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
   5902     var node = try parseCShiftExpr(c, m, scope);
   5903     while (true) {
   5904         switch (m.peek().?) {
   5905             .angle_bracket_right => {
   5906                 _ = m.next();
   5907                 const lhs = try macroIntFromBool(c, node);
   5908                 const rhs = try macroIntFromBool(c, try parseCShiftExpr(c, m, scope));
   5909                 node = try Tag.greater_than.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
   5910             },
   5911             .angle_bracket_right_equal => {
   5912                 _ = m.next();
   5913                 const lhs = try macroIntFromBool(c, node);
   5914                 const rhs = try macroIntFromBool(c, try parseCShiftExpr(c, m, scope));
   5915                 node = try Tag.greater_than_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
   5916             },
   5917             .angle_bracket_left => {
   5918                 _ = m.next();
   5919                 const lhs = try macroIntFromBool(c, node);
   5920                 const rhs = try macroIntFromBool(c, try parseCShiftExpr(c, m, scope));
   5921                 node = try Tag.less_than.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
   5922             },
   5923             .angle_bracket_left_equal => {
   5924                 _ = m.next();
   5925                 const lhs = try macroIntFromBool(c, node);
   5926                 const rhs = try macroIntFromBool(c, try parseCShiftExpr(c, m, scope));
   5927                 node = try Tag.less_than_equal.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
   5928             },
   5929             else => return node,
   5930         }
   5931     }
   5932 }
   5933 
   5934 fn parseCShiftExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
   5935     var node = try parseCAddSubExpr(c, m, scope);
   5936     while (true) {
   5937         switch (m.peek().?) {
   5938             .angle_bracket_angle_bracket_left => {
   5939                 _ = m.next();
   5940                 const lhs = try macroIntFromBool(c, node);
   5941                 const rhs = try macroIntFromBool(c, try parseCAddSubExpr(c, m, scope));
   5942                 node = try Tag.shl.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
   5943             },
   5944             .angle_bracket_angle_bracket_right => {
   5945                 _ = m.next();
   5946                 const lhs = try macroIntFromBool(c, node);
   5947                 const rhs = try macroIntFromBool(c, try parseCAddSubExpr(c, m, scope));
   5948                 node = try Tag.shr.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
   5949             },
   5950             else => return node,
   5951         }
   5952     }
   5953 }
   5954 
   5955 fn parseCAddSubExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
   5956     var node = try parseCMulExpr(c, m, scope);
   5957     while (true) {
   5958         switch (m.peek().?) {
   5959             .plus => {
   5960                 _ = m.next();
   5961                 const lhs = try macroIntFromBool(c, node);
   5962                 const rhs = try macroIntFromBool(c, try parseCMulExpr(c, m, scope));
   5963                 node = try Tag.add.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
   5964             },
   5965             .minus => {
   5966                 _ = m.next();
   5967                 const lhs = try macroIntFromBool(c, node);
   5968                 const rhs = try macroIntFromBool(c, try parseCMulExpr(c, m, scope));
   5969                 node = try Tag.sub.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
   5970             },
   5971             else => return node,
   5972         }
   5973     }
   5974 }
   5975 
   5976 fn parseCMulExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
   5977     var node = try parseCCastExpr(c, m, scope);
   5978     while (true) {
   5979         switch (m.next().?) {
   5980             .asterisk => {
   5981                 const lhs = try macroIntFromBool(c, node);
   5982                 const rhs = try macroIntFromBool(c, try parseCCastExpr(c, m, scope));
   5983                 node = try Tag.mul.create(c.arena, .{ .lhs = lhs, .rhs = rhs });
   5984             },
   5985             .slash => {
   5986                 const lhs = try macroIntFromBool(c, node);
   5987                 const rhs = try macroIntFromBool(c, try parseCCastExpr(c, m, scope));
   5988                 node = try Tag.macro_arithmetic.create(c.arena, .{ .op = .div, .lhs = lhs, .rhs = rhs });
   5989             },
   5990             .percent => {
   5991                 const lhs = try macroIntFromBool(c, node);
   5992                 const rhs = try macroIntFromBool(c, try parseCCastExpr(c, m, scope));
   5993                 node = try Tag.macro_arithmetic.create(c.arena, .{ .op = .rem, .lhs = lhs, .rhs = rhs });
   5994             },
   5995             else => {
   5996                 m.i -= 1;
   5997                 return node;
   5998             },
   5999         }
   6000     }
   6001 }
   6002 
   6003 fn parseCCastExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
   6004     switch (m.next().?) {
   6005         .l_paren => {
   6006             if (try parseCTypeName(c, m, scope, true)) |type_name| {
   6007                 while (true) {
   6008                     const next_token = m.next().?;
   6009                     switch (next_token) {
   6010                         .r_paren => break,
   6011                         else => |next_tag| {
   6012                             // Skip trailing blank defined before the RParen.
   6013                             if ((next_tag == .identifier or next_tag == .extended_identifier) and
   6014                                 c.global_scope.blank_macros.contains(m.slice()))
   6015                                 continue;
   6016 
   6017                             try m.fail(
   6018                                 c,
   6019                                 "unable to translate C expr: expected ')' instead got '{s}'",
   6020                                 .{next_token.symbol()},
   6021                             );
   6022                             return error.ParseError;
   6023                         },
   6024                     }
   6025                 }
   6026                 if (m.peek().? == .l_brace) {
   6027                     // initializer list
   6028                     return parseCPostfixExpr(c, m, scope, type_name);
   6029                 }
   6030                 const node_to_cast = try parseCCastExpr(c, m, scope);
   6031                 return Tag.helpers_cast.create(c.arena, .{ .lhs = type_name, .rhs = node_to_cast });
   6032             }
   6033         },
   6034         else => {},
   6035     }
   6036     m.i -= 1;
   6037     return parseCUnaryExpr(c, m, scope);
   6038 }
   6039 
   6040 // allow_fail is set when unsure if we are parsing a type-name
   6041 fn parseCTypeName(c: *Context, m: *MacroCtx, scope: *Scope, allow_fail: bool) ParseError!?Node {
   6042     if (try parseCSpecifierQualifierList(c, m, scope, allow_fail)) |node| {
   6043         return try parseCAbstractDeclarator(c, m, node);
   6044     } else {
   6045         return null;
   6046     }
   6047 }
   6048 
   6049 fn parseCSpecifierQualifierList(c: *Context, m: *MacroCtx, scope: *Scope, allow_fail: bool) ParseError!?Node {
   6050     const tok = m.next().?;
   6051     switch (tok) {
   6052         .identifier, .extended_identifier => {
   6053             if (c.global_scope.blank_macros.contains(m.slice())) {
   6054                 return try parseCSpecifierQualifierList(c, m, scope, allow_fail);
   6055             }
   6056             const mangled_name = scope.getAlias(m.slice());
   6057             if (!allow_fail or c.typedefs.contains(mangled_name)) {
   6058                 if (builtin_typedef_map.get(mangled_name)) |ty| return try Tag.type.create(c.arena, ty);
   6059                 return try Tag.identifier.create(c.arena, mangled_name);
   6060             }
   6061         },
   6062         .keyword_void => return try Tag.type.create(c.arena, "anyopaque"),
   6063         .keyword_bool => return try Tag.type.create(c.arena, "bool"),
   6064         .keyword_char,
   6065         .keyword_int,
   6066         .keyword_short,
   6067         .keyword_long,
   6068         .keyword_float,
   6069         .keyword_double,
   6070         .keyword_signed,
   6071         .keyword_unsigned,
   6072         .keyword_complex,
   6073         => {
   6074             m.i -= 1;
   6075             return try parseCNumericType(c, m);
   6076         },
   6077         .keyword_enum, .keyword_struct, .keyword_union => {
   6078             // struct Foo will be declared as struct_Foo by transRecordDecl
   6079             const slice = m.slice();
   6080             try m.skip(c, .identifier);
   6081 
   6082             const name = try std.fmt.allocPrint(c.arena, "{s}_{s}", .{ slice, m.slice() });
   6083             return try Tag.identifier.create(c.arena, name);
   6084         },
   6085         else => {},
   6086     }
   6087 
   6088     if (allow_fail) {
   6089         m.i -= 1;
   6090         return null;
   6091     } else {
   6092         try m.fail(c, "unable to translate C expr: unexpected token '{s}'", .{tok.symbol()});
   6093         return error.ParseError;
   6094     }
   6095 }
   6096 
   6097 fn parseCNumericType(c: *Context, m: *MacroCtx) ParseError!Node {
   6098     const KwCounter = struct {
   6099         double: u8 = 0,
   6100         long: u8 = 0,
   6101         int: u8 = 0,
   6102         float: u8 = 0,
   6103         short: u8 = 0,
   6104         char: u8 = 0,
   6105         unsigned: u8 = 0,
   6106         signed: u8 = 0,
   6107         complex: u8 = 0,
   6108 
   6109         fn eql(self: @This(), other: @This()) bool {
   6110             return meta.eql(self, other);
   6111         }
   6112     };
   6113 
   6114     // Yes, these can be in *any* order
   6115     // This still doesn't cover cases where for example volatile is intermixed
   6116 
   6117     var kw = KwCounter{};
   6118     // prevent overflow
   6119     var i: u8 = 0;
   6120     while (i < math.maxInt(u8)) : (i += 1) {
   6121         switch (m.next().?) {
   6122             .keyword_double => kw.double += 1,
   6123             .keyword_long => kw.long += 1,
   6124             .keyword_int => kw.int += 1,
   6125             .keyword_float => kw.float += 1,
   6126             .keyword_short => kw.short += 1,
   6127             .keyword_char => kw.char += 1,
   6128             .keyword_unsigned => kw.unsigned += 1,
   6129             .keyword_signed => kw.signed += 1,
   6130             .keyword_complex => kw.complex += 1,
   6131             else => {
   6132                 m.i -= 1;
   6133                 break;
   6134             },
   6135         }
   6136     }
   6137 
   6138     if (kw.eql(.{ .int = 1 }) or kw.eql(.{ .signed = 1 }) or kw.eql(.{ .signed = 1, .int = 1 }))
   6139         return Tag.type.create(c.arena, "c_int");
   6140 
   6141     if (kw.eql(.{ .unsigned = 1 }) or kw.eql(.{ .unsigned = 1, .int = 1 }))
   6142         return Tag.type.create(c.arena, "c_uint");
   6143 
   6144     if (kw.eql(.{ .long = 1 }) or kw.eql(.{ .signed = 1, .long = 1 }) or kw.eql(.{ .long = 1, .int = 1 }) or kw.eql(.{ .signed = 1, .long = 1, .int = 1 }))
   6145         return Tag.type.create(c.arena, "c_long");
   6146 
   6147     if (kw.eql(.{ .unsigned = 1, .long = 1 }) or kw.eql(.{ .unsigned = 1, .long = 1, .int = 1 }))
   6148         return Tag.type.create(c.arena, "c_ulong");
   6149 
   6150     if (kw.eql(.{ .long = 2 }) or kw.eql(.{ .signed = 1, .long = 2 }) or kw.eql(.{ .long = 2, .int = 1 }) or kw.eql(.{ .signed = 1, .long = 2, .int = 1 }))
   6151         return Tag.type.create(c.arena, "c_longlong");
   6152 
   6153     if (kw.eql(.{ .unsigned = 1, .long = 2 }) or kw.eql(.{ .unsigned = 1, .long = 2, .int = 1 }))
   6154         return Tag.type.create(c.arena, "c_ulonglong");
   6155 
   6156     if (kw.eql(.{ .signed = 1, .char = 1 }))
   6157         return Tag.type.create(c.arena, "i8");
   6158 
   6159     if (kw.eql(.{ .char = 1 }) or kw.eql(.{ .unsigned = 1, .char = 1 }))
   6160         return Tag.type.create(c.arena, "u8");
   6161 
   6162     if (kw.eql(.{ .short = 1 }) or kw.eql(.{ .signed = 1, .short = 1 }) or kw.eql(.{ .short = 1, .int = 1 }) or kw.eql(.{ .signed = 1, .short = 1, .int = 1 }))
   6163         return Tag.type.create(c.arena, "c_short");
   6164 
   6165     if (kw.eql(.{ .unsigned = 1, .short = 1 }) or kw.eql(.{ .unsigned = 1, .short = 1, .int = 1 }))
   6166         return Tag.type.create(c.arena, "c_ushort");
   6167 
   6168     if (kw.eql(.{ .float = 1 }))
   6169         return Tag.type.create(c.arena, "f32");
   6170 
   6171     if (kw.eql(.{ .double = 1 }))
   6172         return Tag.type.create(c.arena, "f64");
   6173 
   6174     if (kw.eql(.{ .long = 1, .double = 1 })) {
   6175         try m.fail(c, "unable to translate: TODO long double", .{});
   6176         return error.ParseError;
   6177     }
   6178 
   6179     if (kw.eql(.{ .float = 1, .complex = 1 })) {
   6180         try m.fail(c, "unable to translate: TODO _Complex", .{});
   6181         return error.ParseError;
   6182     }
   6183 
   6184     if (kw.eql(.{ .double = 1, .complex = 1 })) {
   6185         try m.fail(c, "unable to translate: TODO _Complex", .{});
   6186         return error.ParseError;
   6187     }
   6188 
   6189     if (kw.eql(.{ .long = 1, .double = 1, .complex = 1 })) {
   6190         try m.fail(c, "unable to translate: TODO _Complex", .{});
   6191         return error.ParseError;
   6192     }
   6193 
   6194     try m.fail(c, "unable to translate: invalid numeric type", .{});
   6195     return error.ParseError;
   6196 }
   6197 
   6198 fn parseCAbstractDeclarator(c: *Context, m: *MacroCtx, node: Node) ParseError!Node {
   6199     switch (m.next().?) {
   6200         .asterisk => {
   6201             // last token of `node`
   6202             const prev_id = m.list[m.i - 1].id;
   6203 
   6204             if (prev_id == .keyword_void) {
   6205                 const ptr = try Tag.single_pointer.create(c.arena, .{
   6206                     .is_const = false,
   6207                     .is_volatile = false,
   6208                     .elem_type = node,
   6209                 });
   6210                 return Tag.optional_type.create(c.arena, ptr);
   6211             } else {
   6212                 return Tag.c_pointer.create(c.arena, .{
   6213                     .is_const = false,
   6214                     .is_volatile = false,
   6215                     .elem_type = node,
   6216                 });
   6217             }
   6218         },
   6219         else => {
   6220             m.i -= 1;
   6221             return node;
   6222         },
   6223     }
   6224 }
   6225 
   6226 fn parseCPostfixExpr(c: *Context, m: *MacroCtx, scope: *Scope, type_name: ?Node) ParseError!Node {
   6227     var node = try parseCPostfixExprInner(c, m, scope, type_name);
   6228     // In C the preprocessor would handle concatting strings while expanding macros.
   6229     // This should do approximately the same by concatting any strings and identifiers
   6230     // after a primary or postfix expression.
   6231     while (true) {
   6232         switch (m.peek().?) {
   6233             .string_literal,
   6234             .string_literal_utf_16,
   6235             .string_literal_utf_8,
   6236             .string_literal_utf_32,
   6237             .string_literal_wide,
   6238             => {},
   6239             .identifier, .extended_identifier => {
   6240                 const tok = m.list[m.i + 1];
   6241                 const slice = m.source[tok.start..tok.end];
   6242                 if (c.global_scope.blank_macros.contains(slice)) {
   6243                     m.i += 1;
   6244                     continue;
   6245                 }
   6246             },
   6247             else => break,
   6248         }
   6249         const rhs = try parseCPostfixExprInner(c, m, scope, type_name);
   6250         node = try Tag.array_cat.create(c.arena, .{ .lhs = node, .rhs = rhs });
   6251     }
   6252     return node;
   6253 }
   6254 
   6255 fn parseCPostfixExprInner(c: *Context, m: *MacroCtx, scope: *Scope, type_name: ?Node) ParseError!Node {
   6256     var node = type_name orelse try parseCPrimaryExpr(c, m, scope);
   6257     while (true) {
   6258         switch (m.next().?) {
   6259             .period => {
   6260                 try m.skip(c, .identifier);
   6261 
   6262                 node = try Tag.field_access.create(c.arena, .{ .lhs = node, .field_name = m.slice() });
   6263             },
   6264             .arrow => {
   6265                 try m.skip(c, .identifier);
   6266 
   6267                 const deref = try Tag.deref.create(c.arena, node);
   6268                 node = try Tag.field_access.create(c.arena, .{ .lhs = deref, .field_name = m.slice() });
   6269             },
   6270             .l_bracket => {
   6271                 const index_val = try macroIntFromBool(c, try parseCExpr(c, m, scope));
   6272                 const index = try Tag.as.create(c.arena, .{
   6273                     .lhs = try Tag.type.create(c.arena, "usize"),
   6274                     .rhs = try Tag.int_cast.create(c.arena, index_val),
   6275                 });
   6276                 node = try Tag.array_access.create(c.arena, .{ .lhs = node, .rhs = index });
   6277                 try m.skip(c, .r_bracket);
   6278             },
   6279             .l_paren => {
   6280                 if (m.peek().? == .r_paren) {
   6281                     m.i += 1;
   6282                     node = try Tag.call.create(c.arena, .{ .lhs = node, .args = &[0]Node{} });
   6283                 } else {
   6284                     var args = std.ArrayList(Node).init(c.gpa);
   6285                     defer args.deinit();
   6286                     while (true) {
   6287                         const arg = try parseCCondExpr(c, m, scope);
   6288                         try args.append(arg);
   6289                         const next_id = m.next().?;
   6290                         switch (next_id) {
   6291                             .comma => {},
   6292                             .r_paren => break,
   6293                             else => {
   6294                                 try m.fail(c, "unable to translate C expr: expected ',' or ')' instead got '{s}'", .{next_id.symbol()});
   6295                                 return error.ParseError;
   6296                             },
   6297                         }
   6298                     }
   6299                     node = try Tag.call.create(c.arena, .{ .lhs = node, .args = try c.arena.dupe(Node, args.items) });
   6300                 }
   6301             },
   6302             .l_brace => {
   6303                 // Check for designated field initializers
   6304                 if (m.peek().? == .period) {
   6305                     var init_vals = std.ArrayList(ast.Payload.ContainerInitDot.Initializer).init(c.gpa);
   6306                     defer init_vals.deinit();
   6307 
   6308                     while (true) {
   6309                         try m.skip(c, .period);
   6310                         try m.skip(c, .identifier);
   6311                         const name = m.slice();
   6312                         try m.skip(c, .equal);
   6313 
   6314                         const val = try parseCCondExpr(c, m, scope);
   6315                         try init_vals.append(.{ .name = name, .value = val });
   6316                         const next_id = m.next().?;
   6317                         switch (next_id) {
   6318                             .comma => {},
   6319                             .r_brace => break,
   6320                             else => {
   6321                                 try m.fail(c, "unable to translate C expr: expected ',' or '}}' instead got '{s}'", .{next_id.symbol()});
   6322                                 return error.ParseError;
   6323                             },
   6324                         }
   6325                     }
   6326                     const tuple_node = try Tag.container_init_dot.create(c.arena, try c.arena.dupe(ast.Payload.ContainerInitDot.Initializer, init_vals.items));
   6327                     node = try Tag.std_mem_zeroinit.create(c.arena, .{ .lhs = node, .rhs = tuple_node });
   6328                     continue;
   6329                 }
   6330 
   6331                 var init_vals = std.ArrayList(Node).init(c.gpa);
   6332                 defer init_vals.deinit();
   6333 
   6334                 while (true) {
   6335                     const val = try parseCCondExpr(c, m, scope);
   6336                     try init_vals.append(val);
   6337                     const next_id = m.next().?;
   6338                     switch (next_id) {
   6339                         .comma => {},
   6340                         .r_brace => break,
   6341                         else => {
   6342                             try m.fail(c, "unable to translate C expr: expected ',' or '}}' instead got '{s}'", .{next_id.symbol()});
   6343                             return error.ParseError;
   6344                         },
   6345                     }
   6346                 }
   6347                 const tuple_node = try Tag.tuple.create(c.arena, try c.arena.dupe(Node, init_vals.items));
   6348                 node = try Tag.std_mem_zeroinit.create(c.arena, .{ .lhs = node, .rhs = tuple_node });
   6349             },
   6350             .plus_plus, .minus_minus => {
   6351                 try m.fail(c, "TODO postfix inc/dec expr", .{});
   6352                 return error.ParseError;
   6353             },
   6354             else => {
   6355                 m.i -= 1;
   6356                 return node;
   6357             },
   6358         }
   6359     }
   6360 }
   6361 
   6362 fn parseCUnaryExpr(c: *Context, m: *MacroCtx, scope: *Scope) ParseError!Node {
   6363     switch (m.next().?) {
   6364         .bang => {
   6365             const operand = try macroIntToBool(c, try parseCCastExpr(c, m, scope));
   6366             return Tag.not.create(c.arena, operand);
   6367         },
   6368         .minus => {
   6369             const operand = try macroIntFromBool(c, try parseCCastExpr(c, m, scope));
   6370             return Tag.negate.create(c.arena, operand);
   6371         },
   6372         .plus => return try parseCCastExpr(c, m, scope),
   6373         .tilde => {
   6374             const operand = try macroIntFromBool(c, try parseCCastExpr(c, m, scope));
   6375             return Tag.bit_not.create(c.arena, operand);
   6376         },
   6377         .asterisk => {
   6378             const operand = try parseCCastExpr(c, m, scope);
   6379             return Tag.deref.create(c.arena, operand);
   6380         },
   6381         .ampersand => {
   6382             const operand = try parseCCastExpr(c, m, scope);
   6383             return Tag.address_of.create(c.arena, operand);
   6384         },
   6385         .keyword_sizeof => {
   6386             const operand = if (m.peek().? == .l_paren) blk: {
   6387                 _ = m.next();
   6388                 const inner = (try parseCTypeName(c, m, scope, false)).?;
   6389                 try m.skip(c, .r_paren);
   6390                 break :blk inner;
   6391             } else try parseCUnaryExpr(c, m, scope);
   6392 
   6393             return Tag.helpers_sizeof.create(c.arena, operand);
   6394         },
   6395         .keyword_alignof => {
   6396             // TODO this won't work if using <stdalign.h>'s
   6397             // #define alignof _Alignof
   6398             try m.skip(c, .l_paren);
   6399             const operand = (try parseCTypeName(c, m, scope, false)).?;
   6400             try m.skip(c, .r_paren);
   6401 
   6402             return Tag.alignof.create(c.arena, operand);
   6403         },
   6404         .plus_plus, .minus_minus => {
   6405             try m.fail(c, "TODO unary inc/dec expr", .{});
   6406             return error.ParseError;
   6407         },
   6408         else => {
   6409             m.i -= 1;
   6410             return try parseCPostfixExpr(c, m, scope, null);
   6411         },
   6412     }
   6413 }
   6414 
   6415 fn getContainer(c: *Context, node: Node) ?Node {
   6416     switch (node.tag()) {
   6417         .@"union",
   6418         .@"struct",
   6419         .address_of,
   6420         .bit_not,
   6421         .not,
   6422         .optional_type,
   6423         .negate,
   6424         .negate_wrap,
   6425         .array_type,
   6426         .c_pointer,
   6427         .single_pointer,
   6428         => return node,
   6429 
   6430         .identifier => {
   6431             const ident = node.castTag(.identifier).?;
   6432             if (c.global_scope.sym_table.get(ident.data)) |value| {
   6433                 if (value.castTag(.var_decl)) |var_decl|
   6434                     return getContainer(c, var_decl.data.init.?);
   6435                 if (value.castTag(.var_simple) orelse value.castTag(.pub_var_simple)) |var_decl|
   6436                     return getContainer(c, var_decl.data.init);
   6437             }
   6438         },
   6439 
   6440         .field_access => {
   6441             const field_access = node.castTag(.field_access).?;
   6442 
   6443             if (getContainerTypeOf(c, field_access.data.lhs)) |ty_node| {
   6444                 if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| {
   6445                     for (container.data.fields) |field| {
   6446                         if (mem.eql(u8, field.name, field_access.data.field_name)) {
   6447                             return getContainer(c, field.type);
   6448                         }
   6449                     }
   6450                 }
   6451             }
   6452         },
   6453 
   6454         else => {},
   6455     }
   6456     return null;
   6457 }
   6458 
   6459 fn getContainerTypeOf(c: *Context, ref: Node) ?Node {
   6460     if (ref.castTag(.identifier)) |ident| {
   6461         if (c.global_scope.sym_table.get(ident.data)) |value| {
   6462             if (value.castTag(.var_decl)) |var_decl| {
   6463                 return getContainer(c, var_decl.data.type);
   6464             }
   6465         }
   6466     } else if (ref.castTag(.field_access)) |field_access| {
   6467         if (getContainerTypeOf(c, field_access.data.lhs)) |ty_node| {
   6468             if (ty_node.castTag(.@"struct") orelse ty_node.castTag(.@"union")) |container| {
   6469                 for (container.data.fields) |field| {
   6470                     if (mem.eql(u8, field.name, field_access.data.field_name)) {
   6471                         return getContainer(c, field.type);
   6472                     }
   6473                 }
   6474             } else return ty_node;
   6475         }
   6476     }
   6477     return null;
   6478 }
   6479 
   6480 fn getFnProto(c: *Context, ref: Node) ?*ast.Payload.Func {
   6481     const init = if (ref.castTag(.var_decl)) |v|
   6482         v.data.init orelse return null
   6483     else if (ref.castTag(.var_simple) orelse ref.castTag(.pub_var_simple)) |v|
   6484         v.data.init
   6485     else
   6486         return null;
   6487     if (getContainerTypeOf(c, init)) |ty_node| {
   6488         if (ty_node.castTag(.optional_type)) |prefix| {
   6489             if (prefix.data.castTag(.single_pointer)) |sp| {
   6490                 if (sp.data.elem_type.castTag(.func)) |fn_proto| {
   6491                     return fn_proto;
   6492                 }
   6493             }
   6494         }
   6495     }
   6496     return null;
   6497 }
   6498 
   6499 fn addMacros(c: *Context) !void {
   6500     var it = c.global_scope.macro_table.iterator();
   6501     while (it.next()) |entry| {
   6502         if (getFnProto(c, entry.value_ptr.*)) |proto_node| {
   6503             // If a macro aliases a global variable which is a function pointer, we conclude that
   6504             // the macro is intended to represent a function that assumes the function pointer
   6505             // variable is non-null and calls it.
   6506             try addTopLevelDecl(c, entry.key_ptr.*, try transCreateNodeMacroFn(c, entry.key_ptr.*, entry.value_ptr.*, proto_node));
   6507         } else {
   6508             try addTopLevelDecl(c, entry.key_ptr.*, entry.value_ptr.*);
   6509         }
   6510     }
   6511 }