zig

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

blob fcec69ed (566247B) - Raw


      1 //! Ingests an AST and produces ZIR code.
      2 const AstGen = @This();
      3 
      4 const std = @import("std");
      5 const Ast = std.zig.Ast;
      6 const mem = std.mem;
      7 const Allocator = std.mem.Allocator;
      8 const assert = std.debug.assert;
      9 const ArrayListUnmanaged = std.ArrayListUnmanaged;
     10 const StringIndexAdapter = std.hash_map.StringIndexAdapter;
     11 const StringIndexContext = std.hash_map.StringIndexContext;
     12 
     13 const isPrimitive = std.zig.primitives.isPrimitive;
     14 
     15 const Zir = std.zig.Zir;
     16 const BuiltinFn = std.zig.BuiltinFn;
     17 const AstRlAnnotate = std.zig.AstRlAnnotate;
     18 
     19 gpa: Allocator,
     20 tree: *const Ast,
     21 /// The set of nodes which, given the choice, must expose a result pointer to
     22 /// sub-expressions. See `AstRlAnnotate` for details.
     23 nodes_need_rl: *const AstRlAnnotate.RlNeededSet,
     24 instructions: std.MultiArrayList(Zir.Inst) = .{},
     25 extra: ArrayListUnmanaged(u32) = .empty,
     26 string_bytes: ArrayListUnmanaged(u8) = .empty,
     27 /// Tracks the current byte offset within the source file.
     28 /// Used to populate line deltas in the ZIR. AstGen maintains
     29 /// this "cursor" throughout the entire AST lowering process in order
     30 /// to avoid starting over the line/column scan for every declaration, which
     31 /// would be O(N^2).
     32 source_offset: u32 = 0,
     33 /// Tracks the corresponding line of `source_offset`.
     34 /// This value is absolute.
     35 source_line: u32 = 0,
     36 /// Tracks the corresponding column of `source_offset`.
     37 /// This value is absolute.
     38 source_column: u32 = 0,
     39 /// Used for temporary allocations; freed after AstGen is complete.
     40 /// The resulting ZIR code has no references to anything in this arena.
     41 arena: Allocator,
     42 string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .empty,
     43 compile_errors: ArrayListUnmanaged(Zir.Inst.CompileErrors.Item) = .empty,
     44 /// The topmost block of the current function.
     45 fn_block: ?*GenZir = null,
     46 fn_var_args: bool = false,
     47 /// Whether we are somewhere within a function. If `true`, any container decls may be
     48 /// generic and thus must be tunneled through closure.
     49 within_fn: bool = false,
     50 /// The return type of the current function. This may be a trivial `Ref`, or
     51 /// otherwise it refers to a `ret_type` instruction.
     52 fn_ret_ty: Zir.Inst.Ref = .none,
     53 /// Maps string table indexes to the first `@import` ZIR instruction
     54 /// that uses this string as the operand.
     55 imports: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, Ast.TokenIndex) = .empty,
     56 /// Used for temporary storage when building payloads.
     57 scratch: std.ArrayListUnmanaged(u32) = .empty,
     58 /// Whenever a `ref` instruction is needed, it is created and saved in this
     59 /// table instead of being immediately appended to the current block body.
     60 /// Then, when the instruction is being added to the parent block (typically from
     61 /// setBlockBody), if it has a ref_table entry, then the ref instruction is added
     62 /// there. This makes sure two properties are upheld:
     63 /// 1. All pointers to the same locals return the same address. This is required
     64 ///    to be compliant with the language specification.
     65 /// 2. `ref` instructions will dominate their uses. This is a required property
     66 ///    of ZIR.
     67 /// The key is the ref operand; the value is the ref instruction.
     68 ref_table: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .empty,
     69 /// Any information which should trigger invalidation of incremental compilation
     70 /// data should be used to update this hasher. The result is the final source
     71 /// hash of the enclosing declaration/etc.
     72 src_hasher: std.zig.SrcHasher,
     73 
     74 const InnerError = error{ OutOfMemory, AnalysisFail };
     75 
     76 fn addExtra(astgen: *AstGen, extra: anytype) Allocator.Error!u32 {
     77     const fields = std.meta.fields(@TypeOf(extra));
     78     try astgen.extra.ensureUnusedCapacity(astgen.gpa, fields.len);
     79     return addExtraAssumeCapacity(astgen, extra);
     80 }
     81 
     82 fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {
     83     const fields = std.meta.fields(@TypeOf(extra));
     84     const extra_index: u32 = @intCast(astgen.extra.items.len);
     85     astgen.extra.items.len += fields.len;
     86     setExtra(astgen, extra_index, extra);
     87     return extra_index;
     88 }
     89 
     90 fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void {
     91     const fields = std.meta.fields(@TypeOf(extra));
     92     var i = index;
     93     inline for (fields) |field| {
     94         astgen.extra.items[i] = switch (field.type) {
     95             u32 => @field(extra, field.name),
     96 
     97             Zir.Inst.Ref,
     98             Zir.Inst.Index,
     99             Zir.Inst.Declaration.Name,
    100             Zir.NullTerminatedString,
    101             => @intFromEnum(@field(extra, field.name)),
    102 
    103             i32,
    104             Zir.Inst.Call.Flags,
    105             Zir.Inst.BuiltinCall.Flags,
    106             Zir.Inst.SwitchBlock.Bits,
    107             Zir.Inst.SwitchBlockErrUnion.Bits,
    108             Zir.Inst.FuncFancy.Bits,
    109             Zir.Inst.Declaration.Flags,
    110             => @bitCast(@field(extra, field.name)),
    111 
    112             else => @compileError("bad field type"),
    113         };
    114         i += 1;
    115     }
    116 }
    117 
    118 fn reserveExtra(astgen: *AstGen, size: usize) Allocator.Error!u32 {
    119     const extra_index: u32 = @intCast(astgen.extra.items.len);
    120     try astgen.extra.resize(astgen.gpa, extra_index + size);
    121     return extra_index;
    122 }
    123 
    124 fn appendRefs(astgen: *AstGen, refs: []const Zir.Inst.Ref) !void {
    125     return astgen.extra.appendSlice(astgen.gpa, @ptrCast(refs));
    126 }
    127 
    128 fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const Zir.Inst.Ref) void {
    129     astgen.extra.appendSliceAssumeCapacity(@ptrCast(refs));
    130 }
    131 
    132 pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
    133     assert(tree.mode == .zig);
    134 
    135     var arena = std.heap.ArenaAllocator.init(gpa);
    136     defer arena.deinit();
    137 
    138     var nodes_need_rl = try AstRlAnnotate.annotate(gpa, arena.allocator(), tree);
    139     defer nodes_need_rl.deinit(gpa);
    140 
    141     var astgen: AstGen = .{
    142         .gpa = gpa,
    143         .arena = arena.allocator(),
    144         .tree = &tree,
    145         .nodes_need_rl = &nodes_need_rl,
    146         .src_hasher = undefined, // `structDeclInner` for the root struct will set this
    147     };
    148     defer astgen.deinit(gpa);
    149 
    150     // String table index 0 is reserved for `NullTerminatedString.empty`.
    151     try astgen.string_bytes.append(gpa, 0);
    152 
    153     // We expect at least as many ZIR instructions and extra data items
    154     // as AST nodes.
    155     try astgen.instructions.ensureTotalCapacity(gpa, tree.nodes.len);
    156 
    157     // First few indexes of extra are reserved and set at the end.
    158     const reserved_count = @typeInfo(Zir.ExtraIndex).@"enum".fields.len;
    159     try astgen.extra.ensureTotalCapacity(gpa, tree.nodes.len + reserved_count);
    160     astgen.extra.items.len += reserved_count;
    161 
    162     var top_scope: Scope.Top = .{};
    163 
    164     var gz_instructions: std.ArrayListUnmanaged(Zir.Inst.Index) = .empty;
    165     var gen_scope: GenZir = .{
    166         .is_comptime = true,
    167         .parent = &top_scope.base,
    168         .anon_name_strategy = .parent,
    169         .decl_node_index = 0,
    170         .decl_line = 0,
    171         .astgen = &astgen,
    172         .instructions = &gz_instructions,
    173         .instructions_top = 0,
    174     };
    175     defer gz_instructions.deinit(gpa);
    176 
    177     // The AST -> ZIR lowering process assumes an AST that does not have any parse errors.
    178     // Parse errors, or AstGen errors in the root struct, are considered "fatal", so we emit no ZIR.
    179     const fatal = if (tree.errors.len == 0) fatal: {
    180         if (AstGen.structDeclInner(
    181             &gen_scope,
    182             &gen_scope.base,
    183             0,
    184             tree.containerDeclRoot(),
    185             .auto,
    186             0,
    187         )) |struct_decl_ref| {
    188             assert(struct_decl_ref.toIndex().? == .main_struct_inst);
    189             break :fatal false;
    190         } else |err| switch (err) {
    191             error.OutOfMemory => return error.OutOfMemory,
    192             error.AnalysisFail => break :fatal true, // Handled via compile_errors below.
    193         }
    194     } else fatal: {
    195         try lowerAstErrors(&astgen);
    196         break :fatal true;
    197     };
    198 
    199     const err_index = @intFromEnum(Zir.ExtraIndex.compile_errors);
    200     if (astgen.compile_errors.items.len == 0) {
    201         astgen.extra.items[err_index] = 0;
    202     } else {
    203         try astgen.extra.ensureUnusedCapacity(gpa, 1 + astgen.compile_errors.items.len *
    204             @typeInfo(Zir.Inst.CompileErrors.Item).@"struct".fields.len);
    205 
    206         astgen.extra.items[err_index] = astgen.addExtraAssumeCapacity(Zir.Inst.CompileErrors{
    207             .items_len = @intCast(astgen.compile_errors.items.len),
    208         });
    209 
    210         for (astgen.compile_errors.items) |item| {
    211             _ = astgen.addExtraAssumeCapacity(item);
    212         }
    213     }
    214 
    215     const imports_index = @intFromEnum(Zir.ExtraIndex.imports);
    216     if (astgen.imports.count() == 0) {
    217         astgen.extra.items[imports_index] = 0;
    218     } else {
    219         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Imports).@"struct".fields.len +
    220             astgen.imports.count() * @typeInfo(Zir.Inst.Imports.Item).@"struct".fields.len);
    221 
    222         astgen.extra.items[imports_index] = astgen.addExtraAssumeCapacity(Zir.Inst.Imports{
    223             .imports_len = @intCast(astgen.imports.count()),
    224         });
    225 
    226         var it = astgen.imports.iterator();
    227         while (it.next()) |entry| {
    228             _ = astgen.addExtraAssumeCapacity(Zir.Inst.Imports.Item{
    229                 .name = entry.key_ptr.*,
    230                 .token = entry.value_ptr.*,
    231             });
    232         }
    233     }
    234 
    235     return .{
    236         .instructions = if (fatal) .empty else astgen.instructions.toOwnedSlice(),
    237         .string_bytes = try astgen.string_bytes.toOwnedSlice(gpa),
    238         .extra = try astgen.extra.toOwnedSlice(gpa),
    239     };
    240 }
    241 
    242 fn deinit(astgen: *AstGen, gpa: Allocator) void {
    243     astgen.instructions.deinit(gpa);
    244     astgen.extra.deinit(gpa);
    245     astgen.string_table.deinit(gpa);
    246     astgen.string_bytes.deinit(gpa);
    247     astgen.compile_errors.deinit(gpa);
    248     astgen.imports.deinit(gpa);
    249     astgen.scratch.deinit(gpa);
    250     astgen.ref_table.deinit(gpa);
    251 }
    252 
    253 const ResultInfo = struct {
    254     /// The semantics requested for the result location
    255     rl: Loc,
    256 
    257     /// The "operator" consuming the result location
    258     ctx: Context = .none,
    259 
    260     /// Turns a `coerced_ty` back into a `ty`. Should be called at branch points
    261     /// such as if and switch expressions.
    262     fn br(ri: ResultInfo) ResultInfo {
    263         return switch (ri.rl) {
    264             .coerced_ty => |ty| .{
    265                 .rl = .{ .ty = ty },
    266                 .ctx = ri.ctx,
    267             },
    268             else => ri,
    269         };
    270     }
    271 
    272     fn zirTag(ri: ResultInfo) Zir.Inst.Tag {
    273         switch (ri.rl) {
    274             .ty => return switch (ri.ctx) {
    275                 .shift_op => .as_shift_operand,
    276                 else => .as_node,
    277             },
    278             else => unreachable,
    279         }
    280     }
    281 
    282     const Loc = union(enum) {
    283         /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
    284         /// expression should be generated. The result instruction from the expression must
    285         /// be ignored.
    286         discard,
    287         /// The expression has an inferred type, and it will be evaluated as an rvalue.
    288         none,
    289         /// The expression will be coerced into this type, but it will be evaluated as an rvalue.
    290         ty: Zir.Inst.Ref,
    291         /// Same as `ty` but it is guaranteed that Sema will additionally perform the coercion,
    292         /// so no `as` instruction needs to be emitted.
    293         coerced_ty: Zir.Inst.Ref,
    294         /// The expression must generate a pointer rather than a value. For example, the left hand side
    295         /// of an assignment uses this kind of result location.
    296         ref,
    297         /// The expression must generate a pointer rather than a value, and the pointer will be coerced
    298         /// by other code to this type, which is guaranteed by earlier instructions to be a pointer type.
    299         ref_coerced_ty: Zir.Inst.Ref,
    300         /// The expression must store its result into this typed pointer. The result instruction
    301         /// from the expression must be ignored.
    302         ptr: PtrResultLoc,
    303         /// The expression must store its result into this allocation, which has an inferred type.
    304         /// The result instruction from the expression must be ignored.
    305         /// Always an instruction with tag `alloc_inferred`.
    306         inferred_ptr: Zir.Inst.Ref,
    307         /// The expression has a sequence of pointers to store its results into due to a destructure
    308         /// operation. Each of these pointers may or may not have an inferred type.
    309         destructure: struct {
    310             /// The AST node of the destructure operation itself.
    311             src_node: Ast.Node.Index,
    312             /// The pointers to store results into.
    313             components: []const DestructureComponent,
    314         },
    315 
    316         const DestructureComponent = union(enum) {
    317             typed_ptr: PtrResultLoc,
    318             inferred_ptr: Zir.Inst.Ref,
    319             discard,
    320         };
    321 
    322         const PtrResultLoc = struct {
    323             inst: Zir.Inst.Ref,
    324             src_node: ?Ast.Node.Index = null,
    325         };
    326 
    327         /// Find the result type for a cast builtin given the result location.
    328         /// If the location does not have a known result type, returns `null`.
    329         fn resultType(rl: Loc, gz: *GenZir, node: Ast.Node.Index) !?Zir.Inst.Ref {
    330             return switch (rl) {
    331                 .discard, .none, .ref, .inferred_ptr, .destructure => null,
    332                 .ty, .coerced_ty => |ty_ref| ty_ref,
    333                 .ref_coerced_ty => |ptr_ty| try gz.addUnNode(.elem_type, ptr_ty, node),
    334                 .ptr => |ptr| {
    335                     const ptr_ty = try gz.addUnNode(.typeof, ptr.inst, node);
    336                     return try gz.addUnNode(.elem_type, ptr_ty, node);
    337                 },
    338             };
    339         }
    340 
    341         /// Find the result type for a cast builtin given the result location.
    342         /// If the location does not have a known result type, emits an error on
    343         /// the given node.
    344         fn resultTypeForCast(rl: Loc, gz: *GenZir, node: Ast.Node.Index, builtin_name: []const u8) !Zir.Inst.Ref {
    345             const astgen = gz.astgen;
    346             if (try rl.resultType(gz, node)) |ty| return ty;
    347             switch (rl) {
    348                 .destructure => |destructure| return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{
    349                     try astgen.errNoteNode(destructure.src_node, "destructure expressions do not provide a single result type", .{}),
    350                     try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}),
    351                 }),
    352                 else => return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{
    353                     try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}),
    354                 }),
    355             }
    356         }
    357     };
    358 
    359     const Context = enum {
    360         /// The expression is the operand to a return expression.
    361         @"return",
    362         /// The expression is the input to an error-handling operator (if-else, try, or catch).
    363         error_handling_expr,
    364         /// The expression is the right-hand side of a shift operation.
    365         shift_op,
    366         /// The expression is an argument in a function call.
    367         fn_arg,
    368         /// The expression is the right-hand side of an initializer for a `const` variable
    369         const_init,
    370         /// The expression is the right-hand side of an assignment expression.
    371         assignment,
    372         /// No specific operator in particular.
    373         none,
    374     };
    375 };
    376 
    377 const coerced_align_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .u29_type } };
    378 const coerced_linksection_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .slice_const_u8_type } };
    379 const coerced_type_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .type_type } };
    380 const coerced_bool_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .bool_type } };
    381 
    382 fn typeExpr(gz: *GenZir, scope: *Scope, type_node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
    383     return comptimeExpr(gz, scope, coerced_type_ri, type_node);
    384 }
    385 
    386 fn reachableTypeExpr(
    387     gz: *GenZir,
    388     scope: *Scope,
    389     type_node: Ast.Node.Index,
    390     reachable_node: Ast.Node.Index,
    391 ) InnerError!Zir.Inst.Ref {
    392     return reachableExprComptime(gz, scope, coerced_type_ri, type_node, reachable_node, true);
    393 }
    394 
    395 /// Same as `expr` but fails with a compile error if the result type is `noreturn`.
    396 fn reachableExpr(
    397     gz: *GenZir,
    398     scope: *Scope,
    399     ri: ResultInfo,
    400     node: Ast.Node.Index,
    401     reachable_node: Ast.Node.Index,
    402 ) InnerError!Zir.Inst.Ref {
    403     return reachableExprComptime(gz, scope, ri, node, reachable_node, false);
    404 }
    405 
    406 fn reachableExprComptime(
    407     gz: *GenZir,
    408     scope: *Scope,
    409     ri: ResultInfo,
    410     node: Ast.Node.Index,
    411     reachable_node: Ast.Node.Index,
    412     force_comptime: bool,
    413 ) InnerError!Zir.Inst.Ref {
    414     const result_inst = if (force_comptime)
    415         try comptimeExpr(gz, scope, ri, node)
    416     else
    417         try expr(gz, scope, ri, node);
    418 
    419     if (gz.refIsNoReturn(result_inst)) {
    420         try gz.astgen.appendErrorNodeNotes(reachable_node, "unreachable code", .{}, &[_]u32{
    421             try gz.astgen.errNoteNode(node, "control flow is diverted here", .{}),
    422         });
    423     }
    424     return result_inst;
    425 }
    426 
    427 fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
    428     const astgen = gz.astgen;
    429     const tree = astgen.tree;
    430     const node_tags = tree.nodes.items(.tag);
    431     const main_tokens = tree.nodes.items(.main_token);
    432     switch (node_tags[node]) {
    433         .root => unreachable,
    434         .@"usingnamespace" => unreachable,
    435         .test_decl => unreachable,
    436         .global_var_decl => unreachable,
    437         .local_var_decl => unreachable,
    438         .simple_var_decl => unreachable,
    439         .aligned_var_decl => unreachable,
    440         .switch_case => unreachable,
    441         .switch_case_inline => unreachable,
    442         .switch_case_one => unreachable,
    443         .switch_case_inline_one => unreachable,
    444         .container_field_init => unreachable,
    445         .container_field_align => unreachable,
    446         .container_field => unreachable,
    447         .asm_output => unreachable,
    448         .asm_input => unreachable,
    449 
    450         .assign,
    451         .assign_destructure,
    452         .assign_bit_and,
    453         .assign_bit_or,
    454         .assign_shl,
    455         .assign_shl_sat,
    456         .assign_shr,
    457         .assign_bit_xor,
    458         .assign_div,
    459         .assign_sub,
    460         .assign_sub_wrap,
    461         .assign_sub_sat,
    462         .assign_mod,
    463         .assign_add,
    464         .assign_add_wrap,
    465         .assign_add_sat,
    466         .assign_mul,
    467         .assign_mul_wrap,
    468         .assign_mul_sat,
    469         .add,
    470         .add_wrap,
    471         .add_sat,
    472         .sub,
    473         .sub_wrap,
    474         .sub_sat,
    475         .mul,
    476         .mul_wrap,
    477         .mul_sat,
    478         .div,
    479         .mod,
    480         .bit_and,
    481         .bit_or,
    482         .shl,
    483         .shl_sat,
    484         .shr,
    485         .bit_xor,
    486         .bang_equal,
    487         .equal_equal,
    488         .greater_than,
    489         .greater_or_equal,
    490         .less_than,
    491         .less_or_equal,
    492         .array_cat,
    493         .array_mult,
    494         .bool_and,
    495         .bool_or,
    496         .@"asm",
    497         .asm_simple,
    498         .string_literal,
    499         .number_literal,
    500         .call,
    501         .call_comma,
    502         .async_call,
    503         .async_call_comma,
    504         .call_one,
    505         .call_one_comma,
    506         .async_call_one,
    507         .async_call_one_comma,
    508         .unreachable_literal,
    509         .@"return",
    510         .@"if",
    511         .if_simple,
    512         .@"while",
    513         .while_simple,
    514         .while_cont,
    515         .bool_not,
    516         .address_of,
    517         .optional_type,
    518         .block,
    519         .block_semicolon,
    520         .block_two,
    521         .block_two_semicolon,
    522         .@"break",
    523         .ptr_type_aligned,
    524         .ptr_type_sentinel,
    525         .ptr_type,
    526         .ptr_type_bit_range,
    527         .array_type,
    528         .array_type_sentinel,
    529         .enum_literal,
    530         .multiline_string_literal,
    531         .char_literal,
    532         .@"defer",
    533         .@"errdefer",
    534         .@"catch",
    535         .error_union,
    536         .merge_error_sets,
    537         .switch_range,
    538         .for_range,
    539         .@"await",
    540         .bit_not,
    541         .negation,
    542         .negation_wrap,
    543         .@"resume",
    544         .@"try",
    545         .slice,
    546         .slice_open,
    547         .slice_sentinel,
    548         .array_init_one,
    549         .array_init_one_comma,
    550         .array_init_dot_two,
    551         .array_init_dot_two_comma,
    552         .array_init_dot,
    553         .array_init_dot_comma,
    554         .array_init,
    555         .array_init_comma,
    556         .struct_init_one,
    557         .struct_init_one_comma,
    558         .struct_init_dot_two,
    559         .struct_init_dot_two_comma,
    560         .struct_init_dot,
    561         .struct_init_dot_comma,
    562         .struct_init,
    563         .struct_init_comma,
    564         .@"switch",
    565         .switch_comma,
    566         .@"for",
    567         .for_simple,
    568         .@"suspend",
    569         .@"continue",
    570         .fn_proto_simple,
    571         .fn_proto_multi,
    572         .fn_proto_one,
    573         .fn_proto,
    574         .fn_decl,
    575         .anyframe_type,
    576         .anyframe_literal,
    577         .error_set_decl,
    578         .container_decl,
    579         .container_decl_trailing,
    580         .container_decl_two,
    581         .container_decl_two_trailing,
    582         .container_decl_arg,
    583         .container_decl_arg_trailing,
    584         .tagged_union,
    585         .tagged_union_trailing,
    586         .tagged_union_two,
    587         .tagged_union_two_trailing,
    588         .tagged_union_enum_tag,
    589         .tagged_union_enum_tag_trailing,
    590         .@"comptime",
    591         .@"nosuspend",
    592         .error_value,
    593         => return astgen.failNode(node, "invalid left-hand side to assignment", .{}),
    594 
    595         .builtin_call,
    596         .builtin_call_comma,
    597         .builtin_call_two,
    598         .builtin_call_two_comma,
    599         => {
    600             const builtin_token = main_tokens[node];
    601             const builtin_name = tree.tokenSlice(builtin_token);
    602             // If the builtin is an invalid name, we don't cause an error here; instead
    603             // let it pass, and the error will be "invalid builtin function" later.
    604             if (BuiltinFn.list.get(builtin_name)) |info| {
    605                 if (!info.allows_lvalue) {
    606                     return astgen.failNode(node, "invalid left-hand side to assignment", .{});
    607                 }
    608             }
    609         },
    610 
    611         // These can be assigned to.
    612         .unwrap_optional,
    613         .deref,
    614         .field_access,
    615         .array_access,
    616         .identifier,
    617         .grouped_expression,
    618         .@"orelse",
    619         => {},
    620     }
    621     return expr(gz, scope, .{ .rl = .ref }, node);
    622 }
    623 
    624 /// Turn Zig AST into untyped ZIR instructions.
    625 /// When `rl` is discard, ptr, inferred_ptr, or inferred_ptr, the
    626 /// result instruction can be used to inspect whether it is isNoReturn() but that is it,
    627 /// it must otherwise not be used.
    628 fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
    629     const astgen = gz.astgen;
    630     const tree = astgen.tree;
    631     const main_tokens = tree.nodes.items(.main_token);
    632     const token_tags = tree.tokens.items(.tag);
    633     const node_datas = tree.nodes.items(.data);
    634     const node_tags = tree.nodes.items(.tag);
    635 
    636     const prev_anon_name_strategy = gz.anon_name_strategy;
    637     defer gz.anon_name_strategy = prev_anon_name_strategy;
    638     if (!nodeUsesAnonNameStrategy(tree, node)) {
    639         gz.anon_name_strategy = .anon;
    640     }
    641 
    642     switch (node_tags[node]) {
    643         .root => unreachable, // Top-level declaration.
    644         .@"usingnamespace" => unreachable, // Top-level declaration.
    645         .test_decl => unreachable, // Top-level declaration.
    646         .container_field_init => unreachable, // Top-level declaration.
    647         .container_field_align => unreachable, // Top-level declaration.
    648         .container_field => unreachable, // Top-level declaration.
    649         .fn_decl => unreachable, // Top-level declaration.
    650 
    651         .global_var_decl => unreachable, // Handled in `blockExpr`.
    652         .local_var_decl => unreachable, // Handled in `blockExpr`.
    653         .simple_var_decl => unreachable, // Handled in `blockExpr`.
    654         .aligned_var_decl => unreachable, // Handled in `blockExpr`.
    655         .@"defer" => unreachable, // Handled in `blockExpr`.
    656         .@"errdefer" => unreachable, // Handled in `blockExpr`.
    657 
    658         .switch_case => unreachable, // Handled in `switchExpr`.
    659         .switch_case_inline => unreachable, // Handled in `switchExpr`.
    660         .switch_case_one => unreachable, // Handled in `switchExpr`.
    661         .switch_case_inline_one => unreachable, // Handled in `switchExpr`.
    662         .switch_range => unreachable, // Handled in `switchExpr`.
    663 
    664         .asm_output => unreachable, // Handled in `asmExpr`.
    665         .asm_input => unreachable, // Handled in `asmExpr`.
    666 
    667         .for_range => unreachable, // Handled in `forExpr`.
    668 
    669         .assign => {
    670             try assign(gz, scope, node);
    671             return rvalue(gz, ri, .void_value, node);
    672         },
    673 
    674         .assign_destructure => {
    675             // Note that this variant does not declare any new var/const: that
    676             // variant is handled by `blockExprStmts`.
    677             try assignDestructure(gz, scope, node);
    678             return rvalue(gz, ri, .void_value, node);
    679         },
    680 
    681         .assign_shl => {
    682             try assignShift(gz, scope, node, .shl);
    683             return rvalue(gz, ri, .void_value, node);
    684         },
    685         .assign_shl_sat => {
    686             try assignShiftSat(gz, scope, node);
    687             return rvalue(gz, ri, .void_value, node);
    688         },
    689         .assign_shr => {
    690             try assignShift(gz, scope, node, .shr);
    691             return rvalue(gz, ri, .void_value, node);
    692         },
    693 
    694         .assign_bit_and => {
    695             try assignOp(gz, scope, node, .bit_and);
    696             return rvalue(gz, ri, .void_value, node);
    697         },
    698         .assign_bit_or => {
    699             try assignOp(gz, scope, node, .bit_or);
    700             return rvalue(gz, ri, .void_value, node);
    701         },
    702         .assign_bit_xor => {
    703             try assignOp(gz, scope, node, .xor);
    704             return rvalue(gz, ri, .void_value, node);
    705         },
    706         .assign_div => {
    707             try assignOp(gz, scope, node, .div);
    708             return rvalue(gz, ri, .void_value, node);
    709         },
    710         .assign_sub => {
    711             try assignOp(gz, scope, node, .sub);
    712             return rvalue(gz, ri, .void_value, node);
    713         },
    714         .assign_sub_wrap => {
    715             try assignOp(gz, scope, node, .subwrap);
    716             return rvalue(gz, ri, .void_value, node);
    717         },
    718         .assign_sub_sat => {
    719             try assignOp(gz, scope, node, .sub_sat);
    720             return rvalue(gz, ri, .void_value, node);
    721         },
    722         .assign_mod => {
    723             try assignOp(gz, scope, node, .mod_rem);
    724             return rvalue(gz, ri, .void_value, node);
    725         },
    726         .assign_add => {
    727             try assignOp(gz, scope, node, .add);
    728             return rvalue(gz, ri, .void_value, node);
    729         },
    730         .assign_add_wrap => {
    731             try assignOp(gz, scope, node, .addwrap);
    732             return rvalue(gz, ri, .void_value, node);
    733         },
    734         .assign_add_sat => {
    735             try assignOp(gz, scope, node, .add_sat);
    736             return rvalue(gz, ri, .void_value, node);
    737         },
    738         .assign_mul => {
    739             try assignOp(gz, scope, node, .mul);
    740             return rvalue(gz, ri, .void_value, node);
    741         },
    742         .assign_mul_wrap => {
    743             try assignOp(gz, scope, node, .mulwrap);
    744             return rvalue(gz, ri, .void_value, node);
    745         },
    746         .assign_mul_sat => {
    747             try assignOp(gz, scope, node, .mul_sat);
    748             return rvalue(gz, ri, .void_value, node);
    749         },
    750 
    751         // zig fmt: off
    752         .shl => return shiftOp(gz, scope, ri, node, node_datas[node].lhs, node_datas[node].rhs, .shl),
    753         .shr => return shiftOp(gz, scope, ri, node, node_datas[node].lhs, node_datas[node].rhs, .shr),
    754 
    755         .add      => return simpleBinOp(gz, scope, ri, node, .add),
    756         .add_wrap => return simpleBinOp(gz, scope, ri, node, .addwrap),
    757         .add_sat  => return simpleBinOp(gz, scope, ri, node, .add_sat),
    758         .sub      => return simpleBinOp(gz, scope, ri, node, .sub),
    759         .sub_wrap => return simpleBinOp(gz, scope, ri, node, .subwrap),
    760         .sub_sat  => return simpleBinOp(gz, scope, ri, node, .sub_sat),
    761         .mul      => return simpleBinOp(gz, scope, ri, node, .mul),
    762         .mul_wrap => return simpleBinOp(gz, scope, ri, node, .mulwrap),
    763         .mul_sat  => return simpleBinOp(gz, scope, ri, node, .mul_sat),
    764         .div      => return simpleBinOp(gz, scope, ri, node, .div),
    765         .mod      => return simpleBinOp(gz, scope, ri, node, .mod_rem),
    766         .shl_sat  => return simpleBinOp(gz, scope, ri, node, .shl_sat),
    767 
    768         .bit_and          => return simpleBinOp(gz, scope, ri, node, .bit_and),
    769         .bit_or           => return simpleBinOp(gz, scope, ri, node, .bit_or),
    770         .bit_xor          => return simpleBinOp(gz, scope, ri, node, .xor),
    771         .bang_equal       => return simpleBinOp(gz, scope, ri, node, .cmp_neq),
    772         .equal_equal      => return simpleBinOp(gz, scope, ri, node, .cmp_eq),
    773         .greater_than     => return simpleBinOp(gz, scope, ri, node, .cmp_gt),
    774         .greater_or_equal => return simpleBinOp(gz, scope, ri, node, .cmp_gte),
    775         .less_than        => return simpleBinOp(gz, scope, ri, node, .cmp_lt),
    776         .less_or_equal    => return simpleBinOp(gz, scope, ri, node, .cmp_lte),
    777         .array_cat        => return simpleBinOp(gz, scope, ri, node, .array_cat),
    778 
    779         .array_mult => {
    780             // This syntax form does not currently use the result type in the language specification.
    781             // However, the result type can be used to emit more optimal code for large multiplications by
    782             // having Sema perform a coercion before the multiplication operation.
    783             const result = try gz.addPlNode(.array_mul, node, Zir.Inst.ArrayMul{
    784                 .res_ty = if (try ri.rl.resultType(gz, node)) |t| t else .none,
    785                 .lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs),
    786                 .rhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs),
    787             });
    788             return rvalue(gz, ri, result, node);
    789         },
    790 
    791         .error_union      => return simpleBinOp(gz, scope, ri, node, .error_union_type),
    792         .merge_error_sets => return simpleBinOp(gz, scope, ri, node, .merge_error_sets),
    793 
    794         .bool_and => return boolBinOp(gz, scope, ri, node, .bool_br_and),
    795         .bool_or  => return boolBinOp(gz, scope, ri, node, .bool_br_or),
    796 
    797         .bool_not => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri, node_datas[node].lhs, .bool_not),
    798         .bit_not  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, node_datas[node].lhs, .bit_not),
    799 
    800         .negation      => return   negation(gz, scope, ri, node),
    801         .negation_wrap => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, node_datas[node].lhs, .negate_wrap),
    802 
    803         .identifier => return identifier(gz, scope, ri, node),
    804 
    805         .asm_simple,
    806         .@"asm",
    807         => return asmExpr(gz, scope, ri, node, tree.fullAsm(node).?),
    808 
    809         .string_literal           => return stringLiteral(gz, ri, node),
    810         .multiline_string_literal => return multilineStringLiteral(gz, ri, node),
    811 
    812         .number_literal => return numberLiteral(gz, ri, node, node, .positive),
    813         // zig fmt: on
    814 
    815         .builtin_call_two, .builtin_call_two_comma => {
    816             if (node_datas[node].lhs == 0) {
    817                 const params = [_]Ast.Node.Index{};
    818                 return builtinCall(gz, scope, ri, node, &params, false);
    819             } else if (node_datas[node].rhs == 0) {
    820                 const params = [_]Ast.Node.Index{node_datas[node].lhs};
    821                 return builtinCall(gz, scope, ri, node, &params, false);
    822             } else {
    823                 const params = [_]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
    824                 return builtinCall(gz, scope, ri, node, &params, false);
    825             }
    826         },
    827         .builtin_call, .builtin_call_comma => {
    828             const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
    829             return builtinCall(gz, scope, ri, node, params, false);
    830         },
    831 
    832         .call_one,
    833         .call_one_comma,
    834         .async_call_one,
    835         .async_call_one_comma,
    836         .call,
    837         .call_comma,
    838         .async_call,
    839         .async_call_comma,
    840         => {
    841             var buf: [1]Ast.Node.Index = undefined;
    842             return callExpr(gz, scope, ri, node, tree.fullCall(&buf, node).?);
    843         },
    844 
    845         .unreachable_literal => {
    846             try emitDbgNode(gz, node);
    847             _ = try gz.addAsIndex(.{
    848                 .tag = .@"unreachable",
    849                 .data = .{ .@"unreachable" = .{
    850                     .src_node = gz.nodeIndexToRelative(node),
    851                 } },
    852             });
    853             return Zir.Inst.Ref.unreachable_value;
    854         },
    855         .@"return" => return ret(gz, scope, node),
    856         .field_access => return fieldAccess(gz, scope, ri, node),
    857 
    858         .if_simple,
    859         .@"if",
    860         => {
    861             const if_full = tree.fullIf(node).?;
    862             no_switch_on_err: {
    863                 const error_token = if_full.error_token orelse break :no_switch_on_err;
    864                 const full_switch = tree.fullSwitch(if_full.ast.else_expr) orelse break :no_switch_on_err;
    865                 if (full_switch.label_token != null) break :no_switch_on_err;
    866                 if (node_tags[full_switch.ast.condition] != .identifier) break :no_switch_on_err;
    867                 if (!mem.eql(u8, tree.tokenSlice(error_token), tree.tokenSlice(main_tokens[full_switch.ast.condition]))) break :no_switch_on_err;
    868                 return switchExprErrUnion(gz, scope, ri.br(), node, .@"if");
    869             }
    870             return ifExpr(gz, scope, ri.br(), node, if_full);
    871         },
    872 
    873         .while_simple,
    874         .while_cont,
    875         .@"while",
    876         => return whileExpr(gz, scope, ri.br(), node, tree.fullWhile(node).?, false),
    877 
    878         .for_simple, .@"for" => return forExpr(gz, scope, ri.br(), node, tree.fullFor(node).?, false),
    879 
    880         .slice_open => {
    881             const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
    882 
    883             const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
    884             const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs);
    885             try emitDbgStmt(gz, cursor);
    886             const result = try gz.addPlNode(.slice_start, node, Zir.Inst.SliceStart{
    887                 .lhs = lhs,
    888                 .start = start,
    889             });
    890             return rvalue(gz, ri, result, node);
    891         },
    892         .slice => {
    893             const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);
    894             const lhs_node = node_datas[node].lhs;
    895             const lhs_tag = node_tags[lhs_node];
    896             const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel;
    897             const lhs_is_open_slice = lhs_tag == .slice_open or
    898                 (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0);
    899             if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) {
    900                 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs);
    901 
    902                 const start = if (lhs_is_slice_sentinel) start: {
    903                     const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel);
    904                     break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start);
    905                 } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs);
    906 
    907                 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
    908                 const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
    909                 try emitDbgStmt(gz, cursor);
    910                 const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{
    911                     .lhs = lhs,
    912                     .start = start,
    913                     .len = len,
    914                     .start_src_node_offset = gz.nodeIndexToRelative(lhs_node),
    915                     .sentinel = .none,
    916                 });
    917                 return rvalue(gz, ri, result, node);
    918             }
    919             const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
    920 
    921             const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
    922             const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
    923             const end = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end);
    924             try emitDbgStmt(gz, cursor);
    925             const result = try gz.addPlNode(.slice_end, node, Zir.Inst.SliceEnd{
    926                 .lhs = lhs,
    927                 .start = start,
    928                 .end = end,
    929             });
    930             return rvalue(gz, ri, result, node);
    931         },
    932         .slice_sentinel => {
    933             const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
    934             const lhs_node = node_datas[node].lhs;
    935             const lhs_tag = node_tags[lhs_node];
    936             const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel;
    937             const lhs_is_open_slice = lhs_tag == .slice_open or
    938                 (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0);
    939             if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) {
    940                 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs);
    941 
    942                 const start = if (lhs_is_slice_sentinel) start: {
    943                     const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel);
    944                     break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start);
    945                 } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs);
    946 
    947                 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
    948                 const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
    949                 const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);
    950                 try emitDbgStmt(gz, cursor);
    951                 const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{
    952                     .lhs = lhs,
    953                     .start = start,
    954                     .len = len,
    955                     .start_src_node_offset = gz.nodeIndexToRelative(lhs_node),
    956                     .sentinel = sentinel,
    957                 });
    958                 return rvalue(gz, ri, result, node);
    959             }
    960             const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
    961 
    962             const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
    963             const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
    964             const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
    965             const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);
    966             try emitDbgStmt(gz, cursor);
    967             const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{
    968                 .lhs = lhs,
    969                 .start = start,
    970                 .end = end,
    971                 .sentinel = sentinel,
    972             });
    973             return rvalue(gz, ri, result, node);
    974         },
    975 
    976         .deref => {
    977             const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs);
    978             _ = try gz.addUnNode(.validate_deref, lhs, node);
    979             switch (ri.rl) {
    980                 .ref, .ref_coerced_ty => return lhs,
    981                 else => {
    982                     const result = try gz.addUnNode(.load, lhs, node);
    983                     return rvalue(gz, ri, result, node);
    984                 },
    985             }
    986         },
    987         .address_of => {
    988             const operand_rl: ResultInfo.Loc = if (try ri.rl.resultType(gz, node)) |res_ty_inst| rl: {
    989                 _ = try gz.addUnTok(.validate_ref_ty, res_ty_inst, tree.firstToken(node));
    990                 break :rl .{ .ref_coerced_ty = res_ty_inst };
    991             } else .ref;
    992             const result = try expr(gz, scope, .{ .rl = operand_rl }, node_datas[node].lhs);
    993             return rvalue(gz, ri, result, node);
    994         },
    995         .optional_type => {
    996             const operand = try typeExpr(gz, scope, node_datas[node].lhs);
    997             const result = try gz.addUnNode(.optional_type, operand, node);
    998             return rvalue(gz, ri, result, node);
    999         },
   1000         .unwrap_optional => switch (ri.rl) {
   1001             .ref, .ref_coerced_ty => {
   1002                 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
   1003 
   1004                 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   1005                 try emitDbgStmt(gz, cursor);
   1006 
   1007                 return gz.addUnNode(.optional_payload_safe_ptr, lhs, node);
   1008             },
   1009             else => {
   1010                 const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs);
   1011 
   1012                 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   1013                 try emitDbgStmt(gz, cursor);
   1014 
   1015                 return rvalue(gz, ri, try gz.addUnNode(.optional_payload_safe, lhs, node), node);
   1016             },
   1017         },
   1018         .block_two, .block_two_semicolon => {
   1019             const statements = [2]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
   1020             if (node_datas[node].lhs == 0) {
   1021                 return blockExpr(gz, scope, ri, node, statements[0..0], .normal);
   1022             } else if (node_datas[node].rhs == 0) {
   1023                 return blockExpr(gz, scope, ri, node, statements[0..1], .normal);
   1024             } else {
   1025                 return blockExpr(gz, scope, ri, node, statements[0..2], .normal);
   1026             }
   1027         },
   1028         .block, .block_semicolon => {
   1029             const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
   1030             return blockExpr(gz, scope, ri, node, statements, .normal);
   1031         },
   1032         .enum_literal => if (try ri.rl.resultType(gz, node)) |res_ty| {
   1033             const str_index = try astgen.identAsString(main_tokens[node]);
   1034             const res = try gz.addPlNode(.decl_literal, node, Zir.Inst.Field{
   1035                 .lhs = res_ty,
   1036                 .field_name_start = str_index,
   1037             });
   1038             switch (ri.rl) {
   1039                 .discard, .none, .ref => unreachable, // no result type
   1040                 .ty, .coerced_ty => return res, // `decl_literal` does the coercion for us
   1041                 .ref_coerced_ty, .ptr, .inferred_ptr, .destructure => return rvalue(gz, ri, res, node),
   1042             }
   1043         } else return simpleStrTok(gz, ri, main_tokens[node], node, .enum_literal),
   1044         .error_value => return simpleStrTok(gz, ri, node_datas[node].rhs, node, .error_value),
   1045         // TODO restore this when implementing https://github.com/ziglang/zig/issues/6025
   1046         // .anyframe_literal => return rvalue(gz, ri, .anyframe_type, node),
   1047         .anyframe_literal => {
   1048             const result = try gz.addUnNode(.anyframe_type, .void_type, node);
   1049             return rvalue(gz, ri, result, node);
   1050         },
   1051         .anyframe_type => {
   1052             const return_type = try typeExpr(gz, scope, node_datas[node].rhs);
   1053             const result = try gz.addUnNode(.anyframe_type, return_type, node);
   1054             return rvalue(gz, ri, result, node);
   1055         },
   1056         .@"catch" => {
   1057             const catch_token = main_tokens[node];
   1058             const payload_token: ?Ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe)
   1059                 catch_token + 2
   1060             else
   1061                 null;
   1062             no_switch_on_err: {
   1063                 const capture_token = payload_token orelse break :no_switch_on_err;
   1064                 const full_switch = tree.fullSwitch(node_datas[node].rhs) orelse break :no_switch_on_err;
   1065                 if (full_switch.label_token != null) break :no_switch_on_err;
   1066                 if (node_tags[full_switch.ast.condition] != .identifier) break :no_switch_on_err;
   1067                 if (!mem.eql(u8, tree.tokenSlice(capture_token), tree.tokenSlice(main_tokens[full_switch.ast.condition]))) break :no_switch_on_err;
   1068                 return switchExprErrUnion(gz, scope, ri.br(), node, .@"catch");
   1069             }
   1070             switch (ri.rl) {
   1071                 .ref, .ref_coerced_ty => return orelseCatchExpr(
   1072                     gz,
   1073                     scope,
   1074                     ri,
   1075                     node,
   1076                     node_datas[node].lhs,
   1077                     .is_non_err_ptr,
   1078                     .err_union_payload_unsafe_ptr,
   1079                     .err_union_code_ptr,
   1080                     node_datas[node].rhs,
   1081                     payload_token,
   1082                 ),
   1083                 else => return orelseCatchExpr(
   1084                     gz,
   1085                     scope,
   1086                     ri,
   1087                     node,
   1088                     node_datas[node].lhs,
   1089                     .is_non_err,
   1090                     .err_union_payload_unsafe,
   1091                     .err_union_code,
   1092                     node_datas[node].rhs,
   1093                     payload_token,
   1094                 ),
   1095             }
   1096         },
   1097         .@"orelse" => switch (ri.rl) {
   1098             .ref, .ref_coerced_ty => return orelseCatchExpr(
   1099                 gz,
   1100                 scope,
   1101                 ri,
   1102                 node,
   1103                 node_datas[node].lhs,
   1104                 .is_non_null_ptr,
   1105                 .optional_payload_unsafe_ptr,
   1106                 undefined,
   1107                 node_datas[node].rhs,
   1108                 null,
   1109             ),
   1110             else => return orelseCatchExpr(
   1111                 gz,
   1112                 scope,
   1113                 ri,
   1114                 node,
   1115                 node_datas[node].lhs,
   1116                 .is_non_null,
   1117                 .optional_payload_unsafe,
   1118                 undefined,
   1119                 node_datas[node].rhs,
   1120                 null,
   1121             ),
   1122         },
   1123 
   1124         .ptr_type_aligned,
   1125         .ptr_type_sentinel,
   1126         .ptr_type,
   1127         .ptr_type_bit_range,
   1128         => return ptrType(gz, scope, ri, node, tree.fullPtrType(node).?),
   1129 
   1130         .container_decl,
   1131         .container_decl_trailing,
   1132         .container_decl_arg,
   1133         .container_decl_arg_trailing,
   1134         .container_decl_two,
   1135         .container_decl_two_trailing,
   1136         .tagged_union,
   1137         .tagged_union_trailing,
   1138         .tagged_union_enum_tag,
   1139         .tagged_union_enum_tag_trailing,
   1140         .tagged_union_two,
   1141         .tagged_union_two_trailing,
   1142         => {
   1143             var buf: [2]Ast.Node.Index = undefined;
   1144             return containerDecl(gz, scope, ri, node, tree.fullContainerDecl(&buf, node).?);
   1145         },
   1146 
   1147         .@"break" => return breakExpr(gz, scope, node),
   1148         .@"continue" => return continueExpr(gz, scope, node),
   1149         .grouped_expression => return expr(gz, scope, ri, node_datas[node].lhs),
   1150         .array_type => return arrayType(gz, scope, ri, node),
   1151         .array_type_sentinel => return arrayTypeSentinel(gz, scope, ri, node),
   1152         .char_literal => return charLiteral(gz, ri, node),
   1153         .error_set_decl => return errorSetDecl(gz, ri, node),
   1154         .array_access => return arrayAccess(gz, scope, ri, node),
   1155         .@"comptime" => return comptimeExprAst(gz, scope, ri, node),
   1156         .@"switch", .switch_comma => return switchExpr(gz, scope, ri.br(), node, tree.fullSwitch(node).?),
   1157 
   1158         .@"nosuspend" => return nosuspendExpr(gz, scope, ri, node),
   1159         .@"suspend" => return suspendExpr(gz, scope, node),
   1160         .@"await" => return awaitExpr(gz, scope, ri, node),
   1161         .@"resume" => return resumeExpr(gz, scope, ri, node),
   1162 
   1163         .@"try" => return tryExpr(gz, scope, ri, node, node_datas[node].lhs),
   1164 
   1165         .array_init_one,
   1166         .array_init_one_comma,
   1167         .array_init_dot_two,
   1168         .array_init_dot_two_comma,
   1169         .array_init_dot,
   1170         .array_init_dot_comma,
   1171         .array_init,
   1172         .array_init_comma,
   1173         => {
   1174             var buf: [2]Ast.Node.Index = undefined;
   1175             return arrayInitExpr(gz, scope, ri, node, tree.fullArrayInit(&buf, node).?);
   1176         },
   1177 
   1178         .struct_init_one,
   1179         .struct_init_one_comma,
   1180         .struct_init_dot_two,
   1181         .struct_init_dot_two_comma,
   1182         .struct_init_dot,
   1183         .struct_init_dot_comma,
   1184         .struct_init,
   1185         .struct_init_comma,
   1186         => {
   1187             var buf: [2]Ast.Node.Index = undefined;
   1188             return structInitExpr(gz, scope, ri, node, tree.fullStructInit(&buf, node).?);
   1189         },
   1190 
   1191         .fn_proto_simple,
   1192         .fn_proto_multi,
   1193         .fn_proto_one,
   1194         .fn_proto,
   1195         => {
   1196             var buf: [1]Ast.Node.Index = undefined;
   1197             return fnProtoExpr(gz, scope, ri, node, tree.fullFnProto(&buf, node).?);
   1198         },
   1199     }
   1200 }
   1201 
   1202 fn nosuspendExpr(
   1203     gz: *GenZir,
   1204     scope: *Scope,
   1205     ri: ResultInfo,
   1206     node: Ast.Node.Index,
   1207 ) InnerError!Zir.Inst.Ref {
   1208     const astgen = gz.astgen;
   1209     const tree = astgen.tree;
   1210     const node_datas = tree.nodes.items(.data);
   1211     const body_node = node_datas[node].lhs;
   1212     assert(body_node != 0);
   1213     if (gz.nosuspend_node != 0) {
   1214         try astgen.appendErrorNodeNotes(node, "redundant nosuspend block", .{}, &[_]u32{
   1215             try astgen.errNoteNode(gz.nosuspend_node, "other nosuspend block here", .{}),
   1216         });
   1217     }
   1218     gz.nosuspend_node = node;
   1219     defer gz.nosuspend_node = 0;
   1220     return expr(gz, scope, ri, body_node);
   1221 }
   1222 
   1223 fn suspendExpr(
   1224     gz: *GenZir,
   1225     scope: *Scope,
   1226     node: Ast.Node.Index,
   1227 ) InnerError!Zir.Inst.Ref {
   1228     const astgen = gz.astgen;
   1229     const gpa = astgen.gpa;
   1230     const tree = astgen.tree;
   1231     const node_datas = tree.nodes.items(.data);
   1232     const body_node = node_datas[node].lhs;
   1233 
   1234     if (gz.nosuspend_node != 0) {
   1235         return astgen.failNodeNotes(node, "suspend inside nosuspend block", .{}, &[_]u32{
   1236             try astgen.errNoteNode(gz.nosuspend_node, "nosuspend block here", .{}),
   1237         });
   1238     }
   1239     if (gz.suspend_node != 0) {
   1240         return astgen.failNodeNotes(node, "cannot suspend inside suspend block", .{}, &[_]u32{
   1241             try astgen.errNoteNode(gz.suspend_node, "other suspend block here", .{}),
   1242         });
   1243     }
   1244     assert(body_node != 0);
   1245 
   1246     const suspend_inst = try gz.makeBlockInst(.suspend_block, node);
   1247     try gz.instructions.append(gpa, suspend_inst);
   1248 
   1249     var suspend_scope = gz.makeSubBlock(scope);
   1250     suspend_scope.suspend_node = node;
   1251     defer suspend_scope.unstack();
   1252 
   1253     const body_result = try fullBodyExpr(&suspend_scope, &suspend_scope.base, .{ .rl = .none }, body_node, .normal);
   1254     if (!gz.refIsNoReturn(body_result)) {
   1255         _ = try suspend_scope.addBreak(.break_inline, suspend_inst, .void_value);
   1256     }
   1257     try suspend_scope.setBlockBody(suspend_inst);
   1258 
   1259     return suspend_inst.toRef();
   1260 }
   1261 
   1262 fn awaitExpr(
   1263     gz: *GenZir,
   1264     scope: *Scope,
   1265     ri: ResultInfo,
   1266     node: Ast.Node.Index,
   1267 ) InnerError!Zir.Inst.Ref {
   1268     const astgen = gz.astgen;
   1269     const tree = astgen.tree;
   1270     const node_datas = tree.nodes.items(.data);
   1271     const rhs_node = node_datas[node].lhs;
   1272 
   1273     if (gz.suspend_node != 0) {
   1274         return astgen.failNodeNotes(node, "cannot await inside suspend block", .{}, &[_]u32{
   1275             try astgen.errNoteNode(gz.suspend_node, "suspend block here", .{}),
   1276         });
   1277     }
   1278     const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node);
   1279     const result = if (gz.nosuspend_node != 0)
   1280         try gz.addExtendedPayload(.await_nosuspend, Zir.Inst.UnNode{
   1281             .node = gz.nodeIndexToRelative(node),
   1282             .operand = operand,
   1283         })
   1284     else
   1285         try gz.addUnNode(.@"await", operand, node);
   1286 
   1287     return rvalue(gz, ri, result, node);
   1288 }
   1289 
   1290 fn resumeExpr(
   1291     gz: *GenZir,
   1292     scope: *Scope,
   1293     ri: ResultInfo,
   1294     node: Ast.Node.Index,
   1295 ) InnerError!Zir.Inst.Ref {
   1296     const astgen = gz.astgen;
   1297     const tree = astgen.tree;
   1298     const node_datas = tree.nodes.items(.data);
   1299     const rhs_node = node_datas[node].lhs;
   1300     const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node);
   1301     const result = try gz.addUnNode(.@"resume", operand, node);
   1302     return rvalue(gz, ri, result, node);
   1303 }
   1304 
   1305 fn fnProtoExpr(
   1306     gz: *GenZir,
   1307     scope: *Scope,
   1308     ri: ResultInfo,
   1309     node: Ast.Node.Index,
   1310     fn_proto: Ast.full.FnProto,
   1311 ) InnerError!Zir.Inst.Ref {
   1312     const astgen = gz.astgen;
   1313     const tree = astgen.tree;
   1314     const token_tags = tree.tokens.items(.tag);
   1315 
   1316     if (fn_proto.name_token) |some| {
   1317         return astgen.failTok(some, "function type cannot have a name", .{});
   1318     }
   1319 
   1320     const is_extern = blk: {
   1321         const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false;
   1322         break :blk token_tags[maybe_extern_token] == .keyword_extern;
   1323     };
   1324     assert(!is_extern);
   1325 
   1326     var block_scope = gz.makeSubBlock(scope);
   1327     defer block_scope.unstack();
   1328 
   1329     const block_inst = try gz.makeBlockInst(.block_inline, node);
   1330 
   1331     var noalias_bits: u32 = 0;
   1332     const is_var_args = is_var_args: {
   1333         var param_type_i: usize = 0;
   1334         var it = fn_proto.iterate(tree);
   1335         while (it.next()) |param| : (param_type_i += 1) {
   1336             const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) {
   1337                 .keyword_noalias => is_comptime: {
   1338                     noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse
   1339                         return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
   1340                     break :is_comptime false;
   1341                 },
   1342                 .keyword_comptime => true,
   1343                 else => false,
   1344             } else false;
   1345 
   1346             const is_anytype = if (param.anytype_ellipsis3) |token| blk: {
   1347                 switch (token_tags[token]) {
   1348                     .keyword_anytype => break :blk true,
   1349                     .ellipsis3 => break :is_var_args true,
   1350                     else => unreachable,
   1351                 }
   1352             } else false;
   1353 
   1354             const param_name = if (param.name_token) |name_token| blk: {
   1355                 if (mem.eql(u8, "_", tree.tokenSlice(name_token)))
   1356                     break :blk .empty;
   1357 
   1358                 break :blk try astgen.identAsString(name_token);
   1359             } else .empty;
   1360 
   1361             if (is_anytype) {
   1362                 const name_token = param.name_token orelse param.anytype_ellipsis3.?;
   1363 
   1364                 const tag: Zir.Inst.Tag = if (is_comptime)
   1365                     .param_anytype_comptime
   1366                 else
   1367                     .param_anytype;
   1368                 _ = try block_scope.addStrTok(tag, param_name, name_token);
   1369             } else {
   1370                 const param_type_node = param.type_expr;
   1371                 assert(param_type_node != 0);
   1372                 var param_gz = block_scope.makeSubBlock(scope);
   1373                 defer param_gz.unstack();
   1374                 const param_type = try fullBodyExpr(&param_gz, scope, coerced_type_ri, param_type_node, .normal);
   1375                 const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1);
   1376                 _ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node);
   1377                 const main_tokens = tree.nodes.items(.main_token);
   1378                 const name_token = param.name_token orelse main_tokens[param_type_node];
   1379                 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param;
   1380                 // We pass `prev_param_insts` as `&.{}` here because a function prototype can't refer to previous
   1381                 // arguments (we haven't set up scopes here).
   1382                 const param_inst = try block_scope.addParam(&param_gz, &.{}, tag, name_token, param_name);
   1383                 assert(param_inst_expected == param_inst);
   1384             }
   1385         }
   1386         break :is_var_args false;
   1387     };
   1388 
   1389     if (fn_proto.ast.align_expr != 0) {
   1390         return astgen.failNode(fn_proto.ast.align_expr, "function type cannot have an alignment", .{});
   1391     }
   1392 
   1393     if (fn_proto.ast.addrspace_expr != 0) {
   1394         return astgen.failNode(fn_proto.ast.addrspace_expr, "function type cannot have an addrspace", .{});
   1395     }
   1396 
   1397     if (fn_proto.ast.section_expr != 0) {
   1398         return astgen.failNode(fn_proto.ast.section_expr, "function type cannot have a linksection", .{});
   1399     }
   1400 
   1401     const cc: Zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0)
   1402         try expr(
   1403             &block_scope,
   1404             scope,
   1405             .{ .rl = .{ .coerced_ty = try block_scope.addBuiltinValue(fn_proto.ast.callconv_expr, .calling_convention) } },
   1406             fn_proto.ast.callconv_expr,
   1407         )
   1408     else
   1409         Zir.Inst.Ref.none;
   1410 
   1411     const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
   1412     const is_inferred_error = token_tags[maybe_bang] == .bang;
   1413     if (is_inferred_error) {
   1414         return astgen.failTok(maybe_bang, "function type cannot have an inferred error set", .{});
   1415     }
   1416     const ret_ty = try expr(&block_scope, scope, coerced_type_ri, fn_proto.ast.return_type);
   1417 
   1418     const result = try block_scope.addFunc(.{
   1419         .src_node = fn_proto.ast.proto_node,
   1420 
   1421         .cc_ref = cc,
   1422         .cc_gz = null,
   1423         .ret_ref = ret_ty,
   1424         .ret_gz = null,
   1425 
   1426         .ret_param_refs = &.{},
   1427         .param_insts = &.{},
   1428 
   1429         .param_block = block_inst,
   1430         .body_gz = null,
   1431         .lib_name = .empty,
   1432         .is_var_args = is_var_args,
   1433         .is_inferred_error = false,
   1434         .is_test = false,
   1435         .is_extern = false,
   1436         .is_noinline = false,
   1437         .noalias_bits = noalias_bits,
   1438 
   1439         .proto_hash = undefined, // ignored for `body_gz == null`
   1440     });
   1441 
   1442     _ = try block_scope.addBreak(.break_inline, block_inst, result);
   1443     try block_scope.setBlockBody(block_inst);
   1444     try gz.instructions.append(astgen.gpa, block_inst);
   1445 
   1446     return rvalue(gz, ri, block_inst.toRef(), fn_proto.ast.proto_node);
   1447 }
   1448 
   1449 fn arrayInitExpr(
   1450     gz: *GenZir,
   1451     scope: *Scope,
   1452     ri: ResultInfo,
   1453     node: Ast.Node.Index,
   1454     array_init: Ast.full.ArrayInit,
   1455 ) InnerError!Zir.Inst.Ref {
   1456     const astgen = gz.astgen;
   1457     const tree = astgen.tree;
   1458     const node_tags = tree.nodes.items(.tag);
   1459     const main_tokens = tree.nodes.items(.main_token);
   1460 
   1461     assert(array_init.ast.elements.len != 0); // Otherwise it would be struct init.
   1462 
   1463     const array_ty: Zir.Inst.Ref, const elem_ty: Zir.Inst.Ref = inst: {
   1464         if (array_init.ast.type_expr == 0) break :inst .{ .none, .none };
   1465 
   1466         infer: {
   1467             const array_type: Ast.full.ArrayType = tree.fullArrayType(array_init.ast.type_expr) orelse break :infer;
   1468             // This intentionally does not support `@"_"` syntax.
   1469             if (node_tags[array_type.ast.elem_count] == .identifier and
   1470                 mem.eql(u8, tree.tokenSlice(main_tokens[array_type.ast.elem_count]), "_"))
   1471             {
   1472                 const len_inst = try gz.addInt(array_init.ast.elements.len);
   1473                 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type);
   1474                 if (array_type.ast.sentinel == 0) {
   1475                     const array_type_inst = try gz.addPlNode(.array_type, array_init.ast.type_expr, Zir.Inst.Bin{
   1476                         .lhs = len_inst,
   1477                         .rhs = elem_type,
   1478                     });
   1479                     break :inst .{ array_type_inst, elem_type };
   1480                 } else {
   1481                     const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, array_type.ast.sentinel);
   1482                     const array_type_inst = try gz.addPlNode(
   1483                         .array_type_sentinel,
   1484                         array_init.ast.type_expr,
   1485                         Zir.Inst.ArrayTypeSentinel{
   1486                             .len = len_inst,
   1487                             .elem_type = elem_type,
   1488                             .sentinel = sentinel,
   1489                         },
   1490                     );
   1491                     break :inst .{ array_type_inst, elem_type };
   1492                 }
   1493             }
   1494         }
   1495         const array_type_inst = try typeExpr(gz, scope, array_init.ast.type_expr);
   1496         _ = try gz.addPlNode(.validate_array_init_ty, node, Zir.Inst.ArrayInit{
   1497             .ty = array_type_inst,
   1498             .init_count = @intCast(array_init.ast.elements.len),
   1499         });
   1500         break :inst .{ array_type_inst, .none };
   1501     };
   1502 
   1503     if (array_ty != .none) {
   1504         // Typed inits do not use RLS for language simplicity.
   1505         switch (ri.rl) {
   1506             .discard => {
   1507                 if (elem_ty != .none) {
   1508                     const elem_ri: ResultInfo = .{ .rl = .{ .ty = elem_ty } };
   1509                     for (array_init.ast.elements) |elem_init| {
   1510                         _ = try expr(gz, scope, elem_ri, elem_init);
   1511                     }
   1512                 } else {
   1513                     for (array_init.ast.elements, 0..) |elem_init, i| {
   1514                         const this_elem_ty = try gz.add(.{
   1515                             .tag = .array_init_elem_type,
   1516                             .data = .{ .bin = .{
   1517                                 .lhs = array_ty,
   1518                                 .rhs = @enumFromInt(i),
   1519                             } },
   1520                         });
   1521                         _ = try expr(gz, scope, .{ .rl = .{ .ty = this_elem_ty } }, elem_init);
   1522                     }
   1523                 }
   1524                 return .void_value;
   1525             },
   1526             .ref => return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, true),
   1527             else => {
   1528                 const array_inst = try arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, false);
   1529                 return rvalue(gz, ri, array_inst, node);
   1530             },
   1531         }
   1532     }
   1533 
   1534     switch (ri.rl) {
   1535         .none => return arrayInitExprAnon(gz, scope, node, array_init.ast.elements),
   1536         .discard => {
   1537             for (array_init.ast.elements) |elem_init| {
   1538                 _ = try expr(gz, scope, .{ .rl = .discard }, elem_init);
   1539             }
   1540             return Zir.Inst.Ref.void_value;
   1541         },
   1542         .ref => {
   1543             const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements);
   1544             return gz.addUnTok(.ref, result, tree.firstToken(node));
   1545         },
   1546         .ref_coerced_ty => |ptr_ty_inst| {
   1547             const dest_arr_ty_inst = try gz.addPlNode(.validate_array_init_ref_ty, node, Zir.Inst.ArrayInitRefTy{
   1548                 .ptr_ty = ptr_ty_inst,
   1549                 .elem_count = @intCast(array_init.ast.elements.len),
   1550             });
   1551             return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, dest_arr_ty_inst, .none, true);
   1552         },
   1553         .ty, .coerced_ty => |result_ty_inst| {
   1554             _ = try gz.addPlNode(.validate_array_init_result_ty, node, Zir.Inst.ArrayInit{
   1555                 .ty = result_ty_inst,
   1556                 .init_count = @intCast(array_init.ast.elements.len),
   1557             });
   1558             return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, result_ty_inst, .none, false);
   1559         },
   1560         .ptr => |ptr| {
   1561             try arrayInitExprPtr(gz, scope, node, array_init.ast.elements, ptr.inst);
   1562             return .void_value;
   1563         },
   1564         .inferred_ptr => {
   1565             // We can't get elem pointers of an untyped inferred alloc, so must perform a
   1566             // standard anonymous initialization followed by an rvalue store.
   1567             // See corresponding logic in structInitExpr.
   1568             const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements);
   1569             return rvalue(gz, ri, result, node);
   1570         },
   1571         .destructure => |destructure| {
   1572             // Untyped init - destructure directly into result pointers
   1573             if (array_init.ast.elements.len != destructure.components.len) {
   1574                 return astgen.failNodeNotes(node, "expected {} elements for destructure, found {}", .{
   1575                     destructure.components.len,
   1576                     array_init.ast.elements.len,
   1577                 }, &.{
   1578                     try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}),
   1579                 });
   1580             }
   1581             for (array_init.ast.elements, destructure.components) |elem_init, ds_comp| {
   1582                 const elem_ri: ResultInfo = .{ .rl = switch (ds_comp) {
   1583                     .typed_ptr => |ptr_rl| .{ .ptr = ptr_rl },
   1584                     .inferred_ptr => |ptr_inst| .{ .inferred_ptr = ptr_inst },
   1585                     .discard => .discard,
   1586                 } };
   1587                 _ = try expr(gz, scope, elem_ri, elem_init);
   1588             }
   1589             return .void_value;
   1590         },
   1591     }
   1592 }
   1593 
   1594 /// An array initialization expression using an `array_init_anon` instruction.
   1595 fn arrayInitExprAnon(
   1596     gz: *GenZir,
   1597     scope: *Scope,
   1598     node: Ast.Node.Index,
   1599     elements: []const Ast.Node.Index,
   1600 ) InnerError!Zir.Inst.Ref {
   1601     const astgen = gz.astgen;
   1602 
   1603     const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{
   1604         .operands_len = @intCast(elements.len),
   1605     });
   1606     var extra_index = try reserveExtra(astgen, elements.len);
   1607 
   1608     for (elements) |elem_init| {
   1609         const elem_ref = try expr(gz, scope, .{ .rl = .none }, elem_init);
   1610         astgen.extra.items[extra_index] = @intFromEnum(elem_ref);
   1611         extra_index += 1;
   1612     }
   1613     return try gz.addPlNodePayloadIndex(.array_init_anon, node, payload_index);
   1614 }
   1615 
   1616 /// An array initialization expression using an `array_init` or `array_init_ref` instruction.
   1617 fn arrayInitExprTyped(
   1618     gz: *GenZir,
   1619     scope: *Scope,
   1620     node: Ast.Node.Index,
   1621     elements: []const Ast.Node.Index,
   1622     ty_inst: Zir.Inst.Ref,
   1623     maybe_elem_ty_inst: Zir.Inst.Ref,
   1624     is_ref: bool,
   1625 ) InnerError!Zir.Inst.Ref {
   1626     const astgen = gz.astgen;
   1627 
   1628     const len = elements.len + 1; // +1 for type
   1629     const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{
   1630         .operands_len = @intCast(len),
   1631     });
   1632     var extra_index = try reserveExtra(astgen, len);
   1633     astgen.extra.items[extra_index] = @intFromEnum(ty_inst);
   1634     extra_index += 1;
   1635 
   1636     if (maybe_elem_ty_inst != .none) {
   1637         const elem_ri: ResultInfo = .{ .rl = .{ .coerced_ty = maybe_elem_ty_inst } };
   1638         for (elements) |elem_init| {
   1639             const elem_inst = try expr(gz, scope, elem_ri, elem_init);
   1640             astgen.extra.items[extra_index] = @intFromEnum(elem_inst);
   1641             extra_index += 1;
   1642         }
   1643     } else {
   1644         for (elements, 0..) |elem_init, i| {
   1645             const ri: ResultInfo = .{ .rl = .{ .coerced_ty = try gz.add(.{
   1646                 .tag = .array_init_elem_type,
   1647                 .data = .{ .bin = .{
   1648                     .lhs = ty_inst,
   1649                     .rhs = @enumFromInt(i),
   1650                 } },
   1651             }) } };
   1652 
   1653             const elem_inst = try expr(gz, scope, ri, elem_init);
   1654             astgen.extra.items[extra_index] = @intFromEnum(elem_inst);
   1655             extra_index += 1;
   1656         }
   1657     }
   1658 
   1659     const tag: Zir.Inst.Tag = if (is_ref) .array_init_ref else .array_init;
   1660     return try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1661 }
   1662 
   1663 /// An array initialization expression using element pointers.
   1664 fn arrayInitExprPtr(
   1665     gz: *GenZir,
   1666     scope: *Scope,
   1667     node: Ast.Node.Index,
   1668     elements: []const Ast.Node.Index,
   1669     ptr_inst: Zir.Inst.Ref,
   1670 ) InnerError!void {
   1671     const astgen = gz.astgen;
   1672 
   1673     const array_ptr_inst = try gz.addUnNode(.opt_eu_base_ptr_init, ptr_inst, node);
   1674 
   1675     const payload_index = try addExtra(astgen, Zir.Inst.Block{
   1676         .body_len = @intCast(elements.len),
   1677     });
   1678     var extra_index = try reserveExtra(astgen, elements.len);
   1679 
   1680     for (elements, 0..) |elem_init, i| {
   1681         const elem_ptr_inst = try gz.addPlNode(.array_init_elem_ptr, elem_init, Zir.Inst.ElemPtrImm{
   1682             .ptr = array_ptr_inst,
   1683             .index = @intCast(i),
   1684         });
   1685         astgen.extra.items[extra_index] = @intFromEnum(elem_ptr_inst.toIndex().?);
   1686         extra_index += 1;
   1687         _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = elem_ptr_inst } } }, elem_init);
   1688     }
   1689 
   1690     _ = try gz.addPlNodePayloadIndex(.validate_ptr_array_init, node, payload_index);
   1691 }
   1692 
   1693 fn structInitExpr(
   1694     gz: *GenZir,
   1695     scope: *Scope,
   1696     ri: ResultInfo,
   1697     node: Ast.Node.Index,
   1698     struct_init: Ast.full.StructInit,
   1699 ) InnerError!Zir.Inst.Ref {
   1700     const astgen = gz.astgen;
   1701     const tree = astgen.tree;
   1702 
   1703     if (struct_init.ast.type_expr == 0) {
   1704         if (struct_init.ast.fields.len == 0) {
   1705             // Anonymous init with no fields.
   1706             switch (ri.rl) {
   1707                 .discard => return .void_value,
   1708                 .ref_coerced_ty => |ptr_ty_inst| return gz.addUnNode(.struct_init_empty_ref_result, ptr_ty_inst, node),
   1709                 .ty, .coerced_ty => |ty_inst| return gz.addUnNode(.struct_init_empty_result, ty_inst, node),
   1710                 .ptr => {
   1711                     // TODO: should we modify this to use RLS for the field stores here?
   1712                     const ty_inst = (try ri.rl.resultType(gz, node)).?;
   1713                     const val = try gz.addUnNode(.struct_init_empty_result, ty_inst, node);
   1714                     return rvalue(gz, ri, val, node);
   1715                 },
   1716                 .none, .ref, .inferred_ptr => {
   1717                     return rvalue(gz, ri, .empty_tuple, node);
   1718                 },
   1719                 .destructure => |destructure| {
   1720                     return astgen.failNodeNotes(node, "empty initializer cannot be destructured", .{}, &.{
   1721                         try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}),
   1722                     });
   1723                 },
   1724             }
   1725         }
   1726     } else array: {
   1727         const node_tags = tree.nodes.items(.tag);
   1728         const main_tokens = tree.nodes.items(.main_token);
   1729         const array_type: Ast.full.ArrayType = tree.fullArrayType(struct_init.ast.type_expr) orelse {
   1730             if (struct_init.ast.fields.len == 0) {
   1731                 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1732                 const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
   1733                 return rvalue(gz, ri, result, node);
   1734             }
   1735             break :array;
   1736         };
   1737         const is_inferred_array_len = node_tags[array_type.ast.elem_count] == .identifier and
   1738             // This intentionally does not support `@"_"` syntax.
   1739             mem.eql(u8, tree.tokenSlice(main_tokens[array_type.ast.elem_count]), "_");
   1740         if (struct_init.ast.fields.len == 0) {
   1741             if (is_inferred_array_len) {
   1742                 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type);
   1743                 const array_type_inst = if (array_type.ast.sentinel == 0) blk: {
   1744                     break :blk try gz.addPlNode(.array_type, struct_init.ast.type_expr, Zir.Inst.Bin{
   1745                         .lhs = .zero_usize,
   1746                         .rhs = elem_type,
   1747                     });
   1748                 } else blk: {
   1749                     const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, array_type.ast.sentinel);
   1750                     break :blk try gz.addPlNode(
   1751                         .array_type_sentinel,
   1752                         struct_init.ast.type_expr,
   1753                         Zir.Inst.ArrayTypeSentinel{
   1754                             .len = .zero_usize,
   1755                             .elem_type = elem_type,
   1756                             .sentinel = sentinel,
   1757                         },
   1758                     );
   1759                 };
   1760                 const result = try gz.addUnNode(.struct_init_empty, array_type_inst, node);
   1761                 return rvalue(gz, ri, result, node);
   1762             }
   1763             const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1764             const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
   1765             return rvalue(gz, ri, result, node);
   1766         } else {
   1767             return astgen.failNode(
   1768                 struct_init.ast.type_expr,
   1769                 "initializing array with struct syntax",
   1770                 .{},
   1771             );
   1772         }
   1773     }
   1774 
   1775     {
   1776         var sfba = std.heap.stackFallback(256, astgen.arena);
   1777         const sfba_allocator = sfba.get();
   1778 
   1779         var duplicate_names = std.AutoArrayHashMap(Zir.NullTerminatedString, ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
   1780         try duplicate_names.ensureTotalCapacity(@intCast(struct_init.ast.fields.len));
   1781 
   1782         // When there aren't errors, use this to avoid a second iteration.
   1783         var any_duplicate = false;
   1784 
   1785         for (struct_init.ast.fields) |field| {
   1786             const name_token = tree.firstToken(field) - 2;
   1787             const name_index = try astgen.identAsString(name_token);
   1788 
   1789             const gop = try duplicate_names.getOrPut(name_index);
   1790 
   1791             if (gop.found_existing) {
   1792                 try gop.value_ptr.append(sfba_allocator, name_token);
   1793                 any_duplicate = true;
   1794             } else {
   1795                 gop.value_ptr.* = .{};
   1796                 try gop.value_ptr.append(sfba_allocator, name_token);
   1797             }
   1798         }
   1799 
   1800         if (any_duplicate) {
   1801             var it = duplicate_names.iterator();
   1802 
   1803             while (it.next()) |entry| {
   1804                 const record = entry.value_ptr.*;
   1805                 if (record.items.len > 1) {
   1806                     var error_notes = std.ArrayList(u32).init(astgen.arena);
   1807 
   1808                     for (record.items[1..]) |duplicate| {
   1809                         try error_notes.append(try astgen.errNoteTok(duplicate, "duplicate name here", .{}));
   1810                     }
   1811 
   1812                     try error_notes.append(try astgen.errNoteNode(node, "struct declared here", .{}));
   1813 
   1814                     try astgen.appendErrorTokNotes(
   1815                         record.items[0],
   1816                         "duplicate struct field name",
   1817                         .{},
   1818                         error_notes.items,
   1819                     );
   1820                 }
   1821             }
   1822 
   1823             return error.AnalysisFail;
   1824         }
   1825     }
   1826 
   1827     if (struct_init.ast.type_expr != 0) {
   1828         // Typed inits do not use RLS for language simplicity.
   1829         const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1830         _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
   1831         switch (ri.rl) {
   1832             .ref => return structInitExprTyped(gz, scope, node, struct_init, ty_inst, true),
   1833             else => {
   1834                 const struct_inst = try structInitExprTyped(gz, scope, node, struct_init, ty_inst, false);
   1835                 return rvalue(gz, ri, struct_inst, node);
   1836             },
   1837         }
   1838     }
   1839 
   1840     switch (ri.rl) {
   1841         .none => return structInitExprAnon(gz, scope, node, struct_init),
   1842         .discard => {
   1843             // Even if discarding we must perform side-effects.
   1844             for (struct_init.ast.fields) |field_init| {
   1845                 _ = try expr(gz, scope, .{ .rl = .discard }, field_init);
   1846             }
   1847             return .void_value;
   1848         },
   1849         .ref => {
   1850             const result = try structInitExprAnon(gz, scope, node, struct_init);
   1851             return gz.addUnTok(.ref, result, tree.firstToken(node));
   1852         },
   1853         .ref_coerced_ty => |ptr_ty_inst| {
   1854             const result_ty_inst = try gz.addUnNode(.elem_type, ptr_ty_inst, node);
   1855             _ = try gz.addUnNode(.validate_struct_init_result_ty, result_ty_inst, node);
   1856             return structInitExprTyped(gz, scope, node, struct_init, result_ty_inst, true);
   1857         },
   1858         .ty, .coerced_ty => |result_ty_inst| {
   1859             _ = try gz.addUnNode(.validate_struct_init_result_ty, result_ty_inst, node);
   1860             return structInitExprTyped(gz, scope, node, struct_init, result_ty_inst, false);
   1861         },
   1862         .ptr => |ptr| {
   1863             try structInitExprPtr(gz, scope, node, struct_init, ptr.inst);
   1864             return .void_value;
   1865         },
   1866         .inferred_ptr => {
   1867             // We can't get field pointers of an untyped inferred alloc, so must perform a
   1868             // standard anonymous initialization followed by an rvalue store.
   1869             // See corresponding logic in arrayInitExpr.
   1870             const struct_inst = try structInitExprAnon(gz, scope, node, struct_init);
   1871             return rvalue(gz, ri, struct_inst, node);
   1872         },
   1873         .destructure => |destructure| {
   1874             // This is an untyped init, so is an actual struct, which does
   1875             // not support destructuring.
   1876             return astgen.failNodeNotes(node, "struct value cannot be destructured", .{}, &.{
   1877                 try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}),
   1878             });
   1879         },
   1880     }
   1881 }
   1882 
   1883 /// A struct initialization expression using a `struct_init_anon` instruction.
   1884 fn structInitExprAnon(
   1885     gz: *GenZir,
   1886     scope: *Scope,
   1887     node: Ast.Node.Index,
   1888     struct_init: Ast.full.StructInit,
   1889 ) InnerError!Zir.Inst.Ref {
   1890     const astgen = gz.astgen;
   1891     const tree = astgen.tree;
   1892 
   1893     const payload_index = try addExtra(astgen, Zir.Inst.StructInitAnon{
   1894         .abs_node = node,
   1895         .abs_line = astgen.source_line,
   1896         .fields_len = @intCast(struct_init.ast.fields.len),
   1897     });
   1898     const field_size = @typeInfo(Zir.Inst.StructInitAnon.Item).@"struct".fields.len;
   1899     var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size);
   1900 
   1901     for (struct_init.ast.fields) |field_init| {
   1902         const name_token = tree.firstToken(field_init) - 2;
   1903         const str_index = try astgen.identAsString(name_token);
   1904         setExtra(astgen, extra_index, Zir.Inst.StructInitAnon.Item{
   1905             .field_name = str_index,
   1906             .init = try expr(gz, scope, .{ .rl = .none }, field_init),
   1907         });
   1908         extra_index += field_size;
   1909     }
   1910 
   1911     return gz.addPlNodePayloadIndex(.struct_init_anon, node, payload_index);
   1912 }
   1913 
   1914 /// A struct initialization expression using a `struct_init` or `struct_init_ref` instruction.
   1915 fn structInitExprTyped(
   1916     gz: *GenZir,
   1917     scope: *Scope,
   1918     node: Ast.Node.Index,
   1919     struct_init: Ast.full.StructInit,
   1920     ty_inst: Zir.Inst.Ref,
   1921     is_ref: bool,
   1922 ) InnerError!Zir.Inst.Ref {
   1923     const astgen = gz.astgen;
   1924     const tree = astgen.tree;
   1925 
   1926     const payload_index = try addExtra(astgen, Zir.Inst.StructInit{
   1927         .abs_node = node,
   1928         .abs_line = astgen.source_line,
   1929         .fields_len = @intCast(struct_init.ast.fields.len),
   1930     });
   1931     const field_size = @typeInfo(Zir.Inst.StructInit.Item).@"struct".fields.len;
   1932     var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size);
   1933 
   1934     for (struct_init.ast.fields) |field_init| {
   1935         const name_token = tree.firstToken(field_init) - 2;
   1936         const str_index = try astgen.identAsString(name_token);
   1937         const field_ty_inst = try gz.addPlNode(.struct_init_field_type, field_init, Zir.Inst.FieldType{
   1938             .container_type = ty_inst,
   1939             .name_start = str_index,
   1940         });
   1941         setExtra(astgen, extra_index, Zir.Inst.StructInit.Item{
   1942             .field_type = field_ty_inst.toIndex().?,
   1943             .init = try expr(gz, scope, .{ .rl = .{ .coerced_ty = field_ty_inst } }, field_init),
   1944         });
   1945         extra_index += field_size;
   1946     }
   1947 
   1948     const tag: Zir.Inst.Tag = if (is_ref) .struct_init_ref else .struct_init;
   1949     return gz.addPlNodePayloadIndex(tag, node, payload_index);
   1950 }
   1951 
   1952 /// A struct initialization expression using field pointers.
   1953 fn structInitExprPtr(
   1954     gz: *GenZir,
   1955     scope: *Scope,
   1956     node: Ast.Node.Index,
   1957     struct_init: Ast.full.StructInit,
   1958     ptr_inst: Zir.Inst.Ref,
   1959 ) InnerError!void {
   1960     const astgen = gz.astgen;
   1961     const tree = astgen.tree;
   1962 
   1963     const struct_ptr_inst = try gz.addUnNode(.opt_eu_base_ptr_init, ptr_inst, node);
   1964 
   1965     const payload_index = try addExtra(astgen, Zir.Inst.Block{
   1966         .body_len = @intCast(struct_init.ast.fields.len),
   1967     });
   1968     var extra_index = try reserveExtra(astgen, struct_init.ast.fields.len);
   1969 
   1970     for (struct_init.ast.fields) |field_init| {
   1971         const name_token = tree.firstToken(field_init) - 2;
   1972         const str_index = try astgen.identAsString(name_token);
   1973         const field_ptr = try gz.addPlNode(.struct_init_field_ptr, field_init, Zir.Inst.Field{
   1974             .lhs = struct_ptr_inst,
   1975             .field_name_start = str_index,
   1976         });
   1977         astgen.extra.items[extra_index] = @intFromEnum(field_ptr.toIndex().?);
   1978         extra_index += 1;
   1979         _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = field_ptr } } }, field_init);
   1980     }
   1981 
   1982     _ = try gz.addPlNodePayloadIndex(.validate_ptr_struct_init, node, payload_index);
   1983 }
   1984 
   1985 /// This explicitly calls expr in a comptime scope by wrapping it in a `block_comptime` if
   1986 /// necessary. It should be used whenever we need to force compile-time evaluation of something,
   1987 /// such as a type.
   1988 /// The function corresponding to `comptime` expression syntax is `comptimeExprAst`.
   1989 fn comptimeExpr(
   1990     gz: *GenZir,
   1991     scope: *Scope,
   1992     ri: ResultInfo,
   1993     node: Ast.Node.Index,
   1994 ) InnerError!Zir.Inst.Ref {
   1995     if (gz.is_comptime) {
   1996         // No need to change anything!
   1997         return expr(gz, scope, ri, node);
   1998     }
   1999 
   2000     // There's an optimization here: if the body will be evaluated at comptime regardless, there's
   2001     // no need to wrap it in a block. This is hard to determine in general, but we can identify a
   2002     // common subset of trivially comptime expressions to take down the size of the ZIR a bit.
   2003     const tree = gz.astgen.tree;
   2004     const main_tokens = tree.nodes.items(.main_token);
   2005     const node_tags = tree.nodes.items(.tag);
   2006     switch (node_tags[node]) {
   2007         // Any identifier in `primitive_instrs` is trivially comptime. In particular, this includes
   2008         // some common types, so we can elide `block_comptime` for a few common type annotations.
   2009         .identifier => {
   2010             const ident_token = main_tokens[node];
   2011             const ident_name_raw = tree.tokenSlice(ident_token);
   2012             if (primitive_instrs.get(ident_name_raw)) |zir_const_ref| {
   2013                 // No need to worry about result location here, we're not creating a comptime block!
   2014                 return rvalue(gz, ri, zir_const_ref, node);
   2015             }
   2016         },
   2017 
   2018         // We can also avoid the block for a few trivial AST tags which are always comptime-known.
   2019         .number_literal, .string_literal, .multiline_string_literal, .enum_literal, .error_value => {
   2020             // No need to worry about result location here, we're not creating a comptime block!
   2021             return expr(gz, scope, ri, node);
   2022         },
   2023 
   2024         // Lastly, for labelled blocks, avoid emitting a labelled block directly inside this
   2025         // comptime block, because that would be silly! Note that we don't bother doing this for
   2026         // unlabelled blocks, since they don't generate blocks at comptime anyway (see `blockExpr`).
   2027         .block_two, .block_two_semicolon, .block, .block_semicolon => {
   2028             const token_tags = tree.tokens.items(.tag);
   2029             const lbrace = main_tokens[node];
   2030             // Careful! We can't pass in the real result location here, since it may
   2031             // refer to runtime memory. A runtime-to-comptime boundary has to remove
   2032             // result location information, compute the result, and copy it to the true
   2033             // result location at runtime. We do this below as well.
   2034             const ty_only_ri: ResultInfo = .{
   2035                 .ctx = ri.ctx,
   2036                 .rl = if (try ri.rl.resultType(gz, node)) |res_ty|
   2037                     .{ .coerced_ty = res_ty }
   2038                 else
   2039                     .none,
   2040             };
   2041             if (token_tags[lbrace - 1] == .colon and
   2042                 token_tags[lbrace - 2] == .identifier)
   2043             {
   2044                 const node_datas = tree.nodes.items(.data);
   2045                 switch (node_tags[node]) {
   2046                     .block_two, .block_two_semicolon => {
   2047                         const stmts: [2]Ast.Node.Index = .{ node_datas[node].lhs, node_datas[node].rhs };
   2048                         const stmt_slice = if (stmts[0] == 0)
   2049                             stmts[0..0]
   2050                         else if (stmts[1] == 0)
   2051                             stmts[0..1]
   2052                         else
   2053                             stmts[0..2];
   2054 
   2055                         const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmt_slice, true, .normal);
   2056                         return rvalue(gz, ri, block_ref, node);
   2057                     },
   2058                     .block, .block_semicolon => {
   2059                         const stmts = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
   2060                         // Replace result location and copy back later - see above.
   2061                         const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmts, true, .normal);
   2062                         return rvalue(gz, ri, block_ref, node);
   2063                     },
   2064                     else => unreachable,
   2065                 }
   2066             }
   2067         },
   2068 
   2069         // In other cases, we don't optimize anything - we need a wrapper comptime block.
   2070         else => {},
   2071     }
   2072 
   2073     var block_scope = gz.makeSubBlock(scope);
   2074     block_scope.is_comptime = true;
   2075     defer block_scope.unstack();
   2076 
   2077     const block_inst = try gz.makeBlockInst(.block_comptime, node);
   2078     // Replace result location and copy back later - see above.
   2079     const ty_only_ri: ResultInfo = .{
   2080         .ctx = ri.ctx,
   2081         .rl = if (try ri.rl.resultType(gz, node)) |res_ty|
   2082             .{ .coerced_ty = res_ty }
   2083         else
   2084             .none,
   2085     };
   2086     const block_result = try fullBodyExpr(&block_scope, scope, ty_only_ri, node, .normal);
   2087     if (!gz.refIsNoReturn(block_result)) {
   2088         _ = try block_scope.addBreak(.@"break", block_inst, block_result);
   2089     }
   2090     try block_scope.setBlockBody(block_inst);
   2091     try gz.instructions.append(gz.astgen.gpa, block_inst);
   2092 
   2093     return rvalue(gz, ri, block_inst.toRef(), node);
   2094 }
   2095 
   2096 /// This one is for an actual `comptime` syntax, and will emit a compile error if
   2097 /// the scope is already known to be comptime-evaluated.
   2098 /// See `comptimeExpr` for the helper function for calling expr in a comptime scope.
   2099 fn comptimeExprAst(
   2100     gz: *GenZir,
   2101     scope: *Scope,
   2102     ri: ResultInfo,
   2103     node: Ast.Node.Index,
   2104 ) InnerError!Zir.Inst.Ref {
   2105     const astgen = gz.astgen;
   2106     if (gz.is_comptime) {
   2107         try astgen.appendErrorNode(node, "redundant comptime keyword in already comptime scope", .{});
   2108     }
   2109     const tree = astgen.tree;
   2110     const node_datas = tree.nodes.items(.data);
   2111     const body_node = node_datas[node].lhs;
   2112     return comptimeExpr(gz, scope, ri, body_node);
   2113 }
   2114 
   2115 /// Restore the error return trace index. Performs the restore only if the result is a non-error or
   2116 /// if the result location is a non-error-handling expression.
   2117 fn restoreErrRetIndex(
   2118     gz: *GenZir,
   2119     bt: GenZir.BranchTarget,
   2120     ri: ResultInfo,
   2121     node: Ast.Node.Index,
   2122     result: Zir.Inst.Ref,
   2123 ) !void {
   2124     const op = switch (nodeMayEvalToError(gz.astgen.tree, node)) {
   2125         .always => return, // never restore/pop
   2126         .never => .none, // always restore/pop
   2127         .maybe => switch (ri.ctx) {
   2128             .error_handling_expr, .@"return", .fn_arg, .const_init => switch (ri.rl) {
   2129                 .ptr => |ptr_res| try gz.addUnNode(.load, ptr_res.inst, node),
   2130                 .inferred_ptr => blk: {
   2131                     // This is a terrible workaround for Sema's inability to load from a .alloc_inferred ptr
   2132                     // before its type has been resolved. There is no valid operand to use here, so error
   2133                     // traces will be popped prematurely.
   2134                     // TODO: Update this to do a proper load from the rl_ptr, once Sema can support it.
   2135                     break :blk .none;
   2136                 },
   2137                 .destructure => return, // value must be a tuple or array, so never restore/pop
   2138                 else => result,
   2139             },
   2140             else => .none, // always restore/pop
   2141         },
   2142     };
   2143     _ = try gz.addRestoreErrRetIndex(bt, .{ .if_non_error = op }, node);
   2144 }
   2145 
   2146 fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   2147     const astgen = parent_gz.astgen;
   2148     const tree = astgen.tree;
   2149     const node_datas = tree.nodes.items(.data);
   2150     const break_label = node_datas[node].lhs;
   2151     const rhs = node_datas[node].rhs;
   2152 
   2153     // Look for the label in the scope.
   2154     var scope = parent_scope;
   2155     while (true) {
   2156         switch (scope.tag) {
   2157             .gen_zir => {
   2158                 const block_gz = scope.cast(GenZir).?;
   2159 
   2160                 if (block_gz.cur_defer_node != 0) {
   2161                     // We are breaking out of a `defer` block.
   2162                     return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{
   2163                         try astgen.errNoteNode(
   2164                             block_gz.cur_defer_node,
   2165                             "defer expression here",
   2166                             .{},
   2167                         ),
   2168                     });
   2169                 }
   2170 
   2171                 const block_inst = blk: {
   2172                     if (break_label != 0) {
   2173                         if (block_gz.label) |*label| {
   2174                             if (try astgen.tokenIdentEql(label.token, break_label)) {
   2175                                 label.used = true;
   2176                                 break :blk label.block_inst;
   2177                             }
   2178                         }
   2179                     } else if (block_gz.break_block.unwrap()) |i| {
   2180                         break :blk i;
   2181                     }
   2182                     // If not the target, start over with the parent
   2183                     scope = block_gz.parent;
   2184                     continue;
   2185                 };
   2186                 // If we made it here, this block is the target of the break expr
   2187 
   2188                 const break_tag: Zir.Inst.Tag = if (block_gz.is_inline)
   2189                     .break_inline
   2190                 else
   2191                     .@"break";
   2192 
   2193                 if (rhs == 0) {
   2194                     _ = try rvalue(parent_gz, block_gz.break_result_info, .void_value, node);
   2195 
   2196                     try genDefers(parent_gz, scope, parent_scope, .normal_only);
   2197 
   2198                     // As our last action before the break, "pop" the error trace if needed
   2199                     if (!block_gz.is_comptime)
   2200                         _ = try parent_gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, node);
   2201 
   2202                     _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
   2203                     return Zir.Inst.Ref.unreachable_value;
   2204                 }
   2205 
   2206                 const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_info, rhs, node);
   2207 
   2208                 try genDefers(parent_gz, scope, parent_scope, .normal_only);
   2209 
   2210                 // As our last action before the break, "pop" the error trace if needed
   2211                 if (!block_gz.is_comptime)
   2212                     try restoreErrRetIndex(parent_gz, .{ .block = block_inst }, block_gz.break_result_info, rhs, operand);
   2213 
   2214                 switch (block_gz.break_result_info.rl) {
   2215                     .ptr => {
   2216                         // In this case we don't have any mechanism to intercept it;
   2217                         // we assume the result location is written, and we break with void.
   2218                         _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
   2219                     },
   2220                     .discard => {
   2221                         _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
   2222                     },
   2223                     else => {
   2224                         _ = try parent_gz.addBreakWithSrcNode(break_tag, block_inst, operand, rhs);
   2225                     },
   2226                 }
   2227                 return Zir.Inst.Ref.unreachable_value;
   2228             },
   2229             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   2230             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   2231             .namespace => break,
   2232             .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   2233             .top => unreachable,
   2234         }
   2235     }
   2236     if (break_label != 0) {
   2237         const label_name = try astgen.identifierTokenString(break_label);
   2238         return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
   2239     } else {
   2240         return astgen.failNode(node, "break expression outside loop", .{});
   2241     }
   2242 }
   2243 
   2244 fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   2245     const astgen = parent_gz.astgen;
   2246     const tree = astgen.tree;
   2247     const node_datas = tree.nodes.items(.data);
   2248     const break_label = node_datas[node].lhs;
   2249     const rhs = node_datas[node].rhs;
   2250 
   2251     if (break_label == 0 and rhs != 0) {
   2252         return astgen.failNode(node, "cannot continue with operand without label", .{});
   2253     }
   2254 
   2255     // Look for the label in the scope.
   2256     var scope = parent_scope;
   2257     while (true) {
   2258         switch (scope.tag) {
   2259             .gen_zir => {
   2260                 const gen_zir = scope.cast(GenZir).?;
   2261 
   2262                 if (gen_zir.cur_defer_node != 0) {
   2263                     return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{
   2264                         try astgen.errNoteNode(
   2265                             gen_zir.cur_defer_node,
   2266                             "defer expression here",
   2267                             .{},
   2268                         ),
   2269                     });
   2270                 }
   2271                 const continue_block = gen_zir.continue_block.unwrap() orelse {
   2272                     scope = gen_zir.parent;
   2273                     continue;
   2274                 };
   2275                 if (break_label != 0) blk: {
   2276                     if (gen_zir.label) |*label| {
   2277                         if (try astgen.tokenIdentEql(label.token, break_label)) {
   2278                             const maybe_switch_tag = astgen.instructions.items(.tag)[@intFromEnum(label.block_inst)];
   2279                             if (rhs != 0) switch (maybe_switch_tag) {
   2280                                 .switch_block, .switch_block_ref => {},
   2281                                 else => return astgen.failNode(node, "cannot continue loop with operand", .{}),
   2282                             } else switch (maybe_switch_tag) {
   2283                                 .switch_block, .switch_block_ref => return astgen.failNode(node, "cannot continue switch without operand", .{}),
   2284                                 else => {},
   2285                             }
   2286 
   2287                             label.used = true;
   2288                             label.used_for_continue = true;
   2289                             break :blk;
   2290                         }
   2291                     }
   2292                     // found continue but either it has a different label, or no label
   2293                     scope = gen_zir.parent;
   2294                     continue;
   2295                 } else if (gen_zir.label) |label| {
   2296                     // This `continue` is unlabeled. If the gz we've found corresponds to a labeled
   2297                     // `switch`, ignore it and continue to parent scopes.
   2298                     switch (astgen.instructions.items(.tag)[@intFromEnum(label.block_inst)]) {
   2299                         .switch_block, .switch_block_ref => {
   2300                             scope = gen_zir.parent;
   2301                             continue;
   2302                         },
   2303                         else => {},
   2304                     }
   2305                 }
   2306 
   2307                 if (rhs != 0) {
   2308                     // We need to figure out the result info to use.
   2309                     // The type should match
   2310                     const operand = try reachableExpr(parent_gz, parent_scope, gen_zir.continue_result_info, rhs, node);
   2311 
   2312                     try genDefers(parent_gz, scope, parent_scope, .normal_only);
   2313 
   2314                     // As our last action before the continue, "pop" the error trace if needed
   2315                     if (!gen_zir.is_comptime)
   2316                         _ = try parent_gz.addRestoreErrRetIndex(.{ .block = continue_block }, .always, node);
   2317 
   2318                     _ = try parent_gz.addBreakWithSrcNode(.switch_continue, continue_block, operand, rhs);
   2319                     return Zir.Inst.Ref.unreachable_value;
   2320                 }
   2321 
   2322                 try genDefers(parent_gz, scope, parent_scope, .normal_only);
   2323 
   2324                 const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline)
   2325                     .break_inline
   2326                 else
   2327                     .@"break";
   2328                 if (break_tag == .break_inline) {
   2329                     _ = try parent_gz.addUnNode(.check_comptime_control_flow, continue_block.toRef(), node);
   2330                 }
   2331 
   2332                 // As our last action before the continue, "pop" the error trace if needed
   2333                 if (!gen_zir.is_comptime)
   2334                     _ = try parent_gz.addRestoreErrRetIndex(.{ .block = continue_block }, .always, node);
   2335 
   2336                 _ = try parent_gz.addBreak(break_tag, continue_block, .void_value);
   2337                 return Zir.Inst.Ref.unreachable_value;
   2338             },
   2339             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   2340             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   2341             .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   2342             .namespace => break,
   2343             .top => unreachable,
   2344         }
   2345     }
   2346     if (break_label != 0) {
   2347         const label_name = try astgen.identifierTokenString(break_label);
   2348         return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
   2349     } else {
   2350         return astgen.failNode(node, "continue expression outside loop", .{});
   2351     }
   2352 }
   2353 
   2354 /// Similar to `expr`, but intended for use when `gz` corresponds to a body
   2355 /// which will contain only this node's code. Differs from `expr` in that if the
   2356 /// root expression is an unlabeled block, does not emit an actual block.
   2357 /// Instead, the block contents are emitted directly into `gz`.
   2358 fn fullBodyExpr(
   2359     gz: *GenZir,
   2360     scope: *Scope,
   2361     ri: ResultInfo,
   2362     node: Ast.Node.Index,
   2363     block_kind: BlockKind,
   2364 ) InnerError!Zir.Inst.Ref {
   2365     const tree = gz.astgen.tree;
   2366     const node_tags = tree.nodes.items(.tag);
   2367     const node_datas = tree.nodes.items(.data);
   2368     const main_tokens = tree.nodes.items(.main_token);
   2369     const token_tags = tree.tokens.items(.tag);
   2370     var stmt_buf: [2]Ast.Node.Index = undefined;
   2371     const statements: []const Ast.Node.Index = switch (node_tags[node]) {
   2372         else => return expr(gz, scope, ri, node),
   2373         .block_two, .block_two_semicolon => if (node_datas[node].lhs == 0) s: {
   2374             break :s &.{};
   2375         } else if (node_datas[node].rhs == 0) s: {
   2376             stmt_buf[0] = node_datas[node].lhs;
   2377             break :s stmt_buf[0..1];
   2378         } else s: {
   2379             stmt_buf[0] = node_datas[node].lhs;
   2380             stmt_buf[1] = node_datas[node].rhs;
   2381             break :s stmt_buf[0..2];
   2382         },
   2383         .block, .block_semicolon => tree.extra_data[node_datas[node].lhs..node_datas[node].rhs],
   2384     };
   2385 
   2386     const lbrace = main_tokens[node];
   2387     if (token_tags[lbrace - 1] == .colon and
   2388         token_tags[lbrace - 2] == .identifier)
   2389     {
   2390         // Labeled blocks are tricky - forwarding result location information properly is non-trivial,
   2391         // plus if this block is exited with a `break_inline` we aren't allowed multiple breaks. This
   2392         // case is rare, so just treat it as a normal expression and create a nested block.
   2393         return blockExpr(gz, scope, ri, node, statements, block_kind);
   2394     }
   2395 
   2396     var sub_gz = gz.makeSubBlock(scope);
   2397     try blockExprStmts(&sub_gz, &sub_gz.base, statements, block_kind);
   2398 
   2399     return rvalue(gz, ri, .void_value, node);
   2400 }
   2401 
   2402 const BlockKind = enum { normal, allow_branch_hint };
   2403 
   2404 fn blockExpr(
   2405     gz: *GenZir,
   2406     scope: *Scope,
   2407     ri: ResultInfo,
   2408     block_node: Ast.Node.Index,
   2409     statements: []const Ast.Node.Index,
   2410     kind: BlockKind,
   2411 ) InnerError!Zir.Inst.Ref {
   2412     const astgen = gz.astgen;
   2413     const tree = astgen.tree;
   2414     const main_tokens = tree.nodes.items(.main_token);
   2415     const token_tags = tree.tokens.items(.tag);
   2416 
   2417     const lbrace = main_tokens[block_node];
   2418     if (token_tags[lbrace - 1] == .colon and
   2419         token_tags[lbrace - 2] == .identifier)
   2420     {
   2421         return labeledBlockExpr(gz, scope, ri, block_node, statements, false, kind);
   2422     }
   2423 
   2424     if (!gz.is_comptime) {
   2425         // Since this block is unlabeled, its control flow is effectively linear and we
   2426         // can *almost* get away with inlining the block here. However, we actually need
   2427         // to preserve the .block for Sema, to properly pop the error return trace.
   2428 
   2429         const block_tag: Zir.Inst.Tag = .block;
   2430         const block_inst = try gz.makeBlockInst(block_tag, block_node);
   2431         try gz.instructions.append(astgen.gpa, block_inst);
   2432 
   2433         var block_scope = gz.makeSubBlock(scope);
   2434         defer block_scope.unstack();
   2435 
   2436         try blockExprStmts(&block_scope, &block_scope.base, statements, kind);
   2437 
   2438         if (!block_scope.endsWithNoReturn()) {
   2439             // As our last action before the break, "pop" the error trace if needed
   2440             _ = try gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, block_node);
   2441             // No `rvalue` call here, as the block result is always `void`, so we do that below.
   2442             _ = try block_scope.addBreak(.@"break", block_inst, .void_value);
   2443         }
   2444 
   2445         try block_scope.setBlockBody(block_inst);
   2446     } else {
   2447         var sub_gz = gz.makeSubBlock(scope);
   2448         try blockExprStmts(&sub_gz, &sub_gz.base, statements, kind);
   2449     }
   2450 
   2451     return rvalue(gz, ri, .void_value, block_node);
   2452 }
   2453 
   2454 fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.TokenIndex) !void {
   2455     // Look for the label in the scope.
   2456     var scope = parent_scope;
   2457     while (true) {
   2458         switch (scope.tag) {
   2459             .gen_zir => {
   2460                 const gen_zir = scope.cast(GenZir).?;
   2461                 if (gen_zir.label) |prev_label| {
   2462                     if (try astgen.tokenIdentEql(label, prev_label.token)) {
   2463                         const label_name = try astgen.identifierTokenString(label);
   2464                         return astgen.failTokNotes(label, "redefinition of label '{s}'", .{
   2465                             label_name,
   2466                         }, &[_]u32{
   2467                             try astgen.errNoteTok(
   2468                                 prev_label.token,
   2469                                 "previous definition here",
   2470                                 .{},
   2471                             ),
   2472                         });
   2473                     }
   2474                 }
   2475                 scope = gen_zir.parent;
   2476             },
   2477             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   2478             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   2479             .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   2480             .namespace => break,
   2481             .top => unreachable,
   2482         }
   2483     }
   2484 }
   2485 
   2486 fn labeledBlockExpr(
   2487     gz: *GenZir,
   2488     parent_scope: *Scope,
   2489     ri: ResultInfo,
   2490     block_node: Ast.Node.Index,
   2491     statements: []const Ast.Node.Index,
   2492     force_comptime: bool,
   2493     block_kind: BlockKind,
   2494 ) InnerError!Zir.Inst.Ref {
   2495     const astgen = gz.astgen;
   2496     const tree = astgen.tree;
   2497     const main_tokens = tree.nodes.items(.main_token);
   2498     const token_tags = tree.tokens.items(.tag);
   2499 
   2500     const lbrace = main_tokens[block_node];
   2501     const label_token = lbrace - 2;
   2502     assert(token_tags[label_token] == .identifier);
   2503 
   2504     try astgen.checkLabelRedefinition(parent_scope, label_token);
   2505 
   2506     const need_rl = astgen.nodes_need_rl.contains(block_node);
   2507     const block_ri: ResultInfo = if (need_rl) ri else .{
   2508         .rl = switch (ri.rl) {
   2509             .ptr => .{ .ty = (try ri.rl.resultType(gz, block_node)).? },
   2510             .inferred_ptr => .none,
   2511             else => ri.rl,
   2512         },
   2513         .ctx = ri.ctx,
   2514     };
   2515     // We need to call `rvalue` to write through to the pointer only if we had a
   2516     // result pointer and aren't forwarding it.
   2517     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   2518     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   2519 
   2520     // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct
   2521     // so that break statements can reference it.
   2522     const block_tag: Zir.Inst.Tag = if (force_comptime) .block_comptime else .block;
   2523     const block_inst = try gz.makeBlockInst(block_tag, block_node);
   2524     try gz.instructions.append(astgen.gpa, block_inst);
   2525     var block_scope = gz.makeSubBlock(parent_scope);
   2526     block_scope.label = GenZir.Label{
   2527         .token = label_token,
   2528         .block_inst = block_inst,
   2529     };
   2530     block_scope.setBreakResultInfo(block_ri);
   2531     if (force_comptime) block_scope.is_comptime = true;
   2532     defer block_scope.unstack();
   2533 
   2534     try blockExprStmts(&block_scope, &block_scope.base, statements, block_kind);
   2535     if (!block_scope.endsWithNoReturn()) {
   2536         // As our last action before the return, "pop" the error trace if needed
   2537         _ = try gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, block_node);
   2538         const result = try rvalue(gz, block_scope.break_result_info, .void_value, block_node);
   2539         _ = try block_scope.addBreak(.@"break", block_inst, result);
   2540     }
   2541 
   2542     if (!block_scope.label.?.used) {
   2543         try astgen.appendErrorTok(label_token, "unused block label", .{});
   2544     }
   2545 
   2546     try block_scope.setBlockBody(block_inst);
   2547     if (need_result_rvalue) {
   2548         return rvalue(gz, ri, block_inst.toRef(), block_node);
   2549     } else {
   2550         return block_inst.toRef();
   2551     }
   2552 }
   2553 
   2554 fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Node.Index, block_kind: BlockKind) !void {
   2555     const astgen = gz.astgen;
   2556     const tree = astgen.tree;
   2557     const node_tags = tree.nodes.items(.tag);
   2558     const node_data = tree.nodes.items(.data);
   2559 
   2560     if (statements.len == 0) return;
   2561 
   2562     var block_arena = std.heap.ArenaAllocator.init(gz.astgen.gpa);
   2563     defer block_arena.deinit();
   2564     const block_arena_allocator = block_arena.allocator();
   2565 
   2566     var noreturn_src_node: Ast.Node.Index = 0;
   2567     var scope = parent_scope;
   2568     for (statements, 0..) |statement, stmt_idx| {
   2569         if (noreturn_src_node != 0) {
   2570             try astgen.appendErrorNodeNotes(
   2571                 statement,
   2572                 "unreachable code",
   2573                 .{},
   2574                 &[_]u32{
   2575                     try astgen.errNoteNode(
   2576                         noreturn_src_node,
   2577                         "control flow is diverted here",
   2578                         .{},
   2579                     ),
   2580                 },
   2581             );
   2582         }
   2583         const allow_branch_hint = switch (block_kind) {
   2584             .normal => false,
   2585             .allow_branch_hint => stmt_idx == 0,
   2586         };
   2587         var inner_node = statement;
   2588         while (true) {
   2589             switch (node_tags[inner_node]) {
   2590                 // zig fmt: off
   2591                 .global_var_decl,
   2592                 .local_var_decl,
   2593                 .simple_var_decl,
   2594                 .aligned_var_decl, => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.fullVarDecl(statement).?),
   2595 
   2596                 .assign_destructure => scope = try assignDestructureMaybeDecls(gz, scope, statement, block_arena_allocator),
   2597 
   2598                 .@"defer"    => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_normal),
   2599                 .@"errdefer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_error),
   2600 
   2601                 .assign => try assign(gz, scope, statement),
   2602 
   2603                 .assign_shl => try assignShift(gz, scope, statement, .shl),
   2604                 .assign_shr => try assignShift(gz, scope, statement, .shr),
   2605 
   2606                 .assign_bit_and  => try assignOp(gz, scope, statement, .bit_and),
   2607                 .assign_bit_or   => try assignOp(gz, scope, statement, .bit_or),
   2608                 .assign_bit_xor  => try assignOp(gz, scope, statement, .xor),
   2609                 .assign_div      => try assignOp(gz, scope, statement, .div),
   2610                 .assign_sub      => try assignOp(gz, scope, statement, .sub),
   2611                 .assign_sub_wrap => try assignOp(gz, scope, statement, .subwrap),
   2612                 .assign_mod      => try assignOp(gz, scope, statement, .mod_rem),
   2613                 .assign_add      => try assignOp(gz, scope, statement, .add),
   2614                 .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap),
   2615                 .assign_mul      => try assignOp(gz, scope, statement, .mul),
   2616                 .assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap),
   2617 
   2618                 .grouped_expression => {
   2619                     inner_node = node_data[statement].lhs;
   2620                     continue;
   2621                 },
   2622 
   2623                 .while_simple,
   2624                 .while_cont,
   2625                 .@"while", => _ = try whileExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullWhile(inner_node).?, true),
   2626 
   2627                 .for_simple,
   2628                 .@"for", => _ = try forExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullFor(inner_node).?, true),
   2629 
   2630                 // These cases are here to allow branch hints.
   2631                 .builtin_call_two, .builtin_call_two_comma => {
   2632                     try emitDbgNode(gz, inner_node);
   2633                     const ri: ResultInfo = .{ .rl = .none };
   2634                     const result = if (node_data[inner_node].lhs == 0) r: {
   2635                         break :r try builtinCall(gz, scope, ri, inner_node, &.{}, allow_branch_hint);
   2636                     } else if (node_data[inner_node].rhs == 0) r: {
   2637                         break :r try builtinCall(gz, scope, ri, inner_node, &.{node_data[inner_node].lhs}, allow_branch_hint);
   2638                     } else r: {
   2639                         break :r try builtinCall(gz, scope, ri, inner_node, &.{
   2640                             node_data[inner_node].lhs,
   2641                             node_data[inner_node].rhs,
   2642                         }, allow_branch_hint);
   2643                     };
   2644                     noreturn_src_node = try addEnsureResult(gz, result, inner_node);
   2645                 },
   2646                 .builtin_call, .builtin_call_comma => {
   2647                     try emitDbgNode(gz, inner_node);
   2648                     const ri: ResultInfo = .{ .rl = .none };
   2649                     const params = tree.extra_data[node_data[inner_node].lhs..node_data[inner_node].rhs];
   2650                     const result = try builtinCall(gz, scope, ri, inner_node, params, allow_branch_hint);
   2651                     noreturn_src_node = try addEnsureResult(gz, result, inner_node);
   2652                 },
   2653 
   2654                 else => noreturn_src_node = try unusedResultExpr(gz, scope, inner_node),
   2655                 // zig fmt: on
   2656             }
   2657             break;
   2658         }
   2659     }
   2660 
   2661     if (noreturn_src_node == 0) {
   2662         try genDefers(gz, parent_scope, scope, .normal_only);
   2663     }
   2664     try checkUsed(gz, parent_scope, scope);
   2665 }
   2666 
   2667 /// Returns AST source node of the thing that is noreturn if the statement is
   2668 /// definitely `noreturn`. Otherwise returns 0.
   2669 fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) InnerError!Ast.Node.Index {
   2670     try emitDbgNode(gz, statement);
   2671     // We need to emit an error if the result is not `noreturn` or `void`, but
   2672     // we want to avoid adding the ZIR instruction if possible for performance.
   2673     const maybe_unused_result = try expr(gz, scope, .{ .rl = .none }, statement);
   2674     return addEnsureResult(gz, maybe_unused_result, statement);
   2675 }
   2676 
   2677 fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: Ast.Node.Index) InnerError!Ast.Node.Index {
   2678     var noreturn_src_node: Ast.Node.Index = 0;
   2679     const elide_check = if (maybe_unused_result.toIndex()) |inst| b: {
   2680         // Note that this array becomes invalid after appending more items to it
   2681         // in the above while loop.
   2682         const zir_tags = gz.astgen.instructions.items(.tag);
   2683         switch (zir_tags[@intFromEnum(inst)]) {
   2684             // For some instructions, modify the zir data
   2685             // so we can avoid a separate ensure_result_used instruction.
   2686             .call, .field_call => {
   2687                 const break_extra = gz.astgen.instructions.items(.data)[@intFromEnum(inst)].pl_node.payload_index;
   2688                 comptime assert(std.meta.fieldIndex(Zir.Inst.Call, "flags") ==
   2689                     std.meta.fieldIndex(Zir.Inst.FieldCall, "flags"));
   2690                 const flags: *Zir.Inst.Call.Flags = @ptrCast(&gz.astgen.extra.items[
   2691                     break_extra + std.meta.fieldIndex(Zir.Inst.Call, "flags").?
   2692                 ]);
   2693                 flags.ensure_result_used = true;
   2694                 break :b true;
   2695             },
   2696             .builtin_call => {
   2697                 const break_extra = gz.astgen.instructions.items(.data)[@intFromEnum(inst)].pl_node.payload_index;
   2698                 const flags: *Zir.Inst.BuiltinCall.Flags = @ptrCast(&gz.astgen.extra.items[
   2699                     break_extra + std.meta.fieldIndex(Zir.Inst.BuiltinCall, "flags").?
   2700                 ]);
   2701                 flags.ensure_result_used = true;
   2702                 break :b true;
   2703             },
   2704 
   2705             // ZIR instructions that might be a type other than `noreturn` or `void`.
   2706             .add,
   2707             .addwrap,
   2708             .add_sat,
   2709             .add_unsafe,
   2710             .param,
   2711             .param_comptime,
   2712             .param_anytype,
   2713             .param_anytype_comptime,
   2714             .alloc,
   2715             .alloc_mut,
   2716             .alloc_comptime_mut,
   2717             .alloc_inferred,
   2718             .alloc_inferred_mut,
   2719             .alloc_inferred_comptime,
   2720             .alloc_inferred_comptime_mut,
   2721             .make_ptr_const,
   2722             .array_cat,
   2723             .array_mul,
   2724             .array_type,
   2725             .array_type_sentinel,
   2726             .elem_type,
   2727             .indexable_ptr_elem_type,
   2728             .vec_arr_elem_type,
   2729             .vector_type,
   2730             .indexable_ptr_len,
   2731             .anyframe_type,
   2732             .as_node,
   2733             .as_shift_operand,
   2734             .bit_and,
   2735             .bitcast,
   2736             .bit_or,
   2737             .block,
   2738             .block_comptime,
   2739             .block_inline,
   2740             .declaration,
   2741             .suspend_block,
   2742             .loop,
   2743             .bool_br_and,
   2744             .bool_br_or,
   2745             .bool_not,
   2746             .cmp_lt,
   2747             .cmp_lte,
   2748             .cmp_eq,
   2749             .cmp_gte,
   2750             .cmp_gt,
   2751             .cmp_neq,
   2752             .decl_ref,
   2753             .decl_val,
   2754             .load,
   2755             .div,
   2756             .elem_ptr,
   2757             .elem_val,
   2758             .elem_ptr_node,
   2759             .elem_val_node,
   2760             .elem_val_imm,
   2761             .field_ptr,
   2762             .field_val,
   2763             .field_ptr_named,
   2764             .field_val_named,
   2765             .func,
   2766             .func_inferred,
   2767             .func_fancy,
   2768             .int,
   2769             .int_big,
   2770             .float,
   2771             .float128,
   2772             .int_type,
   2773             .is_non_null,
   2774             .is_non_null_ptr,
   2775             .is_non_err,
   2776             .is_non_err_ptr,
   2777             .ret_is_non_err,
   2778             .mod_rem,
   2779             .mul,
   2780             .mulwrap,
   2781             .mul_sat,
   2782             .ref,
   2783             .shl,
   2784             .shl_sat,
   2785             .shr,
   2786             .str,
   2787             .sub,
   2788             .subwrap,
   2789             .sub_sat,
   2790             .negate,
   2791             .negate_wrap,
   2792             .typeof,
   2793             .typeof_builtin,
   2794             .xor,
   2795             .optional_type,
   2796             .optional_payload_safe,
   2797             .optional_payload_unsafe,
   2798             .optional_payload_safe_ptr,
   2799             .optional_payload_unsafe_ptr,
   2800             .err_union_payload_unsafe,
   2801             .err_union_payload_unsafe_ptr,
   2802             .err_union_code,
   2803             .err_union_code_ptr,
   2804             .ptr_type,
   2805             .enum_literal,
   2806             .decl_literal,
   2807             .decl_literal_no_coerce,
   2808             .merge_error_sets,
   2809             .error_union_type,
   2810             .bit_not,
   2811             .error_value,
   2812             .slice_start,
   2813             .slice_end,
   2814             .slice_sentinel,
   2815             .slice_length,
   2816             .import,
   2817             .switch_block,
   2818             .switch_block_ref,
   2819             .switch_block_err_union,
   2820             .union_init,
   2821             .field_type_ref,
   2822             .error_set_decl,
   2823             .enum_from_int,
   2824             .int_from_enum,
   2825             .type_info,
   2826             .size_of,
   2827             .bit_size_of,
   2828             .typeof_log2_int_type,
   2829             .int_from_ptr,
   2830             .align_of,
   2831             .int_from_bool,
   2832             .embed_file,
   2833             .error_name,
   2834             .sqrt,
   2835             .sin,
   2836             .cos,
   2837             .tan,
   2838             .exp,
   2839             .exp2,
   2840             .log,
   2841             .log2,
   2842             .log10,
   2843             .abs,
   2844             .floor,
   2845             .ceil,
   2846             .trunc,
   2847             .round,
   2848             .tag_name,
   2849             .type_name,
   2850             .frame_type,
   2851             .frame_size,
   2852             .int_from_float,
   2853             .float_from_int,
   2854             .ptr_from_int,
   2855             .float_cast,
   2856             .int_cast,
   2857             .ptr_cast,
   2858             .truncate,
   2859             .has_decl,
   2860             .has_field,
   2861             .clz,
   2862             .ctz,
   2863             .pop_count,
   2864             .byte_swap,
   2865             .bit_reverse,
   2866             .div_exact,
   2867             .div_floor,
   2868             .div_trunc,
   2869             .mod,
   2870             .rem,
   2871             .shl_exact,
   2872             .shr_exact,
   2873             .bit_offset_of,
   2874             .offset_of,
   2875             .splat,
   2876             .reduce,
   2877             .shuffle,
   2878             .atomic_load,
   2879             .atomic_rmw,
   2880             .mul_add,
   2881             .max,
   2882             .min,
   2883             .c_import,
   2884             .@"resume",
   2885             .@"await",
   2886             .ret_err_value_code,
   2887             .ret_ptr,
   2888             .ret_type,
   2889             .for_len,
   2890             .@"try",
   2891             .try_ptr,
   2892             .opt_eu_base_ptr_init,
   2893             .coerce_ptr_elem_ty,
   2894             .struct_init_empty,
   2895             .struct_init_empty_result,
   2896             .struct_init_empty_ref_result,
   2897             .struct_init_anon,
   2898             .struct_init,
   2899             .struct_init_ref,
   2900             .struct_init_field_type,
   2901             .struct_init_field_ptr,
   2902             .array_init_anon,
   2903             .array_init,
   2904             .array_init_ref,
   2905             .validate_array_init_ref_ty,
   2906             .array_init_elem_type,
   2907             .array_init_elem_ptr,
   2908             => break :b false,
   2909 
   2910             .extended => switch (gz.astgen.instructions.items(.data)[@intFromEnum(inst)].extended.opcode) {
   2911                 .breakpoint,
   2912                 .disable_instrumentation,
   2913                 .set_float_mode,
   2914                 .branch_hint,
   2915                 => break :b true,
   2916                 else => break :b false,
   2917             },
   2918 
   2919             // ZIR instructions that are always `noreturn`.
   2920             .@"break",
   2921             .break_inline,
   2922             .condbr,
   2923             .condbr_inline,
   2924             .compile_error,
   2925             .ret_node,
   2926             .ret_load,
   2927             .ret_implicit,
   2928             .ret_err_value,
   2929             .@"unreachable",
   2930             .repeat,
   2931             .repeat_inline,
   2932             .panic,
   2933             .trap,
   2934             .check_comptime_control_flow,
   2935             .switch_continue,
   2936             => {
   2937                 noreturn_src_node = statement;
   2938                 break :b true;
   2939             },
   2940 
   2941             // ZIR instructions that are always `void`.
   2942             .dbg_stmt,
   2943             .dbg_var_ptr,
   2944             .dbg_var_val,
   2945             .ensure_result_used,
   2946             .ensure_result_non_error,
   2947             .ensure_err_union_payload_void,
   2948             .@"export",
   2949             .set_eval_branch_quota,
   2950             .atomic_store,
   2951             .store_node,
   2952             .store_to_inferred_ptr,
   2953             .resolve_inferred_alloc,
   2954             .set_runtime_safety,
   2955             .memcpy,
   2956             .memset,
   2957             .validate_deref,
   2958             .validate_destructure,
   2959             .save_err_ret_index,
   2960             .restore_err_ret_index_unconditional,
   2961             .restore_err_ret_index_fn_entry,
   2962             .validate_struct_init_ty,
   2963             .validate_struct_init_result_ty,
   2964             .validate_ptr_struct_init,
   2965             .validate_array_init_ty,
   2966             .validate_array_init_result_ty,
   2967             .validate_ptr_array_init,
   2968             .validate_ref_ty,
   2969             .try_operand_ty,
   2970             .try_ref_operand_ty,
   2971             => break :b true,
   2972 
   2973             .@"defer" => unreachable,
   2974             .defer_err_code => unreachable,
   2975         }
   2976     } else switch (maybe_unused_result) {
   2977         .none => unreachable,
   2978 
   2979         .unreachable_value => b: {
   2980             noreturn_src_node = statement;
   2981             break :b true;
   2982         },
   2983 
   2984         .void_value => true,
   2985 
   2986         else => false,
   2987     };
   2988     if (!elide_check) {
   2989         _ = try gz.addUnNode(.ensure_result_used, maybe_unused_result, statement);
   2990     }
   2991     return noreturn_src_node;
   2992 }
   2993 
   2994 fn countDefers(outer_scope: *Scope, inner_scope: *Scope) struct {
   2995     have_any: bool,
   2996     have_normal: bool,
   2997     have_err: bool,
   2998     need_err_code: bool,
   2999 } {
   3000     var have_normal = false;
   3001     var have_err = false;
   3002     var need_err_code = false;
   3003     var scope = inner_scope;
   3004     while (scope != outer_scope) {
   3005         switch (scope.tag) {
   3006             .gen_zir => scope = scope.cast(GenZir).?.parent,
   3007             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   3008             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   3009             .defer_normal => {
   3010                 const defer_scope = scope.cast(Scope.Defer).?;
   3011                 scope = defer_scope.parent;
   3012 
   3013                 have_normal = true;
   3014             },
   3015             .defer_error => {
   3016                 const defer_scope = scope.cast(Scope.Defer).?;
   3017                 scope = defer_scope.parent;
   3018 
   3019                 have_err = true;
   3020 
   3021                 const have_err_payload = defer_scope.remapped_err_code != .none;
   3022                 need_err_code = need_err_code or have_err_payload;
   3023             },
   3024             .namespace => unreachable,
   3025             .top => unreachable,
   3026         }
   3027     }
   3028     return .{
   3029         .have_any = have_normal or have_err,
   3030         .have_normal = have_normal,
   3031         .have_err = have_err,
   3032         .need_err_code = need_err_code,
   3033     };
   3034 }
   3035 
   3036 const DefersToEmit = union(enum) {
   3037     both: Zir.Inst.Ref, // err code
   3038     both_sans_err,
   3039     normal_only,
   3040 };
   3041 
   3042 fn genDefers(
   3043     gz: *GenZir,
   3044     outer_scope: *Scope,
   3045     inner_scope: *Scope,
   3046     which_ones: DefersToEmit,
   3047 ) InnerError!void {
   3048     const gpa = gz.astgen.gpa;
   3049 
   3050     var scope = inner_scope;
   3051     while (scope != outer_scope) {
   3052         switch (scope.tag) {
   3053             .gen_zir => scope = scope.cast(GenZir).?.parent,
   3054             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   3055             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   3056             .defer_normal => {
   3057                 const defer_scope = scope.cast(Scope.Defer).?;
   3058                 scope = defer_scope.parent;
   3059                 try gz.addDefer(defer_scope.index, defer_scope.len);
   3060             },
   3061             .defer_error => {
   3062                 const defer_scope = scope.cast(Scope.Defer).?;
   3063                 scope = defer_scope.parent;
   3064                 switch (which_ones) {
   3065                     .both_sans_err => {
   3066                         try gz.addDefer(defer_scope.index, defer_scope.len);
   3067                     },
   3068                     .both => |err_code| {
   3069                         if (defer_scope.remapped_err_code.unwrap()) |remapped_err_code| {
   3070                             try gz.instructions.ensureUnusedCapacity(gpa, 1);
   3071                             try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
   3072 
   3073                             const payload_index = try gz.astgen.addExtra(Zir.Inst.DeferErrCode{
   3074                                 .remapped_err_code = remapped_err_code,
   3075                                 .index = defer_scope.index,
   3076                                 .len = defer_scope.len,
   3077                             });
   3078                             const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
   3079                             gz.astgen.instructions.appendAssumeCapacity(.{
   3080                                 .tag = .defer_err_code,
   3081                                 .data = .{ .defer_err_code = .{
   3082                                     .err_code = err_code,
   3083                                     .payload_index = payload_index,
   3084                                 } },
   3085                             });
   3086                             gz.instructions.appendAssumeCapacity(new_index);
   3087                         } else {
   3088                             try gz.addDefer(defer_scope.index, defer_scope.len);
   3089                         }
   3090                     },
   3091                     .normal_only => continue,
   3092                 }
   3093             },
   3094             .namespace => unreachable,
   3095             .top => unreachable,
   3096         }
   3097     }
   3098 }
   3099 
   3100 fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!void {
   3101     const astgen = gz.astgen;
   3102 
   3103     var scope = inner_scope;
   3104     while (scope != outer_scope) {
   3105         switch (scope.tag) {
   3106             .gen_zir => scope = scope.cast(GenZir).?.parent,
   3107             .local_val => {
   3108                 const s = scope.cast(Scope.LocalVal).?;
   3109                 if (s.used == 0 and s.discarded == 0) {
   3110                     try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
   3111                 } else if (s.used != 0 and s.discarded != 0) {
   3112                     try astgen.appendErrorTokNotes(s.discarded, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{
   3113                         try gz.astgen.errNoteTok(s.used, "used here", .{}),
   3114                     });
   3115                 }
   3116                 scope = s.parent;
   3117             },
   3118             .local_ptr => {
   3119                 const s = scope.cast(Scope.LocalPtr).?;
   3120                 if (s.used == 0 and s.discarded == 0) {
   3121                     try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
   3122                 } else {
   3123                     if (s.used != 0 and s.discarded != 0) {
   3124                         try astgen.appendErrorTokNotes(s.discarded, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{
   3125                             try astgen.errNoteTok(s.used, "used here", .{}),
   3126                         });
   3127                     }
   3128                     if (s.id_cat == .@"local variable" and !s.used_as_lvalue) {
   3129                         try astgen.appendErrorTokNotes(s.token_src, "local variable is never mutated", .{}, &.{
   3130                             try astgen.errNoteTok(s.token_src, "consider using 'const'", .{}),
   3131                         });
   3132                     }
   3133                 }
   3134 
   3135                 scope = s.parent;
   3136             },
   3137             .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   3138             .namespace => unreachable,
   3139             .top => unreachable,
   3140         }
   3141     }
   3142 }
   3143 
   3144 fn deferStmt(
   3145     gz: *GenZir,
   3146     scope: *Scope,
   3147     node: Ast.Node.Index,
   3148     block_arena: Allocator,
   3149     scope_tag: Scope.Tag,
   3150 ) InnerError!*Scope {
   3151     var defer_gen = gz.makeSubBlock(scope);
   3152     defer_gen.cur_defer_node = node;
   3153     defer_gen.any_defer_node = node;
   3154     defer defer_gen.unstack();
   3155 
   3156     const tree = gz.astgen.tree;
   3157     const node_datas = tree.nodes.items(.data);
   3158     const expr_node = node_datas[node].rhs;
   3159 
   3160     const payload_token = node_datas[node].lhs;
   3161     var local_val_scope: Scope.LocalVal = undefined;
   3162     var opt_remapped_err_code: Zir.Inst.OptionalIndex = .none;
   3163     const have_err_code = scope_tag == .defer_error and payload_token != 0;
   3164     const sub_scope = if (!have_err_code) &defer_gen.base else blk: {
   3165         const ident_name = try gz.astgen.identAsString(payload_token);
   3166         if (std.mem.eql(u8, tree.tokenSlice(payload_token), "_")) {
   3167             try gz.astgen.appendErrorTok(payload_token, "discard of error capture; omit it instead", .{});
   3168             break :blk &defer_gen.base;
   3169         }
   3170         const remapped_err_code: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
   3171         opt_remapped_err_code = remapped_err_code.toOptional();
   3172         try gz.astgen.instructions.append(gz.astgen.gpa, .{
   3173             .tag = .extended,
   3174             .data = .{ .extended = .{
   3175                 .opcode = .value_placeholder,
   3176                 .small = undefined,
   3177                 .operand = undefined,
   3178             } },
   3179         });
   3180         const remapped_err_code_ref = remapped_err_code.toRef();
   3181         local_val_scope = .{
   3182             .parent = &defer_gen.base,
   3183             .gen_zir = gz,
   3184             .name = ident_name,
   3185             .inst = remapped_err_code_ref,
   3186             .token_src = payload_token,
   3187             .id_cat = .capture,
   3188         };
   3189         try gz.addDbgVar(.dbg_var_val, ident_name, remapped_err_code_ref);
   3190         break :blk &local_val_scope.base;
   3191     };
   3192     _ = try unusedResultExpr(&defer_gen, sub_scope, expr_node);
   3193     try checkUsed(gz, scope, sub_scope);
   3194     _ = try defer_gen.addBreak(.break_inline, @enumFromInt(0), .void_value);
   3195 
   3196     const body = defer_gen.instructionsSlice();
   3197     const extra_insts: []const Zir.Inst.Index = if (opt_remapped_err_code.unwrap()) |ec| &.{ec} else &.{};
   3198     const body_len = gz.astgen.countBodyLenAfterFixupsExtraRefs(body, extra_insts);
   3199 
   3200     const index: u32 = @intCast(gz.astgen.extra.items.len);
   3201     try gz.astgen.extra.ensureUnusedCapacity(gz.astgen.gpa, body_len);
   3202     gz.astgen.appendBodyWithFixupsExtraRefsArrayList(&gz.astgen.extra, body, extra_insts);
   3203 
   3204     const defer_scope = try block_arena.create(Scope.Defer);
   3205 
   3206     defer_scope.* = .{
   3207         .base = .{ .tag = scope_tag },
   3208         .parent = scope,
   3209         .index = index,
   3210         .len = body_len,
   3211         .remapped_err_code = opt_remapped_err_code,
   3212     };
   3213     return &defer_scope.base;
   3214 }
   3215 
   3216 fn varDecl(
   3217     gz: *GenZir,
   3218     scope: *Scope,
   3219     node: Ast.Node.Index,
   3220     block_arena: Allocator,
   3221     var_decl: Ast.full.VarDecl,
   3222 ) InnerError!*Scope {
   3223     try emitDbgNode(gz, node);
   3224     const astgen = gz.astgen;
   3225     const tree = astgen.tree;
   3226     const token_tags = tree.tokens.items(.tag);
   3227     const main_tokens = tree.nodes.items(.main_token);
   3228 
   3229     const name_token = var_decl.ast.mut_token + 1;
   3230     const ident_name_raw = tree.tokenSlice(name_token);
   3231     if (mem.eql(u8, ident_name_raw, "_")) {
   3232         return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{});
   3233     }
   3234     const ident_name = try astgen.identAsString(name_token);
   3235 
   3236     try astgen.detectLocalShadowing(
   3237         scope,
   3238         ident_name,
   3239         name_token,
   3240         ident_name_raw,
   3241         if (token_tags[var_decl.ast.mut_token] == .keyword_const) .@"local constant" else .@"local variable",
   3242     );
   3243 
   3244     if (var_decl.ast.init_node == 0) {
   3245         return astgen.failNode(node, "variables must be initialized", .{});
   3246     }
   3247 
   3248     if (var_decl.ast.addrspace_node != 0) {
   3249         return astgen.failTok(main_tokens[var_decl.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw});
   3250     }
   3251 
   3252     if (var_decl.ast.section_node != 0) {
   3253         return astgen.failTok(main_tokens[var_decl.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw});
   3254     }
   3255 
   3256     const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node != 0)
   3257         try expr(gz, scope, coerced_align_ri, var_decl.ast.align_node)
   3258     else
   3259         .none;
   3260 
   3261     switch (token_tags[var_decl.ast.mut_token]) {
   3262         .keyword_const => {
   3263             if (var_decl.comptime_token) |comptime_token| {
   3264                 try astgen.appendErrorTok(comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
   3265             }
   3266 
   3267             // `comptime const` is a non-fatal error; treat it like the init was marked `comptime`.
   3268             const force_comptime = var_decl.comptime_token != null;
   3269 
   3270             // Depending on the type of AST the initialization expression is, we may need an lvalue
   3271             // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
   3272             // the variable, no memory location needed.
   3273             const type_node = var_decl.ast.type_node;
   3274             if (align_inst == .none and
   3275                 !astgen.nodes_need_rl.contains(node))
   3276             {
   3277                 const result_info: ResultInfo = if (type_node != 0) .{
   3278                     .rl = .{ .ty = try typeExpr(gz, scope, type_node) },
   3279                     .ctx = .const_init,
   3280                 } else .{ .rl = .none, .ctx = .const_init };
   3281                 const prev_anon_name_strategy = gz.anon_name_strategy;
   3282                 gz.anon_name_strategy = .dbg_var;
   3283                 const init_inst = try reachableExprComptime(gz, scope, result_info, var_decl.ast.init_node, node, force_comptime);
   3284                 gz.anon_name_strategy = prev_anon_name_strategy;
   3285 
   3286                 try gz.addDbgVar(.dbg_var_val, ident_name, init_inst);
   3287 
   3288                 // The const init expression may have modified the error return trace, so signal
   3289                 // to Sema that it should save the new index for restoring later.
   3290                 if (nodeMayAppendToErrorTrace(tree, var_decl.ast.init_node))
   3291                     _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst });
   3292 
   3293                 const sub_scope = try block_arena.create(Scope.LocalVal);
   3294                 sub_scope.* = .{
   3295                     .parent = scope,
   3296                     .gen_zir = gz,
   3297                     .name = ident_name,
   3298                     .inst = init_inst,
   3299                     .token_src = name_token,
   3300                     .id_cat = .@"local constant",
   3301                 };
   3302                 return &sub_scope.base;
   3303             }
   3304 
   3305             const is_comptime = gz.is_comptime or
   3306                 tree.nodes.items(.tag)[var_decl.ast.init_node] == .@"comptime";
   3307 
   3308             const init_rl: ResultInfo.Loc = if (type_node != 0) init_rl: {
   3309                 const type_inst = try typeExpr(gz, scope, type_node);
   3310                 if (align_inst == .none) {
   3311                     break :init_rl .{ .ptr = .{ .inst = try gz.addUnNode(.alloc, type_inst, node) } };
   3312                 } else {
   3313                     break :init_rl .{ .ptr = .{ .inst = try gz.addAllocExtended(.{
   3314                         .node = node,
   3315                         .type_inst = type_inst,
   3316                         .align_inst = align_inst,
   3317                         .is_const = true,
   3318                         .is_comptime = is_comptime,
   3319                     }) } };
   3320                 }
   3321             } else init_rl: {
   3322                 const alloc_inst = if (align_inst == .none) ptr: {
   3323                     const tag: Zir.Inst.Tag = if (is_comptime)
   3324                         .alloc_inferred_comptime
   3325                     else
   3326                         .alloc_inferred;
   3327                     break :ptr try gz.addNode(tag, node);
   3328                 } else ptr: {
   3329                     break :ptr try gz.addAllocExtended(.{
   3330                         .node = node,
   3331                         .type_inst = .none,
   3332                         .align_inst = align_inst,
   3333                         .is_const = true,
   3334                         .is_comptime = is_comptime,
   3335                     });
   3336                 };
   3337                 break :init_rl .{ .inferred_ptr = alloc_inst };
   3338             };
   3339             const var_ptr: Zir.Inst.Ref, const resolve_inferred: bool = switch (init_rl) {
   3340                 .ptr => |ptr| .{ ptr.inst, false },
   3341                 .inferred_ptr => |inst| .{ inst, true },
   3342                 else => unreachable,
   3343             };
   3344             const init_result_info: ResultInfo = .{ .rl = init_rl, .ctx = .const_init };
   3345 
   3346             const prev_anon_name_strategy = gz.anon_name_strategy;
   3347             gz.anon_name_strategy = .dbg_var;
   3348             defer gz.anon_name_strategy = prev_anon_name_strategy;
   3349             const init_inst = try reachableExprComptime(gz, scope, init_result_info, var_decl.ast.init_node, node, force_comptime);
   3350 
   3351             // The const init expression may have modified the error return trace, so signal
   3352             // to Sema that it should save the new index for restoring later.
   3353             if (nodeMayAppendToErrorTrace(tree, var_decl.ast.init_node))
   3354                 _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst });
   3355 
   3356             const const_ptr = if (resolve_inferred)
   3357                 try gz.addUnNode(.resolve_inferred_alloc, var_ptr, node)
   3358             else
   3359                 try gz.addUnNode(.make_ptr_const, var_ptr, node);
   3360 
   3361             try gz.addDbgVar(.dbg_var_ptr, ident_name, const_ptr);
   3362 
   3363             const sub_scope = try block_arena.create(Scope.LocalPtr);
   3364             sub_scope.* = .{
   3365                 .parent = scope,
   3366                 .gen_zir = gz,
   3367                 .name = ident_name,
   3368                 .ptr = const_ptr,
   3369                 .token_src = name_token,
   3370                 .maybe_comptime = true,
   3371                 .id_cat = .@"local constant",
   3372             };
   3373             return &sub_scope.base;
   3374         },
   3375         .keyword_var => {
   3376             if (var_decl.comptime_token != null and gz.is_comptime)
   3377                 return astgen.failTok(var_decl.comptime_token.?, "'comptime var' is redundant in comptime scope", .{});
   3378             const is_comptime = var_decl.comptime_token != null or gz.is_comptime;
   3379             const alloc: Zir.Inst.Ref, const resolve_inferred: bool, const result_info: ResultInfo = if (var_decl.ast.type_node != 0) a: {
   3380                 const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node);
   3381                 const alloc = alloc: {
   3382                     if (align_inst == .none) {
   3383                         const tag: Zir.Inst.Tag = if (is_comptime)
   3384                             .alloc_comptime_mut
   3385                         else
   3386                             .alloc_mut;
   3387                         break :alloc try gz.addUnNode(tag, type_inst, node);
   3388                     } else {
   3389                         break :alloc try gz.addAllocExtended(.{
   3390                             .node = node,
   3391                             .type_inst = type_inst,
   3392                             .align_inst = align_inst,
   3393                             .is_const = false,
   3394                             .is_comptime = is_comptime,
   3395                         });
   3396                     }
   3397                 };
   3398                 break :a .{ alloc, false, .{ .rl = .{ .ptr = .{ .inst = alloc } } } };
   3399             } else a: {
   3400                 const alloc = alloc: {
   3401                     if (align_inst == .none) {
   3402                         const tag: Zir.Inst.Tag = if (is_comptime)
   3403                             .alloc_inferred_comptime_mut
   3404                         else
   3405                             .alloc_inferred_mut;
   3406                         break :alloc try gz.addNode(tag, node);
   3407                     } else {
   3408                         break :alloc try gz.addAllocExtended(.{
   3409                             .node = node,
   3410                             .type_inst = .none,
   3411                             .align_inst = align_inst,
   3412                             .is_const = false,
   3413                             .is_comptime = is_comptime,
   3414                         });
   3415                     }
   3416                 };
   3417                 break :a .{ alloc, true, .{ .rl = .{ .inferred_ptr = alloc } } };
   3418             };
   3419             const prev_anon_name_strategy = gz.anon_name_strategy;
   3420             gz.anon_name_strategy = .dbg_var;
   3421             _ = try reachableExprComptime(gz, scope, result_info, var_decl.ast.init_node, node, is_comptime);
   3422             gz.anon_name_strategy = prev_anon_name_strategy;
   3423             const final_ptr: Zir.Inst.Ref = if (resolve_inferred) ptr: {
   3424                 break :ptr try gz.addUnNode(.resolve_inferred_alloc, alloc, node);
   3425             } else alloc;
   3426 
   3427             try gz.addDbgVar(.dbg_var_ptr, ident_name, final_ptr);
   3428 
   3429             const sub_scope = try block_arena.create(Scope.LocalPtr);
   3430             sub_scope.* = .{
   3431                 .parent = scope,
   3432                 .gen_zir = gz,
   3433                 .name = ident_name,
   3434                 .ptr = final_ptr,
   3435                 .token_src = name_token,
   3436                 .maybe_comptime = is_comptime,
   3437                 .id_cat = .@"local variable",
   3438             };
   3439             return &sub_scope.base;
   3440         },
   3441         else => unreachable,
   3442     }
   3443 }
   3444 
   3445 fn emitDbgNode(gz: *GenZir, node: Ast.Node.Index) !void {
   3446     // The instruction emitted here is for debugging runtime code.
   3447     // If the current block will be evaluated only during semantic analysis
   3448     // then no dbg_stmt ZIR instruction is needed.
   3449     if (gz.is_comptime) return;
   3450     const astgen = gz.astgen;
   3451     astgen.advanceSourceCursorToNode(node);
   3452     const line = astgen.source_line - gz.decl_line;
   3453     const column = astgen.source_column;
   3454     try emitDbgStmt(gz, .{ line, column });
   3455 }
   3456 
   3457 fn assign(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void {
   3458     try emitDbgNode(gz, infix_node);
   3459     const astgen = gz.astgen;
   3460     const tree = astgen.tree;
   3461     const node_datas = tree.nodes.items(.data);
   3462     const main_tokens = tree.nodes.items(.main_token);
   3463     const node_tags = tree.nodes.items(.tag);
   3464 
   3465     const lhs = node_datas[infix_node].lhs;
   3466     const rhs = node_datas[infix_node].rhs;
   3467     if (node_tags[lhs] == .identifier) {
   3468         // This intentionally does not support `@"_"` syntax.
   3469         const ident_name = tree.tokenSlice(main_tokens[lhs]);
   3470         if (mem.eql(u8, ident_name, "_")) {
   3471             _ = try expr(gz, scope, .{ .rl = .discard, .ctx = .assignment }, rhs);
   3472             return;
   3473         }
   3474     }
   3475     const lvalue = try lvalExpr(gz, scope, lhs);
   3476     _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{
   3477         .inst = lvalue,
   3478         .src_node = infix_node,
   3479     } } }, rhs);
   3480 }
   3481 
   3482 /// Handles destructure assignments where no LHS is a `const` or `var` decl.
   3483 fn assignDestructure(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!void {
   3484     try emitDbgNode(gz, node);
   3485     const astgen = gz.astgen;
   3486     const tree = astgen.tree;
   3487     const main_tokens = tree.nodes.items(.main_token);
   3488     const node_tags = tree.nodes.items(.tag);
   3489 
   3490     const full = tree.assignDestructure(node);
   3491     if (full.comptime_token != null and gz.is_comptime) {
   3492         return astgen.appendErrorNode(node, "redundant comptime keyword in already comptime scope", .{});
   3493     }
   3494 
   3495     // If this expression is marked comptime, we must wrap the whole thing in a comptime block.
   3496     var gz_buf: GenZir = undefined;
   3497     const inner_gz = if (full.comptime_token) |_| bs: {
   3498         gz_buf = gz.makeSubBlock(scope);
   3499         gz_buf.is_comptime = true;
   3500         break :bs &gz_buf;
   3501     } else gz;
   3502     defer if (full.comptime_token) |_| inner_gz.unstack();
   3503 
   3504     const rl_components = try astgen.arena.alloc(ResultInfo.Loc.DestructureComponent, full.ast.variables.len);
   3505     for (rl_components, full.ast.variables) |*variable_rl, variable_node| {
   3506         if (node_tags[variable_node] == .identifier) {
   3507             // This intentionally does not support `@"_"` syntax.
   3508             const ident_name = tree.tokenSlice(main_tokens[variable_node]);
   3509             if (mem.eql(u8, ident_name, "_")) {
   3510                 variable_rl.* = .discard;
   3511                 continue;
   3512             }
   3513         }
   3514         variable_rl.* = .{ .typed_ptr = .{
   3515             .inst = try lvalExpr(inner_gz, scope, variable_node),
   3516             .src_node = variable_node,
   3517         } };
   3518     }
   3519 
   3520     const ri: ResultInfo = .{ .rl = .{ .destructure = .{
   3521         .src_node = node,
   3522         .components = rl_components,
   3523     } } };
   3524 
   3525     _ = try expr(inner_gz, scope, ri, full.ast.value_expr);
   3526 
   3527     if (full.comptime_token) |_| {
   3528         const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node);
   3529         _ = try inner_gz.addBreak(.@"break", comptime_block_inst, .void_value);
   3530         try inner_gz.setBlockBody(comptime_block_inst);
   3531         try gz.instructions.append(gz.astgen.gpa, comptime_block_inst);
   3532     }
   3533 }
   3534 
   3535 /// Handles destructure assignments where the LHS may contain `const` or `var` decls.
   3536 fn assignDestructureMaybeDecls(
   3537     gz: *GenZir,
   3538     scope: *Scope,
   3539     node: Ast.Node.Index,
   3540     block_arena: Allocator,
   3541 ) InnerError!*Scope {
   3542     try emitDbgNode(gz, node);
   3543     const astgen = gz.astgen;
   3544     const tree = astgen.tree;
   3545     const token_tags = tree.tokens.items(.tag);
   3546     const main_tokens = tree.nodes.items(.main_token);
   3547     const node_tags = tree.nodes.items(.tag);
   3548 
   3549     const full = tree.assignDestructure(node);
   3550     if (full.comptime_token != null and gz.is_comptime) {
   3551         try astgen.appendErrorNode(node, "redundant comptime keyword in already comptime scope", .{});
   3552     }
   3553 
   3554     const is_comptime = full.comptime_token != null or gz.is_comptime;
   3555     const value_is_comptime = node_tags[full.ast.value_expr] == .@"comptime";
   3556 
   3557     // When declaring consts via a destructure, we always use a result pointer.
   3558     // This avoids the need to create tuple types, and is also likely easier to
   3559     // optimize, since it's a bit tricky for the optimizer to "split up" the
   3560     // value into individual pointer writes down the line.
   3561 
   3562     // We know this rl information won't live past the evaluation of this
   3563     // expression, so it may as well go in the block arena.
   3564     const rl_components = try block_arena.alloc(ResultInfo.Loc.DestructureComponent, full.ast.variables.len);
   3565     var any_non_const_variables = false;
   3566     var any_lvalue_expr = false;
   3567     for (rl_components, full.ast.variables) |*variable_rl, variable_node| {
   3568         switch (node_tags[variable_node]) {
   3569             .identifier => {
   3570                 // This intentionally does not support `@"_"` syntax.
   3571                 const ident_name = tree.tokenSlice(main_tokens[variable_node]);
   3572                 if (mem.eql(u8, ident_name, "_")) {
   3573                     any_non_const_variables = true;
   3574                     variable_rl.* = .discard;
   3575                     continue;
   3576                 }
   3577             },
   3578             .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => {
   3579                 const full_var_decl = tree.fullVarDecl(variable_node).?;
   3580 
   3581                 const name_token = full_var_decl.ast.mut_token + 1;
   3582                 const ident_name_raw = tree.tokenSlice(name_token);
   3583                 if (mem.eql(u8, ident_name_raw, "_")) {
   3584                     return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{});
   3585                 }
   3586 
   3587                 // We detect shadowing in the second pass over these, while we're creating scopes.
   3588 
   3589                 if (full_var_decl.ast.addrspace_node != 0) {
   3590                     return astgen.failTok(main_tokens[full_var_decl.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw});
   3591                 }
   3592                 if (full_var_decl.ast.section_node != 0) {
   3593                     return astgen.failTok(main_tokens[full_var_decl.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw});
   3594                 }
   3595 
   3596                 const is_const = switch (token_tags[full_var_decl.ast.mut_token]) {
   3597                     .keyword_var => false,
   3598                     .keyword_const => true,
   3599                     else => unreachable,
   3600                 };
   3601                 if (!is_const) any_non_const_variables = true;
   3602 
   3603                 // We also mark `const`s as comptime if the RHS is definitely comptime-known.
   3604                 const this_variable_comptime = is_comptime or (is_const and value_is_comptime);
   3605 
   3606                 const align_inst: Zir.Inst.Ref = if (full_var_decl.ast.align_node != 0)
   3607                     try expr(gz, scope, coerced_align_ri, full_var_decl.ast.align_node)
   3608                 else
   3609                     .none;
   3610 
   3611                 if (full_var_decl.ast.type_node != 0) {
   3612                     // Typed alloc
   3613                     const type_inst = try typeExpr(gz, scope, full_var_decl.ast.type_node);
   3614                     const ptr = if (align_inst == .none) ptr: {
   3615                         const tag: Zir.Inst.Tag = if (is_const)
   3616                             .alloc
   3617                         else if (this_variable_comptime)
   3618                             .alloc_comptime_mut
   3619                         else
   3620                             .alloc_mut;
   3621                         break :ptr try gz.addUnNode(tag, type_inst, node);
   3622                     } else try gz.addAllocExtended(.{
   3623                         .node = node,
   3624                         .type_inst = type_inst,
   3625                         .align_inst = align_inst,
   3626                         .is_const = is_const,
   3627                         .is_comptime = this_variable_comptime,
   3628                     });
   3629                     variable_rl.* = .{ .typed_ptr = .{ .inst = ptr } };
   3630                 } else {
   3631                     // Inferred alloc
   3632                     const ptr = if (align_inst == .none) ptr: {
   3633                         const tag: Zir.Inst.Tag = if (is_const) tag: {
   3634                             break :tag if (this_variable_comptime) .alloc_inferred_comptime else .alloc_inferred;
   3635                         } else tag: {
   3636                             break :tag if (this_variable_comptime) .alloc_inferred_comptime_mut else .alloc_inferred_mut;
   3637                         };
   3638                         break :ptr try gz.addNode(tag, node);
   3639                     } else try gz.addAllocExtended(.{
   3640                         .node = node,
   3641                         .type_inst = .none,
   3642                         .align_inst = align_inst,
   3643                         .is_const = is_const,
   3644                         .is_comptime = this_variable_comptime,
   3645                     });
   3646                     variable_rl.* = .{ .inferred_ptr = ptr };
   3647                 }
   3648 
   3649                 continue;
   3650             },
   3651             else => {},
   3652         }
   3653         // This variable is just an lvalue expression.
   3654         // We will fill in its result pointer later, inside a comptime block.
   3655         any_non_const_variables = true;
   3656         any_lvalue_expr = true;
   3657         variable_rl.* = .{ .typed_ptr = .{
   3658             .inst = undefined,
   3659             .src_node = variable_node,
   3660         } };
   3661     }
   3662 
   3663     if (full.comptime_token != null and !any_non_const_variables) {
   3664         try astgen.appendErrorTok(full.comptime_token.?, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
   3665         // Note that this is non-fatal; we will still evaluate at comptime.
   3666     }
   3667 
   3668     // If this expression is marked comptime, we must wrap it in a comptime block.
   3669     var gz_buf: GenZir = undefined;
   3670     const inner_gz = if (full.comptime_token) |_| bs: {
   3671         gz_buf = gz.makeSubBlock(scope);
   3672         gz_buf.is_comptime = true;
   3673         break :bs &gz_buf;
   3674     } else gz;
   3675     defer if (full.comptime_token) |_| inner_gz.unstack();
   3676 
   3677     if (any_lvalue_expr) {
   3678         // At least one variable was an lvalue expr. Iterate again in order to
   3679         // evaluate the lvalues from within the possible block_comptime.
   3680         for (rl_components, full.ast.variables) |*variable_rl, variable_node| {
   3681             if (variable_rl.* != .typed_ptr) continue;
   3682             switch (node_tags[variable_node]) {
   3683                 .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => continue,
   3684                 else => {},
   3685             }
   3686             variable_rl.typed_ptr.inst = try lvalExpr(inner_gz, scope, variable_node);
   3687         }
   3688     }
   3689 
   3690     // We can't give a reasonable anon name strategy for destructured inits, so
   3691     // leave it at its default of `.anon`.
   3692     _ = try reachableExpr(inner_gz, scope, .{ .rl = .{ .destructure = .{
   3693         .src_node = node,
   3694         .components = rl_components,
   3695     } } }, full.ast.value_expr, node);
   3696 
   3697     if (full.comptime_token) |_| {
   3698         // Finish the block_comptime. Inferred alloc resolution etc will occur
   3699         // in the parent block.
   3700         const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node);
   3701         _ = try inner_gz.addBreak(.@"break", comptime_block_inst, .void_value);
   3702         try inner_gz.setBlockBody(comptime_block_inst);
   3703         try gz.instructions.append(gz.astgen.gpa, comptime_block_inst);
   3704     }
   3705 
   3706     // Now, iterate over the variable exprs to construct any new scopes.
   3707     // If there were any inferred allocations, resolve them.
   3708     // If there were any `const` decls, make the pointer constant.
   3709     var cur_scope = scope;
   3710     for (rl_components, full.ast.variables) |variable_rl, variable_node| {
   3711         switch (node_tags[variable_node]) {
   3712             .local_var_decl, .simple_var_decl, .aligned_var_decl => {},
   3713             else => continue, // We were mutating an existing lvalue - nothing to do
   3714         }
   3715         const full_var_decl = tree.fullVarDecl(variable_node).?;
   3716         const raw_ptr, const resolve_inferred = switch (variable_rl) {
   3717             .discard => unreachable,
   3718             .typed_ptr => |typed_ptr| .{ typed_ptr.inst, false },
   3719             .inferred_ptr => |ptr_inst| .{ ptr_inst, true },
   3720         };
   3721         const is_const = switch (token_tags[full_var_decl.ast.mut_token]) {
   3722             .keyword_var => false,
   3723             .keyword_const => true,
   3724             else => unreachable,
   3725         };
   3726 
   3727         // If the alloc was inferred, resolve it. If the alloc was const, make it const.
   3728         const final_ptr = if (resolve_inferred)
   3729             try gz.addUnNode(.resolve_inferred_alloc, raw_ptr, variable_node)
   3730         else if (is_const)
   3731             try gz.addUnNode(.make_ptr_const, raw_ptr, node)
   3732         else
   3733             raw_ptr;
   3734 
   3735         const name_token = full_var_decl.ast.mut_token + 1;
   3736         const ident_name_raw = tree.tokenSlice(name_token);
   3737         const ident_name = try astgen.identAsString(name_token);
   3738         try astgen.detectLocalShadowing(
   3739             cur_scope,
   3740             ident_name,
   3741             name_token,
   3742             ident_name_raw,
   3743             if (is_const) .@"local constant" else .@"local variable",
   3744         );
   3745         try gz.addDbgVar(.dbg_var_ptr, ident_name, final_ptr);
   3746         // Finally, create the scope.
   3747         const sub_scope = try block_arena.create(Scope.LocalPtr);
   3748         sub_scope.* = .{
   3749             .parent = cur_scope,
   3750             .gen_zir = gz,
   3751             .name = ident_name,
   3752             .ptr = final_ptr,
   3753             .token_src = name_token,
   3754             .maybe_comptime = is_const or is_comptime,
   3755             .id_cat = if (is_const) .@"local constant" else .@"local variable",
   3756         };
   3757         cur_scope = &sub_scope.base;
   3758     }
   3759 
   3760     return cur_scope;
   3761 }
   3762 
   3763 fn assignOp(
   3764     gz: *GenZir,
   3765     scope: *Scope,
   3766     infix_node: Ast.Node.Index,
   3767     op_inst_tag: Zir.Inst.Tag,
   3768 ) InnerError!void {
   3769     try emitDbgNode(gz, infix_node);
   3770     const astgen = gz.astgen;
   3771     const tree = astgen.tree;
   3772     const node_datas = tree.nodes.items(.data);
   3773 
   3774     const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
   3775 
   3776     const cursor = switch (op_inst_tag) {
   3777         .add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, infix_node),
   3778         else => undefined,
   3779     };
   3780     const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
   3781 
   3782     const rhs_res_ty = switch (op_inst_tag) {
   3783         .add,
   3784         .sub,
   3785         => try gz.add(.{
   3786             .tag = .extended,
   3787             .data = .{ .extended = .{
   3788                 .opcode = .inplace_arith_result_ty,
   3789                 .small = @intFromEnum(@as(Zir.Inst.InplaceOp, switch (op_inst_tag) {
   3790                     .add => .add_eq,
   3791                     .sub => .sub_eq,
   3792                     else => unreachable,
   3793                 })),
   3794                 .operand = @intFromEnum(lhs),
   3795             } },
   3796         }),
   3797         else => try gz.addUnNode(.typeof, lhs, infix_node), // same as LHS type
   3798     };
   3799     // Not `coerced_ty` since `add`/etc won't coerce to this type.
   3800     const rhs = try expr(gz, scope, .{ .rl = .{ .ty = rhs_res_ty } }, node_datas[infix_node].rhs);
   3801 
   3802     switch (op_inst_tag) {
   3803         .add, .sub, .mul, .div, .mod_rem => {
   3804             try emitDbgStmt(gz, cursor);
   3805         },
   3806         else => {},
   3807     }
   3808     const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{
   3809         .lhs = lhs,
   3810         .rhs = rhs,
   3811     });
   3812     _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{
   3813         .lhs = lhs_ptr,
   3814         .rhs = result,
   3815     });
   3816 }
   3817 
   3818 fn assignShift(
   3819     gz: *GenZir,
   3820     scope: *Scope,
   3821     infix_node: Ast.Node.Index,
   3822     op_inst_tag: Zir.Inst.Tag,
   3823 ) InnerError!void {
   3824     try emitDbgNode(gz, infix_node);
   3825     const astgen = gz.astgen;
   3826     const tree = astgen.tree;
   3827     const node_datas = tree.nodes.items(.data);
   3828 
   3829     const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
   3830     const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
   3831     const rhs_type = try gz.addUnNode(.typeof_log2_int_type, lhs, infix_node);
   3832     const rhs = try expr(gz, scope, .{ .rl = .{ .ty = rhs_type } }, node_datas[infix_node].rhs);
   3833 
   3834     const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{
   3835         .lhs = lhs,
   3836         .rhs = rhs,
   3837     });
   3838     _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{
   3839         .lhs = lhs_ptr,
   3840         .rhs = result,
   3841     });
   3842 }
   3843 
   3844 fn assignShiftSat(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void {
   3845     try emitDbgNode(gz, infix_node);
   3846     const astgen = gz.astgen;
   3847     const tree = astgen.tree;
   3848     const node_datas = tree.nodes.items(.data);
   3849 
   3850     const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
   3851     const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
   3852     // Saturating shift-left allows any integer type for both the LHS and RHS.
   3853     const rhs = try expr(gz, scope, .{ .rl = .none }, node_datas[infix_node].rhs);
   3854 
   3855     const result = try gz.addPlNode(.shl_sat, infix_node, Zir.Inst.Bin{
   3856         .lhs = lhs,
   3857         .rhs = rhs,
   3858     });
   3859     _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{
   3860         .lhs = lhs_ptr,
   3861         .rhs = result,
   3862     });
   3863 }
   3864 
   3865 fn ptrType(
   3866     gz: *GenZir,
   3867     scope: *Scope,
   3868     ri: ResultInfo,
   3869     node: Ast.Node.Index,
   3870     ptr_info: Ast.full.PtrType,
   3871 ) InnerError!Zir.Inst.Ref {
   3872     if (ptr_info.size == .C and ptr_info.allowzero_token != null) {
   3873         return gz.astgen.failTok(ptr_info.allowzero_token.?, "C pointers always allow address zero", .{});
   3874     }
   3875 
   3876     const source_offset = gz.astgen.source_offset;
   3877     const source_line = gz.astgen.source_line;
   3878     const source_column = gz.astgen.source_column;
   3879     const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type);
   3880 
   3881     var sentinel_ref: Zir.Inst.Ref = .none;
   3882     var align_ref: Zir.Inst.Ref = .none;
   3883     var addrspace_ref: Zir.Inst.Ref = .none;
   3884     var bit_start_ref: Zir.Inst.Ref = .none;
   3885     var bit_end_ref: Zir.Inst.Ref = .none;
   3886     var trailing_count: u32 = 0;
   3887 
   3888     if (ptr_info.ast.sentinel != 0) {
   3889         // These attributes can appear in any order and they all come before the
   3890         // element type so we need to reset the source cursor before generating them.
   3891         gz.astgen.source_offset = source_offset;
   3892         gz.astgen.source_line = source_line;
   3893         gz.astgen.source_column = source_column;
   3894 
   3895         sentinel_ref = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, ptr_info.ast.sentinel);
   3896         trailing_count += 1;
   3897     }
   3898     if (ptr_info.ast.addrspace_node != 0) {
   3899         gz.astgen.source_offset = source_offset;
   3900         gz.astgen.source_line = source_line;
   3901         gz.astgen.source_column = source_column;
   3902 
   3903         const addrspace_ty = try gz.addBuiltinValue(ptr_info.ast.addrspace_node, .address_space);
   3904         addrspace_ref = try expr(gz, scope, .{ .rl = .{ .coerced_ty = addrspace_ty } }, ptr_info.ast.addrspace_node);
   3905         trailing_count += 1;
   3906     }
   3907     if (ptr_info.ast.align_node != 0) {
   3908         gz.astgen.source_offset = source_offset;
   3909         gz.astgen.source_line = source_line;
   3910         gz.astgen.source_column = source_column;
   3911 
   3912         align_ref = try expr(gz, scope, coerced_align_ri, ptr_info.ast.align_node);
   3913         trailing_count += 1;
   3914     }
   3915     if (ptr_info.ast.bit_range_start != 0) {
   3916         assert(ptr_info.ast.bit_range_end != 0);
   3917         bit_start_ref = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, ptr_info.ast.bit_range_start);
   3918         bit_end_ref = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, ptr_info.ast.bit_range_end);
   3919         trailing_count += 2;
   3920     }
   3921 
   3922     const gpa = gz.astgen.gpa;
   3923     try gz.instructions.ensureUnusedCapacity(gpa, 1);
   3924     try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
   3925     try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.PtrType).@"struct".fields.len +
   3926         trailing_count);
   3927 
   3928     const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.PtrType{
   3929         .elem_type = elem_type,
   3930         .src_node = gz.nodeIndexToRelative(node),
   3931     });
   3932     if (sentinel_ref != .none) {
   3933         gz.astgen.extra.appendAssumeCapacity(@intFromEnum(sentinel_ref));
   3934     }
   3935     if (align_ref != .none) {
   3936         gz.astgen.extra.appendAssumeCapacity(@intFromEnum(align_ref));
   3937     }
   3938     if (addrspace_ref != .none) {
   3939         gz.astgen.extra.appendAssumeCapacity(@intFromEnum(addrspace_ref));
   3940     }
   3941     if (bit_start_ref != .none) {
   3942         gz.astgen.extra.appendAssumeCapacity(@intFromEnum(bit_start_ref));
   3943         gz.astgen.extra.appendAssumeCapacity(@intFromEnum(bit_end_ref));
   3944     }
   3945 
   3946     const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
   3947     const result = new_index.toRef();
   3948     gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{
   3949         .ptr_type = .{
   3950             .flags = .{
   3951                 .is_allowzero = ptr_info.allowzero_token != null,
   3952                 .is_mutable = ptr_info.const_token == null,
   3953                 .is_volatile = ptr_info.volatile_token != null,
   3954                 .has_sentinel = sentinel_ref != .none,
   3955                 .has_align = align_ref != .none,
   3956                 .has_addrspace = addrspace_ref != .none,
   3957                 .has_bit_range = bit_start_ref != .none,
   3958             },
   3959             .size = ptr_info.size,
   3960             .payload_index = payload_index,
   3961         },
   3962     } });
   3963     gz.instructions.appendAssumeCapacity(new_index);
   3964 
   3965     return rvalue(gz, ri, result, node);
   3966 }
   3967 
   3968 fn arrayType(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) !Zir.Inst.Ref {
   3969     const astgen = gz.astgen;
   3970     const tree = astgen.tree;
   3971     const node_datas = tree.nodes.items(.data);
   3972     const node_tags = tree.nodes.items(.tag);
   3973     const main_tokens = tree.nodes.items(.main_token);
   3974 
   3975     const len_node = node_datas[node].lhs;
   3976     if (node_tags[len_node] == .identifier and
   3977         mem.eql(u8, tree.tokenSlice(main_tokens[len_node]), "_"))
   3978     {
   3979         return astgen.failNode(len_node, "unable to infer array size", .{});
   3980     }
   3981     const len = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, len_node);
   3982     const elem_type = try typeExpr(gz, scope, node_datas[node].rhs);
   3983 
   3984     const result = try gz.addPlNode(.array_type, node, Zir.Inst.Bin{
   3985         .lhs = len,
   3986         .rhs = elem_type,
   3987     });
   3988     return rvalue(gz, ri, result, node);
   3989 }
   3990 
   3991 fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) !Zir.Inst.Ref {
   3992     const astgen = gz.astgen;
   3993     const tree = astgen.tree;
   3994     const node_datas = tree.nodes.items(.data);
   3995     const node_tags = tree.nodes.items(.tag);
   3996     const main_tokens = tree.nodes.items(.main_token);
   3997     const extra = tree.extraData(node_datas[node].rhs, Ast.Node.ArrayTypeSentinel);
   3998 
   3999     const len_node = node_datas[node].lhs;
   4000     if (node_tags[len_node] == .identifier and
   4001         mem.eql(u8, tree.tokenSlice(main_tokens[len_node]), "_"))
   4002     {
   4003         return astgen.failNode(len_node, "unable to infer array size", .{});
   4004     }
   4005     const len = try reachableExpr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, len_node, node);
   4006     const elem_type = try typeExpr(gz, scope, extra.elem_type);
   4007     const sentinel = try reachableExprComptime(gz, scope, .{ .rl = .{ .coerced_ty = elem_type } }, extra.sentinel, node, true);
   4008 
   4009     const result = try gz.addPlNode(.array_type_sentinel, node, Zir.Inst.ArrayTypeSentinel{
   4010         .len = len,
   4011         .elem_type = elem_type,
   4012         .sentinel = sentinel,
   4013     });
   4014     return rvalue(gz, ri, result, node);
   4015 }
   4016 
   4017 const WipMembers = struct {
   4018     payload: *ArrayListUnmanaged(u32),
   4019     payload_top: usize,
   4020     field_bits_start: u32,
   4021     fields_start: u32,
   4022     fields_end: u32,
   4023     decl_index: u32 = 0,
   4024     field_index: u32 = 0,
   4025 
   4026     const Self = @This();
   4027 
   4028     fn init(gpa: Allocator, payload: *ArrayListUnmanaged(u32), decl_count: u32, field_count: u32, comptime bits_per_field: u32, comptime max_field_size: u32) Allocator.Error!Self {
   4029         const payload_top: u32 = @intCast(payload.items.len);
   4030         const field_bits_start = payload_top + decl_count;
   4031         const fields_start = field_bits_start + if (bits_per_field > 0) blk: {
   4032             const fields_per_u32 = 32 / bits_per_field;
   4033             break :blk (field_count + fields_per_u32 - 1) / fields_per_u32;
   4034         } else 0;
   4035         const payload_end = fields_start + field_count * max_field_size;
   4036         try payload.resize(gpa, payload_end);
   4037         return .{
   4038             .payload = payload,
   4039             .payload_top = payload_top,
   4040             .field_bits_start = field_bits_start,
   4041             .fields_start = fields_start,
   4042             .fields_end = fields_start,
   4043         };
   4044     }
   4045 
   4046     fn nextDecl(self: *Self, decl_inst: Zir.Inst.Index) void {
   4047         self.payload.items[self.payload_top + self.decl_index] = @intFromEnum(decl_inst);
   4048         self.decl_index += 1;
   4049     }
   4050 
   4051     fn nextField(self: *Self, comptime bits_per_field: u32, bits: [bits_per_field]bool) void {
   4052         const fields_per_u32 = 32 / bits_per_field;
   4053         const index = self.field_bits_start + self.field_index / fields_per_u32;
   4054         assert(index < self.fields_start);
   4055         var bit_bag: u32 = if (self.field_index % fields_per_u32 == 0) 0 else self.payload.items[index];
   4056         bit_bag >>= bits_per_field;
   4057         comptime var i = 0;
   4058         inline while (i < bits_per_field) : (i += 1) {
   4059             bit_bag |= @as(u32, @intFromBool(bits[i])) << (32 - bits_per_field + i);
   4060         }
   4061         self.payload.items[index] = bit_bag;
   4062         self.field_index += 1;
   4063     }
   4064 
   4065     fn appendToField(self: *Self, data: u32) void {
   4066         assert(self.fields_end < self.payload.items.len);
   4067         self.payload.items[self.fields_end] = data;
   4068         self.fields_end += 1;
   4069     }
   4070 
   4071     fn finishBits(self: *Self, comptime bits_per_field: u32) void {
   4072         if (bits_per_field > 0) {
   4073             const fields_per_u32 = 32 / bits_per_field;
   4074             const empty_field_slots = fields_per_u32 - (self.field_index % fields_per_u32);
   4075             if (self.field_index > 0 and empty_field_slots < fields_per_u32) {
   4076                 const index = self.field_bits_start + self.field_index / fields_per_u32;
   4077                 self.payload.items[index] >>= @intCast(empty_field_slots * bits_per_field);
   4078             }
   4079         }
   4080     }
   4081 
   4082     fn declsSlice(self: *Self) []u32 {
   4083         return self.payload.items[self.payload_top..][0..self.decl_index];
   4084     }
   4085 
   4086     fn fieldsSlice(self: *Self) []u32 {
   4087         return self.payload.items[self.field_bits_start..self.fields_end];
   4088     }
   4089 
   4090     fn deinit(self: *Self) void {
   4091         self.payload.items.len = self.payload_top;
   4092     }
   4093 };
   4094 
   4095 fn fnDecl(
   4096     astgen: *AstGen,
   4097     gz: *GenZir,
   4098     scope: *Scope,
   4099     wip_members: *WipMembers,
   4100     decl_node: Ast.Node.Index,
   4101     body_node: Ast.Node.Index,
   4102     fn_proto: Ast.full.FnProto,
   4103 ) InnerError!void {
   4104     const tree = astgen.tree;
   4105     const token_tags = tree.tokens.items(.tag);
   4106 
   4107     const old_hasher = astgen.src_hasher;
   4108     defer astgen.src_hasher = old_hasher;
   4109     astgen.src_hasher = std.zig.SrcHasher.init(.{});
   4110     // We don't add the full source yet, because we also need the prototype hash!
   4111     // The source slice is added towards the *end* of this function.
   4112     astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
   4113 
   4114     // missing function name already checked in scanContainer()
   4115     const fn_name_token = fn_proto.name_token.?;
   4116 
   4117     // We insert this at the beginning so that its instruction index marks the
   4118     // start of the top level declaration.
   4119     const decl_inst = try gz.makeDeclaration(fn_proto.ast.proto_node);
   4120     astgen.advanceSourceCursorToNode(decl_node);
   4121 
   4122     const saved_cursor = astgen.saveSourceCursor();
   4123 
   4124     var decl_gz: GenZir = .{
   4125         .is_comptime = true,
   4126         .decl_node_index = fn_proto.ast.proto_node,
   4127         .decl_line = astgen.source_line,
   4128         .parent = scope,
   4129         .astgen = astgen,
   4130         .instructions = gz.instructions,
   4131         .instructions_top = gz.instructions.items.len,
   4132     };
   4133     defer decl_gz.unstack();
   4134 
   4135     const decl_column = astgen.source_column;
   4136 
   4137     // Set this now, since parameter types, return type, etc may be generic.
   4138     const prev_within_fn = astgen.within_fn;
   4139     defer astgen.within_fn = prev_within_fn;
   4140     astgen.within_fn = true;
   4141 
   4142     const is_pub = fn_proto.visib_token != null;
   4143     const is_export = blk: {
   4144         const maybe_export_token = fn_proto.extern_export_inline_token orelse break :blk false;
   4145         break :blk token_tags[maybe_export_token] == .keyword_export;
   4146     };
   4147     const is_extern = blk: {
   4148         const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false;
   4149         break :blk token_tags[maybe_extern_token] == .keyword_extern;
   4150     };
   4151     const has_inline_keyword = blk: {
   4152         const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;
   4153         break :blk token_tags[maybe_inline_token] == .keyword_inline;
   4154     };
   4155     const is_noinline = blk: {
   4156         const maybe_noinline_token = fn_proto.extern_export_inline_token orelse break :blk false;
   4157         break :blk token_tags[maybe_noinline_token] == .keyword_noinline;
   4158     };
   4159 
   4160     wip_members.nextDecl(decl_inst);
   4161 
   4162     // Note that the capacity here may not be sufficient, as this does not include `anytype` parameters.
   4163     var param_insts: std.ArrayListUnmanaged(Zir.Inst.Index) = try .initCapacity(astgen.arena, fn_proto.ast.params.len);
   4164 
   4165     var noalias_bits: u32 = 0;
   4166     var params_scope = scope;
   4167     const is_var_args = is_var_args: {
   4168         var param_type_i: usize = 0;
   4169         var it = fn_proto.iterate(tree);
   4170         while (it.next()) |param| : (param_type_i += 1) {
   4171             const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) {
   4172                 .keyword_noalias => is_comptime: {
   4173                     noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse
   4174                         return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
   4175                     break :is_comptime false;
   4176                 },
   4177                 .keyword_comptime => true,
   4178                 else => false,
   4179             } else false;
   4180 
   4181             const is_anytype = if (param.anytype_ellipsis3) |token| blk: {
   4182                 switch (token_tags[token]) {
   4183                     .keyword_anytype => break :blk true,
   4184                     .ellipsis3 => break :is_var_args true,
   4185                     else => unreachable,
   4186                 }
   4187             } else false;
   4188 
   4189             const param_name: Zir.NullTerminatedString = if (param.name_token) |name_token| blk: {
   4190                 const name_bytes = tree.tokenSlice(name_token);
   4191                 if (mem.eql(u8, "_", name_bytes))
   4192                     break :blk .empty;
   4193 
   4194                 const param_name = try astgen.identAsString(name_token);
   4195                 if (!is_extern) {
   4196                     try astgen.detectLocalShadowing(params_scope, param_name, name_token, name_bytes, .@"function parameter");
   4197                 }
   4198                 break :blk param_name;
   4199             } else if (!is_extern) {
   4200                 if (param.anytype_ellipsis3) |tok| {
   4201                     return astgen.failTok(tok, "missing parameter name", .{});
   4202                 } else {
   4203                     ambiguous: {
   4204                         if (tree.nodes.items(.tag)[param.type_expr] != .identifier) break :ambiguous;
   4205                         const main_token = tree.nodes.items(.main_token)[param.type_expr];
   4206                         const identifier_str = tree.tokenSlice(main_token);
   4207                         if (isPrimitive(identifier_str)) break :ambiguous;
   4208                         return astgen.failNodeNotes(
   4209                             param.type_expr,
   4210                             "missing parameter name or type",
   4211                             .{},
   4212                             &[_]u32{
   4213                                 try astgen.errNoteNode(
   4214                                     param.type_expr,
   4215                                     "if this is a name, annotate its type '{s}: T'",
   4216                                     .{identifier_str},
   4217                                 ),
   4218                                 try astgen.errNoteNode(
   4219                                     param.type_expr,
   4220                                     "if this is a type, give it a name '<name>: {s}'",
   4221                                     .{identifier_str},
   4222                                 ),
   4223                             },
   4224                         );
   4225                     }
   4226                     return astgen.failNode(param.type_expr, "missing parameter name", .{});
   4227                 }
   4228             } else .empty;
   4229 
   4230             const param_inst = if (is_anytype) param: {
   4231                 const name_token = param.name_token orelse param.anytype_ellipsis3.?;
   4232                 const tag: Zir.Inst.Tag = if (is_comptime)
   4233                     .param_anytype_comptime
   4234                 else
   4235                     .param_anytype;
   4236                 break :param try decl_gz.addStrTok(tag, param_name, name_token);
   4237             } else param: {
   4238                 const param_type_node = param.type_expr;
   4239                 assert(param_type_node != 0);
   4240                 var param_gz = decl_gz.makeSubBlock(scope);
   4241                 defer param_gz.unstack();
   4242                 const param_type = try fullBodyExpr(&param_gz, params_scope, coerced_type_ri, param_type_node, .normal);
   4243                 const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1);
   4244                 _ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node);
   4245 
   4246                 const main_tokens = tree.nodes.items(.main_token);
   4247                 const name_token = param.name_token orelse main_tokens[param_type_node];
   4248                 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param;
   4249                 const param_inst = try decl_gz.addParam(&param_gz, param_insts.items, tag, name_token, param_name);
   4250                 assert(param_inst_expected == param_inst);
   4251                 break :param param_inst.toRef();
   4252             };
   4253 
   4254             if (param_name == .empty or is_extern) continue;
   4255 
   4256             const sub_scope = try astgen.arena.create(Scope.LocalVal);
   4257             sub_scope.* = .{
   4258                 .parent = params_scope,
   4259                 .gen_zir = &decl_gz,
   4260                 .name = param_name,
   4261                 .inst = param_inst,
   4262                 .token_src = param.name_token.?,
   4263                 .id_cat = .@"function parameter",
   4264             };
   4265             params_scope = &sub_scope.base;
   4266             try param_insts.append(astgen.arena, param_inst.toIndex().?);
   4267         }
   4268         break :is_var_args false;
   4269     };
   4270 
   4271     const lib_name = if (fn_proto.lib_name) |lib_name_token| blk: {
   4272         const lib_name_str = try astgen.strLitAsString(lib_name_token);
   4273         const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len];
   4274         if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) {
   4275             return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{});
   4276         } else if (lib_name_str.len == 0) {
   4277             return astgen.failTok(lib_name_token, "library name cannot be empty", .{});
   4278         }
   4279         break :blk lib_name_str.index;
   4280     } else .empty;
   4281 
   4282     const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
   4283     const is_inferred_error = token_tags[maybe_bang] == .bang;
   4284 
   4285     // After creating the function ZIR instruction, it will need to update the break
   4286     // instructions inside the expression blocks for align, addrspace, cc, and ret_ty
   4287     // to use the function instruction as the "block" to break from.
   4288 
   4289     var ret_gz = decl_gz.makeSubBlock(params_scope);
   4290     defer ret_gz.unstack();
   4291     const ret_ref: Zir.Inst.Ref = inst: {
   4292         // Parameters are in scope for the return type, so we use `params_scope` here.
   4293         // The calling convention will not have parameters in scope, so we'll just use `scope`.
   4294         // See #22263 for a proposal to solve the inconsistency here.
   4295         const inst = try fullBodyExpr(&ret_gz, params_scope, coerced_type_ri, fn_proto.ast.return_type, .normal);
   4296         if (ret_gz.instructionsSlice().len == 0) {
   4297             // In this case we will send a len=0 body which can be encoded more efficiently.
   4298             break :inst inst;
   4299         }
   4300         _ = try ret_gz.addBreak(.break_inline, @enumFromInt(0), inst);
   4301         break :inst inst;
   4302     };
   4303     const ret_body_param_refs = try astgen.fetchRemoveRefEntries(param_insts.items);
   4304 
   4305     // We're jumping back in source, so restore the cursor.
   4306     astgen.restoreSourceCursor(saved_cursor);
   4307 
   4308     var cc_gz = decl_gz.makeSubBlock(scope);
   4309     defer cc_gz.unstack();
   4310     const cc_ref: Zir.Inst.Ref = blk: {
   4311         if (fn_proto.ast.callconv_expr != 0) {
   4312             if (has_inline_keyword) {
   4313                 return astgen.failNode(
   4314                     fn_proto.ast.callconv_expr,
   4315                     "explicit callconv incompatible with inline keyword",
   4316                     .{},
   4317                 );
   4318             }
   4319             const inst = try expr(
   4320                 &cc_gz,
   4321                 scope,
   4322                 .{ .rl = .{ .coerced_ty = try cc_gz.addBuiltinValue(fn_proto.ast.callconv_expr, .calling_convention) } },
   4323                 fn_proto.ast.callconv_expr,
   4324             );
   4325             if (cc_gz.instructionsSlice().len == 0) {
   4326                 // In this case we will send a len=0 body which can be encoded more efficiently.
   4327                 break :blk inst;
   4328             }
   4329             _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst);
   4330             break :blk inst;
   4331         } else if (is_extern) {
   4332             const inst = try cc_gz.addBuiltinValue(decl_node, .calling_convention_c);
   4333             _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst);
   4334             break :blk inst;
   4335         } else if (has_inline_keyword) {
   4336             const inst = try cc_gz.addBuiltinValue(decl_node, .calling_convention_inline);
   4337             _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst);
   4338             break :blk inst;
   4339         } else {
   4340             break :blk .none;
   4341         }
   4342     };
   4343 
   4344     const func_inst: Zir.Inst.Ref = if (body_node == 0) func: {
   4345         if (!is_extern) {
   4346             return astgen.failTok(fn_proto.ast.fn_token, "non-extern function has no body", .{});
   4347         }
   4348         if (is_inferred_error) {
   4349             return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{});
   4350         }
   4351         break :func try decl_gz.addFunc(.{
   4352             .src_node = decl_node,
   4353             .cc_ref = cc_ref,
   4354             .cc_gz = &cc_gz,
   4355             .ret_ref = ret_ref,
   4356             .ret_gz = &ret_gz,
   4357             .ret_param_refs = ret_body_param_refs,
   4358             .param_block = decl_inst,
   4359             .param_insts = param_insts.items,
   4360             .body_gz = null,
   4361             .lib_name = lib_name,
   4362             .is_var_args = is_var_args,
   4363             .is_inferred_error = false,
   4364             .is_test = false,
   4365             .is_extern = true,
   4366             .is_noinline = is_noinline,
   4367             .noalias_bits = noalias_bits,
   4368             .proto_hash = undefined, // ignored for `body_gz == null`
   4369         });
   4370     } else func: {
   4371         var body_gz: GenZir = .{
   4372             .is_comptime = false,
   4373             .decl_node_index = fn_proto.ast.proto_node,
   4374             .decl_line = decl_gz.decl_line,
   4375             .parent = params_scope,
   4376             .astgen = astgen,
   4377             .instructions = gz.instructions,
   4378             .instructions_top = gz.instructions.items.len,
   4379         };
   4380         defer body_gz.unstack();
   4381 
   4382         // We want `params_scope` to be stacked like this:
   4383         // body_gz (top)
   4384         // param2
   4385         // param1
   4386         // param0
   4387         // decl_gz (bottom)
   4388 
   4389         // Construct the prototype hash.
   4390         // Leave `astgen.src_hasher` unmodified; this will be used for hashing
   4391         // the *whole* function declaration, including its body.
   4392         var proto_hasher = astgen.src_hasher;
   4393         const proto_node = tree.nodes.items(.data)[decl_node].lhs;
   4394         proto_hasher.update(tree.getNodeSource(proto_node));
   4395         var proto_hash: std.zig.SrcHash = undefined;
   4396         proto_hasher.final(&proto_hash);
   4397 
   4398         const prev_fn_block = astgen.fn_block;
   4399         const prev_fn_ret_ty = astgen.fn_ret_ty;
   4400         defer {
   4401             astgen.fn_block = prev_fn_block;
   4402             astgen.fn_ret_ty = prev_fn_ret_ty;
   4403         }
   4404         astgen.fn_block = &body_gz;
   4405         astgen.fn_ret_ty = if (is_inferred_error or ret_ref.toIndex() != null) r: {
   4406             // We're essentially guaranteed to need the return type at some point,
   4407             // since the return type is likely not `void` or `noreturn` so there
   4408             // will probably be an explicit return requiring RLS. Fetch this
   4409             // return type now so the rest of the function can use it.
   4410             break :r try body_gz.addNode(.ret_type, decl_node);
   4411         } else ret_ref;
   4412 
   4413         const prev_var_args = astgen.fn_var_args;
   4414         astgen.fn_var_args = is_var_args;
   4415         defer astgen.fn_var_args = prev_var_args;
   4416 
   4417         astgen.advanceSourceCursorToNode(body_node);
   4418         const lbrace_line = astgen.source_line - decl_gz.decl_line;
   4419         const lbrace_column = astgen.source_column;
   4420 
   4421         _ = try fullBodyExpr(&body_gz, &body_gz.base, .{ .rl = .none }, body_node, .allow_branch_hint);
   4422         try checkUsed(gz, scope, params_scope);
   4423 
   4424         if (!body_gz.endsWithNoReturn()) {
   4425             // As our last action before the return, "pop" the error trace if needed
   4426             _ = try body_gz.addRestoreErrRetIndex(.ret, .always, decl_node);
   4427 
   4428             // Add implicit return at end of function.
   4429             _ = try body_gz.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node));
   4430         }
   4431 
   4432         break :func try decl_gz.addFunc(.{
   4433             .src_node = decl_node,
   4434             .cc_ref = cc_ref,
   4435             .cc_gz = &cc_gz,
   4436             .ret_ref = ret_ref,
   4437             .ret_gz = &ret_gz,
   4438             .ret_param_refs = ret_body_param_refs,
   4439             .lbrace_line = lbrace_line,
   4440             .lbrace_column = lbrace_column,
   4441             .param_block = decl_inst,
   4442             .param_insts = param_insts.items,
   4443             .body_gz = &body_gz,
   4444             .lib_name = lib_name,
   4445             .is_var_args = is_var_args,
   4446             .is_inferred_error = is_inferred_error,
   4447             .is_test = false,
   4448             .is_extern = false,
   4449             .is_noinline = is_noinline,
   4450             .noalias_bits = noalias_bits,
   4451             .proto_hash = proto_hash,
   4452         });
   4453     };
   4454 
   4455     // Before we stack more stuff onto `decl_gz`, add its final instruction.
   4456     _ = try decl_gz.addBreak(.break_inline, decl_inst, func_inst);
   4457 
   4458     // Now that `cc_gz,` `ret_gz`, and `body_gz` are unstacked, we evaluate align, addrspace, and linksection.
   4459 
   4460     // We're jumping back in source, so restore the cursor.
   4461     astgen.restoreSourceCursor(saved_cursor);
   4462 
   4463     var align_gz = decl_gz.makeSubBlock(scope);
   4464     defer align_gz.unstack();
   4465     if (fn_proto.ast.align_expr != 0) {
   4466         const inst = try expr(&decl_gz, &decl_gz.base, coerced_align_ri, fn_proto.ast.align_expr);
   4467         _ = try align_gz.addBreak(.break_inline, decl_inst, inst);
   4468     }
   4469 
   4470     var section_gz = align_gz.makeSubBlock(scope);
   4471     defer section_gz.unstack();
   4472     if (fn_proto.ast.section_expr != 0) {
   4473         const inst = try expr(&decl_gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, fn_proto.ast.section_expr);
   4474         _ = try section_gz.addBreak(.break_inline, decl_inst, inst);
   4475     }
   4476 
   4477     var addrspace_gz = section_gz.makeSubBlock(scope);
   4478     defer addrspace_gz.unstack();
   4479     if (fn_proto.ast.addrspace_expr != 0) {
   4480         const addrspace_ty = try decl_gz.addBuiltinValue(fn_proto.ast.addrspace_expr, .address_space);
   4481         const inst = try expr(&decl_gz, scope, .{ .rl = .{ .coerced_ty = addrspace_ty } }, fn_proto.ast.addrspace_expr);
   4482         _ = try addrspace_gz.addBreak(.break_inline, decl_inst, inst);
   4483     }
   4484 
   4485     // *Now* we can incorporate the full source code into the hasher.
   4486     astgen.src_hasher.update(tree.getNodeSource(decl_node));
   4487 
   4488     var hash: std.zig.SrcHash = undefined;
   4489     astgen.src_hasher.final(&hash);
   4490     try setDeclaration(
   4491         decl_inst,
   4492         hash,
   4493         .{ .named = fn_name_token },
   4494         decl_gz.decl_line,
   4495         decl_column,
   4496         is_pub,
   4497         is_export,
   4498         &decl_gz,
   4499         .{
   4500             .align_gz = &align_gz,
   4501             .linksection_gz = &section_gz,
   4502             .addrspace_gz = &addrspace_gz,
   4503         },
   4504     );
   4505 }
   4506 
   4507 fn globalVarDecl(
   4508     astgen: *AstGen,
   4509     gz: *GenZir,
   4510     scope: *Scope,
   4511     wip_members: *WipMembers,
   4512     node: Ast.Node.Index,
   4513     var_decl: Ast.full.VarDecl,
   4514 ) InnerError!void {
   4515     const tree = astgen.tree;
   4516     const token_tags = tree.tokens.items(.tag);
   4517 
   4518     const old_hasher = astgen.src_hasher;
   4519     defer astgen.src_hasher = old_hasher;
   4520     astgen.src_hasher = std.zig.SrcHasher.init(.{});
   4521     astgen.src_hasher.update(tree.getNodeSource(node));
   4522     astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
   4523 
   4524     const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var;
   4525     // We do this at the beginning so that the instruction index marks the range start
   4526     // of the top level declaration.
   4527     const decl_inst = try gz.makeDeclaration(node);
   4528 
   4529     const name_token = var_decl.ast.mut_token + 1;
   4530     astgen.advanceSourceCursorToNode(node);
   4531 
   4532     var block_scope: GenZir = .{
   4533         .parent = scope,
   4534         .decl_node_index = node,
   4535         .decl_line = astgen.source_line,
   4536         .astgen = astgen,
   4537         .is_comptime = true,
   4538         .instructions = gz.instructions,
   4539         .instructions_top = gz.instructions.items.len,
   4540     };
   4541     defer block_scope.unstack();
   4542 
   4543     const decl_column = astgen.source_column;
   4544 
   4545     const is_pub = var_decl.visib_token != null;
   4546     const is_export = blk: {
   4547         const maybe_export_token = var_decl.extern_export_token orelse break :blk false;
   4548         break :blk token_tags[maybe_export_token] == .keyword_export;
   4549     };
   4550     const is_extern = blk: {
   4551         const maybe_extern_token = var_decl.extern_export_token orelse break :blk false;
   4552         break :blk token_tags[maybe_extern_token] == .keyword_extern;
   4553     };
   4554     wip_members.nextDecl(decl_inst);
   4555 
   4556     const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: {
   4557         if (!is_mutable) {
   4558             return astgen.failTok(tok, "threadlocal variable cannot be constant", .{});
   4559         }
   4560         break :blk true;
   4561     } else false;
   4562 
   4563     const lib_name = if (var_decl.lib_name) |lib_name_token| blk: {
   4564         const lib_name_str = try astgen.strLitAsString(lib_name_token);
   4565         const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len];
   4566         if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) {
   4567             return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{});
   4568         } else if (lib_name_str.len == 0) {
   4569             return astgen.failTok(lib_name_token, "library name cannot be empty", .{});
   4570         }
   4571         break :blk lib_name_str.index;
   4572     } else .empty;
   4573 
   4574     assert(var_decl.comptime_token == null); // handled by parser
   4575 
   4576     const var_inst: Zir.Inst.Ref = if (var_decl.ast.init_node != 0) vi: {
   4577         if (is_extern) {
   4578             return astgen.failNode(
   4579                 var_decl.ast.init_node,
   4580                 "extern variables have no initializers",
   4581                 .{},
   4582             );
   4583         }
   4584 
   4585         const type_inst: Zir.Inst.Ref = if (var_decl.ast.type_node != 0)
   4586             try expr(
   4587                 &block_scope,
   4588                 &block_scope.base,
   4589                 coerced_type_ri,
   4590                 var_decl.ast.type_node,
   4591             )
   4592         else
   4593             .none;
   4594 
   4595         block_scope.anon_name_strategy = .parent;
   4596 
   4597         const init_inst = try expr(
   4598             &block_scope,
   4599             &block_scope.base,
   4600             if (type_inst != .none) .{ .rl = .{ .ty = type_inst } } else .{ .rl = .none },
   4601             var_decl.ast.init_node,
   4602         );
   4603 
   4604         if (is_mutable) {
   4605             const var_inst = try block_scope.addVar(.{
   4606                 .var_type = type_inst,
   4607                 .lib_name = .empty,
   4608                 .align_inst = .none, // passed via the decls data
   4609                 .init = init_inst,
   4610                 .is_extern = false,
   4611                 .is_const = !is_mutable,
   4612                 .is_threadlocal = is_threadlocal,
   4613             });
   4614             break :vi var_inst;
   4615         } else {
   4616             break :vi init_inst;
   4617         }
   4618     } else if (!is_extern) {
   4619         return astgen.failNode(node, "variables must be initialized", .{});
   4620     } else if (var_decl.ast.type_node != 0) vi: {
   4621         // Extern variable which has an explicit type.
   4622         const type_inst = try typeExpr(&block_scope, &block_scope.base, var_decl.ast.type_node);
   4623 
   4624         block_scope.anon_name_strategy = .parent;
   4625 
   4626         const var_inst = try block_scope.addVar(.{
   4627             .var_type = type_inst,
   4628             .lib_name = lib_name,
   4629             .align_inst = .none, // passed via the decls data
   4630             .init = .none,
   4631             .is_extern = true,
   4632             .is_const = !is_mutable,
   4633             .is_threadlocal = is_threadlocal,
   4634         });
   4635         break :vi var_inst;
   4636     } else {
   4637         return astgen.failNode(node, "unable to infer variable type", .{});
   4638     };
   4639 
   4640     // We do this at the end so that the instruction index marks the end
   4641     // range of a top level declaration.
   4642     _ = try block_scope.addBreakWithSrcNode(.break_inline, decl_inst, var_inst, node);
   4643 
   4644     var align_gz = block_scope.makeSubBlock(scope);
   4645     if (var_decl.ast.align_node != 0) {
   4646         const align_inst = try fullBodyExpr(&align_gz, &align_gz.base, coerced_align_ri, var_decl.ast.align_node, .normal);
   4647         _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, align_inst, node);
   4648     }
   4649 
   4650     var linksection_gz = align_gz.makeSubBlock(scope);
   4651     if (var_decl.ast.section_node != 0) {
   4652         const linksection_inst = try fullBodyExpr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, var_decl.ast.section_node, .normal);
   4653         _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, linksection_inst, node);
   4654     }
   4655 
   4656     var addrspace_gz = linksection_gz.makeSubBlock(scope);
   4657     if (var_decl.ast.addrspace_node != 0) {
   4658         const addrspace_ty = try addrspace_gz.addBuiltinValue(var_decl.ast.addrspace_node, .address_space);
   4659         const addrspace_inst = try fullBodyExpr(&addrspace_gz, &addrspace_gz.base, .{ .rl = .{ .coerced_ty = addrspace_ty } }, var_decl.ast.addrspace_node, .normal);
   4660         _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, addrspace_inst, node);
   4661     }
   4662 
   4663     var hash: std.zig.SrcHash = undefined;
   4664     astgen.src_hasher.final(&hash);
   4665     try setDeclaration(
   4666         decl_inst,
   4667         hash,
   4668         .{ .named = name_token },
   4669         block_scope.decl_line,
   4670         decl_column,
   4671         is_pub,
   4672         is_export,
   4673         &block_scope,
   4674         .{
   4675             .align_gz = &align_gz,
   4676             .linksection_gz = &linksection_gz,
   4677             .addrspace_gz = &addrspace_gz,
   4678         },
   4679     );
   4680 }
   4681 
   4682 fn comptimeDecl(
   4683     astgen: *AstGen,
   4684     gz: *GenZir,
   4685     scope: *Scope,
   4686     wip_members: *WipMembers,
   4687     node: Ast.Node.Index,
   4688 ) InnerError!void {
   4689     const tree = astgen.tree;
   4690     const node_datas = tree.nodes.items(.data);
   4691     const body_node = node_datas[node].lhs;
   4692 
   4693     const old_hasher = astgen.src_hasher;
   4694     defer astgen.src_hasher = old_hasher;
   4695     astgen.src_hasher = std.zig.SrcHasher.init(.{});
   4696     astgen.src_hasher.update(tree.getNodeSource(node));
   4697     astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
   4698 
   4699     // Up top so the ZIR instruction index marks the start range of this
   4700     // top-level declaration.
   4701     const decl_inst = try gz.makeDeclaration(node);
   4702     wip_members.nextDecl(decl_inst);
   4703     astgen.advanceSourceCursorToNode(node);
   4704 
   4705     var decl_block: GenZir = .{
   4706         .is_comptime = true,
   4707         .decl_node_index = node,
   4708         .decl_line = astgen.source_line,
   4709         .parent = scope,
   4710         .astgen = astgen,
   4711         .instructions = gz.instructions,
   4712         .instructions_top = gz.instructions.items.len,
   4713     };
   4714     defer decl_block.unstack();
   4715 
   4716     const decl_column = astgen.source_column;
   4717 
   4718     const block_result = try fullBodyExpr(&decl_block, &decl_block.base, .{ .rl = .none }, body_node, .normal);
   4719     if (decl_block.isEmpty() or !decl_block.refIsNoReturn(block_result)) {
   4720         _ = try decl_block.addBreak(.break_inline, decl_inst, .void_value);
   4721     }
   4722 
   4723     var hash: std.zig.SrcHash = undefined;
   4724     astgen.src_hasher.final(&hash);
   4725     try setDeclaration(
   4726         decl_inst,
   4727         hash,
   4728         .@"comptime",
   4729         decl_block.decl_line,
   4730         decl_column,
   4731         false,
   4732         false,
   4733         &decl_block,
   4734         null,
   4735     );
   4736 }
   4737 
   4738 fn usingnamespaceDecl(
   4739     astgen: *AstGen,
   4740     gz: *GenZir,
   4741     scope: *Scope,
   4742     wip_members: *WipMembers,
   4743     node: Ast.Node.Index,
   4744 ) InnerError!void {
   4745     const tree = astgen.tree;
   4746     const node_datas = tree.nodes.items(.data);
   4747 
   4748     const old_hasher = astgen.src_hasher;
   4749     defer astgen.src_hasher = old_hasher;
   4750     astgen.src_hasher = std.zig.SrcHasher.init(.{});
   4751     astgen.src_hasher.update(tree.getNodeSource(node));
   4752     astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
   4753 
   4754     const type_expr = node_datas[node].lhs;
   4755     const is_pub = blk: {
   4756         const main_tokens = tree.nodes.items(.main_token);
   4757         const token_tags = tree.tokens.items(.tag);
   4758         const main_token = main_tokens[node];
   4759         break :blk (main_token > 0 and token_tags[main_token - 1] == .keyword_pub);
   4760     };
   4761     // Up top so the ZIR instruction index marks the start range of this
   4762     // top-level declaration.
   4763     const decl_inst = try gz.makeDeclaration(node);
   4764     wip_members.nextDecl(decl_inst);
   4765     astgen.advanceSourceCursorToNode(node);
   4766 
   4767     var decl_block: GenZir = .{
   4768         .is_comptime = true,
   4769         .decl_node_index = node,
   4770         .decl_line = astgen.source_line,
   4771         .parent = scope,
   4772         .astgen = astgen,
   4773         .instructions = gz.instructions,
   4774         .instructions_top = gz.instructions.items.len,
   4775     };
   4776     defer decl_block.unstack();
   4777 
   4778     const decl_column = astgen.source_column;
   4779 
   4780     const namespace_inst = try typeExpr(&decl_block, &decl_block.base, type_expr);
   4781     _ = try decl_block.addBreak(.break_inline, decl_inst, namespace_inst);
   4782 
   4783     var hash: std.zig.SrcHash = undefined;
   4784     astgen.src_hasher.final(&hash);
   4785     try setDeclaration(
   4786         decl_inst,
   4787         hash,
   4788         .@"usingnamespace",
   4789         decl_block.decl_line,
   4790         decl_column,
   4791         is_pub,
   4792         false,
   4793         &decl_block,
   4794         null,
   4795     );
   4796 }
   4797 
   4798 fn testDecl(
   4799     astgen: *AstGen,
   4800     gz: *GenZir,
   4801     scope: *Scope,
   4802     wip_members: *WipMembers,
   4803     node: Ast.Node.Index,
   4804 ) InnerError!void {
   4805     const tree = astgen.tree;
   4806     const node_datas = tree.nodes.items(.data);
   4807     const body_node = node_datas[node].rhs;
   4808 
   4809     const old_hasher = astgen.src_hasher;
   4810     defer astgen.src_hasher = old_hasher;
   4811     astgen.src_hasher = std.zig.SrcHasher.init(.{});
   4812     astgen.src_hasher.update(tree.getNodeSource(node));
   4813     astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
   4814 
   4815     // Up top so the ZIR instruction index marks the start range of this
   4816     // top-level declaration.
   4817     const decl_inst = try gz.makeDeclaration(node);
   4818 
   4819     wip_members.nextDecl(decl_inst);
   4820     astgen.advanceSourceCursorToNode(node);
   4821 
   4822     var decl_block: GenZir = .{
   4823         .is_comptime = true,
   4824         .decl_node_index = node,
   4825         .decl_line = astgen.source_line,
   4826         .parent = scope,
   4827         .astgen = astgen,
   4828         .instructions = gz.instructions,
   4829         .instructions_top = gz.instructions.items.len,
   4830     };
   4831     defer decl_block.unstack();
   4832 
   4833     const decl_column = astgen.source_column;
   4834 
   4835     const main_tokens = tree.nodes.items(.main_token);
   4836     const token_tags = tree.tokens.items(.tag);
   4837     const test_token = main_tokens[node];
   4838     const test_name_token = test_token + 1;
   4839     const test_name: DeclarationName = switch (token_tags[test_name_token]) {
   4840         else => .unnamed_test,
   4841         .string_literal => .{ .named_test = test_name_token },
   4842         .identifier => blk: {
   4843             const ident_name_raw = tree.tokenSlice(test_name_token);
   4844 
   4845             if (mem.eql(u8, ident_name_raw, "_")) return astgen.failTok(test_name_token, "'_' used as an identifier without @\"_\" syntax", .{});
   4846 
   4847             // if not @"" syntax, just use raw token slice
   4848             if (ident_name_raw[0] != '@') {
   4849                 if (isPrimitive(ident_name_raw)) return astgen.failTok(test_name_token, "cannot test a primitive", .{});
   4850             }
   4851 
   4852             // Local variables, including function parameters.
   4853             const name_str_index = try astgen.identAsString(test_name_token);
   4854             var s = scope;
   4855             var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
   4856             var num_namespaces_out: u32 = 0;
   4857             var capturing_namespace: ?*Scope.Namespace = null;
   4858             while (true) switch (s.tag) {
   4859                 .local_val => {
   4860                     const local_val = s.cast(Scope.LocalVal).?;
   4861                     if (local_val.name == name_str_index) {
   4862                         local_val.used = test_name_token;
   4863                         return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{
   4864                             @tagName(local_val.id_cat),
   4865                         }, &[_]u32{
   4866                             try astgen.errNoteTok(local_val.token_src, "{s} declared here", .{
   4867                                 @tagName(local_val.id_cat),
   4868                             }),
   4869                         });
   4870                     }
   4871                     s = local_val.parent;
   4872                 },
   4873                 .local_ptr => {
   4874                     const local_ptr = s.cast(Scope.LocalPtr).?;
   4875                     if (local_ptr.name == name_str_index) {
   4876                         local_ptr.used = test_name_token;
   4877                         return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{
   4878                             @tagName(local_ptr.id_cat),
   4879                         }, &[_]u32{
   4880                             try astgen.errNoteTok(local_ptr.token_src, "{s} declared here", .{
   4881                                 @tagName(local_ptr.id_cat),
   4882                             }),
   4883                         });
   4884                     }
   4885                     s = local_ptr.parent;
   4886                 },
   4887                 .gen_zir => s = s.cast(GenZir).?.parent,
   4888                 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
   4889                 .namespace => {
   4890                     const ns = s.cast(Scope.Namespace).?;
   4891                     if (ns.decls.get(name_str_index)) |i| {
   4892                         if (found_already) |f| {
   4893                             return astgen.failTokNotes(test_name_token, "ambiguous reference", .{}, &.{
   4894                                 try astgen.errNoteNode(f, "declared here", .{}),
   4895                                 try astgen.errNoteNode(i, "also declared here", .{}),
   4896                             });
   4897                         }
   4898                         // We found a match but must continue looking for ambiguous references to decls.
   4899                         found_already = i;
   4900                     }
   4901                     num_namespaces_out += 1;
   4902                     capturing_namespace = ns;
   4903                     s = ns.parent;
   4904                 },
   4905                 .top => break,
   4906             };
   4907             if (found_already == null) {
   4908                 const ident_name = try astgen.identifierTokenString(test_name_token);
   4909                 return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name});
   4910             }
   4911 
   4912             break :blk .{ .decltest = test_name_token };
   4913         },
   4914     };
   4915 
   4916     var fn_block: GenZir = .{
   4917         .is_comptime = false,
   4918         .decl_node_index = node,
   4919         .decl_line = decl_block.decl_line,
   4920         .parent = &decl_block.base,
   4921         .astgen = astgen,
   4922         .instructions = decl_block.instructions,
   4923         .instructions_top = decl_block.instructions.items.len,
   4924     };
   4925     defer fn_block.unstack();
   4926 
   4927     const prev_within_fn = astgen.within_fn;
   4928     const prev_fn_block = astgen.fn_block;
   4929     const prev_fn_ret_ty = astgen.fn_ret_ty;
   4930     astgen.within_fn = true;
   4931     astgen.fn_block = &fn_block;
   4932     astgen.fn_ret_ty = .anyerror_void_error_union_type;
   4933     defer {
   4934         astgen.within_fn = prev_within_fn;
   4935         astgen.fn_block = prev_fn_block;
   4936         astgen.fn_ret_ty = prev_fn_ret_ty;
   4937     }
   4938 
   4939     astgen.advanceSourceCursorToNode(body_node);
   4940     const lbrace_line = astgen.source_line - decl_block.decl_line;
   4941     const lbrace_column = astgen.source_column;
   4942 
   4943     const block_result = try fullBodyExpr(&fn_block, &fn_block.base, .{ .rl = .none }, body_node, .normal);
   4944     if (fn_block.isEmpty() or !fn_block.refIsNoReturn(block_result)) {
   4945 
   4946         // As our last action before the return, "pop" the error trace if needed
   4947         _ = try fn_block.addRestoreErrRetIndex(.ret, .always, node);
   4948 
   4949         // Add implicit return at end of function.
   4950         _ = try fn_block.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node));
   4951     }
   4952 
   4953     const func_inst = try decl_block.addFunc(.{
   4954         .src_node = node,
   4955 
   4956         .cc_ref = .none,
   4957         .cc_gz = null,
   4958         .ret_ref = .anyerror_void_error_union_type,
   4959         .ret_gz = null,
   4960 
   4961         .ret_param_refs = &.{},
   4962         .param_insts = &.{},
   4963 
   4964         .lbrace_line = lbrace_line,
   4965         .lbrace_column = lbrace_column,
   4966         .param_block = decl_inst,
   4967         .body_gz = &fn_block,
   4968         .lib_name = .empty,
   4969         .is_var_args = false,
   4970         .is_inferred_error = false,
   4971         .is_test = true,
   4972         .is_extern = false,
   4973         .is_noinline = false,
   4974         .noalias_bits = 0,
   4975 
   4976         // Tests don't have a prototype that needs hashing
   4977         .proto_hash = .{0} ** 16,
   4978     });
   4979 
   4980     _ = try decl_block.addBreak(.break_inline, decl_inst, func_inst);
   4981 
   4982     var hash: std.zig.SrcHash = undefined;
   4983     astgen.src_hasher.final(&hash);
   4984     try setDeclaration(
   4985         decl_inst,
   4986         hash,
   4987         test_name,
   4988         decl_block.decl_line,
   4989         decl_column,
   4990         false,
   4991         false,
   4992         &decl_block,
   4993         null,
   4994     );
   4995 }
   4996 
   4997 fn structDeclInner(
   4998     gz: *GenZir,
   4999     scope: *Scope,
   5000     node: Ast.Node.Index,
   5001     container_decl: Ast.full.ContainerDecl,
   5002     layout: std.builtin.Type.ContainerLayout,
   5003     backing_int_node: Ast.Node.Index,
   5004 ) InnerError!Zir.Inst.Ref {
   5005     const astgen = gz.astgen;
   5006     const gpa = astgen.gpa;
   5007     const tree = astgen.tree;
   5008 
   5009     {
   5010         const is_tuple = for (container_decl.ast.members) |member_node| {
   5011             const container_field = tree.fullContainerField(member_node) orelse continue;
   5012             if (container_field.ast.tuple_like) break true;
   5013         } else false;
   5014 
   5015         if (is_tuple) {
   5016             if (node == 0) {
   5017                 return astgen.failTok(0, "file cannot be a tuple", .{});
   5018             } else {
   5019                 return tupleDecl(gz, scope, node, container_decl, layout, backing_int_node);
   5020             }
   5021         }
   5022     }
   5023 
   5024     const decl_inst = try gz.reserveInstructionIndex();
   5025 
   5026     if (container_decl.ast.members.len == 0 and backing_int_node == 0) {
   5027         try gz.setStruct(decl_inst, .{
   5028             .src_node = node,
   5029             .layout = layout,
   5030             .captures_len = 0,
   5031             .fields_len = 0,
   5032             .decls_len = 0,
   5033             .has_backing_int = false,
   5034             .known_non_opv = false,
   5035             .known_comptime_only = false,
   5036             .any_comptime_fields = false,
   5037             .any_default_inits = false,
   5038             .any_aligned_fields = false,
   5039             .fields_hash = std.zig.hashSrc(@tagName(layout)),
   5040         });
   5041         return decl_inst.toRef();
   5042     }
   5043 
   5044     var namespace: Scope.Namespace = .{
   5045         .parent = scope,
   5046         .node = node,
   5047         .inst = decl_inst,
   5048         .declaring_gz = gz,
   5049         .maybe_generic = astgen.within_fn,
   5050     };
   5051     defer namespace.deinit(gpa);
   5052 
   5053     // The struct_decl instruction introduces a scope in which the decls of the struct
   5054     // are in scope, so that field types, alignments, and default value expressions
   5055     // can refer to decls within the struct itself.
   5056     astgen.advanceSourceCursorToNode(node);
   5057     var block_scope: GenZir = .{
   5058         .parent = &namespace.base,
   5059         .decl_node_index = node,
   5060         .decl_line = gz.decl_line,
   5061         .astgen = astgen,
   5062         .is_comptime = true,
   5063         .instructions = gz.instructions,
   5064         .instructions_top = gz.instructions.items.len,
   5065     };
   5066     defer block_scope.unstack();
   5067 
   5068     const scratch_top = astgen.scratch.items.len;
   5069     defer astgen.scratch.items.len = scratch_top;
   5070 
   5071     var backing_int_body_len: usize = 0;
   5072     const backing_int_ref: Zir.Inst.Ref = blk: {
   5073         if (backing_int_node != 0) {
   5074             if (layout != .@"packed") {
   5075                 return astgen.failNode(backing_int_node, "non-packed struct does not support backing integer type", .{});
   5076             } else {
   5077                 const backing_int_ref = try typeExpr(&block_scope, &namespace.base, backing_int_node);
   5078                 if (!block_scope.isEmpty()) {
   5079                     if (!block_scope.endsWithNoReturn()) {
   5080                         _ = try block_scope.addBreak(.break_inline, decl_inst, backing_int_ref);
   5081                     }
   5082 
   5083                     const body = block_scope.instructionsSlice();
   5084                     const old_scratch_len = astgen.scratch.items.len;
   5085                     try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
   5086                     appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
   5087                     backing_int_body_len = astgen.scratch.items.len - old_scratch_len;
   5088                     block_scope.instructions.items.len = block_scope.instructions_top;
   5089                 }
   5090                 break :blk backing_int_ref;
   5091             }
   5092         } else {
   5093             break :blk .none;
   5094         }
   5095     };
   5096 
   5097     const decl_count = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"struct");
   5098     const field_count: u32 = @intCast(container_decl.ast.members.len - decl_count);
   5099 
   5100     const bits_per_field = 4;
   5101     const max_field_size = 5;
   5102     var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size);
   5103     defer wip_members.deinit();
   5104 
   5105     // We will use the scratch buffer, starting here, for the bodies:
   5106     //    bodies: { // for every fields_len
   5107     //        field_type_body_inst: Inst, // for each field_type_body_len
   5108     //        align_body_inst: Inst, // for each align_body_len
   5109     //        init_body_inst: Inst, // for each init_body_len
   5110     //    }
   5111     // Note that the scratch buffer is simultaneously being used by WipMembers, however
   5112     // it will not access any elements beyond this point in the ArrayList. It also
   5113     // accesses via the ArrayList items field so it can handle the scratch buffer being
   5114     // reallocated.
   5115     // No defer needed here because it is handled by `wip_members.deinit()` above.
   5116     const bodies_start = astgen.scratch.items.len;
   5117 
   5118     const old_hasher = astgen.src_hasher;
   5119     defer astgen.src_hasher = old_hasher;
   5120     astgen.src_hasher = std.zig.SrcHasher.init(.{});
   5121     astgen.src_hasher.update(@tagName(layout));
   5122     if (backing_int_node != 0) {
   5123         astgen.src_hasher.update(tree.getNodeSource(backing_int_node));
   5124     }
   5125 
   5126     var known_non_opv = false;
   5127     var known_comptime_only = false;
   5128     var any_comptime_fields = false;
   5129     var any_aligned_fields = false;
   5130     var any_default_inits = false;
   5131     for (container_decl.ast.members) |member_node| {
   5132         var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
   5133             .decl => continue,
   5134             .field => |field| field,
   5135         };
   5136 
   5137         astgen.src_hasher.update(tree.getNodeSource(member_node));
   5138 
   5139         const field_name = try astgen.identAsString(member.ast.main_token);
   5140         member.convertToNonTupleLike(astgen.tree.nodes);
   5141         assert(!member.ast.tuple_like);
   5142         wip_members.appendToField(@intFromEnum(field_name));
   5143 
   5144         if (member.ast.type_expr == 0) {
   5145             return astgen.failTok(member.ast.main_token, "struct field missing type", .{});
   5146         }
   5147 
   5148         const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr);
   5149         const have_type_body = !block_scope.isEmpty();
   5150         const have_align = member.ast.align_expr != 0;
   5151         const have_value = member.ast.value_expr != 0;
   5152         const is_comptime = member.comptime_token != null;
   5153 
   5154         if (is_comptime) {
   5155             switch (layout) {
   5156                 .@"packed", .@"extern" => return astgen.failTok(member.comptime_token.?, "{s} struct fields cannot be marked comptime", .{@tagName(layout)}),
   5157                 .auto => any_comptime_fields = true,
   5158             }
   5159         } else {
   5160             known_non_opv = known_non_opv or
   5161                 nodeImpliesMoreThanOnePossibleValue(tree, member.ast.type_expr);
   5162             known_comptime_only = known_comptime_only or
   5163                 nodeImpliesComptimeOnly(tree, member.ast.type_expr);
   5164         }
   5165         wip_members.nextField(bits_per_field, .{ have_align, have_value, is_comptime, have_type_body });
   5166 
   5167         if (have_type_body) {
   5168             if (!block_scope.endsWithNoReturn()) {
   5169                 _ = try block_scope.addBreak(.break_inline, decl_inst, field_type);
   5170             }
   5171             const body = block_scope.instructionsSlice();
   5172             const old_scratch_len = astgen.scratch.items.len;
   5173             try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
   5174             appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
   5175             wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len));
   5176             block_scope.instructions.items.len = block_scope.instructions_top;
   5177         } else {
   5178             wip_members.appendToField(@intFromEnum(field_type));
   5179         }
   5180 
   5181         if (have_align) {
   5182             if (layout == .@"packed") {
   5183                 return astgen.failNode(member.ast.align_expr, "unable to override alignment of packed struct fields", .{});
   5184             }
   5185             any_aligned_fields = true;
   5186             const align_ref = try expr(&block_scope, &namespace.base, coerced_align_ri, member.ast.align_expr);
   5187             if (!block_scope.endsWithNoReturn()) {
   5188                 _ = try block_scope.addBreak(.break_inline, decl_inst, align_ref);
   5189             }
   5190             const body = block_scope.instructionsSlice();
   5191             const old_scratch_len = astgen.scratch.items.len;
   5192             try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
   5193             appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
   5194             wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len));
   5195             block_scope.instructions.items.len = block_scope.instructions_top;
   5196         }
   5197 
   5198         if (have_value) {
   5199             any_default_inits = true;
   5200 
   5201             // The decl_inst is used as here so that we can easily reconstruct a mapping
   5202             // between it and the field type when the fields inits are analyzed.
   5203             const ri: ResultInfo = .{ .rl = if (field_type == .none) .none else .{ .coerced_ty = decl_inst.toRef() } };
   5204 
   5205             const default_inst = try expr(&block_scope, &namespace.base, ri, member.ast.value_expr);
   5206             if (!block_scope.endsWithNoReturn()) {
   5207                 _ = try block_scope.addBreak(.break_inline, decl_inst, default_inst);
   5208             }
   5209             const body = block_scope.instructionsSlice();
   5210             const old_scratch_len = astgen.scratch.items.len;
   5211             try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
   5212             appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
   5213             wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len));
   5214             block_scope.instructions.items.len = block_scope.instructions_top;
   5215         } else if (member.comptime_token) |comptime_token| {
   5216             return astgen.failTok(comptime_token, "comptime field without default initialization value", .{});
   5217         }
   5218     }
   5219 
   5220     var fields_hash: std.zig.SrcHash = undefined;
   5221     astgen.src_hasher.final(&fields_hash);
   5222 
   5223     try gz.setStruct(decl_inst, .{
   5224         .src_node = node,
   5225         .layout = layout,
   5226         .captures_len = @intCast(namespace.captures.count()),
   5227         .fields_len = field_count,
   5228         .decls_len = decl_count,
   5229         .has_backing_int = backing_int_ref != .none,
   5230         .known_non_opv = known_non_opv,
   5231         .known_comptime_only = known_comptime_only,
   5232         .any_comptime_fields = any_comptime_fields,
   5233         .any_default_inits = any_default_inits,
   5234         .any_aligned_fields = any_aligned_fields,
   5235         .fields_hash = fields_hash,
   5236     });
   5237 
   5238     wip_members.finishBits(bits_per_field);
   5239     const decls_slice = wip_members.declsSlice();
   5240     const fields_slice = wip_members.fieldsSlice();
   5241     const bodies_slice = astgen.scratch.items[bodies_start..];
   5242     try astgen.extra.ensureUnusedCapacity(gpa, backing_int_body_len + 2 +
   5243         decls_slice.len + namespace.captures.count() + fields_slice.len + bodies_slice.len);
   5244     astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys()));
   5245     if (backing_int_ref != .none) {
   5246         astgen.extra.appendAssumeCapacity(@intCast(backing_int_body_len));
   5247         if (backing_int_body_len == 0) {
   5248             astgen.extra.appendAssumeCapacity(@intFromEnum(backing_int_ref));
   5249         } else {
   5250             astgen.extra.appendSliceAssumeCapacity(astgen.scratch.items[scratch_top..][0..backing_int_body_len]);
   5251         }
   5252     }
   5253     astgen.extra.appendSliceAssumeCapacity(decls_slice);
   5254     astgen.extra.appendSliceAssumeCapacity(fields_slice);
   5255     astgen.extra.appendSliceAssumeCapacity(bodies_slice);
   5256 
   5257     block_scope.unstack();
   5258     return decl_inst.toRef();
   5259 }
   5260 
   5261 fn tupleDecl(
   5262     gz: *GenZir,
   5263     scope: *Scope,
   5264     node: Ast.Node.Index,
   5265     container_decl: Ast.full.ContainerDecl,
   5266     layout: std.builtin.Type.ContainerLayout,
   5267     backing_int_node: Ast.Node.Index,
   5268 ) InnerError!Zir.Inst.Ref {
   5269     const astgen = gz.astgen;
   5270     const gpa = astgen.gpa;
   5271     const tree = astgen.tree;
   5272 
   5273     const node_tags = tree.nodes.items(.tag);
   5274 
   5275     switch (layout) {
   5276         .auto => {},
   5277         .@"extern", .@"packed" => return astgen.failNode(node, "{s} tuples are not supported", .{@tagName(layout)}),
   5278     }
   5279 
   5280     if (backing_int_node != 0) {
   5281         return astgen.failNode(backing_int_node, "tuple does not support backing integer type", .{});
   5282     }
   5283 
   5284     // We will use the scratch buffer, starting here, for the field data:
   5285     // 1. fields: { // for every `fields_len` (stored in `extended.small`)
   5286     //        type: Inst.Ref,
   5287     //        init: Inst.Ref, // `.none` for non-`comptime` fields
   5288     //    }
   5289     const fields_start = astgen.scratch.items.len;
   5290     defer astgen.scratch.items.len = fields_start;
   5291 
   5292     try astgen.scratch.ensureUnusedCapacity(gpa, container_decl.ast.members.len * 2);
   5293 
   5294     for (container_decl.ast.members) |member_node| {
   5295         const field = tree.fullContainerField(member_node) orelse {
   5296             const tuple_member = for (container_decl.ast.members) |maybe_tuple| switch (node_tags[maybe_tuple]) {
   5297                 .container_field_init,
   5298                 .container_field_align,
   5299                 .container_field,
   5300                 => break maybe_tuple,
   5301                 else => {},
   5302             } else unreachable;
   5303             return astgen.failNodeNotes(
   5304                 member_node,
   5305                 "tuple declarations cannot contain declarations",
   5306                 .{},
   5307                 &.{try astgen.errNoteNode(tuple_member, "tuple field here", .{})},
   5308             );
   5309         };
   5310 
   5311         if (!field.ast.tuple_like) {
   5312             return astgen.failTok(field.ast.main_token, "tuple field has a name", .{});
   5313         }
   5314 
   5315         if (field.ast.align_expr != 0) {
   5316             return astgen.failTok(field.ast.main_token, "tuple field has alignment", .{});
   5317         }
   5318 
   5319         if (field.ast.value_expr != 0 and field.comptime_token == null) {
   5320             return astgen.failTok(field.ast.main_token, "non-comptime tuple field has default initialization value", .{});
   5321         }
   5322 
   5323         if (field.ast.value_expr == 0 and field.comptime_token != null) {
   5324             return astgen.failTok(field.comptime_token.?, "comptime field without default initialization value", .{});
   5325         }
   5326 
   5327         const field_type_ref = try typeExpr(gz, scope, field.ast.type_expr);
   5328         astgen.scratch.appendAssumeCapacity(@intFromEnum(field_type_ref));
   5329 
   5330         if (field.ast.value_expr != 0) {
   5331             const field_init_ref = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = field_type_ref } }, field.ast.value_expr);
   5332             astgen.scratch.appendAssumeCapacity(@intFromEnum(field_init_ref));
   5333         } else {
   5334             astgen.scratch.appendAssumeCapacity(@intFromEnum(Zir.Inst.Ref.none));
   5335         }
   5336     }
   5337 
   5338     const fields_len = std.math.cast(u16, container_decl.ast.members.len) orelse {
   5339         return astgen.failNode(node, "this compiler implementation only supports 65535 tuple fields", .{});
   5340     };
   5341 
   5342     const extra_trail = astgen.scratch.items[fields_start..];
   5343     assert(extra_trail.len == fields_len * 2);
   5344     try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.TupleDecl).@"struct".fields.len + extra_trail.len);
   5345     const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.TupleDecl{
   5346         .src_node = gz.nodeIndexToRelative(node),
   5347     });
   5348     astgen.extra.appendSliceAssumeCapacity(extra_trail);
   5349 
   5350     return gz.add(.{
   5351         .tag = .extended,
   5352         .data = .{ .extended = .{
   5353             .opcode = .tuple_decl,
   5354             .small = fields_len,
   5355             .operand = payload_index,
   5356         } },
   5357     });
   5358 }
   5359 
   5360 fn unionDeclInner(
   5361     gz: *GenZir,
   5362     scope: *Scope,
   5363     node: Ast.Node.Index,
   5364     members: []const Ast.Node.Index,
   5365     layout: std.builtin.Type.ContainerLayout,
   5366     arg_node: Ast.Node.Index,
   5367     auto_enum_tok: ?Ast.TokenIndex,
   5368 ) InnerError!Zir.Inst.Ref {
   5369     const decl_inst = try gz.reserveInstructionIndex();
   5370 
   5371     const astgen = gz.astgen;
   5372     const gpa = astgen.gpa;
   5373 
   5374     var namespace: Scope.Namespace = .{
   5375         .parent = scope,
   5376         .node = node,
   5377         .inst = decl_inst,
   5378         .declaring_gz = gz,
   5379         .maybe_generic = astgen.within_fn,
   5380     };
   5381     defer namespace.deinit(gpa);
   5382 
   5383     // The union_decl instruction introduces a scope in which the decls of the union
   5384     // are in scope, so that field types, alignments, and default value expressions
   5385     // can refer to decls within the union itself.
   5386     astgen.advanceSourceCursorToNode(node);
   5387     var block_scope: GenZir = .{
   5388         .parent = &namespace.base,
   5389         .decl_node_index = node,
   5390         .decl_line = gz.decl_line,
   5391         .astgen = astgen,
   5392         .is_comptime = true,
   5393         .instructions = gz.instructions,
   5394         .instructions_top = gz.instructions.items.len,
   5395     };
   5396     defer block_scope.unstack();
   5397 
   5398     const decl_count = try astgen.scanContainer(&namespace, members, .@"union");
   5399     const field_count: u32 = @intCast(members.len - decl_count);
   5400 
   5401     if (layout != .auto and (auto_enum_tok != null or arg_node != 0)) {
   5402         if (arg_node != 0) {
   5403             return astgen.failNode(arg_node, "{s} union does not support enum tag type", .{@tagName(layout)});
   5404         } else {
   5405             return astgen.failTok(auto_enum_tok.?, "{s} union does not support enum tag type", .{@tagName(layout)});
   5406         }
   5407     }
   5408 
   5409     const arg_inst: Zir.Inst.Ref = if (arg_node != 0)
   5410         try typeExpr(&block_scope, &namespace.base, arg_node)
   5411     else
   5412         .none;
   5413 
   5414     const bits_per_field = 4;
   5415     const max_field_size = 4;
   5416     var any_aligned_fields = false;
   5417     var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size);
   5418     defer wip_members.deinit();
   5419 
   5420     const old_hasher = astgen.src_hasher;
   5421     defer astgen.src_hasher = old_hasher;
   5422     astgen.src_hasher = std.zig.SrcHasher.init(.{});
   5423     astgen.src_hasher.update(@tagName(layout));
   5424     astgen.src_hasher.update(&.{@intFromBool(auto_enum_tok != null)});
   5425     if (arg_node != 0) {
   5426         astgen.src_hasher.update(astgen.tree.getNodeSource(arg_node));
   5427     }
   5428 
   5429     for (members) |member_node| {
   5430         var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
   5431             .decl => continue,
   5432             .field => |field| field,
   5433         };
   5434         astgen.src_hasher.update(astgen.tree.getNodeSource(member_node));
   5435         member.convertToNonTupleLike(astgen.tree.nodes);
   5436         if (member.ast.tuple_like) {
   5437             return astgen.failTok(member.ast.main_token, "union field missing name", .{});
   5438         }
   5439         if (member.comptime_token) |comptime_token| {
   5440             return astgen.failTok(comptime_token, "union fields cannot be marked comptime", .{});
   5441         }
   5442 
   5443         const field_name = try astgen.identAsString(member.ast.main_token);
   5444         wip_members.appendToField(@intFromEnum(field_name));
   5445 
   5446         const have_type = member.ast.type_expr != 0;
   5447         const have_align = member.ast.align_expr != 0;
   5448         const have_value = member.ast.value_expr != 0;
   5449         const unused = false;
   5450         wip_members.nextField(bits_per_field, .{ have_type, have_align, have_value, unused });
   5451 
   5452         if (have_type) {
   5453             const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr);
   5454             wip_members.appendToField(@intFromEnum(field_type));
   5455         } else if (arg_inst == .none and auto_enum_tok == null) {
   5456             return astgen.failNode(member_node, "union field missing type", .{});
   5457         }
   5458         if (have_align) {
   5459             const align_inst = try expr(&block_scope, &block_scope.base, coerced_align_ri, member.ast.align_expr);
   5460             wip_members.appendToField(@intFromEnum(align_inst));
   5461             any_aligned_fields = true;
   5462         }
   5463         if (have_value) {
   5464             if (arg_inst == .none) {
   5465                 return astgen.failNodeNotes(
   5466                     node,
   5467                     "explicitly valued tagged union missing integer tag type",
   5468                     .{},
   5469                     &[_]u32{
   5470                         try astgen.errNoteNode(
   5471                             member.ast.value_expr,
   5472                             "tag value specified here",
   5473                             .{},
   5474                         ),
   5475                     },
   5476                 );
   5477             }
   5478             if (auto_enum_tok == null) {
   5479                 return astgen.failNodeNotes(
   5480                     node,
   5481                     "explicitly valued tagged union requires inferred enum tag type",
   5482                     .{},
   5483                     &[_]u32{
   5484                         try astgen.errNoteNode(
   5485                             member.ast.value_expr,
   5486                             "tag value specified here",
   5487                             .{},
   5488                         ),
   5489                     },
   5490                 );
   5491             }
   5492             const tag_value = try expr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = arg_inst } }, member.ast.value_expr);
   5493             wip_members.appendToField(@intFromEnum(tag_value));
   5494         }
   5495     }
   5496 
   5497     var fields_hash: std.zig.SrcHash = undefined;
   5498     astgen.src_hasher.final(&fields_hash);
   5499 
   5500     if (!block_scope.isEmpty()) {
   5501         _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
   5502     }
   5503 
   5504     const body = block_scope.instructionsSlice();
   5505     const body_len = astgen.countBodyLenAfterFixups(body);
   5506 
   5507     try gz.setUnion(decl_inst, .{
   5508         .src_node = node,
   5509         .layout = layout,
   5510         .tag_type = arg_inst,
   5511         .captures_len = @intCast(namespace.captures.count()),
   5512         .body_len = body_len,
   5513         .fields_len = field_count,
   5514         .decls_len = decl_count,
   5515         .auto_enum_tag = auto_enum_tok != null,
   5516         .any_aligned_fields = any_aligned_fields,
   5517         .fields_hash = fields_hash,
   5518     });
   5519 
   5520     wip_members.finishBits(bits_per_field);
   5521     const decls_slice = wip_members.declsSlice();
   5522     const fields_slice = wip_members.fieldsSlice();
   5523     try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() + decls_slice.len + body_len + fields_slice.len);
   5524     astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys()));
   5525     astgen.extra.appendSliceAssumeCapacity(decls_slice);
   5526     astgen.appendBodyWithFixups(body);
   5527     astgen.extra.appendSliceAssumeCapacity(fields_slice);
   5528 
   5529     block_scope.unstack();
   5530     return decl_inst.toRef();
   5531 }
   5532 
   5533 fn containerDecl(
   5534     gz: *GenZir,
   5535     scope: *Scope,
   5536     ri: ResultInfo,
   5537     node: Ast.Node.Index,
   5538     container_decl: Ast.full.ContainerDecl,
   5539 ) InnerError!Zir.Inst.Ref {
   5540     const astgen = gz.astgen;
   5541     const gpa = astgen.gpa;
   5542     const tree = astgen.tree;
   5543     const token_tags = tree.tokens.items(.tag);
   5544 
   5545     const prev_fn_block = astgen.fn_block;
   5546     astgen.fn_block = null;
   5547     defer astgen.fn_block = prev_fn_block;
   5548 
   5549     // We must not create any types until Sema. Here the goal is only to generate
   5550     // ZIR for all the field types, alignments, and default value expressions.
   5551 
   5552     switch (token_tags[container_decl.ast.main_token]) {
   5553         .keyword_struct => {
   5554             const layout: std.builtin.Type.ContainerLayout = if (container_decl.layout_token) |t| switch (token_tags[t]) {
   5555                 .keyword_packed => .@"packed",
   5556                 .keyword_extern => .@"extern",
   5557                 else => unreachable,
   5558             } else .auto;
   5559 
   5560             const result = try structDeclInner(gz, scope, node, container_decl, layout, container_decl.ast.arg);
   5561             return rvalue(gz, ri, result, node);
   5562         },
   5563         .keyword_union => {
   5564             const layout: std.builtin.Type.ContainerLayout = if (container_decl.layout_token) |t| switch (token_tags[t]) {
   5565                 .keyword_packed => .@"packed",
   5566                 .keyword_extern => .@"extern",
   5567                 else => unreachable,
   5568             } else .auto;
   5569 
   5570             const result = try unionDeclInner(gz, scope, node, container_decl.ast.members, layout, container_decl.ast.arg, container_decl.ast.enum_token);
   5571             return rvalue(gz, ri, result, node);
   5572         },
   5573         .keyword_enum => {
   5574             if (container_decl.layout_token) |t| {
   5575                 return astgen.failTok(t, "enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", .{});
   5576             }
   5577             // Count total fields as well as how many have explicitly provided tag values.
   5578             const counts = blk: {
   5579                 var values: usize = 0;
   5580                 var total_fields: usize = 0;
   5581                 var decls: usize = 0;
   5582                 var nonexhaustive_node: Ast.Node.Index = 0;
   5583                 var nonfinal_nonexhaustive = false;
   5584                 for (container_decl.ast.members) |member_node| {
   5585                     var member = tree.fullContainerField(member_node) orelse {
   5586                         decls += 1;
   5587                         continue;
   5588                     };
   5589                     member.convertToNonTupleLike(astgen.tree.nodes);
   5590                     if (member.ast.tuple_like) {
   5591                         return astgen.failTok(member.ast.main_token, "enum field missing name", .{});
   5592                     }
   5593                     if (member.comptime_token) |comptime_token| {
   5594                         return astgen.failTok(comptime_token, "enum fields cannot be marked comptime", .{});
   5595                     }
   5596                     if (member.ast.type_expr != 0) {
   5597                         return astgen.failNodeNotes(
   5598                             member.ast.type_expr,
   5599                             "enum fields do not have types",
   5600                             .{},
   5601                             &[_]u32{
   5602                                 try astgen.errNoteNode(
   5603                                     node,
   5604                                     "consider 'union(enum)' here to make it a tagged union",
   5605                                     .{},
   5606                                 ),
   5607                             },
   5608                         );
   5609                     }
   5610                     if (member.ast.align_expr != 0) {
   5611                         return astgen.failNode(member.ast.align_expr, "enum fields cannot be aligned", .{});
   5612                     }
   5613 
   5614                     const name_token = member.ast.main_token;
   5615                     if (mem.eql(u8, tree.tokenSlice(name_token), "_")) {
   5616                         if (nonexhaustive_node != 0) {
   5617                             return astgen.failNodeNotes(
   5618                                 member_node,
   5619                                 "redundant non-exhaustive enum mark",
   5620                                 .{},
   5621                                 &[_]u32{
   5622                                     try astgen.errNoteNode(
   5623                                         nonexhaustive_node,
   5624                                         "other mark here",
   5625                                         .{},
   5626                                     ),
   5627                                 },
   5628                             );
   5629                         }
   5630                         nonexhaustive_node = member_node;
   5631                         if (member.ast.value_expr != 0) {
   5632                             return astgen.failNode(member.ast.value_expr, "'_' is used to mark an enum as non-exhaustive and cannot be assigned a value", .{});
   5633                         }
   5634                         continue;
   5635                     } else if (nonexhaustive_node != 0) {
   5636                         nonfinal_nonexhaustive = true;
   5637                     }
   5638                     total_fields += 1;
   5639                     if (member.ast.value_expr != 0) {
   5640                         if (container_decl.ast.arg == 0) {
   5641                             return astgen.failNode(member.ast.value_expr, "value assigned to enum tag with inferred tag type", .{});
   5642                         }
   5643                         values += 1;
   5644                     }
   5645                 }
   5646                 if (nonfinal_nonexhaustive) {
   5647                     return astgen.failNode(nonexhaustive_node, "'_' field of non-exhaustive enum must be last", .{});
   5648                 }
   5649                 break :blk .{
   5650                     .total_fields = total_fields,
   5651                     .values = values,
   5652                     .decls = decls,
   5653                     .nonexhaustive_node = nonexhaustive_node,
   5654                 };
   5655             };
   5656             if (counts.nonexhaustive_node != 0 and container_decl.ast.arg == 0) {
   5657                 return astgen.failNodeNotes(
   5658                     node,
   5659                     "non-exhaustive enum missing integer tag type",
   5660                     .{},
   5661                     &[_]u32{
   5662                         try astgen.errNoteNode(
   5663                             counts.nonexhaustive_node,
   5664                             "marked non-exhaustive here",
   5665                             .{},
   5666                         ),
   5667                     },
   5668                 );
   5669             }
   5670             // In this case we must generate ZIR code for the tag values, similar to
   5671             // how structs are handled above.
   5672             const nonexhaustive = counts.nonexhaustive_node != 0;
   5673 
   5674             const decl_inst = try gz.reserveInstructionIndex();
   5675 
   5676             var namespace: Scope.Namespace = .{
   5677                 .parent = scope,
   5678                 .node = node,
   5679                 .inst = decl_inst,
   5680                 .declaring_gz = gz,
   5681                 .maybe_generic = astgen.within_fn,
   5682             };
   5683             defer namespace.deinit(gpa);
   5684 
   5685             // The enum_decl instruction introduces a scope in which the decls of the enum
   5686             // are in scope, so that tag values can refer to decls within the enum itself.
   5687             astgen.advanceSourceCursorToNode(node);
   5688             var block_scope: GenZir = .{
   5689                 .parent = &namespace.base,
   5690                 .decl_node_index = node,
   5691                 .decl_line = gz.decl_line,
   5692                 .astgen = astgen,
   5693                 .is_comptime = true,
   5694                 .instructions = gz.instructions,
   5695                 .instructions_top = gz.instructions.items.len,
   5696             };
   5697             defer block_scope.unstack();
   5698 
   5699             _ = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"enum");
   5700             namespace.base.tag = .namespace;
   5701 
   5702             const arg_inst: Zir.Inst.Ref = if (container_decl.ast.arg != 0)
   5703                 try comptimeExpr(&block_scope, &namespace.base, coerced_type_ri, container_decl.ast.arg)
   5704             else
   5705                 .none;
   5706 
   5707             const bits_per_field = 1;
   5708             const max_field_size = 2;
   5709             var wip_members = try WipMembers.init(gpa, &astgen.scratch, @intCast(counts.decls), @intCast(counts.total_fields), bits_per_field, max_field_size);
   5710             defer wip_members.deinit();
   5711 
   5712             const old_hasher = astgen.src_hasher;
   5713             defer astgen.src_hasher = old_hasher;
   5714             astgen.src_hasher = std.zig.SrcHasher.init(.{});
   5715             if (container_decl.ast.arg != 0) {
   5716                 astgen.src_hasher.update(tree.getNodeSource(container_decl.ast.arg));
   5717             }
   5718             astgen.src_hasher.update(&.{@intFromBool(nonexhaustive)});
   5719 
   5720             for (container_decl.ast.members) |member_node| {
   5721                 if (member_node == counts.nonexhaustive_node)
   5722                     continue;
   5723                 astgen.src_hasher.update(tree.getNodeSource(member_node));
   5724                 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
   5725                     .decl => continue,
   5726                     .field => |field| field,
   5727                 };
   5728                 member.convertToNonTupleLike(astgen.tree.nodes);
   5729                 assert(member.comptime_token == null);
   5730                 assert(member.ast.type_expr == 0);
   5731                 assert(member.ast.align_expr == 0);
   5732 
   5733                 const field_name = try astgen.identAsString(member.ast.main_token);
   5734                 wip_members.appendToField(@intFromEnum(field_name));
   5735 
   5736                 const have_value = member.ast.value_expr != 0;
   5737                 wip_members.nextField(bits_per_field, .{have_value});
   5738 
   5739                 if (have_value) {
   5740                     if (arg_inst == .none) {
   5741                         return astgen.failNodeNotes(
   5742                             node,
   5743                             "explicitly valued enum missing integer tag type",
   5744                             .{},
   5745                             &[_]u32{
   5746                                 try astgen.errNoteNode(
   5747                                     member.ast.value_expr,
   5748                                     "tag value specified here",
   5749                                     .{},
   5750                                 ),
   5751                             },
   5752                         );
   5753                     }
   5754                     const tag_value_inst = try expr(&block_scope, &namespace.base, .{ .rl = .{ .ty = arg_inst } }, member.ast.value_expr);
   5755                     wip_members.appendToField(@intFromEnum(tag_value_inst));
   5756                 }
   5757             }
   5758 
   5759             if (!block_scope.isEmpty()) {
   5760                 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
   5761             }
   5762 
   5763             var fields_hash: std.zig.SrcHash = undefined;
   5764             astgen.src_hasher.final(&fields_hash);
   5765 
   5766             const body = block_scope.instructionsSlice();
   5767             const body_len = astgen.countBodyLenAfterFixups(body);
   5768 
   5769             try gz.setEnum(decl_inst, .{
   5770                 .src_node = node,
   5771                 .nonexhaustive = nonexhaustive,
   5772                 .tag_type = arg_inst,
   5773                 .captures_len = @intCast(namespace.captures.count()),
   5774                 .body_len = body_len,
   5775                 .fields_len = @intCast(counts.total_fields),
   5776                 .decls_len = @intCast(counts.decls),
   5777                 .fields_hash = fields_hash,
   5778             });
   5779 
   5780             wip_members.finishBits(bits_per_field);
   5781             const decls_slice = wip_members.declsSlice();
   5782             const fields_slice = wip_members.fieldsSlice();
   5783             try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() + decls_slice.len + body_len + fields_slice.len);
   5784             astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys()));
   5785             astgen.extra.appendSliceAssumeCapacity(decls_slice);
   5786             astgen.appendBodyWithFixups(body);
   5787             astgen.extra.appendSliceAssumeCapacity(fields_slice);
   5788 
   5789             block_scope.unstack();
   5790             return rvalue(gz, ri, decl_inst.toRef(), node);
   5791         },
   5792         .keyword_opaque => {
   5793             assert(container_decl.ast.arg == 0);
   5794 
   5795             const decl_inst = try gz.reserveInstructionIndex();
   5796 
   5797             var namespace: Scope.Namespace = .{
   5798                 .parent = scope,
   5799                 .node = node,
   5800                 .inst = decl_inst,
   5801                 .declaring_gz = gz,
   5802                 .maybe_generic = astgen.within_fn,
   5803             };
   5804             defer namespace.deinit(gpa);
   5805 
   5806             astgen.advanceSourceCursorToNode(node);
   5807             var block_scope: GenZir = .{
   5808                 .parent = &namespace.base,
   5809                 .decl_node_index = node,
   5810                 .decl_line = gz.decl_line,
   5811                 .astgen = astgen,
   5812                 .is_comptime = true,
   5813                 .instructions = gz.instructions,
   5814                 .instructions_top = gz.instructions.items.len,
   5815             };
   5816             defer block_scope.unstack();
   5817 
   5818             const decl_count = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"opaque");
   5819 
   5820             var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, 0, 0, 0);
   5821             defer wip_members.deinit();
   5822 
   5823             for (container_decl.ast.members) |member_node| {
   5824                 const res = try containerMember(&block_scope, &namespace.base, &wip_members, member_node);
   5825                 if (res == .field) {
   5826                     return astgen.failNode(member_node, "opaque types cannot have fields", .{});
   5827                 }
   5828             }
   5829 
   5830             try gz.setOpaque(decl_inst, .{
   5831                 .src_node = node,
   5832                 .captures_len = @intCast(namespace.captures.count()),
   5833                 .decls_len = decl_count,
   5834             });
   5835 
   5836             wip_members.finishBits(0);
   5837             const decls_slice = wip_members.declsSlice();
   5838             try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() + decls_slice.len);
   5839             astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys()));
   5840             astgen.extra.appendSliceAssumeCapacity(decls_slice);
   5841 
   5842             block_scope.unstack();
   5843             return rvalue(gz, ri, decl_inst.toRef(), node);
   5844         },
   5845         else => unreachable,
   5846     }
   5847 }
   5848 
   5849 const ContainerMemberResult = union(enum) { decl, field: Ast.full.ContainerField };
   5850 
   5851 fn containerMember(
   5852     gz: *GenZir,
   5853     scope: *Scope,
   5854     wip_members: *WipMembers,
   5855     member_node: Ast.Node.Index,
   5856 ) InnerError!ContainerMemberResult {
   5857     const astgen = gz.astgen;
   5858     const tree = astgen.tree;
   5859     const node_tags = tree.nodes.items(.tag);
   5860     const node_datas = tree.nodes.items(.data);
   5861     switch (node_tags[member_node]) {
   5862         .container_field_init,
   5863         .container_field_align,
   5864         .container_field,
   5865         => return ContainerMemberResult{ .field = tree.fullContainerField(member_node).? },
   5866 
   5867         .fn_proto,
   5868         .fn_proto_multi,
   5869         .fn_proto_one,
   5870         .fn_proto_simple,
   5871         .fn_decl,
   5872         => {
   5873             var buf: [1]Ast.Node.Index = undefined;
   5874             const full = tree.fullFnProto(&buf, member_node).?;
   5875             const body = if (node_tags[member_node] == .fn_decl) node_datas[member_node].rhs else 0;
   5876 
   5877             const prev_decl_index = wip_members.decl_index;
   5878             astgen.fnDecl(gz, scope, wip_members, member_node, body, full) catch |err| switch (err) {
   5879                 error.OutOfMemory => return error.OutOfMemory,
   5880                 error.AnalysisFail => {
   5881                     wip_members.decl_index = prev_decl_index;
   5882                     try addFailedDeclaration(
   5883                         wip_members,
   5884                         gz,
   5885                         .{ .named = full.name_token.? },
   5886                         full.ast.proto_node,
   5887                         full.visib_token != null,
   5888                     );
   5889                 },
   5890             };
   5891         },
   5892 
   5893         .global_var_decl,
   5894         .local_var_decl,
   5895         .simple_var_decl,
   5896         .aligned_var_decl,
   5897         => {
   5898             const full = tree.fullVarDecl(member_node).?;
   5899             const prev_decl_index = wip_members.decl_index;
   5900             astgen.globalVarDecl(gz, scope, wip_members, member_node, full) catch |err| switch (err) {
   5901                 error.OutOfMemory => return error.OutOfMemory,
   5902                 error.AnalysisFail => {
   5903                     wip_members.decl_index = prev_decl_index;
   5904                     try addFailedDeclaration(
   5905                         wip_members,
   5906                         gz,
   5907                         .{ .named = full.ast.mut_token + 1 },
   5908                         member_node,
   5909                         full.visib_token != null,
   5910                     );
   5911                 },
   5912             };
   5913         },
   5914 
   5915         .@"comptime" => {
   5916             const prev_decl_index = wip_members.decl_index;
   5917             astgen.comptimeDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
   5918                 error.OutOfMemory => return error.OutOfMemory,
   5919                 error.AnalysisFail => {
   5920                     wip_members.decl_index = prev_decl_index;
   5921                     try addFailedDeclaration(
   5922                         wip_members,
   5923                         gz,
   5924                         .@"comptime",
   5925                         member_node,
   5926                         false,
   5927                     );
   5928                 },
   5929             };
   5930         },
   5931         .@"usingnamespace" => {
   5932             const prev_decl_index = wip_members.decl_index;
   5933             astgen.usingnamespaceDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
   5934                 error.OutOfMemory => return error.OutOfMemory,
   5935                 error.AnalysisFail => {
   5936                     wip_members.decl_index = prev_decl_index;
   5937                     try addFailedDeclaration(
   5938                         wip_members,
   5939                         gz,
   5940                         .@"usingnamespace",
   5941                         member_node,
   5942                         is_pub: {
   5943                             const main_tokens = tree.nodes.items(.main_token);
   5944                             const token_tags = tree.tokens.items(.tag);
   5945                             const main_token = main_tokens[member_node];
   5946                             break :is_pub main_token > 0 and token_tags[main_token - 1] == .keyword_pub;
   5947                         },
   5948                     );
   5949                 },
   5950             };
   5951         },
   5952         .test_decl => {
   5953             const prev_decl_index = wip_members.decl_index;
   5954             // We need to have *some* decl here so that the decl count matches what's expected.
   5955             // Since it doesn't strictly matter *what* this is, let's save ourselves the trouble
   5956             // of duplicating the test name logic, and just assume this is an unnamed test.
   5957             astgen.testDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
   5958                 error.OutOfMemory => return error.OutOfMemory,
   5959                 error.AnalysisFail => {
   5960                     wip_members.decl_index = prev_decl_index;
   5961                     try addFailedDeclaration(
   5962                         wip_members,
   5963                         gz,
   5964                         .unnamed_test,
   5965                         member_node,
   5966                         false,
   5967                     );
   5968                 },
   5969             };
   5970         },
   5971         else => unreachable,
   5972     }
   5973     return .decl;
   5974 }
   5975 
   5976 fn errorSetDecl(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   5977     const astgen = gz.astgen;
   5978     const gpa = astgen.gpa;
   5979     const tree = astgen.tree;
   5980     const main_tokens = tree.nodes.items(.main_token);
   5981     const token_tags = tree.tokens.items(.tag);
   5982 
   5983     const payload_index = try reserveExtra(astgen, @typeInfo(Zir.Inst.ErrorSetDecl).@"struct".fields.len);
   5984     var fields_len: usize = 0;
   5985     {
   5986         var idents: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.TokenIndex) = .empty;
   5987         defer idents.deinit(gpa);
   5988 
   5989         const error_token = main_tokens[node];
   5990         var tok_i = error_token + 2;
   5991         while (true) : (tok_i += 1) {
   5992             switch (token_tags[tok_i]) {
   5993                 .doc_comment, .comma => {},
   5994                 .identifier => {
   5995                     const str_index = try astgen.identAsString(tok_i);
   5996                     const gop = try idents.getOrPut(gpa, str_index);
   5997                     if (gop.found_existing) {
   5998                         const name = try gpa.dupe(u8, mem.span(astgen.nullTerminatedString(str_index)));
   5999                         defer gpa.free(name);
   6000                         return astgen.failTokNotes(
   6001                             tok_i,
   6002                             "duplicate error set field '{s}'",
   6003                             .{name},
   6004                             &[_]u32{
   6005                                 try astgen.errNoteTok(
   6006                                     gop.value_ptr.*,
   6007                                     "previous declaration here",
   6008                                     .{},
   6009                                 ),
   6010                             },
   6011                         );
   6012                     }
   6013                     gop.value_ptr.* = tok_i;
   6014 
   6015                     try astgen.extra.append(gpa, @intFromEnum(str_index));
   6016                     fields_len += 1;
   6017                 },
   6018                 .r_brace => break,
   6019                 else => unreachable,
   6020             }
   6021         }
   6022     }
   6023 
   6024     setExtra(astgen, payload_index, Zir.Inst.ErrorSetDecl{
   6025         .fields_len = @intCast(fields_len),
   6026     });
   6027     const result = try gz.addPlNodePayloadIndex(.error_set_decl, node, payload_index);
   6028     return rvalue(gz, ri, result, node);
   6029 }
   6030 
   6031 fn tryExpr(
   6032     parent_gz: *GenZir,
   6033     scope: *Scope,
   6034     ri: ResultInfo,
   6035     node: Ast.Node.Index,
   6036     operand_node: Ast.Node.Index,
   6037 ) InnerError!Zir.Inst.Ref {
   6038     const astgen = parent_gz.astgen;
   6039 
   6040     const fn_block = astgen.fn_block orelse {
   6041         return astgen.failNode(node, "'try' outside function scope", .{});
   6042     };
   6043 
   6044     if (parent_gz.any_defer_node != 0) {
   6045         return astgen.failNodeNotes(node, "'try' not allowed inside defer expression", .{}, &.{
   6046             try astgen.errNoteNode(
   6047                 parent_gz.any_defer_node,
   6048                 "defer expression here",
   6049                 .{},
   6050             ),
   6051         });
   6052     }
   6053 
   6054     // Ensure debug line/column information is emitted for this try expression.
   6055     // Then we will save the line/column so that we can emit another one that goes
   6056     // "backwards" because we want to evaluate the operand, but then put the debug
   6057     // info back at the try keyword for error return tracing.
   6058     if (!parent_gz.is_comptime) {
   6059         try emitDbgNode(parent_gz, node);
   6060     }
   6061     const try_lc: LineColumn = .{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
   6062 
   6063     const operand_rl: ResultInfo.Loc, const block_tag: Zir.Inst.Tag = switch (ri.rl) {
   6064         .ref => .{ .ref, .try_ptr },
   6065         .ref_coerced_ty => |payload_ptr_ty| .{
   6066             .{ .ref_coerced_ty = try parent_gz.addUnNode(.try_ref_operand_ty, payload_ptr_ty, node) },
   6067             .try_ptr,
   6068         },
   6069         else => if (try ri.rl.resultType(parent_gz, node)) |payload_ty| .{
   6070             // `coerced_ty` is OK due to the `rvalue` call below
   6071             .{ .coerced_ty = try parent_gz.addUnNode(.try_operand_ty, payload_ty, node) },
   6072             .@"try",
   6073         } else .{ .none, .@"try" },
   6074     };
   6075     const operand_ri: ResultInfo = .{ .rl = operand_rl, .ctx = .error_handling_expr };
   6076     // This could be a pointer or value depending on the `ri` parameter.
   6077     const operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, node);
   6078     const try_inst = try parent_gz.makeBlockInst(block_tag, node);
   6079     try parent_gz.instructions.append(astgen.gpa, try_inst);
   6080 
   6081     var else_scope = parent_gz.makeSubBlock(scope);
   6082     defer else_scope.unstack();
   6083 
   6084     const err_tag = switch (ri.rl) {
   6085         .ref, .ref_coerced_ty => Zir.Inst.Tag.err_union_code_ptr,
   6086         else => Zir.Inst.Tag.err_union_code,
   6087     };
   6088     const err_code = try else_scope.addUnNode(err_tag, operand, node);
   6089     try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code });
   6090     try emitDbgStmt(&else_scope, try_lc);
   6091     _ = try else_scope.addUnNode(.ret_node, err_code, node);
   6092 
   6093     try else_scope.setTryBody(try_inst, operand);
   6094     const result = try_inst.toRef();
   6095     switch (ri.rl) {
   6096         .ref, .ref_coerced_ty => return result,
   6097         else => return rvalue(parent_gz, ri, result, node),
   6098     }
   6099 }
   6100 
   6101 fn orelseCatchExpr(
   6102     parent_gz: *GenZir,
   6103     scope: *Scope,
   6104     ri: ResultInfo,
   6105     node: Ast.Node.Index,
   6106     lhs: Ast.Node.Index,
   6107     cond_op: Zir.Inst.Tag,
   6108     unwrap_op: Zir.Inst.Tag,
   6109     unwrap_code_op: Zir.Inst.Tag,
   6110     rhs: Ast.Node.Index,
   6111     payload_token: ?Ast.TokenIndex,
   6112 ) InnerError!Zir.Inst.Ref {
   6113     const astgen = parent_gz.astgen;
   6114     const tree = astgen.tree;
   6115 
   6116     const need_rl = astgen.nodes_need_rl.contains(node);
   6117     const block_ri: ResultInfo = if (need_rl) ri else .{
   6118         .rl = switch (ri.rl) {
   6119             .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
   6120             .inferred_ptr => .none,
   6121             else => ri.rl,
   6122         },
   6123         .ctx = ri.ctx,
   6124     };
   6125     // We need to call `rvalue` to write through to the pointer only if we had a
   6126     // result pointer and aren't forwarding it.
   6127     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   6128     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   6129 
   6130     const do_err_trace = astgen.fn_block != null and (cond_op == .is_non_err or cond_op == .is_non_err_ptr);
   6131 
   6132     var block_scope = parent_gz.makeSubBlock(scope);
   6133     block_scope.setBreakResultInfo(block_ri);
   6134     defer block_scope.unstack();
   6135 
   6136     const operand_ri: ResultInfo = switch (block_scope.break_result_info.rl) {
   6137         .ref, .ref_coerced_ty => .{ .rl = .ref, .ctx = if (do_err_trace) .error_handling_expr else .none },
   6138         else => .{ .rl = .none, .ctx = if (do_err_trace) .error_handling_expr else .none },
   6139     };
   6140     // This could be a pointer or value depending on the `operand_ri` parameter.
   6141     // We cannot use `block_scope.break_result_info` because that has the bare
   6142     // type, whereas this expression has the optional type. Later we make
   6143     // up for this fact by calling rvalue on the else branch.
   6144     const operand = try reachableExpr(&block_scope, &block_scope.base, operand_ri, lhs, rhs);
   6145     const cond = try block_scope.addUnNode(cond_op, operand, node);
   6146     const condbr = try block_scope.addCondBr(.condbr, node);
   6147 
   6148     const block = try parent_gz.makeBlockInst(.block, node);
   6149     try block_scope.setBlockBody(block);
   6150     // block_scope unstacked now, can add new instructions to parent_gz
   6151     try parent_gz.instructions.append(astgen.gpa, block);
   6152 
   6153     var then_scope = block_scope.makeSubBlock(scope);
   6154     defer then_scope.unstack();
   6155 
   6156     // This could be a pointer or value depending on `unwrap_op`.
   6157     const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node);
   6158     const then_result = switch (ri.rl) {
   6159         .ref, .ref_coerced_ty => unwrapped_payload,
   6160         else => try rvalue(&then_scope, block_scope.break_result_info, unwrapped_payload, node),
   6161     };
   6162     _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, node);
   6163 
   6164     var else_scope = block_scope.makeSubBlock(scope);
   6165     defer else_scope.unstack();
   6166 
   6167     // We know that the operand (almost certainly) modified the error return trace,
   6168     // so signal to Sema that it should save the new index for restoring later.
   6169     if (do_err_trace and nodeMayAppendToErrorTrace(tree, lhs))
   6170         _ = try else_scope.addSaveErrRetIndex(.always);
   6171 
   6172     var err_val_scope: Scope.LocalVal = undefined;
   6173     const else_sub_scope = blk: {
   6174         const payload = payload_token orelse break :blk &else_scope.base;
   6175         const err_str = tree.tokenSlice(payload);
   6176         if (mem.eql(u8, err_str, "_")) {
   6177             try astgen.appendErrorTok(payload, "discard of error capture; omit it instead", .{});
   6178             break :blk &else_scope.base;
   6179         }
   6180         const err_name = try astgen.identAsString(payload);
   6181 
   6182         try astgen.detectLocalShadowing(scope, err_name, payload, err_str, .capture);
   6183 
   6184         err_val_scope = .{
   6185             .parent = &else_scope.base,
   6186             .gen_zir = &else_scope,
   6187             .name = err_name,
   6188             .inst = try else_scope.addUnNode(unwrap_code_op, operand, node),
   6189             .token_src = payload,
   6190             .id_cat = .capture,
   6191         };
   6192         break :blk &err_val_scope.base;
   6193     };
   6194 
   6195     const else_result = try fullBodyExpr(&else_scope, else_sub_scope, block_scope.break_result_info, rhs, .allow_branch_hint);
   6196     if (!else_scope.endsWithNoReturn()) {
   6197         // As our last action before the break, "pop" the error trace if needed
   6198         if (do_err_trace)
   6199             try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, rhs, else_result);
   6200 
   6201         _ = try else_scope.addBreakWithSrcNode(.@"break", block, else_result, rhs);
   6202     }
   6203     try checkUsed(parent_gz, &else_scope.base, else_sub_scope);
   6204 
   6205     try setCondBrPayload(condbr, cond, &then_scope, &else_scope);
   6206 
   6207     if (need_result_rvalue) {
   6208         return rvalue(parent_gz, ri, block.toRef(), node);
   6209     } else {
   6210         return block.toRef();
   6211     }
   6212 }
   6213 
   6214 /// Return whether the identifier names of two tokens are equal. Resolves @""
   6215 /// tokens without allocating.
   6216 /// OK in theory it could do it without allocating. This implementation
   6217 /// allocates when the @"" form is used.
   6218 fn tokenIdentEql(astgen: *AstGen, token1: Ast.TokenIndex, token2: Ast.TokenIndex) !bool {
   6219     const ident_name_1 = try astgen.identifierTokenString(token1);
   6220     const ident_name_2 = try astgen.identifierTokenString(token2);
   6221     return mem.eql(u8, ident_name_1, ident_name_2);
   6222 }
   6223 
   6224 fn fieldAccess(
   6225     gz: *GenZir,
   6226     scope: *Scope,
   6227     ri: ResultInfo,
   6228     node: Ast.Node.Index,
   6229 ) InnerError!Zir.Inst.Ref {
   6230     switch (ri.rl) {
   6231         .ref, .ref_coerced_ty => return addFieldAccess(.field_ptr, gz, scope, .{ .rl = .ref }, node),
   6232         else => {
   6233             const access = try addFieldAccess(.field_val, gz, scope, .{ .rl = .none }, node);
   6234             return rvalue(gz, ri, access, node);
   6235         },
   6236     }
   6237 }
   6238 
   6239 fn addFieldAccess(
   6240     tag: Zir.Inst.Tag,
   6241     gz: *GenZir,
   6242     scope: *Scope,
   6243     lhs_ri: ResultInfo,
   6244     node: Ast.Node.Index,
   6245 ) InnerError!Zir.Inst.Ref {
   6246     const astgen = gz.astgen;
   6247     const tree = astgen.tree;
   6248     const main_tokens = tree.nodes.items(.main_token);
   6249     const node_datas = tree.nodes.items(.data);
   6250 
   6251     const object_node = node_datas[node].lhs;
   6252     const dot_token = main_tokens[node];
   6253     const field_ident = dot_token + 1;
   6254     const str_index = try astgen.identAsString(field_ident);
   6255     const lhs = try expr(gz, scope, lhs_ri, object_node);
   6256 
   6257     const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   6258     try emitDbgStmt(gz, cursor);
   6259 
   6260     return gz.addPlNode(tag, node, Zir.Inst.Field{
   6261         .lhs = lhs,
   6262         .field_name_start = str_index,
   6263     });
   6264 }
   6265 
   6266 fn arrayAccess(
   6267     gz: *GenZir,
   6268     scope: *Scope,
   6269     ri: ResultInfo,
   6270     node: Ast.Node.Index,
   6271 ) InnerError!Zir.Inst.Ref {
   6272     const tree = gz.astgen.tree;
   6273     const node_datas = tree.nodes.items(.data);
   6274     switch (ri.rl) {
   6275         .ref, .ref_coerced_ty => {
   6276             const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
   6277 
   6278             const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   6279 
   6280             const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs);
   6281             try emitDbgStmt(gz, cursor);
   6282 
   6283             return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
   6284         },
   6285         else => {
   6286             const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs);
   6287 
   6288             const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   6289 
   6290             const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs);
   6291             try emitDbgStmt(gz, cursor);
   6292 
   6293             return rvalue(gz, ri, try gz.addPlNode(.elem_val_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }), node);
   6294         },
   6295     }
   6296 }
   6297 
   6298 fn simpleBinOp(
   6299     gz: *GenZir,
   6300     scope: *Scope,
   6301     ri: ResultInfo,
   6302     node: Ast.Node.Index,
   6303     op_inst_tag: Zir.Inst.Tag,
   6304 ) InnerError!Zir.Inst.Ref {
   6305     const astgen = gz.astgen;
   6306     const tree = astgen.tree;
   6307     const node_datas = tree.nodes.items(.data);
   6308 
   6309     if (op_inst_tag == .cmp_neq or op_inst_tag == .cmp_eq) {
   6310         const node_tags = tree.nodes.items(.tag);
   6311         const str = if (op_inst_tag == .cmp_eq) "==" else "!=";
   6312         if (node_tags[node_datas[node].lhs] == .string_literal or
   6313             node_tags[node_datas[node].rhs] == .string_literal)
   6314             return astgen.failNode(node, "cannot compare strings with {s}", .{str});
   6315     }
   6316 
   6317     const lhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].lhs, node);
   6318     const cursor = switch (op_inst_tag) {
   6319         .add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, node),
   6320         else => undefined,
   6321     };
   6322     const rhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].rhs, node);
   6323 
   6324     switch (op_inst_tag) {
   6325         .add, .sub, .mul, .div, .mod_rem => {
   6326             try emitDbgStmt(gz, cursor);
   6327         },
   6328         else => {},
   6329     }
   6330     const result = try gz.addPlNode(op_inst_tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
   6331     return rvalue(gz, ri, result, node);
   6332 }
   6333 
   6334 fn simpleStrTok(
   6335     gz: *GenZir,
   6336     ri: ResultInfo,
   6337     ident_token: Ast.TokenIndex,
   6338     node: Ast.Node.Index,
   6339     op_inst_tag: Zir.Inst.Tag,
   6340 ) InnerError!Zir.Inst.Ref {
   6341     const astgen = gz.astgen;
   6342     const str_index = try astgen.identAsString(ident_token);
   6343     const result = try gz.addStrTok(op_inst_tag, str_index, ident_token);
   6344     return rvalue(gz, ri, result, node);
   6345 }
   6346 
   6347 fn boolBinOp(
   6348     gz: *GenZir,
   6349     scope: *Scope,
   6350     ri: ResultInfo,
   6351     node: Ast.Node.Index,
   6352     zir_tag: Zir.Inst.Tag,
   6353 ) InnerError!Zir.Inst.Ref {
   6354     const astgen = gz.astgen;
   6355     const tree = astgen.tree;
   6356     const node_datas = tree.nodes.items(.data);
   6357 
   6358     const lhs = try expr(gz, scope, coerced_bool_ri, node_datas[node].lhs);
   6359     const bool_br = (try gz.addPlNodePayloadIndex(zir_tag, node, undefined)).toIndex().?;
   6360 
   6361     var rhs_scope = gz.makeSubBlock(scope);
   6362     defer rhs_scope.unstack();
   6363     const rhs = try fullBodyExpr(&rhs_scope, &rhs_scope.base, coerced_bool_ri, node_datas[node].rhs, .allow_branch_hint);
   6364     if (!gz.refIsNoReturn(rhs)) {
   6365         _ = try rhs_scope.addBreakWithSrcNode(.break_inline, bool_br, rhs, node_datas[node].rhs);
   6366     }
   6367     try rhs_scope.setBoolBrBody(bool_br, lhs);
   6368 
   6369     const block_ref = bool_br.toRef();
   6370     return rvalue(gz, ri, block_ref, node);
   6371 }
   6372 
   6373 fn ifExpr(
   6374     parent_gz: *GenZir,
   6375     scope: *Scope,
   6376     ri: ResultInfo,
   6377     node: Ast.Node.Index,
   6378     if_full: Ast.full.If,
   6379 ) InnerError!Zir.Inst.Ref {
   6380     const astgen = parent_gz.astgen;
   6381     const tree = astgen.tree;
   6382     const token_tags = tree.tokens.items(.tag);
   6383 
   6384     const do_err_trace = astgen.fn_block != null and if_full.error_token != null;
   6385 
   6386     const need_rl = astgen.nodes_need_rl.contains(node);
   6387     const block_ri: ResultInfo = if (need_rl) ri else .{
   6388         .rl = switch (ri.rl) {
   6389             .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
   6390             .inferred_ptr => .none,
   6391             else => ri.rl,
   6392         },
   6393         .ctx = ri.ctx,
   6394     };
   6395     // We need to call `rvalue` to write through to the pointer only if we had a
   6396     // result pointer and aren't forwarding it.
   6397     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   6398     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   6399 
   6400     var block_scope = parent_gz.makeSubBlock(scope);
   6401     block_scope.setBreakResultInfo(block_ri);
   6402     defer block_scope.unstack();
   6403 
   6404     const payload_is_ref = if (if_full.payload_token) |payload_token|
   6405         token_tags[payload_token] == .asterisk
   6406     else
   6407         false;
   6408 
   6409     try emitDbgNode(parent_gz, if_full.ast.cond_expr);
   6410     const cond: struct {
   6411         inst: Zir.Inst.Ref,
   6412         bool_bit: Zir.Inst.Ref,
   6413     } = c: {
   6414         if (if_full.error_token) |_| {
   6415             const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none, .ctx = .error_handling_expr };
   6416             const err_union = try expr(&block_scope, &block_scope.base, cond_ri, if_full.ast.cond_expr);
   6417             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
   6418             break :c .{
   6419                 .inst = err_union,
   6420                 .bool_bit = try block_scope.addUnNode(tag, err_union, if_full.ast.cond_expr),
   6421             };
   6422         } else if (if_full.payload_token) |_| {
   6423             const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
   6424             const optional = try expr(&block_scope, &block_scope.base, cond_ri, if_full.ast.cond_expr);
   6425             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
   6426             break :c .{
   6427                 .inst = optional,
   6428                 .bool_bit = try block_scope.addUnNode(tag, optional, if_full.ast.cond_expr),
   6429             };
   6430         } else {
   6431             const cond = try expr(&block_scope, &block_scope.base, coerced_bool_ri, if_full.ast.cond_expr);
   6432             break :c .{
   6433                 .inst = cond,
   6434                 .bool_bit = cond,
   6435             };
   6436         }
   6437     };
   6438 
   6439     const condbr = try block_scope.addCondBr(.condbr, node);
   6440 
   6441     const block = try parent_gz.makeBlockInst(.block, node);
   6442     try block_scope.setBlockBody(block);
   6443     // block_scope unstacked now, can add new instructions to parent_gz
   6444     try parent_gz.instructions.append(astgen.gpa, block);
   6445 
   6446     var then_scope = parent_gz.makeSubBlock(scope);
   6447     defer then_scope.unstack();
   6448 
   6449     var payload_val_scope: Scope.LocalVal = undefined;
   6450 
   6451     const then_node = if_full.ast.then_expr;
   6452     const then_sub_scope = s: {
   6453         if (if_full.error_token != null) {
   6454             if (if_full.payload_token) |payload_token| {
   6455                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   6456                     .err_union_payload_unsafe_ptr
   6457                 else
   6458                     .err_union_payload_unsafe;
   6459                 const payload_inst = try then_scope.addUnNode(tag, cond.inst, then_node);
   6460                 const token_name_index = payload_token + @intFromBool(payload_is_ref);
   6461                 const ident_name = try astgen.identAsString(token_name_index);
   6462                 const token_name_str = tree.tokenSlice(token_name_index);
   6463                 if (mem.eql(u8, "_", token_name_str)) {
   6464                     if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   6465                     break :s &then_scope.base;
   6466                 }
   6467                 try astgen.detectLocalShadowing(&then_scope.base, ident_name, token_name_index, token_name_str, .capture);
   6468                 payload_val_scope = .{
   6469                     .parent = &then_scope.base,
   6470                     .gen_zir = &then_scope,
   6471                     .name = ident_name,
   6472                     .inst = payload_inst,
   6473                     .token_src = token_name_index,
   6474                     .id_cat = .capture,
   6475                 };
   6476                 try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
   6477                 break :s &payload_val_scope.base;
   6478             } else {
   6479                 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node);
   6480                 break :s &then_scope.base;
   6481             }
   6482         } else if (if_full.payload_token) |payload_token| {
   6483             const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
   6484             const tag: Zir.Inst.Tag = if (payload_is_ref)
   6485                 .optional_payload_unsafe_ptr
   6486             else
   6487                 .optional_payload_unsafe;
   6488             const ident_bytes = tree.tokenSlice(ident_token);
   6489             if (mem.eql(u8, "_", ident_bytes)) {
   6490                 if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   6491                 break :s &then_scope.base;
   6492             }
   6493             const payload_inst = try then_scope.addUnNode(tag, cond.inst, then_node);
   6494             const ident_name = try astgen.identAsString(ident_token);
   6495             try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture);
   6496             payload_val_scope = .{
   6497                 .parent = &then_scope.base,
   6498                 .gen_zir = &then_scope,
   6499                 .name = ident_name,
   6500                 .inst = payload_inst,
   6501                 .token_src = ident_token,
   6502                 .id_cat = .capture,
   6503             };
   6504             try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
   6505             break :s &payload_val_scope.base;
   6506         } else {
   6507             break :s &then_scope.base;
   6508         }
   6509     };
   6510 
   6511     const then_result = try fullBodyExpr(&then_scope, then_sub_scope, block_scope.break_result_info, then_node, .allow_branch_hint);
   6512     try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
   6513     if (!then_scope.endsWithNoReturn()) {
   6514         _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, then_node);
   6515     }
   6516 
   6517     var else_scope = parent_gz.makeSubBlock(scope);
   6518     defer else_scope.unstack();
   6519 
   6520     // We know that the operand (almost certainly) modified the error return trace,
   6521     // so signal to Sema that it should save the new index for restoring later.
   6522     if (do_err_trace and nodeMayAppendToErrorTrace(tree, if_full.ast.cond_expr))
   6523         _ = try else_scope.addSaveErrRetIndex(.always);
   6524 
   6525     const else_node = if_full.ast.else_expr;
   6526     if (else_node != 0) {
   6527         const sub_scope = s: {
   6528             if (if_full.error_token) |error_token| {
   6529                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   6530                     .err_union_code_ptr
   6531                 else
   6532                     .err_union_code;
   6533                 const payload_inst = try else_scope.addUnNode(tag, cond.inst, if_full.ast.cond_expr);
   6534                 const ident_name = try astgen.identAsString(error_token);
   6535                 const error_token_str = tree.tokenSlice(error_token);
   6536                 if (mem.eql(u8, "_", error_token_str))
   6537                     break :s &else_scope.base;
   6538                 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, error_token_str, .capture);
   6539                 payload_val_scope = .{
   6540                     .parent = &else_scope.base,
   6541                     .gen_zir = &else_scope,
   6542                     .name = ident_name,
   6543                     .inst = payload_inst,
   6544                     .token_src = error_token,
   6545                     .id_cat = .capture,
   6546                 };
   6547                 try else_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
   6548                 break :s &payload_val_scope.base;
   6549             } else {
   6550                 break :s &else_scope.base;
   6551             }
   6552         };
   6553         const else_result = try fullBodyExpr(&else_scope, sub_scope, block_scope.break_result_info, else_node, .allow_branch_hint);
   6554         if (!else_scope.endsWithNoReturn()) {
   6555             // As our last action before the break, "pop" the error trace if needed
   6556             if (do_err_trace)
   6557                 try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, else_node, else_result);
   6558             _ = try else_scope.addBreakWithSrcNode(.@"break", block, else_result, else_node);
   6559         }
   6560         try checkUsed(parent_gz, &else_scope.base, sub_scope);
   6561     } else {
   6562         const result = try rvalue(&else_scope, ri, .void_value, node);
   6563         _ = try else_scope.addBreak(.@"break", block, result);
   6564     }
   6565 
   6566     try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope);
   6567 
   6568     if (need_result_rvalue) {
   6569         return rvalue(parent_gz, ri, block.toRef(), node);
   6570     } else {
   6571         return block.toRef();
   6572     }
   6573 }
   6574 
   6575 /// Supports `else_scope` stacked on `then_scope`. Unstacks `else_scope` then `then_scope`.
   6576 fn setCondBrPayload(
   6577     condbr: Zir.Inst.Index,
   6578     cond: Zir.Inst.Ref,
   6579     then_scope: *GenZir,
   6580     else_scope: *GenZir,
   6581 ) !void {
   6582     defer then_scope.unstack();
   6583     defer else_scope.unstack();
   6584     const astgen = then_scope.astgen;
   6585     const then_body = then_scope.instructionsSliceUpto(else_scope);
   6586     const else_body = else_scope.instructionsSlice();
   6587     const then_body_len = astgen.countBodyLenAfterFixups(then_body);
   6588     const else_body_len = astgen.countBodyLenAfterFixups(else_body);
   6589     try astgen.extra.ensureUnusedCapacity(
   6590         astgen.gpa,
   6591         @typeInfo(Zir.Inst.CondBr).@"struct".fields.len + then_body_len + else_body_len,
   6592     );
   6593 
   6594     const zir_datas = astgen.instructions.items(.data);
   6595     zir_datas[@intFromEnum(condbr)].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.CondBr{
   6596         .condition = cond,
   6597         .then_body_len = then_body_len,
   6598         .else_body_len = else_body_len,
   6599     });
   6600     astgen.appendBodyWithFixups(then_body);
   6601     astgen.appendBodyWithFixups(else_body);
   6602 }
   6603 
   6604 fn whileExpr(
   6605     parent_gz: *GenZir,
   6606     scope: *Scope,
   6607     ri: ResultInfo,
   6608     node: Ast.Node.Index,
   6609     while_full: Ast.full.While,
   6610     is_statement: bool,
   6611 ) InnerError!Zir.Inst.Ref {
   6612     const astgen = parent_gz.astgen;
   6613     const tree = astgen.tree;
   6614     const token_tags = tree.tokens.items(.tag);
   6615     const token_starts = tree.tokens.items(.start);
   6616 
   6617     const need_rl = astgen.nodes_need_rl.contains(node);
   6618     const block_ri: ResultInfo = if (need_rl) ri else .{
   6619         .rl = switch (ri.rl) {
   6620             .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
   6621             .inferred_ptr => .none,
   6622             else => ri.rl,
   6623         },
   6624         .ctx = ri.ctx,
   6625     };
   6626     // We need to call `rvalue` to write through to the pointer only if we had a
   6627     // result pointer and aren't forwarding it.
   6628     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   6629     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   6630 
   6631     if (while_full.label_token) |label_token| {
   6632         try astgen.checkLabelRedefinition(scope, label_token);
   6633     }
   6634 
   6635     const is_inline = while_full.inline_token != null;
   6636     if (parent_gz.is_comptime and is_inline) {
   6637         try astgen.appendErrorTok(while_full.inline_token.?, "redundant inline keyword in comptime scope", .{});
   6638     }
   6639     const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop;
   6640     const loop_block = try parent_gz.makeBlockInst(loop_tag, node);
   6641     try parent_gz.instructions.append(astgen.gpa, loop_block);
   6642 
   6643     var loop_scope = parent_gz.makeSubBlock(scope);
   6644     loop_scope.is_inline = is_inline;
   6645     loop_scope.setBreakResultInfo(block_ri);
   6646     defer loop_scope.unstack();
   6647 
   6648     var cond_scope = parent_gz.makeSubBlock(&loop_scope.base);
   6649     defer cond_scope.unstack();
   6650 
   6651     const payload_is_ref = if (while_full.payload_token) |payload_token|
   6652         token_tags[payload_token] == .asterisk
   6653     else
   6654         false;
   6655 
   6656     try emitDbgNode(parent_gz, while_full.ast.cond_expr);
   6657     const cond: struct {
   6658         inst: Zir.Inst.Ref,
   6659         bool_bit: Zir.Inst.Ref,
   6660     } = c: {
   6661         if (while_full.error_token) |_| {
   6662             const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
   6663             const err_union = try fullBodyExpr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr, .normal);
   6664             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
   6665             break :c .{
   6666                 .inst = err_union,
   6667                 .bool_bit = try cond_scope.addUnNode(tag, err_union, while_full.ast.cond_expr),
   6668             };
   6669         } else if (while_full.payload_token) |_| {
   6670             const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
   6671             const optional = try fullBodyExpr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr, .normal);
   6672             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
   6673             break :c .{
   6674                 .inst = optional,
   6675                 .bool_bit = try cond_scope.addUnNode(tag, optional, while_full.ast.cond_expr),
   6676             };
   6677         } else {
   6678             const cond = try fullBodyExpr(&cond_scope, &cond_scope.base, coerced_bool_ri, while_full.ast.cond_expr, .normal);
   6679             break :c .{
   6680                 .inst = cond,
   6681                 .bool_bit = cond,
   6682             };
   6683         }
   6684     };
   6685 
   6686     const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
   6687     const condbr = try cond_scope.addCondBr(condbr_tag, node);
   6688     const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block;
   6689     const cond_block = try loop_scope.makeBlockInst(block_tag, node);
   6690     try cond_scope.setBlockBody(cond_block);
   6691     // cond_scope unstacked now, can add new instructions to loop_scope
   6692     try loop_scope.instructions.append(astgen.gpa, cond_block);
   6693 
   6694     // make scope now but don't stack on parent_gz until loop_scope
   6695     // gets unstacked after cont_expr is emitted and added below
   6696     var then_scope = parent_gz.makeSubBlock(&cond_scope.base);
   6697     then_scope.instructions_top = GenZir.unstacked_top;
   6698     defer then_scope.unstack();
   6699 
   6700     var dbg_var_name: Zir.NullTerminatedString = .empty;
   6701     var dbg_var_inst: Zir.Inst.Ref = undefined;
   6702     var opt_payload_inst: Zir.Inst.OptionalIndex = .none;
   6703     var payload_val_scope: Scope.LocalVal = undefined;
   6704     const then_sub_scope = s: {
   6705         if (while_full.error_token != null) {
   6706             if (while_full.payload_token) |payload_token| {
   6707                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   6708                     .err_union_payload_unsafe_ptr
   6709                 else
   6710                     .err_union_payload_unsafe;
   6711                 // will add this instruction to then_scope.instructions below
   6712                 const payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr);
   6713                 opt_payload_inst = payload_inst.toOptional();
   6714                 const ident_token = payload_token + @intFromBool(payload_is_ref);
   6715                 const ident_bytes = tree.tokenSlice(ident_token);
   6716                 if (mem.eql(u8, "_", ident_bytes)) {
   6717                     if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   6718                     break :s &then_scope.base;
   6719                 }
   6720                 const ident_name = try astgen.identAsString(ident_token);
   6721                 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture);
   6722                 payload_val_scope = .{
   6723                     .parent = &then_scope.base,
   6724                     .gen_zir = &then_scope,
   6725                     .name = ident_name,
   6726                     .inst = payload_inst.toRef(),
   6727                     .token_src = ident_token,
   6728                     .id_cat = .capture,
   6729                 };
   6730                 dbg_var_name = ident_name;
   6731                 dbg_var_inst = payload_inst.toRef();
   6732                 break :s &payload_val_scope.base;
   6733             } else {
   6734                 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node);
   6735                 break :s &then_scope.base;
   6736             }
   6737         } else if (while_full.payload_token) |payload_token| {
   6738             const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
   6739             const tag: Zir.Inst.Tag = if (payload_is_ref)
   6740                 .optional_payload_unsafe_ptr
   6741             else
   6742                 .optional_payload_unsafe;
   6743             // will add this instruction to then_scope.instructions below
   6744             const payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr);
   6745             opt_payload_inst = payload_inst.toOptional();
   6746             const ident_name = try astgen.identAsString(ident_token);
   6747             const ident_bytes = tree.tokenSlice(ident_token);
   6748             if (mem.eql(u8, "_", ident_bytes)) {
   6749                 if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   6750                 break :s &then_scope.base;
   6751             }
   6752             try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture);
   6753             payload_val_scope = .{
   6754                 .parent = &then_scope.base,
   6755                 .gen_zir = &then_scope,
   6756                 .name = ident_name,
   6757                 .inst = payload_inst.toRef(),
   6758                 .token_src = ident_token,
   6759                 .id_cat = .capture,
   6760             };
   6761             dbg_var_name = ident_name;
   6762             dbg_var_inst = payload_inst.toRef();
   6763             break :s &payload_val_scope.base;
   6764         } else {
   6765             break :s &then_scope.base;
   6766         }
   6767     };
   6768 
   6769     var continue_scope = parent_gz.makeSubBlock(then_sub_scope);
   6770     continue_scope.instructions_top = GenZir.unstacked_top;
   6771     defer continue_scope.unstack();
   6772     const continue_block = try then_scope.makeBlockInst(block_tag, node);
   6773 
   6774     const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
   6775     _ = try loop_scope.addNode(repeat_tag, node);
   6776 
   6777     try loop_scope.setBlockBody(loop_block);
   6778     loop_scope.break_block = loop_block.toOptional();
   6779     loop_scope.continue_block = continue_block.toOptional();
   6780     if (while_full.label_token) |label_token| {
   6781         loop_scope.label = .{
   6782             .token = label_token,
   6783             .block_inst = loop_block,
   6784         };
   6785     }
   6786 
   6787     // done adding instructions to loop_scope, can now stack then_scope
   6788     then_scope.instructions_top = then_scope.instructions.items.len;
   6789 
   6790     const then_node = while_full.ast.then_expr;
   6791     if (opt_payload_inst.unwrap()) |payload_inst| {
   6792         try then_scope.instructions.append(astgen.gpa, payload_inst);
   6793     }
   6794     if (dbg_var_name != .empty) try then_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst);
   6795     try then_scope.instructions.append(astgen.gpa, continue_block);
   6796     // This code could be improved to avoid emitting the continue expr when there
   6797     // are no jumps to it. This happens when the last statement of a while body is noreturn
   6798     // and there are no `continue` statements.
   6799     // Tracking issue: https://github.com/ziglang/zig/issues/9185
   6800     if (while_full.ast.cont_expr != 0) {
   6801         _ = try unusedResultExpr(&then_scope, then_sub_scope, while_full.ast.cont_expr);
   6802     }
   6803 
   6804     continue_scope.instructions_top = continue_scope.instructions.items.len;
   6805     {
   6806         try emitDbgNode(&continue_scope, then_node);
   6807         const unused_result = try fullBodyExpr(&continue_scope, &continue_scope.base, .{ .rl = .none }, then_node, .allow_branch_hint);
   6808         _ = try addEnsureResult(&continue_scope, unused_result, then_node);
   6809     }
   6810     try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
   6811     const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
   6812     if (!continue_scope.endsWithNoReturn()) {
   6813         astgen.advanceSourceCursor(token_starts[tree.lastToken(then_node)]);
   6814         try emitDbgStmt(parent_gz, .{ astgen.source_line - parent_gz.decl_line, astgen.source_column });
   6815         _ = try parent_gz.add(.{
   6816             .tag = .extended,
   6817             .data = .{ .extended = .{
   6818                 .opcode = .dbg_empty_stmt,
   6819                 .small = undefined,
   6820                 .operand = undefined,
   6821             } },
   6822         });
   6823         _ = try continue_scope.addBreak(break_tag, continue_block, .void_value);
   6824     }
   6825     try continue_scope.setBlockBody(continue_block);
   6826     _ = try then_scope.addBreak(break_tag, cond_block, .void_value);
   6827 
   6828     var else_scope = parent_gz.makeSubBlock(&cond_scope.base);
   6829     defer else_scope.unstack();
   6830 
   6831     const else_node = while_full.ast.else_expr;
   6832     if (else_node != 0) {
   6833         const sub_scope = s: {
   6834             if (while_full.error_token) |error_token| {
   6835                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   6836                     .err_union_code_ptr
   6837                 else
   6838                     .err_union_code;
   6839                 const else_payload_inst = try else_scope.addUnNode(tag, cond.inst, while_full.ast.cond_expr);
   6840                 const ident_name = try astgen.identAsString(error_token);
   6841                 const ident_bytes = tree.tokenSlice(error_token);
   6842                 if (mem.eql(u8, ident_bytes, "_"))
   6843                     break :s &else_scope.base;
   6844                 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, ident_bytes, .capture);
   6845                 payload_val_scope = .{
   6846                     .parent = &else_scope.base,
   6847                     .gen_zir = &else_scope,
   6848                     .name = ident_name,
   6849                     .inst = else_payload_inst,
   6850                     .token_src = error_token,
   6851                     .id_cat = .capture,
   6852                 };
   6853                 try else_scope.addDbgVar(.dbg_var_val, ident_name, else_payload_inst);
   6854                 break :s &payload_val_scope.base;
   6855             } else {
   6856                 break :s &else_scope.base;
   6857             }
   6858         };
   6859         // Remove the continue block and break block so that `continue` and `break`
   6860         // control flow apply to outer loops; not this one.
   6861         loop_scope.continue_block = .none;
   6862         loop_scope.break_block = .none;
   6863         const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node, .allow_branch_hint);
   6864         if (is_statement) {
   6865             _ = try addEnsureResult(&else_scope, else_result, else_node);
   6866         }
   6867 
   6868         try checkUsed(parent_gz, &else_scope.base, sub_scope);
   6869         if (!else_scope.endsWithNoReturn()) {
   6870             _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node);
   6871         }
   6872     } else {
   6873         const result = try rvalue(&else_scope, ri, .void_value, node);
   6874         _ = try else_scope.addBreak(break_tag, loop_block, result);
   6875     }
   6876 
   6877     if (loop_scope.label) |some| {
   6878         if (!some.used) {
   6879             try astgen.appendErrorTok(some.token, "unused while loop label", .{});
   6880         }
   6881     }
   6882 
   6883     try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope);
   6884 
   6885     const result = if (need_result_rvalue)
   6886         try rvalue(parent_gz, ri, loop_block.toRef(), node)
   6887     else
   6888         loop_block.toRef();
   6889 
   6890     if (is_statement) {
   6891         _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
   6892     }
   6893 
   6894     return result;
   6895 }
   6896 
   6897 fn forExpr(
   6898     parent_gz: *GenZir,
   6899     scope: *Scope,
   6900     ri: ResultInfo,
   6901     node: Ast.Node.Index,
   6902     for_full: Ast.full.For,
   6903     is_statement: bool,
   6904 ) InnerError!Zir.Inst.Ref {
   6905     const astgen = parent_gz.astgen;
   6906 
   6907     if (for_full.label_token) |label_token| {
   6908         try astgen.checkLabelRedefinition(scope, label_token);
   6909     }
   6910 
   6911     const need_rl = astgen.nodes_need_rl.contains(node);
   6912     const block_ri: ResultInfo = if (need_rl) ri else .{
   6913         .rl = switch (ri.rl) {
   6914             .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
   6915             .inferred_ptr => .none,
   6916             else => ri.rl,
   6917         },
   6918         .ctx = ri.ctx,
   6919     };
   6920     // We need to call `rvalue` to write through to the pointer only if we had a
   6921     // result pointer and aren't forwarding it.
   6922     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   6923     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   6924 
   6925     const is_inline = for_full.inline_token != null;
   6926     if (parent_gz.is_comptime and is_inline) {
   6927         try astgen.appendErrorTok(for_full.inline_token.?, "redundant inline keyword in comptime scope", .{});
   6928     }
   6929     const tree = astgen.tree;
   6930     const token_tags = tree.tokens.items(.tag);
   6931     const token_starts = tree.tokens.items(.start);
   6932     const node_tags = tree.nodes.items(.tag);
   6933     const node_data = tree.nodes.items(.data);
   6934     const gpa = astgen.gpa;
   6935 
   6936     // For counters, this is the start value; for indexables, this is the base
   6937     // pointer that can be used with elem_ptr and similar instructions.
   6938     // Special value `none` means that this is a counter and its start value is
   6939     // zero, indicating that the main index counter can be used directly.
   6940     const indexables = try gpa.alloc(Zir.Inst.Ref, for_full.ast.inputs.len);
   6941     defer gpa.free(indexables);
   6942     // elements of this array can be `none`, indicating no length check.
   6943     const lens = try gpa.alloc(Zir.Inst.Ref, for_full.ast.inputs.len);
   6944     defer gpa.free(lens);
   6945 
   6946     // We will use a single zero-based counter no matter how many indexables there are.
   6947     const index_ptr = blk: {
   6948         const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime_mut else .alloc;
   6949         const index_ptr = try parent_gz.addUnNode(alloc_tag, .usize_type, node);
   6950         // initialize to zero
   6951         _ = try parent_gz.addPlNode(.store_node, node, Zir.Inst.Bin{
   6952             .lhs = index_ptr,
   6953             .rhs = .zero_usize,
   6954         });
   6955         break :blk index_ptr;
   6956     };
   6957 
   6958     var any_len_checks = false;
   6959 
   6960     {
   6961         var capture_token = for_full.payload_token;
   6962         for (for_full.ast.inputs, indexables, lens) |input, *indexable_ref, *len_ref| {
   6963             const capture_is_ref = token_tags[capture_token] == .asterisk;
   6964             const ident_tok = capture_token + @intFromBool(capture_is_ref);
   6965             const is_discard = mem.eql(u8, tree.tokenSlice(ident_tok), "_");
   6966 
   6967             if (is_discard and capture_is_ref) {
   6968                 return astgen.failTok(capture_token, "pointer modifier invalid on discard", .{});
   6969             }
   6970             // Skip over the comma, and on to the next capture (or the ending pipe character).
   6971             capture_token = ident_tok + 2;
   6972 
   6973             try emitDbgNode(parent_gz, input);
   6974             if (node_tags[input] == .for_range) {
   6975                 if (capture_is_ref) {
   6976                     return astgen.failTok(ident_tok, "cannot capture reference to range", .{});
   6977                 }
   6978                 const start_node = node_data[input].lhs;
   6979                 const start_val = try expr(parent_gz, scope, .{ .rl = .{ .ty = .usize_type } }, start_node);
   6980 
   6981                 const end_node = node_data[input].rhs;
   6982                 const end_val = if (end_node != 0)
   6983                     try expr(parent_gz, scope, .{ .rl = .{ .ty = .usize_type } }, node_data[input].rhs)
   6984                 else
   6985                     .none;
   6986 
   6987                 if (end_val == .none and is_discard) {
   6988                     try astgen.appendErrorTok(ident_tok, "discard of unbounded counter", .{});
   6989                 }
   6990 
   6991                 const start_is_zero = nodeIsTriviallyZero(tree, start_node);
   6992                 const range_len = if (end_val == .none or start_is_zero)
   6993                     end_val
   6994                 else
   6995                     try parent_gz.addPlNode(.sub, input, Zir.Inst.Bin{
   6996                         .lhs = end_val,
   6997                         .rhs = start_val,
   6998                     });
   6999 
   7000                 any_len_checks = any_len_checks or range_len != .none;
   7001                 indexable_ref.* = if (start_is_zero) .none else start_val;
   7002                 len_ref.* = range_len;
   7003             } else {
   7004                 const indexable = try expr(parent_gz, scope, .{ .rl = .none }, input);
   7005 
   7006                 any_len_checks = true;
   7007                 indexable_ref.* = indexable;
   7008                 len_ref.* = indexable;
   7009             }
   7010         }
   7011     }
   7012 
   7013     if (!any_len_checks) {
   7014         return astgen.failNode(node, "unbounded for loop", .{});
   7015     }
   7016 
   7017     // We use a dedicated ZIR instruction to assert the lengths to assist with
   7018     // nicer error reporting as well as fewer ZIR bytes emitted.
   7019     const len: Zir.Inst.Ref = len: {
   7020         const lens_len: u32 = @intCast(lens.len);
   7021         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.MultiOp).@"struct".fields.len + lens_len);
   7022         const len = try parent_gz.addPlNode(.for_len, node, Zir.Inst.MultiOp{
   7023             .operands_len = lens_len,
   7024         });
   7025         appendRefsAssumeCapacity(astgen, lens);
   7026         break :len len;
   7027     };
   7028 
   7029     const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop;
   7030     const loop_block = try parent_gz.makeBlockInst(loop_tag, node);
   7031     try parent_gz.instructions.append(gpa, loop_block);
   7032 
   7033     var loop_scope = parent_gz.makeSubBlock(scope);
   7034     loop_scope.is_inline = is_inline;
   7035     loop_scope.setBreakResultInfo(block_ri);
   7036     defer loop_scope.unstack();
   7037 
   7038     // We need to finish loop_scope later once we have the deferred refs from then_scope. However, the
   7039     // load must be removed from instructions in the meantime or it appears to be part of parent_gz.
   7040     const index = try loop_scope.addUnNode(.load, index_ptr, node);
   7041     _ = loop_scope.instructions.pop();
   7042 
   7043     var cond_scope = parent_gz.makeSubBlock(&loop_scope.base);
   7044     defer cond_scope.unstack();
   7045 
   7046     // Check the condition.
   7047     const cond = try cond_scope.addPlNode(.cmp_lt, node, Zir.Inst.Bin{
   7048         .lhs = index,
   7049         .rhs = len,
   7050     });
   7051 
   7052     const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
   7053     const condbr = try cond_scope.addCondBr(condbr_tag, node);
   7054     const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block;
   7055     const cond_block = try loop_scope.makeBlockInst(block_tag, node);
   7056     try cond_scope.setBlockBody(cond_block);
   7057 
   7058     loop_scope.break_block = loop_block.toOptional();
   7059     loop_scope.continue_block = cond_block.toOptional();
   7060     if (for_full.label_token) |label_token| {
   7061         loop_scope.label = .{
   7062             .token = label_token,
   7063             .block_inst = loop_block,
   7064         };
   7065     }
   7066 
   7067     const then_node = for_full.ast.then_expr;
   7068     var then_scope = parent_gz.makeSubBlock(&cond_scope.base);
   7069     defer then_scope.unstack();
   7070 
   7071     const capture_scopes = try gpa.alloc(Scope.LocalVal, for_full.ast.inputs.len);
   7072     defer gpa.free(capture_scopes);
   7073 
   7074     const then_sub_scope = blk: {
   7075         var capture_token = for_full.payload_token;
   7076         var capture_sub_scope: *Scope = &then_scope.base;
   7077         for (for_full.ast.inputs, indexables, capture_scopes) |input, indexable_ref, *capture_scope| {
   7078             const capture_is_ref = token_tags[capture_token] == .asterisk;
   7079             const ident_tok = capture_token + @intFromBool(capture_is_ref);
   7080             const capture_name = tree.tokenSlice(ident_tok);
   7081             // Skip over the comma, and on to the next capture (or the ending pipe character).
   7082             capture_token = ident_tok + 2;
   7083 
   7084             if (mem.eql(u8, capture_name, "_")) continue;
   7085 
   7086             const name_str_index = try astgen.identAsString(ident_tok);
   7087             try astgen.detectLocalShadowing(capture_sub_scope, name_str_index, ident_tok, capture_name, .capture);
   7088 
   7089             const capture_inst = inst: {
   7090                 const is_counter = node_tags[input] == .for_range;
   7091 
   7092                 if (indexable_ref == .none) {
   7093                     // Special case: the main index can be used directly.
   7094                     assert(is_counter);
   7095                     assert(!capture_is_ref);
   7096                     break :inst index;
   7097                 }
   7098 
   7099                 // For counters, we add the index variable to the start value; for
   7100                 // indexables, we use it as an element index. This is so similar
   7101                 // that they can share the same code paths, branching only on the
   7102                 // ZIR tag.
   7103                 const switch_cond = (@as(u2, @intFromBool(capture_is_ref)) << 1) | @intFromBool(is_counter);
   7104                 const tag: Zir.Inst.Tag = switch (switch_cond) {
   7105                     0b00 => .elem_val,
   7106                     0b01 => .add,
   7107                     0b10 => .elem_ptr,
   7108                     0b11 => unreachable, // compile error emitted already
   7109                 };
   7110                 break :inst try then_scope.addPlNode(tag, input, Zir.Inst.Bin{
   7111                     .lhs = indexable_ref,
   7112                     .rhs = index,
   7113                 });
   7114             };
   7115 
   7116             capture_scope.* = .{
   7117                 .parent = capture_sub_scope,
   7118                 .gen_zir = &then_scope,
   7119                 .name = name_str_index,
   7120                 .inst = capture_inst,
   7121                 .token_src = ident_tok,
   7122                 .id_cat = .capture,
   7123             };
   7124 
   7125             try then_scope.addDbgVar(.dbg_var_val, name_str_index, capture_inst);
   7126             capture_sub_scope = &capture_scope.base;
   7127         }
   7128 
   7129         break :blk capture_sub_scope;
   7130     };
   7131 
   7132     const then_result = try fullBodyExpr(&then_scope, then_sub_scope, .{ .rl = .none }, then_node, .allow_branch_hint);
   7133     _ = try addEnsureResult(&then_scope, then_result, then_node);
   7134 
   7135     try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
   7136 
   7137     astgen.advanceSourceCursor(token_starts[tree.lastToken(then_node)]);
   7138     try emitDbgStmt(parent_gz, .{ astgen.source_line - parent_gz.decl_line, astgen.source_column });
   7139     _ = try parent_gz.add(.{
   7140         .tag = .extended,
   7141         .data = .{ .extended = .{
   7142             .opcode = .dbg_empty_stmt,
   7143             .small = undefined,
   7144             .operand = undefined,
   7145         } },
   7146     });
   7147 
   7148     const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
   7149     _ = try then_scope.addBreak(break_tag, cond_block, .void_value);
   7150 
   7151     var else_scope = parent_gz.makeSubBlock(&cond_scope.base);
   7152     defer else_scope.unstack();
   7153 
   7154     const else_node = for_full.ast.else_expr;
   7155     if (else_node != 0) {
   7156         const sub_scope = &else_scope.base;
   7157         // Remove the continue block and break block so that `continue` and `break`
   7158         // control flow apply to outer loops; not this one.
   7159         loop_scope.continue_block = .none;
   7160         loop_scope.break_block = .none;
   7161         const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node, .allow_branch_hint);
   7162         if (is_statement) {
   7163             _ = try addEnsureResult(&else_scope, else_result, else_node);
   7164         }
   7165         if (!else_scope.endsWithNoReturn()) {
   7166             _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node);
   7167         }
   7168     } else {
   7169         const result = try rvalue(&else_scope, ri, .void_value, node);
   7170         _ = try else_scope.addBreak(break_tag, loop_block, result);
   7171     }
   7172 
   7173     if (loop_scope.label) |some| {
   7174         if (!some.used) {
   7175             try astgen.appendErrorTok(some.token, "unused for loop label", .{});
   7176         }
   7177     }
   7178 
   7179     try setCondBrPayload(condbr, cond, &then_scope, &else_scope);
   7180 
   7181     // then_block and else_block unstacked now, can resurrect loop_scope to finally finish it
   7182     {
   7183         loop_scope.instructions_top = loop_scope.instructions.items.len;
   7184         try loop_scope.instructions.appendSlice(gpa, &.{ index.toIndex().?, cond_block });
   7185 
   7186         // Increment the index variable.
   7187         const index_plus_one = try loop_scope.addPlNode(.add_unsafe, node, Zir.Inst.Bin{
   7188             .lhs = index,
   7189             .rhs = .one_usize,
   7190         });
   7191         _ = try loop_scope.addPlNode(.store_node, node, Zir.Inst.Bin{
   7192             .lhs = index_ptr,
   7193             .rhs = index_plus_one,
   7194         });
   7195 
   7196         const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
   7197         _ = try loop_scope.addNode(repeat_tag, node);
   7198 
   7199         try loop_scope.setBlockBody(loop_block);
   7200     }
   7201 
   7202     const result = if (need_result_rvalue)
   7203         try rvalue(parent_gz, ri, loop_block.toRef(), node)
   7204     else
   7205         loop_block.toRef();
   7206 
   7207     if (is_statement) {
   7208         _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
   7209     }
   7210     return result;
   7211 }
   7212 
   7213 fn switchExprErrUnion(
   7214     parent_gz: *GenZir,
   7215     scope: *Scope,
   7216     ri: ResultInfo,
   7217     catch_or_if_node: Ast.Node.Index,
   7218     node_ty: enum { @"catch", @"if" },
   7219 ) InnerError!Zir.Inst.Ref {
   7220     const astgen = parent_gz.astgen;
   7221     const gpa = astgen.gpa;
   7222     const tree = astgen.tree;
   7223     const node_datas = tree.nodes.items(.data);
   7224     const node_tags = tree.nodes.items(.tag);
   7225     const main_tokens = tree.nodes.items(.main_token);
   7226     const token_tags = tree.tokens.items(.tag);
   7227 
   7228     const if_full = switch (node_ty) {
   7229         .@"catch" => undefined,
   7230         .@"if" => tree.fullIf(catch_or_if_node).?,
   7231     };
   7232 
   7233     const switch_node, const operand_node, const error_payload = switch (node_ty) {
   7234         .@"catch" => .{
   7235             node_datas[catch_or_if_node].rhs,
   7236             node_datas[catch_or_if_node].lhs,
   7237             main_tokens[catch_or_if_node] + 2,
   7238         },
   7239         .@"if" => .{
   7240             if_full.ast.else_expr,
   7241             if_full.ast.cond_expr,
   7242             if_full.error_token.?,
   7243         },
   7244     };
   7245     assert(node_tags[switch_node] == .@"switch" or node_tags[switch_node] == .switch_comma);
   7246 
   7247     const do_err_trace = astgen.fn_block != null;
   7248 
   7249     const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
   7250     const case_nodes = tree.extra_data[extra.start..extra.end];
   7251 
   7252     const need_rl = astgen.nodes_need_rl.contains(catch_or_if_node);
   7253     const block_ri: ResultInfo = if (need_rl) ri else .{
   7254         .rl = switch (ri.rl) {
   7255             .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, catch_or_if_node)).? },
   7256             .inferred_ptr => .none,
   7257             else => ri.rl,
   7258         },
   7259         .ctx = ri.ctx,
   7260     };
   7261 
   7262     const payload_is_ref = switch (node_ty) {
   7263         .@"if" => if_full.payload_token != null and token_tags[if_full.payload_token.?] == .asterisk,
   7264         .@"catch" => ri.rl == .ref or ri.rl == .ref_coerced_ty,
   7265     };
   7266 
   7267     // We need to call `rvalue` to write through to the pointer only if we had a
   7268     // result pointer and aren't forwarding it.
   7269     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   7270     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   7271     var scalar_cases_len: u32 = 0;
   7272     var multi_cases_len: u32 = 0;
   7273     var inline_cases_len: u32 = 0;
   7274     var has_else = false;
   7275     var else_node: Ast.Node.Index = 0;
   7276     var else_src: ?Ast.TokenIndex = null;
   7277     for (case_nodes) |case_node| {
   7278         const case = tree.fullSwitchCase(case_node).?;
   7279 
   7280         if (case.ast.values.len == 0) {
   7281             const case_src = case.ast.arrow_token - 1;
   7282             if (else_src) |src| {
   7283                 return astgen.failTokNotes(
   7284                     case_src,
   7285                     "multiple else prongs in switch expression",
   7286                     .{},
   7287                     &[_]u32{
   7288                         try astgen.errNoteTok(
   7289                             src,
   7290                             "previous else prong here",
   7291                             .{},
   7292                         ),
   7293                     },
   7294                 );
   7295             }
   7296             has_else = true;
   7297             else_node = case_node;
   7298             else_src = case_src;
   7299             continue;
   7300         } else if (case.ast.values.len == 1 and
   7301             node_tags[case.ast.values[0]] == .identifier and
   7302             mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
   7303         {
   7304             const case_src = case.ast.arrow_token - 1;
   7305             return astgen.failTokNotes(
   7306                 case_src,
   7307                 "'_' prong is not allowed when switching on errors",
   7308                 .{},
   7309                 &[_]u32{
   7310                     try astgen.errNoteTok(
   7311                         case_src,
   7312                         "consider using 'else'",
   7313                         .{},
   7314                     ),
   7315                 },
   7316             );
   7317         }
   7318 
   7319         for (case.ast.values) |val| {
   7320             if (node_tags[val] == .string_literal)
   7321                 return astgen.failNode(val, "cannot switch on strings", .{});
   7322         }
   7323 
   7324         if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] != .switch_range) {
   7325             scalar_cases_len += 1;
   7326         } else {
   7327             multi_cases_len += 1;
   7328         }
   7329         if (case.inline_token != null) {
   7330             inline_cases_len += 1;
   7331         }
   7332     }
   7333 
   7334     const operand_ri: ResultInfo = .{
   7335         .rl = if (payload_is_ref) .ref else .none,
   7336         .ctx = .error_handling_expr,
   7337     };
   7338 
   7339     astgen.advanceSourceCursorToNode(operand_node);
   7340     const operand_lc: LineColumn = .{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
   7341 
   7342     const raw_operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, switch_node);
   7343     const item_ri: ResultInfo = .{ .rl = .none };
   7344 
   7345     // This contains the data that goes into the `extra` array for the SwitchBlockErrUnion, except
   7346     // the first cases_nodes.len slots are a table that indexes payloads later in the array,
   7347     // with the non-error and else case indices coming first, then scalar_cases_len indexes, then
   7348     // multi_cases_len indexes
   7349     const payloads = &astgen.scratch;
   7350     const scratch_top = astgen.scratch.items.len;
   7351     const case_table_start = scratch_top;
   7352     const scalar_case_table = case_table_start + 1 + @intFromBool(has_else);
   7353     const multi_case_table = scalar_case_table + scalar_cases_len;
   7354     const case_table_end = multi_case_table + multi_cases_len;
   7355 
   7356     try astgen.scratch.resize(gpa, case_table_end);
   7357     defer astgen.scratch.items.len = scratch_top;
   7358 
   7359     var block_scope = parent_gz.makeSubBlock(scope);
   7360     // block_scope not used for collecting instructions
   7361     block_scope.instructions_top = GenZir.unstacked_top;
   7362     block_scope.setBreakResultInfo(block_ri);
   7363 
   7364     // Sema expects a dbg_stmt immediately before switch_block_err_union
   7365     try emitDbgStmtForceCurrentIndex(parent_gz, operand_lc);
   7366     // This gets added to the parent block later, after the item expressions.
   7367     const switch_block = try parent_gz.makeBlockInst(.switch_block_err_union, switch_node);
   7368 
   7369     // We re-use this same scope for all cases, including the special prong, if any.
   7370     var case_scope = parent_gz.makeSubBlock(&block_scope.base);
   7371     case_scope.instructions_top = GenZir.unstacked_top;
   7372 
   7373     {
   7374         const body_len_index: u32 = @intCast(payloads.items.len);
   7375         payloads.items[case_table_start] = body_len_index;
   7376         try payloads.resize(gpa, body_len_index + 1); // body_len
   7377 
   7378         case_scope.instructions_top = parent_gz.instructions.items.len;
   7379         defer case_scope.unstack();
   7380 
   7381         const unwrap_payload_tag: Zir.Inst.Tag = if (payload_is_ref)
   7382             .err_union_payload_unsafe_ptr
   7383         else
   7384             .err_union_payload_unsafe;
   7385 
   7386         const unwrapped_payload = try case_scope.addUnNode(
   7387             unwrap_payload_tag,
   7388             raw_operand,
   7389             catch_or_if_node,
   7390         );
   7391 
   7392         switch (node_ty) {
   7393             .@"catch" => {
   7394                 const case_result = switch (ri.rl) {
   7395                     .ref, .ref_coerced_ty => unwrapped_payload,
   7396                     else => try rvalue(
   7397                         &case_scope,
   7398                         block_scope.break_result_info,
   7399                         unwrapped_payload,
   7400                         catch_or_if_node,
   7401                     ),
   7402                 };
   7403                 _ = try case_scope.addBreakWithSrcNode(
   7404                     .@"break",
   7405                     switch_block,
   7406                     case_result,
   7407                     catch_or_if_node,
   7408                 );
   7409             },
   7410             .@"if" => {
   7411                 var payload_val_scope: Scope.LocalVal = undefined;
   7412 
   7413                 const then_node = if_full.ast.then_expr;
   7414                 const then_sub_scope = s: {
   7415                     assert(if_full.error_token != null);
   7416                     if (if_full.payload_token) |payload_token| {
   7417                         const token_name_index = payload_token + @intFromBool(payload_is_ref);
   7418                         const ident_name = try astgen.identAsString(token_name_index);
   7419                         const token_name_str = tree.tokenSlice(token_name_index);
   7420                         if (mem.eql(u8, "_", token_name_str))
   7421                             break :s &case_scope.base;
   7422                         try astgen.detectLocalShadowing(
   7423                             &case_scope.base,
   7424                             ident_name,
   7425                             token_name_index,
   7426                             token_name_str,
   7427                             .capture,
   7428                         );
   7429                         payload_val_scope = .{
   7430                             .parent = &case_scope.base,
   7431                             .gen_zir = &case_scope,
   7432                             .name = ident_name,
   7433                             .inst = unwrapped_payload,
   7434                             .token_src = token_name_index,
   7435                             .id_cat = .capture,
   7436                         };
   7437                         try case_scope.addDbgVar(.dbg_var_val, ident_name, unwrapped_payload);
   7438                         break :s &payload_val_scope.base;
   7439                     } else {
   7440                         _ = try case_scope.addUnNode(
   7441                             .ensure_err_union_payload_void,
   7442                             raw_operand,
   7443                             catch_or_if_node,
   7444                         );
   7445                         break :s &case_scope.base;
   7446                     }
   7447                 };
   7448                 const then_result = try expr(
   7449                     &case_scope,
   7450                     then_sub_scope,
   7451                     block_scope.break_result_info,
   7452                     then_node,
   7453                 );
   7454                 try checkUsed(parent_gz, &case_scope.base, then_sub_scope);
   7455                 if (!case_scope.endsWithNoReturn()) {
   7456                     _ = try case_scope.addBreakWithSrcNode(
   7457                         .@"break",
   7458                         switch_block,
   7459                         then_result,
   7460                         then_node,
   7461                     );
   7462                 }
   7463             },
   7464         }
   7465 
   7466         const case_slice = case_scope.instructionsSlice();
   7467         const body_len = astgen.countBodyLenAfterFixupsExtraRefs(case_slice, &.{switch_block});
   7468         try payloads.ensureUnusedCapacity(gpa, body_len);
   7469         const capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = switch (node_ty) {
   7470             .@"catch" => .none,
   7471             .@"if" => if (if_full.payload_token == null)
   7472                 .none
   7473             else if (payload_is_ref)
   7474                 .by_ref
   7475             else
   7476                 .by_val,
   7477         };
   7478         payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
   7479             .body_len = @intCast(body_len),
   7480             .capture = capture,
   7481             .is_inline = false,
   7482             .has_tag_capture = false,
   7483         });
   7484         appendBodyWithFixupsExtraRefsArrayList(astgen, payloads, case_slice, &.{switch_block});
   7485     }
   7486 
   7487     const err_name = blk: {
   7488         const err_str = tree.tokenSlice(error_payload);
   7489         if (mem.eql(u8, err_str, "_")) {
   7490             // This is fatal because we already know we're switching on the captured error.
   7491             return astgen.failTok(error_payload, "discard of error capture; omit it instead", .{});
   7492         }
   7493         const err_name = try astgen.identAsString(error_payload);
   7494         try astgen.detectLocalShadowing(scope, err_name, error_payload, err_str, .capture);
   7495 
   7496         break :blk err_name;
   7497     };
   7498 
   7499     // allocate a shared dummy instruction for the error capture
   7500     const err_inst = err_inst: {
   7501         const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
   7502         try astgen.instructions.append(astgen.gpa, .{
   7503             .tag = .extended,
   7504             .data = .{ .extended = .{
   7505                 .opcode = .value_placeholder,
   7506                 .small = undefined,
   7507                 .operand = undefined,
   7508             } },
   7509         });
   7510         break :err_inst inst;
   7511     };
   7512 
   7513     // In this pass we generate all the item and prong expressions for error cases.
   7514     var multi_case_index: u32 = 0;
   7515     var scalar_case_index: u32 = 0;
   7516     var any_uses_err_capture = false;
   7517     for (case_nodes) |case_node| {
   7518         const case = tree.fullSwitchCase(case_node).?;
   7519 
   7520         const is_multi_case = case.ast.values.len > 1 or
   7521             (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .switch_range);
   7522 
   7523         var dbg_var_name: Zir.NullTerminatedString = .empty;
   7524         var dbg_var_inst: Zir.Inst.Ref = undefined;
   7525         var err_scope: Scope.LocalVal = undefined;
   7526         var capture_scope: Scope.LocalVal = undefined;
   7527 
   7528         const sub_scope = blk: {
   7529             err_scope = .{
   7530                 .parent = &case_scope.base,
   7531                 .gen_zir = &case_scope,
   7532                 .name = err_name,
   7533                 .inst = err_inst.toRef(),
   7534                 .token_src = error_payload,
   7535                 .id_cat = .capture,
   7536             };
   7537 
   7538             const capture_token = case.payload_token orelse break :blk &err_scope.base;
   7539             if (token_tags[capture_token] != .identifier) {
   7540                 return astgen.failTok(capture_token + 1, "error set cannot be captured by reference", .{});
   7541             }
   7542 
   7543             const capture_slice = tree.tokenSlice(capture_token);
   7544             if (mem.eql(u8, capture_slice, "_")) {
   7545                 try astgen.appendErrorTok(capture_token, "discard of error capture; omit it instead", .{});
   7546             }
   7547             const tag_name = try astgen.identAsString(capture_token);
   7548             try astgen.detectLocalShadowing(&case_scope.base, tag_name, capture_token, capture_slice, .capture);
   7549 
   7550             capture_scope = .{
   7551                 .parent = &case_scope.base,
   7552                 .gen_zir = &case_scope,
   7553                 .name = tag_name,
   7554                 .inst = switch_block.toRef(),
   7555                 .token_src = capture_token,
   7556                 .id_cat = .capture,
   7557             };
   7558             dbg_var_name = tag_name;
   7559             dbg_var_inst = switch_block.toRef();
   7560 
   7561             err_scope.parent = &capture_scope.base;
   7562 
   7563             break :blk &err_scope.base;
   7564         };
   7565 
   7566         const header_index: u32 = @intCast(payloads.items.len);
   7567         const body_len_index = if (is_multi_case) blk: {
   7568             payloads.items[multi_case_table + multi_case_index] = header_index;
   7569             multi_case_index += 1;
   7570             try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len
   7571 
   7572             // items
   7573             var items_len: u32 = 0;
   7574             for (case.ast.values) |item_node| {
   7575                 if (node_tags[item_node] == .switch_range) continue;
   7576                 items_len += 1;
   7577 
   7578                 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node);
   7579                 try payloads.append(gpa, @intFromEnum(item_inst));
   7580             }
   7581 
   7582             // ranges
   7583             var ranges_len: u32 = 0;
   7584             for (case.ast.values) |range| {
   7585                 if (node_tags[range] != .switch_range) continue;
   7586                 ranges_len += 1;
   7587 
   7588                 const first = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].lhs);
   7589                 const last = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].rhs);
   7590                 try payloads.appendSlice(gpa, &[_]u32{
   7591                     @intFromEnum(first), @intFromEnum(last),
   7592                 });
   7593             }
   7594 
   7595             payloads.items[header_index] = items_len;
   7596             payloads.items[header_index + 1] = ranges_len;
   7597             break :blk header_index + 2;
   7598         } else if (case_node == else_node) blk: {
   7599             payloads.items[case_table_start + 1] = header_index;
   7600             try payloads.resize(gpa, header_index + 1); // body_len
   7601             break :blk header_index;
   7602         } else blk: {
   7603             payloads.items[scalar_case_table + scalar_case_index] = header_index;
   7604             scalar_case_index += 1;
   7605             try payloads.resize(gpa, header_index + 2); // item, body_len
   7606             const item_node = case.ast.values[0];
   7607             const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node);
   7608             payloads.items[header_index] = @intFromEnum(item_inst);
   7609             break :blk header_index + 1;
   7610         };
   7611 
   7612         {
   7613             // temporarily stack case_scope on parent_gz
   7614             case_scope.instructions_top = parent_gz.instructions.items.len;
   7615             defer case_scope.unstack();
   7616 
   7617             if (do_err_trace and nodeMayAppendToErrorTrace(tree, operand_node))
   7618                 _ = try case_scope.addSaveErrRetIndex(.always);
   7619 
   7620             if (dbg_var_name != .empty) {
   7621                 try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst);
   7622             }
   7623 
   7624             const target_expr_node = case.ast.target_expr;
   7625             const case_result = try fullBodyExpr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node, .allow_branch_hint);
   7626             // check capture_scope, not err_scope to avoid false positive unused error capture
   7627             try checkUsed(parent_gz, &case_scope.base, err_scope.parent);
   7628             const uses_err = err_scope.used != 0 or err_scope.discarded != 0;
   7629             if (uses_err) {
   7630                 try case_scope.addDbgVar(.dbg_var_val, err_name, err_inst.toRef());
   7631                 any_uses_err_capture = true;
   7632             }
   7633 
   7634             if (!parent_gz.refIsNoReturn(case_result)) {
   7635                 if (do_err_trace)
   7636                     try restoreErrRetIndex(
   7637                         &case_scope,
   7638                         .{ .block = switch_block },
   7639                         block_scope.break_result_info,
   7640                         target_expr_node,
   7641                         case_result,
   7642                     );
   7643 
   7644                 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node);
   7645             }
   7646 
   7647             const case_slice = case_scope.instructionsSlice();
   7648             const extra_insts: []const Zir.Inst.Index = if (uses_err) &.{ switch_block, err_inst } else &.{switch_block};
   7649             const body_len = astgen.countBodyLenAfterFixupsExtraRefs(case_slice, extra_insts);
   7650             try payloads.ensureUnusedCapacity(gpa, body_len);
   7651             payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
   7652                 .body_len = @intCast(body_len),
   7653                 .capture = if (case.payload_token != null) .by_val else .none,
   7654                 .is_inline = case.inline_token != null,
   7655                 .has_tag_capture = false,
   7656             });
   7657             appendBodyWithFixupsExtraRefsArrayList(astgen, payloads, case_slice, extra_insts);
   7658         }
   7659     }
   7660     // Now that the item expressions are generated we can add this.
   7661     try parent_gz.instructions.append(gpa, switch_block);
   7662 
   7663     try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlockErrUnion).@"struct".fields.len +
   7664         @intFromBool(multi_cases_len != 0) +
   7665         payloads.items.len - case_table_end +
   7666         (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).@"struct".fields.len);
   7667 
   7668     const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlockErrUnion{
   7669         .operand = raw_operand,
   7670         .bits = Zir.Inst.SwitchBlockErrUnion.Bits{
   7671             .has_multi_cases = multi_cases_len != 0,
   7672             .has_else = has_else,
   7673             .scalar_cases_len = @intCast(scalar_cases_len),
   7674             .any_uses_err_capture = any_uses_err_capture,
   7675             .payload_is_ref = payload_is_ref,
   7676         },
   7677         .main_src_node_offset = parent_gz.nodeIndexToRelative(catch_or_if_node),
   7678     });
   7679 
   7680     if (multi_cases_len != 0) {
   7681         astgen.extra.appendAssumeCapacity(multi_cases_len);
   7682     }
   7683 
   7684     if (any_uses_err_capture) {
   7685         astgen.extra.appendAssumeCapacity(@intFromEnum(err_inst));
   7686     }
   7687 
   7688     const zir_datas = astgen.instructions.items(.data);
   7689     zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;
   7690 
   7691     for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| {
   7692         var body_len_index = start_index;
   7693         var end_index = start_index;
   7694         const table_index = case_table_start + i;
   7695         if (table_index < scalar_case_table) {
   7696             end_index += 1;
   7697         } else if (table_index < multi_case_table) {
   7698             body_len_index += 1;
   7699             end_index += 2;
   7700         } else {
   7701             body_len_index += 2;
   7702             const items_len = payloads.items[start_index];
   7703             const ranges_len = payloads.items[start_index + 1];
   7704             end_index += 3 + items_len + 2 * ranges_len;
   7705         }
   7706         const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
   7707         end_index += prong_info.body_len;
   7708         astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
   7709     }
   7710 
   7711     if (need_result_rvalue) {
   7712         return rvalue(parent_gz, ri, switch_block.toRef(), switch_node);
   7713     } else {
   7714         return switch_block.toRef();
   7715     }
   7716 }
   7717 
   7718 fn switchExpr(
   7719     parent_gz: *GenZir,
   7720     scope: *Scope,
   7721     ri: ResultInfo,
   7722     node: Ast.Node.Index,
   7723     switch_full: Ast.full.Switch,
   7724 ) InnerError!Zir.Inst.Ref {
   7725     const astgen = parent_gz.astgen;
   7726     const gpa = astgen.gpa;
   7727     const tree = astgen.tree;
   7728     const node_datas = tree.nodes.items(.data);
   7729     const node_tags = tree.nodes.items(.tag);
   7730     const main_tokens = tree.nodes.items(.main_token);
   7731     const token_tags = tree.tokens.items(.tag);
   7732     const operand_node = switch_full.ast.condition;
   7733     const case_nodes = switch_full.ast.cases;
   7734 
   7735     const need_rl = astgen.nodes_need_rl.contains(node);
   7736     const block_ri: ResultInfo = if (need_rl) ri else .{
   7737         .rl = switch (ri.rl) {
   7738             .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
   7739             .inferred_ptr => .none,
   7740             else => ri.rl,
   7741         },
   7742         .ctx = ri.ctx,
   7743     };
   7744     // We need to call `rvalue` to write through to the pointer only if we had a
   7745     // result pointer and aren't forwarding it.
   7746     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   7747     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   7748 
   7749     if (switch_full.label_token) |label_token| {
   7750         try astgen.checkLabelRedefinition(scope, label_token);
   7751     }
   7752 
   7753     // We perform two passes over the AST. This first pass is to collect information
   7754     // for the following variables, make note of the special prong AST node index,
   7755     // and bail out with a compile error if there are multiple special prongs present.
   7756     var any_payload_is_ref = false;
   7757     var any_has_tag_capture = false;
   7758     var any_non_inline_capture = false;
   7759     var scalar_cases_len: u32 = 0;
   7760     var multi_cases_len: u32 = 0;
   7761     var inline_cases_len: u32 = 0;
   7762     var special_prong: Zir.SpecialProng = .none;
   7763     var special_node: Ast.Node.Index = 0;
   7764     var else_src: ?Ast.TokenIndex = null;
   7765     var underscore_src: ?Ast.TokenIndex = null;
   7766     for (case_nodes) |case_node| {
   7767         const case = tree.fullSwitchCase(case_node).?;
   7768         if (case.payload_token) |payload_token| {
   7769             const ident = if (token_tags[payload_token] == .asterisk) blk: {
   7770                 any_payload_is_ref = true;
   7771                 break :blk payload_token + 1;
   7772             } else payload_token;
   7773             if (token_tags[ident + 1] == .comma) {
   7774                 any_has_tag_capture = true;
   7775             }
   7776 
   7777             // If the first capture is ignored, then there is no runtime-known
   7778             // capture, as the tag capture must be for an inline prong.
   7779             // This check isn't perfect, because for things like enums, the
   7780             // first prong *is* comptime-known for inline prongs! But such
   7781             // knowledge requires semantic analysis.
   7782             if (!mem.eql(u8, tree.tokenSlice(ident), "_")) {
   7783                 any_non_inline_capture = true;
   7784             }
   7785         }
   7786         // Check for else/`_` prong.
   7787         if (case.ast.values.len == 0) {
   7788             const case_src = case.ast.arrow_token - 1;
   7789             if (else_src) |src| {
   7790                 return astgen.failTokNotes(
   7791                     case_src,
   7792                     "multiple else prongs in switch expression",
   7793                     .{},
   7794                     &[_]u32{
   7795                         try astgen.errNoteTok(
   7796                             src,
   7797                             "previous else prong here",
   7798                             .{},
   7799                         ),
   7800                     },
   7801                 );
   7802             } else if (underscore_src) |some_underscore| {
   7803                 return astgen.failNodeNotes(
   7804                     node,
   7805                     "else and '_' prong in switch expression",
   7806                     .{},
   7807                     &[_]u32{
   7808                         try astgen.errNoteTok(
   7809                             case_src,
   7810                             "else prong here",
   7811                             .{},
   7812                         ),
   7813                         try astgen.errNoteTok(
   7814                             some_underscore,
   7815                             "'_' prong here",
   7816                             .{},
   7817                         ),
   7818                     },
   7819                 );
   7820             }
   7821             special_node = case_node;
   7822             special_prong = .@"else";
   7823             else_src = case_src;
   7824             continue;
   7825         } else if (case.ast.values.len == 1 and
   7826             node_tags[case.ast.values[0]] == .identifier and
   7827             mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
   7828         {
   7829             const case_src = case.ast.arrow_token - 1;
   7830             if (underscore_src) |src| {
   7831                 return astgen.failTokNotes(
   7832                     case_src,
   7833                     "multiple '_' prongs in switch expression",
   7834                     .{},
   7835                     &[_]u32{
   7836                         try astgen.errNoteTok(
   7837                             src,
   7838                             "previous '_' prong here",
   7839                             .{},
   7840                         ),
   7841                     },
   7842                 );
   7843             } else if (else_src) |some_else| {
   7844                 return astgen.failNodeNotes(
   7845                     node,
   7846                     "else and '_' prong in switch expression",
   7847                     .{},
   7848                     &[_]u32{
   7849                         try astgen.errNoteTok(
   7850                             some_else,
   7851                             "else prong here",
   7852                             .{},
   7853                         ),
   7854                         try astgen.errNoteTok(
   7855                             case_src,
   7856                             "'_' prong here",
   7857                             .{},
   7858                         ),
   7859                     },
   7860                 );
   7861             }
   7862             if (case.inline_token != null) {
   7863                 return astgen.failTok(case_src, "cannot inline '_' prong", .{});
   7864             }
   7865             special_node = case_node;
   7866             special_prong = .under;
   7867             underscore_src = case_src;
   7868             continue;
   7869         }
   7870 
   7871         for (case.ast.values) |val| {
   7872             if (node_tags[val] == .string_literal)
   7873                 return astgen.failNode(val, "cannot switch on strings", .{});
   7874         }
   7875 
   7876         if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] != .switch_range) {
   7877             scalar_cases_len += 1;
   7878         } else {
   7879             multi_cases_len += 1;
   7880         }
   7881         if (case.inline_token != null) {
   7882             inline_cases_len += 1;
   7883         }
   7884     }
   7885 
   7886     const operand_ri: ResultInfo = .{ .rl = if (any_payload_is_ref) .ref else .none };
   7887 
   7888     astgen.advanceSourceCursorToNode(operand_node);
   7889     const operand_lc: LineColumn = .{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
   7890 
   7891     const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node);
   7892     const item_ri: ResultInfo = .{ .rl = .none };
   7893 
   7894     // If this switch is labeled, it may have `continue`s targeting it, and thus we need the operand type
   7895     // to provide a result type.
   7896     const raw_operand_ty_ref = if (switch_full.label_token != null) t: {
   7897         break :t try parent_gz.addUnNode(.typeof, raw_operand, operand_node);
   7898     } else undefined;
   7899 
   7900     // This contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti,
   7901     // except the first cases_nodes.len slots are a table that indexes payloads later in the array, with
   7902     // the special case index coming first, then scalar_case_len indexes, then multi_cases_len indexes
   7903     const payloads = &astgen.scratch;
   7904     const scratch_top = astgen.scratch.items.len;
   7905     const case_table_start = scratch_top;
   7906     const scalar_case_table = case_table_start + @intFromBool(special_prong != .none);
   7907     const multi_case_table = scalar_case_table + scalar_cases_len;
   7908     const case_table_end = multi_case_table + multi_cases_len;
   7909     try astgen.scratch.resize(gpa, case_table_end);
   7910     defer astgen.scratch.items.len = scratch_top;
   7911 
   7912     var block_scope = parent_gz.makeSubBlock(scope);
   7913     // block_scope not used for collecting instructions
   7914     block_scope.instructions_top = GenZir.unstacked_top;
   7915     block_scope.setBreakResultInfo(block_ri);
   7916 
   7917     // Sema expects a dbg_stmt immediately before switch_block(_ref)
   7918     try emitDbgStmtForceCurrentIndex(parent_gz, operand_lc);
   7919     // This gets added to the parent block later, after the item expressions.
   7920     const switch_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_block_ref else .switch_block;
   7921     const switch_block = try parent_gz.makeBlockInst(switch_tag, node);
   7922 
   7923     if (switch_full.label_token) |label_token| {
   7924         block_scope.continue_block = switch_block.toOptional();
   7925         block_scope.continue_result_info = .{
   7926             .rl = if (any_payload_is_ref)
   7927                 .{ .ref_coerced_ty = raw_operand_ty_ref }
   7928             else
   7929                 .{ .coerced_ty = raw_operand_ty_ref },
   7930         };
   7931 
   7932         block_scope.label = .{
   7933             .token = label_token,
   7934             .block_inst = switch_block,
   7935         };
   7936         // `break` can target this via `label.block_inst`
   7937         // `break_result_info` already set by `setBreakResultInfo`
   7938     }
   7939 
   7940     // We re-use this same scope for all cases, including the special prong, if any.
   7941     var case_scope = parent_gz.makeSubBlock(&block_scope.base);
   7942     case_scope.instructions_top = GenZir.unstacked_top;
   7943 
   7944     // If any prong has an inline tag capture, allocate a shared dummy instruction for it
   7945     const tag_inst = if (any_has_tag_capture) tag_inst: {
   7946         const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
   7947         try astgen.instructions.append(astgen.gpa, .{
   7948             .tag = .extended,
   7949             .data = .{ .extended = .{
   7950                 .opcode = .value_placeholder,
   7951                 .small = undefined,
   7952                 .operand = undefined,
   7953             } },
   7954         });
   7955         break :tag_inst inst;
   7956     } else undefined;
   7957 
   7958     // In this pass we generate all the item and prong expressions.
   7959     var multi_case_index: u32 = 0;
   7960     var scalar_case_index: u32 = 0;
   7961     for (case_nodes) |case_node| {
   7962         const case = tree.fullSwitchCase(case_node).?;
   7963 
   7964         const is_multi_case = case.ast.values.len > 1 or
   7965             (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .switch_range);
   7966 
   7967         var dbg_var_name: Zir.NullTerminatedString = .empty;
   7968         var dbg_var_inst: Zir.Inst.Ref = undefined;
   7969         var dbg_var_tag_name: Zir.NullTerminatedString = .empty;
   7970         var dbg_var_tag_inst: Zir.Inst.Ref = undefined;
   7971         var has_tag_capture = false;
   7972         var capture_val_scope: Scope.LocalVal = undefined;
   7973         var tag_scope: Scope.LocalVal = undefined;
   7974 
   7975         var capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = .none;
   7976 
   7977         const sub_scope = blk: {
   7978             const payload_token = case.payload_token orelse break :blk &case_scope.base;
   7979             const ident = if (token_tags[payload_token] == .asterisk)
   7980                 payload_token + 1
   7981             else
   7982                 payload_token;
   7983 
   7984             const is_ptr = ident != payload_token;
   7985             capture = if (is_ptr) .by_ref else .by_val;
   7986 
   7987             const ident_slice = tree.tokenSlice(ident);
   7988             var payload_sub_scope: *Scope = undefined;
   7989             if (mem.eql(u8, ident_slice, "_")) {
   7990                 if (is_ptr) {
   7991                     return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   7992                 }
   7993                 payload_sub_scope = &case_scope.base;
   7994             } else {
   7995                 const capture_name = try astgen.identAsString(ident);
   7996                 try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice, .capture);
   7997                 capture_val_scope = .{
   7998                     .parent = &case_scope.base,
   7999                     .gen_zir = &case_scope,
   8000                     .name = capture_name,
   8001                     .inst = switch_block.toRef(),
   8002                     .token_src = ident,
   8003                     .id_cat = .capture,
   8004                 };
   8005                 dbg_var_name = capture_name;
   8006                 dbg_var_inst = switch_block.toRef();
   8007                 payload_sub_scope = &capture_val_scope.base;
   8008             }
   8009 
   8010             const tag_token = if (token_tags[ident + 1] == .comma)
   8011                 ident + 2
   8012             else
   8013                 break :blk payload_sub_scope;
   8014             const tag_slice = tree.tokenSlice(tag_token);
   8015             if (mem.eql(u8, tag_slice, "_")) {
   8016                 try astgen.appendErrorTok(tag_token, "discard of tag capture; omit it instead", .{});
   8017             } else if (case.inline_token == null) {
   8018                 return astgen.failTok(tag_token, "tag capture on non-inline prong", .{});
   8019             }
   8020             const tag_name = try astgen.identAsString(tag_token);
   8021             try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice, .@"switch tag capture");
   8022 
   8023             assert(any_has_tag_capture);
   8024             has_tag_capture = true;
   8025 
   8026             tag_scope = .{
   8027                 .parent = payload_sub_scope,
   8028                 .gen_zir = &case_scope,
   8029                 .name = tag_name,
   8030                 .inst = tag_inst.toRef(),
   8031                 .token_src = tag_token,
   8032                 .id_cat = .@"switch tag capture",
   8033             };
   8034             dbg_var_tag_name = tag_name;
   8035             dbg_var_tag_inst = tag_inst.toRef();
   8036             break :blk &tag_scope.base;
   8037         };
   8038 
   8039         const header_index: u32 = @intCast(payloads.items.len);
   8040         const body_len_index = if (is_multi_case) blk: {
   8041             payloads.items[multi_case_table + multi_case_index] = header_index;
   8042             multi_case_index += 1;
   8043             try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len
   8044 
   8045             // items
   8046             var items_len: u32 = 0;
   8047             for (case.ast.values) |item_node| {
   8048                 if (node_tags[item_node] == .switch_range) continue;
   8049                 items_len += 1;
   8050 
   8051                 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node);
   8052                 try payloads.append(gpa, @intFromEnum(item_inst));
   8053             }
   8054 
   8055             // ranges
   8056             var ranges_len: u32 = 0;
   8057             for (case.ast.values) |range| {
   8058                 if (node_tags[range] != .switch_range) continue;
   8059                 ranges_len += 1;
   8060 
   8061                 const first = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].lhs);
   8062                 const last = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].rhs);
   8063                 try payloads.appendSlice(gpa, &[_]u32{
   8064                     @intFromEnum(first), @intFromEnum(last),
   8065                 });
   8066             }
   8067 
   8068             payloads.items[header_index] = items_len;
   8069             payloads.items[header_index + 1] = ranges_len;
   8070             break :blk header_index + 2;
   8071         } else if (case_node == special_node) blk: {
   8072             payloads.items[case_table_start] = header_index;
   8073             try payloads.resize(gpa, header_index + 1); // body_len
   8074             break :blk header_index;
   8075         } else blk: {
   8076             payloads.items[scalar_case_table + scalar_case_index] = header_index;
   8077             scalar_case_index += 1;
   8078             try payloads.resize(gpa, header_index + 2); // item, body_len
   8079             const item_node = case.ast.values[0];
   8080             const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node);
   8081             payloads.items[header_index] = @intFromEnum(item_inst);
   8082             break :blk header_index + 1;
   8083         };
   8084 
   8085         {
   8086             // temporarily stack case_scope on parent_gz
   8087             case_scope.instructions_top = parent_gz.instructions.items.len;
   8088             defer case_scope.unstack();
   8089 
   8090             if (dbg_var_name != .empty) {
   8091                 try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst);
   8092             }
   8093             if (dbg_var_tag_name != .empty) {
   8094                 try case_scope.addDbgVar(.dbg_var_val, dbg_var_tag_name, dbg_var_tag_inst);
   8095             }
   8096             const target_expr_node = case.ast.target_expr;
   8097             const case_result = try fullBodyExpr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node, .allow_branch_hint);
   8098             try checkUsed(parent_gz, &case_scope.base, sub_scope);
   8099             if (!parent_gz.refIsNoReturn(case_result)) {
   8100                 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node);
   8101             }
   8102 
   8103             const case_slice = case_scope.instructionsSlice();
   8104             const extra_insts: []const Zir.Inst.Index = if (has_tag_capture) &.{ switch_block, tag_inst } else &.{switch_block};
   8105             const body_len = astgen.countBodyLenAfterFixupsExtraRefs(case_slice, extra_insts);
   8106             try payloads.ensureUnusedCapacity(gpa, body_len);
   8107             payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
   8108                 .body_len = @intCast(body_len),
   8109                 .capture = capture,
   8110                 .is_inline = case.inline_token != null,
   8111                 .has_tag_capture = has_tag_capture,
   8112             });
   8113             appendBodyWithFixupsExtraRefsArrayList(astgen, payloads, case_slice, extra_insts);
   8114         }
   8115     }
   8116 
   8117     if (switch_full.label_token) |label_token| if (!block_scope.label.?.used) {
   8118         try astgen.appendErrorTok(label_token, "unused switch label", .{});
   8119     };
   8120 
   8121     // Now that the item expressions are generated we can add this.
   8122     try parent_gz.instructions.append(gpa, switch_block);
   8123 
   8124     try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).@"struct".fields.len +
   8125         @intFromBool(multi_cases_len != 0) +
   8126         @intFromBool(any_has_tag_capture) +
   8127         payloads.items.len - case_table_end +
   8128         (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).@"struct".fields.len);
   8129 
   8130     const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
   8131         .operand = raw_operand,
   8132         .bits = Zir.Inst.SwitchBlock.Bits{
   8133             .has_multi_cases = multi_cases_len != 0,
   8134             .has_else = special_prong == .@"else",
   8135             .has_under = special_prong == .under,
   8136             .any_has_tag_capture = any_has_tag_capture,
   8137             .any_non_inline_capture = any_non_inline_capture,
   8138             .has_continue = switch_full.label_token != null and block_scope.label.?.used_for_continue,
   8139             .scalar_cases_len = @intCast(scalar_cases_len),
   8140         },
   8141     });
   8142 
   8143     if (multi_cases_len != 0) {
   8144         astgen.extra.appendAssumeCapacity(multi_cases_len);
   8145     }
   8146 
   8147     if (any_has_tag_capture) {
   8148         astgen.extra.appendAssumeCapacity(@intFromEnum(tag_inst));
   8149     }
   8150 
   8151     const zir_datas = astgen.instructions.items(.data);
   8152     zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;
   8153 
   8154     for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| {
   8155         var body_len_index = start_index;
   8156         var end_index = start_index;
   8157         const table_index = case_table_start + i;
   8158         if (table_index < scalar_case_table) {
   8159             end_index += 1;
   8160         } else if (table_index < multi_case_table) {
   8161             body_len_index += 1;
   8162             end_index += 2;
   8163         } else {
   8164             body_len_index += 2;
   8165             const items_len = payloads.items[start_index];
   8166             const ranges_len = payloads.items[start_index + 1];
   8167             end_index += 3 + items_len + 2 * ranges_len;
   8168         }
   8169         const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
   8170         end_index += prong_info.body_len;
   8171         astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
   8172     }
   8173 
   8174     if (need_result_rvalue) {
   8175         return rvalue(parent_gz, ri, switch_block.toRef(), node);
   8176     } else {
   8177         return switch_block.toRef();
   8178     }
   8179 }
   8180 
   8181 fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   8182     const astgen = gz.astgen;
   8183     const tree = astgen.tree;
   8184     const node_datas = tree.nodes.items(.data);
   8185     const node_tags = tree.nodes.items(.tag);
   8186 
   8187     if (astgen.fn_block == null) {
   8188         return astgen.failNode(node, "'return' outside function scope", .{});
   8189     }
   8190 
   8191     if (gz.any_defer_node != 0) {
   8192         return astgen.failNodeNotes(node, "cannot return from defer expression", .{}, &.{
   8193             try astgen.errNoteNode(
   8194                 gz.any_defer_node,
   8195                 "defer expression here",
   8196                 .{},
   8197             ),
   8198         });
   8199     }
   8200 
   8201     // Ensure debug line/column information is emitted for this return expression.
   8202     // Then we will save the line/column so that we can emit another one that goes
   8203     // "backwards" because we want to evaluate the operand, but then put the debug
   8204     // info back at the return keyword for error return tracing.
   8205     if (!gz.is_comptime) {
   8206         try emitDbgNode(gz, node);
   8207     }
   8208     const ret_lc: LineColumn = .{ astgen.source_line - gz.decl_line, astgen.source_column };
   8209 
   8210     const defer_outer = &astgen.fn_block.?.base;
   8211 
   8212     const operand_node = node_datas[node].lhs;
   8213     if (operand_node == 0) {
   8214         // Returning a void value; skip error defers.
   8215         try genDefers(gz, defer_outer, scope, .normal_only);
   8216 
   8217         // As our last action before the return, "pop" the error trace if needed
   8218         _ = try gz.addRestoreErrRetIndex(.ret, .always, node);
   8219 
   8220         _ = try gz.addUnNode(.ret_node, .void_value, node);
   8221         return Zir.Inst.Ref.unreachable_value;
   8222     }
   8223 
   8224     if (node_tags[operand_node] == .error_value) {
   8225         // Hot path for `return error.Foo`. This bypasses result location logic as well as logic
   8226         // for detecting whether to add something to the function's inferred error set.
   8227         const ident_token = node_datas[operand_node].rhs;
   8228         const err_name_str_index = try astgen.identAsString(ident_token);
   8229         const defer_counts = countDefers(defer_outer, scope);
   8230         if (!defer_counts.need_err_code) {
   8231             try genDefers(gz, defer_outer, scope, .both_sans_err);
   8232             try emitDbgStmt(gz, ret_lc);
   8233             _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token);
   8234             return Zir.Inst.Ref.unreachable_value;
   8235         }
   8236         const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token);
   8237         try genDefers(gz, defer_outer, scope, .{ .both = err_code });
   8238         try emitDbgStmt(gz, ret_lc);
   8239         _ = try gz.addUnNode(.ret_node, err_code, node);
   8240         return Zir.Inst.Ref.unreachable_value;
   8241     }
   8242 
   8243     const ri: ResultInfo = if (astgen.nodes_need_rl.contains(node)) .{
   8244         .rl = .{ .ptr = .{ .inst = try gz.addNode(.ret_ptr, node) } },
   8245         .ctx = .@"return",
   8246     } else .{
   8247         .rl = .{ .coerced_ty = astgen.fn_ret_ty },
   8248         .ctx = .@"return",
   8249     };
   8250     const prev_anon_name_strategy = gz.anon_name_strategy;
   8251     gz.anon_name_strategy = .func;
   8252     const operand = try reachableExpr(gz, scope, ri, operand_node, node);
   8253     gz.anon_name_strategy = prev_anon_name_strategy;
   8254 
   8255     switch (nodeMayEvalToError(tree, operand_node)) {
   8256         .never => {
   8257             // Returning a value that cannot be an error; skip error defers.
   8258             try genDefers(gz, defer_outer, scope, .normal_only);
   8259 
   8260             // As our last action before the return, "pop" the error trace if needed
   8261             _ = try gz.addRestoreErrRetIndex(.ret, .always, node);
   8262 
   8263             try emitDbgStmt(gz, ret_lc);
   8264             try gz.addRet(ri, operand, node);
   8265             return Zir.Inst.Ref.unreachable_value;
   8266         },
   8267         .always => {
   8268             // Value is always an error. Emit both error defers and regular defers.
   8269             const err_code = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
   8270             try genDefers(gz, defer_outer, scope, .{ .both = err_code });
   8271             try emitDbgStmt(gz, ret_lc);
   8272             try gz.addRet(ri, operand, node);
   8273             return Zir.Inst.Ref.unreachable_value;
   8274         },
   8275         .maybe => {
   8276             const defer_counts = countDefers(defer_outer, scope);
   8277             if (!defer_counts.have_err) {
   8278                 // Only regular defers; no branch needed.
   8279                 try genDefers(gz, defer_outer, scope, .normal_only);
   8280                 try emitDbgStmt(gz, ret_lc);
   8281 
   8282                 // As our last action before the return, "pop" the error trace if needed
   8283                 const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
   8284                 _ = try gz.addRestoreErrRetIndex(.ret, .{ .if_non_error = result }, node);
   8285 
   8286                 try gz.addRet(ri, operand, node);
   8287                 return Zir.Inst.Ref.unreachable_value;
   8288             }
   8289 
   8290             // Emit conditional branch for generating errdefers.
   8291             const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
   8292             const is_non_err = try gz.addUnNode(.ret_is_non_err, result, node);
   8293             const condbr = try gz.addCondBr(.condbr, node);
   8294 
   8295             var then_scope = gz.makeSubBlock(scope);
   8296             defer then_scope.unstack();
   8297 
   8298             try genDefers(&then_scope, defer_outer, scope, .normal_only);
   8299 
   8300             // As our last action before the return, "pop" the error trace if needed
   8301             _ = try then_scope.addRestoreErrRetIndex(.ret, .always, node);
   8302 
   8303             try emitDbgStmt(&then_scope, ret_lc);
   8304             try then_scope.addRet(ri, operand, node);
   8305 
   8306             var else_scope = gz.makeSubBlock(scope);
   8307             defer else_scope.unstack();
   8308 
   8309             const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{
   8310                 .both = try else_scope.addUnNode(.err_union_code, result, node),
   8311             };
   8312             try genDefers(&else_scope, defer_outer, scope, which_ones);
   8313             try emitDbgStmt(&else_scope, ret_lc);
   8314             try else_scope.addRet(ri, operand, node);
   8315 
   8316             try setCondBrPayload(condbr, is_non_err, &then_scope, &else_scope);
   8317 
   8318             return Zir.Inst.Ref.unreachable_value;
   8319         },
   8320     }
   8321 }
   8322 
   8323 /// Parses the string `buf` as a base 10 integer of type `u16`.
   8324 ///
   8325 /// Unlike std.fmt.parseInt, does not allow the '_' character in `buf`.
   8326 fn parseBitCount(buf: []const u8) std.fmt.ParseIntError!u16 {
   8327     if (buf.len == 0) return error.InvalidCharacter;
   8328 
   8329     var x: u16 = 0;
   8330 
   8331     for (buf) |c| {
   8332         const digit = switch (c) {
   8333             '0'...'9' => c - '0',
   8334             else => return error.InvalidCharacter,
   8335         };
   8336 
   8337         if (x != 0) x = try std.math.mul(u16, x, 10);
   8338         x = try std.math.add(u16, x, digit);
   8339     }
   8340 
   8341     return x;
   8342 }
   8343 
   8344 fn identifier(
   8345     gz: *GenZir,
   8346     scope: *Scope,
   8347     ri: ResultInfo,
   8348     ident: Ast.Node.Index,
   8349 ) InnerError!Zir.Inst.Ref {
   8350     const astgen = gz.astgen;
   8351     const tree = astgen.tree;
   8352     const main_tokens = tree.nodes.items(.main_token);
   8353 
   8354     const ident_token = main_tokens[ident];
   8355     const ident_name_raw = tree.tokenSlice(ident_token);
   8356     if (mem.eql(u8, ident_name_raw, "_")) {
   8357         return astgen.failNode(ident, "'_' used as an identifier without @\"_\" syntax", .{});
   8358     }
   8359 
   8360     // if not @"" syntax, just use raw token slice
   8361     if (ident_name_raw[0] != '@') {
   8362         if (primitive_instrs.get(ident_name_raw)) |zir_const_ref| {
   8363             return rvalue(gz, ri, zir_const_ref, ident);
   8364         }
   8365 
   8366         if (ident_name_raw.len >= 2) integer: {
   8367             const first_c = ident_name_raw[0];
   8368             if (first_c == 'i' or first_c == 'u') {
   8369                 const signedness: std.builtin.Signedness = switch (first_c == 'i') {
   8370                     true => .signed,
   8371                     false => .unsigned,
   8372                 };
   8373                 if (ident_name_raw.len >= 3 and ident_name_raw[1] == '0') {
   8374                     return astgen.failNode(
   8375                         ident,
   8376                         "primitive integer type '{s}' has leading zero",
   8377                         .{ident_name_raw},
   8378                     );
   8379                 }
   8380                 const bit_count = parseBitCount(ident_name_raw[1..]) catch |err| switch (err) {
   8381                     error.Overflow => return astgen.failNode(
   8382                         ident,
   8383                         "primitive integer type '{s}' exceeds maximum bit width of 65535",
   8384                         .{ident_name_raw},
   8385                     ),
   8386                     error.InvalidCharacter => break :integer,
   8387                 };
   8388                 const result = try gz.add(.{
   8389                     .tag = .int_type,
   8390                     .data = .{ .int_type = .{
   8391                         .src_node = gz.nodeIndexToRelative(ident),
   8392                         .signedness = signedness,
   8393                         .bit_count = bit_count,
   8394                     } },
   8395                 });
   8396                 return rvalue(gz, ri, result, ident);
   8397             }
   8398         }
   8399     }
   8400 
   8401     // Local variables, including function parameters.
   8402     return localVarRef(gz, scope, ri, ident, ident_token);
   8403 }
   8404 
   8405 fn localVarRef(
   8406     gz: *GenZir,
   8407     scope: *Scope,
   8408     ri: ResultInfo,
   8409     ident: Ast.Node.Index,
   8410     ident_token: Ast.TokenIndex,
   8411 ) InnerError!Zir.Inst.Ref {
   8412     const astgen = gz.astgen;
   8413     const name_str_index = try astgen.identAsString(ident_token);
   8414     var s = scope;
   8415     var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
   8416     var found_needs_tunnel: bool = undefined; // defined when `found_already != null`
   8417     var found_namespaces_out: u32 = undefined; // defined when `found_already != null`
   8418 
   8419     // The number of namespaces above `gz` we currently are
   8420     var num_namespaces_out: u32 = 0;
   8421     // defined by `num_namespaces_out != 0`
   8422     var capturing_namespace: *Scope.Namespace = undefined;
   8423 
   8424     while (true) switch (s.tag) {
   8425         .local_val => {
   8426             const local_val = s.cast(Scope.LocalVal).?;
   8427 
   8428             if (local_val.name == name_str_index) {
   8429                 // Locals cannot shadow anything, so we do not need to look for ambiguous
   8430                 // references in this case.
   8431                 if (ri.rl == .discard and ri.ctx == .assignment) {
   8432                     local_val.discarded = ident_token;
   8433                 } else {
   8434                     local_val.used = ident_token;
   8435                 }
   8436 
   8437                 const value_inst = if (num_namespaces_out != 0) try tunnelThroughClosure(
   8438                     gz,
   8439                     ident,
   8440                     num_namespaces_out,
   8441                     .{ .ref = local_val.inst },
   8442                     .{ .token = local_val.token_src },
   8443                 ) else local_val.inst;
   8444 
   8445                 return rvalueNoCoercePreRef(gz, ri, value_inst, ident);
   8446             }
   8447             s = local_val.parent;
   8448         },
   8449         .local_ptr => {
   8450             const local_ptr = s.cast(Scope.LocalPtr).?;
   8451             if (local_ptr.name == name_str_index) {
   8452                 if (ri.rl == .discard and ri.ctx == .assignment) {
   8453                     local_ptr.discarded = ident_token;
   8454                 } else {
   8455                     local_ptr.used = ident_token;
   8456                 }
   8457 
   8458                 // Can't close over a runtime variable
   8459                 if (num_namespaces_out != 0 and !local_ptr.maybe_comptime and !gz.is_typeof) {
   8460                     const ident_name = try astgen.identifierTokenString(ident_token);
   8461                     return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{
   8462                         try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}),
   8463                         try astgen.errNoteNode(capturing_namespace.node, "crosses namespace boundary here", .{}),
   8464                     });
   8465                 }
   8466 
   8467                 switch (ri.rl) {
   8468                     .ref, .ref_coerced_ty => {
   8469                         const ptr_inst = if (num_namespaces_out != 0) try tunnelThroughClosure(
   8470                             gz,
   8471                             ident,
   8472                             num_namespaces_out,
   8473                             .{ .ref = local_ptr.ptr },
   8474                             .{ .token = local_ptr.token_src },
   8475                         ) else local_ptr.ptr;
   8476                         local_ptr.used_as_lvalue = true;
   8477                         return ptr_inst;
   8478                     },
   8479                     else => {
   8480                         const val_inst = if (num_namespaces_out != 0) try tunnelThroughClosure(
   8481                             gz,
   8482                             ident,
   8483                             num_namespaces_out,
   8484                             .{ .ref_load = local_ptr.ptr },
   8485                             .{ .token = local_ptr.token_src },
   8486                         ) else try gz.addUnNode(.load, local_ptr.ptr, ident);
   8487                         return rvalueNoCoercePreRef(gz, ri, val_inst, ident);
   8488                     },
   8489                 }
   8490             }
   8491             s = local_ptr.parent;
   8492         },
   8493         .gen_zir => s = s.cast(GenZir).?.parent,
   8494         .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
   8495         .namespace => {
   8496             const ns = s.cast(Scope.Namespace).?;
   8497             if (ns.decls.get(name_str_index)) |i| {
   8498                 if (found_already) |f| {
   8499                     return astgen.failNodeNotes(ident, "ambiguous reference", .{}, &.{
   8500                         try astgen.errNoteNode(f, "declared here", .{}),
   8501                         try astgen.errNoteNode(i, "also declared here", .{}),
   8502                     });
   8503                 }
   8504                 // We found a match but must continue looking for ambiguous references to decls.
   8505                 found_already = i;
   8506                 found_needs_tunnel = ns.maybe_generic;
   8507                 found_namespaces_out = num_namespaces_out;
   8508             }
   8509             num_namespaces_out += 1;
   8510             capturing_namespace = ns;
   8511             s = ns.parent;
   8512         },
   8513         .top => break,
   8514     };
   8515     if (found_already == null) {
   8516         const ident_name = try astgen.identifierTokenString(ident_token);
   8517         return astgen.failNode(ident, "use of undeclared identifier '{s}'", .{ident_name});
   8518     }
   8519 
   8520     // Decl references happen by name rather than ZIR index so that when unrelated
   8521     // decls are modified, ZIR code containing references to them can be unmodified.
   8522 
   8523     if (found_namespaces_out > 0 and found_needs_tunnel) {
   8524         switch (ri.rl) {
   8525             .ref, .ref_coerced_ty => return tunnelThroughClosure(
   8526                 gz,
   8527                 ident,
   8528                 found_namespaces_out,
   8529                 .{ .decl_ref = name_str_index },
   8530                 .{ .node = found_already.? },
   8531             ),
   8532             else => {
   8533                 const result = try tunnelThroughClosure(
   8534                     gz,
   8535                     ident,
   8536                     found_namespaces_out,
   8537                     .{ .decl_val = name_str_index },
   8538                     .{ .node = found_already.? },
   8539                 );
   8540                 return rvalueNoCoercePreRef(gz, ri, result, ident);
   8541             },
   8542         }
   8543     }
   8544 
   8545     switch (ri.rl) {
   8546         .ref, .ref_coerced_ty => return gz.addStrTok(.decl_ref, name_str_index, ident_token),
   8547         else => {
   8548             const result = try gz.addStrTok(.decl_val, name_str_index, ident_token);
   8549             return rvalueNoCoercePreRef(gz, ri, result, ident);
   8550         },
   8551     }
   8552 }
   8553 
   8554 /// Access a ZIR instruction through closure. May tunnel through arbitrarily
   8555 /// many namespaces, adding closure captures as required.
   8556 /// Returns the index of the `closure_get` instruction added to `gz`.
   8557 fn tunnelThroughClosure(
   8558     gz: *GenZir,
   8559     /// The node which references the value to be captured.
   8560     inner_ref_node: Ast.Node.Index,
   8561     /// The number of namespaces being tunnelled through. At least 1.
   8562     num_tunnels: u32,
   8563     /// The value being captured.
   8564     value: union(enum) {
   8565         ref: Zir.Inst.Ref,
   8566         ref_load: Zir.Inst.Ref,
   8567         decl_val: Zir.NullTerminatedString,
   8568         decl_ref: Zir.NullTerminatedString,
   8569     },
   8570     /// The location of the value's declaration.
   8571     decl_src: union(enum) {
   8572         token: Ast.TokenIndex,
   8573         node: Ast.Node.Index,
   8574     },
   8575 ) !Zir.Inst.Ref {
   8576     switch (value) {
   8577         .ref => |v| if (v.toIndex() == null) return v, // trivial value; do not need tunnel
   8578         .ref_load => |v| assert(v.toIndex() != null), // there are no constant pointer refs
   8579         .decl_val, .decl_ref => {},
   8580     }
   8581 
   8582     const astgen = gz.astgen;
   8583     const gpa = astgen.gpa;
   8584 
   8585     // Otherwise we need a tunnel. First, figure out the path of namespaces we
   8586     // are tunneling through. This is usually only going to be one or two, so
   8587     // use an SFBA to optimize for the common case.
   8588     var sfba = std.heap.stackFallback(@sizeOf(usize) * 2, astgen.arena);
   8589     var intermediate_tunnels = try sfba.get().alloc(*Scope.Namespace, num_tunnels - 1);
   8590 
   8591     const root_ns = ns: {
   8592         var i: usize = num_tunnels - 1;
   8593         var scope: *Scope = gz.parent;
   8594         while (i > 0) {
   8595             if (scope.cast(Scope.Namespace)) |mid_ns| {
   8596                 i -= 1;
   8597                 intermediate_tunnels[i] = mid_ns;
   8598             }
   8599             scope = scope.parent().?;
   8600         }
   8601         while (true) {
   8602             if (scope.cast(Scope.Namespace)) |ns| break :ns ns;
   8603             scope = scope.parent().?;
   8604         }
   8605     };
   8606 
   8607     // Now that we know the scopes we're tunneling through, begin adding
   8608     // captures as required, starting with the outermost namespace.
   8609     const root_capture = Zir.Inst.Capture.wrap(switch (value) {
   8610         .ref => |v| .{ .instruction = v.toIndex().? },
   8611         .ref_load => |v| .{ .instruction_load = v.toIndex().? },
   8612         .decl_val => |str| .{ .decl_val = str },
   8613         .decl_ref => |str| .{ .decl_ref = str },
   8614     });
   8615     var cur_capture_index = std.math.cast(
   8616         u16,
   8617         (try root_ns.captures.getOrPut(gpa, root_capture)).index,
   8618     ) orelse return astgen.failNodeNotes(root_ns.node, "this compiler implementation only supports up to 65536 captures per namespace", .{}, &.{
   8619         switch (decl_src) {
   8620             .token => |t| try astgen.errNoteTok(t, "captured value here", .{}),
   8621             .node => |n| try astgen.errNoteNode(n, "captured value here", .{}),
   8622         },
   8623         try astgen.errNoteNode(inner_ref_node, "value used here", .{}),
   8624     });
   8625 
   8626     for (intermediate_tunnels) |tunnel_ns| {
   8627         cur_capture_index = std.math.cast(
   8628             u16,
   8629             (try tunnel_ns.captures.getOrPut(gpa, Zir.Inst.Capture.wrap(.{ .nested = cur_capture_index }))).index,
   8630         ) orelse return astgen.failNodeNotes(tunnel_ns.node, "this compiler implementation only supports up to 65536 captures per namespace", .{}, &.{
   8631             switch (decl_src) {
   8632                 .token => |t| try astgen.errNoteTok(t, "captured value here", .{}),
   8633                 .node => |n| try astgen.errNoteNode(n, "captured value here", .{}),
   8634             },
   8635             try astgen.errNoteNode(inner_ref_node, "value used here", .{}),
   8636         });
   8637     }
   8638 
   8639     // Incorporate the capture index into the source hash, so that changes in
   8640     // the order of captures cause suitable re-analysis.
   8641     astgen.src_hasher.update(std.mem.asBytes(&cur_capture_index));
   8642 
   8643     // Add an instruction to get the value from the closure.
   8644     return gz.addExtendedNodeSmall(.closure_get, inner_ref_node, cur_capture_index);
   8645 }
   8646 
   8647 fn stringLiteral(
   8648     gz: *GenZir,
   8649     ri: ResultInfo,
   8650     node: Ast.Node.Index,
   8651 ) InnerError!Zir.Inst.Ref {
   8652     const astgen = gz.astgen;
   8653     const tree = astgen.tree;
   8654     const main_tokens = tree.nodes.items(.main_token);
   8655     const str_lit_token = main_tokens[node];
   8656     const str = try astgen.strLitAsString(str_lit_token);
   8657     const result = try gz.add(.{
   8658         .tag = .str,
   8659         .data = .{ .str = .{
   8660             .start = str.index,
   8661             .len = str.len,
   8662         } },
   8663     });
   8664     return rvalue(gz, ri, result, node);
   8665 }
   8666 
   8667 fn multilineStringLiteral(
   8668     gz: *GenZir,
   8669     ri: ResultInfo,
   8670     node: Ast.Node.Index,
   8671 ) InnerError!Zir.Inst.Ref {
   8672     const astgen = gz.astgen;
   8673     const str = try astgen.strLitNodeAsString(node);
   8674     const result = try gz.add(.{
   8675         .tag = .str,
   8676         .data = .{ .str = .{
   8677             .start = str.index,
   8678             .len = str.len,
   8679         } },
   8680     });
   8681     return rvalue(gz, ri, result, node);
   8682 }
   8683 
   8684 fn charLiteral(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   8685     const astgen = gz.astgen;
   8686     const tree = astgen.tree;
   8687     const main_tokens = tree.nodes.items(.main_token);
   8688     const main_token = main_tokens[node];
   8689     const slice = tree.tokenSlice(main_token);
   8690 
   8691     switch (std.zig.parseCharLiteral(slice)) {
   8692         .success => |codepoint| {
   8693             const result = try gz.addInt(codepoint);
   8694             return rvalue(gz, ri, result, node);
   8695         },
   8696         .failure => |err| return astgen.failWithStrLitError(err, main_token, slice, 0),
   8697     }
   8698 }
   8699 
   8700 const Sign = enum { negative, positive };
   8701 
   8702 fn numberLiteral(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index, source_node: Ast.Node.Index, sign: Sign) InnerError!Zir.Inst.Ref {
   8703     const astgen = gz.astgen;
   8704     const tree = astgen.tree;
   8705     const main_tokens = tree.nodes.items(.main_token);
   8706     const num_token = main_tokens[node];
   8707     const bytes = tree.tokenSlice(num_token);
   8708 
   8709     const result: Zir.Inst.Ref = switch (std.zig.parseNumberLiteral(bytes)) {
   8710         .int => |num| switch (num) {
   8711             0 => if (sign == .positive) .zero else return astgen.failTokNotes(
   8712                 num_token,
   8713                 "integer literal '-0' is ambiguous",
   8714                 .{},
   8715                 &.{
   8716                     try astgen.errNoteTok(num_token, "use '0' for an integer zero", .{}),
   8717                     try astgen.errNoteTok(num_token, "use '-0.0' for a floating-point signed zero", .{}),
   8718                 },
   8719             ),
   8720             1 => {
   8721                 // Handle the negation here!
   8722                 const result: Zir.Inst.Ref = switch (sign) {
   8723                     .positive => .one,
   8724                     .negative => .negative_one,
   8725                 };
   8726                 return rvalue(gz, ri, result, source_node);
   8727             },
   8728             else => try gz.addInt(num),
   8729         },
   8730         .big_int => |base| big: {
   8731             const gpa = astgen.gpa;
   8732             var big_int = try std.math.big.int.Managed.init(gpa);
   8733             defer big_int.deinit();
   8734             const prefix_offset: usize = if (base == .decimal) 0 else 2;
   8735             big_int.setString(@intFromEnum(base), bytes[prefix_offset..]) catch |err| switch (err) {
   8736                 error.InvalidCharacter => unreachable, // caught in `parseNumberLiteral`
   8737                 error.InvalidBase => unreachable, // we only pass 16, 8, 2, see above
   8738                 error.OutOfMemory => return error.OutOfMemory,
   8739             };
   8740 
   8741             const limbs = big_int.limbs[0..big_int.len()];
   8742             assert(big_int.isPositive());
   8743             break :big try gz.addIntBig(limbs);
   8744         },
   8745         .float => {
   8746             const unsigned_float_number = std.fmt.parseFloat(f128, bytes) catch |err| switch (err) {
   8747                 error.InvalidCharacter => unreachable, // validated by tokenizer
   8748             };
   8749             const float_number = switch (sign) {
   8750                 .negative => -unsigned_float_number,
   8751                 .positive => unsigned_float_number,
   8752             };
   8753             // If the value fits into a f64 without losing any precision, store it that way.
   8754             @setFloatMode(.strict);
   8755             const smaller_float: f64 = @floatCast(float_number);
   8756             const bigger_again: f128 = smaller_float;
   8757             if (bigger_again == float_number) {
   8758                 const result = try gz.addFloat(smaller_float);
   8759                 return rvalue(gz, ri, result, source_node);
   8760             }
   8761             // We need to use 128 bits. Break the float into 4 u32 values so we can
   8762             // put it into the `extra` array.
   8763             const int_bits: u128 = @bitCast(float_number);
   8764             const result = try gz.addPlNode(.float128, node, Zir.Inst.Float128{
   8765                 .piece0 = @truncate(int_bits),
   8766                 .piece1 = @truncate(int_bits >> 32),
   8767                 .piece2 = @truncate(int_bits >> 64),
   8768                 .piece3 = @truncate(int_bits >> 96),
   8769             });
   8770             return rvalue(gz, ri, result, source_node);
   8771         },
   8772         .failure => |err| return astgen.failWithNumberError(err, num_token, bytes),
   8773     };
   8774 
   8775     if (sign == .positive) {
   8776         return rvalue(gz, ri, result, source_node);
   8777     } else {
   8778         const negated = try gz.addUnNode(.negate, result, source_node);
   8779         return rvalue(gz, ri, negated, source_node);
   8780     }
   8781 }
   8782 
   8783 fn failWithNumberError(astgen: *AstGen, err: std.zig.number_literal.Error, token: Ast.TokenIndex, bytes: []const u8) InnerError {
   8784     const is_float = std.mem.indexOfScalar(u8, bytes, '.') != null;
   8785     switch (err) {
   8786         .leading_zero => if (is_float) {
   8787             return astgen.failTok(token, "number '{s}' has leading zero", .{bytes});
   8788         } else {
   8789             return astgen.failTokNotes(token, "number '{s}' has leading zero", .{bytes}, &.{
   8790                 try astgen.errNoteTok(token, "use '0o' prefix for octal literals", .{}),
   8791             });
   8792         },
   8793         .digit_after_base => return astgen.failTok(token, "expected a digit after base prefix", .{}),
   8794         .upper_case_base => |i| return astgen.failOff(token, @intCast(i), "base prefix must be lowercase", .{}),
   8795         .invalid_float_base => |i| return astgen.failOff(token, @intCast(i), "invalid base for float literal", .{}),
   8796         .repeated_underscore => |i| return astgen.failOff(token, @intCast(i), "repeated digit separator", .{}),
   8797         .invalid_underscore_after_special => |i| return astgen.failOff(token, @intCast(i), "expected digit before digit separator", .{}),
   8798         .invalid_digit => |info| return astgen.failOff(token, @intCast(info.i), "invalid digit '{c}' for {s} base", .{ bytes[info.i], @tagName(info.base) }),
   8799         .invalid_digit_exponent => |i| return astgen.failOff(token, @intCast(i), "invalid digit '{c}' in exponent", .{bytes[i]}),
   8800         .duplicate_exponent => |i| return astgen.failOff(token, @intCast(i), "duplicate exponent", .{}),
   8801         .exponent_after_underscore => |i| return astgen.failOff(token, @intCast(i), "expected digit before exponent", .{}),
   8802         .special_after_underscore => |i| return astgen.failOff(token, @intCast(i), "expected digit before '{c}'", .{bytes[i]}),
   8803         .trailing_special => |i| return astgen.failOff(token, @intCast(i), "expected digit after '{c}'", .{bytes[i - 1]}),
   8804         .trailing_underscore => |i| return astgen.failOff(token, @intCast(i), "trailing digit separator", .{}),
   8805         .duplicate_period => unreachable, // Validated by tokenizer
   8806         .invalid_character => unreachable, // Validated by tokenizer
   8807         .invalid_exponent_sign => |i| {
   8808             assert(bytes.len >= 2 and bytes[0] == '0' and bytes[1] == 'x'); // Validated by tokenizer
   8809             return astgen.failOff(token, @intCast(i), "sign '{c}' cannot follow digit '{c}' in hex base", .{ bytes[i], bytes[i - 1] });
   8810         },
   8811         .period_after_exponent => |i| return astgen.failOff(token, @intCast(i), "unexpected period after exponent", .{}),
   8812     }
   8813 }
   8814 
   8815 fn asmExpr(
   8816     gz: *GenZir,
   8817     scope: *Scope,
   8818     ri: ResultInfo,
   8819     node: Ast.Node.Index,
   8820     full: Ast.full.Asm,
   8821 ) InnerError!Zir.Inst.Ref {
   8822     const astgen = gz.astgen;
   8823     const tree = astgen.tree;
   8824     const main_tokens = tree.nodes.items(.main_token);
   8825     const node_datas = tree.nodes.items(.data);
   8826     const node_tags = tree.nodes.items(.tag);
   8827     const token_tags = tree.tokens.items(.tag);
   8828 
   8829     const TagAndTmpl = struct { tag: Zir.Inst.Extended, tmpl: Zir.NullTerminatedString };
   8830     const tag_and_tmpl: TagAndTmpl = switch (node_tags[full.ast.template]) {
   8831         .string_literal => .{
   8832             .tag = .@"asm",
   8833             .tmpl = (try astgen.strLitAsString(main_tokens[full.ast.template])).index,
   8834         },
   8835         .multiline_string_literal => .{
   8836             .tag = .@"asm",
   8837             .tmpl = (try astgen.strLitNodeAsString(full.ast.template)).index,
   8838         },
   8839         else => .{
   8840             .tag = .asm_expr,
   8841             .tmpl = @enumFromInt(@intFromEnum(try comptimeExpr(gz, scope, .{ .rl = .none }, full.ast.template))),
   8842         },
   8843     };
   8844 
   8845     // See https://github.com/ziglang/zig/issues/215 and related issues discussing
   8846     // possible inline assembly improvements. Until then here is status quo AstGen
   8847     // for assembly syntax. It's used by std lib crypto aesni.zig.
   8848     const is_container_asm = astgen.fn_block == null;
   8849     if (is_container_asm) {
   8850         if (full.volatile_token) |t|
   8851             return astgen.failTok(t, "volatile is meaningless on global assembly", .{});
   8852         if (full.outputs.len != 0 or full.inputs.len != 0 or full.first_clobber != null)
   8853             return astgen.failNode(node, "global assembly cannot have inputs, outputs, or clobbers", .{});
   8854     } else {
   8855         if (full.outputs.len == 0 and full.volatile_token == null) {
   8856             return astgen.failNode(node, "assembly expression with no output must be marked volatile", .{});
   8857         }
   8858     }
   8859     if (full.outputs.len > 32) {
   8860         return astgen.failNode(full.outputs[32], "too many asm outputs", .{});
   8861     }
   8862     var outputs_buffer: [32]Zir.Inst.Asm.Output = undefined;
   8863     const outputs = outputs_buffer[0..full.outputs.len];
   8864 
   8865     var output_type_bits: u32 = 0;
   8866 
   8867     for (full.outputs, 0..) |output_node, i| {
   8868         const symbolic_name = main_tokens[output_node];
   8869         const name = try astgen.identAsString(symbolic_name);
   8870         const constraint_token = symbolic_name + 2;
   8871         const constraint = (try astgen.strLitAsString(constraint_token)).index;
   8872         const has_arrow = token_tags[symbolic_name + 4] == .arrow;
   8873         if (has_arrow) {
   8874             if (output_type_bits != 0) {
   8875                 return astgen.failNode(output_node, "inline assembly allows up to one output value", .{});
   8876             }
   8877             output_type_bits |= @as(u32, 1) << @intCast(i);
   8878             const out_type_node = node_datas[output_node].lhs;
   8879             const out_type_inst = try typeExpr(gz, scope, out_type_node);
   8880             outputs[i] = .{
   8881                 .name = name,
   8882                 .constraint = constraint,
   8883                 .operand = out_type_inst,
   8884             };
   8885         } else {
   8886             const ident_token = symbolic_name + 4;
   8887             // TODO have a look at #215 and related issues and decide how to
   8888             // handle outputs. Do we want this to be identifiers?
   8889             // Or maybe we want to force this to be expressions with a pointer type.
   8890             outputs[i] = .{
   8891                 .name = name,
   8892                 .constraint = constraint,
   8893                 .operand = try localVarRef(gz, scope, .{ .rl = .ref }, node, ident_token),
   8894             };
   8895         }
   8896     }
   8897 
   8898     if (full.inputs.len > 32) {
   8899         return astgen.failNode(full.inputs[32], "too many asm inputs", .{});
   8900     }
   8901     var inputs_buffer: [32]Zir.Inst.Asm.Input = undefined;
   8902     const inputs = inputs_buffer[0..full.inputs.len];
   8903 
   8904     for (full.inputs, 0..) |input_node, i| {
   8905         const symbolic_name = main_tokens[input_node];
   8906         const name = try astgen.identAsString(symbolic_name);
   8907         const constraint_token = symbolic_name + 2;
   8908         const constraint = (try astgen.strLitAsString(constraint_token)).index;
   8909         const operand = try expr(gz, scope, .{ .rl = .none }, node_datas[input_node].lhs);
   8910         inputs[i] = .{
   8911             .name = name,
   8912             .constraint = constraint,
   8913             .operand = operand,
   8914         };
   8915     }
   8916 
   8917     var clobbers_buffer: [32]u32 = undefined;
   8918     var clobber_i: usize = 0;
   8919     if (full.first_clobber) |first_clobber| clobbers: {
   8920         // asm ("foo" ::: "a", "b")
   8921         // asm ("foo" ::: "a", "b",)
   8922         var tok_i = first_clobber;
   8923         while (true) : (tok_i += 1) {
   8924             if (clobber_i >= clobbers_buffer.len) {
   8925                 return astgen.failTok(tok_i, "too many asm clobbers", .{});
   8926             }
   8927             clobbers_buffer[clobber_i] = @intFromEnum((try astgen.strLitAsString(tok_i)).index);
   8928             clobber_i += 1;
   8929             tok_i += 1;
   8930             switch (token_tags[tok_i]) {
   8931                 .r_paren => break :clobbers,
   8932                 .comma => {
   8933                     if (token_tags[tok_i + 1] == .r_paren) {
   8934                         break :clobbers;
   8935                     } else {
   8936                         continue;
   8937                     }
   8938                 },
   8939                 else => unreachable,
   8940             }
   8941         }
   8942     }
   8943 
   8944     const result = try gz.addAsm(.{
   8945         .tag = tag_and_tmpl.tag,
   8946         .node = node,
   8947         .asm_source = tag_and_tmpl.tmpl,
   8948         .is_volatile = full.volatile_token != null,
   8949         .output_type_bits = output_type_bits,
   8950         .outputs = outputs,
   8951         .inputs = inputs,
   8952         .clobbers = clobbers_buffer[0..clobber_i],
   8953     });
   8954     return rvalue(gz, ri, result, node);
   8955 }
   8956 
   8957 fn as(
   8958     gz: *GenZir,
   8959     scope: *Scope,
   8960     ri: ResultInfo,
   8961     node: Ast.Node.Index,
   8962     lhs: Ast.Node.Index,
   8963     rhs: Ast.Node.Index,
   8964 ) InnerError!Zir.Inst.Ref {
   8965     const dest_type = try typeExpr(gz, scope, lhs);
   8966     const result = try reachableExpr(gz, scope, .{ .rl = .{ .ty = dest_type } }, rhs, node);
   8967     return rvalue(gz, ri, result, node);
   8968 }
   8969 
   8970 fn unionInit(
   8971     gz: *GenZir,
   8972     scope: *Scope,
   8973     ri: ResultInfo,
   8974     node: Ast.Node.Index,
   8975     params: []const Ast.Node.Index,
   8976 ) InnerError!Zir.Inst.Ref {
   8977     const union_type = try typeExpr(gz, scope, params[0]);
   8978     const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]);
   8979     const field_type = try gz.addPlNode(.field_type_ref, node, Zir.Inst.FieldTypeRef{
   8980         .container_type = union_type,
   8981         .field_name = field_name,
   8982     });
   8983     const init = try reachableExpr(gz, scope, .{ .rl = .{ .ty = field_type } }, params[2], node);
   8984     const result = try gz.addPlNode(.union_init, node, Zir.Inst.UnionInit{
   8985         .union_type = union_type,
   8986         .init = init,
   8987         .field_name = field_name,
   8988     });
   8989     return rvalue(gz, ri, result, node);
   8990 }
   8991 
   8992 fn bitCast(
   8993     gz: *GenZir,
   8994     scope: *Scope,
   8995     ri: ResultInfo,
   8996     node: Ast.Node.Index,
   8997     operand_node: Ast.Node.Index,
   8998 ) InnerError!Zir.Inst.Ref {
   8999     const dest_type = try ri.rl.resultTypeForCast(gz, node, "@bitCast");
   9000     const operand = try reachableExpr(gz, scope, .{ .rl = .none }, operand_node, node);
   9001     const result = try gz.addPlNode(.bitcast, node, Zir.Inst.Bin{
   9002         .lhs = dest_type,
   9003         .rhs = operand,
   9004     });
   9005     return rvalue(gz, ri, result, node);
   9006 }
   9007 
   9008 /// Handle one or more nested pointer cast builtins:
   9009 /// * @ptrCast
   9010 /// * @alignCast
   9011 /// * @addrSpaceCast
   9012 /// * @constCast
   9013 /// * @volatileCast
   9014 /// Any sequence of such builtins is treated as a single operation. This allowed
   9015 /// for sequences like `@ptrCast(@alignCast(ptr))` to work correctly despite the
   9016 /// intermediate result type being unknown.
   9017 fn ptrCast(
   9018     gz: *GenZir,
   9019     scope: *Scope,
   9020     ri: ResultInfo,
   9021     root_node: Ast.Node.Index,
   9022 ) InnerError!Zir.Inst.Ref {
   9023     const astgen = gz.astgen;
   9024     const tree = astgen.tree;
   9025     const main_tokens = tree.nodes.items(.main_token);
   9026     const node_datas = tree.nodes.items(.data);
   9027     const node_tags = tree.nodes.items(.tag);
   9028 
   9029     const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?;
   9030     var flags: Zir.Inst.FullPtrCastFlags = .{};
   9031 
   9032     // Note that all pointer cast builtins have one parameter, so we only need
   9033     // to handle `builtin_call_two`.
   9034     var node = root_node;
   9035     while (true) {
   9036         switch (node_tags[node]) {
   9037             .builtin_call_two, .builtin_call_two_comma => {},
   9038             .grouped_expression => {
   9039                 // Handle the chaining even with redundant parentheses
   9040                 node = node_datas[node].lhs;
   9041                 continue;
   9042             },
   9043             else => break,
   9044         }
   9045 
   9046         if (node_datas[node].lhs == 0) break; // 0 args
   9047 
   9048         const builtin_token = main_tokens[node];
   9049         const builtin_name = tree.tokenSlice(builtin_token);
   9050         const info = BuiltinFn.list.get(builtin_name) orelse break;
   9051         if (node_datas[node].rhs == 0) {
   9052             // 1 arg
   9053             if (info.param_count != 1) break;
   9054 
   9055             switch (info.tag) {
   9056                 else => break,
   9057                 inline .ptr_cast,
   9058                 .align_cast,
   9059                 .addrspace_cast,
   9060                 .const_cast,
   9061                 .volatile_cast,
   9062                 => |tag| {
   9063                     if (@field(flags, @tagName(tag))) {
   9064                         return astgen.failNode(node, "redundant {s}", .{builtin_name});
   9065                     }
   9066                     @field(flags, @tagName(tag)) = true;
   9067                 },
   9068             }
   9069 
   9070             node = node_datas[node].lhs;
   9071         } else {
   9072             // 2 args
   9073             if (info.param_count != 2) break;
   9074 
   9075             switch (info.tag) {
   9076                 else => break,
   9077                 .field_parent_ptr => {
   9078                     if (flags.ptr_cast) break;
   9079 
   9080                     const flags_int: FlagsInt = @bitCast(flags);
   9081                     const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node);
   9082                     const parent_ptr_type = try ri.rl.resultTypeForCast(gz, root_node, "@alignCast");
   9083                     const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, node_datas[node].lhs);
   9084                     const field_ptr = try expr(gz, scope, .{ .rl = .none }, node_datas[node].rhs);
   9085                     try emitDbgStmt(gz, cursor);
   9086                     const result = try gz.addExtendedPayloadSmall(.field_parent_ptr, flags_int, Zir.Inst.FieldParentPtr{
   9087                         .src_node = gz.nodeIndexToRelative(node),
   9088                         .parent_ptr_type = parent_ptr_type,
   9089                         .field_name = field_name,
   9090                         .field_ptr = field_ptr,
   9091                     });
   9092                     return rvalue(gz, ri, result, root_node);
   9093                 },
   9094             }
   9095         }
   9096     }
   9097 
   9098     const flags_int: FlagsInt = @bitCast(flags);
   9099     assert(flags_int != 0);
   9100 
   9101     const ptr_only: Zir.Inst.FullPtrCastFlags = .{ .ptr_cast = true };
   9102     if (flags_int == @as(FlagsInt, @bitCast(ptr_only))) {
   9103         // Special case: simpler representation
   9104         return typeCast(gz, scope, ri, root_node, node, .ptr_cast, "@ptrCast");
   9105     }
   9106 
   9107     const no_result_ty_flags: Zir.Inst.FullPtrCastFlags = .{
   9108         .const_cast = true,
   9109         .volatile_cast = true,
   9110     };
   9111     if ((flags_int & ~@as(FlagsInt, @bitCast(no_result_ty_flags))) == 0) {
   9112         // Result type not needed
   9113         const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node);
   9114         const operand = try expr(gz, scope, .{ .rl = .none }, node);
   9115         try emitDbgStmt(gz, cursor);
   9116         const result = try gz.addExtendedPayloadSmall(.ptr_cast_no_dest, flags_int, Zir.Inst.UnNode{
   9117             .node = gz.nodeIndexToRelative(root_node),
   9118             .operand = operand,
   9119         });
   9120         return rvalue(gz, ri, result, root_node);
   9121     }
   9122 
   9123     // Full cast including result type
   9124 
   9125     const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node);
   9126     const result_type = try ri.rl.resultTypeForCast(gz, root_node, flags.needResultTypeBuiltinName());
   9127     const operand = try expr(gz, scope, .{ .rl = .none }, node);
   9128     try emitDbgStmt(gz, cursor);
   9129     const result = try gz.addExtendedPayloadSmall(.ptr_cast_full, flags_int, Zir.Inst.BinNode{
   9130         .node = gz.nodeIndexToRelative(root_node),
   9131         .lhs = result_type,
   9132         .rhs = operand,
   9133     });
   9134     return rvalue(gz, ri, result, root_node);
   9135 }
   9136 
   9137 fn typeOf(
   9138     gz: *GenZir,
   9139     scope: *Scope,
   9140     ri: ResultInfo,
   9141     node: Ast.Node.Index,
   9142     args: []const Ast.Node.Index,
   9143 ) InnerError!Zir.Inst.Ref {
   9144     const astgen = gz.astgen;
   9145     if (args.len < 1) {
   9146         return astgen.failNode(node, "expected at least 1 argument, found 0", .{});
   9147     }
   9148     const gpa = astgen.gpa;
   9149     if (args.len == 1) {
   9150         const typeof_inst = try gz.makeBlockInst(.typeof_builtin, node);
   9151 
   9152         var typeof_scope = gz.makeSubBlock(scope);
   9153         typeof_scope.is_comptime = false;
   9154         typeof_scope.is_typeof = true;
   9155         typeof_scope.c_import = false;
   9156         defer typeof_scope.unstack();
   9157 
   9158         const ty_expr = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, args[0], node);
   9159         if (!gz.refIsNoReturn(ty_expr)) {
   9160             _ = try typeof_scope.addBreak(.break_inline, typeof_inst, ty_expr);
   9161         }
   9162         try typeof_scope.setBlockBody(typeof_inst);
   9163 
   9164         // typeof_scope unstacked now, can add new instructions to gz
   9165         try gz.instructions.append(gpa, typeof_inst);
   9166         return rvalue(gz, ri, typeof_inst.toRef(), node);
   9167     }
   9168     const payload_size: u32 = std.meta.fields(Zir.Inst.TypeOfPeer).len;
   9169     const payload_index = try reserveExtra(astgen, payload_size + args.len);
   9170     const args_index = payload_index + payload_size;
   9171 
   9172     const typeof_inst = try gz.addExtendedMultiOpPayloadIndex(.typeof_peer, payload_index, args.len);
   9173 
   9174     var typeof_scope = gz.makeSubBlock(scope);
   9175     typeof_scope.is_comptime = false;
   9176 
   9177     for (args, 0..) |arg, i| {
   9178         const param_ref = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, arg, node);
   9179         astgen.extra.items[args_index + i] = @intFromEnum(param_ref);
   9180     }
   9181     _ = try typeof_scope.addBreak(.break_inline, typeof_inst.toIndex().?, .void_value);
   9182 
   9183     const body = typeof_scope.instructionsSlice();
   9184     const body_len = astgen.countBodyLenAfterFixups(body);
   9185     astgen.setExtra(payload_index, Zir.Inst.TypeOfPeer{
   9186         .body_len = @intCast(body_len),
   9187         .body_index = @intCast(astgen.extra.items.len),
   9188         .src_node = gz.nodeIndexToRelative(node),
   9189     });
   9190     try astgen.extra.ensureUnusedCapacity(gpa, body_len);
   9191     astgen.appendBodyWithFixups(body);
   9192     typeof_scope.unstack();
   9193 
   9194     return rvalue(gz, ri, typeof_inst, node);
   9195 }
   9196 
   9197 fn minMax(
   9198     gz: *GenZir,
   9199     scope: *Scope,
   9200     ri: ResultInfo,
   9201     node: Ast.Node.Index,
   9202     args: []const Ast.Node.Index,
   9203     comptime op: enum { min, max },
   9204 ) InnerError!Zir.Inst.Ref {
   9205     const astgen = gz.astgen;
   9206     if (args.len < 2) {
   9207         return astgen.failNode(node, "expected at least 2 arguments, found {}", .{args.len});
   9208     }
   9209     if (args.len == 2) {
   9210         const tag: Zir.Inst.Tag = switch (op) {
   9211             .min => .min,
   9212             .max => .max,
   9213         };
   9214         const a = try expr(gz, scope, .{ .rl = .none }, args[0]);
   9215         const b = try expr(gz, scope, .{ .rl = .none }, args[1]);
   9216         const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   9217             .lhs = a,
   9218             .rhs = b,
   9219         });
   9220         return rvalue(gz, ri, result, node);
   9221     }
   9222     const payload_index = try addExtra(astgen, Zir.Inst.NodeMultiOp{
   9223         .src_node = gz.nodeIndexToRelative(node),
   9224     });
   9225     var extra_index = try reserveExtra(gz.astgen, args.len);
   9226     for (args) |arg| {
   9227         const arg_ref = try expr(gz, scope, .{ .rl = .none }, arg);
   9228         astgen.extra.items[extra_index] = @intFromEnum(arg_ref);
   9229         extra_index += 1;
   9230     }
   9231     const tag: Zir.Inst.Extended = switch (op) {
   9232         .min => .min_multi,
   9233         .max => .max_multi,
   9234     };
   9235     const result = try gz.addExtendedMultiOpPayloadIndex(tag, payload_index, args.len);
   9236     return rvalue(gz, ri, result, node);
   9237 }
   9238 
   9239 fn builtinCall(
   9240     gz: *GenZir,
   9241     scope: *Scope,
   9242     ri: ResultInfo,
   9243     node: Ast.Node.Index,
   9244     params: []const Ast.Node.Index,
   9245     allow_branch_hint: bool,
   9246 ) InnerError!Zir.Inst.Ref {
   9247     const astgen = gz.astgen;
   9248     const tree = astgen.tree;
   9249     const main_tokens = tree.nodes.items(.main_token);
   9250 
   9251     const builtin_token = main_tokens[node];
   9252     const builtin_name = tree.tokenSlice(builtin_token);
   9253 
   9254     // We handle the different builtins manually because they have different semantics depending
   9255     // on the function. For example, `@as` and others participate in result location semantics,
   9256     // and `@cImport` creates a special scope that collects a .c source code text buffer.
   9257     // Also, some builtins have a variable number of parameters.
   9258 
   9259     const info = BuiltinFn.list.get(builtin_name) orelse {
   9260         return astgen.failNode(node, "invalid builtin function: '{s}'", .{
   9261             builtin_name,
   9262         });
   9263     };
   9264     if (info.param_count) |expected| {
   9265         if (expected != params.len) {
   9266             const s = if (expected == 1) "" else "s";
   9267             return astgen.failNode(node, "expected {d} argument{s}, found {d}", .{
   9268                 expected, s, params.len,
   9269             });
   9270         }
   9271     }
   9272 
   9273     // Check function scope-only builtins
   9274 
   9275     if (astgen.fn_block == null and info.illegal_outside_function)
   9276         return astgen.failNode(node, "'{s}' outside function scope", .{builtin_name});
   9277 
   9278     switch (info.tag) {
   9279         .branch_hint => {
   9280             if (!allow_branch_hint) {
   9281                 return astgen.failNode(node, "'@branchHint' must appear as the first statement in a function or conditional branch", .{});
   9282             }
   9283             const hint_ty = try gz.addBuiltinValue(node, .branch_hint);
   9284             const hint_val = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = hint_ty } }, params[0]);
   9285             _ = try gz.addExtendedPayload(.branch_hint, Zir.Inst.UnNode{
   9286                 .node = gz.nodeIndexToRelative(node),
   9287                 .operand = hint_val,
   9288             });
   9289             return rvalue(gz, ri, .void_value, node);
   9290         },
   9291         .import => {
   9292             const node_tags = tree.nodes.items(.tag);
   9293             const operand_node = params[0];
   9294 
   9295             if (node_tags[operand_node] != .string_literal) {
   9296                 // Spec reference: https://github.com/ziglang/zig/issues/2206
   9297                 return astgen.failNode(operand_node, "@import operand must be a string literal", .{});
   9298             }
   9299             const str_lit_token = main_tokens[operand_node];
   9300             const str = try astgen.strLitAsString(str_lit_token);
   9301             const str_slice = astgen.string_bytes.items[@intFromEnum(str.index)..][0..str.len];
   9302             if (mem.indexOfScalar(u8, str_slice, 0) != null) {
   9303                 return astgen.failTok(str_lit_token, "import path cannot contain null bytes", .{});
   9304             } else if (str.len == 0) {
   9305                 return astgen.failTok(str_lit_token, "import path cannot be empty", .{});
   9306             }
   9307             const result = try gz.addStrTok(.import, str.index, str_lit_token);
   9308             const gop = try astgen.imports.getOrPut(astgen.gpa, str.index);
   9309             if (!gop.found_existing) {
   9310                 gop.value_ptr.* = str_lit_token;
   9311             }
   9312             return rvalue(gz, ri, result, node);
   9313         },
   9314         .compile_log => {
   9315             const payload_index = try addExtra(gz.astgen, Zir.Inst.NodeMultiOp{
   9316                 .src_node = gz.nodeIndexToRelative(node),
   9317             });
   9318             var extra_index = try reserveExtra(gz.astgen, params.len);
   9319             for (params) |param| {
   9320                 const param_ref = try expr(gz, scope, .{ .rl = .none }, param);
   9321                 astgen.extra.items[extra_index] = @intFromEnum(param_ref);
   9322                 extra_index += 1;
   9323             }
   9324             const result = try gz.addExtendedMultiOpPayloadIndex(.compile_log, payload_index, params.len);
   9325             return rvalue(gz, ri, result, node);
   9326         },
   9327         .field => {
   9328             if (ri.rl == .ref or ri.rl == .ref_coerced_ty) {
   9329                 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{
   9330                     .lhs = try expr(gz, scope, .{ .rl = .ref }, params[0]),
   9331                     .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]),
   9332                 });
   9333             }
   9334             const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{
   9335                 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
   9336                 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]),
   9337             });
   9338             return rvalue(gz, ri, result, node);
   9339         },
   9340         .FieldType => {
   9341             const ty_inst = try typeExpr(gz, scope, params[0]);
   9342             const name_inst = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]);
   9343             const result = try gz.addPlNode(.field_type_ref, node, Zir.Inst.FieldTypeRef{
   9344                 .container_type = ty_inst,
   9345                 .field_name = name_inst,
   9346             });
   9347             return rvalue(gz, ri, result, node);
   9348         },
   9349 
   9350         // zig fmt: off
   9351         .as         => return as(       gz, scope, ri, node, params[0], params[1]),
   9352         .bit_cast   => return bitCast(  gz, scope, ri, node, params[0]),
   9353         .TypeOf     => return typeOf(   gz, scope, ri, node, params),
   9354         .union_init => return unionInit(gz, scope, ri, node, params),
   9355         .c_import   => return cImport(  gz, scope,     node, params[0]),
   9356         .min        => return minMax(   gz, scope, ri, node, params, .min),
   9357         .max        => return minMax(   gz, scope, ri, node, params, .max),
   9358         // zig fmt: on
   9359 
   9360         .@"export" => {
   9361             const exported = try expr(gz, scope, .{ .rl = .none }, params[0]);
   9362             const export_options_ty = try gz.addBuiltinValue(node, .export_options);
   9363             const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = export_options_ty } }, params[1]);
   9364             _ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{
   9365                 .exported = exported,
   9366                 .options = options,
   9367             });
   9368             return rvalue(gz, ri, .void_value, node);
   9369         },
   9370         .@"extern" => {
   9371             const type_inst = try typeExpr(gz, scope, params[0]);
   9372             const extern_options_ty = try gz.addBuiltinValue(node, .extern_options);
   9373             const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = extern_options_ty } }, params[1]);
   9374             const result = try gz.addExtendedPayload(.builtin_extern, Zir.Inst.BinNode{
   9375                 .node = gz.nodeIndexToRelative(node),
   9376                 .lhs = type_inst,
   9377                 .rhs = options,
   9378             });
   9379             return rvalue(gz, ri, result, node);
   9380         },
   9381         .set_float_mode => {
   9382             const float_mode_ty = try gz.addBuiltinValue(node, .float_mode);
   9383             const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_mode_ty } }, params[0]);
   9384             _ = try gz.addExtendedPayload(.set_float_mode, Zir.Inst.UnNode{
   9385                 .node = gz.nodeIndexToRelative(node),
   9386                 .operand = order,
   9387             });
   9388             return rvalue(gz, ri, .void_value, node);
   9389         },
   9390 
   9391         .src => {
   9392             // Incorporate the source location into the source hash, so that
   9393             // changes in the source location of `@src()` result in re-analysis.
   9394             astgen.src_hasher.update(
   9395                 std.mem.asBytes(&astgen.source_line) ++
   9396                     std.mem.asBytes(&astgen.source_column),
   9397             );
   9398 
   9399             const token_starts = tree.tokens.items(.start);
   9400             const node_start = token_starts[tree.firstToken(node)];
   9401             astgen.advanceSourceCursor(node_start);
   9402             const result = try gz.addExtendedPayload(.builtin_src, Zir.Inst.Src{
   9403                 .node = gz.nodeIndexToRelative(node),
   9404                 .line = astgen.source_line,
   9405                 .column = astgen.source_column,
   9406             });
   9407             return rvalue(gz, ri, result, node);
   9408         },
   9409 
   9410         // zig fmt: off
   9411         .This                    => return rvalue(gz, ri, try gz.addNodeExtended(.this,                    node), node),
   9412         .return_address          => return rvalue(gz, ri, try gz.addNodeExtended(.ret_addr,                node), node),
   9413         .error_return_trace      => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace,      node), node),
   9414         .frame                   => return rvalue(gz, ri, try gz.addNodeExtended(.frame,                   node), node),
   9415         .frame_address           => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address,           node), node),
   9416         .breakpoint              => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint,              node), node),
   9417         .disable_instrumentation => return rvalue(gz, ri, try gz.addNodeExtended(.disable_instrumentation, node), node),
   9418 
   9419         .type_info   => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info),
   9420         .size_of     => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of),
   9421         .bit_size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .bit_size_of),
   9422         .align_of    => return simpleUnOpType(gz, scope, ri, node, params[0], .align_of),
   9423 
   9424         .int_from_ptr          => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .int_from_ptr),
   9425         .compile_error         => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } },   params[0], .compile_error),
   9426         .set_eval_branch_quota => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .u32_type } },              params[0], .set_eval_branch_quota),
   9427         .int_from_enum         => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .int_from_enum),
   9428         .int_from_bool         => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .int_from_bool),
   9429         .embed_file            => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } },   params[0], .embed_file),
   9430         .error_name            => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .anyerror_type } },         params[0], .error_name),
   9431         .set_runtime_safety    => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri,                                      params[0], .set_runtime_safety),
   9432         .sqrt                  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .sqrt),
   9433         .sin                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .sin),
   9434         .cos                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .cos),
   9435         .tan                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .tan),
   9436         .exp                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .exp),
   9437         .exp2                  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .exp2),
   9438         .log                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .log),
   9439         .log2                  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .log2),
   9440         .log10                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .log10),
   9441         .abs                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .abs),
   9442         .floor                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .floor),
   9443         .ceil                  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .ceil),
   9444         .trunc                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .trunc),
   9445         .round                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .round),
   9446         .tag_name              => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .tag_name),
   9447         .type_name             => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .type_name),
   9448         .Frame                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .frame_type),
   9449         .frame_size            => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .frame_size),
   9450 
   9451         .int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name),
   9452         .float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name),
   9453         .ptr_from_int   => return typeCast(gz, scope, ri, node, params[0], .ptr_from_int, builtin_name),
   9454         .enum_from_int  => return typeCast(gz, scope, ri, node, params[0], .enum_from_int, builtin_name),
   9455         .float_cast     => return typeCast(gz, scope, ri, node, params[0], .float_cast, builtin_name),
   9456         .int_cast       => return typeCast(gz, scope, ri, node, params[0], .int_cast, builtin_name),
   9457         .truncate       => return typeCast(gz, scope, ri, node, params[0], .truncate, builtin_name),
   9458         // zig fmt: on
   9459 
   9460         .in_comptime => if (gz.is_comptime) {
   9461             return astgen.failNode(node, "redundant '@inComptime' in comptime scope", .{});
   9462         } else {
   9463             return rvalue(gz, ri, try gz.addNodeExtended(.in_comptime, node), node);
   9464         },
   9465 
   9466         .Type => {
   9467             const type_info_ty = try gz.addBuiltinValue(node, .type_info);
   9468             const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = type_info_ty } }, params[0]);
   9469 
   9470             const gpa = gz.astgen.gpa;
   9471 
   9472             try gz.instructions.ensureUnusedCapacity(gpa, 1);
   9473             try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
   9474 
   9475             const payload_index = try gz.astgen.addExtra(Zir.Inst.Reify{
   9476                 .node = node, // Absolute node index -- see the definition of `Reify`.
   9477                 .operand = operand,
   9478                 .src_line = astgen.source_line,
   9479             });
   9480             const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
   9481             gz.astgen.instructions.appendAssumeCapacity(.{
   9482                 .tag = .extended,
   9483                 .data = .{ .extended = .{
   9484                     .opcode = .reify,
   9485                     .small = @intFromEnum(gz.anon_name_strategy),
   9486                     .operand = payload_index,
   9487                 } },
   9488             });
   9489             gz.instructions.appendAssumeCapacity(new_index);
   9490             const result = new_index.toRef();
   9491             return rvalue(gz, ri, result, node);
   9492         },
   9493         .panic => {
   9494             try emitDbgNode(gz, node);
   9495             return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .panic);
   9496         },
   9497         .trap => {
   9498             try emitDbgNode(gz, node);
   9499             _ = try gz.addNode(.trap, node);
   9500             return rvalue(gz, ri, .unreachable_value, node);
   9501         },
   9502         .int_from_error => {
   9503             const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
   9504             const result = try gz.addExtendedPayload(.int_from_error, Zir.Inst.UnNode{
   9505                 .node = gz.nodeIndexToRelative(node),
   9506                 .operand = operand,
   9507             });
   9508             return rvalue(gz, ri, result, node);
   9509         },
   9510         .error_from_int => {
   9511             const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
   9512             const result = try gz.addExtendedPayload(.error_from_int, Zir.Inst.UnNode{
   9513                 .node = gz.nodeIndexToRelative(node),
   9514                 .operand = operand,
   9515             });
   9516             return rvalue(gz, ri, result, node);
   9517         },
   9518         .error_cast => {
   9519             try emitDbgNode(gz, node);
   9520 
   9521             const result = try gz.addExtendedPayload(.error_cast, Zir.Inst.BinNode{
   9522                 .lhs = try ri.rl.resultTypeForCast(gz, node, builtin_name),
   9523                 .rhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
   9524                 .node = gz.nodeIndexToRelative(node),
   9525             });
   9526             return rvalue(gz, ri, result, node);
   9527         },
   9528         .ptr_cast,
   9529         .align_cast,
   9530         .addrspace_cast,
   9531         .const_cast,
   9532         .volatile_cast,
   9533         => return ptrCast(gz, scope, ri, node),
   9534 
   9535         // zig fmt: off
   9536         .has_decl  => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl),
   9537         .has_field => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_field),
   9538 
   9539         .clz         => return bitBuiltin(gz, scope, ri, node, params[0], .clz),
   9540         .ctz         => return bitBuiltin(gz, scope, ri, node, params[0], .ctz),
   9541         .pop_count   => return bitBuiltin(gz, scope, ri, node, params[0], .pop_count),
   9542         .byte_swap   => return bitBuiltin(gz, scope, ri, node, params[0], .byte_swap),
   9543         .bit_reverse => return bitBuiltin(gz, scope, ri, node, params[0], .bit_reverse),
   9544 
   9545         .div_exact => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_exact),
   9546         .div_floor => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_floor),
   9547         .div_trunc => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_trunc),
   9548         .mod       => return divBuiltin(gz, scope, ri, node, params[0], params[1], .mod),
   9549         .rem       => return divBuiltin(gz, scope, ri, node, params[0], params[1], .rem),
   9550 
   9551         .shl_exact => return shiftOp(gz, scope, ri, node, params[0], params[1], .shl_exact),
   9552         .shr_exact => return shiftOp(gz, scope, ri, node, params[0], params[1], .shr_exact),
   9553 
   9554         .bit_offset_of => return offsetOf(gz, scope, ri, node, params[0], params[1], .bit_offset_of),
   9555         .offset_of     => return offsetOf(gz, scope, ri, node, params[0], params[1], .offset_of),
   9556 
   9557         .c_undef   => return simpleCBuiltin(gz, scope, ri, node, params[0], .c_undef),
   9558         .c_include => return simpleCBuiltin(gz, scope, ri, node, params[0], .c_include),
   9559 
   9560         .cmpxchg_strong => return cmpxchg(gz, scope, ri, node, params, 1),
   9561         .cmpxchg_weak   => return cmpxchg(gz, scope, ri, node, params, 0),
   9562         // zig fmt: on
   9563 
   9564         .wasm_memory_size => {
   9565             const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
   9566             const result = try gz.addExtendedPayload(.wasm_memory_size, Zir.Inst.UnNode{
   9567                 .node = gz.nodeIndexToRelative(node),
   9568                 .operand = operand,
   9569             });
   9570             return rvalue(gz, ri, result, node);
   9571         },
   9572         .wasm_memory_grow => {
   9573             const index_arg = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
   9574             const delta_arg = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[1]);
   9575             const result = try gz.addExtendedPayload(.wasm_memory_grow, Zir.Inst.BinNode{
   9576                 .node = gz.nodeIndexToRelative(node),
   9577                 .lhs = index_arg,
   9578                 .rhs = delta_arg,
   9579             });
   9580             return rvalue(gz, ri, result, node);
   9581         },
   9582         .c_define => {
   9583             if (!gz.c_import) return gz.astgen.failNode(node, "C define valid only inside C import block", .{});
   9584             const name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0]);
   9585             const value = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]);
   9586             const result = try gz.addExtendedPayload(.c_define, Zir.Inst.BinNode{
   9587                 .node = gz.nodeIndexToRelative(node),
   9588                 .lhs = name,
   9589                 .rhs = value,
   9590             });
   9591             return rvalue(gz, ri, result, node);
   9592         },
   9593 
   9594         .splat => {
   9595             const result_type = try ri.rl.resultTypeForCast(gz, node, builtin_name);
   9596             const elem_type = try gz.addUnNode(.vec_arr_elem_type, result_type, node);
   9597             const scalar = try expr(gz, scope, .{ .rl = .{ .ty = elem_type } }, params[0]);
   9598             const result = try gz.addPlNode(.splat, node, Zir.Inst.Bin{
   9599                 .lhs = result_type,
   9600                 .rhs = scalar,
   9601             });
   9602             return rvalue(gz, ri, result, node);
   9603         },
   9604         .reduce => {
   9605             const reduce_op_ty = try gz.addBuiltinValue(node, .reduce_op);
   9606             const op = try expr(gz, scope, .{ .rl = .{ .coerced_ty = reduce_op_ty } }, params[0]);
   9607             const scalar = try expr(gz, scope, .{ .rl = .none }, params[1]);
   9608             const result = try gz.addPlNode(.reduce, node, Zir.Inst.Bin{
   9609                 .lhs = op,
   9610                 .rhs = scalar,
   9611             });
   9612             return rvalue(gz, ri, result, node);
   9613         },
   9614 
   9615         .add_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .add_with_overflow),
   9616         .sub_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .sub_with_overflow),
   9617         .mul_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .mul_with_overflow),
   9618         .shl_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .shl_with_overflow),
   9619 
   9620         .atomic_load => {
   9621             const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order);
   9622             const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.AtomicLoad{
   9623                 // zig fmt: off
   9624                 .elem_type = try typeExpr(gz, scope,                                                  params[0]),
   9625                 .ptr       = try expr    (gz, scope, .{ .rl = .none },                                params[1]),
   9626                 .ordering  = try expr    (gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[2]),
   9627                 // zig fmt: on
   9628             });
   9629             return rvalue(gz, ri, result, node);
   9630         },
   9631         .atomic_rmw => {
   9632             const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order);
   9633             const atomic_rmw_op_type = try gz.addBuiltinValue(node, .atomic_rmw_op);
   9634             const int_type = try typeExpr(gz, scope, params[0]);
   9635             const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{
   9636                 // zig fmt: off
   9637                 .ptr       = try expr(gz, scope, .{ .rl = .none },                                 params[1]),
   9638                 .operation = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_rmw_op_type } }, params[2]),
   9639                 .operand   = try expr(gz, scope, .{ .rl = .{ .ty = int_type } },                   params[3]),
   9640                 .ordering  = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } },  params[4]),
   9641                 // zig fmt: on
   9642             });
   9643             return rvalue(gz, ri, result, node);
   9644         },
   9645         .atomic_store => {
   9646             const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order);
   9647             const int_type = try typeExpr(gz, scope, params[0]);
   9648             _ = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{
   9649                 // zig fmt: off
   9650                 .ptr      = try expr(gz, scope, .{ .rl = .none },                                params[1]),
   9651                 .operand  = try expr(gz, scope, .{ .rl = .{ .ty = int_type } },                  params[2]),
   9652                 .ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[3]),
   9653                 // zig fmt: on
   9654             });
   9655             return rvalue(gz, ri, .void_value, node);
   9656         },
   9657         .mul_add => {
   9658             const float_type = try typeExpr(gz, scope, params[0]);
   9659             const mulend1 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_type } }, params[1]);
   9660             const mulend2 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_type } }, params[2]);
   9661             const addend = try expr(gz, scope, .{ .rl = .{ .ty = float_type } }, params[3]);
   9662             const result = try gz.addPlNode(.mul_add, node, Zir.Inst.MulAdd{
   9663                 .mulend1 = mulend1,
   9664                 .mulend2 = mulend2,
   9665                 .addend = addend,
   9666             });
   9667             return rvalue(gz, ri, result, node);
   9668         },
   9669         .call => {
   9670             const call_modifier_ty = try gz.addBuiltinValue(node, .call_modifier);
   9671             const modifier = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = call_modifier_ty } }, params[0]);
   9672             const callee = try expr(gz, scope, .{ .rl = .none }, params[1]);
   9673             const args = try expr(gz, scope, .{ .rl = .none }, params[2]);
   9674             const result = try gz.addPlNode(.builtin_call, node, Zir.Inst.BuiltinCall{
   9675                 .modifier = modifier,
   9676                 .callee = callee,
   9677                 .args = args,
   9678                 .flags = .{
   9679                     .is_nosuspend = gz.nosuspend_node != 0,
   9680                     .ensure_result_used = false,
   9681                 },
   9682             });
   9683             return rvalue(gz, ri, result, node);
   9684         },
   9685         .field_parent_ptr => {
   9686             const parent_ptr_type = try ri.rl.resultTypeForCast(gz, node, builtin_name);
   9687             const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0]);
   9688             const result = try gz.addExtendedPayloadSmall(.field_parent_ptr, 0, Zir.Inst.FieldParentPtr{
   9689                 .src_node = gz.nodeIndexToRelative(node),
   9690                 .parent_ptr_type = parent_ptr_type,
   9691                 .field_name = field_name,
   9692                 .field_ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),
   9693             });
   9694             return rvalue(gz, ri, result, node);
   9695         },
   9696         .memcpy => {
   9697             _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Bin{
   9698                 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
   9699                 .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]),
   9700             });
   9701             return rvalue(gz, ri, .void_value, node);
   9702         },
   9703         .memset => {
   9704             const lhs = try expr(gz, scope, .{ .rl = .none }, params[0]);
   9705             const lhs_ty = try gz.addUnNode(.typeof, lhs, params[0]);
   9706             const elem_ty = try gz.addUnNode(.indexable_ptr_elem_type, lhs_ty, params[0]);
   9707             _ = try gz.addPlNode(.memset, node, Zir.Inst.Bin{
   9708                 .lhs = lhs,
   9709                 .rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = elem_ty } }, params[1]),
   9710             });
   9711             return rvalue(gz, ri, .void_value, node);
   9712         },
   9713         .shuffle => {
   9714             const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{
   9715                 .elem_type = try typeExpr(gz, scope, params[0]),
   9716                 .a = try expr(gz, scope, .{ .rl = .none }, params[1]),
   9717                 .b = try expr(gz, scope, .{ .rl = .none }, params[2]),
   9718                 .mask = try comptimeExpr(gz, scope, .{ .rl = .none }, params[3]),
   9719             });
   9720             return rvalue(gz, ri, result, node);
   9721         },
   9722         .select => {
   9723             const result = try gz.addExtendedPayload(.select, Zir.Inst.Select{
   9724                 .node = gz.nodeIndexToRelative(node),
   9725                 .elem_type = try typeExpr(gz, scope, params[0]),
   9726                 .pred = try expr(gz, scope, .{ .rl = .none }, params[1]),
   9727                 .a = try expr(gz, scope, .{ .rl = .none }, params[2]),
   9728                 .b = try expr(gz, scope, .{ .rl = .none }, params[3]),
   9729             });
   9730             return rvalue(gz, ri, result, node);
   9731         },
   9732         .async_call => {
   9733             const result = try gz.addExtendedPayload(.builtin_async_call, Zir.Inst.AsyncCall{
   9734                 .node = gz.nodeIndexToRelative(node),
   9735                 .frame_buffer = try expr(gz, scope, .{ .rl = .none }, params[0]),
   9736                 .result_ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),
   9737                 .fn_ptr = try expr(gz, scope, .{ .rl = .none }, params[2]),
   9738                 .args = try expr(gz, scope, .{ .rl = .none }, params[3]),
   9739             });
   9740             return rvalue(gz, ri, result, node);
   9741         },
   9742         .Vector => {
   9743             const result = try gz.addPlNode(.vector_type, node, Zir.Inst.Bin{
   9744                 .lhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]),
   9745                 .rhs = try typeExpr(gz, scope, params[1]),
   9746             });
   9747             return rvalue(gz, ri, result, node);
   9748         },
   9749         .prefetch => {
   9750             const prefetch_options_ty = try gz.addBuiltinValue(node, .prefetch_options);
   9751             const ptr = try expr(gz, scope, .{ .rl = .none }, params[0]);
   9752             const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = prefetch_options_ty } }, params[1]);
   9753             _ = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{
   9754                 .node = gz.nodeIndexToRelative(node),
   9755                 .lhs = ptr,
   9756                 .rhs = options,
   9757             });
   9758             return rvalue(gz, ri, .void_value, node);
   9759         },
   9760         .c_va_arg => {
   9761             const result = try gz.addExtendedPayload(.c_va_arg, Zir.Inst.BinNode{
   9762                 .node = gz.nodeIndexToRelative(node),
   9763                 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
   9764                 .rhs = try typeExpr(gz, scope, params[1]),
   9765             });
   9766             return rvalue(gz, ri, result, node);
   9767         },
   9768         .c_va_copy => {
   9769             const result = try gz.addExtendedPayload(.c_va_copy, Zir.Inst.UnNode{
   9770                 .node = gz.nodeIndexToRelative(node),
   9771                 .operand = try expr(gz, scope, .{ .rl = .none }, params[0]),
   9772             });
   9773             return rvalue(gz, ri, result, node);
   9774         },
   9775         .c_va_end => {
   9776             const result = try gz.addExtendedPayload(.c_va_end, Zir.Inst.UnNode{
   9777                 .node = gz.nodeIndexToRelative(node),
   9778                 .operand = try expr(gz, scope, .{ .rl = .none }, params[0]),
   9779             });
   9780             return rvalue(gz, ri, result, node);
   9781         },
   9782         .c_va_start => {
   9783             if (!astgen.fn_var_args) {
   9784                 return astgen.failNode(node, "'@cVaStart' in a non-variadic function", .{});
   9785             }
   9786             return rvalue(gz, ri, try gz.addNodeExtended(.c_va_start, node), node);
   9787         },
   9788 
   9789         .work_item_id => {
   9790             const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
   9791             const result = try gz.addExtendedPayload(.work_item_id, Zir.Inst.UnNode{
   9792                 .node = gz.nodeIndexToRelative(node),
   9793                 .operand = operand,
   9794             });
   9795             return rvalue(gz, ri, result, node);
   9796         },
   9797         .work_group_size => {
   9798             const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
   9799             const result = try gz.addExtendedPayload(.work_group_size, Zir.Inst.UnNode{
   9800                 .node = gz.nodeIndexToRelative(node),
   9801                 .operand = operand,
   9802             });
   9803             return rvalue(gz, ri, result, node);
   9804         },
   9805         .work_group_id => {
   9806             const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
   9807             const result = try gz.addExtendedPayload(.work_group_id, Zir.Inst.UnNode{
   9808                 .node = gz.nodeIndexToRelative(node),
   9809                 .operand = operand,
   9810             });
   9811             return rvalue(gz, ri, result, node);
   9812         },
   9813     }
   9814 }
   9815 
   9816 fn hasDeclOrField(
   9817     gz: *GenZir,
   9818     scope: *Scope,
   9819     ri: ResultInfo,
   9820     node: Ast.Node.Index,
   9821     lhs_node: Ast.Node.Index,
   9822     rhs_node: Ast.Node.Index,
   9823     tag: Zir.Inst.Tag,
   9824 ) InnerError!Zir.Inst.Ref {
   9825     const container_type = try typeExpr(gz, scope, lhs_node);
   9826     const name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, rhs_node);
   9827     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   9828         .lhs = container_type,
   9829         .rhs = name,
   9830     });
   9831     return rvalue(gz, ri, result, node);
   9832 }
   9833 
   9834 fn typeCast(
   9835     gz: *GenZir,
   9836     scope: *Scope,
   9837     ri: ResultInfo,
   9838     node: Ast.Node.Index,
   9839     operand_node: Ast.Node.Index,
   9840     tag: Zir.Inst.Tag,
   9841     builtin_name: []const u8,
   9842 ) InnerError!Zir.Inst.Ref {
   9843     const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   9844     const result_type = try ri.rl.resultTypeForCast(gz, node, builtin_name);
   9845     const operand = try expr(gz, scope, .{ .rl = .none }, operand_node);
   9846 
   9847     try emitDbgStmt(gz, cursor);
   9848     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   9849         .lhs = result_type,
   9850         .rhs = operand,
   9851     });
   9852     return rvalue(gz, ri, result, node);
   9853 }
   9854 
   9855 fn simpleUnOpType(
   9856     gz: *GenZir,
   9857     scope: *Scope,
   9858     ri: ResultInfo,
   9859     node: Ast.Node.Index,
   9860     operand_node: Ast.Node.Index,
   9861     tag: Zir.Inst.Tag,
   9862 ) InnerError!Zir.Inst.Ref {
   9863     const operand = try typeExpr(gz, scope, operand_node);
   9864     const result = try gz.addUnNode(tag, operand, node);
   9865     return rvalue(gz, ri, result, node);
   9866 }
   9867 
   9868 fn simpleUnOp(
   9869     gz: *GenZir,
   9870     scope: *Scope,
   9871     ri: ResultInfo,
   9872     node: Ast.Node.Index,
   9873     operand_ri: ResultInfo,
   9874     operand_node: Ast.Node.Index,
   9875     tag: Zir.Inst.Tag,
   9876 ) InnerError!Zir.Inst.Ref {
   9877     const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   9878     const operand = if (tag == .compile_error)
   9879         try comptimeExpr(gz, scope, operand_ri, operand_node)
   9880     else
   9881         try expr(gz, scope, operand_ri, operand_node);
   9882     switch (tag) {
   9883         .tag_name, .error_name, .int_from_ptr => try emitDbgStmt(gz, cursor),
   9884         else => {},
   9885     }
   9886     const result = try gz.addUnNode(tag, operand, node);
   9887     return rvalue(gz, ri, result, node);
   9888 }
   9889 
   9890 fn negation(
   9891     gz: *GenZir,
   9892     scope: *Scope,
   9893     ri: ResultInfo,
   9894     node: Ast.Node.Index,
   9895 ) InnerError!Zir.Inst.Ref {
   9896     const astgen = gz.astgen;
   9897     const tree = astgen.tree;
   9898     const node_tags = tree.nodes.items(.tag);
   9899     const node_datas = tree.nodes.items(.data);
   9900 
   9901     // Check for float literal as the sub-expression because we want to preserve
   9902     // its negativity rather than having it go through comptime subtraction.
   9903     const operand_node = node_datas[node].lhs;
   9904     if (node_tags[operand_node] == .number_literal) {
   9905         return numberLiteral(gz, ri, operand_node, node, .negative);
   9906     }
   9907 
   9908     const operand = try expr(gz, scope, .{ .rl = .none }, operand_node);
   9909     const result = try gz.addUnNode(.negate, operand, node);
   9910     return rvalue(gz, ri, result, node);
   9911 }
   9912 
   9913 fn cmpxchg(
   9914     gz: *GenZir,
   9915     scope: *Scope,
   9916     ri: ResultInfo,
   9917     node: Ast.Node.Index,
   9918     params: []const Ast.Node.Index,
   9919     small: u16,
   9920 ) InnerError!Zir.Inst.Ref {
   9921     const int_type = try typeExpr(gz, scope, params[0]);
   9922     const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order);
   9923     const result = try gz.addExtendedPayloadSmall(.cmpxchg, small, Zir.Inst.Cmpxchg{
   9924         // zig fmt: off
   9925         .node           = gz.nodeIndexToRelative(node),
   9926         .ptr            = try expr(gz, scope, .{ .rl = .none },                                params[1]),
   9927         .expected_value = try expr(gz, scope, .{ .rl = .{ .ty = int_type } },                  params[2]),
   9928         .new_value      = try expr(gz, scope, .{ .rl = .{ .coerced_ty = int_type } },          params[3]),
   9929         .success_order  = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[4]),
   9930         .failure_order  = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[5]),
   9931         // zig fmt: on
   9932     });
   9933     return rvalue(gz, ri, result, node);
   9934 }
   9935 
   9936 fn bitBuiltin(
   9937     gz: *GenZir,
   9938     scope: *Scope,
   9939     ri: ResultInfo,
   9940     node: Ast.Node.Index,
   9941     operand_node: Ast.Node.Index,
   9942     tag: Zir.Inst.Tag,
   9943 ) InnerError!Zir.Inst.Ref {
   9944     const operand = try expr(gz, scope, .{ .rl = .none }, operand_node);
   9945     const result = try gz.addUnNode(tag, operand, node);
   9946     return rvalue(gz, ri, result, node);
   9947 }
   9948 
   9949 fn divBuiltin(
   9950     gz: *GenZir,
   9951     scope: *Scope,
   9952     ri: ResultInfo,
   9953     node: Ast.Node.Index,
   9954     lhs_node: Ast.Node.Index,
   9955     rhs_node: Ast.Node.Index,
   9956     tag: Zir.Inst.Tag,
   9957 ) InnerError!Zir.Inst.Ref {
   9958     const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   9959     const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node);
   9960     const rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node);
   9961 
   9962     try emitDbgStmt(gz, cursor);
   9963     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
   9964     return rvalue(gz, ri, result, node);
   9965 }
   9966 
   9967 fn simpleCBuiltin(
   9968     gz: *GenZir,
   9969     scope: *Scope,
   9970     ri: ResultInfo,
   9971     node: Ast.Node.Index,
   9972     operand_node: Ast.Node.Index,
   9973     tag: Zir.Inst.Extended,
   9974 ) InnerError!Zir.Inst.Ref {
   9975     const name: []const u8 = if (tag == .c_undef) "C undef" else "C include";
   9976     if (!gz.c_import) return gz.astgen.failNode(node, "{s} valid only inside C import block", .{name});
   9977     const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, operand_node);
   9978     _ = try gz.addExtendedPayload(tag, Zir.Inst.UnNode{
   9979         .node = gz.nodeIndexToRelative(node),
   9980         .operand = operand,
   9981     });
   9982     return rvalue(gz, ri, .void_value, node);
   9983 }
   9984 
   9985 fn offsetOf(
   9986     gz: *GenZir,
   9987     scope: *Scope,
   9988     ri: ResultInfo,
   9989     node: Ast.Node.Index,
   9990     lhs_node: Ast.Node.Index,
   9991     rhs_node: Ast.Node.Index,
   9992     tag: Zir.Inst.Tag,
   9993 ) InnerError!Zir.Inst.Ref {
   9994     const type_inst = try typeExpr(gz, scope, lhs_node);
   9995     const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, rhs_node);
   9996     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   9997         .lhs = type_inst,
   9998         .rhs = field_name,
   9999     });
  10000     return rvalue(gz, ri, result, node);
  10001 }
  10002 
  10003 fn shiftOp(
  10004     gz: *GenZir,
  10005     scope: *Scope,
  10006     ri: ResultInfo,
  10007     node: Ast.Node.Index,
  10008     lhs_node: Ast.Node.Index,
  10009     rhs_node: Ast.Node.Index,
  10010     tag: Zir.Inst.Tag,
  10011 ) InnerError!Zir.Inst.Ref {
  10012     const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node);
  10013 
  10014     const cursor = switch (gz.astgen.tree.nodes.items(.tag)[node]) {
  10015         .shl, .shr => maybeAdvanceSourceCursorToMainToken(gz, node),
  10016         else => undefined,
  10017     };
  10018 
  10019     const log2_int_type = try gz.addUnNode(.typeof_log2_int_type, lhs, lhs_node);
  10020     const rhs = try expr(gz, scope, .{ .rl = .{ .ty = log2_int_type }, .ctx = .shift_op }, rhs_node);
  10021 
  10022     switch (gz.astgen.tree.nodes.items(.tag)[node]) {
  10023         .shl, .shr => try emitDbgStmt(gz, cursor),
  10024         else => undefined,
  10025     }
  10026 
  10027     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
  10028         .lhs = lhs,
  10029         .rhs = rhs,
  10030     });
  10031     return rvalue(gz, ri, result, node);
  10032 }
  10033 
  10034 fn cImport(
  10035     gz: *GenZir,
  10036     scope: *Scope,
  10037     node: Ast.Node.Index,
  10038     body_node: Ast.Node.Index,
  10039 ) InnerError!Zir.Inst.Ref {
  10040     const astgen = gz.astgen;
  10041     const gpa = astgen.gpa;
  10042 
  10043     if (gz.c_import) return gz.astgen.failNode(node, "cannot nest @cImport", .{});
  10044 
  10045     var block_scope = gz.makeSubBlock(scope);
  10046     block_scope.is_comptime = true;
  10047     block_scope.c_import = true;
  10048     defer block_scope.unstack();
  10049 
  10050     const block_inst = try gz.makeBlockInst(.c_import, node);
  10051     const block_result = try fullBodyExpr(&block_scope, &block_scope.base, .{ .rl = .none }, body_node, .normal);
  10052     _ = try gz.addUnNode(.ensure_result_used, block_result, node);
  10053     if (!gz.refIsNoReturn(block_result)) {
  10054         _ = try block_scope.addBreak(.break_inline, block_inst, .void_value);
  10055     }
  10056     try block_scope.setBlockBody(block_inst);
  10057     // block_scope unstacked now, can add new instructions to gz
  10058     try gz.instructions.append(gpa, block_inst);
  10059 
  10060     return block_inst.toRef();
  10061 }
  10062 
  10063 fn overflowArithmetic(
  10064     gz: *GenZir,
  10065     scope: *Scope,
  10066     ri: ResultInfo,
  10067     node: Ast.Node.Index,
  10068     params: []const Ast.Node.Index,
  10069     tag: Zir.Inst.Extended,
  10070 ) InnerError!Zir.Inst.Ref {
  10071     const lhs = try expr(gz, scope, .{ .rl = .none }, params[0]);
  10072     const rhs = try expr(gz, scope, .{ .rl = .none }, params[1]);
  10073     const result = try gz.addExtendedPayload(tag, Zir.Inst.BinNode{
  10074         .node = gz.nodeIndexToRelative(node),
  10075         .lhs = lhs,
  10076         .rhs = rhs,
  10077     });
  10078     return rvalue(gz, ri, result, node);
  10079 }
  10080 
  10081 fn callExpr(
  10082     gz: *GenZir,
  10083     scope: *Scope,
  10084     ri: ResultInfo,
  10085     node: Ast.Node.Index,
  10086     call: Ast.full.Call,
  10087 ) InnerError!Zir.Inst.Ref {
  10088     const astgen = gz.astgen;
  10089 
  10090     const callee = try calleeExpr(gz, scope, ri.rl, call.ast.fn_expr);
  10091     const modifier: std.builtin.CallModifier = blk: {
  10092         if (gz.is_comptime) {
  10093             break :blk .compile_time;
  10094         }
  10095         if (call.async_token != null) {
  10096             break :blk .async_kw;
  10097         }
  10098         if (gz.nosuspend_node != 0) {
  10099             break :blk .no_async;
  10100         }
  10101         break :blk .auto;
  10102     };
  10103 
  10104     {
  10105         astgen.advanceSourceCursor(astgen.tree.tokens.items(.start)[call.ast.lparen]);
  10106         const line = astgen.source_line - gz.decl_line;
  10107         const column = astgen.source_column;
  10108         // Sema expects a dbg_stmt immediately before call,
  10109         try emitDbgStmtForceCurrentIndex(gz, .{ line, column });
  10110     }
  10111 
  10112     switch (callee) {
  10113         .direct => |obj| assert(obj != .none),
  10114         .field => |field| assert(field.obj_ptr != .none),
  10115     }
  10116     assert(node != 0);
  10117 
  10118     const call_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  10119     const call_inst = call_index.toRef();
  10120     try gz.astgen.instructions.append(astgen.gpa, undefined);
  10121     try gz.instructions.append(astgen.gpa, call_index);
  10122 
  10123     const scratch_top = astgen.scratch.items.len;
  10124     defer astgen.scratch.items.len = scratch_top;
  10125 
  10126     var scratch_index = scratch_top;
  10127     try astgen.scratch.resize(astgen.gpa, scratch_top + call.ast.params.len);
  10128 
  10129     for (call.ast.params) |param_node| {
  10130         var arg_block = gz.makeSubBlock(scope);
  10131         defer arg_block.unstack();
  10132 
  10133         // `call_inst` is reused to provide the param type.
  10134         const arg_ref = try fullBodyExpr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node, .normal);
  10135         _ = try arg_block.addBreakWithSrcNode(.break_inline, call_index, arg_ref, param_node);
  10136 
  10137         const body = arg_block.instructionsSlice();
  10138         try astgen.scratch.ensureUnusedCapacity(astgen.gpa, countBodyLenAfterFixups(astgen, body));
  10139         appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
  10140 
  10141         astgen.scratch.items[scratch_index] = @intCast(astgen.scratch.items.len - scratch_top);
  10142         scratch_index += 1;
  10143     }
  10144 
  10145     // If our result location is a try/catch/error-union-if/return, a function argument,
  10146     // or an initializer for a `const` variable, the error trace propagates.
  10147     // Otherwise, it should always be popped (handled in Sema).
  10148     const propagate_error_trace = switch (ri.ctx) {
  10149         .error_handling_expr, .@"return", .fn_arg, .const_init => true,
  10150         else => false,
  10151     };
  10152 
  10153     switch (callee) {
  10154         .direct => |callee_obj| {
  10155             const payload_index = try addExtra(astgen, Zir.Inst.Call{
  10156                 .callee = callee_obj,
  10157                 .flags = .{
  10158                     .pop_error_return_trace = !propagate_error_trace,
  10159                     .packed_modifier = @intCast(@intFromEnum(modifier)),
  10160                     .args_len = @intCast(call.ast.params.len),
  10161                 },
  10162             });
  10163             if (call.ast.params.len != 0) {
  10164                 try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]);
  10165             }
  10166             gz.astgen.instructions.set(@intFromEnum(call_index), .{
  10167                 .tag = .call,
  10168                 .data = .{ .pl_node = .{
  10169                     .src_node = gz.nodeIndexToRelative(node),
  10170                     .payload_index = payload_index,
  10171                 } },
  10172             });
  10173         },
  10174         .field => |callee_field| {
  10175             const payload_index = try addExtra(astgen, Zir.Inst.FieldCall{
  10176                 .obj_ptr = callee_field.obj_ptr,
  10177                 .field_name_start = callee_field.field_name_start,
  10178                 .flags = .{
  10179                     .pop_error_return_trace = !propagate_error_trace,
  10180                     .packed_modifier = @intCast(@intFromEnum(modifier)),
  10181                     .args_len = @intCast(call.ast.params.len),
  10182                 },
  10183             });
  10184             if (call.ast.params.len != 0) {
  10185                 try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]);
  10186             }
  10187             gz.astgen.instructions.set(@intFromEnum(call_index), .{
  10188                 .tag = .field_call,
  10189                 .data = .{ .pl_node = .{
  10190                     .src_node = gz.nodeIndexToRelative(node),
  10191                     .payload_index = payload_index,
  10192                 } },
  10193             });
  10194         },
  10195     }
  10196     return rvalue(gz, ri, call_inst, node); // TODO function call with result location
  10197 }
  10198 
  10199 const Callee = union(enum) {
  10200     field: struct {
  10201         /// A *pointer* to the object the field is fetched on, so that we can
  10202         /// promote the lvalue to an address if the first parameter requires it.
  10203         obj_ptr: Zir.Inst.Ref,
  10204         /// Offset into `string_bytes`.
  10205         field_name_start: Zir.NullTerminatedString,
  10206     },
  10207     direct: Zir.Inst.Ref,
  10208 };
  10209 
  10210 /// calleeExpr generates the function part of a call expression (f in f(x)), but
  10211 /// *not* the callee argument to the @call() builtin. Its purpose is to
  10212 /// distinguish between standard calls and method call syntax `a.b()`. Thus, if
  10213 /// the lhs is a field access, we return using the `field` union field;
  10214 /// otherwise, we use the `direct` union field.
  10215 fn calleeExpr(
  10216     gz: *GenZir,
  10217     scope: *Scope,
  10218     call_rl: ResultInfo.Loc,
  10219     node: Ast.Node.Index,
  10220 ) InnerError!Callee {
  10221     const astgen = gz.astgen;
  10222     const tree = astgen.tree;
  10223 
  10224     const tag = tree.nodes.items(.tag)[node];
  10225     switch (tag) {
  10226         .field_access => {
  10227             const main_tokens = tree.nodes.items(.main_token);
  10228             const node_datas = tree.nodes.items(.data);
  10229             const object_node = node_datas[node].lhs;
  10230             const dot_token = main_tokens[node];
  10231             const field_ident = dot_token + 1;
  10232             const str_index = try astgen.identAsString(field_ident);
  10233             // Capture the object by reference so we can promote it to an
  10234             // address in Sema if needed.
  10235             const lhs = try expr(gz, scope, .{ .rl = .ref }, object_node);
  10236 
  10237             const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
  10238             try emitDbgStmt(gz, cursor);
  10239 
  10240             return .{ .field = .{
  10241                 .obj_ptr = lhs,
  10242                 .field_name_start = str_index,
  10243             } };
  10244         },
  10245         .enum_literal => if (try call_rl.resultType(gz, node)) |res_ty| {
  10246             // Decl literal call syntax, e.g.
  10247             // `const foo: T = .init();`
  10248             // Look up `init` in `T`, but don't try and coerce it.
  10249             const str_index = try astgen.identAsString(tree.nodes.items(.main_token)[node]);
  10250             const callee = try gz.addPlNode(.decl_literal_no_coerce, node, Zir.Inst.Field{
  10251                 .lhs = res_ty,
  10252                 .field_name_start = str_index,
  10253             });
  10254             return .{ .direct = callee };
  10255         } else {
  10256             return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) };
  10257         },
  10258         else => return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) },
  10259     }
  10260 }
  10261 
  10262 const primitive_instrs = std.StaticStringMap(Zir.Inst.Ref).initComptime(.{
  10263     .{ "anyerror", .anyerror_type },
  10264     .{ "anyframe", .anyframe_type },
  10265     .{ "anyopaque", .anyopaque_type },
  10266     .{ "bool", .bool_type },
  10267     .{ "c_int", .c_int_type },
  10268     .{ "c_long", .c_long_type },
  10269     .{ "c_longdouble", .c_longdouble_type },
  10270     .{ "c_longlong", .c_longlong_type },
  10271     .{ "c_char", .c_char_type },
  10272     .{ "c_short", .c_short_type },
  10273     .{ "c_uint", .c_uint_type },
  10274     .{ "c_ulong", .c_ulong_type },
  10275     .{ "c_ulonglong", .c_ulonglong_type },
  10276     .{ "c_ushort", .c_ushort_type },
  10277     .{ "comptime_float", .comptime_float_type },
  10278     .{ "comptime_int", .comptime_int_type },
  10279     .{ "f128", .f128_type },
  10280     .{ "f16", .f16_type },
  10281     .{ "f32", .f32_type },
  10282     .{ "f64", .f64_type },
  10283     .{ "f80", .f80_type },
  10284     .{ "false", .bool_false },
  10285     .{ "i16", .i16_type },
  10286     .{ "i32", .i32_type },
  10287     .{ "i64", .i64_type },
  10288     .{ "i128", .i128_type },
  10289     .{ "i8", .i8_type },
  10290     .{ "isize", .isize_type },
  10291     .{ "noreturn", .noreturn_type },
  10292     .{ "null", .null_value },
  10293     .{ "true", .bool_true },
  10294     .{ "type", .type_type },
  10295     .{ "u16", .u16_type },
  10296     .{ "u29", .u29_type },
  10297     .{ "u32", .u32_type },
  10298     .{ "u64", .u64_type },
  10299     .{ "u128", .u128_type },
  10300     .{ "u1", .u1_type },
  10301     .{ "u8", .u8_type },
  10302     .{ "undefined", .undef },
  10303     .{ "usize", .usize_type },
  10304     .{ "void", .void_type },
  10305 });
  10306 
  10307 comptime {
  10308     // These checks ensure that std.zig.primitives stays in sync with the primitive->Zir map.
  10309     const primitives = std.zig.primitives;
  10310     for (primitive_instrs.keys(), primitive_instrs.values()) |key, value| {
  10311         if (!primitives.isPrimitive(key)) {
  10312             @compileError("std.zig.isPrimitive() is not aware of Zir instr '" ++ @tagName(value) ++ "'");
  10313         }
  10314     }
  10315     for (primitives.names.keys()) |key| {
  10316         if (primitive_instrs.get(key) == null) {
  10317             @compileError("std.zig.primitives entry '" ++ key ++ "' does not have a corresponding Zir instr");
  10318         }
  10319     }
  10320 }
  10321 
  10322 fn nodeIsTriviallyZero(tree: *const Ast, node: Ast.Node.Index) bool {
  10323     const node_tags = tree.nodes.items(.tag);
  10324     const main_tokens = tree.nodes.items(.main_token);
  10325 
  10326     switch (node_tags[node]) {
  10327         .number_literal => {
  10328             const ident = main_tokens[node];
  10329             return switch (std.zig.parseNumberLiteral(tree.tokenSlice(ident))) {
  10330                 .int => |number| switch (number) {
  10331                     0 => true,
  10332                     else => false,
  10333                 },
  10334                 else => false,
  10335             };
  10336         },
  10337         else => return false,
  10338     }
  10339 }
  10340 
  10341 fn nodeMayAppendToErrorTrace(tree: *const Ast, start_node: Ast.Node.Index) bool {
  10342     const node_tags = tree.nodes.items(.tag);
  10343     const node_datas = tree.nodes.items(.data);
  10344 
  10345     var node = start_node;
  10346     while (true) {
  10347         switch (node_tags[node]) {
  10348             // These don't have the opportunity to call any runtime functions.
  10349             .error_value,
  10350             .identifier,
  10351             .@"comptime",
  10352             => return false,
  10353 
  10354             // Forward the question to the LHS sub-expression.
  10355             .grouped_expression,
  10356             .@"try",
  10357             .@"nosuspend",
  10358             .unwrap_optional,
  10359             => node = node_datas[node].lhs,
  10360 
  10361             // Anything that does not eval to an error is guaranteed to pop any
  10362             // additions to the error trace, so it effectively does not append.
  10363             else => return nodeMayEvalToError(tree, start_node) != .never,
  10364         }
  10365     }
  10366 }
  10367 
  10368 fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.EvalToError {
  10369     const node_tags = tree.nodes.items(.tag);
  10370     const node_datas = tree.nodes.items(.data);
  10371     const main_tokens = tree.nodes.items(.main_token);
  10372     const token_tags = tree.tokens.items(.tag);
  10373 
  10374     var node = start_node;
  10375     while (true) {
  10376         switch (node_tags[node]) {
  10377             .root,
  10378             .@"usingnamespace",
  10379             .test_decl,
  10380             .switch_case,
  10381             .switch_case_inline,
  10382             .switch_case_one,
  10383             .switch_case_inline_one,
  10384             .container_field_init,
  10385             .container_field_align,
  10386             .container_field,
  10387             .asm_output,
  10388             .asm_input,
  10389             => unreachable,
  10390 
  10391             .error_value => return .always,
  10392 
  10393             .@"asm",
  10394             .asm_simple,
  10395             .identifier,
  10396             .field_access,
  10397             .deref,
  10398             .array_access,
  10399             .while_simple,
  10400             .while_cont,
  10401             .for_simple,
  10402             .if_simple,
  10403             .@"while",
  10404             .@"if",
  10405             .@"for",
  10406             .@"switch",
  10407             .switch_comma,
  10408             .call_one,
  10409             .call_one_comma,
  10410             .async_call_one,
  10411             .async_call_one_comma,
  10412             .call,
  10413             .call_comma,
  10414             .async_call,
  10415             .async_call_comma,
  10416             => return .maybe,
  10417 
  10418             .@"return",
  10419             .@"break",
  10420             .@"continue",
  10421             .bit_not,
  10422             .bool_not,
  10423             .global_var_decl,
  10424             .local_var_decl,
  10425             .simple_var_decl,
  10426             .aligned_var_decl,
  10427             .@"defer",
  10428             .@"errdefer",
  10429             .address_of,
  10430             .optional_type,
  10431             .negation,
  10432             .negation_wrap,
  10433             .@"resume",
  10434             .array_type,
  10435             .array_type_sentinel,
  10436             .ptr_type_aligned,
  10437             .ptr_type_sentinel,
  10438             .ptr_type,
  10439             .ptr_type_bit_range,
  10440             .@"suspend",
  10441             .fn_proto_simple,
  10442             .fn_proto_multi,
  10443             .fn_proto_one,
  10444             .fn_proto,
  10445             .fn_decl,
  10446             .anyframe_type,
  10447             .anyframe_literal,
  10448             .number_literal,
  10449             .enum_literal,
  10450             .string_literal,
  10451             .multiline_string_literal,
  10452             .char_literal,
  10453             .unreachable_literal,
  10454             .error_set_decl,
  10455             .container_decl,
  10456             .container_decl_trailing,
  10457             .container_decl_two,
  10458             .container_decl_two_trailing,
  10459             .container_decl_arg,
  10460             .container_decl_arg_trailing,
  10461             .tagged_union,
  10462             .tagged_union_trailing,
  10463             .tagged_union_two,
  10464             .tagged_union_two_trailing,
  10465             .tagged_union_enum_tag,
  10466             .tagged_union_enum_tag_trailing,
  10467             .add,
  10468             .add_wrap,
  10469             .add_sat,
  10470             .array_cat,
  10471             .array_mult,
  10472             .assign,
  10473             .assign_destructure,
  10474             .assign_bit_and,
  10475             .assign_bit_or,
  10476             .assign_shl,
  10477             .assign_shl_sat,
  10478             .assign_shr,
  10479             .assign_bit_xor,
  10480             .assign_div,
  10481             .assign_sub,
  10482             .assign_sub_wrap,
  10483             .assign_sub_sat,
  10484             .assign_mod,
  10485             .assign_add,
  10486             .assign_add_wrap,
  10487             .assign_add_sat,
  10488             .assign_mul,
  10489             .assign_mul_wrap,
  10490             .assign_mul_sat,
  10491             .bang_equal,
  10492             .bit_and,
  10493             .bit_or,
  10494             .shl,
  10495             .shl_sat,
  10496             .shr,
  10497             .bit_xor,
  10498             .bool_and,
  10499             .bool_or,
  10500             .div,
  10501             .equal_equal,
  10502             .error_union,
  10503             .greater_or_equal,
  10504             .greater_than,
  10505             .less_or_equal,
  10506             .less_than,
  10507             .merge_error_sets,
  10508             .mod,
  10509             .mul,
  10510             .mul_wrap,
  10511             .mul_sat,
  10512             .switch_range,
  10513             .for_range,
  10514             .sub,
  10515             .sub_wrap,
  10516             .sub_sat,
  10517             .slice,
  10518             .slice_open,
  10519             .slice_sentinel,
  10520             .array_init_one,
  10521             .array_init_one_comma,
  10522             .array_init_dot_two,
  10523             .array_init_dot_two_comma,
  10524             .array_init_dot,
  10525             .array_init_dot_comma,
  10526             .array_init,
  10527             .array_init_comma,
  10528             .struct_init_one,
  10529             .struct_init_one_comma,
  10530             .struct_init_dot_two,
  10531             .struct_init_dot_two_comma,
  10532             .struct_init_dot,
  10533             .struct_init_dot_comma,
  10534             .struct_init,
  10535             .struct_init_comma,
  10536             => return .never,
  10537 
  10538             // Forward the question to the LHS sub-expression.
  10539             .grouped_expression,
  10540             .@"try",
  10541             .@"await",
  10542             .@"comptime",
  10543             .@"nosuspend",
  10544             .unwrap_optional,
  10545             => node = node_datas[node].lhs,
  10546 
  10547             // LHS sub-expression may still be an error under the outer optional or error union
  10548             .@"catch",
  10549             .@"orelse",
  10550             => return .maybe,
  10551 
  10552             .block_two,
  10553             .block_two_semicolon,
  10554             .block,
  10555             .block_semicolon,
  10556             => {
  10557                 const lbrace = main_tokens[node];
  10558                 if (token_tags[lbrace - 1] == .colon) {
  10559                     // Labeled blocks may need a memory location to forward
  10560                     // to their break statements.
  10561                     return .maybe;
  10562                 } else {
  10563                     return .never;
  10564                 }
  10565             },
  10566 
  10567             .builtin_call,
  10568             .builtin_call_comma,
  10569             .builtin_call_two,
  10570             .builtin_call_two_comma,
  10571             => {
  10572                 const builtin_token = main_tokens[node];
  10573                 const builtin_name = tree.tokenSlice(builtin_token);
  10574                 // If the builtin is an invalid name, we don't cause an error here; instead
  10575                 // let it pass, and the error will be "invalid builtin function" later.
  10576                 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return .maybe;
  10577                 return builtin_info.eval_to_error;
  10578             },
  10579         }
  10580     }
  10581 }
  10582 
  10583 /// Returns `true` if it is known the type expression has more than one possible value;
  10584 /// `false` otherwise.
  10585 fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.Index) bool {
  10586     const node_tags = tree.nodes.items(.tag);
  10587     const node_datas = tree.nodes.items(.data);
  10588 
  10589     var node = start_node;
  10590     while (true) {
  10591         switch (node_tags[node]) {
  10592             .root,
  10593             .@"usingnamespace",
  10594             .test_decl,
  10595             .switch_case,
  10596             .switch_case_inline,
  10597             .switch_case_one,
  10598             .switch_case_inline_one,
  10599             .container_field_init,
  10600             .container_field_align,
  10601             .container_field,
  10602             .asm_output,
  10603             .asm_input,
  10604             .global_var_decl,
  10605             .local_var_decl,
  10606             .simple_var_decl,
  10607             .aligned_var_decl,
  10608             => unreachable,
  10609 
  10610             .@"return",
  10611             .@"break",
  10612             .@"continue",
  10613             .bit_not,
  10614             .bool_not,
  10615             .@"defer",
  10616             .@"errdefer",
  10617             .address_of,
  10618             .negation,
  10619             .negation_wrap,
  10620             .@"resume",
  10621             .array_type,
  10622             .@"suspend",
  10623             .fn_decl,
  10624             .anyframe_literal,
  10625             .number_literal,
  10626             .enum_literal,
  10627             .string_literal,
  10628             .multiline_string_literal,
  10629             .char_literal,
  10630             .unreachable_literal,
  10631             .error_set_decl,
  10632             .container_decl,
  10633             .container_decl_trailing,
  10634             .container_decl_two,
  10635             .container_decl_two_trailing,
  10636             .container_decl_arg,
  10637             .container_decl_arg_trailing,
  10638             .tagged_union,
  10639             .tagged_union_trailing,
  10640             .tagged_union_two,
  10641             .tagged_union_two_trailing,
  10642             .tagged_union_enum_tag,
  10643             .tagged_union_enum_tag_trailing,
  10644             .@"asm",
  10645             .asm_simple,
  10646             .add,
  10647             .add_wrap,
  10648             .add_sat,
  10649             .array_cat,
  10650             .array_mult,
  10651             .assign,
  10652             .assign_destructure,
  10653             .assign_bit_and,
  10654             .assign_bit_or,
  10655             .assign_shl,
  10656             .assign_shl_sat,
  10657             .assign_shr,
  10658             .assign_bit_xor,
  10659             .assign_div,
  10660             .assign_sub,
  10661             .assign_sub_wrap,
  10662             .assign_sub_sat,
  10663             .assign_mod,
  10664             .assign_add,
  10665             .assign_add_wrap,
  10666             .assign_add_sat,
  10667             .assign_mul,
  10668             .assign_mul_wrap,
  10669             .assign_mul_sat,
  10670             .bang_equal,
  10671             .bit_and,
  10672             .bit_or,
  10673             .shl,
  10674             .shl_sat,
  10675             .shr,
  10676             .bit_xor,
  10677             .bool_and,
  10678             .bool_or,
  10679             .div,
  10680             .equal_equal,
  10681             .error_union,
  10682             .greater_or_equal,
  10683             .greater_than,
  10684             .less_or_equal,
  10685             .less_than,
  10686             .merge_error_sets,
  10687             .mod,
  10688             .mul,
  10689             .mul_wrap,
  10690             .mul_sat,
  10691             .switch_range,
  10692             .for_range,
  10693             .field_access,
  10694             .sub,
  10695             .sub_wrap,
  10696             .sub_sat,
  10697             .slice,
  10698             .slice_open,
  10699             .slice_sentinel,
  10700             .deref,
  10701             .array_access,
  10702             .error_value,
  10703             .while_simple,
  10704             .while_cont,
  10705             .for_simple,
  10706             .if_simple,
  10707             .@"catch",
  10708             .@"orelse",
  10709             .array_init_one,
  10710             .array_init_one_comma,
  10711             .array_init_dot_two,
  10712             .array_init_dot_two_comma,
  10713             .array_init_dot,
  10714             .array_init_dot_comma,
  10715             .array_init,
  10716             .array_init_comma,
  10717             .struct_init_one,
  10718             .struct_init_one_comma,
  10719             .struct_init_dot_two,
  10720             .struct_init_dot_two_comma,
  10721             .struct_init_dot,
  10722             .struct_init_dot_comma,
  10723             .struct_init,
  10724             .struct_init_comma,
  10725             .@"while",
  10726             .@"if",
  10727             .@"for",
  10728             .@"switch",
  10729             .switch_comma,
  10730             .call_one,
  10731             .call_one_comma,
  10732             .async_call_one,
  10733             .async_call_one_comma,
  10734             .call,
  10735             .call_comma,
  10736             .async_call,
  10737             .async_call_comma,
  10738             .block_two,
  10739             .block_two_semicolon,
  10740             .block,
  10741             .block_semicolon,
  10742             .builtin_call,
  10743             .builtin_call_comma,
  10744             .builtin_call_two,
  10745             .builtin_call_two_comma,
  10746             // these are function bodies, not pointers
  10747             .fn_proto_simple,
  10748             .fn_proto_multi,
  10749             .fn_proto_one,
  10750             .fn_proto,
  10751             => return false,
  10752 
  10753             // Forward the question to the LHS sub-expression.
  10754             .grouped_expression,
  10755             .@"try",
  10756             .@"await",
  10757             .@"comptime",
  10758             .@"nosuspend",
  10759             .unwrap_optional,
  10760             => node = node_datas[node].lhs,
  10761 
  10762             .ptr_type_aligned,
  10763             .ptr_type_sentinel,
  10764             .ptr_type,
  10765             .ptr_type_bit_range,
  10766             .optional_type,
  10767             .anyframe_type,
  10768             .array_type_sentinel,
  10769             => return true,
  10770 
  10771             .identifier => {
  10772                 const main_tokens = tree.nodes.items(.main_token);
  10773                 const ident_bytes = tree.tokenSlice(main_tokens[node]);
  10774                 if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) {
  10775                     .anyerror_type,
  10776                     .anyframe_type,
  10777                     .anyopaque_type,
  10778                     .bool_type,
  10779                     .c_int_type,
  10780                     .c_long_type,
  10781                     .c_longdouble_type,
  10782                     .c_longlong_type,
  10783                     .c_char_type,
  10784                     .c_short_type,
  10785                     .c_uint_type,
  10786                     .c_ulong_type,
  10787                     .c_ulonglong_type,
  10788                     .c_ushort_type,
  10789                     .comptime_float_type,
  10790                     .comptime_int_type,
  10791                     .f16_type,
  10792                     .f32_type,
  10793                     .f64_type,
  10794                     .f80_type,
  10795                     .f128_type,
  10796                     .i16_type,
  10797                     .i32_type,
  10798                     .i64_type,
  10799                     .i128_type,
  10800                     .i8_type,
  10801                     .isize_type,
  10802                     .type_type,
  10803                     .u16_type,
  10804                     .u29_type,
  10805                     .u32_type,
  10806                     .u64_type,
  10807                     .u128_type,
  10808                     .u1_type,
  10809                     .u8_type,
  10810                     .usize_type,
  10811                     => return true,
  10812 
  10813                     .void_type,
  10814                     .bool_false,
  10815                     .bool_true,
  10816                     .null_value,
  10817                     .undef,
  10818                     .noreturn_type,
  10819                     => return false,
  10820 
  10821                     else => unreachable, // that's all the values from `primitives`.
  10822                 } else {
  10823                     return false;
  10824                 }
  10825             },
  10826         }
  10827     }
  10828 }
  10829 
  10830 /// Returns `true` if it is known the expression is a type that cannot be used at runtime;
  10831 /// `false` otherwise.
  10832 fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
  10833     const node_tags = tree.nodes.items(.tag);
  10834     const node_datas = tree.nodes.items(.data);
  10835 
  10836     var node = start_node;
  10837     while (true) {
  10838         switch (node_tags[node]) {
  10839             .root,
  10840             .@"usingnamespace",
  10841             .test_decl,
  10842             .switch_case,
  10843             .switch_case_inline,
  10844             .switch_case_one,
  10845             .switch_case_inline_one,
  10846             .container_field_init,
  10847             .container_field_align,
  10848             .container_field,
  10849             .asm_output,
  10850             .asm_input,
  10851             .global_var_decl,
  10852             .local_var_decl,
  10853             .simple_var_decl,
  10854             .aligned_var_decl,
  10855             => unreachable,
  10856 
  10857             .@"return",
  10858             .@"break",
  10859             .@"continue",
  10860             .bit_not,
  10861             .bool_not,
  10862             .@"defer",
  10863             .@"errdefer",
  10864             .address_of,
  10865             .negation,
  10866             .negation_wrap,
  10867             .@"resume",
  10868             .array_type,
  10869             .@"suspend",
  10870             .fn_decl,
  10871             .anyframe_literal,
  10872             .number_literal,
  10873             .enum_literal,
  10874             .string_literal,
  10875             .multiline_string_literal,
  10876             .char_literal,
  10877             .unreachable_literal,
  10878             .error_set_decl,
  10879             .container_decl,
  10880             .container_decl_trailing,
  10881             .container_decl_two,
  10882             .container_decl_two_trailing,
  10883             .container_decl_arg,
  10884             .container_decl_arg_trailing,
  10885             .tagged_union,
  10886             .tagged_union_trailing,
  10887             .tagged_union_two,
  10888             .tagged_union_two_trailing,
  10889             .tagged_union_enum_tag,
  10890             .tagged_union_enum_tag_trailing,
  10891             .@"asm",
  10892             .asm_simple,
  10893             .add,
  10894             .add_wrap,
  10895             .add_sat,
  10896             .array_cat,
  10897             .array_mult,
  10898             .assign,
  10899             .assign_destructure,
  10900             .assign_bit_and,
  10901             .assign_bit_or,
  10902             .assign_shl,
  10903             .assign_shl_sat,
  10904             .assign_shr,
  10905             .assign_bit_xor,
  10906             .assign_div,
  10907             .assign_sub,
  10908             .assign_sub_wrap,
  10909             .assign_sub_sat,
  10910             .assign_mod,
  10911             .assign_add,
  10912             .assign_add_wrap,
  10913             .assign_add_sat,
  10914             .assign_mul,
  10915             .assign_mul_wrap,
  10916             .assign_mul_sat,
  10917             .bang_equal,
  10918             .bit_and,
  10919             .bit_or,
  10920             .shl,
  10921             .shl_sat,
  10922             .shr,
  10923             .bit_xor,
  10924             .bool_and,
  10925             .bool_or,
  10926             .div,
  10927             .equal_equal,
  10928             .error_union,
  10929             .greater_or_equal,
  10930             .greater_than,
  10931             .less_or_equal,
  10932             .less_than,
  10933             .merge_error_sets,
  10934             .mod,
  10935             .mul,
  10936             .mul_wrap,
  10937             .mul_sat,
  10938             .switch_range,
  10939             .for_range,
  10940             .field_access,
  10941             .sub,
  10942             .sub_wrap,
  10943             .sub_sat,
  10944             .slice,
  10945             .slice_open,
  10946             .slice_sentinel,
  10947             .deref,
  10948             .array_access,
  10949             .error_value,
  10950             .while_simple,
  10951             .while_cont,
  10952             .for_simple,
  10953             .if_simple,
  10954             .@"catch",
  10955             .@"orelse",
  10956             .array_init_one,
  10957             .array_init_one_comma,
  10958             .array_init_dot_two,
  10959             .array_init_dot_two_comma,
  10960             .array_init_dot,
  10961             .array_init_dot_comma,
  10962             .array_init,
  10963             .array_init_comma,
  10964             .struct_init_one,
  10965             .struct_init_one_comma,
  10966             .struct_init_dot_two,
  10967             .struct_init_dot_two_comma,
  10968             .struct_init_dot,
  10969             .struct_init_dot_comma,
  10970             .struct_init,
  10971             .struct_init_comma,
  10972             .@"while",
  10973             .@"if",
  10974             .@"for",
  10975             .@"switch",
  10976             .switch_comma,
  10977             .call_one,
  10978             .call_one_comma,
  10979             .async_call_one,
  10980             .async_call_one_comma,
  10981             .call,
  10982             .call_comma,
  10983             .async_call,
  10984             .async_call_comma,
  10985             .block_two,
  10986             .block_two_semicolon,
  10987             .block,
  10988             .block_semicolon,
  10989             .builtin_call,
  10990             .builtin_call_comma,
  10991             .builtin_call_two,
  10992             .builtin_call_two_comma,
  10993             .ptr_type_aligned,
  10994             .ptr_type_sentinel,
  10995             .ptr_type,
  10996             .ptr_type_bit_range,
  10997             .optional_type,
  10998             .anyframe_type,
  10999             .array_type_sentinel,
  11000             => return false,
  11001 
  11002             // these are function bodies, not pointers
  11003             .fn_proto_simple,
  11004             .fn_proto_multi,
  11005             .fn_proto_one,
  11006             .fn_proto,
  11007             => return true,
  11008 
  11009             // Forward the question to the LHS sub-expression.
  11010             .grouped_expression,
  11011             .@"try",
  11012             .@"await",
  11013             .@"comptime",
  11014             .@"nosuspend",
  11015             .unwrap_optional,
  11016             => node = node_datas[node].lhs,
  11017 
  11018             .identifier => {
  11019                 const main_tokens = tree.nodes.items(.main_token);
  11020                 const ident_bytes = tree.tokenSlice(main_tokens[node]);
  11021                 if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) {
  11022                     .anyerror_type,
  11023                     .anyframe_type,
  11024                     .anyopaque_type,
  11025                     .bool_type,
  11026                     .c_int_type,
  11027                     .c_long_type,
  11028                     .c_longdouble_type,
  11029                     .c_longlong_type,
  11030                     .c_char_type,
  11031                     .c_short_type,
  11032                     .c_uint_type,
  11033                     .c_ulong_type,
  11034                     .c_ulonglong_type,
  11035                     .c_ushort_type,
  11036                     .f16_type,
  11037                     .f32_type,
  11038                     .f64_type,
  11039                     .f80_type,
  11040                     .f128_type,
  11041                     .i16_type,
  11042                     .i32_type,
  11043                     .i64_type,
  11044                     .i128_type,
  11045                     .i8_type,
  11046                     .isize_type,
  11047                     .u16_type,
  11048                     .u29_type,
  11049                     .u32_type,
  11050                     .u64_type,
  11051                     .u128_type,
  11052                     .u1_type,
  11053                     .u8_type,
  11054                     .usize_type,
  11055                     .void_type,
  11056                     .bool_false,
  11057                     .bool_true,
  11058                     .null_value,
  11059                     .undef,
  11060                     .noreturn_type,
  11061                     => return false,
  11062 
  11063                     .comptime_float_type,
  11064                     .comptime_int_type,
  11065                     .type_type,
  11066                     => return true,
  11067 
  11068                     else => unreachable, // that's all the values from `primitives`.
  11069                 } else {
  11070                     return false;
  11071                 }
  11072             },
  11073         }
  11074     }
  11075 }
  11076 
  11077 /// Returns `true` if the node uses `gz.anon_name_strategy`.
  11078 fn nodeUsesAnonNameStrategy(tree: *const Ast, node: Ast.Node.Index) bool {
  11079     const node_tags = tree.nodes.items(.tag);
  11080     switch (node_tags[node]) {
  11081         .container_decl,
  11082         .container_decl_trailing,
  11083         .container_decl_two,
  11084         .container_decl_two_trailing,
  11085         .container_decl_arg,
  11086         .container_decl_arg_trailing,
  11087         .tagged_union,
  11088         .tagged_union_trailing,
  11089         .tagged_union_two,
  11090         .tagged_union_two_trailing,
  11091         .tagged_union_enum_tag,
  11092         .tagged_union_enum_tag_trailing,
  11093         => return true,
  11094         .builtin_call_two, .builtin_call_two_comma, .builtin_call, .builtin_call_comma => {
  11095             const builtin_token = tree.nodes.items(.main_token)[node];
  11096             const builtin_name = tree.tokenSlice(builtin_token);
  11097             return std.mem.eql(u8, builtin_name, "@Type");
  11098         },
  11099         else => return false,
  11100     }
  11101 }
  11102 
  11103 /// Applies `rl` semantics to `result`. Expressions which do not do their own handling of
  11104 /// result locations must call this function on their result.
  11105 /// As an example, if `ri.rl` is `.ptr`, it will write the result to the pointer.
  11106 /// If `ri.rl` is `.ty`, it will coerce the result to the type.
  11107 /// Assumes nothing stacked on `gz`.
  11108 fn rvalue(
  11109     gz: *GenZir,
  11110     ri: ResultInfo,
  11111     raw_result: Zir.Inst.Ref,
  11112     src_node: Ast.Node.Index,
  11113 ) InnerError!Zir.Inst.Ref {
  11114     return rvalueInner(gz, ri, raw_result, src_node, true);
  11115 }
  11116 
  11117 /// Like `rvalue`, but refuses to perform coercions before taking references for
  11118 /// the `ref_coerced_ty` result type. This is used for local variables which do
  11119 /// not have `alloc`s, because we want variables to have consistent addresses,
  11120 /// i.e. we want them to act like lvalues.
  11121 fn rvalueNoCoercePreRef(
  11122     gz: *GenZir,
  11123     ri: ResultInfo,
  11124     raw_result: Zir.Inst.Ref,
  11125     src_node: Ast.Node.Index,
  11126 ) InnerError!Zir.Inst.Ref {
  11127     return rvalueInner(gz, ri, raw_result, src_node, false);
  11128 }
  11129 
  11130 fn rvalueInner(
  11131     gz: *GenZir,
  11132     ri: ResultInfo,
  11133     raw_result: Zir.Inst.Ref,
  11134     src_node: Ast.Node.Index,
  11135     allow_coerce_pre_ref: bool,
  11136 ) InnerError!Zir.Inst.Ref {
  11137     const result = r: {
  11138         if (raw_result.toIndex()) |result_index| {
  11139             const zir_tags = gz.astgen.instructions.items(.tag);
  11140             const data = gz.astgen.instructions.items(.data)[@intFromEnum(result_index)];
  11141             if (zir_tags[@intFromEnum(result_index)].isAlwaysVoid(data)) {
  11142                 break :r Zir.Inst.Ref.void_value;
  11143             }
  11144         }
  11145         break :r raw_result;
  11146     };
  11147     if (gz.endsWithNoReturn()) return result;
  11148     switch (ri.rl) {
  11149         .none, .coerced_ty => return result,
  11150         .discard => {
  11151             // Emit a compile error for discarding error values.
  11152             _ = try gz.addUnNode(.ensure_result_non_error, result, src_node);
  11153             return .void_value;
  11154         },
  11155         .ref, .ref_coerced_ty => {
  11156             const coerced_result = if (allow_coerce_pre_ref and ri.rl == .ref_coerced_ty) res: {
  11157                 const ptr_ty = ri.rl.ref_coerced_ty;
  11158                 break :res try gz.addPlNode(.coerce_ptr_elem_ty, src_node, Zir.Inst.Bin{
  11159                     .lhs = ptr_ty,
  11160                     .rhs = result,
  11161                 });
  11162             } else result;
  11163             // We need a pointer but we have a value.
  11164             // Unfortunately it's not quite as simple as directly emitting a ref
  11165             // instruction here because we need subsequent address-of operator on
  11166             // const locals to return the same address.
  11167             const astgen = gz.astgen;
  11168             const tree = astgen.tree;
  11169             const src_token = tree.firstToken(src_node);
  11170             const result_index = coerced_result.toIndex() orelse
  11171                 return gz.addUnTok(.ref, coerced_result, src_token);
  11172             const gop = try astgen.ref_table.getOrPut(astgen.gpa, result_index);
  11173             if (!gop.found_existing) {
  11174                 gop.value_ptr.* = try gz.makeUnTok(.ref, coerced_result, src_token);
  11175             }
  11176             return gop.value_ptr.*.toRef();
  11177         },
  11178         .ty => |ty_inst| {
  11179             // Quickly eliminate some common, unnecessary type coercion.
  11180             const as_ty = @as(u64, @intFromEnum(Zir.Inst.Ref.type_type)) << 32;
  11181             const as_bool = @as(u64, @intFromEnum(Zir.Inst.Ref.bool_type)) << 32;
  11182             const as_void = @as(u64, @intFromEnum(Zir.Inst.Ref.void_type)) << 32;
  11183             const as_comptime_int = @as(u64, @intFromEnum(Zir.Inst.Ref.comptime_int_type)) << 32;
  11184             const as_usize = @as(u64, @intFromEnum(Zir.Inst.Ref.usize_type)) << 32;
  11185             const as_u8 = @as(u64, @intFromEnum(Zir.Inst.Ref.u8_type)) << 32;
  11186             switch ((@as(u64, @intFromEnum(ty_inst)) << 32) | @as(u64, @intFromEnum(result))) {
  11187                 as_ty | @intFromEnum(Zir.Inst.Ref.u1_type),
  11188                 as_ty | @intFromEnum(Zir.Inst.Ref.u8_type),
  11189                 as_ty | @intFromEnum(Zir.Inst.Ref.i8_type),
  11190                 as_ty | @intFromEnum(Zir.Inst.Ref.u16_type),
  11191                 as_ty | @intFromEnum(Zir.Inst.Ref.u29_type),
  11192                 as_ty | @intFromEnum(Zir.Inst.Ref.i16_type),
  11193                 as_ty | @intFromEnum(Zir.Inst.Ref.u32_type),
  11194                 as_ty | @intFromEnum(Zir.Inst.Ref.i32_type),
  11195                 as_ty | @intFromEnum(Zir.Inst.Ref.u64_type),
  11196                 as_ty | @intFromEnum(Zir.Inst.Ref.i64_type),
  11197                 as_ty | @intFromEnum(Zir.Inst.Ref.u128_type),
  11198                 as_ty | @intFromEnum(Zir.Inst.Ref.i128_type),
  11199                 as_ty | @intFromEnum(Zir.Inst.Ref.usize_type),
  11200                 as_ty | @intFromEnum(Zir.Inst.Ref.isize_type),
  11201                 as_ty | @intFromEnum(Zir.Inst.Ref.c_char_type),
  11202                 as_ty | @intFromEnum(Zir.Inst.Ref.c_short_type),
  11203                 as_ty | @intFromEnum(Zir.Inst.Ref.c_ushort_type),
  11204                 as_ty | @intFromEnum(Zir.Inst.Ref.c_int_type),
  11205                 as_ty | @intFromEnum(Zir.Inst.Ref.c_uint_type),
  11206                 as_ty | @intFromEnum(Zir.Inst.Ref.c_long_type),
  11207                 as_ty | @intFromEnum(Zir.Inst.Ref.c_ulong_type),
  11208                 as_ty | @intFromEnum(Zir.Inst.Ref.c_longlong_type),
  11209                 as_ty | @intFromEnum(Zir.Inst.Ref.c_ulonglong_type),
  11210                 as_ty | @intFromEnum(Zir.Inst.Ref.c_longdouble_type),
  11211                 as_ty | @intFromEnum(Zir.Inst.Ref.f16_type),
  11212                 as_ty | @intFromEnum(Zir.Inst.Ref.f32_type),
  11213                 as_ty | @intFromEnum(Zir.Inst.Ref.f64_type),
  11214                 as_ty | @intFromEnum(Zir.Inst.Ref.f80_type),
  11215                 as_ty | @intFromEnum(Zir.Inst.Ref.f128_type),
  11216                 as_ty | @intFromEnum(Zir.Inst.Ref.anyopaque_type),
  11217                 as_ty | @intFromEnum(Zir.Inst.Ref.bool_type),
  11218                 as_ty | @intFromEnum(Zir.Inst.Ref.void_type),
  11219                 as_ty | @intFromEnum(Zir.Inst.Ref.type_type),
  11220                 as_ty | @intFromEnum(Zir.Inst.Ref.anyerror_type),
  11221                 as_ty | @intFromEnum(Zir.Inst.Ref.comptime_int_type),
  11222                 as_ty | @intFromEnum(Zir.Inst.Ref.comptime_float_type),
  11223                 as_ty | @intFromEnum(Zir.Inst.Ref.noreturn_type),
  11224                 as_ty | @intFromEnum(Zir.Inst.Ref.anyframe_type),
  11225                 as_ty | @intFromEnum(Zir.Inst.Ref.null_type),
  11226                 as_ty | @intFromEnum(Zir.Inst.Ref.undefined_type),
  11227                 as_ty | @intFromEnum(Zir.Inst.Ref.enum_literal_type),
  11228                 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_u8_type),
  11229                 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_const_u8_type),
  11230                 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_const_u8_sentinel_0_type),
  11231                 as_ty | @intFromEnum(Zir.Inst.Ref.single_const_pointer_to_comptime_int_type),
  11232                 as_ty | @intFromEnum(Zir.Inst.Ref.slice_const_u8_type),
  11233                 as_ty | @intFromEnum(Zir.Inst.Ref.slice_const_u8_sentinel_0_type),
  11234                 as_ty | @intFromEnum(Zir.Inst.Ref.anyerror_void_error_union_type),
  11235                 as_ty | @intFromEnum(Zir.Inst.Ref.generic_poison_type),
  11236                 as_ty | @intFromEnum(Zir.Inst.Ref.empty_tuple_type),
  11237                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero),
  11238                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one),
  11239                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.negative_one),
  11240                 as_usize | @intFromEnum(Zir.Inst.Ref.zero_usize),
  11241                 as_usize | @intFromEnum(Zir.Inst.Ref.one_usize),
  11242                 as_u8 | @intFromEnum(Zir.Inst.Ref.zero_u8),
  11243                 as_u8 | @intFromEnum(Zir.Inst.Ref.one_u8),
  11244                 as_u8 | @intFromEnum(Zir.Inst.Ref.four_u8),
  11245                 as_bool | @intFromEnum(Zir.Inst.Ref.bool_true),
  11246                 as_bool | @intFromEnum(Zir.Inst.Ref.bool_false),
  11247                 as_void | @intFromEnum(Zir.Inst.Ref.void_value),
  11248                 => return result, // type of result is already correct
  11249 
  11250                 as_usize | @intFromEnum(Zir.Inst.Ref.zero) => return .zero_usize,
  11251                 as_u8 | @intFromEnum(Zir.Inst.Ref.zero) => return .zero_u8,
  11252                 as_usize | @intFromEnum(Zir.Inst.Ref.one) => return .one_usize,
  11253                 as_u8 | @intFromEnum(Zir.Inst.Ref.one) => return .one_u8,
  11254                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero_usize) => return .zero,
  11255                 as_u8 | @intFromEnum(Zir.Inst.Ref.zero_usize) => return .zero_u8,
  11256                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one_usize) => return .one,
  11257                 as_u8 | @intFromEnum(Zir.Inst.Ref.one_usize) => return .one_u8,
  11258                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero_u8) => return .zero,
  11259                 as_usize | @intFromEnum(Zir.Inst.Ref.zero_u8) => return .zero_usize,
  11260                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one_u8) => return .one,
  11261                 as_usize | @intFromEnum(Zir.Inst.Ref.one_u8) => return .one_usize,
  11262 
  11263                 // Need an explicit type coercion instruction.
  11264                 else => return gz.addPlNode(ri.zirTag(), src_node, Zir.Inst.As{
  11265                     .dest_type = ty_inst,
  11266                     .operand = result,
  11267                 }),
  11268             }
  11269         },
  11270         .ptr => |ptr_res| {
  11271             _ = try gz.addPlNode(.store_node, ptr_res.src_node orelse src_node, Zir.Inst.Bin{
  11272                 .lhs = ptr_res.inst,
  11273                 .rhs = result,
  11274             });
  11275             return .void_value;
  11276         },
  11277         .inferred_ptr => |alloc| {
  11278             _ = try gz.addPlNode(.store_to_inferred_ptr, src_node, Zir.Inst.Bin{
  11279                 .lhs = alloc,
  11280                 .rhs = result,
  11281             });
  11282             return .void_value;
  11283         },
  11284         .destructure => |destructure| {
  11285             const components = destructure.components;
  11286             _ = try gz.addPlNode(.validate_destructure, src_node, Zir.Inst.ValidateDestructure{
  11287                 .operand = result,
  11288                 .destructure_node = gz.nodeIndexToRelative(destructure.src_node),
  11289                 .expect_len = @intCast(components.len),
  11290             });
  11291             for (components, 0..) |component, i| {
  11292                 if (component == .discard) continue;
  11293                 const elem_val = try gz.add(.{
  11294                     .tag = .elem_val_imm,
  11295                     .data = .{ .elem_val_imm = .{
  11296                         .operand = result,
  11297                         .idx = @intCast(i),
  11298                     } },
  11299                 });
  11300                 switch (component) {
  11301                     .typed_ptr => |ptr_res| {
  11302                         _ = try gz.addPlNode(.store_node, ptr_res.src_node orelse src_node, Zir.Inst.Bin{
  11303                             .lhs = ptr_res.inst,
  11304                             .rhs = elem_val,
  11305                         });
  11306                     },
  11307                     .inferred_ptr => |ptr_inst| {
  11308                         _ = try gz.addPlNode(.store_to_inferred_ptr, src_node, Zir.Inst.Bin{
  11309                             .lhs = ptr_inst,
  11310                             .rhs = elem_val,
  11311                         });
  11312                     },
  11313                     .discard => unreachable,
  11314                 }
  11315             }
  11316             return .void_value;
  11317         },
  11318     }
  11319 }
  11320 
  11321 /// Given an identifier token, obtain the string for it.
  11322 /// If the token uses @"" syntax, parses as a string, reports errors if applicable,
  11323 /// and allocates the result within `astgen.arena`.
  11324 /// Otherwise, returns a reference to the source code bytes directly.
  11325 /// See also `appendIdentStr` and `parseStrLit`.
  11326 fn identifierTokenString(astgen: *AstGen, token: Ast.TokenIndex) InnerError![]const u8 {
  11327     const tree = astgen.tree;
  11328     const token_tags = tree.tokens.items(.tag);
  11329     assert(token_tags[token] == .identifier);
  11330     const ident_name = tree.tokenSlice(token);
  11331     if (!mem.startsWith(u8, ident_name, "@")) {
  11332         return ident_name;
  11333     }
  11334     var buf: ArrayListUnmanaged(u8) = .empty;
  11335     defer buf.deinit(astgen.gpa);
  11336     try astgen.parseStrLit(token, &buf, ident_name, 1);
  11337     if (mem.indexOfScalar(u8, buf.items, 0) != null) {
  11338         return astgen.failTok(token, "identifier cannot contain null bytes", .{});
  11339     } else if (buf.items.len == 0) {
  11340         return astgen.failTok(token, "identifier cannot be empty", .{});
  11341     }
  11342     const duped = try astgen.arena.dupe(u8, buf.items);
  11343     return duped;
  11344 }
  11345 
  11346 /// Given an identifier token, obtain the string for it (possibly parsing as a string
  11347 /// literal if it is @"" syntax), and append the string to `buf`.
  11348 /// See also `identifierTokenString` and `parseStrLit`.
  11349 fn appendIdentStr(
  11350     astgen: *AstGen,
  11351     token: Ast.TokenIndex,
  11352     buf: *ArrayListUnmanaged(u8),
  11353 ) InnerError!void {
  11354     const tree = astgen.tree;
  11355     const token_tags = tree.tokens.items(.tag);
  11356     assert(token_tags[token] == .identifier);
  11357     const ident_name = tree.tokenSlice(token);
  11358     if (!mem.startsWith(u8, ident_name, "@")) {
  11359         return buf.appendSlice(astgen.gpa, ident_name);
  11360     } else {
  11361         const start = buf.items.len;
  11362         try astgen.parseStrLit(token, buf, ident_name, 1);
  11363         const slice = buf.items[start..];
  11364         if (mem.indexOfScalar(u8, slice, 0) != null) {
  11365             return astgen.failTok(token, "identifier cannot contain null bytes", .{});
  11366         } else if (slice.len == 0) {
  11367             return astgen.failTok(token, "identifier cannot be empty", .{});
  11368         }
  11369     }
  11370 }
  11371 
  11372 /// Appends the result to `buf`.
  11373 fn parseStrLit(
  11374     astgen: *AstGen,
  11375     token: Ast.TokenIndex,
  11376     buf: *ArrayListUnmanaged(u8),
  11377     bytes: []const u8,
  11378     offset: u32,
  11379 ) InnerError!void {
  11380     const raw_string = bytes[offset..];
  11381     var buf_managed = buf.toManaged(astgen.gpa);
  11382     const result = std.zig.string_literal.parseWrite(buf_managed.writer(), raw_string);
  11383     buf.* = buf_managed.moveToUnmanaged();
  11384     switch (try result) {
  11385         .success => return,
  11386         .failure => |err| return astgen.failWithStrLitError(err, token, bytes, offset),
  11387     }
  11388 }
  11389 
  11390 fn failWithStrLitError(astgen: *AstGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, bytes: []const u8, offset: u32) InnerError {
  11391     const raw_string = bytes[offset..];
  11392     return err.lower(raw_string, offset, AstGen.failOff, .{ astgen, token });
  11393 }
  11394 
  11395 fn failNode(
  11396     astgen: *AstGen,
  11397     node: Ast.Node.Index,
  11398     comptime format: []const u8,
  11399     args: anytype,
  11400 ) InnerError {
  11401     return astgen.failNodeNotes(node, format, args, &[0]u32{});
  11402 }
  11403 
  11404 fn appendErrorNode(
  11405     astgen: *AstGen,
  11406     node: Ast.Node.Index,
  11407     comptime format: []const u8,
  11408     args: anytype,
  11409 ) Allocator.Error!void {
  11410     try astgen.appendErrorNodeNotes(node, format, args, &[0]u32{});
  11411 }
  11412 
  11413 fn appendErrorNodeNotes(
  11414     astgen: *AstGen,
  11415     node: Ast.Node.Index,
  11416     comptime format: []const u8,
  11417     args: anytype,
  11418     notes: []const u32,
  11419 ) Allocator.Error!void {
  11420     @branchHint(.cold);
  11421     const string_bytes = &astgen.string_bytes;
  11422     const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
  11423     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
  11424     const notes_index: u32 = if (notes.len != 0) blk: {
  11425         const notes_start = astgen.extra.items.len;
  11426         try astgen.extra.ensureTotalCapacity(astgen.gpa, notes_start + 1 + notes.len);
  11427         astgen.extra.appendAssumeCapacity(@intCast(notes.len));
  11428         astgen.extra.appendSliceAssumeCapacity(notes);
  11429         break :blk @intCast(notes_start);
  11430     } else 0;
  11431     try astgen.compile_errors.append(astgen.gpa, .{
  11432         .msg = msg,
  11433         .node = node,
  11434         .token = 0,
  11435         .byte_offset = 0,
  11436         .notes = notes_index,
  11437     });
  11438 }
  11439 
  11440 fn failNodeNotes(
  11441     astgen: *AstGen,
  11442     node: Ast.Node.Index,
  11443     comptime format: []const u8,
  11444     args: anytype,
  11445     notes: []const u32,
  11446 ) InnerError {
  11447     try appendErrorNodeNotes(astgen, node, format, args, notes);
  11448     return error.AnalysisFail;
  11449 }
  11450 
  11451 fn failTok(
  11452     astgen: *AstGen,
  11453     token: Ast.TokenIndex,
  11454     comptime format: []const u8,
  11455     args: anytype,
  11456 ) InnerError {
  11457     return astgen.failTokNotes(token, format, args, &[0]u32{});
  11458 }
  11459 
  11460 fn appendErrorTok(
  11461     astgen: *AstGen,
  11462     token: Ast.TokenIndex,
  11463     comptime format: []const u8,
  11464     args: anytype,
  11465 ) !void {
  11466     try astgen.appendErrorTokNotesOff(token, 0, format, args, &[0]u32{});
  11467 }
  11468 
  11469 fn failTokNotes(
  11470     astgen: *AstGen,
  11471     token: Ast.TokenIndex,
  11472     comptime format: []const u8,
  11473     args: anytype,
  11474     notes: []const u32,
  11475 ) InnerError {
  11476     try appendErrorTokNotesOff(astgen, token, 0, format, args, notes);
  11477     return error.AnalysisFail;
  11478 }
  11479 
  11480 fn appendErrorTokNotes(
  11481     astgen: *AstGen,
  11482     token: Ast.TokenIndex,
  11483     comptime format: []const u8,
  11484     args: anytype,
  11485     notes: []const u32,
  11486 ) !void {
  11487     return appendErrorTokNotesOff(astgen, token, 0, format, args, notes);
  11488 }
  11489 
  11490 /// Same as `fail`, except given a token plus an offset from its starting byte
  11491 /// offset.
  11492 fn failOff(
  11493     astgen: *AstGen,
  11494     token: Ast.TokenIndex,
  11495     byte_offset: u32,
  11496     comptime format: []const u8,
  11497     args: anytype,
  11498 ) InnerError {
  11499     try appendErrorTokNotesOff(astgen, token, byte_offset, format, args, &.{});
  11500     return error.AnalysisFail;
  11501 }
  11502 
  11503 fn appendErrorTokNotesOff(
  11504     astgen: *AstGen,
  11505     token: Ast.TokenIndex,
  11506     byte_offset: u32,
  11507     comptime format: []const u8,
  11508     args: anytype,
  11509     notes: []const u32,
  11510 ) !void {
  11511     @branchHint(.cold);
  11512     const gpa = astgen.gpa;
  11513     const string_bytes = &astgen.string_bytes;
  11514     const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
  11515     try string_bytes.writer(gpa).print(format ++ "\x00", args);
  11516     const notes_index: u32 = if (notes.len != 0) blk: {
  11517         const notes_start = astgen.extra.items.len;
  11518         try astgen.extra.ensureTotalCapacity(gpa, notes_start + 1 + notes.len);
  11519         astgen.extra.appendAssumeCapacity(@intCast(notes.len));
  11520         astgen.extra.appendSliceAssumeCapacity(notes);
  11521         break :blk @intCast(notes_start);
  11522     } else 0;
  11523     try astgen.compile_errors.append(gpa, .{
  11524         .msg = msg,
  11525         .node = 0,
  11526         .token = token,
  11527         .byte_offset = byte_offset,
  11528         .notes = notes_index,
  11529     });
  11530 }
  11531 
  11532 fn errNoteTok(
  11533     astgen: *AstGen,
  11534     token: Ast.TokenIndex,
  11535     comptime format: []const u8,
  11536     args: anytype,
  11537 ) Allocator.Error!u32 {
  11538     return errNoteTokOff(astgen, token, 0, format, args);
  11539 }
  11540 
  11541 fn errNoteTokOff(
  11542     astgen: *AstGen,
  11543     token: Ast.TokenIndex,
  11544     byte_offset: u32,
  11545     comptime format: []const u8,
  11546     args: anytype,
  11547 ) Allocator.Error!u32 {
  11548     @branchHint(.cold);
  11549     const string_bytes = &astgen.string_bytes;
  11550     const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
  11551     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
  11552     return astgen.addExtra(Zir.Inst.CompileErrors.Item{
  11553         .msg = msg,
  11554         .node = 0,
  11555         .token = token,
  11556         .byte_offset = byte_offset,
  11557         .notes = 0,
  11558     });
  11559 }
  11560 
  11561 fn errNoteNode(
  11562     astgen: *AstGen,
  11563     node: Ast.Node.Index,
  11564     comptime format: []const u8,
  11565     args: anytype,
  11566 ) Allocator.Error!u32 {
  11567     @branchHint(.cold);
  11568     const string_bytes = &astgen.string_bytes;
  11569     const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
  11570     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
  11571     return astgen.addExtra(Zir.Inst.CompileErrors.Item{
  11572         .msg = msg,
  11573         .node = node,
  11574         .token = 0,
  11575         .byte_offset = 0,
  11576         .notes = 0,
  11577     });
  11578 }
  11579 
  11580 fn identAsString(astgen: *AstGen, ident_token: Ast.TokenIndex) !Zir.NullTerminatedString {
  11581     const gpa = astgen.gpa;
  11582     const string_bytes = &astgen.string_bytes;
  11583     const str_index: u32 = @intCast(string_bytes.items.len);
  11584     try astgen.appendIdentStr(ident_token, string_bytes);
  11585     const key: []const u8 = string_bytes.items[str_index..];
  11586     const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
  11587         .bytes = string_bytes,
  11588     }, StringIndexContext{
  11589         .bytes = string_bytes,
  11590     });
  11591     if (gop.found_existing) {
  11592         string_bytes.shrinkRetainingCapacity(str_index);
  11593         return @enumFromInt(gop.key_ptr.*);
  11594     } else {
  11595         gop.key_ptr.* = str_index;
  11596         try string_bytes.append(gpa, 0);
  11597         return @enumFromInt(str_index);
  11598     }
  11599 }
  11600 
  11601 const IndexSlice = struct { index: Zir.NullTerminatedString, len: u32 };
  11602 
  11603 fn strLitAsString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !IndexSlice {
  11604     const gpa = astgen.gpa;
  11605     const string_bytes = &astgen.string_bytes;
  11606     const str_index: u32 = @intCast(string_bytes.items.len);
  11607     const token_bytes = astgen.tree.tokenSlice(str_lit_token);
  11608     try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0);
  11609     const key: []const u8 = string_bytes.items[str_index..];
  11610     if (std.mem.indexOfScalar(u8, key, 0)) |_| return .{
  11611         .index = @enumFromInt(str_index),
  11612         .len = @intCast(key.len),
  11613     };
  11614     const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
  11615         .bytes = string_bytes,
  11616     }, StringIndexContext{
  11617         .bytes = string_bytes,
  11618     });
  11619     if (gop.found_existing) {
  11620         string_bytes.shrinkRetainingCapacity(str_index);
  11621         return .{
  11622             .index = @enumFromInt(gop.key_ptr.*),
  11623             .len = @intCast(key.len),
  11624         };
  11625     } else {
  11626         gop.key_ptr.* = str_index;
  11627         // Still need a null byte because we are using the same table
  11628         // to lookup null terminated strings, so if we get a match, it has to
  11629         // be null terminated for that to work.
  11630         try string_bytes.append(gpa, 0);
  11631         return .{
  11632             .index = @enumFromInt(str_index),
  11633             .len = @intCast(key.len),
  11634         };
  11635     }
  11636 }
  11637 
  11638 fn strLitNodeAsString(astgen: *AstGen, node: Ast.Node.Index) !IndexSlice {
  11639     const tree = astgen.tree;
  11640     const node_datas = tree.nodes.items(.data);
  11641 
  11642     const start = node_datas[node].lhs;
  11643     const end = node_datas[node].rhs;
  11644 
  11645     const gpa = astgen.gpa;
  11646     const string_bytes = &astgen.string_bytes;
  11647     const str_index = string_bytes.items.len;
  11648 
  11649     // First line: do not append a newline.
  11650     var tok_i = start;
  11651     {
  11652         const slice = tree.tokenSlice(tok_i);
  11653         const line_bytes = slice[2..];
  11654         try string_bytes.appendSlice(gpa, line_bytes);
  11655         tok_i += 1;
  11656     }
  11657     // Following lines: each line prepends a newline.
  11658     while (tok_i <= end) : (tok_i += 1) {
  11659         const slice = tree.tokenSlice(tok_i);
  11660         const line_bytes = slice[2..];
  11661         try string_bytes.ensureUnusedCapacity(gpa, line_bytes.len + 1);
  11662         string_bytes.appendAssumeCapacity('\n');
  11663         string_bytes.appendSliceAssumeCapacity(line_bytes);
  11664     }
  11665     const len = string_bytes.items.len - str_index;
  11666     try string_bytes.append(gpa, 0);
  11667     return IndexSlice{
  11668         .index = @enumFromInt(str_index),
  11669         .len = @intCast(len),
  11670     };
  11671 }
  11672 
  11673 fn testNameString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !Zir.NullTerminatedString {
  11674     const gpa = astgen.gpa;
  11675     const string_bytes = &astgen.string_bytes;
  11676     const str_index: u32 = @intCast(string_bytes.items.len);
  11677     const token_bytes = astgen.tree.tokenSlice(str_lit_token);
  11678     try string_bytes.append(gpa, 0); // Indicates this is a test.
  11679     try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0);
  11680     const slice = string_bytes.items[str_index + 1 ..];
  11681     if (mem.indexOfScalar(u8, slice, 0) != null) {
  11682         return astgen.failTok(str_lit_token, "test name cannot contain null bytes", .{});
  11683     } else if (slice.len == 0) {
  11684         return astgen.failTok(str_lit_token, "empty test name must be omitted", .{});
  11685     }
  11686     try string_bytes.append(gpa, 0);
  11687     return @enumFromInt(str_index);
  11688 }
  11689 
  11690 const Scope = struct {
  11691     tag: Tag,
  11692 
  11693     fn cast(base: *Scope, comptime T: type) ?*T {
  11694         if (T == Defer) {
  11695             switch (base.tag) {
  11696                 .defer_normal, .defer_error => return @alignCast(@fieldParentPtr("base", base)),
  11697                 else => return null,
  11698             }
  11699         }
  11700         if (T == Namespace) {
  11701             switch (base.tag) {
  11702                 .namespace => return @alignCast(@fieldParentPtr("base", base)),
  11703                 else => return null,
  11704             }
  11705         }
  11706         if (base.tag != T.base_tag)
  11707             return null;
  11708 
  11709         return @alignCast(@fieldParentPtr("base", base));
  11710     }
  11711 
  11712     fn parent(base: *Scope) ?*Scope {
  11713         return switch (base.tag) {
  11714             .gen_zir => base.cast(GenZir).?.parent,
  11715             .local_val => base.cast(LocalVal).?.parent,
  11716             .local_ptr => base.cast(LocalPtr).?.parent,
  11717             .defer_normal, .defer_error => base.cast(Defer).?.parent,
  11718             .namespace => base.cast(Namespace).?.parent,
  11719             .top => null,
  11720         };
  11721     }
  11722 
  11723     const Tag = enum {
  11724         gen_zir,
  11725         local_val,
  11726         local_ptr,
  11727         defer_normal,
  11728         defer_error,
  11729         namespace,
  11730         top,
  11731     };
  11732 
  11733     /// The category of identifier. These tag names are user-visible in compile errors.
  11734     const IdCat = enum {
  11735         @"function parameter",
  11736         @"local constant",
  11737         @"local variable",
  11738         @"switch tag capture",
  11739         capture,
  11740     };
  11741 
  11742     /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.
  11743     /// This structure lives as long as the AST generation of the Block
  11744     /// node that contains the variable.
  11745     const LocalVal = struct {
  11746         const base_tag: Tag = .local_val;
  11747         base: Scope = Scope{ .tag = base_tag },
  11748         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
  11749         parent: *Scope,
  11750         gen_zir: *GenZir,
  11751         inst: Zir.Inst.Ref,
  11752         /// Source location of the corresponding variable declaration.
  11753         token_src: Ast.TokenIndex,
  11754         /// Track the first identifier where it is referenced.
  11755         /// 0 means never referenced.
  11756         used: Ast.TokenIndex = 0,
  11757         /// Track the identifier where it is discarded, like this `_ = foo;`.
  11758         /// 0 means never discarded.
  11759         discarded: Ast.TokenIndex = 0,
  11760         /// String table index.
  11761         name: Zir.NullTerminatedString,
  11762         id_cat: IdCat,
  11763     };
  11764 
  11765     /// This could be a `const` or `var` local. It has a pointer instead of a value.
  11766     /// This structure lives as long as the AST generation of the Block
  11767     /// node that contains the variable.
  11768     const LocalPtr = struct {
  11769         const base_tag: Tag = .local_ptr;
  11770         base: Scope = Scope{ .tag = base_tag },
  11771         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
  11772         parent: *Scope,
  11773         gen_zir: *GenZir,
  11774         ptr: Zir.Inst.Ref,
  11775         /// Source location of the corresponding variable declaration.
  11776         token_src: Ast.TokenIndex,
  11777         /// Track the first identifier where it is referenced.
  11778         /// 0 means never referenced.
  11779         used: Ast.TokenIndex = 0,
  11780         /// Track the identifier where it is discarded, like this `_ = foo;`.
  11781         /// 0 means never discarded.
  11782         discarded: Ast.TokenIndex = 0,
  11783         /// Whether this value is used as an lvalue after initialization.
  11784         /// If not, we know it can be `const`, so will emit a compile error if it is `var`.
  11785         used_as_lvalue: bool = false,
  11786         /// String table index.
  11787         name: Zir.NullTerminatedString,
  11788         id_cat: IdCat,
  11789         /// true means we find out during Sema whether the value is comptime.
  11790         /// false means it is already known at AstGen the value is runtime-known.
  11791         maybe_comptime: bool,
  11792     };
  11793 
  11794     const Defer = struct {
  11795         base: Scope,
  11796         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
  11797         parent: *Scope,
  11798         index: u32,
  11799         len: u32,
  11800         remapped_err_code: Zir.Inst.OptionalIndex = .none,
  11801     };
  11802 
  11803     /// Represents a global scope that has any number of declarations in it.
  11804     /// Each declaration has this as the parent scope.
  11805     const Namespace = struct {
  11806         const base_tag: Tag = .namespace;
  11807         base: Scope = Scope{ .tag = base_tag },
  11808 
  11809         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
  11810         parent: *Scope,
  11811         /// Maps string table index to the source location of declaration,
  11812         /// for the purposes of reporting name shadowing compile errors.
  11813         decls: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.Node.Index) = .empty,
  11814         node: Ast.Node.Index,
  11815         inst: Zir.Inst.Index,
  11816         maybe_generic: bool,
  11817 
  11818         /// The astgen scope containing this namespace.
  11819         /// Only valid during astgen.
  11820         declaring_gz: ?*GenZir,
  11821 
  11822         /// Set of captures used by this namespace.
  11823         captures: std.AutoArrayHashMapUnmanaged(Zir.Inst.Capture, void) = .empty,
  11824 
  11825         fn deinit(self: *Namespace, gpa: Allocator) void {
  11826             self.decls.deinit(gpa);
  11827             self.captures.deinit(gpa);
  11828             self.* = undefined;
  11829         }
  11830     };
  11831 
  11832     const Top = struct {
  11833         const base_tag: Scope.Tag = .top;
  11834         base: Scope = Scope{ .tag = base_tag },
  11835     };
  11836 };
  11837 
  11838 /// This is a temporary structure; references to it are valid only
  11839 /// while constructing a `Zir`.
  11840 const GenZir = struct {
  11841     const base_tag: Scope.Tag = .gen_zir;
  11842     base: Scope = Scope{ .tag = base_tag },
  11843     /// Whether we're already in a scope known to be comptime. This is set
  11844     /// whenever we know Sema will analyze the current block with `is_comptime`,
  11845     /// for instance when we're within a `struct_decl` or a `block_comptime`.
  11846     is_comptime: bool,
  11847     /// Whether we're in an expression within a `@TypeOf` operand. In this case, closure of runtime
  11848     /// variables is permitted where it is usually not.
  11849     is_typeof: bool = false,
  11850     /// This is set to true for a `GenZir` of a `block_inline`, indicating that
  11851     /// exits from this block should use `break_inline` rather than `break`.
  11852     is_inline: bool = false,
  11853     c_import: bool = false,
  11854     /// How decls created in this scope should be named.
  11855     anon_name_strategy: Zir.Inst.NameStrategy = .anon,
  11856     /// The containing decl AST node.
  11857     decl_node_index: Ast.Node.Index,
  11858     /// The containing decl line index, absolute.
  11859     decl_line: u32,
  11860     /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
  11861     parent: *Scope,
  11862     /// All `GenZir` scopes for the same ZIR share this.
  11863     astgen: *AstGen,
  11864     /// Keeps track of the list of instructions in this scope. Possibly shared.
  11865     /// Indexes to instructions in `astgen`.
  11866     instructions: *ArrayListUnmanaged(Zir.Inst.Index),
  11867     /// A sub-block may share its instructions ArrayList with containing GenZir,
  11868     /// if use is strictly nested. This saves prior size of list for unstacking.
  11869     instructions_top: usize,
  11870     label: ?Label = null,
  11871     break_block: Zir.Inst.OptionalIndex = .none,
  11872     continue_block: Zir.Inst.OptionalIndex = .none,
  11873     /// Only valid when setBreakResultInfo is called.
  11874     break_result_info: AstGen.ResultInfo = undefined,
  11875     continue_result_info: AstGen.ResultInfo = undefined,
  11876 
  11877     suspend_node: Ast.Node.Index = 0,
  11878     nosuspend_node: Ast.Node.Index = 0,
  11879     /// Set if this GenZir is a defer.
  11880     cur_defer_node: Ast.Node.Index = 0,
  11881     // Set if this GenZir is a defer or it is inside a defer.
  11882     any_defer_node: Ast.Node.Index = 0,
  11883 
  11884     const unstacked_top = std.math.maxInt(usize);
  11885     /// Call unstack before adding any new instructions to containing GenZir.
  11886     fn unstack(self: *GenZir) void {
  11887         if (self.instructions_top != unstacked_top) {
  11888             self.instructions.items.len = self.instructions_top;
  11889             self.instructions_top = unstacked_top;
  11890         }
  11891     }
  11892 
  11893     fn isEmpty(self: *const GenZir) bool {
  11894         return (self.instructions_top == unstacked_top) or
  11895             (self.instructions.items.len == self.instructions_top);
  11896     }
  11897 
  11898     fn instructionsSlice(self: *const GenZir) []Zir.Inst.Index {
  11899         return if (self.instructions_top == unstacked_top)
  11900             &[0]Zir.Inst.Index{}
  11901         else
  11902             self.instructions.items[self.instructions_top..];
  11903     }
  11904 
  11905     fn instructionsSliceUpto(self: *const GenZir, stacked_gz: *GenZir) []Zir.Inst.Index {
  11906         return if (self.instructions_top == unstacked_top)
  11907             &[0]Zir.Inst.Index{}
  11908         else if (self.instructions == stacked_gz.instructions and stacked_gz.instructions_top != unstacked_top)
  11909             self.instructions.items[self.instructions_top..stacked_gz.instructions_top]
  11910         else
  11911             self.instructions.items[self.instructions_top..];
  11912     }
  11913 
  11914     fn instructionsSliceUptoOpt(gz: *const GenZir, maybe_stacked_gz: ?*GenZir) []Zir.Inst.Index {
  11915         if (maybe_stacked_gz) |stacked_gz| {
  11916             return gz.instructionsSliceUpto(stacked_gz);
  11917         } else {
  11918             return gz.instructionsSlice();
  11919         }
  11920     }
  11921 
  11922     fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {
  11923         return .{
  11924             .is_comptime = gz.is_comptime,
  11925             .is_typeof = gz.is_typeof,
  11926             .c_import = gz.c_import,
  11927             .decl_node_index = gz.decl_node_index,
  11928             .decl_line = gz.decl_line,
  11929             .parent = scope,
  11930             .astgen = gz.astgen,
  11931             .suspend_node = gz.suspend_node,
  11932             .nosuspend_node = gz.nosuspend_node,
  11933             .any_defer_node = gz.any_defer_node,
  11934             .instructions = gz.instructions,
  11935             .instructions_top = gz.instructions.items.len,
  11936         };
  11937     }
  11938 
  11939     const Label = struct {
  11940         token: Ast.TokenIndex,
  11941         block_inst: Zir.Inst.Index,
  11942         used: bool = false,
  11943         used_for_continue: bool = false,
  11944     };
  11945 
  11946     /// Assumes nothing stacked on `gz`.
  11947     fn endsWithNoReturn(gz: GenZir) bool {
  11948         if (gz.isEmpty()) return false;
  11949         const tags = gz.astgen.instructions.items(.tag);
  11950         const last_inst = gz.instructions.items[gz.instructions.items.len - 1];
  11951         return tags[@intFromEnum(last_inst)].isNoReturn();
  11952     }
  11953 
  11954     /// TODO all uses of this should be replaced with uses of `endsWithNoReturn`.
  11955     fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool {
  11956         if (inst_ref == .unreachable_value) return true;
  11957         if (inst_ref.toIndex()) |inst_index| {
  11958             return gz.astgen.instructions.items(.tag)[@intFromEnum(inst_index)].isNoReturn();
  11959         }
  11960         return false;
  11961     }
  11962 
  11963     fn nodeIndexToRelative(gz: GenZir, node_index: Ast.Node.Index) i32 {
  11964         return @as(i32, @bitCast(node_index)) - @as(i32, @bitCast(gz.decl_node_index));
  11965     }
  11966 
  11967     fn tokenIndexToRelative(gz: GenZir, token: Ast.TokenIndex) u32 {
  11968         return token - gz.srcToken();
  11969     }
  11970 
  11971     fn srcToken(gz: GenZir) Ast.TokenIndex {
  11972         return gz.astgen.tree.firstToken(gz.decl_node_index);
  11973     }
  11974 
  11975     fn setBreakResultInfo(gz: *GenZir, parent_ri: AstGen.ResultInfo) void {
  11976         // Depending on whether the result location is a pointer or value, different
  11977         // ZIR needs to be generated. In the former case we rely on storing to the
  11978         // pointer to communicate the result, and use breakvoid; in the latter case
  11979         // the block break instructions will have the result values.
  11980         switch (parent_ri.rl) {
  11981             .coerced_ty => |ty_inst| {
  11982                 // Type coercion needs to happen before breaks.
  11983                 gz.break_result_info = .{ .rl = .{ .ty = ty_inst }, .ctx = parent_ri.ctx };
  11984             },
  11985             .discard => {
  11986                 // We don't forward the result context here. This prevents
  11987                 // "unnecessary discard" errors from being caused by expressions
  11988                 // far from the actual discard, such as a `break` from a
  11989                 // discarded block.
  11990                 gz.break_result_info = .{ .rl = .discard };
  11991             },
  11992             else => {
  11993                 gz.break_result_info = parent_ri;
  11994             },
  11995         }
  11996     }
  11997 
  11998     /// Assumes nothing stacked on `gz`. Unstacks `gz`.
  11999     fn setBoolBrBody(gz: *GenZir, bool_br: Zir.Inst.Index, bool_br_lhs: Zir.Inst.Ref) !void {
  12000         const astgen = gz.astgen;
  12001         const gpa = astgen.gpa;
  12002         const body = gz.instructionsSlice();
  12003         const body_len = astgen.countBodyLenAfterFixups(body);
  12004         try astgen.extra.ensureUnusedCapacity(
  12005             gpa,
  12006             @typeInfo(Zir.Inst.BoolBr).@"struct".fields.len + body_len,
  12007         );
  12008         const zir_datas = astgen.instructions.items(.data);
  12009         zir_datas[@intFromEnum(bool_br)].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.BoolBr{
  12010             .lhs = bool_br_lhs,
  12011             .body_len = body_len,
  12012         });
  12013         astgen.appendBodyWithFixups(body);
  12014         gz.unstack();
  12015     }
  12016 
  12017     /// Assumes nothing stacked on `gz`. Unstacks `gz`.
  12018     fn setBlockBody(gz: *GenZir, inst: Zir.Inst.Index) !void {
  12019         const astgen = gz.astgen;
  12020         const gpa = astgen.gpa;
  12021         const body = gz.instructionsSlice();
  12022         const body_len = astgen.countBodyLenAfterFixups(body);
  12023         try astgen.extra.ensureUnusedCapacity(
  12024             gpa,
  12025             @typeInfo(Zir.Inst.Block).@"struct".fields.len + body_len,
  12026         );
  12027         const zir_datas = astgen.instructions.items(.data);
  12028         zir_datas[@intFromEnum(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity(
  12029             Zir.Inst.Block{ .body_len = body_len },
  12030         );
  12031         astgen.appendBodyWithFixups(body);
  12032         gz.unstack();
  12033     }
  12034 
  12035     /// Assumes nothing stacked on `gz`. Unstacks `gz`.
  12036     fn setTryBody(gz: *GenZir, inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !void {
  12037         const astgen = gz.astgen;
  12038         const gpa = astgen.gpa;
  12039         const body = gz.instructionsSlice();
  12040         const body_len = astgen.countBodyLenAfterFixups(body);
  12041         try astgen.extra.ensureUnusedCapacity(
  12042             gpa,
  12043             @typeInfo(Zir.Inst.Try).@"struct".fields.len + body_len,
  12044         );
  12045         const zir_datas = astgen.instructions.items(.data);
  12046         zir_datas[@intFromEnum(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity(
  12047             Zir.Inst.Try{
  12048                 .operand = operand,
  12049                 .body_len = body_len,
  12050             },
  12051         );
  12052         astgen.appendBodyWithFixups(body);
  12053         gz.unstack();
  12054     }
  12055 
  12056     /// Must be called with the following stack set up:
  12057     ///  * gz (bottom)
  12058     ///  * ret_gz
  12059     ///  * cc_gz
  12060     ///  * body_gz (top)
  12061     /// Unstacks all of those except for `gz`.
  12062     fn addFunc(
  12063         gz: *GenZir,
  12064         args: struct {
  12065             src_node: Ast.Node.Index,
  12066             lbrace_line: u32 = 0,
  12067             lbrace_column: u32 = 0,
  12068             param_block: Zir.Inst.Index,
  12069 
  12070             ret_gz: ?*GenZir,
  12071             body_gz: ?*GenZir,
  12072             cc_gz: ?*GenZir,
  12073 
  12074             ret_param_refs: []Zir.Inst.Index,
  12075             param_insts: []Zir.Inst.Index, // refs to params in `body_gz` should still be in `astgen.ref_table`
  12076 
  12077             cc_ref: Zir.Inst.Ref,
  12078             ret_ref: Zir.Inst.Ref,
  12079 
  12080             lib_name: Zir.NullTerminatedString,
  12081             noalias_bits: u32,
  12082             is_var_args: bool,
  12083             is_inferred_error: bool,
  12084             is_test: bool,
  12085             is_extern: bool,
  12086             is_noinline: bool,
  12087 
  12088             /// Ignored if `body_gz == null`.
  12089             proto_hash: std.zig.SrcHash,
  12090         },
  12091     ) !Zir.Inst.Ref {
  12092         assert(args.src_node != 0);
  12093         const astgen = gz.astgen;
  12094         const gpa = astgen.gpa;
  12095         const ret_ref = if (args.ret_ref == .void_type) .none else args.ret_ref;
  12096         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12097 
  12098         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12099         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12100 
  12101         const body, const cc_body, const ret_body = bodies: {
  12102             var stacked_gz: ?*GenZir = null;
  12103             const body: []const Zir.Inst.Index = if (args.body_gz) |body_gz| body: {
  12104                 const body = body_gz.instructionsSliceUptoOpt(stacked_gz);
  12105                 stacked_gz = body_gz;
  12106                 break :body body;
  12107             } else &.{};
  12108             const cc_body: []const Zir.Inst.Index = if (args.cc_gz) |cc_gz| body: {
  12109                 const cc_body = cc_gz.instructionsSliceUptoOpt(stacked_gz);
  12110                 stacked_gz = cc_gz;
  12111                 break :body cc_body;
  12112             } else &.{};
  12113             const ret_body: []const Zir.Inst.Index = if (args.ret_gz) |ret_gz| body: {
  12114                 const ret_body = ret_gz.instructionsSliceUptoOpt(stacked_gz);
  12115                 stacked_gz = ret_gz;
  12116                 break :body ret_body;
  12117             } else &.{};
  12118             break :bodies .{ body, cc_body, ret_body };
  12119         };
  12120 
  12121         var src_locs_and_hash_buffer: [7]u32 = undefined;
  12122         const src_locs_and_hash: []const u32 = if (args.body_gz != null) src_locs_and_hash: {
  12123             const tree = astgen.tree;
  12124             const node_tags = tree.nodes.items(.tag);
  12125             const node_datas = tree.nodes.items(.data);
  12126             const token_starts = tree.tokens.items(.start);
  12127             const fn_decl = args.src_node;
  12128             assert(node_tags[fn_decl] == .fn_decl or node_tags[fn_decl] == .test_decl);
  12129             const block = node_datas[fn_decl].rhs;
  12130             const rbrace_start = token_starts[tree.lastToken(block)];
  12131             astgen.advanceSourceCursor(rbrace_start);
  12132             const rbrace_line: u32 = @intCast(astgen.source_line - gz.decl_line);
  12133             const rbrace_column: u32 = @intCast(astgen.source_column);
  12134 
  12135             const columns = args.lbrace_column | (rbrace_column << 16);
  12136 
  12137             const proto_hash_arr: [4]u32 = @bitCast(args.proto_hash);
  12138 
  12139             src_locs_and_hash_buffer = .{
  12140                 args.lbrace_line,
  12141                 rbrace_line,
  12142                 columns,
  12143                 proto_hash_arr[0],
  12144                 proto_hash_arr[1],
  12145                 proto_hash_arr[2],
  12146                 proto_hash_arr[3],
  12147             };
  12148             break :src_locs_and_hash &src_locs_and_hash_buffer;
  12149         } else &.{};
  12150 
  12151         const body_len = astgen.countBodyLenAfterFixupsExtraRefs(body, args.param_insts);
  12152 
  12153         const tag: Zir.Inst.Tag, const payload_index: u32 = if (args.cc_ref != .none or args.lib_name != .empty or
  12154             args.is_var_args or args.is_test or args.is_extern or
  12155             args.noalias_bits != 0 or args.is_noinline)
  12156         inst_info: {
  12157             try astgen.extra.ensureUnusedCapacity(
  12158                 gpa,
  12159                 @typeInfo(Zir.Inst.FuncFancy).@"struct".fields.len +
  12160                     fancyFnExprExtraLen(astgen, &.{}, cc_body, args.cc_ref) +
  12161                     fancyFnExprExtraLen(astgen, args.ret_param_refs, ret_body, ret_ref) +
  12162                     body_len + src_locs_and_hash.len +
  12163                     @intFromBool(args.lib_name != .empty) +
  12164                     @intFromBool(args.noalias_bits != 0),
  12165             );
  12166             const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.FuncFancy{
  12167                 .param_block = args.param_block,
  12168                 .body_len = body_len,
  12169                 .bits = .{
  12170                     .is_var_args = args.is_var_args,
  12171                     .is_inferred_error = args.is_inferred_error,
  12172                     .is_test = args.is_test,
  12173                     .is_extern = args.is_extern,
  12174                     .is_noinline = args.is_noinline,
  12175                     .has_lib_name = args.lib_name != .empty,
  12176                     .has_any_noalias = args.noalias_bits != 0,
  12177 
  12178                     .has_cc_ref = args.cc_ref != .none,
  12179                     .has_ret_ty_ref = ret_ref != .none,
  12180 
  12181                     .has_cc_body = cc_body.len != 0,
  12182                     .has_ret_ty_body = ret_body.len != 0,
  12183                 },
  12184             });
  12185             if (args.lib_name != .empty) {
  12186                 astgen.extra.appendAssumeCapacity(@intFromEnum(args.lib_name));
  12187             }
  12188 
  12189             const zir_datas = astgen.instructions.items(.data);
  12190             if (cc_body.len != 0) {
  12191                 astgen.extra.appendAssumeCapacity(astgen.countBodyLenAfterFixups(cc_body));
  12192                 astgen.appendBodyWithFixups(cc_body);
  12193                 const break_extra = zir_datas[@intFromEnum(cc_body[cc_body.len - 1])].@"break".payload_index;
  12194                 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
  12195                     @intFromEnum(new_index);
  12196             } else if (args.cc_ref != .none) {
  12197                 astgen.extra.appendAssumeCapacity(@intFromEnum(args.cc_ref));
  12198             }
  12199             if (ret_body.len != 0) {
  12200                 astgen.extra.appendAssumeCapacity(
  12201                     astgen.countBodyLenAfterFixups(args.ret_param_refs) +
  12202                         astgen.countBodyLenAfterFixups(ret_body),
  12203                 );
  12204                 astgen.appendBodyWithFixups(args.ret_param_refs);
  12205                 astgen.appendBodyWithFixups(ret_body);
  12206                 const break_extra = zir_datas[@intFromEnum(ret_body[ret_body.len - 1])].@"break".payload_index;
  12207                 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
  12208                     @intFromEnum(new_index);
  12209             } else if (ret_ref != .none) {
  12210                 astgen.extra.appendAssumeCapacity(@intFromEnum(ret_ref));
  12211             }
  12212 
  12213             if (args.noalias_bits != 0) {
  12214                 astgen.extra.appendAssumeCapacity(args.noalias_bits);
  12215             }
  12216 
  12217             astgen.appendBodyWithFixupsExtraRefsArrayList(&astgen.extra, body, args.param_insts);
  12218             astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash);
  12219 
  12220             break :inst_info .{ .func_fancy, payload_index };
  12221         } else inst_info: {
  12222             try astgen.extra.ensureUnusedCapacity(
  12223                 gpa,
  12224                 @typeInfo(Zir.Inst.Func).@"struct".fields.len + 1 +
  12225                     fancyFnExprExtraLen(astgen, args.ret_param_refs, ret_body, ret_ref) +
  12226                     body_len + src_locs_and_hash.len,
  12227             );
  12228 
  12229             const ret_body_len = if (ret_body.len != 0)
  12230                 countBodyLenAfterFixups(astgen, args.ret_param_refs) + countBodyLenAfterFixups(astgen, ret_body)
  12231             else
  12232                 @intFromBool(ret_ref != .none);
  12233 
  12234             const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.Func{
  12235                 .param_block = args.param_block,
  12236                 .ret_body_len = ret_body_len,
  12237                 .body_len = body_len,
  12238             });
  12239             const zir_datas = astgen.instructions.items(.data);
  12240             if (ret_body.len != 0) {
  12241                 astgen.appendBodyWithFixups(args.ret_param_refs);
  12242                 astgen.appendBodyWithFixups(ret_body);
  12243 
  12244                 const break_extra = zir_datas[@intFromEnum(ret_body[ret_body.len - 1])].@"break".payload_index;
  12245                 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
  12246                     @intFromEnum(new_index);
  12247             } else if (ret_ref != .none) {
  12248                 astgen.extra.appendAssumeCapacity(@intFromEnum(ret_ref));
  12249             }
  12250             astgen.appendBodyWithFixupsExtraRefsArrayList(&astgen.extra, body, args.param_insts);
  12251             astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash);
  12252 
  12253             break :inst_info .{
  12254                 if (args.is_inferred_error) .func_inferred else .func,
  12255                 payload_index,
  12256             };
  12257         };
  12258 
  12259         // Order is important when unstacking.
  12260         if (args.body_gz) |body_gz| body_gz.unstack();
  12261         if (args.cc_gz) |cc_gz| cc_gz.unstack();
  12262         if (args.ret_gz) |ret_gz| ret_gz.unstack();
  12263 
  12264         astgen.instructions.appendAssumeCapacity(.{
  12265             .tag = tag,
  12266             .data = .{ .pl_node = .{
  12267                 .src_node = gz.nodeIndexToRelative(args.src_node),
  12268                 .payload_index = payload_index,
  12269             } },
  12270         });
  12271         gz.instructions.appendAssumeCapacity(new_index);
  12272         return new_index.toRef();
  12273     }
  12274 
  12275     fn fancyFnExprExtraLen(astgen: *AstGen, param_refs_body: []const Zir.Inst.Index, main_body: []const Zir.Inst.Index, ref: Zir.Inst.Ref) u32 {
  12276         return countBodyLenAfterFixups(astgen, param_refs_body) +
  12277             countBodyLenAfterFixups(astgen, main_body) +
  12278             // If there is a body, we need an element for its length; otherwise, if there is a ref, we need to include that.
  12279             @intFromBool(main_body.len > 0 or ref != .none);
  12280     }
  12281 
  12282     fn addVar(gz: *GenZir, args: struct {
  12283         align_inst: Zir.Inst.Ref,
  12284         lib_name: Zir.NullTerminatedString,
  12285         var_type: Zir.Inst.Ref,
  12286         init: Zir.Inst.Ref,
  12287         is_extern: bool,
  12288         is_const: bool,
  12289         is_threadlocal: bool,
  12290     }) !Zir.Inst.Ref {
  12291         const astgen = gz.astgen;
  12292         const gpa = astgen.gpa;
  12293 
  12294         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12295         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12296 
  12297         try astgen.extra.ensureUnusedCapacity(
  12298             gpa,
  12299             @typeInfo(Zir.Inst.ExtendedVar).@"struct".fields.len +
  12300                 @intFromBool(args.lib_name != .empty) +
  12301                 @intFromBool(args.align_inst != .none) +
  12302                 @intFromBool(args.init != .none),
  12303         );
  12304         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.ExtendedVar{
  12305             .var_type = args.var_type,
  12306         });
  12307         if (args.lib_name != .empty) {
  12308             astgen.extra.appendAssumeCapacity(@intFromEnum(args.lib_name));
  12309         }
  12310         if (args.align_inst != .none) {
  12311             astgen.extra.appendAssumeCapacity(@intFromEnum(args.align_inst));
  12312         }
  12313         if (args.init != .none) {
  12314             astgen.extra.appendAssumeCapacity(@intFromEnum(args.init));
  12315         }
  12316 
  12317         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12318         astgen.instructions.appendAssumeCapacity(.{
  12319             .tag = .extended,
  12320             .data = .{ .extended = .{
  12321                 .opcode = .variable,
  12322                 .small = @bitCast(Zir.Inst.ExtendedVar.Small{
  12323                     .has_lib_name = args.lib_name != .empty,
  12324                     .has_align = args.align_inst != .none,
  12325                     .has_init = args.init != .none,
  12326                     .is_extern = args.is_extern,
  12327                     .is_const = args.is_const,
  12328                     .is_threadlocal = args.is_threadlocal,
  12329                 }),
  12330                 .operand = payload_index,
  12331             } },
  12332         });
  12333         gz.instructions.appendAssumeCapacity(new_index);
  12334         return new_index.toRef();
  12335     }
  12336 
  12337     fn addInt(gz: *GenZir, integer: u64) !Zir.Inst.Ref {
  12338         return gz.add(.{
  12339             .tag = .int,
  12340             .data = .{ .int = integer },
  12341         });
  12342     }
  12343 
  12344     fn addIntBig(gz: *GenZir, limbs: []const std.math.big.Limb) !Zir.Inst.Ref {
  12345         const astgen = gz.astgen;
  12346         const gpa = astgen.gpa;
  12347         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12348         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12349         try astgen.string_bytes.ensureUnusedCapacity(gpa, @sizeOf(std.math.big.Limb) * limbs.len);
  12350 
  12351         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12352         astgen.instructions.appendAssumeCapacity(.{
  12353             .tag = .int_big,
  12354             .data = .{ .str = .{
  12355                 .start = @enumFromInt(astgen.string_bytes.items.len),
  12356                 .len = @intCast(limbs.len),
  12357             } },
  12358         });
  12359         gz.instructions.appendAssumeCapacity(new_index);
  12360         astgen.string_bytes.appendSliceAssumeCapacity(mem.sliceAsBytes(limbs));
  12361         return new_index.toRef();
  12362     }
  12363 
  12364     fn addFloat(gz: *GenZir, number: f64) !Zir.Inst.Ref {
  12365         return gz.add(.{
  12366             .tag = .float,
  12367             .data = .{ .float = number },
  12368         });
  12369     }
  12370 
  12371     fn addUnNode(
  12372         gz: *GenZir,
  12373         tag: Zir.Inst.Tag,
  12374         operand: Zir.Inst.Ref,
  12375         /// Absolute node index. This function does the conversion to offset from Decl.
  12376         src_node: Ast.Node.Index,
  12377     ) !Zir.Inst.Ref {
  12378         assert(operand != .none);
  12379         return gz.add(.{
  12380             .tag = tag,
  12381             .data = .{ .un_node = .{
  12382                 .operand = operand,
  12383                 .src_node = gz.nodeIndexToRelative(src_node),
  12384             } },
  12385         });
  12386     }
  12387 
  12388     fn makeUnNode(
  12389         gz: *GenZir,
  12390         tag: Zir.Inst.Tag,
  12391         operand: Zir.Inst.Ref,
  12392         /// Absolute node index. This function does the conversion to offset from Decl.
  12393         src_node: Ast.Node.Index,
  12394     ) !Zir.Inst.Index {
  12395         assert(operand != .none);
  12396         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  12397         try gz.astgen.instructions.append(gz.astgen.gpa, .{
  12398             .tag = tag,
  12399             .data = .{ .un_node = .{
  12400                 .operand = operand,
  12401                 .src_node = gz.nodeIndexToRelative(src_node),
  12402             } },
  12403         });
  12404         return new_index;
  12405     }
  12406 
  12407     fn addPlNode(
  12408         gz: *GenZir,
  12409         tag: Zir.Inst.Tag,
  12410         /// Absolute node index. This function does the conversion to offset from Decl.
  12411         src_node: Ast.Node.Index,
  12412         extra: anytype,
  12413     ) !Zir.Inst.Ref {
  12414         const gpa = gz.astgen.gpa;
  12415         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12416         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12417 
  12418         const payload_index = try gz.astgen.addExtra(extra);
  12419         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  12420         gz.astgen.instructions.appendAssumeCapacity(.{
  12421             .tag = tag,
  12422             .data = .{ .pl_node = .{
  12423                 .src_node = gz.nodeIndexToRelative(src_node),
  12424                 .payload_index = payload_index,
  12425             } },
  12426         });
  12427         gz.instructions.appendAssumeCapacity(new_index);
  12428         return new_index.toRef();
  12429     }
  12430 
  12431     fn addPlNodePayloadIndex(
  12432         gz: *GenZir,
  12433         tag: Zir.Inst.Tag,
  12434         /// Absolute node index. This function does the conversion to offset from Decl.
  12435         src_node: Ast.Node.Index,
  12436         payload_index: u32,
  12437     ) !Zir.Inst.Ref {
  12438         return try gz.add(.{
  12439             .tag = tag,
  12440             .data = .{ .pl_node = .{
  12441                 .src_node = gz.nodeIndexToRelative(src_node),
  12442                 .payload_index = payload_index,
  12443             } },
  12444         });
  12445     }
  12446 
  12447     /// Supports `param_gz` stacked on `gz`. Assumes nothing stacked on `param_gz`. Unstacks `param_gz`.
  12448     fn addParam(
  12449         gz: *GenZir,
  12450         param_gz: *GenZir,
  12451         /// Previous parameters, which might be referenced in `param_gz` (the new parameter type).
  12452         /// `ref`s of these instructions will be put into this param's type body, and removed from `AstGen.ref_table`.
  12453         prev_param_insts: []const Zir.Inst.Index,
  12454         tag: Zir.Inst.Tag,
  12455         /// Absolute token index. This function does the conversion to Decl offset.
  12456         abs_tok_index: Ast.TokenIndex,
  12457         name: Zir.NullTerminatedString,
  12458     ) !Zir.Inst.Index {
  12459         const gpa = gz.astgen.gpa;
  12460         const param_body = param_gz.instructionsSlice();
  12461         const body_len = gz.astgen.countBodyLenAfterFixupsExtraRefs(param_body, prev_param_insts);
  12462         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12463         try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Param).@"struct".fields.len + body_len);
  12464 
  12465         const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Param{
  12466             .name = name,
  12467             .body_len = @intCast(body_len),
  12468         });
  12469         gz.astgen.appendBodyWithFixupsExtraRefsArrayList(&gz.astgen.extra, param_body, prev_param_insts);
  12470         param_gz.unstack();
  12471 
  12472         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  12473         gz.astgen.instructions.appendAssumeCapacity(.{
  12474             .tag = tag,
  12475             .data = .{ .pl_tok = .{
  12476                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  12477                 .payload_index = payload_index,
  12478             } },
  12479         });
  12480         gz.instructions.appendAssumeCapacity(new_index);
  12481         return new_index;
  12482     }
  12483 
  12484     fn addBuiltinValue(gz: *GenZir, src_node: Ast.Node.Index, val: Zir.Inst.BuiltinValue) !Zir.Inst.Ref {
  12485         return addExtendedNodeSmall(gz, .builtin_value, src_node, @intFromEnum(val));
  12486     }
  12487 
  12488     fn addExtendedPayload(gz: *GenZir, opcode: Zir.Inst.Extended, extra: anytype) !Zir.Inst.Ref {
  12489         return addExtendedPayloadSmall(gz, opcode, undefined, extra);
  12490     }
  12491 
  12492     fn addExtendedPayloadSmall(
  12493         gz: *GenZir,
  12494         opcode: Zir.Inst.Extended,
  12495         small: u16,
  12496         extra: anytype,
  12497     ) !Zir.Inst.Ref {
  12498         const gpa = gz.astgen.gpa;
  12499 
  12500         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12501         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12502 
  12503         const payload_index = try gz.astgen.addExtra(extra);
  12504         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  12505         gz.astgen.instructions.appendAssumeCapacity(.{
  12506             .tag = .extended,
  12507             .data = .{ .extended = .{
  12508                 .opcode = opcode,
  12509                 .small = small,
  12510                 .operand = payload_index,
  12511             } },
  12512         });
  12513         gz.instructions.appendAssumeCapacity(new_index);
  12514         return new_index.toRef();
  12515     }
  12516 
  12517     fn addExtendedMultiOp(
  12518         gz: *GenZir,
  12519         opcode: Zir.Inst.Extended,
  12520         node: Ast.Node.Index,
  12521         operands: []const Zir.Inst.Ref,
  12522     ) !Zir.Inst.Ref {
  12523         const astgen = gz.astgen;
  12524         const gpa = astgen.gpa;
  12525 
  12526         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12527         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12528         try astgen.extra.ensureUnusedCapacity(
  12529             gpa,
  12530             @typeInfo(Zir.Inst.NodeMultiOp).@"struct".fields.len + operands.len,
  12531         );
  12532 
  12533         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.NodeMultiOp{
  12534             .src_node = gz.nodeIndexToRelative(node),
  12535         });
  12536         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12537         astgen.instructions.appendAssumeCapacity(.{
  12538             .tag = .extended,
  12539             .data = .{ .extended = .{
  12540                 .opcode = opcode,
  12541                 .small = @intCast(operands.len),
  12542                 .operand = payload_index,
  12543             } },
  12544         });
  12545         gz.instructions.appendAssumeCapacity(new_index);
  12546         astgen.appendRefsAssumeCapacity(operands);
  12547         return new_index.toRef();
  12548     }
  12549 
  12550     fn addExtendedMultiOpPayloadIndex(
  12551         gz: *GenZir,
  12552         opcode: Zir.Inst.Extended,
  12553         payload_index: u32,
  12554         trailing_len: usize,
  12555     ) !Zir.Inst.Ref {
  12556         const astgen = gz.astgen;
  12557         const gpa = astgen.gpa;
  12558 
  12559         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12560         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12561         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12562         astgen.instructions.appendAssumeCapacity(.{
  12563             .tag = .extended,
  12564             .data = .{ .extended = .{
  12565                 .opcode = opcode,
  12566                 .small = @intCast(trailing_len),
  12567                 .operand = payload_index,
  12568             } },
  12569         });
  12570         gz.instructions.appendAssumeCapacity(new_index);
  12571         return new_index.toRef();
  12572     }
  12573 
  12574     fn addExtendedNodeSmall(
  12575         gz: *GenZir,
  12576         opcode: Zir.Inst.Extended,
  12577         src_node: Ast.Node.Index,
  12578         small: u16,
  12579     ) !Zir.Inst.Ref {
  12580         const astgen = gz.astgen;
  12581         const gpa = astgen.gpa;
  12582 
  12583         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12584         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12585         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12586         astgen.instructions.appendAssumeCapacity(.{
  12587             .tag = .extended,
  12588             .data = .{ .extended = .{
  12589                 .opcode = opcode,
  12590                 .small = small,
  12591                 .operand = @bitCast(gz.nodeIndexToRelative(src_node)),
  12592             } },
  12593         });
  12594         gz.instructions.appendAssumeCapacity(new_index);
  12595         return new_index.toRef();
  12596     }
  12597 
  12598     fn addUnTok(
  12599         gz: *GenZir,
  12600         tag: Zir.Inst.Tag,
  12601         operand: Zir.Inst.Ref,
  12602         /// Absolute token index. This function does the conversion to Decl offset.
  12603         abs_tok_index: Ast.TokenIndex,
  12604     ) !Zir.Inst.Ref {
  12605         assert(operand != .none);
  12606         return gz.add(.{
  12607             .tag = tag,
  12608             .data = .{ .un_tok = .{
  12609                 .operand = operand,
  12610                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  12611             } },
  12612         });
  12613     }
  12614 
  12615     fn makeUnTok(
  12616         gz: *GenZir,
  12617         tag: Zir.Inst.Tag,
  12618         operand: Zir.Inst.Ref,
  12619         /// Absolute token index. This function does the conversion to Decl offset.
  12620         abs_tok_index: Ast.TokenIndex,
  12621     ) !Zir.Inst.Index {
  12622         const astgen = gz.astgen;
  12623         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12624         assert(operand != .none);
  12625         try astgen.instructions.append(astgen.gpa, .{
  12626             .tag = tag,
  12627             .data = .{ .un_tok = .{
  12628                 .operand = operand,
  12629                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  12630             } },
  12631         });
  12632         return new_index;
  12633     }
  12634 
  12635     fn addStrTok(
  12636         gz: *GenZir,
  12637         tag: Zir.Inst.Tag,
  12638         str_index: Zir.NullTerminatedString,
  12639         /// Absolute token index. This function does the conversion to Decl offset.
  12640         abs_tok_index: Ast.TokenIndex,
  12641     ) !Zir.Inst.Ref {
  12642         return gz.add(.{
  12643             .tag = tag,
  12644             .data = .{ .str_tok = .{
  12645                 .start = str_index,
  12646                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  12647             } },
  12648         });
  12649     }
  12650 
  12651     fn addSaveErrRetIndex(
  12652         gz: *GenZir,
  12653         cond: union(enum) {
  12654             always: void,
  12655             if_of_error_type: Zir.Inst.Ref,
  12656         },
  12657     ) !Zir.Inst.Index {
  12658         return gz.addAsIndex(.{
  12659             .tag = .save_err_ret_index,
  12660             .data = .{ .save_err_ret_index = .{
  12661                 .operand = switch (cond) {
  12662                     .if_of_error_type => |x| x,
  12663                     else => .none,
  12664                 },
  12665             } },
  12666         });
  12667     }
  12668 
  12669     const BranchTarget = union(enum) {
  12670         ret,
  12671         block: Zir.Inst.Index,
  12672     };
  12673 
  12674     fn addRestoreErrRetIndex(
  12675         gz: *GenZir,
  12676         bt: BranchTarget,
  12677         cond: union(enum) {
  12678             always: void,
  12679             if_non_error: Zir.Inst.Ref,
  12680         },
  12681         src_node: Ast.Node.Index,
  12682     ) !Zir.Inst.Index {
  12683         switch (cond) {
  12684             .always => return gz.addAsIndex(.{
  12685                 .tag = .restore_err_ret_index_unconditional,
  12686                 .data = .{ .un_node = .{
  12687                     .operand = switch (bt) {
  12688                         .ret => .none,
  12689                         .block => |b| b.toRef(),
  12690                     },
  12691                     .src_node = gz.nodeIndexToRelative(src_node),
  12692                 } },
  12693             }),
  12694             .if_non_error => |operand| switch (bt) {
  12695                 .ret => return gz.addAsIndex(.{
  12696                     .tag = .restore_err_ret_index_fn_entry,
  12697                     .data = .{ .un_node = .{
  12698                         .operand = operand,
  12699                         .src_node = gz.nodeIndexToRelative(src_node),
  12700                     } },
  12701                 }),
  12702                 .block => |block| return (try gz.addExtendedPayload(
  12703                     .restore_err_ret_index,
  12704                     Zir.Inst.RestoreErrRetIndex{
  12705                         .src_node = gz.nodeIndexToRelative(src_node),
  12706                         .block = block.toRef(),
  12707                         .operand = operand,
  12708                     },
  12709                 )).toIndex().?,
  12710             },
  12711         }
  12712     }
  12713 
  12714     fn addBreak(
  12715         gz: *GenZir,
  12716         tag: Zir.Inst.Tag,
  12717         block_inst: Zir.Inst.Index,
  12718         operand: Zir.Inst.Ref,
  12719     ) !Zir.Inst.Index {
  12720         const gpa = gz.astgen.gpa;
  12721         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12722 
  12723         const new_index = try gz.makeBreak(tag, block_inst, operand);
  12724         gz.instructions.appendAssumeCapacity(new_index);
  12725         return new_index;
  12726     }
  12727 
  12728     fn makeBreak(
  12729         gz: *GenZir,
  12730         tag: Zir.Inst.Tag,
  12731         block_inst: Zir.Inst.Index,
  12732         operand: Zir.Inst.Ref,
  12733     ) !Zir.Inst.Index {
  12734         return gz.makeBreakCommon(tag, block_inst, operand, null);
  12735     }
  12736 
  12737     fn addBreakWithSrcNode(
  12738         gz: *GenZir,
  12739         tag: Zir.Inst.Tag,
  12740         block_inst: Zir.Inst.Index,
  12741         operand: Zir.Inst.Ref,
  12742         operand_src_node: Ast.Node.Index,
  12743     ) !Zir.Inst.Index {
  12744         const gpa = gz.astgen.gpa;
  12745         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12746 
  12747         const new_index = try gz.makeBreakWithSrcNode(tag, block_inst, operand, operand_src_node);
  12748         gz.instructions.appendAssumeCapacity(new_index);
  12749         return new_index;
  12750     }
  12751 
  12752     fn makeBreakWithSrcNode(
  12753         gz: *GenZir,
  12754         tag: Zir.Inst.Tag,
  12755         block_inst: Zir.Inst.Index,
  12756         operand: Zir.Inst.Ref,
  12757         operand_src_node: Ast.Node.Index,
  12758     ) !Zir.Inst.Index {
  12759         return gz.makeBreakCommon(tag, block_inst, operand, operand_src_node);
  12760     }
  12761 
  12762     fn makeBreakCommon(
  12763         gz: *GenZir,
  12764         tag: Zir.Inst.Tag,
  12765         block_inst: Zir.Inst.Index,
  12766         operand: Zir.Inst.Ref,
  12767         operand_src_node: ?Ast.Node.Index,
  12768     ) !Zir.Inst.Index {
  12769         const gpa = gz.astgen.gpa;
  12770         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12771         try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Break).@"struct".fields.len);
  12772 
  12773         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  12774         gz.astgen.instructions.appendAssumeCapacity(.{
  12775             .tag = tag,
  12776             .data = .{ .@"break" = .{
  12777                 .operand = operand,
  12778                 .payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Break{
  12779                     .operand_src_node = if (operand_src_node) |src_node|
  12780                         gz.nodeIndexToRelative(src_node)
  12781                     else
  12782                         Zir.Inst.Break.no_src_node,
  12783                     .block_inst = block_inst,
  12784                 }),
  12785             } },
  12786         });
  12787         return new_index;
  12788     }
  12789 
  12790     fn addBin(
  12791         gz: *GenZir,
  12792         tag: Zir.Inst.Tag,
  12793         lhs: Zir.Inst.Ref,
  12794         rhs: Zir.Inst.Ref,
  12795     ) !Zir.Inst.Ref {
  12796         assert(lhs != .none);
  12797         assert(rhs != .none);
  12798         return gz.add(.{
  12799             .tag = tag,
  12800             .data = .{ .bin = .{
  12801                 .lhs = lhs,
  12802                 .rhs = rhs,
  12803             } },
  12804         });
  12805     }
  12806 
  12807     fn addDefer(gz: *GenZir, index: u32, len: u32) !void {
  12808         _ = try gz.add(.{
  12809             .tag = .@"defer",
  12810             .data = .{ .@"defer" = .{
  12811                 .index = index,
  12812                 .len = len,
  12813             } },
  12814         });
  12815     }
  12816 
  12817     fn addDecl(
  12818         gz: *GenZir,
  12819         tag: Zir.Inst.Tag,
  12820         decl_index: u32,
  12821         src_node: Ast.Node.Index,
  12822     ) !Zir.Inst.Ref {
  12823         return gz.add(.{
  12824             .tag = tag,
  12825             .data = .{ .pl_node = .{
  12826                 .src_node = gz.nodeIndexToRelative(src_node),
  12827                 .payload_index = decl_index,
  12828             } },
  12829         });
  12830     }
  12831 
  12832     fn addNode(
  12833         gz: *GenZir,
  12834         tag: Zir.Inst.Tag,
  12835         /// Absolute node index. This function does the conversion to offset from Decl.
  12836         src_node: Ast.Node.Index,
  12837     ) !Zir.Inst.Ref {
  12838         return gz.add(.{
  12839             .tag = tag,
  12840             .data = .{ .node = gz.nodeIndexToRelative(src_node) },
  12841         });
  12842     }
  12843 
  12844     fn addInstNode(
  12845         gz: *GenZir,
  12846         tag: Zir.Inst.Tag,
  12847         inst: Zir.Inst.Index,
  12848         /// Absolute node index. This function does the conversion to offset from Decl.
  12849         src_node: Ast.Node.Index,
  12850     ) !Zir.Inst.Ref {
  12851         return gz.add(.{
  12852             .tag = tag,
  12853             .data = .{ .inst_node = .{
  12854                 .inst = inst,
  12855                 .src_node = gz.nodeIndexToRelative(src_node),
  12856             } },
  12857         });
  12858     }
  12859 
  12860     fn addNodeExtended(
  12861         gz: *GenZir,
  12862         opcode: Zir.Inst.Extended,
  12863         /// Absolute node index. This function does the conversion to offset from Decl.
  12864         src_node: Ast.Node.Index,
  12865     ) !Zir.Inst.Ref {
  12866         return gz.add(.{
  12867             .tag = .extended,
  12868             .data = .{ .extended = .{
  12869                 .opcode = opcode,
  12870                 .small = undefined,
  12871                 .operand = @bitCast(gz.nodeIndexToRelative(src_node)),
  12872             } },
  12873         });
  12874     }
  12875 
  12876     fn addAllocExtended(
  12877         gz: *GenZir,
  12878         args: struct {
  12879             /// Absolute node index. This function does the conversion to offset from Decl.
  12880             node: Ast.Node.Index,
  12881             type_inst: Zir.Inst.Ref,
  12882             align_inst: Zir.Inst.Ref,
  12883             is_const: bool,
  12884             is_comptime: bool,
  12885         },
  12886     ) !Zir.Inst.Ref {
  12887         const astgen = gz.astgen;
  12888         const gpa = astgen.gpa;
  12889 
  12890         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12891         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12892         try astgen.extra.ensureUnusedCapacity(
  12893             gpa,
  12894             @typeInfo(Zir.Inst.AllocExtended).@"struct".fields.len +
  12895                 @intFromBool(args.type_inst != .none) +
  12896                 @intFromBool(args.align_inst != .none),
  12897         );
  12898         const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.AllocExtended{
  12899             .src_node = gz.nodeIndexToRelative(args.node),
  12900         });
  12901         if (args.type_inst != .none) {
  12902             astgen.extra.appendAssumeCapacity(@intFromEnum(args.type_inst));
  12903         }
  12904         if (args.align_inst != .none) {
  12905             astgen.extra.appendAssumeCapacity(@intFromEnum(args.align_inst));
  12906         }
  12907 
  12908         const has_type: u4 = @intFromBool(args.type_inst != .none);
  12909         const has_align: u4 = @intFromBool(args.align_inst != .none);
  12910         const is_const: u4 = @intFromBool(args.is_const);
  12911         const is_comptime: u4 = @intFromBool(args.is_comptime);
  12912         const small: u16 = has_type | (has_align << 1) | (is_const << 2) | (is_comptime << 3);
  12913 
  12914         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12915         astgen.instructions.appendAssumeCapacity(.{
  12916             .tag = .extended,
  12917             .data = .{ .extended = .{
  12918                 .opcode = .alloc,
  12919                 .small = small,
  12920                 .operand = payload_index,
  12921             } },
  12922         });
  12923         gz.instructions.appendAssumeCapacity(new_index);
  12924         return new_index.toRef();
  12925     }
  12926 
  12927     fn addAsm(
  12928         gz: *GenZir,
  12929         args: struct {
  12930             tag: Zir.Inst.Extended,
  12931             /// Absolute node index. This function does the conversion to offset from Decl.
  12932             node: Ast.Node.Index,
  12933             asm_source: Zir.NullTerminatedString,
  12934             output_type_bits: u32,
  12935             is_volatile: bool,
  12936             outputs: []const Zir.Inst.Asm.Output,
  12937             inputs: []const Zir.Inst.Asm.Input,
  12938             clobbers: []const u32,
  12939         },
  12940     ) !Zir.Inst.Ref {
  12941         const astgen = gz.astgen;
  12942         const gpa = astgen.gpa;
  12943 
  12944         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12945         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12946         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Asm).@"struct".fields.len +
  12947             args.outputs.len * @typeInfo(Zir.Inst.Asm.Output).@"struct".fields.len +
  12948             args.inputs.len * @typeInfo(Zir.Inst.Asm.Input).@"struct".fields.len +
  12949             args.clobbers.len);
  12950 
  12951         const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Asm{
  12952             .src_node = gz.nodeIndexToRelative(args.node),
  12953             .asm_source = args.asm_source,
  12954             .output_type_bits = args.output_type_bits,
  12955         });
  12956         for (args.outputs) |output| {
  12957             _ = gz.astgen.addExtraAssumeCapacity(output);
  12958         }
  12959         for (args.inputs) |input| {
  12960             _ = gz.astgen.addExtraAssumeCapacity(input);
  12961         }
  12962         gz.astgen.extra.appendSliceAssumeCapacity(args.clobbers);
  12963 
  12964         //  * 0b00000000_000XXXXX - `outputs_len`.
  12965         //  * 0b000000XX_XXX00000 - `inputs_len`.
  12966         //  * 0b0XXXXX00_00000000 - `clobbers_len`.
  12967         //  * 0bX0000000_00000000 - is volatile
  12968         const small: u16 = @as(u16, @intCast(args.outputs.len)) |
  12969             @as(u16, @intCast(args.inputs.len << 5)) |
  12970             @as(u16, @intCast(args.clobbers.len << 10)) |
  12971             (@as(u16, @intFromBool(args.is_volatile)) << 15);
  12972 
  12973         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12974         astgen.instructions.appendAssumeCapacity(.{
  12975             .tag = .extended,
  12976             .data = .{ .extended = .{
  12977                 .opcode = args.tag,
  12978                 .small = small,
  12979                 .operand = payload_index,
  12980             } },
  12981         });
  12982         gz.instructions.appendAssumeCapacity(new_index);
  12983         return new_index.toRef();
  12984     }
  12985 
  12986     /// Note that this returns a `Zir.Inst.Index` not a ref.
  12987     /// Does *not* append the block instruction to the scope.
  12988     /// Leaves the `payload_index` field undefined.
  12989     fn makeBlockInst(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
  12990         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  12991         const gpa = gz.astgen.gpa;
  12992         try gz.astgen.instructions.append(gpa, .{
  12993             .tag = tag,
  12994             .data = .{ .pl_node = .{
  12995                 .src_node = gz.nodeIndexToRelative(node),
  12996                 .payload_index = undefined,
  12997             } },
  12998         });
  12999         return new_index;
  13000     }
  13001 
  13002     /// Note that this returns a `Zir.Inst.Index` not a ref.
  13003     /// Does *not* append the block instruction to the scope.
  13004     /// Leaves the `payload_index` field undefined. Use `setDeclaration` to finalize.
  13005     fn makeDeclaration(gz: *GenZir, node: Ast.Node.Index) !Zir.Inst.Index {
  13006         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  13007         try gz.astgen.instructions.append(gz.astgen.gpa, .{
  13008             .tag = .declaration,
  13009             .data = .{ .declaration = .{
  13010                 .src_node = node,
  13011                 .payload_index = undefined,
  13012             } },
  13013         });
  13014         return new_index;
  13015     }
  13016 
  13017     /// Note that this returns a `Zir.Inst.Index` not a ref.
  13018     /// Leaves the `payload_index` field undefined.
  13019     fn addCondBr(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
  13020         const gpa = gz.astgen.gpa;
  13021         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  13022         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  13023         try gz.astgen.instructions.append(gpa, .{
  13024             .tag = tag,
  13025             .data = .{ .pl_node = .{
  13026                 .src_node = gz.nodeIndexToRelative(node),
  13027                 .payload_index = undefined,
  13028             } },
  13029         });
  13030         gz.instructions.appendAssumeCapacity(new_index);
  13031         return new_index;
  13032     }
  13033 
  13034     fn setStruct(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  13035         src_node: Ast.Node.Index,
  13036         captures_len: u32,
  13037         fields_len: u32,
  13038         decls_len: u32,
  13039         has_backing_int: bool,
  13040         layout: std.builtin.Type.ContainerLayout,
  13041         known_non_opv: bool,
  13042         known_comptime_only: bool,
  13043         any_comptime_fields: bool,
  13044         any_default_inits: bool,
  13045         any_aligned_fields: bool,
  13046         fields_hash: std.zig.SrcHash,
  13047     }) !void {
  13048         const astgen = gz.astgen;
  13049         const gpa = astgen.gpa;
  13050 
  13051         // Node 0 is valid for the root `struct_decl` of a file!
  13052         assert(args.src_node != 0 or gz.parent.tag == .top);
  13053 
  13054         const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);
  13055 
  13056         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len + 3);
  13057         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.StructDecl{
  13058             .fields_hash_0 = fields_hash_arr[0],
  13059             .fields_hash_1 = fields_hash_arr[1],
  13060             .fields_hash_2 = fields_hash_arr[2],
  13061             .fields_hash_3 = fields_hash_arr[3],
  13062             .src_line = astgen.source_line,
  13063             .src_node = args.src_node,
  13064         });
  13065 
  13066         if (args.captures_len != 0) {
  13067             astgen.extra.appendAssumeCapacity(args.captures_len);
  13068         }
  13069         if (args.fields_len != 0) {
  13070             astgen.extra.appendAssumeCapacity(args.fields_len);
  13071         }
  13072         if (args.decls_len != 0) {
  13073             astgen.extra.appendAssumeCapacity(args.decls_len);
  13074         }
  13075         astgen.instructions.set(@intFromEnum(inst), .{
  13076             .tag = .extended,
  13077             .data = .{ .extended = .{
  13078                 .opcode = .struct_decl,
  13079                 .small = @bitCast(Zir.Inst.StructDecl.Small{
  13080                     .has_captures_len = args.captures_len != 0,
  13081                     .has_fields_len = args.fields_len != 0,
  13082                     .has_decls_len = args.decls_len != 0,
  13083                     .has_backing_int = args.has_backing_int,
  13084                     .known_non_opv = args.known_non_opv,
  13085                     .known_comptime_only = args.known_comptime_only,
  13086                     .name_strategy = gz.anon_name_strategy,
  13087                     .layout = args.layout,
  13088                     .any_comptime_fields = args.any_comptime_fields,
  13089                     .any_default_inits = args.any_default_inits,
  13090                     .any_aligned_fields = args.any_aligned_fields,
  13091                 }),
  13092                 .operand = payload_index,
  13093             } },
  13094         });
  13095     }
  13096 
  13097     fn setUnion(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  13098         src_node: Ast.Node.Index,
  13099         tag_type: Zir.Inst.Ref,
  13100         captures_len: u32,
  13101         body_len: u32,
  13102         fields_len: u32,
  13103         decls_len: u32,
  13104         layout: std.builtin.Type.ContainerLayout,
  13105         auto_enum_tag: bool,
  13106         any_aligned_fields: bool,
  13107         fields_hash: std.zig.SrcHash,
  13108     }) !void {
  13109         const astgen = gz.astgen;
  13110         const gpa = astgen.gpa;
  13111 
  13112         assert(args.src_node != 0);
  13113 
  13114         const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);
  13115 
  13116         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.UnionDecl).@"struct".fields.len + 5);
  13117         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.UnionDecl{
  13118             .fields_hash_0 = fields_hash_arr[0],
  13119             .fields_hash_1 = fields_hash_arr[1],
  13120             .fields_hash_2 = fields_hash_arr[2],
  13121             .fields_hash_3 = fields_hash_arr[3],
  13122             .src_line = astgen.source_line,
  13123             .src_node = args.src_node,
  13124         });
  13125 
  13126         if (args.tag_type != .none) {
  13127             astgen.extra.appendAssumeCapacity(@intFromEnum(args.tag_type));
  13128         }
  13129         if (args.captures_len != 0) {
  13130             astgen.extra.appendAssumeCapacity(args.captures_len);
  13131         }
  13132         if (args.body_len != 0) {
  13133             astgen.extra.appendAssumeCapacity(args.body_len);
  13134         }
  13135         if (args.fields_len != 0) {
  13136             astgen.extra.appendAssumeCapacity(args.fields_len);
  13137         }
  13138         if (args.decls_len != 0) {
  13139             astgen.extra.appendAssumeCapacity(args.decls_len);
  13140         }
  13141         astgen.instructions.set(@intFromEnum(inst), .{
  13142             .tag = .extended,
  13143             .data = .{ .extended = .{
  13144                 .opcode = .union_decl,
  13145                 .small = @bitCast(Zir.Inst.UnionDecl.Small{
  13146                     .has_tag_type = args.tag_type != .none,
  13147                     .has_captures_len = args.captures_len != 0,
  13148                     .has_body_len = args.body_len != 0,
  13149                     .has_fields_len = args.fields_len != 0,
  13150                     .has_decls_len = args.decls_len != 0,
  13151                     .name_strategy = gz.anon_name_strategy,
  13152                     .layout = args.layout,
  13153                     .auto_enum_tag = args.auto_enum_tag,
  13154                     .any_aligned_fields = args.any_aligned_fields,
  13155                 }),
  13156                 .operand = payload_index,
  13157             } },
  13158         });
  13159     }
  13160 
  13161     fn setEnum(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  13162         src_node: Ast.Node.Index,
  13163         tag_type: Zir.Inst.Ref,
  13164         captures_len: u32,
  13165         body_len: u32,
  13166         fields_len: u32,
  13167         decls_len: u32,
  13168         nonexhaustive: bool,
  13169         fields_hash: std.zig.SrcHash,
  13170     }) !void {
  13171         const astgen = gz.astgen;
  13172         const gpa = astgen.gpa;
  13173 
  13174         assert(args.src_node != 0);
  13175 
  13176         const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);
  13177 
  13178         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.EnumDecl).@"struct".fields.len + 5);
  13179         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.EnumDecl{
  13180             .fields_hash_0 = fields_hash_arr[0],
  13181             .fields_hash_1 = fields_hash_arr[1],
  13182             .fields_hash_2 = fields_hash_arr[2],
  13183             .fields_hash_3 = fields_hash_arr[3],
  13184             .src_line = astgen.source_line,
  13185             .src_node = args.src_node,
  13186         });
  13187 
  13188         if (args.tag_type != .none) {
  13189             astgen.extra.appendAssumeCapacity(@intFromEnum(args.tag_type));
  13190         }
  13191         if (args.captures_len != 0) {
  13192             astgen.extra.appendAssumeCapacity(args.captures_len);
  13193         }
  13194         if (args.body_len != 0) {
  13195             astgen.extra.appendAssumeCapacity(args.body_len);
  13196         }
  13197         if (args.fields_len != 0) {
  13198             astgen.extra.appendAssumeCapacity(args.fields_len);
  13199         }
  13200         if (args.decls_len != 0) {
  13201             astgen.extra.appendAssumeCapacity(args.decls_len);
  13202         }
  13203         astgen.instructions.set(@intFromEnum(inst), .{
  13204             .tag = .extended,
  13205             .data = .{ .extended = .{
  13206                 .opcode = .enum_decl,
  13207                 .small = @bitCast(Zir.Inst.EnumDecl.Small{
  13208                     .has_tag_type = args.tag_type != .none,
  13209                     .has_captures_len = args.captures_len != 0,
  13210                     .has_body_len = args.body_len != 0,
  13211                     .has_fields_len = args.fields_len != 0,
  13212                     .has_decls_len = args.decls_len != 0,
  13213                     .name_strategy = gz.anon_name_strategy,
  13214                     .nonexhaustive = args.nonexhaustive,
  13215                 }),
  13216                 .operand = payload_index,
  13217             } },
  13218         });
  13219     }
  13220 
  13221     fn setOpaque(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  13222         src_node: Ast.Node.Index,
  13223         captures_len: u32,
  13224         decls_len: u32,
  13225     }) !void {
  13226         const astgen = gz.astgen;
  13227         const gpa = astgen.gpa;
  13228 
  13229         assert(args.src_node != 0);
  13230 
  13231         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.OpaqueDecl).@"struct".fields.len + 2);
  13232         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.OpaqueDecl{
  13233             .src_line = astgen.source_line,
  13234             .src_node = args.src_node,
  13235         });
  13236 
  13237         if (args.captures_len != 0) {
  13238             astgen.extra.appendAssumeCapacity(args.captures_len);
  13239         }
  13240         if (args.decls_len != 0) {
  13241             astgen.extra.appendAssumeCapacity(args.decls_len);
  13242         }
  13243         astgen.instructions.set(@intFromEnum(inst), .{
  13244             .tag = .extended,
  13245             .data = .{ .extended = .{
  13246                 .opcode = .opaque_decl,
  13247                 .small = @bitCast(Zir.Inst.OpaqueDecl.Small{
  13248                     .has_captures_len = args.captures_len != 0,
  13249                     .has_decls_len = args.decls_len != 0,
  13250                     .name_strategy = gz.anon_name_strategy,
  13251                 }),
  13252                 .operand = payload_index,
  13253             } },
  13254         });
  13255     }
  13256 
  13257     fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref {
  13258         return (try gz.addAsIndex(inst)).toRef();
  13259     }
  13260 
  13261     fn addAsIndex(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Index {
  13262         const gpa = gz.astgen.gpa;
  13263         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  13264         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  13265 
  13266         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  13267         gz.astgen.instructions.appendAssumeCapacity(inst);
  13268         gz.instructions.appendAssumeCapacity(new_index);
  13269         return new_index;
  13270     }
  13271 
  13272     fn reserveInstructionIndex(gz: *GenZir) !Zir.Inst.Index {
  13273         const gpa = gz.astgen.gpa;
  13274         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  13275         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  13276 
  13277         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  13278         gz.astgen.instructions.len += 1;
  13279         gz.instructions.appendAssumeCapacity(new_index);
  13280         return new_index;
  13281     }
  13282 
  13283     fn addRet(gz: *GenZir, ri: ResultInfo, operand: Zir.Inst.Ref, node: Ast.Node.Index) !void {
  13284         switch (ri.rl) {
  13285             .ptr => |ptr_res| _ = try gz.addUnNode(.ret_load, ptr_res.inst, node),
  13286             .coerced_ty => _ = try gz.addUnNode(.ret_node, operand, node),
  13287             else => unreachable,
  13288         }
  13289     }
  13290 
  13291     fn addDbgVar(gz: *GenZir, tag: Zir.Inst.Tag, name: Zir.NullTerminatedString, inst: Zir.Inst.Ref) !void {
  13292         if (gz.is_comptime) return;
  13293 
  13294         _ = try gz.add(.{ .tag = tag, .data = .{
  13295             .str_op = .{
  13296                 .str = name,
  13297                 .operand = inst,
  13298             },
  13299         } });
  13300     }
  13301 };
  13302 
  13303 /// This can only be for short-lived references; the memory becomes invalidated
  13304 /// when another string is added.
  13305 fn nullTerminatedString(astgen: AstGen, index: Zir.NullTerminatedString) [*:0]const u8 {
  13306     return @ptrCast(astgen.string_bytes.items[@intFromEnum(index)..]);
  13307 }
  13308 
  13309 /// Local variables shadowing detection, including function parameters.
  13310 fn detectLocalShadowing(
  13311     astgen: *AstGen,
  13312     scope: *Scope,
  13313     ident_name: Zir.NullTerminatedString,
  13314     name_token: Ast.TokenIndex,
  13315     token_bytes: []const u8,
  13316     id_cat: Scope.IdCat,
  13317 ) !void {
  13318     const gpa = astgen.gpa;
  13319     if (token_bytes[0] != '@' and isPrimitive(token_bytes)) {
  13320         return astgen.failTokNotes(name_token, "name shadows primitive '{s}'", .{
  13321             token_bytes,
  13322         }, &[_]u32{
  13323             try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{
  13324                 token_bytes,
  13325             }),
  13326         });
  13327     }
  13328 
  13329     var s = scope;
  13330     var outer_scope = false;
  13331     while (true) switch (s.tag) {
  13332         .local_val => {
  13333             const local_val = s.cast(Scope.LocalVal).?;
  13334             if (local_val.name == ident_name) {
  13335                 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
  13336                 const name = try gpa.dupe(u8, name_slice);
  13337                 defer gpa.free(name);
  13338                 if (outer_scope) {
  13339                     return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{
  13340                         @tagName(id_cat), name, @tagName(local_val.id_cat),
  13341                     }, &[_]u32{
  13342                         try astgen.errNoteTok(
  13343                             local_val.token_src,
  13344                             "previous declaration here",
  13345                             .{},
  13346                         ),
  13347                     });
  13348                 }
  13349                 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{
  13350                     @tagName(local_val.id_cat), name,
  13351                 }, &[_]u32{
  13352                     try astgen.errNoteTok(
  13353                         local_val.token_src,
  13354                         "previous declaration here",
  13355                         .{},
  13356                     ),
  13357                 });
  13358             }
  13359             s = local_val.parent;
  13360         },
  13361         .local_ptr => {
  13362             const local_ptr = s.cast(Scope.LocalPtr).?;
  13363             if (local_ptr.name == ident_name) {
  13364                 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
  13365                 const name = try gpa.dupe(u8, name_slice);
  13366                 defer gpa.free(name);
  13367                 if (outer_scope) {
  13368                     return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{
  13369                         @tagName(id_cat), name, @tagName(local_ptr.id_cat),
  13370                     }, &[_]u32{
  13371                         try astgen.errNoteTok(
  13372                             local_ptr.token_src,
  13373                             "previous declaration here",
  13374                             .{},
  13375                         ),
  13376                     });
  13377                 }
  13378                 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{
  13379                     @tagName(local_ptr.id_cat), name,
  13380                 }, &[_]u32{
  13381                     try astgen.errNoteTok(
  13382                         local_ptr.token_src,
  13383                         "previous declaration here",
  13384                         .{},
  13385                     ),
  13386                 });
  13387             }
  13388             s = local_ptr.parent;
  13389         },
  13390         .namespace => {
  13391             outer_scope = true;
  13392             const ns = s.cast(Scope.Namespace).?;
  13393             const decl_node = ns.decls.get(ident_name) orelse {
  13394                 s = ns.parent;
  13395                 continue;
  13396             };
  13397             const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
  13398             const name = try gpa.dupe(u8, name_slice);
  13399             defer gpa.free(name);
  13400             return astgen.failTokNotes(name_token, "{s} shadows declaration of '{s}'", .{
  13401                 @tagName(id_cat), name,
  13402             }, &[_]u32{
  13403                 try astgen.errNoteNode(decl_node, "declared here", .{}),
  13404             });
  13405         },
  13406         .gen_zir => {
  13407             s = s.cast(GenZir).?.parent;
  13408             outer_scope = true;
  13409         },
  13410         .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
  13411         .top => break,
  13412     };
  13413 }
  13414 
  13415 const LineColumn = struct { u32, u32 };
  13416 
  13417 /// Advances the source cursor to the main token of `node` if not in comptime scope.
  13418 /// Usually paired with `emitDbgStmt`.
  13419 fn maybeAdvanceSourceCursorToMainToken(gz: *GenZir, node: Ast.Node.Index) LineColumn {
  13420     if (gz.is_comptime) return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column };
  13421 
  13422     const tree = gz.astgen.tree;
  13423     const token_starts = tree.tokens.items(.start);
  13424     const main_tokens = tree.nodes.items(.main_token);
  13425     const node_start = token_starts[main_tokens[node]];
  13426     gz.astgen.advanceSourceCursor(node_start);
  13427 
  13428     return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column };
  13429 }
  13430 
  13431 /// Advances the source cursor to the beginning of `node`.
  13432 fn advanceSourceCursorToNode(astgen: *AstGen, node: Ast.Node.Index) void {
  13433     const tree = astgen.tree;
  13434     const token_starts = tree.tokens.items(.start);
  13435     const node_start = token_starts[tree.firstToken(node)];
  13436     astgen.advanceSourceCursor(node_start);
  13437 }
  13438 
  13439 /// Advances the source cursor to an absolute byte offset `end` in the file.
  13440 fn advanceSourceCursor(astgen: *AstGen, end: usize) void {
  13441     const source = astgen.tree.source;
  13442     var i = astgen.source_offset;
  13443     var line = astgen.source_line;
  13444     var column = astgen.source_column;
  13445     assert(i <= end);
  13446     while (i < end) : (i += 1) {
  13447         if (source[i] == '\n') {
  13448             line += 1;
  13449             column = 0;
  13450         } else {
  13451             column += 1;
  13452         }
  13453     }
  13454     astgen.source_offset = i;
  13455     astgen.source_line = line;
  13456     astgen.source_column = column;
  13457 }
  13458 
  13459 const SourceCursor = struct {
  13460     offset: u32,
  13461     line: u32,
  13462     column: u32,
  13463 };
  13464 
  13465 /// Get the current source cursor, to be restored later with `restoreSourceCursor`.
  13466 /// This is useful when analyzing source code out-of-order.
  13467 fn saveSourceCursor(astgen: *const AstGen) SourceCursor {
  13468     return .{
  13469         .offset = astgen.source_offset,
  13470         .line = astgen.source_line,
  13471         .column = astgen.source_column,
  13472     };
  13473 }
  13474 fn restoreSourceCursor(astgen: *AstGen, cursor: SourceCursor) void {
  13475     astgen.source_offset = cursor.offset;
  13476     astgen.source_line = cursor.line;
  13477     astgen.source_column = cursor.column;
  13478 }
  13479 
  13480 /// Detects name conflicts for decls and fields, and populates `namespace.decls` with all named declarations.
  13481 /// Returns the number of declarations in the namespace, including unnamed declarations (e.g. `comptime` decls).
  13482 fn scanContainer(
  13483     astgen: *AstGen,
  13484     namespace: *Scope.Namespace,
  13485     members: []const Ast.Node.Index,
  13486     container_kind: enum { @"struct", @"union", @"enum", @"opaque" },
  13487 ) !u32 {
  13488     const gpa = astgen.gpa;
  13489     const tree = astgen.tree;
  13490     const node_tags = tree.nodes.items(.tag);
  13491     const main_tokens = tree.nodes.items(.main_token);
  13492     const token_tags = tree.tokens.items(.tag);
  13493 
  13494     var any_invalid_declarations = false;
  13495 
  13496     // This type forms a linked list of source tokens declaring the same name.
  13497     const NameEntry = struct {
  13498         tok: Ast.TokenIndex,
  13499         /// Using a linked list here simplifies memory management, and is acceptable since
  13500         ///ewntries are only allocated in error situations. The entries are allocated into the
  13501         /// AstGen arena.
  13502         next: ?*@This(),
  13503     };
  13504 
  13505     // The maps below are allocated into this SFBA to avoid using the GPA for small namespaces.
  13506     var sfba_state = std.heap.stackFallback(512, astgen.gpa);
  13507     const sfba = sfba_state.get();
  13508 
  13509     var names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .empty;
  13510     var test_names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .empty;
  13511     var decltest_names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .empty;
  13512     defer {
  13513         names.deinit(sfba);
  13514         test_names.deinit(sfba);
  13515         decltest_names.deinit(sfba);
  13516     }
  13517 
  13518     var any_duplicates = false;
  13519     var decl_count: u32 = 0;
  13520     for (members) |member_node| {
  13521         const Kind = enum { decl, field };
  13522         const kind: Kind, const name_token = switch (node_tags[member_node]) {
  13523             .container_field_init,
  13524             .container_field_align,
  13525             .container_field,
  13526             => blk: {
  13527                 var full = tree.fullContainerField(member_node).?;
  13528                 switch (container_kind) {
  13529                     .@"struct", .@"opaque" => {},
  13530                     .@"union", .@"enum" => full.convertToNonTupleLike(astgen.tree.nodes),
  13531                 }
  13532                 if (full.ast.tuple_like) continue;
  13533                 break :blk .{ .field, full.ast.main_token };
  13534             },
  13535 
  13536             .global_var_decl,
  13537             .local_var_decl,
  13538             .simple_var_decl,
  13539             .aligned_var_decl,
  13540             => blk: {
  13541                 decl_count += 1;
  13542                 break :blk .{ .decl, main_tokens[member_node] + 1 };
  13543             },
  13544 
  13545             .fn_proto_simple,
  13546             .fn_proto_multi,
  13547             .fn_proto_one,
  13548             .fn_proto,
  13549             .fn_decl,
  13550             => blk: {
  13551                 decl_count += 1;
  13552                 const ident = main_tokens[member_node] + 1;
  13553                 if (token_tags[ident] != .identifier) {
  13554                     try astgen.appendErrorNode(member_node, "missing function name", .{});
  13555                     any_invalid_declarations = true;
  13556                     continue;
  13557                 }
  13558                 break :blk .{ .decl, ident };
  13559             },
  13560 
  13561             .@"comptime", .@"usingnamespace" => {
  13562                 decl_count += 1;
  13563                 continue;
  13564             },
  13565 
  13566             .test_decl => {
  13567                 decl_count += 1;
  13568                 // We don't want shadowing detection here, and test names work a bit differently, so
  13569                 // we must do the redeclaration detection ourselves.
  13570                 const test_name_token = main_tokens[member_node] + 1;
  13571                 const new_ent: NameEntry = .{
  13572                     .tok = test_name_token,
  13573                     .next = null,
  13574                 };
  13575                 switch (token_tags[test_name_token]) {
  13576                     else => {}, // unnamed test
  13577                     .string_literal => {
  13578                         const name = try astgen.strLitAsString(test_name_token);
  13579                         const gop = try test_names.getOrPut(sfba, name.index);
  13580                         if (gop.found_existing) {
  13581                             var e = gop.value_ptr;
  13582                             while (e.next) |n| e = n;
  13583                             e.next = try astgen.arena.create(NameEntry);
  13584                             e.next.?.* = new_ent;
  13585                             any_duplicates = true;
  13586                         } else {
  13587                             gop.value_ptr.* = new_ent;
  13588                         }
  13589                     },
  13590                     .identifier => {
  13591                         const name = try astgen.identAsString(test_name_token);
  13592                         const gop = try decltest_names.getOrPut(sfba, name);
  13593                         if (gop.found_existing) {
  13594                             var e = gop.value_ptr;
  13595                             while (e.next) |n| e = n;
  13596                             e.next = try astgen.arena.create(NameEntry);
  13597                             e.next.?.* = new_ent;
  13598                             any_duplicates = true;
  13599                         } else {
  13600                             gop.value_ptr.* = new_ent;
  13601                         }
  13602                     },
  13603                 }
  13604                 continue;
  13605             },
  13606 
  13607             else => unreachable,
  13608         };
  13609 
  13610         const name_str_index = try astgen.identAsString(name_token);
  13611 
  13612         if (kind == .decl) {
  13613             // Put the name straight into `decls`, even if there are compile errors.
  13614             // This avoids incorrect "undeclared identifier" errors later on.
  13615             try namespace.decls.put(gpa, name_str_index, member_node);
  13616         }
  13617 
  13618         {
  13619             const gop = try names.getOrPut(sfba, name_str_index);
  13620             const new_ent: NameEntry = .{
  13621                 .tok = name_token,
  13622                 .next = null,
  13623             };
  13624             if (gop.found_existing) {
  13625                 var e = gop.value_ptr;
  13626                 while (e.next) |n| e = n;
  13627                 e.next = try astgen.arena.create(NameEntry);
  13628                 e.next.?.* = new_ent;
  13629                 any_duplicates = true;
  13630                 continue;
  13631             } else {
  13632                 gop.value_ptr.* = new_ent;
  13633             }
  13634         }
  13635 
  13636         // For fields, we only needed the duplicate check! Decls have some more checks to do, though.
  13637         switch (kind) {
  13638             .decl => {},
  13639             .field => continue,
  13640         }
  13641 
  13642         const token_bytes = astgen.tree.tokenSlice(name_token);
  13643         if (token_bytes[0] != '@' and isPrimitive(token_bytes)) {
  13644             try astgen.appendErrorTokNotes(name_token, "name shadows primitive '{s}'", .{
  13645                 token_bytes,
  13646             }, &.{
  13647                 try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{
  13648                     token_bytes,
  13649                 }),
  13650             });
  13651             any_invalid_declarations = true;
  13652             continue;
  13653         }
  13654 
  13655         var s = namespace.parent;
  13656         while (true) switch (s.tag) {
  13657             .local_val => {
  13658                 const local_val = s.cast(Scope.LocalVal).?;
  13659                 if (local_val.name == name_str_index) {
  13660                     try astgen.appendErrorTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{
  13661                         token_bytes, @tagName(local_val.id_cat),
  13662                     }, &.{
  13663                         try astgen.errNoteTok(
  13664                             local_val.token_src,
  13665                             "previous declaration here",
  13666                             .{},
  13667                         ),
  13668                     });
  13669                     any_invalid_declarations = true;
  13670                     break;
  13671                 }
  13672                 s = local_val.parent;
  13673             },
  13674             .local_ptr => {
  13675                 const local_ptr = s.cast(Scope.LocalPtr).?;
  13676                 if (local_ptr.name == name_str_index) {
  13677                     try astgen.appendErrorTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{
  13678                         token_bytes, @tagName(local_ptr.id_cat),
  13679                     }, &.{
  13680                         try astgen.errNoteTok(
  13681                             local_ptr.token_src,
  13682                             "previous declaration here",
  13683                             .{},
  13684                         ),
  13685                     });
  13686                     any_invalid_declarations = true;
  13687                     break;
  13688                 }
  13689                 s = local_ptr.parent;
  13690             },
  13691             .namespace => s = s.cast(Scope.Namespace).?.parent,
  13692             .gen_zir => s = s.cast(GenZir).?.parent,
  13693             .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
  13694             .top => break,
  13695         };
  13696     }
  13697 
  13698     if (!any_duplicates) {
  13699         if (any_invalid_declarations) return error.AnalysisFail;
  13700         return decl_count;
  13701     }
  13702 
  13703     for (names.keys(), names.values()) |name, first| {
  13704         if (first.next == null) continue;
  13705         var notes: std.ArrayListUnmanaged(u32) = .empty;
  13706         var prev: NameEntry = first;
  13707         while (prev.next) |cur| : (prev = cur.*) {
  13708             try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate name here", .{}));
  13709         }
  13710         try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
  13711         const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
  13712         try astgen.appendErrorTokNotes(first.tok, "duplicate {s} member name '{s}'", .{ @tagName(container_kind), name_duped }, notes.items);
  13713         any_invalid_declarations = true;
  13714     }
  13715 
  13716     for (test_names.keys(), test_names.values()) |name, first| {
  13717         if (first.next == null) continue;
  13718         var notes: std.ArrayListUnmanaged(u32) = .empty;
  13719         var prev: NameEntry = first;
  13720         while (prev.next) |cur| : (prev = cur.*) {
  13721             try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate test here", .{}));
  13722         }
  13723         try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
  13724         const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
  13725         try astgen.appendErrorTokNotes(first.tok, "duplicate test name '{s}'", .{name_duped}, notes.items);
  13726         any_invalid_declarations = true;
  13727     }
  13728 
  13729     for (decltest_names.keys(), decltest_names.values()) |name, first| {
  13730         if (first.next == null) continue;
  13731         var notes: std.ArrayListUnmanaged(u32) = .empty;
  13732         var prev: NameEntry = first;
  13733         while (prev.next) |cur| : (prev = cur.*) {
  13734             try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate decltest here", .{}));
  13735         }
  13736         try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
  13737         const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
  13738         try astgen.appendErrorTokNotes(first.tok, "duplicate decltest '{s}'", .{name_duped}, notes.items);
  13739         any_invalid_declarations = true;
  13740     }
  13741 
  13742     assert(any_invalid_declarations);
  13743     return error.AnalysisFail;
  13744 }
  13745 
  13746 /// Assumes capacity for body has already been added. Needed capacity taking into
  13747 /// account fixups can be found with `countBodyLenAfterFixups`.
  13748 fn appendBodyWithFixups(astgen: *AstGen, body: []const Zir.Inst.Index) void {
  13749     return appendBodyWithFixupsArrayList(astgen, &astgen.extra, body);
  13750 }
  13751 
  13752 fn appendBodyWithFixupsArrayList(
  13753     astgen: *AstGen,
  13754     list: *std.ArrayListUnmanaged(u32),
  13755     body: []const Zir.Inst.Index,
  13756 ) void {
  13757     astgen.appendBodyWithFixupsExtraRefsArrayList(list, body, &.{});
  13758 }
  13759 
  13760 fn appendBodyWithFixupsExtraRefsArrayList(
  13761     astgen: *AstGen,
  13762     list: *std.ArrayListUnmanaged(u32),
  13763     body: []const Zir.Inst.Index,
  13764     extra_refs: []const Zir.Inst.Index,
  13765 ) void {
  13766     for (extra_refs) |extra_inst| {
  13767         if (astgen.ref_table.fetchRemove(extra_inst)) |kv| {
  13768             appendPossiblyRefdBodyInst(astgen, list, kv.value);
  13769         }
  13770     }
  13771     for (body) |body_inst| {
  13772         appendPossiblyRefdBodyInst(astgen, list, body_inst);
  13773     }
  13774 }
  13775 
  13776 fn appendPossiblyRefdBodyInst(
  13777     astgen: *AstGen,
  13778     list: *std.ArrayListUnmanaged(u32),
  13779     body_inst: Zir.Inst.Index,
  13780 ) void {
  13781     list.appendAssumeCapacity(@intFromEnum(body_inst));
  13782     const kv = astgen.ref_table.fetchRemove(body_inst) orelse return;
  13783     const ref_inst = kv.value;
  13784     return appendPossiblyRefdBodyInst(astgen, list, ref_inst);
  13785 }
  13786 
  13787 fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 {
  13788     return astgen.countBodyLenAfterFixupsExtraRefs(body, &.{});
  13789 }
  13790 
  13791 /// Return the number of instructions in `body` after prepending the `ref` instructions in `ref_table`.
  13792 /// As well as all instructions in `body`, we also prepend `ref`s of any instruction in `extra_refs`.
  13793 /// For instance, if an index has been reserved with a special meaning to a child block, it must be
  13794 /// passed to `extra_refs` to ensure `ref`s of that index are added correctly.
  13795 fn countBodyLenAfterFixupsExtraRefs(astgen: *AstGen, body: []const Zir.Inst.Index, extra_refs: []const Zir.Inst.Index) u32 {
  13796     var count = body.len;
  13797     for (body) |body_inst| {
  13798         var check_inst = body_inst;
  13799         while (astgen.ref_table.get(check_inst)) |ref_inst| {
  13800             count += 1;
  13801             check_inst = ref_inst;
  13802         }
  13803     }
  13804     for (extra_refs) |extra_inst| {
  13805         var check_inst = extra_inst;
  13806         while (astgen.ref_table.get(check_inst)) |ref_inst| {
  13807             count += 1;
  13808             check_inst = ref_inst;
  13809         }
  13810     }
  13811     return @intCast(count);
  13812 }
  13813 
  13814 fn emitDbgStmt(gz: *GenZir, lc: LineColumn) !void {
  13815     if (gz.is_comptime) return;
  13816     if (gz.instructions.items.len > gz.instructions_top) {
  13817         const astgen = gz.astgen;
  13818         const last = gz.instructions.items[gz.instructions.items.len - 1];
  13819         if (astgen.instructions.items(.tag)[@intFromEnum(last)] == .dbg_stmt) {
  13820             astgen.instructions.items(.data)[@intFromEnum(last)].dbg_stmt = .{
  13821                 .line = lc[0],
  13822                 .column = lc[1],
  13823             };
  13824             return;
  13825         }
  13826     }
  13827 
  13828     _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
  13829         .dbg_stmt = .{
  13830             .line = lc[0],
  13831             .column = lc[1],
  13832         },
  13833     } });
  13834 }
  13835 
  13836 /// In some cases, Sema expects us to generate a `dbg_stmt` at the instruction
  13837 /// *index* directly preceding the next instruction (e.g. if a call is %10, it
  13838 /// expects a dbg_stmt at %9). TODO: this logic may allow redundant dbg_stmt
  13839 /// instructions; fix up Sema so we don't need it!
  13840 fn emitDbgStmtForceCurrentIndex(gz: *GenZir, lc: LineColumn) !void {
  13841     const astgen = gz.astgen;
  13842     if (gz.instructions.items.len > gz.instructions_top and
  13843         @intFromEnum(gz.instructions.items[gz.instructions.items.len - 1]) == astgen.instructions.len - 1)
  13844     {
  13845         const last = astgen.instructions.len - 1;
  13846         if (astgen.instructions.items(.tag)[last] == .dbg_stmt) {
  13847             astgen.instructions.items(.data)[last].dbg_stmt = .{
  13848                 .line = lc[0],
  13849                 .column = lc[1],
  13850             };
  13851             return;
  13852         }
  13853     }
  13854 
  13855     _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
  13856         .dbg_stmt = .{
  13857             .line = lc[0],
  13858             .column = lc[1],
  13859         },
  13860     } });
  13861 }
  13862 
  13863 fn lowerAstErrors(astgen: *AstGen) !void {
  13864     const gpa = astgen.gpa;
  13865     const tree = astgen.tree;
  13866     assert(tree.errors.len > 0);
  13867 
  13868     var msg: std.ArrayListUnmanaged(u8) = .empty;
  13869     defer msg.deinit(gpa);
  13870 
  13871     var notes: std.ArrayListUnmanaged(u32) = .empty;
  13872     defer notes.deinit(gpa);
  13873 
  13874     var cur_err = tree.errors[0];
  13875     for (tree.errors[1..]) |err| {
  13876         if (err.is_note) {
  13877             try tree.renderError(err, msg.writer(gpa));
  13878             try notes.append(gpa, try astgen.errNoteTok(err.token, "{s}", .{msg.items}));
  13879         } else {
  13880             // Flush error
  13881             const extra_offset = tree.errorOffset(cur_err);
  13882             try tree.renderError(cur_err, msg.writer(gpa));
  13883             try astgen.appendErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.items}, notes.items);
  13884             notes.clearRetainingCapacity();
  13885             cur_err = err;
  13886 
  13887             // TODO: `Parse` currently does not have good error recovery mechanisms, so the remaining errors could be bogus.
  13888             // As such, we'll ignore all remaining errors for now. We should improve `Parse` so that we can report all the errors.
  13889             return;
  13890         }
  13891         msg.clearRetainingCapacity();
  13892     }
  13893 
  13894     // Flush error
  13895     const extra_offset = tree.errorOffset(cur_err);
  13896     try tree.renderError(cur_err, msg.writer(gpa));
  13897     try astgen.appendErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.items}, notes.items);
  13898 }
  13899 
  13900 const DeclarationName = union(enum) {
  13901     named: Ast.TokenIndex,
  13902     named_test: Ast.TokenIndex,
  13903     decltest: Ast.TokenIndex,
  13904     unnamed_test,
  13905     @"comptime",
  13906     @"usingnamespace",
  13907 };
  13908 
  13909 fn addFailedDeclaration(
  13910     wip_members: *WipMembers,
  13911     gz: *GenZir,
  13912     name: DeclarationName,
  13913     src_node: Ast.Node.Index,
  13914     is_pub: bool,
  13915 ) !void {
  13916     const decl_inst = try gz.makeDeclaration(src_node);
  13917     wip_members.nextDecl(decl_inst);
  13918     var decl_gz = gz.makeSubBlock(&gz.base); // scope doesn't matter here
  13919     _ = try decl_gz.add(.{
  13920         .tag = .extended,
  13921         .data = .{ .extended = .{
  13922             .opcode = .astgen_error,
  13923             .small = undefined,
  13924             .operand = undefined,
  13925         } },
  13926     });
  13927     try setDeclaration(
  13928         decl_inst,
  13929         @splat(0), // use a fixed hash to represent an AstGen failure; we don't care about source changes if AstGen still failed!
  13930         name,
  13931         gz.astgen.source_line,
  13932         gz.astgen.source_column,
  13933         is_pub,
  13934         false, // we don't care about exports since semantic analysis will fail
  13935         &decl_gz,
  13936         null,
  13937     );
  13938 }
  13939 
  13940 /// Sets all extra data for a `declaration` instruction.
  13941 /// Unstacks `value_gz`, `align_gz`, `linksection_gz`, and `addrspace_gz`.
  13942 fn setDeclaration(
  13943     decl_inst: Zir.Inst.Index,
  13944     src_hash: std.zig.SrcHash,
  13945     name: DeclarationName,
  13946     src_line: u32,
  13947     src_column: u32,
  13948     is_pub: bool,
  13949     is_export: bool,
  13950     value_gz: *GenZir,
  13951     /// May be `null` if all these blocks would be empty.
  13952     /// If `null`, then `value_gz` must have nothing stacked on it.
  13953     extra_gzs: ?struct {
  13954         /// Must be stacked on `value_gz`.
  13955         align_gz: *GenZir,
  13956         /// Must be stacked on `align_gz`.
  13957         linksection_gz: *GenZir,
  13958         /// Must be stacked on `linksection_gz`, and have nothing stacked on it.
  13959         addrspace_gz: *GenZir,
  13960     },
  13961 ) !void {
  13962     const astgen = value_gz.astgen;
  13963     const gpa = astgen.gpa;
  13964 
  13965     const empty_body: []Zir.Inst.Index = &.{};
  13966     const value_body, const align_body, const linksection_body, const addrspace_body = if (extra_gzs) |e| .{
  13967         value_gz.instructionsSliceUpto(e.align_gz),
  13968         e.align_gz.instructionsSliceUpto(e.linksection_gz),
  13969         e.linksection_gz.instructionsSliceUpto(e.addrspace_gz),
  13970         e.addrspace_gz.instructionsSlice(),
  13971     } else .{ value_gz.instructionsSlice(), empty_body, empty_body, empty_body };
  13972 
  13973     const value_len = astgen.countBodyLenAfterFixups(value_body);
  13974     const align_len = astgen.countBodyLenAfterFixups(align_body);
  13975     const linksection_len = astgen.countBodyLenAfterFixups(linksection_body);
  13976     const addrspace_len = astgen.countBodyLenAfterFixups(addrspace_body);
  13977 
  13978     const src_hash_arr: [4]u32 = @bitCast(src_hash);
  13979 
  13980     const extra: Zir.Inst.Declaration = .{
  13981         .src_hash_0 = src_hash_arr[0],
  13982         .src_hash_1 = src_hash_arr[1],
  13983         .src_hash_2 = src_hash_arr[2],
  13984         .src_hash_3 = src_hash_arr[3],
  13985         .name = switch (name) {
  13986             .named => |tok| @enumFromInt(@intFromEnum(try astgen.identAsString(tok))),
  13987             .named_test => |tok| @enumFromInt(@intFromEnum(try astgen.testNameString(tok))),
  13988             .decltest => |tok| @enumFromInt(str_idx: {
  13989                 const idx = astgen.string_bytes.items.len;
  13990                 try astgen.string_bytes.append(gpa, 0); // indicates this is a test
  13991                 try astgen.appendIdentStr(tok, &astgen.string_bytes);
  13992                 try astgen.string_bytes.append(gpa, 0); // end of the string
  13993                 break :str_idx idx;
  13994             }),
  13995             .unnamed_test => .unnamed_test,
  13996             .@"comptime" => .@"comptime",
  13997             .@"usingnamespace" => .@"usingnamespace",
  13998         },
  13999         .src_line = src_line,
  14000         .src_column = src_column,
  14001         .flags = .{
  14002             .value_body_len = @intCast(value_len),
  14003             .is_pub = is_pub,
  14004             .is_export = is_export,
  14005             .test_is_decltest = name == .decltest,
  14006             .has_align_linksection_addrspace = align_len != 0 or linksection_len != 0 or addrspace_len != 0,
  14007         },
  14008     };
  14009     astgen.instructions.items(.data)[@intFromEnum(decl_inst)].declaration.payload_index = try astgen.addExtra(extra);
  14010     if (extra.flags.has_align_linksection_addrspace) {
  14011         try astgen.extra.appendSlice(gpa, &.{
  14012             align_len,
  14013             linksection_len,
  14014             addrspace_len,
  14015         });
  14016     }
  14017     try astgen.extra.ensureUnusedCapacity(gpa, value_len + align_len + linksection_len + addrspace_len);
  14018     astgen.appendBodyWithFixups(value_body);
  14019     if (extra.flags.has_align_linksection_addrspace) {
  14020         astgen.appendBodyWithFixups(align_body);
  14021         astgen.appendBodyWithFixups(linksection_body);
  14022         astgen.appendBodyWithFixups(addrspace_body);
  14023     }
  14024 
  14025     if (extra_gzs) |e| {
  14026         e.addrspace_gz.unstack();
  14027         e.linksection_gz.unstack();
  14028         e.align_gz.unstack();
  14029     }
  14030     value_gz.unstack();
  14031 }
  14032 
  14033 /// Given a list of instructions, returns a list of all instructions which are a `ref` of one of the originals,
  14034 /// from `astgen.ref_table`, non-recursively. The entries are removed from `astgen.ref_table`, and the returned
  14035 /// slice can then be treated as its own body, to append `ref` instructions to a body other than the one they
  14036 /// would normally exist in.
  14037 ///
  14038 /// This is used when lowering functions. Very rarely, the callconv expression, align expression, etc may reference
  14039 /// function parameters via `&param`; in this case, we need to lower to a `ref` instruction in the callconv/align/etc
  14040 /// body, rather than in the declaration body. However, we don't append these bodies to `extra` until we've evaluated
  14041 /// *all* of the bodies into a big `GenZir` stack. Therefore, we use this function to pull out these per-body `ref`
  14042 /// instructions which must be emitted.
  14043 fn fetchRemoveRefEntries(astgen: *AstGen, param_insts: []const Zir.Inst.Index) ![]Zir.Inst.Index {
  14044     var refs: std.ArrayListUnmanaged(Zir.Inst.Index) = .empty;
  14045     for (param_insts) |param_inst| {
  14046         if (astgen.ref_table.fetchRemove(param_inst)) |kv| {
  14047             try refs.append(astgen.arena, kv.value);
  14048         }
  14049     }
  14050     return refs.items;
  14051 }