zig

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

blob 670dbd75 (568294B) - 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             std.zig.SimpleComptimeReason,
    101             Zir.NullTerminatedString,
    102             // Ast.TokenIndex is missing because it is a u32.
    103             Ast.OptionalTokenIndex,
    104             Ast.Node.Index,
    105             Ast.Node.OptionalIndex,
    106             => @intFromEnum(@field(extra, field.name)),
    107 
    108             Ast.TokenOffset,
    109             Ast.OptionalTokenOffset,
    110             Ast.Node.Offset,
    111             Ast.Node.OptionalOffset,
    112             => @bitCast(@intFromEnum(@field(extra, field.name))),
    113 
    114             i32,
    115             Zir.Inst.Call.Flags,
    116             Zir.Inst.BuiltinCall.Flags,
    117             Zir.Inst.SwitchBlock.Bits,
    118             Zir.Inst.SwitchBlockErrUnion.Bits,
    119             Zir.Inst.FuncFancy.Bits,
    120             Zir.Inst.Param.Type,
    121             Zir.Inst.Func.RetTy,
    122             => @bitCast(@field(extra, field.name)),
    123 
    124             else => @compileError("bad field type"),
    125         };
    126         i += 1;
    127     }
    128 }
    129 
    130 fn reserveExtra(astgen: *AstGen, size: usize) Allocator.Error!u32 {
    131     const extra_index: u32 = @intCast(astgen.extra.items.len);
    132     try astgen.extra.resize(astgen.gpa, extra_index + size);
    133     return extra_index;
    134 }
    135 
    136 fn appendRefs(astgen: *AstGen, refs: []const Zir.Inst.Ref) !void {
    137     return astgen.extra.appendSlice(astgen.gpa, @ptrCast(refs));
    138 }
    139 
    140 fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const Zir.Inst.Ref) void {
    141     astgen.extra.appendSliceAssumeCapacity(@ptrCast(refs));
    142 }
    143 
    144 pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
    145     assert(tree.mode == .zig);
    146 
    147     var arena = std.heap.ArenaAllocator.init(gpa);
    148     defer arena.deinit();
    149 
    150     var nodes_need_rl = try AstRlAnnotate.annotate(gpa, arena.allocator(), tree);
    151     defer nodes_need_rl.deinit(gpa);
    152 
    153     var astgen: AstGen = .{
    154         .gpa = gpa,
    155         .arena = arena.allocator(),
    156         .tree = &tree,
    157         .nodes_need_rl = &nodes_need_rl,
    158         .src_hasher = undefined, // `structDeclInner` for the root struct will set this
    159     };
    160     defer astgen.deinit(gpa);
    161 
    162     // String table index 0 is reserved for `NullTerminatedString.empty`.
    163     try astgen.string_bytes.append(gpa, 0);
    164 
    165     // We expect at least as many ZIR instructions and extra data items
    166     // as AST nodes.
    167     try astgen.instructions.ensureTotalCapacity(gpa, tree.nodes.len);
    168 
    169     // First few indexes of extra are reserved and set at the end.
    170     const reserved_count = @typeInfo(Zir.ExtraIndex).@"enum".fields.len;
    171     try astgen.extra.ensureTotalCapacity(gpa, tree.nodes.len + reserved_count);
    172     astgen.extra.items.len += reserved_count;
    173 
    174     var top_scope: Scope.Top = .{};
    175 
    176     var gz_instructions: std.ArrayListUnmanaged(Zir.Inst.Index) = .empty;
    177     var gen_scope: GenZir = .{
    178         .is_comptime = true,
    179         .parent = &top_scope.base,
    180         .decl_node_index = .root,
    181         .decl_line = 0,
    182         .astgen = &astgen,
    183         .instructions = &gz_instructions,
    184         .instructions_top = 0,
    185     };
    186     defer gz_instructions.deinit(gpa);
    187 
    188     // The AST -> ZIR lowering process assumes an AST that does not have any parse errors.
    189     // Parse errors, or AstGen errors in the root struct, are considered "fatal", so we emit no ZIR.
    190     const fatal = if (tree.errors.len == 0) fatal: {
    191         if (AstGen.structDeclInner(
    192             &gen_scope,
    193             &gen_scope.base,
    194             .root,
    195             tree.containerDeclRoot(),
    196             .auto,
    197             .none,
    198             .parent,
    199         )) |struct_decl_ref| {
    200             assert(struct_decl_ref.toIndex().? == .main_struct_inst);
    201             break :fatal false;
    202         } else |err| switch (err) {
    203             error.OutOfMemory => return error.OutOfMemory,
    204             error.AnalysisFail => break :fatal true, // Handled via compile_errors below.
    205         }
    206     } else fatal: {
    207         try lowerAstErrors(&astgen);
    208         break :fatal true;
    209     };
    210 
    211     const err_index = @intFromEnum(Zir.ExtraIndex.compile_errors);
    212     if (astgen.compile_errors.items.len == 0) {
    213         astgen.extra.items[err_index] = 0;
    214     } else {
    215         try astgen.extra.ensureUnusedCapacity(gpa, 1 + astgen.compile_errors.items.len *
    216             @typeInfo(Zir.Inst.CompileErrors.Item).@"struct".fields.len);
    217 
    218         astgen.extra.items[err_index] = astgen.addExtraAssumeCapacity(Zir.Inst.CompileErrors{
    219             .items_len = @intCast(astgen.compile_errors.items.len),
    220         });
    221 
    222         for (astgen.compile_errors.items) |item| {
    223             _ = astgen.addExtraAssumeCapacity(item);
    224         }
    225     }
    226 
    227     const imports_index = @intFromEnum(Zir.ExtraIndex.imports);
    228     if (astgen.imports.count() == 0) {
    229         astgen.extra.items[imports_index] = 0;
    230     } else {
    231         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Imports).@"struct".fields.len +
    232             astgen.imports.count() * @typeInfo(Zir.Inst.Imports.Item).@"struct".fields.len);
    233 
    234         astgen.extra.items[imports_index] = astgen.addExtraAssumeCapacity(Zir.Inst.Imports{
    235             .imports_len = @intCast(astgen.imports.count()),
    236         });
    237 
    238         var it = astgen.imports.iterator();
    239         while (it.next()) |entry| {
    240             _ = astgen.addExtraAssumeCapacity(Zir.Inst.Imports.Item{
    241                 .name = entry.key_ptr.*,
    242                 .token = entry.value_ptr.*,
    243             });
    244         }
    245     }
    246 
    247     return .{
    248         .instructions = if (fatal) .empty else astgen.instructions.toOwnedSlice(),
    249         .string_bytes = try astgen.string_bytes.toOwnedSlice(gpa),
    250         .extra = try astgen.extra.toOwnedSlice(gpa),
    251     };
    252 }
    253 
    254 fn deinit(astgen: *AstGen, gpa: Allocator) void {
    255     astgen.instructions.deinit(gpa);
    256     astgen.extra.deinit(gpa);
    257     astgen.string_table.deinit(gpa);
    258     astgen.string_bytes.deinit(gpa);
    259     astgen.compile_errors.deinit(gpa);
    260     astgen.imports.deinit(gpa);
    261     astgen.scratch.deinit(gpa);
    262     astgen.ref_table.deinit(gpa);
    263 }
    264 
    265 const ResultInfo = struct {
    266     /// The semantics requested for the result location
    267     rl: Loc,
    268 
    269     /// The "operator" consuming the result location
    270     ctx: Context = .none,
    271 
    272     /// Turns a `coerced_ty` back into a `ty`. Should be called at branch points
    273     /// such as if and switch expressions.
    274     fn br(ri: ResultInfo) ResultInfo {
    275         return switch (ri.rl) {
    276             .coerced_ty => |ty| .{
    277                 .rl = .{ .ty = ty },
    278                 .ctx = ri.ctx,
    279             },
    280             else => ri,
    281         };
    282     }
    283 
    284     fn zirTag(ri: ResultInfo) Zir.Inst.Tag {
    285         switch (ri.rl) {
    286             .ty => return switch (ri.ctx) {
    287                 .shift_op => .as_shift_operand,
    288                 else => .as_node,
    289             },
    290             else => unreachable,
    291         }
    292     }
    293 
    294     const Loc = union(enum) {
    295         /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
    296         /// expression should be generated. The result instruction from the expression must
    297         /// be ignored.
    298         discard,
    299         /// The expression has an inferred type, and it will be evaluated as an rvalue.
    300         none,
    301         /// The expression will be coerced into this type, but it will be evaluated as an rvalue.
    302         ty: Zir.Inst.Ref,
    303         /// Same as `ty` but it is guaranteed that Sema will additionally perform the coercion,
    304         /// so no `as` instruction needs to be emitted.
    305         coerced_ty: Zir.Inst.Ref,
    306         /// The expression must generate a pointer rather than a value. For example, the left hand side
    307         /// of an assignment uses this kind of result location.
    308         ref,
    309         /// The expression must generate a pointer rather than a value, and the pointer will be coerced
    310         /// by other code to this type, which is guaranteed by earlier instructions to be a pointer type.
    311         ref_coerced_ty: Zir.Inst.Ref,
    312         /// The expression must store its result into this typed pointer. The result instruction
    313         /// from the expression must be ignored.
    314         ptr: PtrResultLoc,
    315         /// The expression must store its result into this allocation, which has an inferred type.
    316         /// The result instruction from the expression must be ignored.
    317         /// Always an instruction with tag `alloc_inferred`.
    318         inferred_ptr: Zir.Inst.Ref,
    319         /// The expression has a sequence of pointers to store its results into due to a destructure
    320         /// operation. Each of these pointers may or may not have an inferred type.
    321         destructure: struct {
    322             /// The AST node of the destructure operation itself.
    323             src_node: Ast.Node.Index,
    324             /// The pointers to store results into.
    325             components: []const DestructureComponent,
    326         },
    327 
    328         const DestructureComponent = union(enum) {
    329             typed_ptr: PtrResultLoc,
    330             inferred_ptr: Zir.Inst.Ref,
    331             discard,
    332         };
    333 
    334         const PtrResultLoc = struct {
    335             inst: Zir.Inst.Ref,
    336             src_node: ?Ast.Node.Index = null,
    337         };
    338 
    339         /// Find the result type for a cast builtin given the result location.
    340         /// If the location does not have a known result type, returns `null`.
    341         fn resultType(rl: Loc, gz: *GenZir, node: Ast.Node.Index) !?Zir.Inst.Ref {
    342             return switch (rl) {
    343                 .discard, .none, .ref, .inferred_ptr, .destructure => null,
    344                 .ty, .coerced_ty => |ty_ref| ty_ref,
    345                 .ref_coerced_ty => |ptr_ty| try gz.addUnNode(.elem_type, ptr_ty, node),
    346                 .ptr => |ptr| {
    347                     const ptr_ty = try gz.addUnNode(.typeof, ptr.inst, node);
    348                     return try gz.addUnNode(.elem_type, ptr_ty, node);
    349                 },
    350             };
    351         }
    352 
    353         /// Find the result type for a cast builtin given the result location.
    354         /// If the location does not have a known result type, emits an error on
    355         /// the given node.
    356         fn resultTypeForCast(rl: Loc, gz: *GenZir, node: Ast.Node.Index, builtin_name: []const u8) !Zir.Inst.Ref {
    357             const astgen = gz.astgen;
    358             if (try rl.resultType(gz, node)) |ty| return ty;
    359             switch (rl) {
    360                 .destructure => |destructure| return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{
    361                     try astgen.errNoteNode(destructure.src_node, "destructure expressions do not provide a single result type", .{}),
    362                     try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}),
    363                 }),
    364                 else => return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{
    365                     try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}),
    366                 }),
    367             }
    368         }
    369     };
    370 
    371     const Context = enum {
    372         /// The expression is the operand to a return expression.
    373         @"return",
    374         /// The expression is the input to an error-handling operator (if-else, try, or catch).
    375         error_handling_expr,
    376         /// The expression is the right-hand side of a shift operation.
    377         shift_op,
    378         /// The expression is an argument in a function call.
    379         fn_arg,
    380         /// The expression is the right-hand side of an initializer for a `const` variable
    381         const_init,
    382         /// The expression is the right-hand side of an assignment expression.
    383         assignment,
    384         /// No specific operator in particular.
    385         none,
    386     };
    387 };
    388 
    389 const coerced_align_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .u29_type } };
    390 const coerced_linksection_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .slice_const_u8_type } };
    391 const coerced_type_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .type_type } };
    392 const coerced_bool_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .bool_type } };
    393 
    394 fn typeExpr(gz: *GenZir, scope: *Scope, type_node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
    395     return comptimeExpr(gz, scope, coerced_type_ri, type_node, .type);
    396 }
    397 
    398 fn reachableTypeExpr(
    399     gz: *GenZir,
    400     scope: *Scope,
    401     type_node: Ast.Node.Index,
    402     reachable_node: Ast.Node.Index,
    403 ) InnerError!Zir.Inst.Ref {
    404     return reachableExprComptime(gz, scope, coerced_type_ri, type_node, reachable_node, .type);
    405 }
    406 
    407 /// Same as `expr` but fails with a compile error if the result type is `noreturn`.
    408 fn reachableExpr(
    409     gz: *GenZir,
    410     scope: *Scope,
    411     ri: ResultInfo,
    412     node: Ast.Node.Index,
    413     reachable_node: Ast.Node.Index,
    414 ) InnerError!Zir.Inst.Ref {
    415     return reachableExprComptime(gz, scope, ri, node, reachable_node, null);
    416 }
    417 
    418 fn reachableExprComptime(
    419     gz: *GenZir,
    420     scope: *Scope,
    421     ri: ResultInfo,
    422     node: Ast.Node.Index,
    423     reachable_node: Ast.Node.Index,
    424     /// If `null`, the expression is not evaluated in a comptime context.
    425     comptime_reason: ?std.zig.SimpleComptimeReason,
    426 ) InnerError!Zir.Inst.Ref {
    427     const result_inst = if (comptime_reason) |r|
    428         try comptimeExpr(gz, scope, ri, node, r)
    429     else
    430         try expr(gz, scope, ri, node);
    431 
    432     if (gz.refIsNoReturn(result_inst)) {
    433         try gz.astgen.appendErrorNodeNotes(reachable_node, "unreachable code", .{}, &[_]u32{
    434             try gz.astgen.errNoteNode(node, "control flow is diverted here", .{}),
    435         });
    436     }
    437     return result_inst;
    438 }
    439 
    440 fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
    441     const astgen = gz.astgen;
    442     const tree = astgen.tree;
    443     switch (tree.nodeTag(node)) {
    444         .root => unreachable,
    445         .test_decl => unreachable,
    446         .global_var_decl => unreachable,
    447         .local_var_decl => unreachable,
    448         .simple_var_decl => unreachable,
    449         .aligned_var_decl => unreachable,
    450         .switch_case => unreachable,
    451         .switch_case_inline => unreachable,
    452         .switch_case_one => unreachable,
    453         .switch_case_inline_one => unreachable,
    454         .container_field_init => unreachable,
    455         .container_field_align => unreachable,
    456         .container_field => unreachable,
    457         .asm_output => unreachable,
    458         .asm_input => unreachable,
    459 
    460         .assign,
    461         .assign_destructure,
    462         .assign_bit_and,
    463         .assign_bit_or,
    464         .assign_shl,
    465         .assign_shl_sat,
    466         .assign_shr,
    467         .assign_bit_xor,
    468         .assign_div,
    469         .assign_sub,
    470         .assign_sub_wrap,
    471         .assign_sub_sat,
    472         .assign_mod,
    473         .assign_add,
    474         .assign_add_wrap,
    475         .assign_add_sat,
    476         .assign_mul,
    477         .assign_mul_wrap,
    478         .assign_mul_sat,
    479         .add,
    480         .add_wrap,
    481         .add_sat,
    482         .sub,
    483         .sub_wrap,
    484         .sub_sat,
    485         .mul,
    486         .mul_wrap,
    487         .mul_sat,
    488         .div,
    489         .mod,
    490         .bit_and,
    491         .bit_or,
    492         .shl,
    493         .shl_sat,
    494         .shr,
    495         .bit_xor,
    496         .bang_equal,
    497         .equal_equal,
    498         .greater_than,
    499         .greater_or_equal,
    500         .less_than,
    501         .less_or_equal,
    502         .array_cat,
    503         .array_mult,
    504         .bool_and,
    505         .bool_or,
    506         .@"asm",
    507         .asm_simple,
    508         .asm_legacy,
    509         .string_literal,
    510         .number_literal,
    511         .call,
    512         .call_comma,
    513         .call_one,
    514         .call_one_comma,
    515         .unreachable_literal,
    516         .@"return",
    517         .@"if",
    518         .if_simple,
    519         .@"while",
    520         .while_simple,
    521         .while_cont,
    522         .bool_not,
    523         .address_of,
    524         .optional_type,
    525         .block,
    526         .block_semicolon,
    527         .block_two,
    528         .block_two_semicolon,
    529         .@"break",
    530         .ptr_type_aligned,
    531         .ptr_type_sentinel,
    532         .ptr_type,
    533         .ptr_type_bit_range,
    534         .array_type,
    535         .array_type_sentinel,
    536         .enum_literal,
    537         .multiline_string_literal,
    538         .char_literal,
    539         .@"defer",
    540         .@"errdefer",
    541         .@"catch",
    542         .error_union,
    543         .merge_error_sets,
    544         .switch_range,
    545         .for_range,
    546         .bit_not,
    547         .negation,
    548         .negation_wrap,
    549         .@"resume",
    550         .@"try",
    551         .slice,
    552         .slice_open,
    553         .slice_sentinel,
    554         .array_init_one,
    555         .array_init_one_comma,
    556         .array_init_dot_two,
    557         .array_init_dot_two_comma,
    558         .array_init_dot,
    559         .array_init_dot_comma,
    560         .array_init,
    561         .array_init_comma,
    562         .struct_init_one,
    563         .struct_init_one_comma,
    564         .struct_init_dot_two,
    565         .struct_init_dot_two_comma,
    566         .struct_init_dot,
    567         .struct_init_dot_comma,
    568         .struct_init,
    569         .struct_init_comma,
    570         .@"switch",
    571         .switch_comma,
    572         .@"for",
    573         .for_simple,
    574         .@"suspend",
    575         .@"continue",
    576         .fn_proto_simple,
    577         .fn_proto_multi,
    578         .fn_proto_one,
    579         .fn_proto,
    580         .fn_decl,
    581         .anyframe_type,
    582         .anyframe_literal,
    583         .error_set_decl,
    584         .container_decl,
    585         .container_decl_trailing,
    586         .container_decl_two,
    587         .container_decl_two_trailing,
    588         .container_decl_arg,
    589         .container_decl_arg_trailing,
    590         .tagged_union,
    591         .tagged_union_trailing,
    592         .tagged_union_two,
    593         .tagged_union_two_trailing,
    594         .tagged_union_enum_tag,
    595         .tagged_union_enum_tag_trailing,
    596         .@"comptime",
    597         .@"nosuspend",
    598         .error_value,
    599         => return astgen.failNode(node, "invalid left-hand side to assignment", .{}),
    600 
    601         .builtin_call,
    602         .builtin_call_comma,
    603         .builtin_call_two,
    604         .builtin_call_two_comma,
    605         => {
    606             const builtin_token = tree.nodeMainToken(node);
    607             const builtin_name = tree.tokenSlice(builtin_token);
    608             // If the builtin is an invalid name, we don't cause an error here; instead
    609             // let it pass, and the error will be "invalid builtin function" later.
    610             if (BuiltinFn.list.get(builtin_name)) |info| {
    611                 if (!info.allows_lvalue) {
    612                     return astgen.failNode(node, "invalid left-hand side to assignment", .{});
    613                 }
    614             }
    615         },
    616 
    617         // These can be assigned to.
    618         .unwrap_optional,
    619         .deref,
    620         .field_access,
    621         .array_access,
    622         .identifier,
    623         .grouped_expression,
    624         .@"orelse",
    625         => {},
    626     }
    627     return expr(gz, scope, .{ .rl = .ref }, node);
    628 }
    629 
    630 /// Turn Zig AST into untyped ZIR instructions.
    631 /// When `rl` is discard, ptr, inferred_ptr, or inferred_ptr, the
    632 /// result instruction can be used to inspect whether it is isNoReturn() but that is it,
    633 /// it must otherwise not be used.
    634 fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
    635     const astgen = gz.astgen;
    636     const tree = astgen.tree;
    637 
    638     switch (tree.nodeTag(node)) {
    639         .root => unreachable, // Top-level declaration.
    640         .test_decl => unreachable, // Top-level declaration.
    641         .container_field_init => unreachable, // Top-level declaration.
    642         .container_field_align => unreachable, // Top-level declaration.
    643         .container_field => unreachable, // Top-level declaration.
    644         .fn_decl => unreachable, // Top-level declaration.
    645 
    646         .global_var_decl => unreachable, // Handled in `blockExpr`.
    647         .local_var_decl => unreachable, // Handled in `blockExpr`.
    648         .simple_var_decl => unreachable, // Handled in `blockExpr`.
    649         .aligned_var_decl => unreachable, // Handled in `blockExpr`.
    650         .@"defer" => unreachable, // Handled in `blockExpr`.
    651         .@"errdefer" => unreachable, // Handled in `blockExpr`.
    652 
    653         .switch_case => unreachable, // Handled in `switchExpr`.
    654         .switch_case_inline => unreachable, // Handled in `switchExpr`.
    655         .switch_case_one => unreachable, // Handled in `switchExpr`.
    656         .switch_case_inline_one => unreachable, // Handled in `switchExpr`.
    657         .switch_range => unreachable, // Handled in `switchExpr`.
    658 
    659         .asm_output => unreachable, // Handled in `asmExpr`.
    660         .asm_input => unreachable, // Handled in `asmExpr`.
    661 
    662         .for_range => unreachable, // Handled in `forExpr`.
    663 
    664         .assign => {
    665             try assign(gz, scope, node);
    666             return rvalue(gz, ri, .void_value, node);
    667         },
    668 
    669         .assign_destructure => {
    670             // Note that this variant does not declare any new var/const: that
    671             // variant is handled by `blockExprStmts`.
    672             try assignDestructure(gz, scope, node);
    673             return rvalue(gz, ri, .void_value, node);
    674         },
    675 
    676         .assign_shl => {
    677             try assignShift(gz, scope, node, .shl);
    678             return rvalue(gz, ri, .void_value, node);
    679         },
    680         .assign_shl_sat => {
    681             try assignShiftSat(gz, scope, node);
    682             return rvalue(gz, ri, .void_value, node);
    683         },
    684         .assign_shr => {
    685             try assignShift(gz, scope, node, .shr);
    686             return rvalue(gz, ri, .void_value, node);
    687         },
    688 
    689         .assign_bit_and => {
    690             try assignOp(gz, scope, node, .bit_and);
    691             return rvalue(gz, ri, .void_value, node);
    692         },
    693         .assign_bit_or => {
    694             try assignOp(gz, scope, node, .bit_or);
    695             return rvalue(gz, ri, .void_value, node);
    696         },
    697         .assign_bit_xor => {
    698             try assignOp(gz, scope, node, .xor);
    699             return rvalue(gz, ri, .void_value, node);
    700         },
    701         .assign_div => {
    702             try assignOp(gz, scope, node, .div);
    703             return rvalue(gz, ri, .void_value, node);
    704         },
    705         .assign_sub => {
    706             try assignOp(gz, scope, node, .sub);
    707             return rvalue(gz, ri, .void_value, node);
    708         },
    709         .assign_sub_wrap => {
    710             try assignOp(gz, scope, node, .subwrap);
    711             return rvalue(gz, ri, .void_value, node);
    712         },
    713         .assign_sub_sat => {
    714             try assignOp(gz, scope, node, .sub_sat);
    715             return rvalue(gz, ri, .void_value, node);
    716         },
    717         .assign_mod => {
    718             try assignOp(gz, scope, node, .mod_rem);
    719             return rvalue(gz, ri, .void_value, node);
    720         },
    721         .assign_add => {
    722             try assignOp(gz, scope, node, .add);
    723             return rvalue(gz, ri, .void_value, node);
    724         },
    725         .assign_add_wrap => {
    726             try assignOp(gz, scope, node, .addwrap);
    727             return rvalue(gz, ri, .void_value, node);
    728         },
    729         .assign_add_sat => {
    730             try assignOp(gz, scope, node, .add_sat);
    731             return rvalue(gz, ri, .void_value, node);
    732         },
    733         .assign_mul => {
    734             try assignOp(gz, scope, node, .mul);
    735             return rvalue(gz, ri, .void_value, node);
    736         },
    737         .assign_mul_wrap => {
    738             try assignOp(gz, scope, node, .mulwrap);
    739             return rvalue(gz, ri, .void_value, node);
    740         },
    741         .assign_mul_sat => {
    742             try assignOp(gz, scope, node, .mul_sat);
    743             return rvalue(gz, ri, .void_value, node);
    744         },
    745 
    746         // zig fmt: off
    747         .shl => return shiftOp(gz, scope, ri, node, tree.nodeData(node).node_and_node[0], tree.nodeData(node).node_and_node[1], .shl),
    748         .shr => return shiftOp(gz, scope, ri, node, tree.nodeData(node).node_and_node[0], tree.nodeData(node).node_and_node[1], .shr),
    749 
    750         .add      => return simpleBinOp(gz, scope, ri, node, .add),
    751         .add_wrap => return simpleBinOp(gz, scope, ri, node, .addwrap),
    752         .add_sat  => return simpleBinOp(gz, scope, ri, node, .add_sat),
    753         .sub      => return simpleBinOp(gz, scope, ri, node, .sub),
    754         .sub_wrap => return simpleBinOp(gz, scope, ri, node, .subwrap),
    755         .sub_sat  => return simpleBinOp(gz, scope, ri, node, .sub_sat),
    756         .mul      => return simpleBinOp(gz, scope, ri, node, .mul),
    757         .mul_wrap => return simpleBinOp(gz, scope, ri, node, .mulwrap),
    758         .mul_sat  => return simpleBinOp(gz, scope, ri, node, .mul_sat),
    759         .div      => return simpleBinOp(gz, scope, ri, node, .div),
    760         .mod      => return simpleBinOp(gz, scope, ri, node, .mod_rem),
    761         .shl_sat  => return simpleBinOp(gz, scope, ri, node, .shl_sat),
    762 
    763         .bit_and          => return simpleBinOp(gz, scope, ri, node, .bit_and),
    764         .bit_or           => return simpleBinOp(gz, scope, ri, node, .bit_or),
    765         .bit_xor          => return simpleBinOp(gz, scope, ri, node, .xor),
    766         .bang_equal       => return simpleBinOp(gz, scope, ri, node, .cmp_neq),
    767         .equal_equal      => return simpleBinOp(gz, scope, ri, node, .cmp_eq),
    768         .greater_than     => return simpleBinOp(gz, scope, ri, node, .cmp_gt),
    769         .greater_or_equal => return simpleBinOp(gz, scope, ri, node, .cmp_gte),
    770         .less_than        => return simpleBinOp(gz, scope, ri, node, .cmp_lt),
    771         .less_or_equal    => return simpleBinOp(gz, scope, ri, node, .cmp_lte),
    772         .array_cat        => return simpleBinOp(gz, scope, ri, node, .array_cat),
    773 
    774         .array_mult => {
    775             // This syntax form does not currently use the result type in the language specification.
    776             // However, the result type can be used to emit more optimal code for large multiplications by
    777             // having Sema perform a coercion before the multiplication operation.
    778             const lhs_node, const rhs_node = tree.nodeData(node).node_and_node;
    779             const result = try gz.addPlNode(.array_mul, node, Zir.Inst.ArrayMul{
    780                 .res_ty = if (try ri.rl.resultType(gz, node)) |t| t else .none,
    781                 .lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node),
    782                 .rhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, rhs_node, .array_mul_factor),
    783             });
    784             return rvalue(gz, ri, result, node);
    785         },
    786 
    787         .error_union, .merge_error_sets => |tag| {
    788             const inst_tag: Zir.Inst.Tag = switch (tag) {
    789                 .error_union => .error_union_type,
    790                 .merge_error_sets => .merge_error_sets,
    791                 else => unreachable,
    792             };
    793             const lhs_node, const rhs_node = tree.nodeData(node).node_and_node;
    794             const lhs = try reachableTypeExpr(gz, scope, lhs_node, node);
    795             const rhs = try reachableTypeExpr(gz, scope, rhs_node, node);
    796             const result = try gz.addPlNode(inst_tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
    797             return rvalue(gz, ri, result, node);
    798         },
    799 
    800         .bool_and => return boolBinOp(gz, scope, ri, node, .bool_br_and),
    801         .bool_or  => return boolBinOp(gz, scope, ri, node, .bool_br_or),
    802 
    803         .bool_not => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, tree.nodeData(node).node, .bool_not),
    804         .bit_not  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, tree.nodeData(node).node, .bit_not),
    805 
    806         .negation      => return   negation(gz, scope, ri, node),
    807         .negation_wrap => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, tree.nodeData(node).node, .negate_wrap),
    808 
    809         .identifier => return identifier(gz, scope, ri, node, null),
    810 
    811         .asm_simple,
    812         .@"asm",
    813         => return asmExpr(gz, scope, ri, node, tree.fullAsm(node).?),
    814 
    815         .asm_legacy => {
    816             return astgen.failNodeNotes(node, "legacy asm clobbers syntax", .{}, &[_]u32{
    817                 try astgen.errNoteNode(node, "use 'zig fmt' to auto-upgrade", .{}),
    818             });
    819         },
    820 
    821         .string_literal           => return stringLiteral(gz, ri, node),
    822         .multiline_string_literal => return multilineStringLiteral(gz, ri, node),
    823 
    824         .number_literal => return numberLiteral(gz, ri, node, node, .positive),
    825         // zig fmt: on
    826 
    827         .builtin_call_two,
    828         .builtin_call_two_comma,
    829         .builtin_call,
    830         .builtin_call_comma,
    831         => {
    832             var buf: [2]Ast.Node.Index = undefined;
    833             const params = tree.builtinCallParams(&buf, node).?;
    834             return builtinCall(gz, scope, ri, node, params, false);
    835         },
    836 
    837         .call_one,
    838         .call_one_comma,
    839         .call,
    840         .call_comma,
    841         => {
    842             var buf: [1]Ast.Node.Index = undefined;
    843             return callExpr(gz, scope, ri, .none, node, tree.fullCall(&buf, node).?);
    844         },
    845 
    846         .unreachable_literal => {
    847             try emitDbgNode(gz, node);
    848             _ = try gz.addAsIndex(.{
    849                 .tag = .@"unreachable",
    850                 .data = .{ .@"unreachable" = .{
    851                     .src_node = gz.nodeIndexToRelative(node),
    852                 } },
    853             });
    854             return Zir.Inst.Ref.unreachable_value;
    855         },
    856         .@"return" => return ret(gz, scope, node),
    857         .field_access => return fieldAccess(gz, scope, ri, node),
    858 
    859         .if_simple,
    860         .@"if",
    861         => {
    862             const if_full = tree.fullIf(node).?;
    863             no_switch_on_err: {
    864                 const error_token = if_full.error_token orelse break :no_switch_on_err;
    865                 const else_node = if_full.ast.else_expr.unwrap() orelse break :no_switch_on_err;
    866                 const full_switch = tree.fullSwitch(else_node) orelse break :no_switch_on_err;
    867                 if (full_switch.label_token != null) break :no_switch_on_err;
    868                 if (tree.nodeTag(full_switch.ast.condition) != .identifier) break :no_switch_on_err;
    869                 if (!mem.eql(u8, tree.tokenSlice(error_token), tree.tokenSlice(tree.nodeMainToken(full_switch.ast.condition)))) break :no_switch_on_err;
    870                 return switchExprErrUnion(gz, scope, ri.br(), node, .@"if");
    871             }
    872             return ifExpr(gz, scope, ri.br(), node, if_full);
    873         },
    874 
    875         .while_simple,
    876         .while_cont,
    877         .@"while",
    878         => return whileExpr(gz, scope, ri.br(), node, tree.fullWhile(node).?, false),
    879 
    880         .for_simple, .@"for" => return forExpr(gz, scope, ri.br(), node, tree.fullFor(node).?, false),
    881 
    882         .slice_open,
    883         .slice,
    884         .slice_sentinel,
    885         => {
    886             const full = tree.fullSlice(node).?;
    887             if (full.ast.end != .none and
    888                 tree.nodeTag(full.ast.sliced) == .slice_open and
    889                 nodeIsTriviallyZero(tree, full.ast.start))
    890             {
    891                 const lhs_extra = tree.sliceOpen(full.ast.sliced).ast;
    892 
    893                 const lhs = try expr(gz, scope, .{ .rl = .ref }, lhs_extra.sliced);
    894                 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start);
    895                 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
    896                 const len = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, full.ast.end.unwrap().?);
    897                 const sentinel = if (full.ast.sentinel.unwrap()) |sentinel| try expr(gz, scope, .{ .rl = .none }, sentinel) else .none;
    898                 try emitDbgStmt(gz, cursor);
    899                 const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{
    900                     .lhs = lhs,
    901                     .start = start,
    902                     .len = len,
    903                     .start_src_node_offset = gz.nodeIndexToRelative(full.ast.sliced),
    904                     .sentinel = sentinel,
    905                 });
    906                 return rvalue(gz, ri, result, node);
    907             }
    908             const lhs = try expr(gz, scope, .{ .rl = .ref }, full.ast.sliced);
    909 
    910             const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
    911             const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, full.ast.start);
    912             const end = if (full.ast.end.unwrap()) |end| try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, end) else .none;
    913             const sentinel = if (full.ast.sentinel.unwrap()) |sentinel| s: {
    914                 const sentinel_ty = try gz.addUnNode(.slice_sentinel_ty, lhs, node);
    915                 break :s try expr(gz, scope, .{ .rl = .{ .coerced_ty = sentinel_ty } }, sentinel);
    916             } else .none;
    917             try emitDbgStmt(gz, cursor);
    918             if (sentinel != .none) {
    919                 const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{
    920                     .lhs = lhs,
    921                     .start = start,
    922                     .end = end,
    923                     .sentinel = sentinel,
    924                 });
    925                 return rvalue(gz, ri, result, node);
    926             } else if (end != .none) {
    927                 const result = try gz.addPlNode(.slice_end, node, Zir.Inst.SliceEnd{
    928                     .lhs = lhs,
    929                     .start = start,
    930                     .end = end,
    931                 });
    932                 return rvalue(gz, ri, result, node);
    933             } else {
    934                 const result = try gz.addPlNode(.slice_start, node, Zir.Inst.SliceStart{
    935                     .lhs = lhs,
    936                     .start = start,
    937                 });
    938                 return rvalue(gz, ri, result, node);
    939             }
    940         },
    941 
    942         .deref => {
    943             const lhs = try expr(gz, scope, .{ .rl = .none }, tree.nodeData(node).node);
    944             _ = try gz.addUnNode(.validate_deref, lhs, node);
    945             switch (ri.rl) {
    946                 .ref, .ref_coerced_ty => return lhs,
    947                 else => {
    948                     const result = try gz.addUnNode(.load, lhs, node);
    949                     return rvalue(gz, ri, result, node);
    950                 },
    951             }
    952         },
    953         .address_of => {
    954             const operand_rl: ResultInfo.Loc = if (try ri.rl.resultType(gz, node)) |res_ty_inst| rl: {
    955                 _ = try gz.addUnTok(.validate_ref_ty, res_ty_inst, tree.firstToken(node));
    956                 break :rl .{ .ref_coerced_ty = res_ty_inst };
    957             } else .ref;
    958             const result = try expr(gz, scope, .{ .rl = operand_rl }, tree.nodeData(node).node);
    959             return rvalue(gz, ri, result, node);
    960         },
    961         .optional_type => {
    962             const operand = try typeExpr(gz, scope, tree.nodeData(node).node);
    963             const result = try gz.addUnNode(.optional_type, operand, node);
    964             return rvalue(gz, ri, result, node);
    965         },
    966         .unwrap_optional => switch (ri.rl) {
    967             .ref, .ref_coerced_ty => {
    968                 const lhs = try expr(gz, scope, .{ .rl = .ref }, tree.nodeData(node).node_and_token[0]);
    969 
    970                 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
    971                 try emitDbgStmt(gz, cursor);
    972 
    973                 return gz.addUnNode(.optional_payload_safe_ptr, lhs, node);
    974             },
    975             else => {
    976                 const lhs = try expr(gz, scope, .{ .rl = .none }, tree.nodeData(node).node_and_token[0]);
    977 
    978                 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
    979                 try emitDbgStmt(gz, cursor);
    980 
    981                 return rvalue(gz, ri, try gz.addUnNode(.optional_payload_safe, lhs, node), node);
    982             },
    983         },
    984         .block_two,
    985         .block_two_semicolon,
    986         .block,
    987         .block_semicolon,
    988         => {
    989             var buf: [2]Ast.Node.Index = undefined;
    990             const statements = tree.blockStatements(&buf, node).?;
    991             return blockExpr(gz, scope, ri, node, statements, .normal);
    992         },
    993         .enum_literal => if (try ri.rl.resultType(gz, node)) |res_ty| {
    994             const str_index = try astgen.identAsString(tree.nodeMainToken(node));
    995             const res = try gz.addPlNode(.decl_literal, node, Zir.Inst.Field{
    996                 .lhs = res_ty,
    997                 .field_name_start = str_index,
    998             });
    999             switch (ri.rl) {
   1000                 .discard, .none, .ref => unreachable, // no result type
   1001                 .ty, .coerced_ty => return res, // `decl_literal` does the coercion for us
   1002                 .ref_coerced_ty, .ptr, .inferred_ptr, .destructure => return rvalue(gz, ri, res, node),
   1003             }
   1004         } else return simpleStrTok(gz, ri, tree.nodeMainToken(node), node, .enum_literal),
   1005         .error_value => return simpleStrTok(gz, ri, tree.nodeMainToken(node) + 2, node, .error_value),
   1006         // TODO restore this when implementing https://github.com/ziglang/zig/issues/6025
   1007         // .anyframe_literal => return rvalue(gz, ri, .anyframe_type, node),
   1008         .anyframe_literal => {
   1009             const result = try gz.addUnNode(.anyframe_type, .void_type, node);
   1010             return rvalue(gz, ri, result, node);
   1011         },
   1012         .anyframe_type => {
   1013             const return_type = try typeExpr(gz, scope, tree.nodeData(node).token_and_node[1]);
   1014             const result = try gz.addUnNode(.anyframe_type, return_type, node);
   1015             return rvalue(gz, ri, result, node);
   1016         },
   1017         .@"catch" => {
   1018             const catch_token = tree.nodeMainToken(node);
   1019             const payload_token: ?Ast.TokenIndex = if (tree.tokenTag(catch_token + 1) == .pipe)
   1020                 catch_token + 2
   1021             else
   1022                 null;
   1023             no_switch_on_err: {
   1024                 const capture_token = payload_token orelse break :no_switch_on_err;
   1025                 const full_switch = tree.fullSwitch(tree.nodeData(node).node_and_node[1]) orelse break :no_switch_on_err;
   1026                 if (full_switch.label_token != null) break :no_switch_on_err;
   1027                 if (tree.nodeTag(full_switch.ast.condition) != .identifier) break :no_switch_on_err;
   1028                 if (!mem.eql(u8, tree.tokenSlice(capture_token), tree.tokenSlice(tree.nodeMainToken(full_switch.ast.condition)))) break :no_switch_on_err;
   1029                 return switchExprErrUnion(gz, scope, ri.br(), node, .@"catch");
   1030             }
   1031             switch (ri.rl) {
   1032                 .ref, .ref_coerced_ty => return orelseCatchExpr(
   1033                     gz,
   1034                     scope,
   1035                     ri,
   1036                     node,
   1037                     .is_non_err_ptr,
   1038                     .err_union_payload_unsafe_ptr,
   1039                     .err_union_code_ptr,
   1040                     payload_token,
   1041                 ),
   1042                 else => return orelseCatchExpr(
   1043                     gz,
   1044                     scope,
   1045                     ri,
   1046                     node,
   1047                     .is_non_err,
   1048                     .err_union_payload_unsafe,
   1049                     .err_union_code,
   1050                     payload_token,
   1051                 ),
   1052             }
   1053         },
   1054         .@"orelse" => switch (ri.rl) {
   1055             .ref, .ref_coerced_ty => return orelseCatchExpr(
   1056                 gz,
   1057                 scope,
   1058                 ri,
   1059                 node,
   1060                 .is_non_null_ptr,
   1061                 .optional_payload_unsafe_ptr,
   1062                 undefined,
   1063                 null,
   1064             ),
   1065             else => return orelseCatchExpr(
   1066                 gz,
   1067                 scope,
   1068                 ri,
   1069                 node,
   1070                 .is_non_null,
   1071                 .optional_payload_unsafe,
   1072                 undefined,
   1073                 null,
   1074             ),
   1075         },
   1076 
   1077         .ptr_type_aligned,
   1078         .ptr_type_sentinel,
   1079         .ptr_type,
   1080         .ptr_type_bit_range,
   1081         => return ptrType(gz, scope, ri, node, tree.fullPtrType(node).?),
   1082 
   1083         .container_decl,
   1084         .container_decl_trailing,
   1085         .container_decl_arg,
   1086         .container_decl_arg_trailing,
   1087         .container_decl_two,
   1088         .container_decl_two_trailing,
   1089         .tagged_union,
   1090         .tagged_union_trailing,
   1091         .tagged_union_enum_tag,
   1092         .tagged_union_enum_tag_trailing,
   1093         .tagged_union_two,
   1094         .tagged_union_two_trailing,
   1095         => {
   1096             var buf: [2]Ast.Node.Index = undefined;
   1097             return containerDecl(gz, scope, ri, node, tree.fullContainerDecl(&buf, node).?, .anon);
   1098         },
   1099 
   1100         .@"break" => return breakExpr(gz, scope, node),
   1101         .@"continue" => return continueExpr(gz, scope, node),
   1102         .grouped_expression => return expr(gz, scope, ri, tree.nodeData(node).node_and_token[0]),
   1103         .array_type => return arrayType(gz, scope, ri, node),
   1104         .array_type_sentinel => return arrayTypeSentinel(gz, scope, ri, node),
   1105         .char_literal => return charLiteral(gz, ri, node),
   1106         .error_set_decl => return errorSetDecl(gz, ri, node),
   1107         .array_access => return arrayAccess(gz, scope, ri, node),
   1108         .@"comptime" => return comptimeExprAst(gz, scope, ri, node),
   1109         .@"switch", .switch_comma => return switchExpr(gz, scope, ri.br(), node, tree.fullSwitch(node).?),
   1110 
   1111         .@"nosuspend" => return nosuspendExpr(gz, scope, ri, node),
   1112         .@"suspend" => return suspendExpr(gz, scope, node),
   1113         .@"resume" => return resumeExpr(gz, scope, ri, node),
   1114 
   1115         .@"try" => return tryExpr(gz, scope, ri, node, tree.nodeData(node).node),
   1116 
   1117         .array_init_one,
   1118         .array_init_one_comma,
   1119         .array_init_dot_two,
   1120         .array_init_dot_two_comma,
   1121         .array_init_dot,
   1122         .array_init_dot_comma,
   1123         .array_init,
   1124         .array_init_comma,
   1125         => {
   1126             var buf: [2]Ast.Node.Index = undefined;
   1127             return arrayInitExpr(gz, scope, ri, node, tree.fullArrayInit(&buf, node).?);
   1128         },
   1129 
   1130         .struct_init_one,
   1131         .struct_init_one_comma,
   1132         .struct_init_dot_two,
   1133         .struct_init_dot_two_comma,
   1134         .struct_init_dot,
   1135         .struct_init_dot_comma,
   1136         .struct_init,
   1137         .struct_init_comma,
   1138         => {
   1139             var buf: [2]Ast.Node.Index = undefined;
   1140             return structInitExpr(gz, scope, ri, node, tree.fullStructInit(&buf, node).?);
   1141         },
   1142 
   1143         .fn_proto_simple,
   1144         .fn_proto_multi,
   1145         .fn_proto_one,
   1146         .fn_proto,
   1147         => {
   1148             var buf: [1]Ast.Node.Index = undefined;
   1149             return fnProtoExpr(gz, scope, ri, node, tree.fullFnProto(&buf, node).?);
   1150         },
   1151     }
   1152 }
   1153 
   1154 /// When a name strategy other than `.anon` is available, for instance when analyzing the init expr
   1155 /// of a variable declaration, try this function before `expr`/`comptimeExpr`/etc, so that the name
   1156 /// strategy can be applied if necessary. If `null` is returned, then `node` does not consume a name
   1157 /// strategy, and a normal evaluation function like `expr` should be used instead. Otherwise, `node`
   1158 /// does consume a name strategy; the expression has been evaluated like `expr`, but using the given
   1159 /// name strategy.
   1160 fn nameStratExpr(
   1161     gz: *GenZir,
   1162     scope: *Scope,
   1163     ri: ResultInfo,
   1164     node: Ast.Node.Index,
   1165     name_strat: Zir.Inst.NameStrategy,
   1166 ) InnerError!?Zir.Inst.Ref {
   1167     const astgen = gz.astgen;
   1168     const tree = astgen.tree;
   1169     switch (tree.nodeTag(node)) {
   1170         .container_decl,
   1171         .container_decl_trailing,
   1172         .container_decl_two,
   1173         .container_decl_two_trailing,
   1174         .container_decl_arg,
   1175         .container_decl_arg_trailing,
   1176         .tagged_union,
   1177         .tagged_union_trailing,
   1178         .tagged_union_two,
   1179         .tagged_union_two_trailing,
   1180         .tagged_union_enum_tag,
   1181         .tagged_union_enum_tag_trailing,
   1182         => {
   1183             var buf: [2]Ast.Node.Index = undefined;
   1184             return try containerDecl(gz, scope, ri, node, tree.fullContainerDecl(&buf, node).?, name_strat);
   1185         },
   1186         .builtin_call_two,
   1187         .builtin_call_two_comma,
   1188         => {
   1189             const builtin_token = tree.nodeMainToken(node);
   1190             const builtin_name = tree.tokenSlice(builtin_token);
   1191             if (!std.mem.eql(u8, builtin_name, "@Type")) return null;
   1192             var buf: [2]Ast.Node.Index = undefined;
   1193             const params = tree.builtinCallParams(&buf, node).?;
   1194             if (params.len != 1) return null; // let `builtinCall` error
   1195             return try builtinReify(gz, scope, ri, node, params[0], name_strat);
   1196         },
   1197         else => return null,
   1198     }
   1199 }
   1200 
   1201 fn nosuspendExpr(
   1202     gz: *GenZir,
   1203     scope: *Scope,
   1204     ri: ResultInfo,
   1205     node: Ast.Node.Index,
   1206 ) InnerError!Zir.Inst.Ref {
   1207     const astgen = gz.astgen;
   1208     const tree = astgen.tree;
   1209     const body_node = tree.nodeData(node).node;
   1210     if (gz.nosuspend_node.unwrap()) |nosuspend_node| {
   1211         try astgen.appendErrorNodeNotes(node, "redundant nosuspend block", .{}, &[_]u32{
   1212             try astgen.errNoteNode(nosuspend_node, "other nosuspend block here", .{}),
   1213         });
   1214     }
   1215     gz.nosuspend_node = node.toOptional();
   1216     defer gz.nosuspend_node = .none;
   1217     return expr(gz, scope, ri, body_node);
   1218 }
   1219 
   1220 fn suspendExpr(
   1221     gz: *GenZir,
   1222     scope: *Scope,
   1223     node: Ast.Node.Index,
   1224 ) InnerError!Zir.Inst.Ref {
   1225     const astgen = gz.astgen;
   1226     const gpa = astgen.gpa;
   1227     const tree = astgen.tree;
   1228     const body_node = tree.nodeData(node).node;
   1229 
   1230     if (gz.nosuspend_node.unwrap()) |nosuspend_node| {
   1231         return astgen.failNodeNotes(node, "suspend inside nosuspend block", .{}, &[_]u32{
   1232             try astgen.errNoteNode(nosuspend_node, "nosuspend block here", .{}),
   1233         });
   1234     }
   1235     if (gz.suspend_node.unwrap()) |suspend_node| {
   1236         return astgen.failNodeNotes(node, "cannot suspend inside suspend block", .{}, &[_]u32{
   1237             try astgen.errNoteNode(suspend_node, "other suspend block here", .{}),
   1238         });
   1239     }
   1240 
   1241     const suspend_inst = try gz.makeBlockInst(.suspend_block, node);
   1242     try gz.instructions.append(gpa, suspend_inst);
   1243 
   1244     var suspend_scope = gz.makeSubBlock(scope);
   1245     suspend_scope.suspend_node = node.toOptional();
   1246     defer suspend_scope.unstack();
   1247 
   1248     const body_result = try fullBodyExpr(&suspend_scope, &suspend_scope.base, .{ .rl = .none }, body_node, .normal);
   1249     if (!gz.refIsNoReturn(body_result)) {
   1250         _ = try suspend_scope.addBreak(.break_inline, suspend_inst, .void_value);
   1251     }
   1252     try suspend_scope.setBlockBody(suspend_inst);
   1253 
   1254     return suspend_inst.toRef();
   1255 }
   1256 
   1257 fn resumeExpr(
   1258     gz: *GenZir,
   1259     scope: *Scope,
   1260     ri: ResultInfo,
   1261     node: Ast.Node.Index,
   1262 ) InnerError!Zir.Inst.Ref {
   1263     const astgen = gz.astgen;
   1264     const tree = astgen.tree;
   1265     const rhs_node = tree.nodeData(node).node;
   1266     const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node);
   1267     const result = try gz.addUnNode(.@"resume", operand, node);
   1268     return rvalue(gz, ri, result, node);
   1269 }
   1270 
   1271 fn fnProtoExpr(
   1272     gz: *GenZir,
   1273     scope: *Scope,
   1274     ri: ResultInfo,
   1275     node: Ast.Node.Index,
   1276     fn_proto: Ast.full.FnProto,
   1277 ) InnerError!Zir.Inst.Ref {
   1278     const astgen = gz.astgen;
   1279     const tree = astgen.tree;
   1280 
   1281     if (fn_proto.name_token) |some| {
   1282         return astgen.failTok(some, "function type cannot have a name", .{});
   1283     }
   1284 
   1285     if (fn_proto.ast.align_expr.unwrap()) |align_expr| {
   1286         return astgen.failNode(align_expr, "function type cannot have an alignment", .{});
   1287     }
   1288 
   1289     if (fn_proto.ast.addrspace_expr.unwrap()) |addrspace_expr| {
   1290         return astgen.failNode(addrspace_expr, "function type cannot have an addrspace", .{});
   1291     }
   1292 
   1293     if (fn_proto.ast.section_expr.unwrap()) |section_expr| {
   1294         return astgen.failNode(section_expr, "function type cannot have a linksection", .{});
   1295     }
   1296 
   1297     const return_type = fn_proto.ast.return_type.unwrap().?;
   1298     const maybe_bang = tree.firstToken(return_type) - 1;
   1299     const is_inferred_error = tree.tokenTag(maybe_bang) == .bang;
   1300     if (is_inferred_error) {
   1301         return astgen.failTok(maybe_bang, "function type cannot have an inferred error set", .{});
   1302     }
   1303 
   1304     const is_extern = blk: {
   1305         const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false;
   1306         break :blk tree.tokenTag(maybe_extern_token) == .keyword_extern;
   1307     };
   1308     assert(!is_extern);
   1309 
   1310     return fnProtoExprInner(gz, scope, ri, node, fn_proto, false);
   1311 }
   1312 
   1313 fn fnProtoExprInner(
   1314     gz: *GenZir,
   1315     scope: *Scope,
   1316     ri: ResultInfo,
   1317     node: Ast.Node.Index,
   1318     fn_proto: Ast.full.FnProto,
   1319     implicit_ccc: bool,
   1320 ) InnerError!Zir.Inst.Ref {
   1321     const astgen = gz.astgen;
   1322     const tree = astgen.tree;
   1323 
   1324     var block_scope = gz.makeSubBlock(scope);
   1325     defer block_scope.unstack();
   1326 
   1327     const block_inst = try gz.makeBlockInst(.block_inline, node);
   1328 
   1329     var noalias_bits: u32 = 0;
   1330     const is_var_args = is_var_args: {
   1331         var param_type_i: usize = 0;
   1332         var it = fn_proto.iterate(tree);
   1333         while (it.next()) |param| : (param_type_i += 1) {
   1334             const is_comptime = if (param.comptime_noalias) |token| switch (tree.tokenTag(token)) {
   1335                 .keyword_noalias => is_comptime: {
   1336                     noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse
   1337                         return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
   1338                     break :is_comptime false;
   1339                 },
   1340                 .keyword_comptime => true,
   1341                 else => false,
   1342             } else false;
   1343 
   1344             const is_anytype = if (param.anytype_ellipsis3) |token| blk: {
   1345                 switch (tree.tokenTag(token)) {
   1346                     .keyword_anytype => break :blk true,
   1347                     .ellipsis3 => break :is_var_args true,
   1348                     else => unreachable,
   1349                 }
   1350             } else false;
   1351 
   1352             const param_name = if (param.name_token) |name_token| blk: {
   1353                 if (mem.eql(u8, "_", tree.tokenSlice(name_token)))
   1354                     break :blk .empty;
   1355 
   1356                 break :blk try astgen.identAsString(name_token);
   1357             } else .empty;
   1358 
   1359             if (is_anytype) {
   1360                 const name_token = param.name_token orelse param.anytype_ellipsis3.?;
   1361 
   1362                 const tag: Zir.Inst.Tag = if (is_comptime)
   1363                     .param_anytype_comptime
   1364                 else
   1365                     .param_anytype;
   1366                 _ = try block_scope.addStrTok(tag, param_name, name_token);
   1367             } else {
   1368                 const param_type_node = param.type_expr.?;
   1369                 var param_gz = block_scope.makeSubBlock(scope);
   1370                 defer param_gz.unstack();
   1371                 param_gz.is_comptime = true;
   1372                 const param_type = try fullBodyExpr(&param_gz, scope, coerced_type_ri, param_type_node, .normal);
   1373                 const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1);
   1374                 _ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node);
   1375                 const name_token = param.name_token orelse tree.nodeMainToken(param_type_node);
   1376                 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param;
   1377                 // We pass `prev_param_insts` as `&.{}` here because a function prototype can't refer to previous
   1378                 // arguments (we haven't set up scopes here).
   1379                 const param_inst = try block_scope.addParam(&param_gz, &.{}, false, tag, name_token, param_name);
   1380                 assert(param_inst_expected == param_inst);
   1381             }
   1382         }
   1383         break :is_var_args false;
   1384     };
   1385 
   1386     const cc: Zir.Inst.Ref = if (fn_proto.ast.callconv_expr.unwrap()) |callconv_expr|
   1387         try comptimeExpr(
   1388             &block_scope,
   1389             scope,
   1390             .{ .rl = .{ .coerced_ty = try block_scope.addBuiltinValue(callconv_expr, .calling_convention) } },
   1391             callconv_expr,
   1392             .@"callconv",
   1393         )
   1394     else if (implicit_ccc)
   1395         try block_scope.addBuiltinValue(node, .calling_convention_c)
   1396     else
   1397         .none;
   1398 
   1399     const ret_ty_node = fn_proto.ast.return_type.unwrap().?;
   1400     const ret_ty = try comptimeExpr(&block_scope, scope, coerced_type_ri, ret_ty_node, .function_ret_ty);
   1401 
   1402     const result = try block_scope.addFunc(.{
   1403         .src_node = fn_proto.ast.proto_node,
   1404 
   1405         .cc_ref = cc,
   1406         .cc_gz = null,
   1407         .ret_ref = ret_ty,
   1408         .ret_gz = null,
   1409 
   1410         .ret_param_refs = &.{},
   1411         .param_insts = &.{},
   1412         .ret_ty_is_generic = false,
   1413 
   1414         .param_block = block_inst,
   1415         .body_gz = null,
   1416         .is_var_args = is_var_args,
   1417         .is_inferred_error = false,
   1418         .is_noinline = false,
   1419         .noalias_bits = noalias_bits,
   1420 
   1421         .proto_hash = undefined, // ignored for `body_gz == null`
   1422     });
   1423 
   1424     _ = try block_scope.addBreak(.break_inline, block_inst, result);
   1425     try block_scope.setBlockBody(block_inst);
   1426     try gz.instructions.append(astgen.gpa, block_inst);
   1427 
   1428     return rvalue(gz, ri, block_inst.toRef(), fn_proto.ast.proto_node);
   1429 }
   1430 
   1431 fn arrayInitExpr(
   1432     gz: *GenZir,
   1433     scope: *Scope,
   1434     ri: ResultInfo,
   1435     node: Ast.Node.Index,
   1436     array_init: Ast.full.ArrayInit,
   1437 ) InnerError!Zir.Inst.Ref {
   1438     const astgen = gz.astgen;
   1439     const tree = astgen.tree;
   1440 
   1441     assert(array_init.ast.elements.len != 0); // Otherwise it would be struct init.
   1442 
   1443     const array_ty: Zir.Inst.Ref, const elem_ty: Zir.Inst.Ref = inst: {
   1444         const type_expr = array_init.ast.type_expr.unwrap() orelse break :inst .{ .none, .none };
   1445 
   1446         infer: {
   1447             const array_type: Ast.full.ArrayType = tree.fullArrayType(type_expr) orelse break :infer;
   1448             // This intentionally does not support `@"_"` syntax.
   1449             if (tree.nodeTag(array_type.ast.elem_count) == .identifier and
   1450                 mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(array_type.ast.elem_count)), "_"))
   1451             {
   1452                 const len_inst = try gz.addInt(array_init.ast.elements.len);
   1453                 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type);
   1454                 if (array_type.ast.sentinel == .none) {
   1455                     const array_type_inst = try gz.addPlNode(.array_type, type_expr, Zir.Inst.Bin{
   1456                         .lhs = len_inst,
   1457                         .rhs = elem_type,
   1458                     });
   1459                     break :inst .{ array_type_inst, elem_type };
   1460                 } else {
   1461                     const sentinel_node = array_type.ast.sentinel.unwrap().?;
   1462                     const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, sentinel_node, .array_sentinel);
   1463                     const array_type_inst = try gz.addPlNode(
   1464                         .array_type_sentinel,
   1465                         type_expr,
   1466                         Zir.Inst.ArrayTypeSentinel{
   1467                             .len = len_inst,
   1468                             .elem_type = elem_type,
   1469                             .sentinel = sentinel,
   1470                         },
   1471                     );
   1472                     break :inst .{ array_type_inst, elem_type };
   1473                 }
   1474             }
   1475         }
   1476         const array_type_inst = try typeExpr(gz, scope, type_expr);
   1477         _ = try gz.addPlNode(.validate_array_init_ty, node, Zir.Inst.ArrayInit{
   1478             .ty = array_type_inst,
   1479             .init_count = @intCast(array_init.ast.elements.len),
   1480         });
   1481         break :inst .{ array_type_inst, .none };
   1482     };
   1483 
   1484     if (array_ty != .none) {
   1485         // Typed inits do not use RLS for language simplicity.
   1486         switch (ri.rl) {
   1487             .discard => {
   1488                 if (elem_ty != .none) {
   1489                     const elem_ri: ResultInfo = .{ .rl = .{ .ty = elem_ty } };
   1490                     for (array_init.ast.elements) |elem_init| {
   1491                         _ = try expr(gz, scope, elem_ri, elem_init);
   1492                     }
   1493                 } else {
   1494                     for (array_init.ast.elements, 0..) |elem_init, i| {
   1495                         const this_elem_ty = try gz.add(.{
   1496                             .tag = .array_init_elem_type,
   1497                             .data = .{ .bin = .{
   1498                                 .lhs = array_ty,
   1499                                 .rhs = @enumFromInt(i),
   1500                             } },
   1501                         });
   1502                         _ = try expr(gz, scope, .{ .rl = .{ .ty = this_elem_ty } }, elem_init);
   1503                     }
   1504                 }
   1505                 return .void_value;
   1506             },
   1507             .ref => return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, true),
   1508             else => {
   1509                 const array_inst = try arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, false);
   1510                 return rvalue(gz, ri, array_inst, node);
   1511             },
   1512         }
   1513     }
   1514 
   1515     switch (ri.rl) {
   1516         .none => return arrayInitExprAnon(gz, scope, node, array_init.ast.elements),
   1517         .discard => {
   1518             for (array_init.ast.elements) |elem_init| {
   1519                 _ = try expr(gz, scope, .{ .rl = .discard }, elem_init);
   1520             }
   1521             return Zir.Inst.Ref.void_value;
   1522         },
   1523         .ref => {
   1524             const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements);
   1525             return gz.addUnTok(.ref, result, tree.firstToken(node));
   1526         },
   1527         .ref_coerced_ty => |ptr_ty_inst| {
   1528             const dest_arr_ty_inst = try gz.addPlNode(.validate_array_init_ref_ty, node, Zir.Inst.ArrayInitRefTy{
   1529                 .ptr_ty = ptr_ty_inst,
   1530                 .elem_count = @intCast(array_init.ast.elements.len),
   1531             });
   1532             return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, dest_arr_ty_inst, .none, true);
   1533         },
   1534         .ty, .coerced_ty => |result_ty_inst| {
   1535             _ = try gz.addPlNode(.validate_array_init_result_ty, node, Zir.Inst.ArrayInit{
   1536                 .ty = result_ty_inst,
   1537                 .init_count = @intCast(array_init.ast.elements.len),
   1538             });
   1539             return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, result_ty_inst, .none, false);
   1540         },
   1541         .ptr => |ptr| {
   1542             try arrayInitExprPtr(gz, scope, node, array_init.ast.elements, ptr.inst);
   1543             return .void_value;
   1544         },
   1545         .inferred_ptr => {
   1546             // We can't get elem pointers of an untyped inferred alloc, so must perform a
   1547             // standard anonymous initialization followed by an rvalue store.
   1548             // See corresponding logic in structInitExpr.
   1549             const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements);
   1550             return rvalue(gz, ri, result, node);
   1551         },
   1552         .destructure => |destructure| {
   1553             // Untyped init - destructure directly into result pointers
   1554             if (array_init.ast.elements.len != destructure.components.len) {
   1555                 return astgen.failNodeNotes(node, "expected {} elements for destructure, found {}", .{
   1556                     destructure.components.len,
   1557                     array_init.ast.elements.len,
   1558                 }, &.{
   1559                     try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}),
   1560                 });
   1561             }
   1562             for (array_init.ast.elements, destructure.components) |elem_init, ds_comp| {
   1563                 const elem_ri: ResultInfo = .{ .rl = switch (ds_comp) {
   1564                     .typed_ptr => |ptr_rl| .{ .ptr = ptr_rl },
   1565                     .inferred_ptr => |ptr_inst| .{ .inferred_ptr = ptr_inst },
   1566                     .discard => .discard,
   1567                 } };
   1568                 _ = try expr(gz, scope, elem_ri, elem_init);
   1569             }
   1570             return .void_value;
   1571         },
   1572     }
   1573 }
   1574 
   1575 /// An array initialization expression using an `array_init_anon` instruction.
   1576 fn arrayInitExprAnon(
   1577     gz: *GenZir,
   1578     scope: *Scope,
   1579     node: Ast.Node.Index,
   1580     elements: []const Ast.Node.Index,
   1581 ) InnerError!Zir.Inst.Ref {
   1582     const astgen = gz.astgen;
   1583 
   1584     const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{
   1585         .operands_len = @intCast(elements.len),
   1586     });
   1587     var extra_index = try reserveExtra(astgen, elements.len);
   1588 
   1589     for (elements) |elem_init| {
   1590         const elem_ref = try expr(gz, scope, .{ .rl = .none }, elem_init);
   1591         astgen.extra.items[extra_index] = @intFromEnum(elem_ref);
   1592         extra_index += 1;
   1593     }
   1594     return try gz.addPlNodePayloadIndex(.array_init_anon, node, payload_index);
   1595 }
   1596 
   1597 /// An array initialization expression using an `array_init` or `array_init_ref` instruction.
   1598 fn arrayInitExprTyped(
   1599     gz: *GenZir,
   1600     scope: *Scope,
   1601     node: Ast.Node.Index,
   1602     elements: []const Ast.Node.Index,
   1603     ty_inst: Zir.Inst.Ref,
   1604     maybe_elem_ty_inst: Zir.Inst.Ref,
   1605     is_ref: bool,
   1606 ) InnerError!Zir.Inst.Ref {
   1607     const astgen = gz.astgen;
   1608 
   1609     const len = elements.len + 1; // +1 for type
   1610     const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{
   1611         .operands_len = @intCast(len),
   1612     });
   1613     var extra_index = try reserveExtra(astgen, len);
   1614     astgen.extra.items[extra_index] = @intFromEnum(ty_inst);
   1615     extra_index += 1;
   1616 
   1617     if (maybe_elem_ty_inst != .none) {
   1618         const elem_ri: ResultInfo = .{ .rl = .{ .coerced_ty = maybe_elem_ty_inst } };
   1619         for (elements) |elem_init| {
   1620             const elem_inst = try expr(gz, scope, elem_ri, elem_init);
   1621             astgen.extra.items[extra_index] = @intFromEnum(elem_inst);
   1622             extra_index += 1;
   1623         }
   1624     } else {
   1625         for (elements, 0..) |elem_init, i| {
   1626             const ri: ResultInfo = .{ .rl = .{ .coerced_ty = try gz.add(.{
   1627                 .tag = .array_init_elem_type,
   1628                 .data = .{ .bin = .{
   1629                     .lhs = ty_inst,
   1630                     .rhs = @enumFromInt(i),
   1631                 } },
   1632             }) } };
   1633 
   1634             const elem_inst = try expr(gz, scope, ri, elem_init);
   1635             astgen.extra.items[extra_index] = @intFromEnum(elem_inst);
   1636             extra_index += 1;
   1637         }
   1638     }
   1639 
   1640     const tag: Zir.Inst.Tag = if (is_ref) .array_init_ref else .array_init;
   1641     return try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1642 }
   1643 
   1644 /// An array initialization expression using element pointers.
   1645 fn arrayInitExprPtr(
   1646     gz: *GenZir,
   1647     scope: *Scope,
   1648     node: Ast.Node.Index,
   1649     elements: []const Ast.Node.Index,
   1650     ptr_inst: Zir.Inst.Ref,
   1651 ) InnerError!void {
   1652     const astgen = gz.astgen;
   1653 
   1654     const array_ptr_inst = try gz.addUnNode(.opt_eu_base_ptr_init, ptr_inst, node);
   1655 
   1656     const payload_index = try addExtra(astgen, Zir.Inst.Block{
   1657         .body_len = @intCast(elements.len),
   1658     });
   1659     var extra_index = try reserveExtra(astgen, elements.len);
   1660 
   1661     for (elements, 0..) |elem_init, i| {
   1662         const elem_ptr_inst = try gz.addPlNode(.array_init_elem_ptr, elem_init, Zir.Inst.ElemPtrImm{
   1663             .ptr = array_ptr_inst,
   1664             .index = @intCast(i),
   1665         });
   1666         astgen.extra.items[extra_index] = @intFromEnum(elem_ptr_inst.toIndex().?);
   1667         extra_index += 1;
   1668         _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = elem_ptr_inst } } }, elem_init);
   1669     }
   1670 
   1671     _ = try gz.addPlNodePayloadIndex(.validate_ptr_array_init, node, payload_index);
   1672 }
   1673 
   1674 fn structInitExpr(
   1675     gz: *GenZir,
   1676     scope: *Scope,
   1677     ri: ResultInfo,
   1678     node: Ast.Node.Index,
   1679     struct_init: Ast.full.StructInit,
   1680 ) InnerError!Zir.Inst.Ref {
   1681     const astgen = gz.astgen;
   1682     const tree = astgen.tree;
   1683 
   1684     if (struct_init.ast.type_expr == .none) {
   1685         if (struct_init.ast.fields.len == 0) {
   1686             // Anonymous init with no fields.
   1687             switch (ri.rl) {
   1688                 .discard => return .void_value,
   1689                 .ref_coerced_ty => |ptr_ty_inst| return gz.addUnNode(.struct_init_empty_ref_result, ptr_ty_inst, node),
   1690                 .ty, .coerced_ty => |ty_inst| return gz.addUnNode(.struct_init_empty_result, ty_inst, node),
   1691                 .ptr => {
   1692                     // TODO: should we modify this to use RLS for the field stores here?
   1693                     const ty_inst = (try ri.rl.resultType(gz, node)).?;
   1694                     const val = try gz.addUnNode(.struct_init_empty_result, ty_inst, node);
   1695                     return rvalue(gz, ri, val, node);
   1696                 },
   1697                 .none, .ref, .inferred_ptr => {
   1698                     return rvalue(gz, ri, .empty_tuple, node);
   1699                 },
   1700                 .destructure => |destructure| {
   1701                     return astgen.failNodeNotes(node, "empty initializer cannot be destructured", .{}, &.{
   1702                         try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}),
   1703                     });
   1704                 },
   1705             }
   1706         }
   1707     } else array: {
   1708         const type_expr = struct_init.ast.type_expr.unwrap().?;
   1709         const array_type: Ast.full.ArrayType = tree.fullArrayType(type_expr) orelse {
   1710             if (struct_init.ast.fields.len == 0) {
   1711                 const ty_inst = try typeExpr(gz, scope, type_expr);
   1712                 const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
   1713                 return rvalue(gz, ri, result, node);
   1714             }
   1715             break :array;
   1716         };
   1717         const is_inferred_array_len = tree.nodeTag(array_type.ast.elem_count) == .identifier and
   1718             // This intentionally does not support `@"_"` syntax.
   1719             mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(array_type.ast.elem_count)), "_");
   1720         if (struct_init.ast.fields.len == 0) {
   1721             if (is_inferred_array_len) {
   1722                 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type);
   1723                 const array_type_inst = if (array_type.ast.sentinel == .none) blk: {
   1724                     break :blk try gz.addPlNode(.array_type, type_expr, Zir.Inst.Bin{
   1725                         .lhs = .zero_usize,
   1726                         .rhs = elem_type,
   1727                     });
   1728                 } else blk: {
   1729                     const sentinel_node = array_type.ast.sentinel.unwrap().?;
   1730                     const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, sentinel_node, .array_sentinel);
   1731                     break :blk try gz.addPlNode(
   1732                         .array_type_sentinel,
   1733                         type_expr,
   1734                         Zir.Inst.ArrayTypeSentinel{
   1735                             .len = .zero_usize,
   1736                             .elem_type = elem_type,
   1737                             .sentinel = sentinel,
   1738                         },
   1739                     );
   1740                 };
   1741                 const result = try gz.addUnNode(.struct_init_empty, array_type_inst, node);
   1742                 return rvalue(gz, ri, result, node);
   1743             }
   1744             const ty_inst = try typeExpr(gz, scope, type_expr);
   1745             const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
   1746             return rvalue(gz, ri, result, node);
   1747         } else {
   1748             return astgen.failNode(
   1749                 type_expr,
   1750                 "initializing array with struct syntax",
   1751                 .{},
   1752             );
   1753         }
   1754     }
   1755 
   1756     {
   1757         var sfba = std.heap.stackFallback(256, astgen.arena);
   1758         const sfba_allocator = sfba.get();
   1759 
   1760         var duplicate_names = std.AutoArrayHashMap(Zir.NullTerminatedString, ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
   1761         try duplicate_names.ensureTotalCapacity(@intCast(struct_init.ast.fields.len));
   1762 
   1763         // When there aren't errors, use this to avoid a second iteration.
   1764         var any_duplicate = false;
   1765 
   1766         for (struct_init.ast.fields) |field| {
   1767             const name_token = tree.firstToken(field) - 2;
   1768             const name_index = try astgen.identAsString(name_token);
   1769 
   1770             const gop = try duplicate_names.getOrPut(name_index);
   1771 
   1772             if (gop.found_existing) {
   1773                 try gop.value_ptr.append(sfba_allocator, name_token);
   1774                 any_duplicate = true;
   1775             } else {
   1776                 gop.value_ptr.* = .{};
   1777                 try gop.value_ptr.append(sfba_allocator, name_token);
   1778             }
   1779         }
   1780 
   1781         if (any_duplicate) {
   1782             var it = duplicate_names.iterator();
   1783 
   1784             while (it.next()) |entry| {
   1785                 const record = entry.value_ptr.*;
   1786                 if (record.items.len > 1) {
   1787                     var error_notes = std.ArrayList(u32).init(astgen.arena);
   1788 
   1789                     for (record.items[1..]) |duplicate| {
   1790                         try error_notes.append(try astgen.errNoteTok(duplicate, "duplicate name here", .{}));
   1791                     }
   1792 
   1793                     try error_notes.append(try astgen.errNoteNode(node, "struct declared here", .{}));
   1794 
   1795                     try astgen.appendErrorTokNotes(
   1796                         record.items[0],
   1797                         "duplicate struct field name",
   1798                         .{},
   1799                         error_notes.items,
   1800                     );
   1801                 }
   1802             }
   1803 
   1804             return error.AnalysisFail;
   1805         }
   1806     }
   1807 
   1808     if (struct_init.ast.type_expr.unwrap()) |type_expr| {
   1809         // Typed inits do not use RLS for language simplicity.
   1810         const ty_inst = try typeExpr(gz, scope, type_expr);
   1811         _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
   1812         switch (ri.rl) {
   1813             .ref => return structInitExprTyped(gz, scope, node, struct_init, ty_inst, true),
   1814             else => {
   1815                 const struct_inst = try structInitExprTyped(gz, scope, node, struct_init, ty_inst, false);
   1816                 return rvalue(gz, ri, struct_inst, node);
   1817             },
   1818         }
   1819     }
   1820 
   1821     switch (ri.rl) {
   1822         .none => return structInitExprAnon(gz, scope, node, struct_init),
   1823         .discard => {
   1824             // Even if discarding we must perform side-effects.
   1825             for (struct_init.ast.fields) |field_init| {
   1826                 _ = try expr(gz, scope, .{ .rl = .discard }, field_init);
   1827             }
   1828             return .void_value;
   1829         },
   1830         .ref => {
   1831             const result = try structInitExprAnon(gz, scope, node, struct_init);
   1832             return gz.addUnTok(.ref, result, tree.firstToken(node));
   1833         },
   1834         .ref_coerced_ty => |ptr_ty_inst| {
   1835             const result_ty_inst = try gz.addUnNode(.elem_type, ptr_ty_inst, node);
   1836             _ = try gz.addUnNode(.validate_struct_init_result_ty, result_ty_inst, node);
   1837             return structInitExprTyped(gz, scope, node, struct_init, result_ty_inst, true);
   1838         },
   1839         .ty, .coerced_ty => |result_ty_inst| {
   1840             _ = try gz.addUnNode(.validate_struct_init_result_ty, result_ty_inst, node);
   1841             return structInitExprTyped(gz, scope, node, struct_init, result_ty_inst, false);
   1842         },
   1843         .ptr => |ptr| {
   1844             try structInitExprPtr(gz, scope, node, struct_init, ptr.inst);
   1845             return .void_value;
   1846         },
   1847         .inferred_ptr => {
   1848             // We can't get field pointers of an untyped inferred alloc, so must perform a
   1849             // standard anonymous initialization followed by an rvalue store.
   1850             // See corresponding logic in arrayInitExpr.
   1851             const struct_inst = try structInitExprAnon(gz, scope, node, struct_init);
   1852             return rvalue(gz, ri, struct_inst, node);
   1853         },
   1854         .destructure => |destructure| {
   1855             // This is an untyped init, so is an actual struct, which does
   1856             // not support destructuring.
   1857             return astgen.failNodeNotes(node, "struct value cannot be destructured", .{}, &.{
   1858                 try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}),
   1859             });
   1860         },
   1861     }
   1862 }
   1863 
   1864 /// A struct initialization expression using a `struct_init_anon` instruction.
   1865 fn structInitExprAnon(
   1866     gz: *GenZir,
   1867     scope: *Scope,
   1868     node: Ast.Node.Index,
   1869     struct_init: Ast.full.StructInit,
   1870 ) InnerError!Zir.Inst.Ref {
   1871     const astgen = gz.astgen;
   1872     const tree = astgen.tree;
   1873 
   1874     const payload_index = try addExtra(astgen, Zir.Inst.StructInitAnon{
   1875         .abs_node = node,
   1876         .abs_line = astgen.source_line,
   1877         .fields_len = @intCast(struct_init.ast.fields.len),
   1878     });
   1879     const field_size = @typeInfo(Zir.Inst.StructInitAnon.Item).@"struct".fields.len;
   1880     var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size);
   1881 
   1882     for (struct_init.ast.fields) |field_init| {
   1883         const name_token = tree.firstToken(field_init) - 2;
   1884         const str_index = try astgen.identAsString(name_token);
   1885         setExtra(astgen, extra_index, Zir.Inst.StructInitAnon.Item{
   1886             .field_name = str_index,
   1887             .init = try expr(gz, scope, .{ .rl = .none }, field_init),
   1888         });
   1889         extra_index += field_size;
   1890     }
   1891 
   1892     return gz.addPlNodePayloadIndex(.struct_init_anon, node, payload_index);
   1893 }
   1894 
   1895 /// A struct initialization expression using a `struct_init` or `struct_init_ref` instruction.
   1896 fn structInitExprTyped(
   1897     gz: *GenZir,
   1898     scope: *Scope,
   1899     node: Ast.Node.Index,
   1900     struct_init: Ast.full.StructInit,
   1901     ty_inst: Zir.Inst.Ref,
   1902     is_ref: bool,
   1903 ) InnerError!Zir.Inst.Ref {
   1904     const astgen = gz.astgen;
   1905     const tree = astgen.tree;
   1906 
   1907     const payload_index = try addExtra(astgen, Zir.Inst.StructInit{
   1908         .abs_node = node,
   1909         .abs_line = astgen.source_line,
   1910         .fields_len = @intCast(struct_init.ast.fields.len),
   1911     });
   1912     const field_size = @typeInfo(Zir.Inst.StructInit.Item).@"struct".fields.len;
   1913     var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size);
   1914 
   1915     for (struct_init.ast.fields) |field_init| {
   1916         const name_token = tree.firstToken(field_init) - 2;
   1917         const str_index = try astgen.identAsString(name_token);
   1918         const field_ty_inst = try gz.addPlNode(.struct_init_field_type, field_init, Zir.Inst.FieldType{
   1919             .container_type = ty_inst,
   1920             .name_start = str_index,
   1921         });
   1922         setExtra(astgen, extra_index, Zir.Inst.StructInit.Item{
   1923             .field_type = field_ty_inst.toIndex().?,
   1924             .init = try expr(gz, scope, .{ .rl = .{ .coerced_ty = field_ty_inst } }, field_init),
   1925         });
   1926         extra_index += field_size;
   1927     }
   1928 
   1929     const tag: Zir.Inst.Tag = if (is_ref) .struct_init_ref else .struct_init;
   1930     return gz.addPlNodePayloadIndex(tag, node, payload_index);
   1931 }
   1932 
   1933 /// A struct initialization expression using field pointers.
   1934 fn structInitExprPtr(
   1935     gz: *GenZir,
   1936     scope: *Scope,
   1937     node: Ast.Node.Index,
   1938     struct_init: Ast.full.StructInit,
   1939     ptr_inst: Zir.Inst.Ref,
   1940 ) InnerError!void {
   1941     const astgen = gz.astgen;
   1942     const tree = astgen.tree;
   1943 
   1944     const struct_ptr_inst = try gz.addUnNode(.opt_eu_base_ptr_init, ptr_inst, node);
   1945 
   1946     const payload_index = try addExtra(astgen, Zir.Inst.Block{
   1947         .body_len = @intCast(struct_init.ast.fields.len),
   1948     });
   1949     var extra_index = try reserveExtra(astgen, struct_init.ast.fields.len);
   1950 
   1951     for (struct_init.ast.fields) |field_init| {
   1952         const name_token = tree.firstToken(field_init) - 2;
   1953         const str_index = try astgen.identAsString(name_token);
   1954         const field_ptr = try gz.addPlNode(.struct_init_field_ptr, field_init, Zir.Inst.Field{
   1955             .lhs = struct_ptr_inst,
   1956             .field_name_start = str_index,
   1957         });
   1958         astgen.extra.items[extra_index] = @intFromEnum(field_ptr.toIndex().?);
   1959         extra_index += 1;
   1960         _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = field_ptr } } }, field_init);
   1961     }
   1962 
   1963     _ = try gz.addPlNodePayloadIndex(.validate_ptr_struct_init, node, payload_index);
   1964 }
   1965 
   1966 /// This explicitly calls expr in a comptime scope by wrapping it in a `block_comptime` if
   1967 /// necessary. It should be used whenever we need to force compile-time evaluation of something,
   1968 /// such as a type.
   1969 /// The function corresponding to `comptime` expression syntax is `comptimeExprAst`.
   1970 fn comptimeExpr(
   1971     gz: *GenZir,
   1972     scope: *Scope,
   1973     ri: ResultInfo,
   1974     node: Ast.Node.Index,
   1975     reason: std.zig.SimpleComptimeReason,
   1976 ) InnerError!Zir.Inst.Ref {
   1977     return comptimeExpr2(gz, scope, ri, node, node, reason);
   1978 }
   1979 
   1980 /// Like `comptimeExpr`, but draws a distinction between `node`, the expression to evaluate at comptime,
   1981 /// and `src_node`, the node to attach to the `block_comptime`.
   1982 fn comptimeExpr2(
   1983     gz: *GenZir,
   1984     scope: *Scope,
   1985     ri: ResultInfo,
   1986     node: Ast.Node.Index,
   1987     src_node: Ast.Node.Index,
   1988     reason: std.zig.SimpleComptimeReason,
   1989 ) InnerError!Zir.Inst.Ref {
   1990     if (gz.is_comptime) {
   1991         // No need to change anything!
   1992         return expr(gz, scope, ri, node);
   1993     }
   1994 
   1995     // There's an optimization here: if the body will be evaluated at comptime regardless, there's
   1996     // no need to wrap it in a block. This is hard to determine in general, but we can identify a
   1997     // common subset of trivially comptime expressions to take down the size of the ZIR a bit.
   1998     const tree = gz.astgen.tree;
   1999     switch (tree.nodeTag(node)) {
   2000         .identifier => {
   2001             // Many identifiers can be handled without a `block_comptime`, so `AstGen.identifier` has
   2002             // special handling for this case.
   2003             return identifier(gz, scope, ri, node, .{ .src_node = src_node, .reason = reason });
   2004         },
   2005 
   2006         // These are leaf nodes which are always comptime-known.
   2007         .number_literal,
   2008         .char_literal,
   2009         .string_literal,
   2010         .multiline_string_literal,
   2011         .enum_literal,
   2012         .error_value,
   2013         .anyframe_literal,
   2014         .error_set_decl,
   2015         // These nodes are not leaves, but will force comptime evaluation of all sub-expressions, and
   2016         // hence behave the same regardless of whether they're in a comptime scope.
   2017         .error_union,
   2018         .merge_error_sets,
   2019         .optional_type,
   2020         .anyframe_type,
   2021         .ptr_type_aligned,
   2022         .ptr_type_sentinel,
   2023         .ptr_type,
   2024         .ptr_type_bit_range,
   2025         .array_type,
   2026         .array_type_sentinel,
   2027         .fn_proto_simple,
   2028         .fn_proto_multi,
   2029         .fn_proto_one,
   2030         .fn_proto,
   2031         .container_decl,
   2032         .container_decl_trailing,
   2033         .container_decl_arg,
   2034         .container_decl_arg_trailing,
   2035         .container_decl_two,
   2036         .container_decl_two_trailing,
   2037         .tagged_union,
   2038         .tagged_union_trailing,
   2039         .tagged_union_enum_tag,
   2040         .tagged_union_enum_tag_trailing,
   2041         .tagged_union_two,
   2042         .tagged_union_two_trailing,
   2043         => {
   2044             // No need to worry about result location here, we're not creating a comptime block!
   2045             return expr(gz, scope, ri, node);
   2046         },
   2047 
   2048         // Lastly, for labelled blocks, avoid emitting a labelled block directly inside this
   2049         // comptime block, because that would be silly! Note that we don't bother doing this for
   2050         // unlabelled blocks, since they don't generate blocks at comptime anyway (see `blockExpr`).
   2051         .block_two, .block_two_semicolon, .block, .block_semicolon => {
   2052             const lbrace = tree.nodeMainToken(node);
   2053             // Careful! We can't pass in the real result location here, since it may
   2054             // refer to runtime memory. A runtime-to-comptime boundary has to remove
   2055             // result location information, compute the result, and copy it to the true
   2056             // result location at runtime. We do this below as well.
   2057             const ty_only_ri: ResultInfo = .{
   2058                 .ctx = ri.ctx,
   2059                 .rl = if (try ri.rl.resultType(gz, node)) |res_ty|
   2060                     .{ .coerced_ty = res_ty }
   2061                 else
   2062                     .none,
   2063             };
   2064             if (tree.isTokenPrecededByTags(lbrace, &.{ .identifier, .colon })) {
   2065                 var buf: [2]Ast.Node.Index = undefined;
   2066                 const stmts = tree.blockStatements(&buf, node).?;
   2067 
   2068                 // Replace result location and copy back later - see above.
   2069                 const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmts, true, .normal);
   2070                 return rvalue(gz, ri, block_ref, node);
   2071             }
   2072         },
   2073 
   2074         // In other cases, we don't optimize anything - we need a wrapper comptime block.
   2075         else => {},
   2076     }
   2077 
   2078     var block_scope = gz.makeSubBlock(scope);
   2079     block_scope.is_comptime = true;
   2080     defer block_scope.unstack();
   2081 
   2082     const block_inst = try gz.makeBlockInst(.block_comptime, src_node);
   2083     // Replace result location and copy back later - see above.
   2084     const ty_only_ri: ResultInfo = .{
   2085         .ctx = ri.ctx,
   2086         .rl = if (try ri.rl.resultType(gz, src_node)) |res_ty|
   2087             .{ .coerced_ty = res_ty }
   2088         else
   2089             .none,
   2090     };
   2091     const block_result = try fullBodyExpr(&block_scope, scope, ty_only_ri, node, .normal);
   2092     if (!gz.refIsNoReturn(block_result)) {
   2093         _ = try block_scope.addBreak(.break_inline, block_inst, block_result);
   2094     }
   2095     try block_scope.setBlockComptimeBody(block_inst, reason);
   2096     try gz.instructions.append(gz.astgen.gpa, block_inst);
   2097 
   2098     return rvalue(gz, ri, block_inst.toRef(), src_node);
   2099 }
   2100 
   2101 /// This one is for an actual `comptime` syntax, and will emit a compile error if
   2102 /// the scope is already known to be comptime-evaluated.
   2103 /// See `comptimeExpr` for the helper function for calling expr in a comptime scope.
   2104 fn comptimeExprAst(
   2105     gz: *GenZir,
   2106     scope: *Scope,
   2107     ri: ResultInfo,
   2108     node: Ast.Node.Index,
   2109 ) InnerError!Zir.Inst.Ref {
   2110     const astgen = gz.astgen;
   2111     if (gz.is_comptime) {
   2112         try astgen.appendErrorNode(node, "redundant comptime keyword in already comptime scope", .{});
   2113     }
   2114     const tree = astgen.tree;
   2115     const body_node = tree.nodeData(node).node;
   2116     return comptimeExpr2(gz, scope, ri, body_node, node, .comptime_keyword);
   2117 }
   2118 
   2119 /// Restore the error return trace index. Performs the restore only if the result is a non-error or
   2120 /// if the result location is a non-error-handling expression.
   2121 fn restoreErrRetIndex(
   2122     gz: *GenZir,
   2123     bt: GenZir.BranchTarget,
   2124     ri: ResultInfo,
   2125     node: Ast.Node.Index,
   2126     result: Zir.Inst.Ref,
   2127 ) !void {
   2128     const op = switch (nodeMayEvalToError(gz.astgen.tree, node)) {
   2129         .always => return, // never restore/pop
   2130         .never => .none, // always restore/pop
   2131         .maybe => switch (ri.ctx) {
   2132             .error_handling_expr, .@"return", .fn_arg, .const_init => switch (ri.rl) {
   2133                 .ptr => |ptr_res| try gz.addUnNode(.load, ptr_res.inst, node),
   2134                 .inferred_ptr => blk: {
   2135                     // This is a terrible workaround for Sema's inability to load from a .alloc_inferred ptr
   2136                     // before its type has been resolved. There is no valid operand to use here, so error
   2137                     // traces will be popped prematurely.
   2138                     // TODO: Update this to do a proper load from the rl_ptr, once Sema can support it.
   2139                     break :blk .none;
   2140                 },
   2141                 .destructure => return, // value must be a tuple or array, so never restore/pop
   2142                 else => result,
   2143             },
   2144             else => .none, // always restore/pop
   2145         },
   2146     };
   2147     _ = try gz.addRestoreErrRetIndex(bt, .{ .if_non_error = op }, node);
   2148 }
   2149 
   2150 fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   2151     const astgen = parent_gz.astgen;
   2152     const tree = astgen.tree;
   2153     const opt_break_label, const opt_rhs = tree.nodeData(node).opt_token_and_opt_node;
   2154 
   2155     // Look for the label in the scope.
   2156     var scope = parent_scope;
   2157     while (true) {
   2158         switch (scope.tag) {
   2159             .gen_zir => {
   2160                 const block_gz = scope.cast(GenZir).?;
   2161 
   2162                 if (block_gz.cur_defer_node.unwrap()) |cur_defer_node| {
   2163                     // We are breaking out of a `defer` block.
   2164                     return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{
   2165                         try astgen.errNoteNode(
   2166                             cur_defer_node,
   2167                             "defer expression here",
   2168                             .{},
   2169                         ),
   2170                     });
   2171                 }
   2172 
   2173                 const block_inst = blk: {
   2174                     if (opt_break_label.unwrap()) |break_label| {
   2175                         if (block_gz.label) |*label| {
   2176                             if (try astgen.tokenIdentEql(label.token, break_label)) {
   2177                                 label.used = true;
   2178                                 break :blk label.block_inst;
   2179                             }
   2180                         }
   2181                     } else if (block_gz.break_block.unwrap()) |i| {
   2182                         break :blk i;
   2183                     }
   2184                     // If not the target, start over with the parent
   2185                     scope = block_gz.parent;
   2186                     continue;
   2187                 };
   2188                 // If we made it here, this block is the target of the break expr
   2189 
   2190                 const break_tag: Zir.Inst.Tag = if (block_gz.is_inline)
   2191                     .break_inline
   2192                 else
   2193                     .@"break";
   2194 
   2195                 const rhs = opt_rhs.unwrap() orelse {
   2196                     _ = try rvalue(parent_gz, block_gz.break_result_info, .void_value, node);
   2197 
   2198                     try genDefers(parent_gz, scope, parent_scope, .normal_only);
   2199 
   2200                     // As our last action before the break, "pop" the error trace if needed
   2201                     if (!block_gz.is_comptime)
   2202                         _ = try parent_gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, node);
   2203 
   2204                     _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
   2205                     return Zir.Inst.Ref.unreachable_value;
   2206                 };
   2207 
   2208                 const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_info, rhs, node);
   2209 
   2210                 try genDefers(parent_gz, scope, parent_scope, .normal_only);
   2211 
   2212                 // As our last action before the break, "pop" the error trace if needed
   2213                 if (!block_gz.is_comptime)
   2214                     try restoreErrRetIndex(parent_gz, .{ .block = block_inst }, block_gz.break_result_info, rhs, operand);
   2215 
   2216                 switch (block_gz.break_result_info.rl) {
   2217                     .ptr => {
   2218                         // In this case we don't have any mechanism to intercept it;
   2219                         // we assume the result location is written, and we break with void.
   2220                         _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
   2221                     },
   2222                     .discard => {
   2223                         _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
   2224                     },
   2225                     else => {
   2226                         _ = try parent_gz.addBreakWithSrcNode(break_tag, block_inst, operand, rhs);
   2227                     },
   2228                 }
   2229                 return Zir.Inst.Ref.unreachable_value;
   2230             },
   2231             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   2232             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   2233             .namespace => break,
   2234             .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   2235             .top => unreachable,
   2236         }
   2237     }
   2238     if (opt_break_label.unwrap()) |break_label| {
   2239         const label_name = try astgen.identifierTokenString(break_label);
   2240         return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
   2241     } else {
   2242         return astgen.failNode(node, "break expression outside loop", .{});
   2243     }
   2244 }
   2245 
   2246 fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   2247     const astgen = parent_gz.astgen;
   2248     const tree = astgen.tree;
   2249     const opt_break_label, const opt_rhs = tree.nodeData(node).opt_token_and_opt_node;
   2250 
   2251     if (opt_break_label == .none and opt_rhs != .none) {
   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.unwrap()) |cur_defer_node| {
   2263                     return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{
   2264                         try astgen.errNoteNode(
   2265                             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 (opt_break_label.unwrap()) |break_label| 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 (opt_rhs != .none) 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 (opt_rhs.unwrap()) |rhs| {
   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 (opt_break_label.unwrap()) |break_label| {
   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 
   2367     var stmt_buf: [2]Ast.Node.Index = undefined;
   2368     const statements = tree.blockStatements(&stmt_buf, node) orelse
   2369         return expr(gz, scope, ri, node);
   2370 
   2371     const lbrace = tree.nodeMainToken(node);
   2372 
   2373     if (tree.isTokenPrecededByTags(lbrace, &.{ .identifier, .colon })) {
   2374         // Labeled blocks are tricky - forwarding result location information properly is non-trivial,
   2375         // plus if this block is exited with a `break_inline` we aren't allowed multiple breaks. This
   2376         // case is rare, so just treat it as a normal expression and create a nested block.
   2377         return blockExpr(gz, scope, ri, node, statements, block_kind);
   2378     }
   2379 
   2380     var sub_gz = gz.makeSubBlock(scope);
   2381     try blockExprStmts(&sub_gz, &sub_gz.base, statements, block_kind);
   2382 
   2383     return rvalue(gz, ri, .void_value, node);
   2384 }
   2385 
   2386 const BlockKind = enum { normal, allow_branch_hint };
   2387 
   2388 fn blockExpr(
   2389     gz: *GenZir,
   2390     scope: *Scope,
   2391     ri: ResultInfo,
   2392     block_node: Ast.Node.Index,
   2393     statements: []const Ast.Node.Index,
   2394     kind: BlockKind,
   2395 ) InnerError!Zir.Inst.Ref {
   2396     const astgen = gz.astgen;
   2397     const tree = astgen.tree;
   2398 
   2399     const lbrace = tree.nodeMainToken(block_node);
   2400     if (tree.isTokenPrecededByTags(lbrace, &.{ .identifier, .colon })) {
   2401         return labeledBlockExpr(gz, scope, ri, block_node, statements, false, kind);
   2402     }
   2403 
   2404     if (!gz.is_comptime) {
   2405         // Since this block is unlabeled, its control flow is effectively linear and we
   2406         // can *almost* get away with inlining the block here. However, we actually need
   2407         // to preserve the .block for Sema, to properly pop the error return trace.
   2408 
   2409         const block_tag: Zir.Inst.Tag = .block;
   2410         const block_inst = try gz.makeBlockInst(block_tag, block_node);
   2411         try gz.instructions.append(astgen.gpa, block_inst);
   2412 
   2413         var block_scope = gz.makeSubBlock(scope);
   2414         defer block_scope.unstack();
   2415 
   2416         try blockExprStmts(&block_scope, &block_scope.base, statements, kind);
   2417 
   2418         if (!block_scope.endsWithNoReturn()) {
   2419             // As our last action before the break, "pop" the error trace if needed
   2420             _ = try gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, block_node);
   2421             // No `rvalue` call here, as the block result is always `void`, so we do that below.
   2422             _ = try block_scope.addBreak(.@"break", block_inst, .void_value);
   2423         }
   2424 
   2425         try block_scope.setBlockBody(block_inst);
   2426     } else {
   2427         var sub_gz = gz.makeSubBlock(scope);
   2428         try blockExprStmts(&sub_gz, &sub_gz.base, statements, kind);
   2429     }
   2430 
   2431     return rvalue(gz, ri, .void_value, block_node);
   2432 }
   2433 
   2434 fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.TokenIndex) !void {
   2435     // Look for the label in the scope.
   2436     var scope = parent_scope;
   2437     while (true) {
   2438         switch (scope.tag) {
   2439             .gen_zir => {
   2440                 const gen_zir = scope.cast(GenZir).?;
   2441                 if (gen_zir.label) |prev_label| {
   2442                     if (try astgen.tokenIdentEql(label, prev_label.token)) {
   2443                         const label_name = try astgen.identifierTokenString(label);
   2444                         return astgen.failTokNotes(label, "redefinition of label '{s}'", .{
   2445                             label_name,
   2446                         }, &[_]u32{
   2447                             try astgen.errNoteTok(
   2448                                 prev_label.token,
   2449                                 "previous definition here",
   2450                                 .{},
   2451                             ),
   2452                         });
   2453                     }
   2454                 }
   2455                 scope = gen_zir.parent;
   2456             },
   2457             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   2458             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   2459             .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   2460             .namespace => break,
   2461             .top => unreachable,
   2462         }
   2463     }
   2464 }
   2465 
   2466 fn labeledBlockExpr(
   2467     gz: *GenZir,
   2468     parent_scope: *Scope,
   2469     ri: ResultInfo,
   2470     block_node: Ast.Node.Index,
   2471     statements: []const Ast.Node.Index,
   2472     force_comptime: bool,
   2473     block_kind: BlockKind,
   2474 ) InnerError!Zir.Inst.Ref {
   2475     const astgen = gz.astgen;
   2476     const tree = astgen.tree;
   2477 
   2478     const lbrace = tree.nodeMainToken(block_node);
   2479     const label_token = lbrace - 2;
   2480     assert(tree.tokenTag(label_token) == .identifier);
   2481 
   2482     try astgen.checkLabelRedefinition(parent_scope, label_token);
   2483 
   2484     const need_rl = astgen.nodes_need_rl.contains(block_node);
   2485     const block_ri: ResultInfo = if (need_rl) ri else .{
   2486         .rl = switch (ri.rl) {
   2487             .ptr => .{ .ty = (try ri.rl.resultType(gz, block_node)).? },
   2488             .inferred_ptr => .none,
   2489             else => ri.rl,
   2490         },
   2491         .ctx = ri.ctx,
   2492     };
   2493     // We need to call `rvalue` to write through to the pointer only if we had a
   2494     // result pointer and aren't forwarding it.
   2495     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   2496     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   2497 
   2498     // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct
   2499     // so that break statements can reference it.
   2500     const block_inst = try gz.makeBlockInst(if (force_comptime) .block_comptime else .block, block_node);
   2501     try gz.instructions.append(astgen.gpa, block_inst);
   2502     var block_scope = gz.makeSubBlock(parent_scope);
   2503     block_scope.is_inline = force_comptime;
   2504     block_scope.label = GenZir.Label{
   2505         .token = label_token,
   2506         .block_inst = block_inst,
   2507     };
   2508     block_scope.setBreakResultInfo(block_ri);
   2509     if (force_comptime) block_scope.is_comptime = true;
   2510     defer block_scope.unstack();
   2511 
   2512     try blockExprStmts(&block_scope, &block_scope.base, statements, block_kind);
   2513     if (!block_scope.endsWithNoReturn()) {
   2514         // As our last action before the return, "pop" the error trace if needed
   2515         _ = try gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, block_node);
   2516         const result = try rvalue(gz, block_scope.break_result_info, .void_value, block_node);
   2517         const break_tag: Zir.Inst.Tag = if (force_comptime) .break_inline else .@"break";
   2518         _ = try block_scope.addBreak(break_tag, block_inst, result);
   2519     }
   2520 
   2521     if (!block_scope.label.?.used) {
   2522         try astgen.appendErrorTok(label_token, "unused block label", .{});
   2523     }
   2524 
   2525     if (force_comptime) {
   2526         try block_scope.setBlockComptimeBody(block_inst, .comptime_keyword);
   2527     } else {
   2528         try block_scope.setBlockBody(block_inst);
   2529     }
   2530 
   2531     if (need_result_rvalue) {
   2532         return rvalue(gz, ri, block_inst.toRef(), block_node);
   2533     } else {
   2534         return block_inst.toRef();
   2535     }
   2536 }
   2537 
   2538 fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Node.Index, block_kind: BlockKind) !void {
   2539     const astgen = gz.astgen;
   2540     const tree = astgen.tree;
   2541 
   2542     if (statements.len == 0) return;
   2543 
   2544     var block_arena = std.heap.ArenaAllocator.init(gz.astgen.gpa);
   2545     defer block_arena.deinit();
   2546     const block_arena_allocator = block_arena.allocator();
   2547 
   2548     var noreturn_src_node: Ast.Node.OptionalIndex = .none;
   2549     var scope = parent_scope;
   2550     for (statements, 0..) |statement, stmt_idx| {
   2551         if (noreturn_src_node.unwrap()) |src_node| {
   2552             try astgen.appendErrorNodeNotes(
   2553                 statement,
   2554                 "unreachable code",
   2555                 .{},
   2556                 &[_]u32{
   2557                     try astgen.errNoteNode(
   2558                         src_node,
   2559                         "control flow is diverted here",
   2560                         .{},
   2561                     ),
   2562                 },
   2563             );
   2564         }
   2565         const allow_branch_hint = switch (block_kind) {
   2566             .normal => false,
   2567             .allow_branch_hint => stmt_idx == 0,
   2568         };
   2569         var inner_node = statement;
   2570         while (true) {
   2571             switch (tree.nodeTag(inner_node)) {
   2572                 // zig fmt: off
   2573                 .global_var_decl,
   2574                 .local_var_decl,
   2575                 .simple_var_decl,
   2576                 .aligned_var_decl, => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.fullVarDecl(statement).?),
   2577 
   2578                 .assign_destructure => scope = try assignDestructureMaybeDecls(gz, scope, statement, block_arena_allocator),
   2579 
   2580                 .@"defer"    => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_normal),
   2581                 .@"errdefer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_error),
   2582 
   2583                 .assign => try assign(gz, scope, statement),
   2584 
   2585                 .assign_shl => try assignShift(gz, scope, statement, .shl),
   2586                 .assign_shr => try assignShift(gz, scope, statement, .shr),
   2587 
   2588                 .assign_bit_and  => try assignOp(gz, scope, statement, .bit_and),
   2589                 .assign_bit_or   => try assignOp(gz, scope, statement, .bit_or),
   2590                 .assign_bit_xor  => try assignOp(gz, scope, statement, .xor),
   2591                 .assign_div      => try assignOp(gz, scope, statement, .div),
   2592                 .assign_sub      => try assignOp(gz, scope, statement, .sub),
   2593                 .assign_sub_wrap => try assignOp(gz, scope, statement, .subwrap),
   2594                 .assign_mod      => try assignOp(gz, scope, statement, .mod_rem),
   2595                 .assign_add      => try assignOp(gz, scope, statement, .add),
   2596                 .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap),
   2597                 .assign_mul      => try assignOp(gz, scope, statement, .mul),
   2598                 .assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap),
   2599 
   2600                 .grouped_expression => {
   2601                     inner_node = tree.nodeData(statement).node_and_token[0];
   2602                     continue;
   2603                 },
   2604 
   2605                 .while_simple,
   2606                 .while_cont,
   2607                 .@"while", => _ = try whileExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullWhile(inner_node).?, true),
   2608 
   2609                 .for_simple,
   2610                 .@"for", => _ = try forExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullFor(inner_node).?, true),
   2611                 // zig fmt: on
   2612 
   2613                 // These cases are here to allow branch hints.
   2614                 .builtin_call_two,
   2615                 .builtin_call_two_comma,
   2616                 .builtin_call,
   2617                 .builtin_call_comma,
   2618                 => {
   2619                     var buf: [2]Ast.Node.Index = undefined;
   2620                     const params = tree.builtinCallParams(&buf, inner_node).?;
   2621 
   2622                     try emitDbgNode(gz, inner_node);
   2623                     const result = try builtinCall(gz, scope, .{ .rl = .none }, inner_node, params, allow_branch_hint);
   2624                     noreturn_src_node = try addEnsureResult(gz, result, inner_node);
   2625                 },
   2626 
   2627                 else => noreturn_src_node = try unusedResultExpr(gz, scope, inner_node),
   2628             }
   2629             break;
   2630         }
   2631     }
   2632 
   2633     if (noreturn_src_node == .none) {
   2634         try genDefers(gz, parent_scope, scope, .normal_only);
   2635     }
   2636     try checkUsed(gz, parent_scope, scope);
   2637 }
   2638 
   2639 /// Returns AST source node of the thing that is noreturn if the statement is
   2640 /// definitely `noreturn`. Otherwise returns .none.
   2641 fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) InnerError!Ast.Node.OptionalIndex {
   2642     try emitDbgNode(gz, statement);
   2643     // We need to emit an error if the result is not `noreturn` or `void`, but
   2644     // we want to avoid adding the ZIR instruction if possible for performance.
   2645     const maybe_unused_result = try expr(gz, scope, .{ .rl = .none }, statement);
   2646     return addEnsureResult(gz, maybe_unused_result, statement);
   2647 }
   2648 
   2649 fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: Ast.Node.Index) InnerError!Ast.Node.OptionalIndex {
   2650     var noreturn_src_node: Ast.Node.OptionalIndex = .none;
   2651     const elide_check = if (maybe_unused_result.toIndex()) |inst| b: {
   2652         // Note that this array becomes invalid after appending more items to it
   2653         // in the above while loop.
   2654         const zir_tags = gz.astgen.instructions.items(.tag);
   2655         switch (zir_tags[@intFromEnum(inst)]) {
   2656             // For some instructions, modify the zir data
   2657             // so we can avoid a separate ensure_result_used instruction.
   2658             .call, .field_call => {
   2659                 const break_extra = gz.astgen.instructions.items(.data)[@intFromEnum(inst)].pl_node.payload_index;
   2660                 comptime assert(std.meta.fieldIndex(Zir.Inst.Call, "flags") ==
   2661                     std.meta.fieldIndex(Zir.Inst.FieldCall, "flags"));
   2662                 const flags: *Zir.Inst.Call.Flags = @ptrCast(&gz.astgen.extra.items[
   2663                     break_extra + std.meta.fieldIndex(Zir.Inst.Call, "flags").?
   2664                 ]);
   2665                 flags.ensure_result_used = true;
   2666                 break :b true;
   2667             },
   2668             .builtin_call => {
   2669                 const break_extra = gz.astgen.instructions.items(.data)[@intFromEnum(inst)].pl_node.payload_index;
   2670                 const flags: *Zir.Inst.BuiltinCall.Flags = @ptrCast(&gz.astgen.extra.items[
   2671                     break_extra + std.meta.fieldIndex(Zir.Inst.BuiltinCall, "flags").?
   2672                 ]);
   2673                 flags.ensure_result_used = true;
   2674                 break :b true;
   2675             },
   2676 
   2677             // ZIR instructions that might be a type other than `noreturn` or `void`.
   2678             .add,
   2679             .addwrap,
   2680             .add_sat,
   2681             .add_unsafe,
   2682             .param,
   2683             .param_comptime,
   2684             .param_anytype,
   2685             .param_anytype_comptime,
   2686             .alloc,
   2687             .alloc_mut,
   2688             .alloc_comptime_mut,
   2689             .alloc_inferred,
   2690             .alloc_inferred_mut,
   2691             .alloc_inferred_comptime,
   2692             .alloc_inferred_comptime_mut,
   2693             .make_ptr_const,
   2694             .array_cat,
   2695             .array_mul,
   2696             .array_type,
   2697             .array_type_sentinel,
   2698             .elem_type,
   2699             .indexable_ptr_elem_type,
   2700             .vec_arr_elem_type,
   2701             .vector_type,
   2702             .indexable_ptr_len,
   2703             .anyframe_type,
   2704             .as_node,
   2705             .as_shift_operand,
   2706             .bit_and,
   2707             .bitcast,
   2708             .bit_or,
   2709             .block,
   2710             .block_comptime,
   2711             .block_inline,
   2712             .declaration,
   2713             .suspend_block,
   2714             .loop,
   2715             .bool_br_and,
   2716             .bool_br_or,
   2717             .bool_not,
   2718             .cmp_lt,
   2719             .cmp_lte,
   2720             .cmp_eq,
   2721             .cmp_gte,
   2722             .cmp_gt,
   2723             .cmp_neq,
   2724             .decl_ref,
   2725             .decl_val,
   2726             .load,
   2727             .div,
   2728             .elem_ptr,
   2729             .elem_val,
   2730             .elem_ptr_node,
   2731             .elem_val_node,
   2732             .elem_val_imm,
   2733             .field_ptr,
   2734             .field_val,
   2735             .field_ptr_named,
   2736             .field_val_named,
   2737             .func,
   2738             .func_inferred,
   2739             .func_fancy,
   2740             .int,
   2741             .int_big,
   2742             .float,
   2743             .float128,
   2744             .int_type,
   2745             .is_non_null,
   2746             .is_non_null_ptr,
   2747             .is_non_err,
   2748             .is_non_err_ptr,
   2749             .ret_is_non_err,
   2750             .mod_rem,
   2751             .mul,
   2752             .mulwrap,
   2753             .mul_sat,
   2754             .ref,
   2755             .shl,
   2756             .shl_sat,
   2757             .shr,
   2758             .str,
   2759             .sub,
   2760             .subwrap,
   2761             .sub_sat,
   2762             .negate,
   2763             .negate_wrap,
   2764             .typeof,
   2765             .typeof_builtin,
   2766             .xor,
   2767             .optional_type,
   2768             .optional_payload_safe,
   2769             .optional_payload_unsafe,
   2770             .optional_payload_safe_ptr,
   2771             .optional_payload_unsafe_ptr,
   2772             .err_union_payload_unsafe,
   2773             .err_union_payload_unsafe_ptr,
   2774             .err_union_code,
   2775             .err_union_code_ptr,
   2776             .ptr_type,
   2777             .enum_literal,
   2778             .decl_literal,
   2779             .decl_literal_no_coerce,
   2780             .merge_error_sets,
   2781             .error_union_type,
   2782             .bit_not,
   2783             .error_value,
   2784             .slice_start,
   2785             .slice_end,
   2786             .slice_sentinel,
   2787             .slice_length,
   2788             .slice_sentinel_ty,
   2789             .import,
   2790             .switch_block,
   2791             .switch_block_ref,
   2792             .switch_block_err_union,
   2793             .union_init,
   2794             .field_type_ref,
   2795             .error_set_decl,
   2796             .enum_from_int,
   2797             .int_from_enum,
   2798             .type_info,
   2799             .size_of,
   2800             .bit_size_of,
   2801             .typeof_log2_int_type,
   2802             .int_from_ptr,
   2803             .align_of,
   2804             .int_from_bool,
   2805             .embed_file,
   2806             .error_name,
   2807             .sqrt,
   2808             .sin,
   2809             .cos,
   2810             .tan,
   2811             .exp,
   2812             .exp2,
   2813             .log,
   2814             .log2,
   2815             .log10,
   2816             .abs,
   2817             .floor,
   2818             .ceil,
   2819             .trunc,
   2820             .round,
   2821             .tag_name,
   2822             .type_name,
   2823             .frame_type,
   2824             .int_from_float,
   2825             .float_from_int,
   2826             .ptr_from_int,
   2827             .float_cast,
   2828             .int_cast,
   2829             .ptr_cast,
   2830             .truncate,
   2831             .has_decl,
   2832             .has_field,
   2833             .clz,
   2834             .ctz,
   2835             .pop_count,
   2836             .byte_swap,
   2837             .bit_reverse,
   2838             .div_exact,
   2839             .div_floor,
   2840             .div_trunc,
   2841             .mod,
   2842             .rem,
   2843             .shl_exact,
   2844             .shr_exact,
   2845             .bit_offset_of,
   2846             .offset_of,
   2847             .splat,
   2848             .reduce,
   2849             .shuffle,
   2850             .atomic_load,
   2851             .atomic_rmw,
   2852             .mul_add,
   2853             .max,
   2854             .min,
   2855             .c_import,
   2856             .@"resume",
   2857             .ret_err_value_code,
   2858             .ret_ptr,
   2859             .ret_type,
   2860             .for_len,
   2861             .@"try",
   2862             .try_ptr,
   2863             .opt_eu_base_ptr_init,
   2864             .coerce_ptr_elem_ty,
   2865             .struct_init_empty,
   2866             .struct_init_empty_result,
   2867             .struct_init_empty_ref_result,
   2868             .struct_init_anon,
   2869             .struct_init,
   2870             .struct_init_ref,
   2871             .struct_init_field_type,
   2872             .struct_init_field_ptr,
   2873             .array_init_anon,
   2874             .array_init,
   2875             .array_init_ref,
   2876             .validate_array_init_ref_ty,
   2877             .array_init_elem_type,
   2878             .array_init_elem_ptr,
   2879             => break :b false,
   2880 
   2881             .extended => switch (gz.astgen.instructions.items(.data)[@intFromEnum(inst)].extended.opcode) {
   2882                 .breakpoint,
   2883                 .disable_instrumentation,
   2884                 .disable_intrinsics,
   2885                 .set_float_mode,
   2886                 .branch_hint,
   2887                 => break :b true,
   2888                 else => break :b false,
   2889             },
   2890 
   2891             // ZIR instructions that are always `noreturn`.
   2892             .@"break",
   2893             .break_inline,
   2894             .condbr,
   2895             .condbr_inline,
   2896             .compile_error,
   2897             .ret_node,
   2898             .ret_load,
   2899             .ret_implicit,
   2900             .ret_err_value,
   2901             .@"unreachable",
   2902             .repeat,
   2903             .repeat_inline,
   2904             .panic,
   2905             .trap,
   2906             .check_comptime_control_flow,
   2907             .switch_continue,
   2908             => {
   2909                 noreturn_src_node = statement.toOptional();
   2910                 break :b true;
   2911             },
   2912 
   2913             // ZIR instructions that are always `void`.
   2914             .dbg_stmt,
   2915             .dbg_var_ptr,
   2916             .dbg_var_val,
   2917             .ensure_result_used,
   2918             .ensure_result_non_error,
   2919             .ensure_err_union_payload_void,
   2920             .@"export",
   2921             .set_eval_branch_quota,
   2922             .atomic_store,
   2923             .store_node,
   2924             .store_to_inferred_ptr,
   2925             .resolve_inferred_alloc,
   2926             .set_runtime_safety,
   2927             .memcpy,
   2928             .memset,
   2929             .memmove,
   2930             .validate_deref,
   2931             .validate_destructure,
   2932             .save_err_ret_index,
   2933             .restore_err_ret_index_unconditional,
   2934             .restore_err_ret_index_fn_entry,
   2935             .validate_struct_init_ty,
   2936             .validate_struct_init_result_ty,
   2937             .validate_ptr_struct_init,
   2938             .validate_array_init_ty,
   2939             .validate_array_init_result_ty,
   2940             .validate_ptr_array_init,
   2941             .validate_ref_ty,
   2942             .validate_const,
   2943             => break :b true,
   2944 
   2945             .@"defer" => unreachable,
   2946             .defer_err_code => unreachable,
   2947         }
   2948     } else switch (maybe_unused_result) {
   2949         .none => unreachable,
   2950 
   2951         .unreachable_value => b: {
   2952             noreturn_src_node = statement.toOptional();
   2953             break :b true;
   2954         },
   2955 
   2956         .void_value => true,
   2957 
   2958         else => false,
   2959     };
   2960     if (!elide_check) {
   2961         _ = try gz.addUnNode(.ensure_result_used, maybe_unused_result, statement);
   2962     }
   2963     return noreturn_src_node;
   2964 }
   2965 
   2966 fn countDefers(outer_scope: *Scope, inner_scope: *Scope) struct {
   2967     have_any: bool,
   2968     have_normal: bool,
   2969     have_err: bool,
   2970     need_err_code: bool,
   2971 } {
   2972     var have_normal = false;
   2973     var have_err = false;
   2974     var need_err_code = false;
   2975     var scope = inner_scope;
   2976     while (scope != outer_scope) {
   2977         switch (scope.tag) {
   2978             .gen_zir => scope = scope.cast(GenZir).?.parent,
   2979             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   2980             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   2981             .defer_normal => {
   2982                 const defer_scope = scope.cast(Scope.Defer).?;
   2983                 scope = defer_scope.parent;
   2984 
   2985                 have_normal = true;
   2986             },
   2987             .defer_error => {
   2988                 const defer_scope = scope.cast(Scope.Defer).?;
   2989                 scope = defer_scope.parent;
   2990 
   2991                 have_err = true;
   2992 
   2993                 const have_err_payload = defer_scope.remapped_err_code != .none;
   2994                 need_err_code = need_err_code or have_err_payload;
   2995             },
   2996             .namespace => unreachable,
   2997             .top => unreachable,
   2998         }
   2999     }
   3000     return .{
   3001         .have_any = have_normal or have_err,
   3002         .have_normal = have_normal,
   3003         .have_err = have_err,
   3004         .need_err_code = need_err_code,
   3005     };
   3006 }
   3007 
   3008 const DefersToEmit = union(enum) {
   3009     both: Zir.Inst.Ref, // err code
   3010     both_sans_err,
   3011     normal_only,
   3012 };
   3013 
   3014 fn genDefers(
   3015     gz: *GenZir,
   3016     outer_scope: *Scope,
   3017     inner_scope: *Scope,
   3018     which_ones: DefersToEmit,
   3019 ) InnerError!void {
   3020     const gpa = gz.astgen.gpa;
   3021 
   3022     var scope = inner_scope;
   3023     while (scope != outer_scope) {
   3024         switch (scope.tag) {
   3025             .gen_zir => scope = scope.cast(GenZir).?.parent,
   3026             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   3027             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   3028             .defer_normal => {
   3029                 const defer_scope = scope.cast(Scope.Defer).?;
   3030                 scope = defer_scope.parent;
   3031                 try gz.addDefer(defer_scope.index, defer_scope.len);
   3032             },
   3033             .defer_error => {
   3034                 const defer_scope = scope.cast(Scope.Defer).?;
   3035                 scope = defer_scope.parent;
   3036                 switch (which_ones) {
   3037                     .both_sans_err => {
   3038                         try gz.addDefer(defer_scope.index, defer_scope.len);
   3039                     },
   3040                     .both => |err_code| {
   3041                         if (defer_scope.remapped_err_code.unwrap()) |remapped_err_code| {
   3042                             try gz.instructions.ensureUnusedCapacity(gpa, 1);
   3043                             try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
   3044 
   3045                             const payload_index = try gz.astgen.addExtra(Zir.Inst.DeferErrCode{
   3046                                 .remapped_err_code = remapped_err_code,
   3047                                 .index = defer_scope.index,
   3048                                 .len = defer_scope.len,
   3049                             });
   3050                             const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
   3051                             gz.astgen.instructions.appendAssumeCapacity(.{
   3052                                 .tag = .defer_err_code,
   3053                                 .data = .{ .defer_err_code = .{
   3054                                     .err_code = err_code,
   3055                                     .payload_index = payload_index,
   3056                                 } },
   3057                             });
   3058                             gz.instructions.appendAssumeCapacity(new_index);
   3059                         } else {
   3060                             try gz.addDefer(defer_scope.index, defer_scope.len);
   3061                         }
   3062                     },
   3063                     .normal_only => continue,
   3064                 }
   3065             },
   3066             .namespace => unreachable,
   3067             .top => unreachable,
   3068         }
   3069     }
   3070 }
   3071 
   3072 fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!void {
   3073     const astgen = gz.astgen;
   3074 
   3075     var scope = inner_scope;
   3076     while (scope != outer_scope) {
   3077         switch (scope.tag) {
   3078             .gen_zir => scope = scope.cast(GenZir).?.parent,
   3079             .local_val => {
   3080                 const s = scope.cast(Scope.LocalVal).?;
   3081                 if (s.used == .none and s.discarded == .none) {
   3082                     try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
   3083                 } else if (s.used != .none and s.discarded != .none) {
   3084                     try astgen.appendErrorTokNotes(s.discarded.unwrap().?, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{
   3085                         try gz.astgen.errNoteTok(s.used.unwrap().?, "used here", .{}),
   3086                     });
   3087                 }
   3088                 scope = s.parent;
   3089             },
   3090             .local_ptr => {
   3091                 const s = scope.cast(Scope.LocalPtr).?;
   3092                 if (s.used == .none and s.discarded == .none) {
   3093                     try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
   3094                 } else {
   3095                     if (s.used != .none and s.discarded != .none) {
   3096                         try astgen.appendErrorTokNotes(s.discarded.unwrap().?, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{
   3097                             try astgen.errNoteTok(s.used.unwrap().?, "used here", .{}),
   3098                         });
   3099                     }
   3100                     if (s.id_cat == .@"local variable" and !s.used_as_lvalue) {
   3101                         try astgen.appendErrorTokNotes(s.token_src, "local variable is never mutated", .{}, &.{
   3102                             try astgen.errNoteTok(s.token_src, "consider using 'const'", .{}),
   3103                         });
   3104                     }
   3105                 }
   3106 
   3107                 scope = s.parent;
   3108             },
   3109             .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   3110             .namespace => unreachable,
   3111             .top => unreachable,
   3112         }
   3113     }
   3114 }
   3115 
   3116 fn deferStmt(
   3117     gz: *GenZir,
   3118     scope: *Scope,
   3119     node: Ast.Node.Index,
   3120     block_arena: Allocator,
   3121     scope_tag: Scope.Tag,
   3122 ) InnerError!*Scope {
   3123     var defer_gen = gz.makeSubBlock(scope);
   3124     defer_gen.cur_defer_node = node.toOptional();
   3125     defer_gen.any_defer_node = node.toOptional();
   3126     defer defer_gen.unstack();
   3127 
   3128     const tree = gz.astgen.tree;
   3129     var local_val_scope: Scope.LocalVal = undefined;
   3130     var opt_remapped_err_code: Zir.Inst.OptionalIndex = .none;
   3131     const sub_scope = if (scope_tag != .defer_error) &defer_gen.base else blk: {
   3132         const payload_token = tree.nodeData(node).opt_token_and_node[0].unwrap() orelse break :blk &defer_gen.base;
   3133         const ident_name = try gz.astgen.identAsString(payload_token);
   3134         if (std.mem.eql(u8, tree.tokenSlice(payload_token), "_")) {
   3135             try gz.astgen.appendErrorTok(payload_token, "discard of error capture; omit it instead", .{});
   3136             break :blk &defer_gen.base;
   3137         }
   3138         const remapped_err_code: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
   3139         opt_remapped_err_code = remapped_err_code.toOptional();
   3140         try gz.astgen.instructions.append(gz.astgen.gpa, .{
   3141             .tag = .extended,
   3142             .data = .{ .extended = .{
   3143                 .opcode = .value_placeholder,
   3144                 .small = undefined,
   3145                 .operand = undefined,
   3146             } },
   3147         });
   3148         const remapped_err_code_ref = remapped_err_code.toRef();
   3149         local_val_scope = .{
   3150             .parent = &defer_gen.base,
   3151             .gen_zir = gz,
   3152             .name = ident_name,
   3153             .inst = remapped_err_code_ref,
   3154             .token_src = payload_token,
   3155             .id_cat = .capture,
   3156         };
   3157         try gz.addDbgVar(.dbg_var_val, ident_name, remapped_err_code_ref);
   3158         break :blk &local_val_scope.base;
   3159     };
   3160     const expr_node = switch (scope_tag) {
   3161         .defer_normal => tree.nodeData(node).node,
   3162         .defer_error => tree.nodeData(node).opt_token_and_node[1],
   3163         else => unreachable,
   3164     };
   3165     _ = try unusedResultExpr(&defer_gen, sub_scope, expr_node);
   3166     try checkUsed(gz, scope, sub_scope);
   3167     _ = try defer_gen.addBreak(.break_inline, @enumFromInt(0), .void_value);
   3168 
   3169     const body = defer_gen.instructionsSlice();
   3170     const extra_insts: []const Zir.Inst.Index = if (opt_remapped_err_code.unwrap()) |ec| &.{ec} else &.{};
   3171     const body_len = gz.astgen.countBodyLenAfterFixupsExtraRefs(body, extra_insts);
   3172 
   3173     const index: u32 = @intCast(gz.astgen.extra.items.len);
   3174     try gz.astgen.extra.ensureUnusedCapacity(gz.astgen.gpa, body_len);
   3175     gz.astgen.appendBodyWithFixupsExtraRefsArrayList(&gz.astgen.extra, body, extra_insts);
   3176 
   3177     const defer_scope = try block_arena.create(Scope.Defer);
   3178 
   3179     defer_scope.* = .{
   3180         .base = .{ .tag = scope_tag },
   3181         .parent = scope,
   3182         .index = index,
   3183         .len = body_len,
   3184         .remapped_err_code = opt_remapped_err_code,
   3185     };
   3186     return &defer_scope.base;
   3187 }
   3188 
   3189 fn varDecl(
   3190     gz: *GenZir,
   3191     scope: *Scope,
   3192     node: Ast.Node.Index,
   3193     block_arena: Allocator,
   3194     var_decl: Ast.full.VarDecl,
   3195 ) InnerError!*Scope {
   3196     try emitDbgNode(gz, node);
   3197     const astgen = gz.astgen;
   3198     const tree = astgen.tree;
   3199 
   3200     const name_token = var_decl.ast.mut_token + 1;
   3201     const ident_name_raw = tree.tokenSlice(name_token);
   3202     if (mem.eql(u8, ident_name_raw, "_")) {
   3203         return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{});
   3204     }
   3205     const ident_name = try astgen.identAsString(name_token);
   3206 
   3207     try astgen.detectLocalShadowing(
   3208         scope,
   3209         ident_name,
   3210         name_token,
   3211         ident_name_raw,
   3212         if (tree.tokenTag(var_decl.ast.mut_token) == .keyword_const) .@"local constant" else .@"local variable",
   3213     );
   3214 
   3215     const init_node = var_decl.ast.init_node.unwrap() orelse {
   3216         return astgen.failNode(node, "variables must be initialized", .{});
   3217     };
   3218 
   3219     if (var_decl.ast.addrspace_node.unwrap()) |addrspace_node| {
   3220         return astgen.failTok(tree.nodeMainToken(addrspace_node), "cannot set address space of local variable '{s}'", .{ident_name_raw});
   3221     }
   3222 
   3223     if (var_decl.ast.section_node.unwrap()) |section_node| {
   3224         return astgen.failTok(tree.nodeMainToken(section_node), "cannot set section of local variable '{s}'", .{ident_name_raw});
   3225     }
   3226 
   3227     const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node.unwrap()) |align_node|
   3228         try expr(gz, scope, coerced_align_ri, align_node)
   3229     else
   3230         .none;
   3231 
   3232     switch (tree.tokenTag(var_decl.ast.mut_token)) {
   3233         .keyword_const => {
   3234             if (var_decl.comptime_token) |comptime_token| {
   3235                 try astgen.appendErrorTok(comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
   3236             }
   3237 
   3238             // `comptime const` is a non-fatal error; treat it like the init was marked `comptime`.
   3239             const force_comptime = var_decl.comptime_token != null;
   3240 
   3241             // Depending on the type of AST the initialization expression is, we may need an lvalue
   3242             // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
   3243             // the variable, no memory location needed.
   3244             if (align_inst == .none and
   3245                 !astgen.nodes_need_rl.contains(node))
   3246             {
   3247                 const result_info: ResultInfo = if (var_decl.ast.type_node.unwrap()) |type_node| .{
   3248                     .rl = .{ .ty = try typeExpr(gz, scope, type_node) },
   3249                     .ctx = .const_init,
   3250                 } else .{ .rl = .none, .ctx = .const_init };
   3251                 const init_inst: Zir.Inst.Ref = try nameStratExpr(gz, scope, result_info, init_node, .dbg_var) orelse
   3252                     try reachableExprComptime(gz, scope, result_info, init_node, node, if (force_comptime) .comptime_keyword else null);
   3253 
   3254                 _ = try gz.addUnNode(.validate_const, init_inst, init_node);
   3255                 try gz.addDbgVar(.dbg_var_val, ident_name, init_inst);
   3256 
   3257                 // The const init expression may have modified the error return trace, so signal
   3258                 // to Sema that it should save the new index for restoring later.
   3259                 if (nodeMayAppendToErrorTrace(tree, init_node))
   3260                     _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst });
   3261 
   3262                 const sub_scope = try block_arena.create(Scope.LocalVal);
   3263                 sub_scope.* = .{
   3264                     .parent = scope,
   3265                     .gen_zir = gz,
   3266                     .name = ident_name,
   3267                     .inst = init_inst,
   3268                     .token_src = name_token,
   3269                     .id_cat = .@"local constant",
   3270                 };
   3271                 return &sub_scope.base;
   3272             }
   3273 
   3274             const is_comptime = gz.is_comptime or
   3275                 tree.nodeTag(init_node) == .@"comptime";
   3276 
   3277             const init_rl: ResultInfo.Loc = if (var_decl.ast.type_node.unwrap()) |type_node| init_rl: {
   3278                 const type_inst = try typeExpr(gz, scope, type_node);
   3279                 if (align_inst == .none) {
   3280                     break :init_rl .{ .ptr = .{ .inst = try gz.addUnNode(.alloc, type_inst, node) } };
   3281                 } else {
   3282                     break :init_rl .{ .ptr = .{ .inst = try gz.addAllocExtended(.{
   3283                         .node = node,
   3284                         .type_inst = type_inst,
   3285                         .align_inst = align_inst,
   3286                         .is_const = true,
   3287                         .is_comptime = is_comptime,
   3288                     }) } };
   3289                 }
   3290             } else init_rl: {
   3291                 const alloc_inst = if (align_inst == .none) ptr: {
   3292                     const tag: Zir.Inst.Tag = if (is_comptime)
   3293                         .alloc_inferred_comptime
   3294                     else
   3295                         .alloc_inferred;
   3296                     break :ptr try gz.addNode(tag, node);
   3297                 } else ptr: {
   3298                     break :ptr try gz.addAllocExtended(.{
   3299                         .node = node,
   3300                         .type_inst = .none,
   3301                         .align_inst = align_inst,
   3302                         .is_const = true,
   3303                         .is_comptime = is_comptime,
   3304                     });
   3305                 };
   3306                 break :init_rl .{ .inferred_ptr = alloc_inst };
   3307             };
   3308             const var_ptr: Zir.Inst.Ref, const resolve_inferred: bool = switch (init_rl) {
   3309                 .ptr => |ptr| .{ ptr.inst, false },
   3310                 .inferred_ptr => |inst| .{ inst, true },
   3311                 else => unreachable,
   3312             };
   3313             const init_result_info: ResultInfo = .{ .rl = init_rl, .ctx = .const_init };
   3314 
   3315             const init_inst: Zir.Inst.Ref = try nameStratExpr(gz, scope, init_result_info, init_node, .dbg_var) orelse
   3316                 try reachableExprComptime(gz, scope, init_result_info, init_node, node, if (force_comptime) .comptime_keyword else null);
   3317 
   3318             // The const init expression may have modified the error return trace, so signal
   3319             // to Sema that it should save the new index for restoring later.
   3320             if (nodeMayAppendToErrorTrace(tree, init_node))
   3321                 _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst });
   3322 
   3323             const const_ptr = if (resolve_inferred)
   3324                 try gz.addUnNode(.resolve_inferred_alloc, var_ptr, node)
   3325             else
   3326                 try gz.addUnNode(.make_ptr_const, var_ptr, node);
   3327 
   3328             try gz.addDbgVar(.dbg_var_ptr, ident_name, const_ptr);
   3329 
   3330             const sub_scope = try block_arena.create(Scope.LocalPtr);
   3331             sub_scope.* = .{
   3332                 .parent = scope,
   3333                 .gen_zir = gz,
   3334                 .name = ident_name,
   3335                 .ptr = const_ptr,
   3336                 .token_src = name_token,
   3337                 .maybe_comptime = true,
   3338                 .id_cat = .@"local constant",
   3339             };
   3340             return &sub_scope.base;
   3341         },
   3342         .keyword_var => {
   3343             if (var_decl.comptime_token != null and gz.is_comptime)
   3344                 return astgen.failTok(var_decl.comptime_token.?, "'comptime var' is redundant in comptime scope", .{});
   3345             const is_comptime = var_decl.comptime_token != null or gz.is_comptime;
   3346             const alloc: Zir.Inst.Ref, const resolve_inferred: bool, const result_info: ResultInfo = if (var_decl.ast.type_node.unwrap()) |type_node| a: {
   3347                 const type_inst = try typeExpr(gz, scope, type_node);
   3348                 const alloc = alloc: {
   3349                     if (align_inst == .none) {
   3350                         const tag: Zir.Inst.Tag = if (is_comptime)
   3351                             .alloc_comptime_mut
   3352                         else
   3353                             .alloc_mut;
   3354                         break :alloc try gz.addUnNode(tag, type_inst, node);
   3355                     } else {
   3356                         break :alloc try gz.addAllocExtended(.{
   3357                             .node = node,
   3358                             .type_inst = type_inst,
   3359                             .align_inst = align_inst,
   3360                             .is_const = false,
   3361                             .is_comptime = is_comptime,
   3362                         });
   3363                     }
   3364                 };
   3365                 break :a .{ alloc, false, .{ .rl = .{ .ptr = .{ .inst = alloc } } } };
   3366             } else a: {
   3367                 const alloc = alloc: {
   3368                     if (align_inst == .none) {
   3369                         const tag: Zir.Inst.Tag = if (is_comptime)
   3370                             .alloc_inferred_comptime_mut
   3371                         else
   3372                             .alloc_inferred_mut;
   3373                         break :alloc try gz.addNode(tag, node);
   3374                     } else {
   3375                         break :alloc try gz.addAllocExtended(.{
   3376                             .node = node,
   3377                             .type_inst = .none,
   3378                             .align_inst = align_inst,
   3379                             .is_const = false,
   3380                             .is_comptime = is_comptime,
   3381                         });
   3382                     }
   3383                 };
   3384                 break :a .{ alloc, true, .{ .rl = .{ .inferred_ptr = alloc } } };
   3385             };
   3386             _ = try nameStratExpr(
   3387                 gz,
   3388                 scope,
   3389                 result_info,
   3390                 init_node,
   3391                 .dbg_var,
   3392             ) orelse try reachableExprComptime(
   3393                 gz,
   3394                 scope,
   3395                 result_info,
   3396                 init_node,
   3397                 node,
   3398                 if (var_decl.comptime_token != null) .comptime_keyword else null,
   3399             );
   3400             const final_ptr: Zir.Inst.Ref = if (resolve_inferred) ptr: {
   3401                 break :ptr try gz.addUnNode(.resolve_inferred_alloc, alloc, node);
   3402             } else alloc;
   3403 
   3404             try gz.addDbgVar(.dbg_var_ptr, ident_name, final_ptr);
   3405 
   3406             const sub_scope = try block_arena.create(Scope.LocalPtr);
   3407             sub_scope.* = .{
   3408                 .parent = scope,
   3409                 .gen_zir = gz,
   3410                 .name = ident_name,
   3411                 .ptr = final_ptr,
   3412                 .token_src = name_token,
   3413                 .maybe_comptime = is_comptime,
   3414                 .id_cat = .@"local variable",
   3415             };
   3416             return &sub_scope.base;
   3417         },
   3418         else => unreachable,
   3419     }
   3420 }
   3421 
   3422 fn emitDbgNode(gz: *GenZir, node: Ast.Node.Index) !void {
   3423     // The instruction emitted here is for debugging runtime code.
   3424     // If the current block will be evaluated only during semantic analysis
   3425     // then no dbg_stmt ZIR instruction is needed.
   3426     if (gz.is_comptime) return;
   3427     const astgen = gz.astgen;
   3428     astgen.advanceSourceCursorToNode(node);
   3429     const line = astgen.source_line - gz.decl_line;
   3430     const column = astgen.source_column;
   3431     try emitDbgStmt(gz, .{ line, column });
   3432 }
   3433 
   3434 fn assign(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void {
   3435     try emitDbgNode(gz, infix_node);
   3436     const astgen = gz.astgen;
   3437     const tree = astgen.tree;
   3438 
   3439     const lhs, const rhs = tree.nodeData(infix_node).node_and_node;
   3440     if (tree.nodeTag(lhs) == .identifier) {
   3441         // This intentionally does not support `@"_"` syntax.
   3442         const ident_name = tree.tokenSlice(tree.nodeMainToken(lhs));
   3443         if (mem.eql(u8, ident_name, "_")) {
   3444             _ = try expr(gz, scope, .{ .rl = .discard, .ctx = .assignment }, rhs);
   3445             return;
   3446         }
   3447     }
   3448     const lvalue = try lvalExpr(gz, scope, lhs);
   3449     _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{
   3450         .inst = lvalue,
   3451         .src_node = infix_node,
   3452     } } }, rhs);
   3453 }
   3454 
   3455 /// Handles destructure assignments where no LHS is a `const` or `var` decl.
   3456 fn assignDestructure(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!void {
   3457     try emitDbgNode(gz, node);
   3458     const astgen = gz.astgen;
   3459     const tree = astgen.tree;
   3460 
   3461     const full = tree.assignDestructure(node);
   3462     if (full.comptime_token != null and gz.is_comptime) {
   3463         return astgen.appendErrorNode(node, "redundant comptime keyword in already comptime scope", .{});
   3464     }
   3465 
   3466     // If this expression is marked comptime, we must wrap the whole thing in a comptime block.
   3467     var gz_buf: GenZir = undefined;
   3468     const inner_gz = if (full.comptime_token) |_| bs: {
   3469         gz_buf = gz.makeSubBlock(scope);
   3470         gz_buf.is_comptime = true;
   3471         break :bs &gz_buf;
   3472     } else gz;
   3473     defer if (full.comptime_token) |_| inner_gz.unstack();
   3474 
   3475     const rl_components = try astgen.arena.alloc(ResultInfo.Loc.DestructureComponent, full.ast.variables.len);
   3476     for (rl_components, full.ast.variables) |*variable_rl, variable_node| {
   3477         if (tree.nodeTag(variable_node) == .identifier) {
   3478             // This intentionally does not support `@"_"` syntax.
   3479             const ident_name = tree.tokenSlice(tree.nodeMainToken(variable_node));
   3480             if (mem.eql(u8, ident_name, "_")) {
   3481                 variable_rl.* = .discard;
   3482                 continue;
   3483             }
   3484         }
   3485         variable_rl.* = .{ .typed_ptr = .{
   3486             .inst = try lvalExpr(inner_gz, scope, variable_node),
   3487             .src_node = variable_node,
   3488         } };
   3489     }
   3490 
   3491     const ri: ResultInfo = .{ .rl = .{ .destructure = .{
   3492         .src_node = node,
   3493         .components = rl_components,
   3494     } } };
   3495 
   3496     _ = try expr(inner_gz, scope, ri, full.ast.value_expr);
   3497 
   3498     if (full.comptime_token) |_| {
   3499         const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node);
   3500         _ = try inner_gz.addBreak(.break_inline, comptime_block_inst, .void_value);
   3501         try inner_gz.setBlockComptimeBody(comptime_block_inst, .comptime_keyword);
   3502         try gz.instructions.append(gz.astgen.gpa, comptime_block_inst);
   3503     }
   3504 }
   3505 
   3506 /// Handles destructure assignments where the LHS may contain `const` or `var` decls.
   3507 fn assignDestructureMaybeDecls(
   3508     gz: *GenZir,
   3509     scope: *Scope,
   3510     node: Ast.Node.Index,
   3511     block_arena: Allocator,
   3512 ) InnerError!*Scope {
   3513     try emitDbgNode(gz, node);
   3514     const astgen = gz.astgen;
   3515     const tree = astgen.tree;
   3516 
   3517     const full = tree.assignDestructure(node);
   3518     if (full.comptime_token != null and gz.is_comptime) {
   3519         try astgen.appendErrorNode(node, "redundant comptime keyword in already comptime scope", .{});
   3520     }
   3521 
   3522     const is_comptime = full.comptime_token != null or gz.is_comptime;
   3523     const value_is_comptime = tree.nodeTag(full.ast.value_expr) == .@"comptime";
   3524 
   3525     // When declaring consts via a destructure, we always use a result pointer.
   3526     // This avoids the need to create tuple types, and is also likely easier to
   3527     // optimize, since it's a bit tricky for the optimizer to "split up" the
   3528     // value into individual pointer writes down the line.
   3529 
   3530     // We know this rl information won't live past the evaluation of this
   3531     // expression, so it may as well go in the block arena.
   3532     const rl_components = try block_arena.alloc(ResultInfo.Loc.DestructureComponent, full.ast.variables.len);
   3533     var any_non_const_variables = false;
   3534     var any_lvalue_expr = false;
   3535     for (rl_components, full.ast.variables) |*variable_rl, variable_node| {
   3536         switch (tree.nodeTag(variable_node)) {
   3537             .identifier => {
   3538                 // This intentionally does not support `@"_"` syntax.
   3539                 const ident_name = tree.tokenSlice(tree.nodeMainToken(variable_node));
   3540                 if (mem.eql(u8, ident_name, "_")) {
   3541                     any_non_const_variables = true;
   3542                     variable_rl.* = .discard;
   3543                     continue;
   3544                 }
   3545             },
   3546             .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => {
   3547                 const full_var_decl = tree.fullVarDecl(variable_node).?;
   3548 
   3549                 const name_token = full_var_decl.ast.mut_token + 1;
   3550                 const ident_name_raw = tree.tokenSlice(name_token);
   3551                 if (mem.eql(u8, ident_name_raw, "_")) {
   3552                     return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{});
   3553                 }
   3554 
   3555                 // We detect shadowing in the second pass over these, while we're creating scopes.
   3556 
   3557                 if (full_var_decl.ast.addrspace_node.unwrap()) |addrspace_node| {
   3558                     return astgen.failTok(tree.nodeMainToken(addrspace_node), "cannot set address space of local variable '{s}'", .{ident_name_raw});
   3559                 }
   3560                 if (full_var_decl.ast.section_node.unwrap()) |section_node| {
   3561                     return astgen.failTok(tree.nodeMainToken(section_node), "cannot set section of local variable '{s}'", .{ident_name_raw});
   3562                 }
   3563 
   3564                 const is_const = switch (tree.tokenTag(full_var_decl.ast.mut_token)) {
   3565                     .keyword_var => false,
   3566                     .keyword_const => true,
   3567                     else => unreachable,
   3568                 };
   3569                 if (!is_const) any_non_const_variables = true;
   3570 
   3571                 // We also mark `const`s as comptime if the RHS is definitely comptime-known.
   3572                 const this_variable_comptime = is_comptime or (is_const and value_is_comptime);
   3573 
   3574                 const align_inst: Zir.Inst.Ref = if (full_var_decl.ast.align_node.unwrap()) |align_node|
   3575                     try expr(gz, scope, coerced_align_ri, align_node)
   3576                 else
   3577                     .none;
   3578 
   3579                 if (full_var_decl.ast.type_node.unwrap()) |type_node| {
   3580                     // Typed alloc
   3581                     const type_inst = try typeExpr(gz, scope, type_node);
   3582                     const ptr = if (align_inst == .none) ptr: {
   3583                         const tag: Zir.Inst.Tag = if (is_const)
   3584                             .alloc
   3585                         else if (this_variable_comptime)
   3586                             .alloc_comptime_mut
   3587                         else
   3588                             .alloc_mut;
   3589                         break :ptr try gz.addUnNode(tag, type_inst, node);
   3590                     } else try gz.addAllocExtended(.{
   3591                         .node = node,
   3592                         .type_inst = type_inst,
   3593                         .align_inst = align_inst,
   3594                         .is_const = is_const,
   3595                         .is_comptime = this_variable_comptime,
   3596                     });
   3597                     variable_rl.* = .{ .typed_ptr = .{ .inst = ptr } };
   3598                 } else {
   3599                     // Inferred alloc
   3600                     const ptr = if (align_inst == .none) ptr: {
   3601                         const tag: Zir.Inst.Tag = if (is_const) tag: {
   3602                             break :tag if (this_variable_comptime) .alloc_inferred_comptime else .alloc_inferred;
   3603                         } else tag: {
   3604                             break :tag if (this_variable_comptime) .alloc_inferred_comptime_mut else .alloc_inferred_mut;
   3605                         };
   3606                         break :ptr try gz.addNode(tag, node);
   3607                     } else try gz.addAllocExtended(.{
   3608                         .node = node,
   3609                         .type_inst = .none,
   3610                         .align_inst = align_inst,
   3611                         .is_const = is_const,
   3612                         .is_comptime = this_variable_comptime,
   3613                     });
   3614                     variable_rl.* = .{ .inferred_ptr = ptr };
   3615                 }
   3616 
   3617                 continue;
   3618             },
   3619             else => {},
   3620         }
   3621         // This variable is just an lvalue expression.
   3622         // We will fill in its result pointer later, inside a comptime block.
   3623         any_non_const_variables = true;
   3624         any_lvalue_expr = true;
   3625         variable_rl.* = .{ .typed_ptr = .{
   3626             .inst = undefined,
   3627             .src_node = variable_node,
   3628         } };
   3629     }
   3630 
   3631     if (full.comptime_token != null and !any_non_const_variables) {
   3632         try astgen.appendErrorTok(full.comptime_token.?, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
   3633         // Note that this is non-fatal; we will still evaluate at comptime.
   3634     }
   3635 
   3636     // If this expression is marked comptime, we must wrap it in a comptime block.
   3637     var gz_buf: GenZir = undefined;
   3638     const inner_gz = if (full.comptime_token) |_| bs: {
   3639         gz_buf = gz.makeSubBlock(scope);
   3640         gz_buf.is_comptime = true;
   3641         break :bs &gz_buf;
   3642     } else gz;
   3643     defer if (full.comptime_token) |_| inner_gz.unstack();
   3644 
   3645     if (any_lvalue_expr) {
   3646         // At least one variable was an lvalue expr. Iterate again in order to
   3647         // evaluate the lvalues from within the possible block_comptime.
   3648         for (rl_components, full.ast.variables) |*variable_rl, variable_node| {
   3649             if (variable_rl.* != .typed_ptr) continue;
   3650             switch (tree.nodeTag(variable_node)) {
   3651                 .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => continue,
   3652                 else => {},
   3653             }
   3654             variable_rl.typed_ptr.inst = try lvalExpr(inner_gz, scope, variable_node);
   3655         }
   3656     }
   3657 
   3658     // We can't give a reasonable anon name strategy for destructured inits, so
   3659     // leave it at its default of `.anon`.
   3660     _ = try reachableExpr(inner_gz, scope, .{ .rl = .{ .destructure = .{
   3661         .src_node = node,
   3662         .components = rl_components,
   3663     } } }, full.ast.value_expr, node);
   3664 
   3665     if (full.comptime_token) |_| {
   3666         // Finish the block_comptime. Inferred alloc resolution etc will occur
   3667         // in the parent block.
   3668         const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node);
   3669         _ = try inner_gz.addBreak(.break_inline, comptime_block_inst, .void_value);
   3670         try inner_gz.setBlockComptimeBody(comptime_block_inst, .comptime_keyword);
   3671         try gz.instructions.append(gz.astgen.gpa, comptime_block_inst);
   3672     }
   3673 
   3674     // Now, iterate over the variable exprs to construct any new scopes.
   3675     // If there were any inferred allocations, resolve them.
   3676     // If there were any `const` decls, make the pointer constant.
   3677     var cur_scope = scope;
   3678     for (rl_components, full.ast.variables) |variable_rl, variable_node| {
   3679         switch (tree.nodeTag(variable_node)) {
   3680             .local_var_decl, .simple_var_decl, .aligned_var_decl => {},
   3681             else => continue, // We were mutating an existing lvalue - nothing to do
   3682         }
   3683         const full_var_decl = tree.fullVarDecl(variable_node).?;
   3684         const raw_ptr, const resolve_inferred = switch (variable_rl) {
   3685             .discard => unreachable,
   3686             .typed_ptr => |typed_ptr| .{ typed_ptr.inst, false },
   3687             .inferred_ptr => |ptr_inst| .{ ptr_inst, true },
   3688         };
   3689         const is_const = switch (tree.tokenTag(full_var_decl.ast.mut_token)) {
   3690             .keyword_var => false,
   3691             .keyword_const => true,
   3692             else => unreachable,
   3693         };
   3694 
   3695         // If the alloc was inferred, resolve it. If the alloc was const, make it const.
   3696         const final_ptr = if (resolve_inferred)
   3697             try gz.addUnNode(.resolve_inferred_alloc, raw_ptr, variable_node)
   3698         else if (is_const)
   3699             try gz.addUnNode(.make_ptr_const, raw_ptr, node)
   3700         else
   3701             raw_ptr;
   3702 
   3703         const name_token = full_var_decl.ast.mut_token + 1;
   3704         const ident_name_raw = tree.tokenSlice(name_token);
   3705         const ident_name = try astgen.identAsString(name_token);
   3706         try astgen.detectLocalShadowing(
   3707             cur_scope,
   3708             ident_name,
   3709             name_token,
   3710             ident_name_raw,
   3711             if (is_const) .@"local constant" else .@"local variable",
   3712         );
   3713         try gz.addDbgVar(.dbg_var_ptr, ident_name, final_ptr);
   3714         // Finally, create the scope.
   3715         const sub_scope = try block_arena.create(Scope.LocalPtr);
   3716         sub_scope.* = .{
   3717             .parent = cur_scope,
   3718             .gen_zir = gz,
   3719             .name = ident_name,
   3720             .ptr = final_ptr,
   3721             .token_src = name_token,
   3722             .maybe_comptime = is_const or is_comptime,
   3723             .id_cat = if (is_const) .@"local constant" else .@"local variable",
   3724         };
   3725         cur_scope = &sub_scope.base;
   3726     }
   3727 
   3728     return cur_scope;
   3729 }
   3730 
   3731 fn assignOp(
   3732     gz: *GenZir,
   3733     scope: *Scope,
   3734     infix_node: Ast.Node.Index,
   3735     op_inst_tag: Zir.Inst.Tag,
   3736 ) InnerError!void {
   3737     try emitDbgNode(gz, infix_node);
   3738     const astgen = gz.astgen;
   3739     const tree = astgen.tree;
   3740 
   3741     const lhs_node, const rhs_node = tree.nodeData(infix_node).node_and_node;
   3742     const lhs_ptr = try lvalExpr(gz, scope, lhs_node);
   3743 
   3744     const cursor = switch (op_inst_tag) {
   3745         .add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, infix_node),
   3746         else => undefined,
   3747     };
   3748     const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
   3749 
   3750     const rhs_res_ty = switch (op_inst_tag) {
   3751         .add,
   3752         .sub,
   3753         => try gz.add(.{
   3754             .tag = .extended,
   3755             .data = .{ .extended = .{
   3756                 .opcode = .inplace_arith_result_ty,
   3757                 .small = @intFromEnum(@as(Zir.Inst.InplaceOp, switch (op_inst_tag) {
   3758                     .add => .add_eq,
   3759                     .sub => .sub_eq,
   3760                     else => unreachable,
   3761                 })),
   3762                 .operand = @intFromEnum(lhs),
   3763             } },
   3764         }),
   3765         else => try gz.addUnNode(.typeof, lhs, infix_node), // same as LHS type
   3766     };
   3767     // Not `coerced_ty` since `add`/etc won't coerce to this type.
   3768     const rhs = try expr(gz, scope, .{ .rl = .{ .ty = rhs_res_ty } }, rhs_node);
   3769 
   3770     switch (op_inst_tag) {
   3771         .add, .sub, .mul, .div, .mod_rem => {
   3772             try emitDbgStmt(gz, cursor);
   3773         },
   3774         else => {},
   3775     }
   3776     const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{
   3777         .lhs = lhs,
   3778         .rhs = rhs,
   3779     });
   3780     _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{
   3781         .lhs = lhs_ptr,
   3782         .rhs = result,
   3783     });
   3784 }
   3785 
   3786 fn assignShift(
   3787     gz: *GenZir,
   3788     scope: *Scope,
   3789     infix_node: Ast.Node.Index,
   3790     op_inst_tag: Zir.Inst.Tag,
   3791 ) InnerError!void {
   3792     try emitDbgNode(gz, infix_node);
   3793     const astgen = gz.astgen;
   3794     const tree = astgen.tree;
   3795 
   3796     const lhs_node, const rhs_node = tree.nodeData(infix_node).node_and_node;
   3797     const lhs_ptr = try lvalExpr(gz, scope, lhs_node);
   3798     const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
   3799     const rhs_type = try gz.addUnNode(.typeof_log2_int_type, lhs, infix_node);
   3800     const rhs = try expr(gz, scope, .{ .rl = .{ .ty = rhs_type } }, rhs_node);
   3801 
   3802     const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{
   3803         .lhs = lhs,
   3804         .rhs = rhs,
   3805     });
   3806     _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{
   3807         .lhs = lhs_ptr,
   3808         .rhs = result,
   3809     });
   3810 }
   3811 
   3812 fn assignShiftSat(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void {
   3813     try emitDbgNode(gz, infix_node);
   3814     const astgen = gz.astgen;
   3815     const tree = astgen.tree;
   3816 
   3817     const lhs_node, const rhs_node = tree.nodeData(infix_node).node_and_node;
   3818     const lhs_ptr = try lvalExpr(gz, scope, lhs_node);
   3819     const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
   3820     // Saturating shift-left allows any integer type for both the LHS and RHS.
   3821     const rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node);
   3822 
   3823     const result = try gz.addPlNode(.shl_sat, infix_node, Zir.Inst.Bin{
   3824         .lhs = lhs,
   3825         .rhs = rhs,
   3826     });
   3827     _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{
   3828         .lhs = lhs_ptr,
   3829         .rhs = result,
   3830     });
   3831 }
   3832 
   3833 fn ptrType(
   3834     gz: *GenZir,
   3835     scope: *Scope,
   3836     ri: ResultInfo,
   3837     node: Ast.Node.Index,
   3838     ptr_info: Ast.full.PtrType,
   3839 ) InnerError!Zir.Inst.Ref {
   3840     if (ptr_info.size == .c and ptr_info.allowzero_token != null) {
   3841         return gz.astgen.failTok(ptr_info.allowzero_token.?, "C pointers always allow address zero", .{});
   3842     }
   3843 
   3844     const source_offset = gz.astgen.source_offset;
   3845     const source_line = gz.astgen.source_line;
   3846     const source_column = gz.astgen.source_column;
   3847     const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type);
   3848 
   3849     var sentinel_ref: Zir.Inst.Ref = .none;
   3850     var align_ref: Zir.Inst.Ref = .none;
   3851     var addrspace_ref: Zir.Inst.Ref = .none;
   3852     var bit_start_ref: Zir.Inst.Ref = .none;
   3853     var bit_end_ref: Zir.Inst.Ref = .none;
   3854     var trailing_count: u32 = 0;
   3855 
   3856     if (ptr_info.ast.sentinel.unwrap()) |sentinel| {
   3857         // These attributes can appear in any order and they all come before the
   3858         // element type so we need to reset the source cursor before generating them.
   3859         gz.astgen.source_offset = source_offset;
   3860         gz.astgen.source_line = source_line;
   3861         gz.astgen.source_column = source_column;
   3862 
   3863         sentinel_ref = try comptimeExpr(
   3864             gz,
   3865             scope,
   3866             .{ .rl = .{ .ty = elem_type } },
   3867             sentinel,
   3868             switch (ptr_info.size) {
   3869                 .slice => .slice_sentinel,
   3870                 else => .pointer_sentinel,
   3871             },
   3872         );
   3873         trailing_count += 1;
   3874     }
   3875     if (ptr_info.ast.addrspace_node.unwrap()) |addrspace_node| {
   3876         gz.astgen.source_offset = source_offset;
   3877         gz.astgen.source_line = source_line;
   3878         gz.astgen.source_column = source_column;
   3879 
   3880         const addrspace_ty = try gz.addBuiltinValue(addrspace_node, .address_space);
   3881         addrspace_ref = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = addrspace_ty } }, addrspace_node, .@"addrspace");
   3882         trailing_count += 1;
   3883     }
   3884     if (ptr_info.ast.align_node.unwrap()) |align_node| {
   3885         gz.astgen.source_offset = source_offset;
   3886         gz.astgen.source_line = source_line;
   3887         gz.astgen.source_column = source_column;
   3888 
   3889         align_ref = try comptimeExpr(gz, scope, coerced_align_ri, align_node, .@"align");
   3890         trailing_count += 1;
   3891     }
   3892     if (ptr_info.ast.bit_range_start.unwrap()) |bit_range_start| {
   3893         const bit_range_end = ptr_info.ast.bit_range_end.unwrap().?;
   3894         bit_start_ref = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, bit_range_start, .type);
   3895         bit_end_ref = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, bit_range_end, .type);
   3896         trailing_count += 2;
   3897     }
   3898 
   3899     const gpa = gz.astgen.gpa;
   3900     try gz.instructions.ensureUnusedCapacity(gpa, 1);
   3901     try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
   3902     try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.PtrType).@"struct".fields.len +
   3903         trailing_count);
   3904 
   3905     const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.PtrType{
   3906         .elem_type = elem_type,
   3907         .src_node = gz.nodeIndexToRelative(node),
   3908     });
   3909     if (sentinel_ref != .none) {
   3910         gz.astgen.extra.appendAssumeCapacity(@intFromEnum(sentinel_ref));
   3911     }
   3912     if (align_ref != .none) {
   3913         gz.astgen.extra.appendAssumeCapacity(@intFromEnum(align_ref));
   3914     }
   3915     if (addrspace_ref != .none) {
   3916         gz.astgen.extra.appendAssumeCapacity(@intFromEnum(addrspace_ref));
   3917     }
   3918     if (bit_start_ref != .none) {
   3919         gz.astgen.extra.appendAssumeCapacity(@intFromEnum(bit_start_ref));
   3920         gz.astgen.extra.appendAssumeCapacity(@intFromEnum(bit_end_ref));
   3921     }
   3922 
   3923     const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
   3924     const result = new_index.toRef();
   3925     gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{
   3926         .ptr_type = .{
   3927             .flags = .{
   3928                 .is_allowzero = ptr_info.allowzero_token != null,
   3929                 .is_mutable = ptr_info.const_token == null,
   3930                 .is_volatile = ptr_info.volatile_token != null,
   3931                 .has_sentinel = sentinel_ref != .none,
   3932                 .has_align = align_ref != .none,
   3933                 .has_addrspace = addrspace_ref != .none,
   3934                 .has_bit_range = bit_start_ref != .none,
   3935             },
   3936             .size = ptr_info.size,
   3937             .payload_index = payload_index,
   3938         },
   3939     } });
   3940     gz.instructions.appendAssumeCapacity(new_index);
   3941 
   3942     return rvalue(gz, ri, result, node);
   3943 }
   3944 
   3945 fn arrayType(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) !Zir.Inst.Ref {
   3946     const astgen = gz.astgen;
   3947     const tree = astgen.tree;
   3948 
   3949     const len_node, const elem_type_node = tree.nodeData(node).node_and_node;
   3950     if (tree.nodeTag(len_node) == .identifier and
   3951         mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(len_node)), "_"))
   3952     {
   3953         return astgen.failNode(len_node, "unable to infer array size", .{});
   3954     }
   3955     const len = try reachableExprComptime(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, len_node, node, .type);
   3956     const elem_type = try typeExpr(gz, scope, elem_type_node);
   3957 
   3958     const result = try gz.addPlNode(.array_type, node, Zir.Inst.Bin{
   3959         .lhs = len,
   3960         .rhs = elem_type,
   3961     });
   3962     return rvalue(gz, ri, result, node);
   3963 }
   3964 
   3965 fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) !Zir.Inst.Ref {
   3966     const astgen = gz.astgen;
   3967     const tree = astgen.tree;
   3968 
   3969     const len_node, const extra_index = tree.nodeData(node).node_and_extra;
   3970     const extra = tree.extraData(extra_index, Ast.Node.ArrayTypeSentinel);
   3971 
   3972     if (tree.nodeTag(len_node) == .identifier and
   3973         mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(len_node)), "_"))
   3974     {
   3975         return astgen.failNode(len_node, "unable to infer array size", .{});
   3976     }
   3977     const len = try reachableExprComptime(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, len_node, node, .array_length);
   3978     const elem_type = try typeExpr(gz, scope, extra.elem_type);
   3979     const sentinel = try reachableExprComptime(gz, scope, .{ .rl = .{ .coerced_ty = elem_type } }, extra.sentinel, node, .array_sentinel);
   3980 
   3981     const result = try gz.addPlNode(.array_type_sentinel, node, Zir.Inst.ArrayTypeSentinel{
   3982         .len = len,
   3983         .elem_type = elem_type,
   3984         .sentinel = sentinel,
   3985     });
   3986     return rvalue(gz, ri, result, node);
   3987 }
   3988 
   3989 const WipMembers = struct {
   3990     payload: *ArrayListUnmanaged(u32),
   3991     payload_top: usize,
   3992     field_bits_start: u32,
   3993     fields_start: u32,
   3994     fields_end: u32,
   3995     decl_index: u32 = 0,
   3996     field_index: u32 = 0,
   3997 
   3998     const Self = @This();
   3999 
   4000     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 {
   4001         const payload_top: u32 = @intCast(payload.items.len);
   4002         const field_bits_start = payload_top + decl_count;
   4003         const fields_start = field_bits_start + if (bits_per_field > 0) blk: {
   4004             const fields_per_u32 = 32 / bits_per_field;
   4005             break :blk (field_count + fields_per_u32 - 1) / fields_per_u32;
   4006         } else 0;
   4007         const payload_end = fields_start + field_count * max_field_size;
   4008         try payload.resize(gpa, payload_end);
   4009         return .{
   4010             .payload = payload,
   4011             .payload_top = payload_top,
   4012             .field_bits_start = field_bits_start,
   4013             .fields_start = fields_start,
   4014             .fields_end = fields_start,
   4015         };
   4016     }
   4017 
   4018     fn nextDecl(self: *Self, decl_inst: Zir.Inst.Index) void {
   4019         self.payload.items[self.payload_top + self.decl_index] = @intFromEnum(decl_inst);
   4020         self.decl_index += 1;
   4021     }
   4022 
   4023     fn nextField(self: *Self, comptime bits_per_field: u32, bits: [bits_per_field]bool) void {
   4024         const fields_per_u32 = 32 / bits_per_field;
   4025         const index = self.field_bits_start + self.field_index / fields_per_u32;
   4026         assert(index < self.fields_start);
   4027         var bit_bag: u32 = if (self.field_index % fields_per_u32 == 0) 0 else self.payload.items[index];
   4028         bit_bag >>= bits_per_field;
   4029         comptime var i = 0;
   4030         inline while (i < bits_per_field) : (i += 1) {
   4031             bit_bag |= @as(u32, @intFromBool(bits[i])) << (32 - bits_per_field + i);
   4032         }
   4033         self.payload.items[index] = bit_bag;
   4034         self.field_index += 1;
   4035     }
   4036 
   4037     fn appendToField(self: *Self, data: u32) void {
   4038         assert(self.fields_end < self.payload.items.len);
   4039         self.payload.items[self.fields_end] = data;
   4040         self.fields_end += 1;
   4041     }
   4042 
   4043     fn finishBits(self: *Self, comptime bits_per_field: u32) void {
   4044         if (bits_per_field > 0) {
   4045             const fields_per_u32 = 32 / bits_per_field;
   4046             const empty_field_slots = fields_per_u32 - (self.field_index % fields_per_u32);
   4047             if (self.field_index > 0 and empty_field_slots < fields_per_u32) {
   4048                 const index = self.field_bits_start + self.field_index / fields_per_u32;
   4049                 self.payload.items[index] >>= @intCast(empty_field_slots * bits_per_field);
   4050             }
   4051         }
   4052     }
   4053 
   4054     fn declsSlice(self: *Self) []u32 {
   4055         return self.payload.items[self.payload_top..][0..self.decl_index];
   4056     }
   4057 
   4058     fn fieldsSlice(self: *Self) []u32 {
   4059         return self.payload.items[self.field_bits_start..self.fields_end];
   4060     }
   4061 
   4062     fn deinit(self: *Self) void {
   4063         self.payload.items.len = self.payload_top;
   4064     }
   4065 };
   4066 
   4067 fn fnDecl(
   4068     astgen: *AstGen,
   4069     gz: *GenZir,
   4070     scope: *Scope,
   4071     wip_members: *WipMembers,
   4072     decl_node: Ast.Node.Index,
   4073     body_node: Ast.Node.OptionalIndex,
   4074     fn_proto: Ast.full.FnProto,
   4075 ) InnerError!void {
   4076     const tree = astgen.tree;
   4077 
   4078     const old_hasher = astgen.src_hasher;
   4079     defer astgen.src_hasher = old_hasher;
   4080     astgen.src_hasher = std.zig.SrcHasher.init(.{});
   4081     // We don't add the full source yet, because we also need the prototype hash!
   4082     // The source slice is added towards the *end* of this function.
   4083     astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
   4084 
   4085     // missing function name already checked in scanContainer()
   4086     const fn_name_token = fn_proto.name_token.?;
   4087 
   4088     // We insert this at the beginning so that its instruction index marks the
   4089     // start of the top level declaration.
   4090     const decl_inst = try gz.makeDeclaration(fn_proto.ast.proto_node);
   4091     astgen.advanceSourceCursorToNode(decl_node);
   4092 
   4093     const saved_cursor = astgen.saveSourceCursor();
   4094 
   4095     const decl_column = astgen.source_column;
   4096 
   4097     // Set this now, since parameter types, return type, etc may be generic.
   4098     const prev_within_fn = astgen.within_fn;
   4099     defer astgen.within_fn = prev_within_fn;
   4100     astgen.within_fn = true;
   4101 
   4102     const is_pub = fn_proto.visib_token != null;
   4103     const is_export = blk: {
   4104         const maybe_export_token = fn_proto.extern_export_inline_token orelse break :blk false;
   4105         break :blk tree.tokenTag(maybe_export_token) == .keyword_export;
   4106     };
   4107     const is_extern = blk: {
   4108         const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false;
   4109         break :blk tree.tokenTag(maybe_extern_token) == .keyword_extern;
   4110     };
   4111     const has_inline_keyword = blk: {
   4112         const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;
   4113         break :blk tree.tokenTag(maybe_inline_token) == .keyword_inline;
   4114     };
   4115     const lib_name = if (fn_proto.lib_name) |lib_name_token| blk: {
   4116         const lib_name_str = try astgen.strLitAsString(lib_name_token);
   4117         const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len];
   4118         if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) {
   4119             return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{});
   4120         } else if (lib_name_str.len == 0) {
   4121             return astgen.failTok(lib_name_token, "library name cannot be empty", .{});
   4122         }
   4123         break :blk lib_name_str.index;
   4124     } else .empty;
   4125     if (fn_proto.ast.callconv_expr != .none and has_inline_keyword) {
   4126         return astgen.failNode(
   4127             fn_proto.ast.callconv_expr.unwrap().?,
   4128             "explicit callconv incompatible with inline keyword",
   4129             .{},
   4130         );
   4131     }
   4132 
   4133     const return_type = fn_proto.ast.return_type.unwrap().?;
   4134     const maybe_bang = tree.firstToken(return_type) - 1;
   4135     const is_inferred_error = tree.tokenTag(maybe_bang) == .bang;
   4136     if (body_node == .none) {
   4137         if (!is_extern) {
   4138             return astgen.failTok(fn_proto.ast.fn_token, "non-extern function has no body", .{});
   4139         }
   4140         if (is_inferred_error) {
   4141             return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{});
   4142         }
   4143     } else {
   4144         assert(!is_extern); // validated by parser (TODO why???)
   4145     }
   4146 
   4147     wip_members.nextDecl(decl_inst);
   4148 
   4149     var type_gz: GenZir = .{
   4150         .is_comptime = true,
   4151         .decl_node_index = fn_proto.ast.proto_node,
   4152         .decl_line = astgen.source_line,
   4153         .parent = scope,
   4154         .astgen = astgen,
   4155         .instructions = gz.instructions,
   4156         .instructions_top = gz.instructions.items.len,
   4157     };
   4158     defer type_gz.unstack();
   4159 
   4160     if (is_extern) {
   4161         // We include a function *type*, not a value.
   4162         const type_inst = try fnProtoExprInner(&type_gz, &type_gz.base, .{ .rl = .none }, decl_node, fn_proto, true);
   4163         _ = try type_gz.addBreakWithSrcNode(.break_inline, decl_inst, type_inst, decl_node);
   4164     }
   4165 
   4166     var align_gz = type_gz.makeSubBlock(scope);
   4167     defer align_gz.unstack();
   4168 
   4169     if (fn_proto.ast.align_expr.unwrap()) |align_expr| {
   4170         astgen.restoreSourceCursor(saved_cursor);
   4171         const inst = try expr(&align_gz, &align_gz.base, coerced_align_ri, align_expr);
   4172         _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node);
   4173     }
   4174 
   4175     var linksection_gz = align_gz.makeSubBlock(scope);
   4176     defer linksection_gz.unstack();
   4177 
   4178     if (fn_proto.ast.section_expr.unwrap()) |section_expr| {
   4179         astgen.restoreSourceCursor(saved_cursor);
   4180         const inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, section_expr);
   4181         _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node);
   4182     }
   4183 
   4184     var addrspace_gz = linksection_gz.makeSubBlock(scope);
   4185     defer addrspace_gz.unstack();
   4186 
   4187     if (fn_proto.ast.addrspace_expr.unwrap()) |addrspace_expr| {
   4188         astgen.restoreSourceCursor(saved_cursor);
   4189         const addrspace_ty = try addrspace_gz.addBuiltinValue(addrspace_expr, .address_space);
   4190         const inst = try expr(&addrspace_gz, &addrspace_gz.base, .{ .rl = .{ .coerced_ty = addrspace_ty } }, addrspace_expr);
   4191         _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, inst, decl_node);
   4192     }
   4193 
   4194     var value_gz = addrspace_gz.makeSubBlock(scope);
   4195     defer value_gz.unstack();
   4196 
   4197     if (!is_extern) {
   4198         // We include a function *value*, not a type.
   4199         astgen.restoreSourceCursor(saved_cursor);
   4200         try astgen.fnDeclInner(&value_gz, &value_gz.base, saved_cursor, decl_inst, decl_node, body_node.unwrap().?, fn_proto);
   4201     }
   4202 
   4203     // *Now* we can incorporate the full source code into the hasher.
   4204     astgen.src_hasher.update(tree.getNodeSource(decl_node));
   4205 
   4206     var hash: std.zig.SrcHash = undefined;
   4207     astgen.src_hasher.final(&hash);
   4208     try setDeclaration(decl_inst, .{
   4209         .src_hash = hash,
   4210         .src_line = type_gz.decl_line,
   4211         .src_column = decl_column,
   4212 
   4213         .kind = .@"const",
   4214         .name = try astgen.identAsString(fn_name_token),
   4215         .is_pub = is_pub,
   4216         .is_threadlocal = false,
   4217         .linkage = if (is_extern) .@"extern" else if (is_export) .@"export" else .normal,
   4218         .lib_name = lib_name,
   4219 
   4220         .type_gz = &type_gz,
   4221         .align_gz = &align_gz,
   4222         .linksection_gz = &linksection_gz,
   4223         .addrspace_gz = &addrspace_gz,
   4224         .value_gz = &value_gz,
   4225     });
   4226 }
   4227 
   4228 fn fnDeclInner(
   4229     astgen: *AstGen,
   4230     decl_gz: *GenZir,
   4231     scope: *Scope,
   4232     saved_cursor: SourceCursor,
   4233     decl_inst: Zir.Inst.Index,
   4234     decl_node: Ast.Node.Index,
   4235     body_node: Ast.Node.Index,
   4236     fn_proto: Ast.full.FnProto,
   4237 ) InnerError!void {
   4238     const tree = astgen.tree;
   4239 
   4240     const is_noinline = blk: {
   4241         const maybe_noinline_token = fn_proto.extern_export_inline_token orelse break :blk false;
   4242         break :blk tree.tokenTag(maybe_noinline_token) == .keyword_noinline;
   4243     };
   4244     const has_inline_keyword = blk: {
   4245         const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;
   4246         break :blk tree.tokenTag(maybe_inline_token) == .keyword_inline;
   4247     };
   4248 
   4249     const return_type = fn_proto.ast.return_type.unwrap().?;
   4250     const maybe_bang = tree.firstToken(return_type) - 1;
   4251     const is_inferred_error = tree.tokenTag(maybe_bang) == .bang;
   4252 
   4253     // Note that the capacity here may not be sufficient, as this does not include `anytype` parameters.
   4254     var param_insts: std.ArrayListUnmanaged(Zir.Inst.Index) = try .initCapacity(astgen.arena, fn_proto.ast.params.len);
   4255 
   4256     // We use this as `is_used_or_discarded` to figure out if parameters / return types are generic.
   4257     var any_param_used = false;
   4258 
   4259     var noalias_bits: u32 = 0;
   4260     var params_scope = scope;
   4261     const is_var_args = is_var_args: {
   4262         var param_type_i: usize = 0;
   4263         var it = fn_proto.iterate(tree);
   4264         while (it.next()) |param| : (param_type_i += 1) {
   4265             const is_comptime = if (param.comptime_noalias) |token| switch (tree.tokenTag(token)) {
   4266                 .keyword_noalias => is_comptime: {
   4267                     noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse
   4268                         return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
   4269                     break :is_comptime false;
   4270                 },
   4271                 .keyword_comptime => true,
   4272                 else => false,
   4273             } else false;
   4274 
   4275             const is_anytype = if (param.anytype_ellipsis3) |token| blk: {
   4276                 switch (tree.tokenTag(token)) {
   4277                     .keyword_anytype => break :blk true,
   4278                     .ellipsis3 => break :is_var_args true,
   4279                     else => unreachable,
   4280                 }
   4281             } else false;
   4282 
   4283             const param_name: Zir.NullTerminatedString = if (param.name_token) |name_token| blk: {
   4284                 const name_bytes = tree.tokenSlice(name_token);
   4285                 if (mem.eql(u8, "_", name_bytes))
   4286                     break :blk .empty;
   4287 
   4288                 const param_name = try astgen.identAsString(name_token);
   4289                 try astgen.detectLocalShadowing(params_scope, param_name, name_token, name_bytes, .@"function parameter");
   4290                 break :blk param_name;
   4291             } else {
   4292                 if (param.anytype_ellipsis3) |tok| {
   4293                     return astgen.failTok(tok, "missing parameter name", .{});
   4294                 } else {
   4295                     const type_expr = param.type_expr.?;
   4296                     ambiguous: {
   4297                         if (tree.nodeTag(type_expr) != .identifier) break :ambiguous;
   4298                         const main_token = tree.nodeMainToken(type_expr);
   4299                         const identifier_str = tree.tokenSlice(main_token);
   4300                         if (isPrimitive(identifier_str)) break :ambiguous;
   4301                         return astgen.failNodeNotes(
   4302                             type_expr,
   4303                             "missing parameter name or type",
   4304                             .{},
   4305                             &[_]u32{
   4306                                 try astgen.errNoteNode(
   4307                                     type_expr,
   4308                                     "if this is a name, annotate its type: '{s}: T'",
   4309                                     .{identifier_str},
   4310                                 ),
   4311                                 try astgen.errNoteNode(
   4312                                     type_expr,
   4313                                     "if this is a type, give it a name: 'name: {s}'",
   4314                                     .{identifier_str},
   4315                                 ),
   4316                             },
   4317                         );
   4318                     }
   4319                     return astgen.failNode(type_expr, "missing parameter name", .{});
   4320                 }
   4321             };
   4322 
   4323             const param_inst = if (is_anytype) param: {
   4324                 const name_token = param.name_token orelse param.anytype_ellipsis3.?;
   4325                 const tag: Zir.Inst.Tag = if (is_comptime)
   4326                     .param_anytype_comptime
   4327                 else
   4328                     .param_anytype;
   4329                 break :param try decl_gz.addStrTok(tag, param_name, name_token);
   4330             } else param: {
   4331                 const param_type_node = param.type_expr.?;
   4332                 any_param_used = false; // we will check this later
   4333                 var param_gz = decl_gz.makeSubBlock(scope);
   4334                 defer param_gz.unstack();
   4335                 const param_type = try fullBodyExpr(&param_gz, params_scope, coerced_type_ri, param_type_node, .normal);
   4336                 const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1);
   4337                 _ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node);
   4338                 const param_type_is_generic = any_param_used;
   4339 
   4340                 const name_token = param.name_token orelse tree.nodeMainToken(param_type_node);
   4341                 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param;
   4342                 const param_inst = try decl_gz.addParam(&param_gz, param_insts.items, param_type_is_generic, tag, name_token, param_name);
   4343                 assert(param_inst_expected == param_inst);
   4344                 break :param param_inst.toRef();
   4345             };
   4346 
   4347             if (param_name == .empty) continue;
   4348 
   4349             const sub_scope = try astgen.arena.create(Scope.LocalVal);
   4350             sub_scope.* = .{
   4351                 .parent = params_scope,
   4352                 .gen_zir = decl_gz,
   4353                 .name = param_name,
   4354                 .inst = param_inst,
   4355                 .token_src = param.name_token.?,
   4356                 .id_cat = .@"function parameter",
   4357                 .is_used_or_discarded = &any_param_used,
   4358             };
   4359             params_scope = &sub_scope.base;
   4360             try param_insts.append(astgen.arena, param_inst.toIndex().?);
   4361         }
   4362         break :is_var_args false;
   4363     };
   4364 
   4365     // After creating the function ZIR instruction, it will need to update the break
   4366     // instructions inside the expression blocks for cc and ret_ty to use the function
   4367     // instruction as the body to break from.
   4368 
   4369     var ret_gz = decl_gz.makeSubBlock(params_scope);
   4370     defer ret_gz.unstack();
   4371     any_param_used = false; // we will check this later
   4372     const ret_ref: Zir.Inst.Ref = inst: {
   4373         // Parameters are in scope for the return type, so we use `params_scope` here.
   4374         // The calling convention will not have parameters in scope, so we'll just use `scope`.
   4375         // See #22263 for a proposal to solve the inconsistency here.
   4376         const inst = try fullBodyExpr(&ret_gz, params_scope, coerced_type_ri, fn_proto.ast.return_type.unwrap().?, .normal);
   4377         if (ret_gz.instructionsSlice().len == 0) {
   4378             // In this case we will send a len=0 body which can be encoded more efficiently.
   4379             break :inst inst;
   4380         }
   4381         _ = try ret_gz.addBreak(.break_inline, @enumFromInt(0), inst);
   4382         break :inst inst;
   4383     };
   4384     const ret_body_param_refs = try astgen.fetchRemoveRefEntries(param_insts.items);
   4385     const ret_ty_is_generic = any_param_used;
   4386 
   4387     // We're jumping back in source, so restore the cursor.
   4388     astgen.restoreSourceCursor(saved_cursor);
   4389 
   4390     var cc_gz = decl_gz.makeSubBlock(scope);
   4391     defer cc_gz.unstack();
   4392     const cc_ref: Zir.Inst.Ref = blk: {
   4393         if (fn_proto.ast.callconv_expr.unwrap()) |callconv_expr| {
   4394             const inst = try expr(
   4395                 &cc_gz,
   4396                 scope,
   4397                 .{ .rl = .{ .coerced_ty = try cc_gz.addBuiltinValue(callconv_expr, .calling_convention) } },
   4398                 callconv_expr,
   4399             );
   4400             if (cc_gz.instructionsSlice().len == 0) {
   4401                 // In this case we will send a len=0 body which can be encoded more efficiently.
   4402                 break :blk inst;
   4403             }
   4404             _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst);
   4405             break :blk inst;
   4406         } else if (has_inline_keyword) {
   4407             const inst = try cc_gz.addBuiltinValue(decl_node, .calling_convention_inline);
   4408             _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst);
   4409             break :blk inst;
   4410         } else {
   4411             break :blk .none;
   4412         }
   4413     };
   4414 
   4415     var body_gz: GenZir = .{
   4416         .is_comptime = false,
   4417         .decl_node_index = fn_proto.ast.proto_node,
   4418         .decl_line = decl_gz.decl_line,
   4419         .parent = params_scope,
   4420         .astgen = astgen,
   4421         .instructions = decl_gz.instructions,
   4422         .instructions_top = decl_gz.instructions.items.len,
   4423     };
   4424     defer body_gz.unstack();
   4425 
   4426     // The scope stack looks like this:
   4427     //  body_gz (top)
   4428     //  param2
   4429     //  param1
   4430     //  param0
   4431     //  decl_gz (bottom)
   4432 
   4433     // Construct the prototype hash.
   4434     // Leave `astgen.src_hasher` unmodified; this will be used for hashing
   4435     // the *whole* function declaration, including its body.
   4436     var proto_hasher = astgen.src_hasher;
   4437     const proto_node = tree.nodeData(decl_node).node_and_node[0];
   4438     proto_hasher.update(tree.getNodeSource(proto_node));
   4439     var proto_hash: std.zig.SrcHash = undefined;
   4440     proto_hasher.final(&proto_hash);
   4441 
   4442     const prev_fn_block = astgen.fn_block;
   4443     const prev_fn_ret_ty = astgen.fn_ret_ty;
   4444     defer {
   4445         astgen.fn_block = prev_fn_block;
   4446         astgen.fn_ret_ty = prev_fn_ret_ty;
   4447     }
   4448     astgen.fn_block = &body_gz;
   4449     astgen.fn_ret_ty = if (is_inferred_error or ret_ref.toIndex() != null) r: {
   4450         // We're essentially guaranteed to need the return type at some point,
   4451         // since the return type is likely not `void` or `noreturn` so there
   4452         // will probably be an explicit return requiring RLS. Fetch this
   4453         // return type now so the rest of the function can use it.
   4454         break :r try body_gz.addNode(.ret_type, decl_node);
   4455     } else ret_ref;
   4456 
   4457     const prev_var_args = astgen.fn_var_args;
   4458     astgen.fn_var_args = is_var_args;
   4459     defer astgen.fn_var_args = prev_var_args;
   4460 
   4461     astgen.advanceSourceCursorToNode(body_node);
   4462     const lbrace_line = astgen.source_line - decl_gz.decl_line;
   4463     const lbrace_column = astgen.source_column;
   4464 
   4465     _ = try fullBodyExpr(&body_gz, &body_gz.base, .{ .rl = .none }, body_node, .allow_branch_hint);
   4466     try checkUsed(decl_gz, scope, params_scope);
   4467 
   4468     if (!body_gz.endsWithNoReturn()) {
   4469         // As our last action before the return, "pop" the error trace if needed
   4470         _ = try body_gz.addRestoreErrRetIndex(.ret, .always, decl_node);
   4471 
   4472         // Add implicit return at end of function.
   4473         _ = try body_gz.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node));
   4474     }
   4475 
   4476     const func_inst = try decl_gz.addFunc(.{
   4477         .src_node = decl_node,
   4478         .cc_ref = cc_ref,
   4479         .cc_gz = &cc_gz,
   4480         .ret_ref = ret_ref,
   4481         .ret_gz = &ret_gz,
   4482         .ret_param_refs = ret_body_param_refs,
   4483         .ret_ty_is_generic = ret_ty_is_generic,
   4484         .lbrace_line = lbrace_line,
   4485         .lbrace_column = lbrace_column,
   4486         .param_block = decl_inst,
   4487         .param_insts = param_insts.items,
   4488         .body_gz = &body_gz,
   4489         .is_var_args = is_var_args,
   4490         .is_inferred_error = is_inferred_error,
   4491         .is_noinline = is_noinline,
   4492         .noalias_bits = noalias_bits,
   4493         .proto_hash = proto_hash,
   4494     });
   4495     _ = try decl_gz.addBreakWithSrcNode(.break_inline, decl_inst, func_inst, decl_node);
   4496 }
   4497 
   4498 fn globalVarDecl(
   4499     astgen: *AstGen,
   4500     gz: *GenZir,
   4501     scope: *Scope,
   4502     wip_members: *WipMembers,
   4503     node: Ast.Node.Index,
   4504     var_decl: Ast.full.VarDecl,
   4505 ) InnerError!void {
   4506     const tree = astgen.tree;
   4507 
   4508     const old_hasher = astgen.src_hasher;
   4509     defer astgen.src_hasher = old_hasher;
   4510     astgen.src_hasher = std.zig.SrcHasher.init(.{});
   4511     astgen.src_hasher.update(tree.getNodeSource(node));
   4512     astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
   4513 
   4514     const is_mutable = tree.tokenTag(var_decl.ast.mut_token) == .keyword_var;
   4515     const name_token = var_decl.ast.mut_token + 1;
   4516     const is_pub = var_decl.visib_token != null;
   4517     const is_export = blk: {
   4518         const maybe_export_token = var_decl.extern_export_token orelse break :blk false;
   4519         break :blk tree.tokenTag(maybe_export_token) == .keyword_export;
   4520     };
   4521     const is_extern = blk: {
   4522         const maybe_extern_token = var_decl.extern_export_token orelse break :blk false;
   4523         break :blk tree.tokenTag(maybe_extern_token) == .keyword_extern;
   4524     };
   4525     const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: {
   4526         if (!is_mutable) {
   4527             return astgen.failTok(tok, "threadlocal variable cannot be constant", .{});
   4528         }
   4529         break :blk true;
   4530     } else false;
   4531     const lib_name = if (var_decl.lib_name) |lib_name_token| blk: {
   4532         const lib_name_str = try astgen.strLitAsString(lib_name_token);
   4533         const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len];
   4534         if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) {
   4535             return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{});
   4536         } else if (lib_name_str.len == 0) {
   4537             return astgen.failTok(lib_name_token, "library name cannot be empty", .{});
   4538         }
   4539         break :blk lib_name_str.index;
   4540     } else .empty;
   4541 
   4542     astgen.advanceSourceCursorToNode(node);
   4543 
   4544     const decl_column = astgen.source_column;
   4545 
   4546     const decl_inst = try gz.makeDeclaration(node);
   4547     wip_members.nextDecl(decl_inst);
   4548 
   4549     if (var_decl.ast.init_node.unwrap()) |init_node| {
   4550         if (is_extern) {
   4551             return astgen.failNode(
   4552                 init_node,
   4553                 "extern variables have no initializers",
   4554                 .{},
   4555             );
   4556         }
   4557     } else {
   4558         if (!is_extern) {
   4559             return astgen.failNode(node, "variables must be initialized", .{});
   4560         }
   4561     }
   4562 
   4563     if (is_extern and var_decl.ast.type_node == .none) {
   4564         return astgen.failNode(node, "unable to infer variable type", .{});
   4565     }
   4566 
   4567     assert(var_decl.comptime_token == null); // handled by parser
   4568 
   4569     var type_gz: GenZir = .{
   4570         .parent = scope,
   4571         .decl_node_index = node,
   4572         .decl_line = astgen.source_line,
   4573         .astgen = astgen,
   4574         .is_comptime = true,
   4575         .instructions = gz.instructions,
   4576         .instructions_top = gz.instructions.items.len,
   4577     };
   4578     defer type_gz.unstack();
   4579 
   4580     if (var_decl.ast.type_node.unwrap()) |type_node| {
   4581         const type_inst = try expr(&type_gz, &type_gz.base, coerced_type_ri, type_node);
   4582         _ = try type_gz.addBreakWithSrcNode(.break_inline, decl_inst, type_inst, node);
   4583     }
   4584 
   4585     var align_gz = type_gz.makeSubBlock(scope);
   4586     defer align_gz.unstack();
   4587 
   4588     if (var_decl.ast.align_node.unwrap()) |align_node| {
   4589         const align_inst = try expr(&align_gz, &align_gz.base, coerced_align_ri, align_node);
   4590         _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, align_inst, node);
   4591     }
   4592 
   4593     var linksection_gz = type_gz.makeSubBlock(scope);
   4594     defer linksection_gz.unstack();
   4595 
   4596     if (var_decl.ast.section_node.unwrap()) |section_node| {
   4597         const linksection_inst = try expr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, section_node);
   4598         _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, linksection_inst, node);
   4599     }
   4600 
   4601     var addrspace_gz = type_gz.makeSubBlock(scope);
   4602     defer addrspace_gz.unstack();
   4603 
   4604     if (var_decl.ast.addrspace_node.unwrap()) |addrspace_node| {
   4605         const addrspace_ty = try addrspace_gz.addBuiltinValue(addrspace_node, .address_space);
   4606         const addrspace_inst = try expr(&addrspace_gz, &addrspace_gz.base, .{ .rl = .{ .coerced_ty = addrspace_ty } }, addrspace_node);
   4607         _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, addrspace_inst, node);
   4608     }
   4609 
   4610     var init_gz = type_gz.makeSubBlock(scope);
   4611     defer init_gz.unstack();
   4612 
   4613     if (var_decl.ast.init_node.unwrap()) |init_node| {
   4614         const init_ri: ResultInfo = if (var_decl.ast.type_node != .none) .{
   4615             .rl = .{ .coerced_ty = decl_inst.toRef() },
   4616         } else .{ .rl = .none };
   4617         const init_inst: Zir.Inst.Ref = try nameStratExpr(&init_gz, &init_gz.base, init_ri, init_node, .parent) orelse init: {
   4618             break :init try expr(&init_gz, &init_gz.base, init_ri, init_node);
   4619         };
   4620         _ = try init_gz.addBreakWithSrcNode(.break_inline, decl_inst, init_inst, node);
   4621     }
   4622 
   4623     var hash: std.zig.SrcHash = undefined;
   4624     astgen.src_hasher.final(&hash);
   4625     try setDeclaration(decl_inst, .{
   4626         .src_hash = hash,
   4627         .src_line = type_gz.decl_line,
   4628         .src_column = decl_column,
   4629 
   4630         .kind = if (is_mutable) .@"var" else .@"const",
   4631         .name = try astgen.identAsString(name_token),
   4632         .is_pub = is_pub,
   4633         .is_threadlocal = is_threadlocal,
   4634         .linkage = if (is_extern) .@"extern" else if (is_export) .@"export" else .normal,
   4635         .lib_name = lib_name,
   4636 
   4637         .type_gz = &type_gz,
   4638         .align_gz = &align_gz,
   4639         .linksection_gz = &linksection_gz,
   4640         .addrspace_gz = &addrspace_gz,
   4641         .value_gz = &init_gz,
   4642     });
   4643 }
   4644 
   4645 fn comptimeDecl(
   4646     astgen: *AstGen,
   4647     gz: *GenZir,
   4648     scope: *Scope,
   4649     wip_members: *WipMembers,
   4650     node: Ast.Node.Index,
   4651 ) InnerError!void {
   4652     const tree = astgen.tree;
   4653     const body_node = tree.nodeData(node).node;
   4654 
   4655     const old_hasher = astgen.src_hasher;
   4656     defer astgen.src_hasher = old_hasher;
   4657     astgen.src_hasher = std.zig.SrcHasher.init(.{});
   4658     astgen.src_hasher.update(tree.getNodeSource(node));
   4659     astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
   4660 
   4661     // Up top so the ZIR instruction index marks the start range of this
   4662     // top-level declaration.
   4663     const decl_inst = try gz.makeDeclaration(node);
   4664     wip_members.nextDecl(decl_inst);
   4665     astgen.advanceSourceCursorToNode(node);
   4666 
   4667     // This is just needed for the `setDeclaration` call.
   4668     var dummy_gz = gz.makeSubBlock(scope);
   4669     defer dummy_gz.unstack();
   4670 
   4671     var comptime_gz: GenZir = .{
   4672         .is_comptime = true,
   4673         .decl_node_index = node,
   4674         .decl_line = astgen.source_line,
   4675         .parent = scope,
   4676         .astgen = astgen,
   4677         .instructions = dummy_gz.instructions,
   4678         .instructions_top = dummy_gz.instructions.items.len,
   4679     };
   4680     defer comptime_gz.unstack();
   4681 
   4682     const decl_column = astgen.source_column;
   4683 
   4684     const block_result = try fullBodyExpr(&comptime_gz, &comptime_gz.base, .{ .rl = .none }, body_node, .normal);
   4685     if (comptime_gz.isEmpty() or !comptime_gz.refIsNoReturn(block_result)) {
   4686         _ = try comptime_gz.addBreak(.break_inline, decl_inst, .void_value);
   4687     }
   4688 
   4689     var hash: std.zig.SrcHash = undefined;
   4690     astgen.src_hasher.final(&hash);
   4691     try setDeclaration(decl_inst, .{
   4692         .src_hash = hash,
   4693         .src_line = comptime_gz.decl_line,
   4694         .src_column = decl_column,
   4695         .kind = .@"comptime",
   4696         .name = .empty,
   4697         .is_pub = false,
   4698         .is_threadlocal = false,
   4699         .linkage = .normal,
   4700         .type_gz = &dummy_gz,
   4701         .align_gz = &dummy_gz,
   4702         .linksection_gz = &dummy_gz,
   4703         .addrspace_gz = &dummy_gz,
   4704         .value_gz = &comptime_gz,
   4705     });
   4706 }
   4707 
   4708 fn testDecl(
   4709     astgen: *AstGen,
   4710     gz: *GenZir,
   4711     scope: *Scope,
   4712     wip_members: *WipMembers,
   4713     node: Ast.Node.Index,
   4714 ) InnerError!void {
   4715     const tree = astgen.tree;
   4716     _, const body_node = tree.nodeData(node).opt_token_and_node;
   4717 
   4718     const old_hasher = astgen.src_hasher;
   4719     defer astgen.src_hasher = old_hasher;
   4720     astgen.src_hasher = std.zig.SrcHasher.init(.{});
   4721     astgen.src_hasher.update(tree.getNodeSource(node));
   4722     astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
   4723 
   4724     // Up top so the ZIR instruction index marks the start range of this
   4725     // top-level declaration.
   4726     const decl_inst = try gz.makeDeclaration(node);
   4727 
   4728     wip_members.nextDecl(decl_inst);
   4729     astgen.advanceSourceCursorToNode(node);
   4730 
   4731     // This is just needed for the `setDeclaration` call.
   4732     var dummy_gz: GenZir = gz.makeSubBlock(scope);
   4733     defer dummy_gz.unstack();
   4734 
   4735     var decl_block: GenZir = .{
   4736         .is_comptime = true,
   4737         .decl_node_index = node,
   4738         .decl_line = astgen.source_line,
   4739         .parent = scope,
   4740         .astgen = astgen,
   4741         .instructions = dummy_gz.instructions,
   4742         .instructions_top = dummy_gz.instructions.items.len,
   4743     };
   4744     defer decl_block.unstack();
   4745 
   4746     const decl_column = astgen.source_column;
   4747 
   4748     const test_token = tree.nodeMainToken(node);
   4749 
   4750     const test_name_token = test_token + 1;
   4751     const test_name: Zir.NullTerminatedString = switch (tree.tokenTag(test_name_token)) {
   4752         else => .empty,
   4753         .string_literal => name: {
   4754             const name = try astgen.strLitAsString(test_name_token);
   4755             const slice = astgen.string_bytes.items[@intFromEnum(name.index)..][0..name.len];
   4756             if (mem.indexOfScalar(u8, slice, 0) != null) {
   4757                 return astgen.failTok(test_name_token, "test name cannot contain null bytes", .{});
   4758             } else if (slice.len == 0) {
   4759                 return astgen.failTok(test_name_token, "empty test name must be omitted", .{});
   4760             }
   4761             break :name name.index;
   4762         },
   4763         .identifier => name: {
   4764             const ident_name_raw = tree.tokenSlice(test_name_token);
   4765 
   4766             if (mem.eql(u8, ident_name_raw, "_")) return astgen.failTok(test_name_token, "'_' used as an identifier without @\"_\" syntax", .{});
   4767 
   4768             // if not @"" syntax, just use raw token slice
   4769             if (ident_name_raw[0] != '@') {
   4770                 if (isPrimitive(ident_name_raw)) return astgen.failTok(test_name_token, "cannot test a primitive", .{});
   4771             }
   4772 
   4773             // Local variables, including function parameters.
   4774             const name_str_index = try astgen.identAsString(test_name_token);
   4775             var s = scope;
   4776             var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
   4777             var num_namespaces_out: u32 = 0;
   4778             var capturing_namespace: ?*Scope.Namespace = null;
   4779             while (true) switch (s.tag) {
   4780                 .local_val => {
   4781                     const local_val = s.cast(Scope.LocalVal).?;
   4782                     if (local_val.name == name_str_index) {
   4783                         local_val.used = .fromToken(test_name_token);
   4784                         return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{
   4785                             @tagName(local_val.id_cat),
   4786                         }, &[_]u32{
   4787                             try astgen.errNoteTok(local_val.token_src, "{s} declared here", .{
   4788                                 @tagName(local_val.id_cat),
   4789                             }),
   4790                         });
   4791                     }
   4792                     s = local_val.parent;
   4793                 },
   4794                 .local_ptr => {
   4795                     const local_ptr = s.cast(Scope.LocalPtr).?;
   4796                     if (local_ptr.name == name_str_index) {
   4797                         local_ptr.used = .fromToken(test_name_token);
   4798                         return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{
   4799                             @tagName(local_ptr.id_cat),
   4800                         }, &[_]u32{
   4801                             try astgen.errNoteTok(local_ptr.token_src, "{s} declared here", .{
   4802                                 @tagName(local_ptr.id_cat),
   4803                             }),
   4804                         });
   4805                     }
   4806                     s = local_ptr.parent;
   4807                 },
   4808                 .gen_zir => s = s.cast(GenZir).?.parent,
   4809                 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
   4810                 .namespace => {
   4811                     const ns = s.cast(Scope.Namespace).?;
   4812                     if (ns.decls.get(name_str_index)) |i| {
   4813                         if (found_already) |f| {
   4814                             return astgen.failTokNotes(test_name_token, "ambiguous reference", .{}, &.{
   4815                                 try astgen.errNoteNode(f, "declared here", .{}),
   4816                                 try astgen.errNoteNode(i, "also declared here", .{}),
   4817                             });
   4818                         }
   4819                         // We found a match but must continue looking for ambiguous references to decls.
   4820                         found_already = i;
   4821                     }
   4822                     num_namespaces_out += 1;
   4823                     capturing_namespace = ns;
   4824                     s = ns.parent;
   4825                 },
   4826                 .top => break,
   4827             };
   4828             if (found_already == null) {
   4829                 const ident_name = try astgen.identifierTokenString(test_name_token);
   4830                 return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name});
   4831             }
   4832 
   4833             break :name try astgen.identAsString(test_name_token);
   4834         },
   4835     };
   4836 
   4837     var fn_block: GenZir = .{
   4838         .is_comptime = false,
   4839         .decl_node_index = node,
   4840         .decl_line = decl_block.decl_line,
   4841         .parent = &decl_block.base,
   4842         .astgen = astgen,
   4843         .instructions = decl_block.instructions,
   4844         .instructions_top = decl_block.instructions.items.len,
   4845     };
   4846     defer fn_block.unstack();
   4847 
   4848     const prev_within_fn = astgen.within_fn;
   4849     const prev_fn_block = astgen.fn_block;
   4850     const prev_fn_ret_ty = astgen.fn_ret_ty;
   4851     astgen.within_fn = true;
   4852     astgen.fn_block = &fn_block;
   4853     astgen.fn_ret_ty = .anyerror_void_error_union_type;
   4854     defer {
   4855         astgen.within_fn = prev_within_fn;
   4856         astgen.fn_block = prev_fn_block;
   4857         astgen.fn_ret_ty = prev_fn_ret_ty;
   4858     }
   4859 
   4860     astgen.advanceSourceCursorToNode(body_node);
   4861     const lbrace_line = astgen.source_line - decl_block.decl_line;
   4862     const lbrace_column = astgen.source_column;
   4863 
   4864     const block_result = try fullBodyExpr(&fn_block, &fn_block.base, .{ .rl = .none }, body_node, .normal);
   4865     if (fn_block.isEmpty() or !fn_block.refIsNoReturn(block_result)) {
   4866 
   4867         // As our last action before the return, "pop" the error trace if needed
   4868         _ = try fn_block.addRestoreErrRetIndex(.ret, .always, node);
   4869 
   4870         // Add implicit return at end of function.
   4871         _ = try fn_block.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node));
   4872     }
   4873 
   4874     const func_inst = try decl_block.addFunc(.{
   4875         .src_node = node,
   4876 
   4877         .cc_ref = .none,
   4878         .cc_gz = null,
   4879         .ret_ref = .anyerror_void_error_union_type,
   4880         .ret_gz = null,
   4881 
   4882         .ret_param_refs = &.{},
   4883         .param_insts = &.{},
   4884         .ret_ty_is_generic = false,
   4885 
   4886         .lbrace_line = lbrace_line,
   4887         .lbrace_column = lbrace_column,
   4888         .param_block = decl_inst,
   4889         .body_gz = &fn_block,
   4890         .is_var_args = false,
   4891         .is_inferred_error = false,
   4892         .is_noinline = false,
   4893         .noalias_bits = 0,
   4894 
   4895         // Tests don't have a prototype that needs hashing
   4896         .proto_hash = .{0} ** 16,
   4897     });
   4898 
   4899     _ = try decl_block.addBreak(.break_inline, decl_inst, func_inst);
   4900 
   4901     var hash: std.zig.SrcHash = undefined;
   4902     astgen.src_hasher.final(&hash);
   4903     try setDeclaration(decl_inst, .{
   4904         .src_hash = hash,
   4905         .src_line = decl_block.decl_line,
   4906         .src_column = decl_column,
   4907 
   4908         .kind = switch (tree.tokenTag(test_name_token)) {
   4909             .string_literal => .@"test",
   4910             .identifier => .decltest,
   4911             else => .unnamed_test,
   4912         },
   4913         .name = test_name,
   4914         .is_pub = false,
   4915         .is_threadlocal = false,
   4916         .linkage = .normal,
   4917 
   4918         .type_gz = &dummy_gz,
   4919         .align_gz = &dummy_gz,
   4920         .linksection_gz = &dummy_gz,
   4921         .addrspace_gz = &dummy_gz,
   4922         .value_gz = &decl_block,
   4923     });
   4924 }
   4925 
   4926 fn structDeclInner(
   4927     gz: *GenZir,
   4928     scope: *Scope,
   4929     node: Ast.Node.Index,
   4930     container_decl: Ast.full.ContainerDecl,
   4931     layout: std.builtin.Type.ContainerLayout,
   4932     backing_int_node: Ast.Node.OptionalIndex,
   4933     name_strat: Zir.Inst.NameStrategy,
   4934 ) InnerError!Zir.Inst.Ref {
   4935     const astgen = gz.astgen;
   4936     const gpa = astgen.gpa;
   4937     const tree = astgen.tree;
   4938 
   4939     is_tuple: {
   4940         const tuple_field_node = for (container_decl.ast.members) |member_node| {
   4941             const container_field = tree.fullContainerField(member_node) orelse continue;
   4942             if (container_field.ast.tuple_like) break member_node;
   4943         } else break :is_tuple;
   4944 
   4945         if (node == .root) {
   4946             return astgen.failNode(tuple_field_node, "file cannot be a tuple", .{});
   4947         } else {
   4948             return tupleDecl(gz, scope, node, container_decl, layout, backing_int_node);
   4949         }
   4950     }
   4951 
   4952     const decl_inst = try gz.reserveInstructionIndex();
   4953 
   4954     if (container_decl.ast.members.len == 0 and backing_int_node == .none) {
   4955         try gz.setStruct(decl_inst, .{
   4956             .src_node = node,
   4957             .layout = layout,
   4958             .captures_len = 0,
   4959             .fields_len = 0,
   4960             .decls_len = 0,
   4961             .has_backing_int = false,
   4962             .known_non_opv = false,
   4963             .known_comptime_only = false,
   4964             .any_comptime_fields = false,
   4965             .any_default_inits = false,
   4966             .any_aligned_fields = false,
   4967             .fields_hash = std.zig.hashSrc(@tagName(layout)),
   4968             .name_strat = name_strat,
   4969         });
   4970         return decl_inst.toRef();
   4971     }
   4972 
   4973     var namespace: Scope.Namespace = .{
   4974         .parent = scope,
   4975         .node = node,
   4976         .inst = decl_inst,
   4977         .declaring_gz = gz,
   4978         .maybe_generic = astgen.within_fn,
   4979     };
   4980     defer namespace.deinit(gpa);
   4981 
   4982     // The struct_decl instruction introduces a scope in which the decls of the struct
   4983     // are in scope, so that field types, alignments, and default value expressions
   4984     // can refer to decls within the struct itself.
   4985     astgen.advanceSourceCursorToNode(node);
   4986     var block_scope: GenZir = .{
   4987         .parent = &namespace.base,
   4988         .decl_node_index = node,
   4989         .decl_line = gz.decl_line,
   4990         .astgen = astgen,
   4991         .is_comptime = true,
   4992         .instructions = gz.instructions,
   4993         .instructions_top = gz.instructions.items.len,
   4994     };
   4995     defer block_scope.unstack();
   4996 
   4997     const scratch_top = astgen.scratch.items.len;
   4998     defer astgen.scratch.items.len = scratch_top;
   4999 
   5000     var backing_int_body_len: usize = 0;
   5001     const backing_int_ref: Zir.Inst.Ref = blk: {
   5002         if (backing_int_node.unwrap()) |arg| {
   5003             if (layout != .@"packed") {
   5004                 return astgen.failNode(arg, "non-packed struct does not support backing integer type", .{});
   5005             } else {
   5006                 const backing_int_ref = try typeExpr(&block_scope, &namespace.base, arg);
   5007                 if (!block_scope.isEmpty()) {
   5008                     if (!block_scope.endsWithNoReturn()) {
   5009                         _ = try block_scope.addBreak(.break_inline, decl_inst, backing_int_ref);
   5010                     }
   5011 
   5012                     const body = block_scope.instructionsSlice();
   5013                     const old_scratch_len = astgen.scratch.items.len;
   5014                     try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
   5015                     appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
   5016                     backing_int_body_len = astgen.scratch.items.len - old_scratch_len;
   5017                     block_scope.instructions.items.len = block_scope.instructions_top;
   5018                 }
   5019                 break :blk backing_int_ref;
   5020             }
   5021         } else {
   5022             break :blk .none;
   5023         }
   5024     };
   5025 
   5026     const decl_count = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"struct");
   5027     const field_count: u32 = @intCast(container_decl.ast.members.len - decl_count);
   5028 
   5029     const bits_per_field = 4;
   5030     const max_field_size = 5;
   5031     var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size);
   5032     defer wip_members.deinit();
   5033 
   5034     // We will use the scratch buffer, starting here, for the bodies:
   5035     //    bodies: { // for every fields_len
   5036     //        field_type_body_inst: Inst, // for each field_type_body_len
   5037     //        align_body_inst: Inst, // for each align_body_len
   5038     //        init_body_inst: Inst, // for each init_body_len
   5039     //    }
   5040     // Note that the scratch buffer is simultaneously being used by WipMembers, however
   5041     // it will not access any elements beyond this point in the ArrayList. It also
   5042     // accesses via the ArrayList items field so it can handle the scratch buffer being
   5043     // reallocated.
   5044     // No defer needed here because it is handled by `wip_members.deinit()` above.
   5045     const bodies_start = astgen.scratch.items.len;
   5046 
   5047     const old_hasher = astgen.src_hasher;
   5048     defer astgen.src_hasher = old_hasher;
   5049     astgen.src_hasher = std.zig.SrcHasher.init(.{});
   5050     astgen.src_hasher.update(@tagName(layout));
   5051     if (backing_int_node.unwrap()) |arg| {
   5052         astgen.src_hasher.update(tree.getNodeSource(arg));
   5053     }
   5054 
   5055     var known_non_opv = false;
   5056     var known_comptime_only = false;
   5057     var any_comptime_fields = false;
   5058     var any_aligned_fields = false;
   5059     var any_default_inits = false;
   5060     for (container_decl.ast.members) |member_node| {
   5061         var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
   5062             .decl => continue,
   5063             .field => |field| field,
   5064         };
   5065 
   5066         astgen.src_hasher.update(tree.getNodeSource(member_node));
   5067 
   5068         const field_name = try astgen.identAsString(member.ast.main_token);
   5069         member.convertToNonTupleLike(astgen.tree);
   5070         assert(!member.ast.tuple_like);
   5071         wip_members.appendToField(@intFromEnum(field_name));
   5072 
   5073         const type_expr = member.ast.type_expr.unwrap() orelse {
   5074             return astgen.failTok(member.ast.main_token, "struct field missing type", .{});
   5075         };
   5076 
   5077         const field_type = try typeExpr(&block_scope, &namespace.base, type_expr);
   5078         const have_type_body = !block_scope.isEmpty();
   5079         const have_align = member.ast.align_expr != .none;
   5080         const have_value = member.ast.value_expr != .none;
   5081         const is_comptime = member.comptime_token != null;
   5082 
   5083         if (is_comptime) {
   5084             switch (layout) {
   5085                 .@"packed", .@"extern" => return astgen.failTok(member.comptime_token.?, "{s} struct fields cannot be marked comptime", .{@tagName(layout)}),
   5086                 .auto => any_comptime_fields = true,
   5087             }
   5088         } else {
   5089             known_non_opv = known_non_opv or
   5090                 nodeImpliesMoreThanOnePossibleValue(tree, type_expr);
   5091             known_comptime_only = known_comptime_only or
   5092                 nodeImpliesComptimeOnly(tree, type_expr);
   5093         }
   5094         wip_members.nextField(bits_per_field, .{ have_align, have_value, is_comptime, have_type_body });
   5095 
   5096         if (have_type_body) {
   5097             if (!block_scope.endsWithNoReturn()) {
   5098                 _ = try block_scope.addBreak(.break_inline, decl_inst, field_type);
   5099             }
   5100             const body = block_scope.instructionsSlice();
   5101             const old_scratch_len = astgen.scratch.items.len;
   5102             try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
   5103             appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
   5104             wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len));
   5105             block_scope.instructions.items.len = block_scope.instructions_top;
   5106         } else {
   5107             wip_members.appendToField(@intFromEnum(field_type));
   5108         }
   5109 
   5110         if (member.ast.align_expr.unwrap()) |align_expr| {
   5111             if (layout == .@"packed") {
   5112                 return astgen.failNode(align_expr, "unable to override alignment of packed struct fields", .{});
   5113             }
   5114             any_aligned_fields = true;
   5115             const align_ref = try expr(&block_scope, &namespace.base, coerced_align_ri, align_expr);
   5116             if (!block_scope.endsWithNoReturn()) {
   5117                 _ = try block_scope.addBreak(.break_inline, decl_inst, align_ref);
   5118             }
   5119             const body = block_scope.instructionsSlice();
   5120             const old_scratch_len = astgen.scratch.items.len;
   5121             try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
   5122             appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
   5123             wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len));
   5124             block_scope.instructions.items.len = block_scope.instructions_top;
   5125         }
   5126 
   5127         if (member.ast.value_expr.unwrap()) |value_expr| {
   5128             any_default_inits = true;
   5129 
   5130             // The decl_inst is used as here so that we can easily reconstruct a mapping
   5131             // between it and the field type when the fields inits are analyzed.
   5132             const ri: ResultInfo = .{ .rl = if (field_type == .none) .none else .{ .coerced_ty = decl_inst.toRef() } };
   5133 
   5134             const default_inst = try expr(&block_scope, &namespace.base, ri, value_expr);
   5135             if (!block_scope.endsWithNoReturn()) {
   5136                 _ = try block_scope.addBreak(.break_inline, decl_inst, default_inst);
   5137             }
   5138             const body = block_scope.instructionsSlice();
   5139             const old_scratch_len = astgen.scratch.items.len;
   5140             try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
   5141             appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
   5142             wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len));
   5143             block_scope.instructions.items.len = block_scope.instructions_top;
   5144         } else if (member.comptime_token) |comptime_token| {
   5145             return astgen.failTok(comptime_token, "comptime field without default initialization value", .{});
   5146         }
   5147     }
   5148 
   5149     var fields_hash: std.zig.SrcHash = undefined;
   5150     astgen.src_hasher.final(&fields_hash);
   5151 
   5152     try gz.setStruct(decl_inst, .{
   5153         .src_node = node,
   5154         .layout = layout,
   5155         .captures_len = @intCast(namespace.captures.count()),
   5156         .fields_len = field_count,
   5157         .decls_len = decl_count,
   5158         .has_backing_int = backing_int_ref != .none,
   5159         .known_non_opv = known_non_opv,
   5160         .known_comptime_only = known_comptime_only,
   5161         .any_comptime_fields = any_comptime_fields,
   5162         .any_default_inits = any_default_inits,
   5163         .any_aligned_fields = any_aligned_fields,
   5164         .fields_hash = fields_hash,
   5165         .name_strat = name_strat,
   5166     });
   5167 
   5168     wip_members.finishBits(bits_per_field);
   5169     const decls_slice = wip_members.declsSlice();
   5170     const fields_slice = wip_members.fieldsSlice();
   5171     const bodies_slice = astgen.scratch.items[bodies_start..];
   5172     try astgen.extra.ensureUnusedCapacity(gpa, backing_int_body_len + 2 +
   5173         decls_slice.len + namespace.captures.count() * 2 + fields_slice.len + bodies_slice.len);
   5174     astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys()));
   5175     astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.values()));
   5176     if (backing_int_ref != .none) {
   5177         astgen.extra.appendAssumeCapacity(@intCast(backing_int_body_len));
   5178         if (backing_int_body_len == 0) {
   5179             astgen.extra.appendAssumeCapacity(@intFromEnum(backing_int_ref));
   5180         } else {
   5181             astgen.extra.appendSliceAssumeCapacity(astgen.scratch.items[scratch_top..][0..backing_int_body_len]);
   5182         }
   5183     }
   5184     astgen.extra.appendSliceAssumeCapacity(decls_slice);
   5185     astgen.extra.appendSliceAssumeCapacity(fields_slice);
   5186     astgen.extra.appendSliceAssumeCapacity(bodies_slice);
   5187 
   5188     block_scope.unstack();
   5189     return decl_inst.toRef();
   5190 }
   5191 
   5192 fn tupleDecl(
   5193     gz: *GenZir,
   5194     scope: *Scope,
   5195     node: Ast.Node.Index,
   5196     container_decl: Ast.full.ContainerDecl,
   5197     layout: std.builtin.Type.ContainerLayout,
   5198     backing_int_node: Ast.Node.OptionalIndex,
   5199 ) InnerError!Zir.Inst.Ref {
   5200     const astgen = gz.astgen;
   5201     const gpa = astgen.gpa;
   5202     const tree = astgen.tree;
   5203 
   5204     switch (layout) {
   5205         .auto => {},
   5206         .@"extern", .@"packed" => return astgen.failNode(node, "{s} tuples are not supported", .{@tagName(layout)}),
   5207     }
   5208 
   5209     if (backing_int_node.unwrap()) |arg| {
   5210         return astgen.failNode(arg, "tuple does not support backing integer type", .{});
   5211     }
   5212 
   5213     // We will use the scratch buffer, starting here, for the field data:
   5214     // 1. fields: { // for every `fields_len` (stored in `extended.small`)
   5215     //        type: Inst.Ref,
   5216     //        init: Inst.Ref, // `.none` for non-`comptime` fields
   5217     //    }
   5218     const fields_start = astgen.scratch.items.len;
   5219     defer astgen.scratch.items.len = fields_start;
   5220 
   5221     try astgen.scratch.ensureUnusedCapacity(gpa, container_decl.ast.members.len * 2);
   5222 
   5223     for (container_decl.ast.members) |member_node| {
   5224         const field = tree.fullContainerField(member_node) orelse {
   5225             const tuple_member = for (container_decl.ast.members) |maybe_tuple| switch (tree.nodeTag(maybe_tuple)) {
   5226                 .container_field_init,
   5227                 .container_field_align,
   5228                 .container_field,
   5229                 => break maybe_tuple,
   5230                 else => {},
   5231             } else unreachable;
   5232             return astgen.failNodeNotes(
   5233                 member_node,
   5234                 "tuple declarations cannot contain declarations",
   5235                 .{},
   5236                 &.{try astgen.errNoteNode(tuple_member, "tuple field here", .{})},
   5237             );
   5238         };
   5239 
   5240         if (!field.ast.tuple_like) {
   5241             return astgen.failTok(field.ast.main_token, "tuple field has a name", .{});
   5242         }
   5243 
   5244         if (field.ast.align_expr != .none) {
   5245             return astgen.failTok(field.ast.main_token, "tuple field has alignment", .{});
   5246         }
   5247 
   5248         if (field.ast.value_expr != .none and field.comptime_token == null) {
   5249             return astgen.failTok(field.ast.main_token, "non-comptime tuple field has default initialization value", .{});
   5250         }
   5251 
   5252         if (field.ast.value_expr == .none and field.comptime_token != null) {
   5253             return astgen.failTok(field.comptime_token.?, "comptime field without default initialization value", .{});
   5254         }
   5255 
   5256         const field_type_ref = try typeExpr(gz, scope, field.ast.type_expr.unwrap().?);
   5257         astgen.scratch.appendAssumeCapacity(@intFromEnum(field_type_ref));
   5258 
   5259         if (field.ast.value_expr.unwrap()) |value_expr| {
   5260             const field_init_ref = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = field_type_ref } }, value_expr, .tuple_field_default_value);
   5261             astgen.scratch.appendAssumeCapacity(@intFromEnum(field_init_ref));
   5262         } else {
   5263             astgen.scratch.appendAssumeCapacity(@intFromEnum(Zir.Inst.Ref.none));
   5264         }
   5265     }
   5266 
   5267     const fields_len = std.math.cast(u16, container_decl.ast.members.len) orelse {
   5268         return astgen.failNode(node, "this compiler implementation only supports 65535 tuple fields", .{});
   5269     };
   5270 
   5271     const extra_trail = astgen.scratch.items[fields_start..];
   5272     assert(extra_trail.len == fields_len * 2);
   5273     try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.TupleDecl).@"struct".fields.len + extra_trail.len);
   5274     const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.TupleDecl{
   5275         .src_node = gz.nodeIndexToRelative(node),
   5276     });
   5277     astgen.extra.appendSliceAssumeCapacity(extra_trail);
   5278 
   5279     return gz.add(.{
   5280         .tag = .extended,
   5281         .data = .{ .extended = .{
   5282             .opcode = .tuple_decl,
   5283             .small = fields_len,
   5284             .operand = payload_index,
   5285         } },
   5286     });
   5287 }
   5288 
   5289 fn unionDeclInner(
   5290     gz: *GenZir,
   5291     scope: *Scope,
   5292     node: Ast.Node.Index,
   5293     members: []const Ast.Node.Index,
   5294     layout: std.builtin.Type.ContainerLayout,
   5295     opt_arg_node: Ast.Node.OptionalIndex,
   5296     auto_enum_tok: ?Ast.TokenIndex,
   5297     name_strat: Zir.Inst.NameStrategy,
   5298 ) InnerError!Zir.Inst.Ref {
   5299     const decl_inst = try gz.reserveInstructionIndex();
   5300 
   5301     const astgen = gz.astgen;
   5302     const gpa = astgen.gpa;
   5303 
   5304     var namespace: Scope.Namespace = .{
   5305         .parent = scope,
   5306         .node = node,
   5307         .inst = decl_inst,
   5308         .declaring_gz = gz,
   5309         .maybe_generic = astgen.within_fn,
   5310     };
   5311     defer namespace.deinit(gpa);
   5312 
   5313     // The union_decl instruction introduces a scope in which the decls of the union
   5314     // are in scope, so that field types, alignments, and default value expressions
   5315     // can refer to decls within the union itself.
   5316     astgen.advanceSourceCursorToNode(node);
   5317     var block_scope: GenZir = .{
   5318         .parent = &namespace.base,
   5319         .decl_node_index = node,
   5320         .decl_line = gz.decl_line,
   5321         .astgen = astgen,
   5322         .is_comptime = true,
   5323         .instructions = gz.instructions,
   5324         .instructions_top = gz.instructions.items.len,
   5325     };
   5326     defer block_scope.unstack();
   5327 
   5328     const decl_count = try astgen.scanContainer(&namespace, members, .@"union");
   5329     const field_count: u32 = @intCast(members.len - decl_count);
   5330 
   5331     if (layout != .auto and (auto_enum_tok != null or opt_arg_node != .none)) {
   5332         if (opt_arg_node.unwrap()) |arg_node| {
   5333             return astgen.failNode(arg_node, "{s} union does not support enum tag type", .{@tagName(layout)});
   5334         } else {
   5335             return astgen.failTok(auto_enum_tok.?, "{s} union does not support enum tag type", .{@tagName(layout)});
   5336         }
   5337     }
   5338 
   5339     const arg_inst: Zir.Inst.Ref = if (opt_arg_node.unwrap()) |arg_node|
   5340         try typeExpr(&block_scope, &namespace.base, arg_node)
   5341     else
   5342         .none;
   5343 
   5344     const bits_per_field = 4;
   5345     const max_field_size = 4;
   5346     var any_aligned_fields = false;
   5347     var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size);
   5348     defer wip_members.deinit();
   5349 
   5350     const old_hasher = astgen.src_hasher;
   5351     defer astgen.src_hasher = old_hasher;
   5352     astgen.src_hasher = std.zig.SrcHasher.init(.{});
   5353     astgen.src_hasher.update(@tagName(layout));
   5354     astgen.src_hasher.update(&.{@intFromBool(auto_enum_tok != null)});
   5355     if (opt_arg_node.unwrap()) |arg_node| {
   5356         astgen.src_hasher.update(astgen.tree.getNodeSource(arg_node));
   5357     }
   5358 
   5359     for (members) |member_node| {
   5360         var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
   5361             .decl => continue,
   5362             .field => |field| field,
   5363         };
   5364         astgen.src_hasher.update(astgen.tree.getNodeSource(member_node));
   5365         member.convertToNonTupleLike(astgen.tree);
   5366         if (member.ast.tuple_like) {
   5367             return astgen.failTok(member.ast.main_token, "union field missing name", .{});
   5368         }
   5369         if (member.comptime_token) |comptime_token| {
   5370             return astgen.failTok(comptime_token, "union fields cannot be marked comptime", .{});
   5371         }
   5372 
   5373         const field_name = try astgen.identAsString(member.ast.main_token);
   5374         wip_members.appendToField(@intFromEnum(field_name));
   5375 
   5376         const have_type = member.ast.type_expr != .none;
   5377         const have_align = member.ast.align_expr != .none;
   5378         const have_value = member.ast.value_expr != .none;
   5379         const unused = false;
   5380         wip_members.nextField(bits_per_field, .{ have_type, have_align, have_value, unused });
   5381 
   5382         if (member.ast.type_expr.unwrap()) |type_expr| {
   5383             const field_type = try typeExpr(&block_scope, &namespace.base, type_expr);
   5384             wip_members.appendToField(@intFromEnum(field_type));
   5385         } else if (arg_inst == .none and auto_enum_tok == null) {
   5386             return astgen.failNode(member_node, "union field missing type", .{});
   5387         }
   5388         if (member.ast.align_expr.unwrap()) |align_expr| {
   5389             if (layout == .@"packed") {
   5390                 return astgen.failNode(align_expr, "unable to override alignment of packed union fields", .{});
   5391             }
   5392             const align_inst = try expr(&block_scope, &block_scope.base, coerced_align_ri, align_expr);
   5393             wip_members.appendToField(@intFromEnum(align_inst));
   5394             any_aligned_fields = true;
   5395         }
   5396         if (member.ast.value_expr.unwrap()) |value_expr| {
   5397             if (arg_inst == .none) {
   5398                 return astgen.failNodeNotes(
   5399                     node,
   5400                     "explicitly valued tagged union missing integer tag type",
   5401                     .{},
   5402                     &[_]u32{
   5403                         try astgen.errNoteNode(
   5404                             value_expr,
   5405                             "tag value specified here",
   5406                             .{},
   5407                         ),
   5408                     },
   5409                 );
   5410             }
   5411             if (auto_enum_tok == null) {
   5412                 return astgen.failNodeNotes(
   5413                     node,
   5414                     "explicitly valued tagged union requires inferred enum tag type",
   5415                     .{},
   5416                     &[_]u32{
   5417                         try astgen.errNoteNode(
   5418                             value_expr,
   5419                             "tag value specified here",
   5420                             .{},
   5421                         ),
   5422                     },
   5423                 );
   5424             }
   5425             const tag_value = try expr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = arg_inst } }, value_expr);
   5426             wip_members.appendToField(@intFromEnum(tag_value));
   5427         }
   5428     }
   5429 
   5430     var fields_hash: std.zig.SrcHash = undefined;
   5431     astgen.src_hasher.final(&fields_hash);
   5432 
   5433     if (!block_scope.isEmpty()) {
   5434         _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
   5435     }
   5436 
   5437     const body = block_scope.instructionsSlice();
   5438     const body_len = astgen.countBodyLenAfterFixups(body);
   5439 
   5440     try gz.setUnion(decl_inst, .{
   5441         .src_node = node,
   5442         .layout = layout,
   5443         .tag_type = arg_inst,
   5444         .captures_len = @intCast(namespace.captures.count()),
   5445         .body_len = body_len,
   5446         .fields_len = field_count,
   5447         .decls_len = decl_count,
   5448         .auto_enum_tag = auto_enum_tok != null,
   5449         .any_aligned_fields = any_aligned_fields,
   5450         .fields_hash = fields_hash,
   5451         .name_strat = name_strat,
   5452     });
   5453 
   5454     wip_members.finishBits(bits_per_field);
   5455     const decls_slice = wip_members.declsSlice();
   5456     const fields_slice = wip_members.fieldsSlice();
   5457     try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() * 2 + decls_slice.len + body_len + fields_slice.len);
   5458     astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys()));
   5459     astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.values()));
   5460     astgen.extra.appendSliceAssumeCapacity(decls_slice);
   5461     astgen.appendBodyWithFixups(body);
   5462     astgen.extra.appendSliceAssumeCapacity(fields_slice);
   5463 
   5464     block_scope.unstack();
   5465     return decl_inst.toRef();
   5466 }
   5467 
   5468 fn containerDecl(
   5469     gz: *GenZir,
   5470     scope: *Scope,
   5471     ri: ResultInfo,
   5472     node: Ast.Node.Index,
   5473     container_decl: Ast.full.ContainerDecl,
   5474     name_strat: Zir.Inst.NameStrategy,
   5475 ) InnerError!Zir.Inst.Ref {
   5476     const astgen = gz.astgen;
   5477     const gpa = astgen.gpa;
   5478     const tree = astgen.tree;
   5479 
   5480     const prev_fn_block = astgen.fn_block;
   5481     astgen.fn_block = null;
   5482     defer astgen.fn_block = prev_fn_block;
   5483 
   5484     // We must not create any types until Sema. Here the goal is only to generate
   5485     // ZIR for all the field types, alignments, and default value expressions.
   5486 
   5487     switch (tree.tokenTag(container_decl.ast.main_token)) {
   5488         .keyword_struct => {
   5489             const layout: std.builtin.Type.ContainerLayout = if (container_decl.layout_token) |t| switch (tree.tokenTag(t)) {
   5490                 .keyword_packed => .@"packed",
   5491                 .keyword_extern => .@"extern",
   5492                 else => unreachable,
   5493             } else .auto;
   5494 
   5495             const result = try structDeclInner(gz, scope, node, container_decl, layout, container_decl.ast.arg, name_strat);
   5496             return rvalue(gz, ri, result, node);
   5497         },
   5498         .keyword_union => {
   5499             const layout: std.builtin.Type.ContainerLayout = if (container_decl.layout_token) |t| switch (tree.tokenTag(t)) {
   5500                 .keyword_packed => .@"packed",
   5501                 .keyword_extern => .@"extern",
   5502                 else => unreachable,
   5503             } else .auto;
   5504 
   5505             const result = try unionDeclInner(gz, scope, node, container_decl.ast.members, layout, container_decl.ast.arg, container_decl.ast.enum_token, name_strat);
   5506             return rvalue(gz, ri, result, node);
   5507         },
   5508         .keyword_enum => {
   5509             if (container_decl.layout_token) |t| {
   5510                 return astgen.failTok(t, "enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", .{});
   5511             }
   5512             // Count total fields as well as how many have explicitly provided tag values.
   5513             const counts = blk: {
   5514                 var values: usize = 0;
   5515                 var total_fields: usize = 0;
   5516                 var decls: usize = 0;
   5517                 var opt_nonexhaustive_node: Ast.Node.OptionalIndex = .none;
   5518                 var nonfinal_nonexhaustive = false;
   5519                 for (container_decl.ast.members) |member_node| {
   5520                     var member = tree.fullContainerField(member_node) orelse {
   5521                         decls += 1;
   5522                         continue;
   5523                     };
   5524                     member.convertToNonTupleLike(astgen.tree);
   5525                     if (member.ast.tuple_like) {
   5526                         return astgen.failTok(member.ast.main_token, "enum field missing name", .{});
   5527                     }
   5528                     if (member.comptime_token) |comptime_token| {
   5529                         return astgen.failTok(comptime_token, "enum fields cannot be marked comptime", .{});
   5530                     }
   5531                     if (member.ast.type_expr.unwrap()) |type_expr| {
   5532                         return astgen.failNodeNotes(
   5533                             type_expr,
   5534                             "enum fields do not have types",
   5535                             .{},
   5536                             &[_]u32{
   5537                                 try astgen.errNoteNode(
   5538                                     node,
   5539                                     "consider 'union(enum)' here to make it a tagged union",
   5540                                     .{},
   5541                                 ),
   5542                             },
   5543                         );
   5544                     }
   5545                     if (member.ast.align_expr.unwrap()) |align_expr| {
   5546                         return astgen.failNode(align_expr, "enum fields cannot be aligned", .{});
   5547                     }
   5548 
   5549                     const name_token = member.ast.main_token;
   5550                     if (mem.eql(u8, tree.tokenSlice(name_token), "_")) {
   5551                         if (opt_nonexhaustive_node.unwrap()) |nonexhaustive_node| {
   5552                             return astgen.failNodeNotes(
   5553                                 member_node,
   5554                                 "redundant non-exhaustive enum mark",
   5555                                 .{},
   5556                                 &[_]u32{
   5557                                     try astgen.errNoteNode(
   5558                                         nonexhaustive_node,
   5559                                         "other mark here",
   5560                                         .{},
   5561                                     ),
   5562                                 },
   5563                             );
   5564                         }
   5565                         opt_nonexhaustive_node = member_node.toOptional();
   5566                         if (member.ast.value_expr.unwrap()) |value_expr| {
   5567                             return astgen.failNode(value_expr, "'_' is used to mark an enum as non-exhaustive and cannot be assigned a value", .{});
   5568                         }
   5569                         continue;
   5570                     } else if (opt_nonexhaustive_node != .none) {
   5571                         nonfinal_nonexhaustive = true;
   5572                     }
   5573                     total_fields += 1;
   5574                     if (member.ast.value_expr.unwrap()) |value_expr| {
   5575                         if (container_decl.ast.arg == .none) {
   5576                             return astgen.failNode(value_expr, "value assigned to enum tag with inferred tag type", .{});
   5577                         }
   5578                         values += 1;
   5579                     }
   5580                 }
   5581                 if (nonfinal_nonexhaustive) {
   5582                     return astgen.failNode(opt_nonexhaustive_node.unwrap().?, "'_' field of non-exhaustive enum must be last", .{});
   5583                 }
   5584                 break :blk .{
   5585                     .total_fields = total_fields,
   5586                     .values = values,
   5587                     .decls = decls,
   5588                     .nonexhaustive_node = opt_nonexhaustive_node,
   5589                 };
   5590             };
   5591             if (counts.nonexhaustive_node != .none and container_decl.ast.arg == .none) {
   5592                 const nonexhaustive_node = counts.nonexhaustive_node.unwrap().?;
   5593                 return astgen.failNodeNotes(
   5594                     node,
   5595                     "non-exhaustive enum missing integer tag type",
   5596                     .{},
   5597                     &[_]u32{
   5598                         try astgen.errNoteNode(
   5599                             nonexhaustive_node,
   5600                             "marked non-exhaustive here",
   5601                             .{},
   5602                         ),
   5603                     },
   5604                 );
   5605             }
   5606             // In this case we must generate ZIR code for the tag values, similar to
   5607             // how structs are handled above.
   5608             const nonexhaustive = counts.nonexhaustive_node != .none;
   5609 
   5610             const decl_inst = try gz.reserveInstructionIndex();
   5611 
   5612             var namespace: Scope.Namespace = .{
   5613                 .parent = scope,
   5614                 .node = node,
   5615                 .inst = decl_inst,
   5616                 .declaring_gz = gz,
   5617                 .maybe_generic = astgen.within_fn,
   5618             };
   5619             defer namespace.deinit(gpa);
   5620 
   5621             // The enum_decl instruction introduces a scope in which the decls of the enum
   5622             // are in scope, so that tag values can refer to decls within the enum itself.
   5623             astgen.advanceSourceCursorToNode(node);
   5624             var block_scope: GenZir = .{
   5625                 .parent = &namespace.base,
   5626                 .decl_node_index = node,
   5627                 .decl_line = gz.decl_line,
   5628                 .astgen = astgen,
   5629                 .is_comptime = true,
   5630                 .instructions = gz.instructions,
   5631                 .instructions_top = gz.instructions.items.len,
   5632             };
   5633             defer block_scope.unstack();
   5634 
   5635             _ = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"enum");
   5636             namespace.base.tag = .namespace;
   5637 
   5638             const arg_inst: Zir.Inst.Ref = if (container_decl.ast.arg.unwrap()) |arg|
   5639                 try comptimeExpr(&block_scope, &namespace.base, coerced_type_ri, arg, .type)
   5640             else
   5641                 .none;
   5642 
   5643             const bits_per_field = 1;
   5644             const max_field_size = 2;
   5645             var wip_members = try WipMembers.init(gpa, &astgen.scratch, @intCast(counts.decls), @intCast(counts.total_fields), bits_per_field, max_field_size);
   5646             defer wip_members.deinit();
   5647 
   5648             const old_hasher = astgen.src_hasher;
   5649             defer astgen.src_hasher = old_hasher;
   5650             astgen.src_hasher = std.zig.SrcHasher.init(.{});
   5651             if (container_decl.ast.arg.unwrap()) |arg| {
   5652                 astgen.src_hasher.update(tree.getNodeSource(arg));
   5653             }
   5654             astgen.src_hasher.update(&.{@intFromBool(nonexhaustive)});
   5655 
   5656             for (container_decl.ast.members) |member_node| {
   5657                 if (member_node.toOptional() == counts.nonexhaustive_node)
   5658                     continue;
   5659                 astgen.src_hasher.update(tree.getNodeSource(member_node));
   5660                 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
   5661                     .decl => continue,
   5662                     .field => |field| field,
   5663                 };
   5664                 member.convertToNonTupleLike(astgen.tree);
   5665                 assert(member.comptime_token == null);
   5666                 assert(member.ast.type_expr == .none);
   5667                 assert(member.ast.align_expr == .none);
   5668 
   5669                 const field_name = try astgen.identAsString(member.ast.main_token);
   5670                 wip_members.appendToField(@intFromEnum(field_name));
   5671 
   5672                 const have_value = member.ast.value_expr != .none;
   5673                 wip_members.nextField(bits_per_field, .{have_value});
   5674 
   5675                 if (member.ast.value_expr.unwrap()) |value_expr| {
   5676                     if (arg_inst == .none) {
   5677                         return astgen.failNodeNotes(
   5678                             node,
   5679                             "explicitly valued enum missing integer tag type",
   5680                             .{},
   5681                             &[_]u32{
   5682                                 try astgen.errNoteNode(
   5683                                     value_expr,
   5684                                     "tag value specified here",
   5685                                     .{},
   5686                                 ),
   5687                             },
   5688                         );
   5689                     }
   5690                     const tag_value_inst = try expr(&block_scope, &namespace.base, .{ .rl = .{ .ty = arg_inst } }, value_expr);
   5691                     wip_members.appendToField(@intFromEnum(tag_value_inst));
   5692                 }
   5693             }
   5694 
   5695             if (!block_scope.isEmpty()) {
   5696                 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
   5697             }
   5698 
   5699             var fields_hash: std.zig.SrcHash = undefined;
   5700             astgen.src_hasher.final(&fields_hash);
   5701 
   5702             const body = block_scope.instructionsSlice();
   5703             const body_len = astgen.countBodyLenAfterFixups(body);
   5704 
   5705             try gz.setEnum(decl_inst, .{
   5706                 .src_node = node,
   5707                 .nonexhaustive = nonexhaustive,
   5708                 .tag_type = arg_inst,
   5709                 .captures_len = @intCast(namespace.captures.count()),
   5710                 .body_len = body_len,
   5711                 .fields_len = @intCast(counts.total_fields),
   5712                 .decls_len = @intCast(counts.decls),
   5713                 .fields_hash = fields_hash,
   5714                 .name_strat = name_strat,
   5715             });
   5716 
   5717             wip_members.finishBits(bits_per_field);
   5718             const decls_slice = wip_members.declsSlice();
   5719             const fields_slice = wip_members.fieldsSlice();
   5720             try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() * 2 + decls_slice.len + body_len + fields_slice.len);
   5721             astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys()));
   5722             astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.values()));
   5723             astgen.extra.appendSliceAssumeCapacity(decls_slice);
   5724             astgen.appendBodyWithFixups(body);
   5725             astgen.extra.appendSliceAssumeCapacity(fields_slice);
   5726 
   5727             block_scope.unstack();
   5728             return rvalue(gz, ri, decl_inst.toRef(), node);
   5729         },
   5730         .keyword_opaque => {
   5731             assert(container_decl.ast.arg == .none);
   5732 
   5733             const decl_inst = try gz.reserveInstructionIndex();
   5734 
   5735             var namespace: Scope.Namespace = .{
   5736                 .parent = scope,
   5737                 .node = node,
   5738                 .inst = decl_inst,
   5739                 .declaring_gz = gz,
   5740                 .maybe_generic = astgen.within_fn,
   5741             };
   5742             defer namespace.deinit(gpa);
   5743 
   5744             astgen.advanceSourceCursorToNode(node);
   5745             var block_scope: GenZir = .{
   5746                 .parent = &namespace.base,
   5747                 .decl_node_index = node,
   5748                 .decl_line = gz.decl_line,
   5749                 .astgen = astgen,
   5750                 .is_comptime = true,
   5751                 .instructions = gz.instructions,
   5752                 .instructions_top = gz.instructions.items.len,
   5753             };
   5754             defer block_scope.unstack();
   5755 
   5756             const decl_count = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"opaque");
   5757 
   5758             var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, 0, 0, 0);
   5759             defer wip_members.deinit();
   5760 
   5761             if (container_decl.layout_token) |layout_token| {
   5762                 return astgen.failTok(layout_token, "opaque types do not support 'packed' or 'extern'", .{});
   5763             }
   5764 
   5765             for (container_decl.ast.members) |member_node| {
   5766                 const res = try containerMember(&block_scope, &namespace.base, &wip_members, member_node);
   5767                 if (res == .field) {
   5768                     return astgen.failNode(member_node, "opaque types cannot have fields", .{});
   5769                 }
   5770             }
   5771 
   5772             try gz.setOpaque(decl_inst, .{
   5773                 .src_node = node,
   5774                 .captures_len = @intCast(namespace.captures.count()),
   5775                 .decls_len = decl_count,
   5776                 .name_strat = name_strat,
   5777             });
   5778 
   5779             wip_members.finishBits(0);
   5780             const decls_slice = wip_members.declsSlice();
   5781             try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() * 2 + decls_slice.len);
   5782             astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys()));
   5783             astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.values()));
   5784             astgen.extra.appendSliceAssumeCapacity(decls_slice);
   5785 
   5786             block_scope.unstack();
   5787             return rvalue(gz, ri, decl_inst.toRef(), node);
   5788         },
   5789         else => unreachable,
   5790     }
   5791 }
   5792 
   5793 const ContainerMemberResult = union(enum) { decl, field: Ast.full.ContainerField };
   5794 
   5795 fn containerMember(
   5796     gz: *GenZir,
   5797     scope: *Scope,
   5798     wip_members: *WipMembers,
   5799     member_node: Ast.Node.Index,
   5800 ) InnerError!ContainerMemberResult {
   5801     const astgen = gz.astgen;
   5802     const tree = astgen.tree;
   5803     switch (tree.nodeTag(member_node)) {
   5804         .container_field_init,
   5805         .container_field_align,
   5806         .container_field,
   5807         => return ContainerMemberResult{ .field = tree.fullContainerField(member_node).? },
   5808 
   5809         .fn_proto,
   5810         .fn_proto_multi,
   5811         .fn_proto_one,
   5812         .fn_proto_simple,
   5813         .fn_decl,
   5814         => {
   5815             var buf: [1]Ast.Node.Index = undefined;
   5816             const full = tree.fullFnProto(&buf, member_node).?;
   5817 
   5818             const body: Ast.Node.OptionalIndex = if (tree.nodeTag(member_node) == .fn_decl)
   5819                 tree.nodeData(member_node).node_and_node[1].toOptional()
   5820             else
   5821                 .none;
   5822 
   5823             const prev_decl_index = wip_members.decl_index;
   5824             astgen.fnDecl(gz, scope, wip_members, member_node, body, full) catch |err| switch (err) {
   5825                 error.OutOfMemory => return error.OutOfMemory,
   5826                 error.AnalysisFail => {
   5827                     wip_members.decl_index = prev_decl_index;
   5828                     try addFailedDeclaration(
   5829                         wip_members,
   5830                         gz,
   5831                         .@"const",
   5832                         try astgen.identAsString(full.name_token.?),
   5833                         full.ast.proto_node,
   5834                         full.visib_token != null,
   5835                     );
   5836                 },
   5837             };
   5838         },
   5839 
   5840         .global_var_decl,
   5841         .local_var_decl,
   5842         .simple_var_decl,
   5843         .aligned_var_decl,
   5844         => {
   5845             const full = tree.fullVarDecl(member_node).?;
   5846             const prev_decl_index = wip_members.decl_index;
   5847             astgen.globalVarDecl(gz, scope, wip_members, member_node, full) catch |err| switch (err) {
   5848                 error.OutOfMemory => return error.OutOfMemory,
   5849                 error.AnalysisFail => {
   5850                     wip_members.decl_index = prev_decl_index;
   5851                     try addFailedDeclaration(
   5852                         wip_members,
   5853                         gz,
   5854                         .@"const", // doesn't really matter
   5855                         try astgen.identAsString(full.ast.mut_token + 1),
   5856                         member_node,
   5857                         full.visib_token != null,
   5858                     );
   5859                 },
   5860             };
   5861         },
   5862 
   5863         .@"comptime" => {
   5864             const prev_decl_index = wip_members.decl_index;
   5865             astgen.comptimeDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
   5866                 error.OutOfMemory => return error.OutOfMemory,
   5867                 error.AnalysisFail => {
   5868                     wip_members.decl_index = prev_decl_index;
   5869                     try addFailedDeclaration(
   5870                         wip_members,
   5871                         gz,
   5872                         .@"comptime",
   5873                         .empty,
   5874                         member_node,
   5875                         false,
   5876                     );
   5877                 },
   5878             };
   5879         },
   5880         .test_decl => {
   5881             const prev_decl_index = wip_members.decl_index;
   5882             // We need to have *some* decl here so that the decl count matches what's expected.
   5883             // Since it doesn't strictly matter *what* this is, let's save ourselves the trouble
   5884             // of duplicating the test name logic, and just assume this is an unnamed test.
   5885             astgen.testDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
   5886                 error.OutOfMemory => return error.OutOfMemory,
   5887                 error.AnalysisFail => {
   5888                     wip_members.decl_index = prev_decl_index;
   5889                     try addFailedDeclaration(
   5890                         wip_members,
   5891                         gz,
   5892                         .unnamed_test,
   5893                         .empty,
   5894                         member_node,
   5895                         false,
   5896                     );
   5897                 },
   5898             };
   5899         },
   5900         else => unreachable,
   5901     }
   5902     return .decl;
   5903 }
   5904 
   5905 fn errorSetDecl(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   5906     const astgen = gz.astgen;
   5907     const gpa = astgen.gpa;
   5908     const tree = astgen.tree;
   5909 
   5910     const payload_index = try reserveExtra(astgen, @typeInfo(Zir.Inst.ErrorSetDecl).@"struct".fields.len);
   5911     var fields_len: usize = 0;
   5912     {
   5913         var idents: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.TokenIndex) = .empty;
   5914         defer idents.deinit(gpa);
   5915 
   5916         const lbrace, const rbrace = tree.nodeData(node).token_and_token;
   5917         for (lbrace + 1..rbrace) |i| {
   5918             const tok_i: Ast.TokenIndex = @intCast(i);
   5919             switch (tree.tokenTag(tok_i)) {
   5920                 .doc_comment, .comma => {},
   5921                 .identifier => {
   5922                     const str_index = try astgen.identAsString(tok_i);
   5923                     const gop = try idents.getOrPut(gpa, str_index);
   5924                     if (gop.found_existing) {
   5925                         const name = try gpa.dupe(u8, mem.span(astgen.nullTerminatedString(str_index)));
   5926                         defer gpa.free(name);
   5927                         return astgen.failTokNotes(
   5928                             tok_i,
   5929                             "duplicate error set field '{s}'",
   5930                             .{name},
   5931                             &[_]u32{
   5932                                 try astgen.errNoteTok(
   5933                                     gop.value_ptr.*,
   5934                                     "previous declaration here",
   5935                                     .{},
   5936                                 ),
   5937                             },
   5938                         );
   5939                     }
   5940                     gop.value_ptr.* = tok_i;
   5941 
   5942                     try astgen.extra.append(gpa, @intFromEnum(str_index));
   5943                     fields_len += 1;
   5944                 },
   5945                 else => unreachable,
   5946             }
   5947         }
   5948     }
   5949 
   5950     setExtra(astgen, payload_index, Zir.Inst.ErrorSetDecl{
   5951         .fields_len = @intCast(fields_len),
   5952     });
   5953     const result = try gz.addPlNodePayloadIndex(.error_set_decl, node, payload_index);
   5954     return rvalue(gz, ri, result, node);
   5955 }
   5956 
   5957 fn tryExpr(
   5958     parent_gz: *GenZir,
   5959     scope: *Scope,
   5960     ri: ResultInfo,
   5961     node: Ast.Node.Index,
   5962     operand_node: Ast.Node.Index,
   5963 ) InnerError!Zir.Inst.Ref {
   5964     const astgen = parent_gz.astgen;
   5965 
   5966     const fn_block = astgen.fn_block orelse {
   5967         return astgen.failNode(node, "'try' outside function scope", .{});
   5968     };
   5969 
   5970     if (parent_gz.any_defer_node.unwrap()) |any_defer_node| {
   5971         return astgen.failNodeNotes(node, "'try' not allowed inside defer expression", .{}, &.{
   5972             try astgen.errNoteNode(
   5973                 any_defer_node,
   5974                 "defer expression here",
   5975                 .{},
   5976             ),
   5977         });
   5978     }
   5979 
   5980     // Ensure debug line/column information is emitted for this try expression.
   5981     // Then we will save the line/column so that we can emit another one that goes
   5982     // "backwards" because we want to evaluate the operand, but then put the debug
   5983     // info back at the try keyword for error return tracing.
   5984     if (!parent_gz.is_comptime) {
   5985         try emitDbgNode(parent_gz, node);
   5986     }
   5987     const try_lc: LineColumn = .{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
   5988 
   5989     const operand_rl: ResultInfo.Loc, const block_tag: Zir.Inst.Tag = switch (ri.rl) {
   5990         .ref, .ref_coerced_ty => .{ .ref, .try_ptr },
   5991         else => .{ .none, .@"try" },
   5992     };
   5993     const operand_ri: ResultInfo = .{ .rl = operand_rl, .ctx = .error_handling_expr };
   5994     const operand = operand: {
   5995         // As a special case, we need to detect this form:
   5996         // `try .foo(...)`
   5997         // This is a decl literal form, even though we don't propagate a result type through `try`.
   5998         var buf: [1]Ast.Node.Index = undefined;
   5999         if (astgen.tree.fullCall(&buf, operand_node)) |full_call| {
   6000             const res_ty: Zir.Inst.Ref = try ri.rl.resultType(parent_gz, operand_node) orelse .none;
   6001             break :operand try callExpr(parent_gz, scope, operand_ri, res_ty, operand_node, full_call);
   6002         }
   6003 
   6004         // This could be a pointer or value depending on the `ri` parameter.
   6005         break :operand try reachableExpr(parent_gz, scope, operand_ri, operand_node, node);
   6006     };
   6007 
   6008     const try_inst = try parent_gz.makeBlockInst(block_tag, node);
   6009     try parent_gz.instructions.append(astgen.gpa, try_inst);
   6010 
   6011     var else_scope = parent_gz.makeSubBlock(scope);
   6012     defer else_scope.unstack();
   6013 
   6014     const err_tag = switch (ri.rl) {
   6015         .ref, .ref_coerced_ty => Zir.Inst.Tag.err_union_code_ptr,
   6016         else => Zir.Inst.Tag.err_union_code,
   6017     };
   6018     const err_code = try else_scope.addUnNode(err_tag, operand, node);
   6019     try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code });
   6020     try emitDbgStmt(&else_scope, try_lc);
   6021     _ = try else_scope.addUnNode(.ret_node, err_code, node);
   6022 
   6023     try else_scope.setTryBody(try_inst, operand);
   6024     const result = try_inst.toRef();
   6025     switch (ri.rl) {
   6026         .ref, .ref_coerced_ty => return result,
   6027         else => return rvalue(parent_gz, ri, result, node),
   6028     }
   6029 }
   6030 
   6031 fn orelseCatchExpr(
   6032     parent_gz: *GenZir,
   6033     scope: *Scope,
   6034     ri: ResultInfo,
   6035     node: Ast.Node.Index,
   6036     cond_op: Zir.Inst.Tag,
   6037     unwrap_op: Zir.Inst.Tag,
   6038     unwrap_code_op: Zir.Inst.Tag,
   6039     payload_token: ?Ast.TokenIndex,
   6040 ) InnerError!Zir.Inst.Ref {
   6041     const astgen = parent_gz.astgen;
   6042     const tree = astgen.tree;
   6043 
   6044     const lhs, const rhs = tree.nodeData(node).node_and_node;
   6045 
   6046     const need_rl = astgen.nodes_need_rl.contains(node);
   6047     const block_ri: ResultInfo = if (need_rl) ri else .{
   6048         .rl = switch (ri.rl) {
   6049             .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
   6050             .inferred_ptr => .none,
   6051             else => ri.rl,
   6052         },
   6053         .ctx = ri.ctx,
   6054     };
   6055     // We need to call `rvalue` to write through to the pointer only if we had a
   6056     // result pointer and aren't forwarding it.
   6057     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   6058     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   6059 
   6060     const do_err_trace = astgen.fn_block != null and (cond_op == .is_non_err or cond_op == .is_non_err_ptr);
   6061 
   6062     var block_scope = parent_gz.makeSubBlock(scope);
   6063     block_scope.setBreakResultInfo(block_ri);
   6064     defer block_scope.unstack();
   6065 
   6066     const operand_ri: ResultInfo = switch (block_scope.break_result_info.rl) {
   6067         .ref, .ref_coerced_ty => .{ .rl = .ref, .ctx = if (do_err_trace) .error_handling_expr else .none },
   6068         else => .{ .rl = .none, .ctx = if (do_err_trace) .error_handling_expr else .none },
   6069     };
   6070     // This could be a pointer or value depending on the `operand_ri` parameter.
   6071     // We cannot use `block_scope.break_result_info` because that has the bare
   6072     // type, whereas this expression has the optional type. Later we make
   6073     // up for this fact by calling rvalue on the else branch.
   6074     const operand = try reachableExpr(&block_scope, &block_scope.base, operand_ri, lhs, rhs);
   6075     const cond = try block_scope.addUnNode(cond_op, operand, node);
   6076     const condbr = try block_scope.addCondBr(.condbr, node);
   6077 
   6078     const block = try parent_gz.makeBlockInst(.block, node);
   6079     try block_scope.setBlockBody(block);
   6080     // block_scope unstacked now, can add new instructions to parent_gz
   6081     try parent_gz.instructions.append(astgen.gpa, block);
   6082 
   6083     var then_scope = block_scope.makeSubBlock(scope);
   6084     defer then_scope.unstack();
   6085 
   6086     // This could be a pointer or value depending on `unwrap_op`.
   6087     const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node);
   6088     const then_result = switch (ri.rl) {
   6089         .ref, .ref_coerced_ty => unwrapped_payload,
   6090         else => try rvalue(&then_scope, block_scope.break_result_info, unwrapped_payload, node),
   6091     };
   6092     _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, node);
   6093 
   6094     var else_scope = block_scope.makeSubBlock(scope);
   6095     defer else_scope.unstack();
   6096 
   6097     // We know that the operand (almost certainly) modified the error return trace,
   6098     // so signal to Sema that it should save the new index for restoring later.
   6099     if (do_err_trace and nodeMayAppendToErrorTrace(tree, lhs))
   6100         _ = try else_scope.addSaveErrRetIndex(.always);
   6101 
   6102     var err_val_scope: Scope.LocalVal = undefined;
   6103     const else_sub_scope = blk: {
   6104         const payload = payload_token orelse break :blk &else_scope.base;
   6105         const err_str = tree.tokenSlice(payload);
   6106         if (mem.eql(u8, err_str, "_")) {
   6107             try astgen.appendErrorTok(payload, "discard of error capture; omit it instead", .{});
   6108             break :blk &else_scope.base;
   6109         }
   6110         const err_name = try astgen.identAsString(payload);
   6111 
   6112         try astgen.detectLocalShadowing(scope, err_name, payload, err_str, .capture);
   6113 
   6114         err_val_scope = .{
   6115             .parent = &else_scope.base,
   6116             .gen_zir = &else_scope,
   6117             .name = err_name,
   6118             .inst = try else_scope.addUnNode(unwrap_code_op, operand, node),
   6119             .token_src = payload,
   6120             .id_cat = .capture,
   6121         };
   6122         break :blk &err_val_scope.base;
   6123     };
   6124 
   6125     const else_result = try fullBodyExpr(&else_scope, else_sub_scope, block_scope.break_result_info, rhs, .allow_branch_hint);
   6126     if (!else_scope.endsWithNoReturn()) {
   6127         // As our last action before the break, "pop" the error trace if needed
   6128         if (do_err_trace)
   6129             try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, rhs, else_result);
   6130 
   6131         _ = try else_scope.addBreakWithSrcNode(.@"break", block, else_result, rhs);
   6132     }
   6133     try checkUsed(parent_gz, &else_scope.base, else_sub_scope);
   6134 
   6135     try setCondBrPayload(condbr, cond, &then_scope, &else_scope);
   6136 
   6137     if (need_result_rvalue) {
   6138         return rvalue(parent_gz, ri, block.toRef(), node);
   6139     } else {
   6140         return block.toRef();
   6141     }
   6142 }
   6143 
   6144 /// Return whether the identifier names of two tokens are equal. Resolves @""
   6145 /// tokens without allocating.
   6146 /// OK in theory it could do it without allocating. This implementation
   6147 /// allocates when the @"" form is used.
   6148 fn tokenIdentEql(astgen: *AstGen, token1: Ast.TokenIndex, token2: Ast.TokenIndex) !bool {
   6149     const ident_name_1 = try astgen.identifierTokenString(token1);
   6150     const ident_name_2 = try astgen.identifierTokenString(token2);
   6151     return mem.eql(u8, ident_name_1, ident_name_2);
   6152 }
   6153 
   6154 fn fieldAccess(
   6155     gz: *GenZir,
   6156     scope: *Scope,
   6157     ri: ResultInfo,
   6158     node: Ast.Node.Index,
   6159 ) InnerError!Zir.Inst.Ref {
   6160     switch (ri.rl) {
   6161         .ref, .ref_coerced_ty => return addFieldAccess(.field_ptr, gz, scope, .{ .rl = .ref }, node),
   6162         else => {
   6163             const access = try addFieldAccess(.field_val, gz, scope, .{ .rl = .none }, node);
   6164             return rvalue(gz, ri, access, node);
   6165         },
   6166     }
   6167 }
   6168 
   6169 fn addFieldAccess(
   6170     tag: Zir.Inst.Tag,
   6171     gz: *GenZir,
   6172     scope: *Scope,
   6173     lhs_ri: ResultInfo,
   6174     node: Ast.Node.Index,
   6175 ) InnerError!Zir.Inst.Ref {
   6176     const astgen = gz.astgen;
   6177     const tree = astgen.tree;
   6178 
   6179     const object_node, const field_ident = tree.nodeData(node).node_and_token;
   6180     const str_index = try astgen.identAsString(field_ident);
   6181     const lhs = try expr(gz, scope, lhs_ri, object_node);
   6182 
   6183     const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   6184     try emitDbgStmt(gz, cursor);
   6185 
   6186     return gz.addPlNode(tag, node, Zir.Inst.Field{
   6187         .lhs = lhs,
   6188         .field_name_start = str_index,
   6189     });
   6190 }
   6191 
   6192 fn arrayAccess(
   6193     gz: *GenZir,
   6194     scope: *Scope,
   6195     ri: ResultInfo,
   6196     node: Ast.Node.Index,
   6197 ) InnerError!Zir.Inst.Ref {
   6198     const tree = gz.astgen.tree;
   6199     switch (ri.rl) {
   6200         .ref, .ref_coerced_ty => {
   6201             const lhs_node, const rhs_node = tree.nodeData(node).node_and_node;
   6202             const lhs = try expr(gz, scope, .{ .rl = .ref }, lhs_node);
   6203 
   6204             const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   6205 
   6206             const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, rhs_node);
   6207             try emitDbgStmt(gz, cursor);
   6208 
   6209             return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
   6210         },
   6211         else => {
   6212             const lhs_node, const rhs_node = tree.nodeData(node).node_and_node;
   6213             const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node);
   6214 
   6215             const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   6216 
   6217             const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, rhs_node);
   6218             try emitDbgStmt(gz, cursor);
   6219 
   6220             return rvalue(gz, ri, try gz.addPlNode(.elem_val_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }), node);
   6221         },
   6222     }
   6223 }
   6224 
   6225 fn simpleBinOp(
   6226     gz: *GenZir,
   6227     scope: *Scope,
   6228     ri: ResultInfo,
   6229     node: Ast.Node.Index,
   6230     op_inst_tag: Zir.Inst.Tag,
   6231 ) InnerError!Zir.Inst.Ref {
   6232     const astgen = gz.astgen;
   6233     const tree = astgen.tree;
   6234 
   6235     const lhs_node, const rhs_node = tree.nodeData(node).node_and_node;
   6236 
   6237     if (op_inst_tag == .cmp_neq or op_inst_tag == .cmp_eq) {
   6238         const str = if (op_inst_tag == .cmp_eq) "==" else "!=";
   6239         if (tree.nodeTag(lhs_node) == .string_literal or
   6240             tree.nodeTag(rhs_node) == .string_literal)
   6241             return astgen.failNode(node, "cannot compare strings with {s}", .{str});
   6242     }
   6243 
   6244     const lhs = try reachableExpr(gz, scope, .{ .rl = .none }, lhs_node, node);
   6245     const cursor = switch (op_inst_tag) {
   6246         .add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, node),
   6247         else => undefined,
   6248     };
   6249     const rhs = try reachableExpr(gz, scope, .{ .rl = .none }, rhs_node, node);
   6250 
   6251     switch (op_inst_tag) {
   6252         .add, .sub, .mul, .div, .mod_rem => {
   6253             try emitDbgStmt(gz, cursor);
   6254         },
   6255         else => {},
   6256     }
   6257     const result = try gz.addPlNode(op_inst_tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
   6258     return rvalue(gz, ri, result, node);
   6259 }
   6260 
   6261 fn simpleStrTok(
   6262     gz: *GenZir,
   6263     ri: ResultInfo,
   6264     ident_token: Ast.TokenIndex,
   6265     node: Ast.Node.Index,
   6266     op_inst_tag: Zir.Inst.Tag,
   6267 ) InnerError!Zir.Inst.Ref {
   6268     const astgen = gz.astgen;
   6269     const str_index = try astgen.identAsString(ident_token);
   6270     const result = try gz.addStrTok(op_inst_tag, str_index, ident_token);
   6271     return rvalue(gz, ri, result, node);
   6272 }
   6273 
   6274 fn boolBinOp(
   6275     gz: *GenZir,
   6276     scope: *Scope,
   6277     ri: ResultInfo,
   6278     node: Ast.Node.Index,
   6279     zir_tag: Zir.Inst.Tag,
   6280 ) InnerError!Zir.Inst.Ref {
   6281     const astgen = gz.astgen;
   6282     const tree = astgen.tree;
   6283 
   6284     const lhs_node, const rhs_node = tree.nodeData(node).node_and_node;
   6285     const lhs = try expr(gz, scope, coerced_bool_ri, lhs_node);
   6286     const bool_br = (try gz.addPlNodePayloadIndex(zir_tag, node, undefined)).toIndex().?;
   6287 
   6288     var rhs_scope = gz.makeSubBlock(scope);
   6289     defer rhs_scope.unstack();
   6290     const rhs = try fullBodyExpr(&rhs_scope, &rhs_scope.base, coerced_bool_ri, rhs_node, .allow_branch_hint);
   6291     if (!gz.refIsNoReturn(rhs)) {
   6292         _ = try rhs_scope.addBreakWithSrcNode(.break_inline, bool_br, rhs, rhs_node);
   6293     }
   6294     try rhs_scope.setBoolBrBody(bool_br, lhs);
   6295 
   6296     const block_ref = bool_br.toRef();
   6297     return rvalue(gz, ri, block_ref, node);
   6298 }
   6299 
   6300 fn ifExpr(
   6301     parent_gz: *GenZir,
   6302     scope: *Scope,
   6303     ri: ResultInfo,
   6304     node: Ast.Node.Index,
   6305     if_full: Ast.full.If,
   6306 ) InnerError!Zir.Inst.Ref {
   6307     const astgen = parent_gz.astgen;
   6308     const tree = astgen.tree;
   6309 
   6310     const do_err_trace = astgen.fn_block != null and if_full.error_token != null;
   6311 
   6312     const need_rl = astgen.nodes_need_rl.contains(node);
   6313     const block_ri: ResultInfo = if (need_rl) ri else .{
   6314         .rl = switch (ri.rl) {
   6315             .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
   6316             .inferred_ptr => .none,
   6317             else => ri.rl,
   6318         },
   6319         .ctx = ri.ctx,
   6320     };
   6321     // We need to call `rvalue` to write through to the pointer only if we had a
   6322     // result pointer and aren't forwarding it.
   6323     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   6324     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   6325 
   6326     var block_scope = parent_gz.makeSubBlock(scope);
   6327     block_scope.setBreakResultInfo(block_ri);
   6328     defer block_scope.unstack();
   6329 
   6330     const payload_is_ref = if (if_full.payload_token) |payload_token|
   6331         tree.tokenTag(payload_token) == .asterisk
   6332     else
   6333         false;
   6334 
   6335     try emitDbgNode(parent_gz, if_full.ast.cond_expr);
   6336     const cond: struct {
   6337         inst: Zir.Inst.Ref,
   6338         bool_bit: Zir.Inst.Ref,
   6339     } = c: {
   6340         if (if_full.error_token) |_| {
   6341             const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none, .ctx = .error_handling_expr };
   6342             const err_union = try expr(&block_scope, &block_scope.base, cond_ri, if_full.ast.cond_expr);
   6343             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
   6344             break :c .{
   6345                 .inst = err_union,
   6346                 .bool_bit = try block_scope.addUnNode(tag, err_union, if_full.ast.cond_expr),
   6347             };
   6348         } else if (if_full.payload_token) |_| {
   6349             const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
   6350             const optional = try expr(&block_scope, &block_scope.base, cond_ri, if_full.ast.cond_expr);
   6351             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
   6352             break :c .{
   6353                 .inst = optional,
   6354                 .bool_bit = try block_scope.addUnNode(tag, optional, if_full.ast.cond_expr),
   6355             };
   6356         } else {
   6357             const cond = try expr(&block_scope, &block_scope.base, coerced_bool_ri, if_full.ast.cond_expr);
   6358             break :c .{
   6359                 .inst = cond,
   6360                 .bool_bit = cond,
   6361             };
   6362         }
   6363     };
   6364 
   6365     const condbr = try block_scope.addCondBr(.condbr, node);
   6366 
   6367     const block = try parent_gz.makeBlockInst(.block, node);
   6368     try block_scope.setBlockBody(block);
   6369     // block_scope unstacked now, can add new instructions to parent_gz
   6370     try parent_gz.instructions.append(astgen.gpa, block);
   6371 
   6372     var then_scope = parent_gz.makeSubBlock(scope);
   6373     defer then_scope.unstack();
   6374 
   6375     var payload_val_scope: Scope.LocalVal = undefined;
   6376 
   6377     const then_node = if_full.ast.then_expr;
   6378     const then_sub_scope = s: {
   6379         if (if_full.error_token != null) {
   6380             if (if_full.payload_token) |payload_token| {
   6381                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   6382                     .err_union_payload_unsafe_ptr
   6383                 else
   6384                     .err_union_payload_unsafe;
   6385                 const payload_inst = try then_scope.addUnNode(tag, cond.inst, then_node);
   6386                 const token_name_index = payload_token + @intFromBool(payload_is_ref);
   6387                 const ident_name = try astgen.identAsString(token_name_index);
   6388                 const token_name_str = tree.tokenSlice(token_name_index);
   6389                 if (mem.eql(u8, "_", token_name_str)) {
   6390                     if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   6391                     break :s &then_scope.base;
   6392                 }
   6393                 try astgen.detectLocalShadowing(&then_scope.base, ident_name, token_name_index, token_name_str, .capture);
   6394                 payload_val_scope = .{
   6395                     .parent = &then_scope.base,
   6396                     .gen_zir = &then_scope,
   6397                     .name = ident_name,
   6398                     .inst = payload_inst,
   6399                     .token_src = token_name_index,
   6400                     .id_cat = .capture,
   6401                 };
   6402                 try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
   6403                 break :s &payload_val_scope.base;
   6404             } else {
   6405                 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node);
   6406                 break :s &then_scope.base;
   6407             }
   6408         } else if (if_full.payload_token) |payload_token| {
   6409             const ident_token = payload_token + @intFromBool(payload_is_ref);
   6410             const tag: Zir.Inst.Tag = if (payload_is_ref)
   6411                 .optional_payload_unsafe_ptr
   6412             else
   6413                 .optional_payload_unsafe;
   6414             const ident_bytes = tree.tokenSlice(ident_token);
   6415             if (mem.eql(u8, "_", ident_bytes)) {
   6416                 if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   6417                 break :s &then_scope.base;
   6418             }
   6419             const payload_inst = try then_scope.addUnNode(tag, cond.inst, then_node);
   6420             const ident_name = try astgen.identAsString(ident_token);
   6421             try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture);
   6422             payload_val_scope = .{
   6423                 .parent = &then_scope.base,
   6424                 .gen_zir = &then_scope,
   6425                 .name = ident_name,
   6426                 .inst = payload_inst,
   6427                 .token_src = ident_token,
   6428                 .id_cat = .capture,
   6429             };
   6430             try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
   6431             break :s &payload_val_scope.base;
   6432         } else {
   6433             break :s &then_scope.base;
   6434         }
   6435     };
   6436 
   6437     const then_result = try fullBodyExpr(&then_scope, then_sub_scope, block_scope.break_result_info, then_node, .allow_branch_hint);
   6438     try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
   6439     if (!then_scope.endsWithNoReturn()) {
   6440         _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, then_node);
   6441     }
   6442 
   6443     var else_scope = parent_gz.makeSubBlock(scope);
   6444     defer else_scope.unstack();
   6445 
   6446     // We know that the operand (almost certainly) modified the error return trace,
   6447     // so signal to Sema that it should save the new index for restoring later.
   6448     if (do_err_trace and nodeMayAppendToErrorTrace(tree, if_full.ast.cond_expr))
   6449         _ = try else_scope.addSaveErrRetIndex(.always);
   6450 
   6451     if (if_full.ast.else_expr.unwrap()) |else_node| {
   6452         const sub_scope = s: {
   6453             if (if_full.error_token) |error_token| {
   6454                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   6455                     .err_union_code_ptr
   6456                 else
   6457                     .err_union_code;
   6458                 const payload_inst = try else_scope.addUnNode(tag, cond.inst, if_full.ast.cond_expr);
   6459                 const ident_name = try astgen.identAsString(error_token);
   6460                 const error_token_str = tree.tokenSlice(error_token);
   6461                 if (mem.eql(u8, "_", error_token_str))
   6462                     break :s &else_scope.base;
   6463                 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, error_token_str, .capture);
   6464                 payload_val_scope = .{
   6465                     .parent = &else_scope.base,
   6466                     .gen_zir = &else_scope,
   6467                     .name = ident_name,
   6468                     .inst = payload_inst,
   6469                     .token_src = error_token,
   6470                     .id_cat = .capture,
   6471                 };
   6472                 try else_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
   6473                 break :s &payload_val_scope.base;
   6474             } else {
   6475                 break :s &else_scope.base;
   6476             }
   6477         };
   6478         const else_result = try fullBodyExpr(&else_scope, sub_scope, block_scope.break_result_info, else_node, .allow_branch_hint);
   6479         if (!else_scope.endsWithNoReturn()) {
   6480             // As our last action before the break, "pop" the error trace if needed
   6481             if (do_err_trace)
   6482                 try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, else_node, else_result);
   6483             _ = try else_scope.addBreakWithSrcNode(.@"break", block, else_result, else_node);
   6484         }
   6485         try checkUsed(parent_gz, &else_scope.base, sub_scope);
   6486     } else {
   6487         const result = try rvalue(&else_scope, ri, .void_value, node);
   6488         _ = try else_scope.addBreak(.@"break", block, result);
   6489     }
   6490 
   6491     try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope);
   6492 
   6493     if (need_result_rvalue) {
   6494         return rvalue(parent_gz, ri, block.toRef(), node);
   6495     } else {
   6496         return block.toRef();
   6497     }
   6498 }
   6499 
   6500 /// Supports `else_scope` stacked on `then_scope`. Unstacks `else_scope` then `then_scope`.
   6501 fn setCondBrPayload(
   6502     condbr: Zir.Inst.Index,
   6503     cond: Zir.Inst.Ref,
   6504     then_scope: *GenZir,
   6505     else_scope: *GenZir,
   6506 ) !void {
   6507     defer then_scope.unstack();
   6508     defer else_scope.unstack();
   6509     const astgen = then_scope.astgen;
   6510     const then_body = then_scope.instructionsSliceUpto(else_scope);
   6511     const else_body = else_scope.instructionsSlice();
   6512     const then_body_len = astgen.countBodyLenAfterFixups(then_body);
   6513     const else_body_len = astgen.countBodyLenAfterFixups(else_body);
   6514     try astgen.extra.ensureUnusedCapacity(
   6515         astgen.gpa,
   6516         @typeInfo(Zir.Inst.CondBr).@"struct".fields.len + then_body_len + else_body_len,
   6517     );
   6518 
   6519     const zir_datas = astgen.instructions.items(.data);
   6520     zir_datas[@intFromEnum(condbr)].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.CondBr{
   6521         .condition = cond,
   6522         .then_body_len = then_body_len,
   6523         .else_body_len = else_body_len,
   6524     });
   6525     astgen.appendBodyWithFixups(then_body);
   6526     astgen.appendBodyWithFixups(else_body);
   6527 }
   6528 
   6529 fn whileExpr(
   6530     parent_gz: *GenZir,
   6531     scope: *Scope,
   6532     ri: ResultInfo,
   6533     node: Ast.Node.Index,
   6534     while_full: Ast.full.While,
   6535     is_statement: bool,
   6536 ) InnerError!Zir.Inst.Ref {
   6537     const astgen = parent_gz.astgen;
   6538     const tree = astgen.tree;
   6539 
   6540     const need_rl = astgen.nodes_need_rl.contains(node);
   6541     const block_ri: ResultInfo = if (need_rl) ri else .{
   6542         .rl = switch (ri.rl) {
   6543             .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
   6544             .inferred_ptr => .none,
   6545             else => ri.rl,
   6546         },
   6547         .ctx = ri.ctx,
   6548     };
   6549     // We need to call `rvalue` to write through to the pointer only if we had a
   6550     // result pointer and aren't forwarding it.
   6551     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   6552     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   6553 
   6554     if (while_full.label_token) |label_token| {
   6555         try astgen.checkLabelRedefinition(scope, label_token);
   6556     }
   6557 
   6558     const is_inline = while_full.inline_token != null;
   6559     if (parent_gz.is_comptime and is_inline) {
   6560         try astgen.appendErrorTok(while_full.inline_token.?, "redundant inline keyword in comptime scope", .{});
   6561     }
   6562     const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop;
   6563     const loop_block = try parent_gz.makeBlockInst(loop_tag, node);
   6564     try parent_gz.instructions.append(astgen.gpa, loop_block);
   6565 
   6566     var loop_scope = parent_gz.makeSubBlock(scope);
   6567     loop_scope.is_inline = is_inline;
   6568     loop_scope.setBreakResultInfo(block_ri);
   6569     defer loop_scope.unstack();
   6570 
   6571     var cond_scope = parent_gz.makeSubBlock(&loop_scope.base);
   6572     defer cond_scope.unstack();
   6573 
   6574     const payload_is_ref = if (while_full.payload_token) |payload_token|
   6575         tree.tokenTag(payload_token) == .asterisk
   6576     else
   6577         false;
   6578 
   6579     try emitDbgNode(parent_gz, while_full.ast.cond_expr);
   6580     const cond: struct {
   6581         inst: Zir.Inst.Ref,
   6582         bool_bit: Zir.Inst.Ref,
   6583     } = c: {
   6584         if (while_full.error_token) |_| {
   6585             const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
   6586             const err_union = try fullBodyExpr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr, .normal);
   6587             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
   6588             break :c .{
   6589                 .inst = err_union,
   6590                 .bool_bit = try cond_scope.addUnNode(tag, err_union, while_full.ast.cond_expr),
   6591             };
   6592         } else if (while_full.payload_token) |_| {
   6593             const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
   6594             const optional = try fullBodyExpr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr, .normal);
   6595             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
   6596             break :c .{
   6597                 .inst = optional,
   6598                 .bool_bit = try cond_scope.addUnNode(tag, optional, while_full.ast.cond_expr),
   6599             };
   6600         } else {
   6601             const cond = try fullBodyExpr(&cond_scope, &cond_scope.base, coerced_bool_ri, while_full.ast.cond_expr, .normal);
   6602             break :c .{
   6603                 .inst = cond,
   6604                 .bool_bit = cond,
   6605             };
   6606         }
   6607     };
   6608 
   6609     const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
   6610     const condbr = try cond_scope.addCondBr(condbr_tag, node);
   6611     const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block;
   6612     const cond_block = try loop_scope.makeBlockInst(block_tag, node);
   6613     try cond_scope.setBlockBody(cond_block);
   6614     // cond_scope unstacked now, can add new instructions to loop_scope
   6615     try loop_scope.instructions.append(astgen.gpa, cond_block);
   6616 
   6617     // make scope now but don't stack on parent_gz until loop_scope
   6618     // gets unstacked after cont_expr is emitted and added below
   6619     var then_scope = parent_gz.makeSubBlock(&cond_scope.base);
   6620     then_scope.instructions_top = GenZir.unstacked_top;
   6621     defer then_scope.unstack();
   6622 
   6623     var dbg_var_name: Zir.NullTerminatedString = .empty;
   6624     var dbg_var_inst: Zir.Inst.Ref = undefined;
   6625     var opt_payload_inst: Zir.Inst.OptionalIndex = .none;
   6626     var payload_val_scope: Scope.LocalVal = undefined;
   6627     const then_sub_scope = s: {
   6628         if (while_full.error_token != null) {
   6629             if (while_full.payload_token) |payload_token| {
   6630                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   6631                     .err_union_payload_unsafe_ptr
   6632                 else
   6633                     .err_union_payload_unsafe;
   6634                 // will add this instruction to then_scope.instructions below
   6635                 const payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr);
   6636                 opt_payload_inst = payload_inst.toOptional();
   6637                 const ident_token = payload_token + @intFromBool(payload_is_ref);
   6638                 const ident_bytes = tree.tokenSlice(ident_token);
   6639                 if (mem.eql(u8, "_", ident_bytes)) {
   6640                     if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   6641                     break :s &then_scope.base;
   6642                 }
   6643                 const ident_name = try astgen.identAsString(ident_token);
   6644                 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture);
   6645                 payload_val_scope = .{
   6646                     .parent = &then_scope.base,
   6647                     .gen_zir = &then_scope,
   6648                     .name = ident_name,
   6649                     .inst = payload_inst.toRef(),
   6650                     .token_src = ident_token,
   6651                     .id_cat = .capture,
   6652                 };
   6653                 dbg_var_name = ident_name;
   6654                 dbg_var_inst = payload_inst.toRef();
   6655                 break :s &payload_val_scope.base;
   6656             } else {
   6657                 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node);
   6658                 break :s &then_scope.base;
   6659             }
   6660         } else if (while_full.payload_token) |payload_token| {
   6661             const tag: Zir.Inst.Tag = if (payload_is_ref)
   6662                 .optional_payload_unsafe_ptr
   6663             else
   6664                 .optional_payload_unsafe;
   6665             // will add this instruction to then_scope.instructions below
   6666             const payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr);
   6667             opt_payload_inst = payload_inst.toOptional();
   6668             const ident_token = payload_token + @intFromBool(payload_is_ref);
   6669             const ident_name = try astgen.identAsString(ident_token);
   6670             const ident_bytes = tree.tokenSlice(ident_token);
   6671             if (mem.eql(u8, "_", ident_bytes)) {
   6672                 if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   6673                 break :s &then_scope.base;
   6674             }
   6675             try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture);
   6676             payload_val_scope = .{
   6677                 .parent = &then_scope.base,
   6678                 .gen_zir = &then_scope,
   6679                 .name = ident_name,
   6680                 .inst = payload_inst.toRef(),
   6681                 .token_src = ident_token,
   6682                 .id_cat = .capture,
   6683             };
   6684             dbg_var_name = ident_name;
   6685             dbg_var_inst = payload_inst.toRef();
   6686             break :s &payload_val_scope.base;
   6687         } else {
   6688             break :s &then_scope.base;
   6689         }
   6690     };
   6691 
   6692     var continue_scope = parent_gz.makeSubBlock(then_sub_scope);
   6693     continue_scope.instructions_top = GenZir.unstacked_top;
   6694     defer continue_scope.unstack();
   6695     const continue_block = try then_scope.makeBlockInst(block_tag, node);
   6696 
   6697     const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
   6698     _ = try loop_scope.addNode(repeat_tag, node);
   6699 
   6700     try loop_scope.setBlockBody(loop_block);
   6701     loop_scope.break_block = loop_block.toOptional();
   6702     loop_scope.continue_block = continue_block.toOptional();
   6703     if (while_full.label_token) |label_token| {
   6704         loop_scope.label = .{
   6705             .token = label_token,
   6706             .block_inst = loop_block,
   6707         };
   6708     }
   6709 
   6710     // done adding instructions to loop_scope, can now stack then_scope
   6711     then_scope.instructions_top = then_scope.instructions.items.len;
   6712 
   6713     const then_node = while_full.ast.then_expr;
   6714     if (opt_payload_inst.unwrap()) |payload_inst| {
   6715         try then_scope.instructions.append(astgen.gpa, payload_inst);
   6716     }
   6717     if (dbg_var_name != .empty) try then_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst);
   6718     try then_scope.instructions.append(astgen.gpa, continue_block);
   6719     // This code could be improved to avoid emitting the continue expr when there
   6720     // are no jumps to it. This happens when the last statement of a while body is noreturn
   6721     // and there are no `continue` statements.
   6722     // Tracking issue: https://github.com/ziglang/zig/issues/9185
   6723     if (while_full.ast.cont_expr.unwrap()) |cont_expr| {
   6724         _ = try unusedResultExpr(&then_scope, then_sub_scope, cont_expr);
   6725     }
   6726 
   6727     continue_scope.instructions_top = continue_scope.instructions.items.len;
   6728     {
   6729         try emitDbgNode(&continue_scope, then_node);
   6730         const unused_result = try fullBodyExpr(&continue_scope, &continue_scope.base, .{ .rl = .none }, then_node, .allow_branch_hint);
   6731         _ = try addEnsureResult(&continue_scope, unused_result, then_node);
   6732     }
   6733     try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
   6734     const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
   6735     if (!continue_scope.endsWithNoReturn()) {
   6736         astgen.advanceSourceCursor(tree.tokenStart(tree.lastToken(then_node)));
   6737         try emitDbgStmt(parent_gz, .{ astgen.source_line - parent_gz.decl_line, astgen.source_column });
   6738         _ = try parent_gz.add(.{
   6739             .tag = .extended,
   6740             .data = .{ .extended = .{
   6741                 .opcode = .dbg_empty_stmt,
   6742                 .small = undefined,
   6743                 .operand = undefined,
   6744             } },
   6745         });
   6746         _ = try continue_scope.addBreak(break_tag, continue_block, .void_value);
   6747     }
   6748     try continue_scope.setBlockBody(continue_block);
   6749     _ = try then_scope.addBreak(break_tag, cond_block, .void_value);
   6750 
   6751     var else_scope = parent_gz.makeSubBlock(&cond_scope.base);
   6752     defer else_scope.unstack();
   6753 
   6754     if (while_full.ast.else_expr.unwrap()) |else_node| {
   6755         const sub_scope = s: {
   6756             if (while_full.error_token) |error_token| {
   6757                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   6758                     .err_union_code_ptr
   6759                 else
   6760                     .err_union_code;
   6761                 const else_payload_inst = try else_scope.addUnNode(tag, cond.inst, while_full.ast.cond_expr);
   6762                 const ident_name = try astgen.identAsString(error_token);
   6763                 const ident_bytes = tree.tokenSlice(error_token);
   6764                 if (mem.eql(u8, ident_bytes, "_"))
   6765                     break :s &else_scope.base;
   6766                 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, ident_bytes, .capture);
   6767                 payload_val_scope = .{
   6768                     .parent = &else_scope.base,
   6769                     .gen_zir = &else_scope,
   6770                     .name = ident_name,
   6771                     .inst = else_payload_inst,
   6772                     .token_src = error_token,
   6773                     .id_cat = .capture,
   6774                 };
   6775                 try else_scope.addDbgVar(.dbg_var_val, ident_name, else_payload_inst);
   6776                 break :s &payload_val_scope.base;
   6777             } else {
   6778                 break :s &else_scope.base;
   6779             }
   6780         };
   6781         // Remove the continue block and break block so that `continue` and `break`
   6782         // control flow apply to outer loops; not this one.
   6783         loop_scope.continue_block = .none;
   6784         loop_scope.break_block = .none;
   6785         const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node, .allow_branch_hint);
   6786         if (is_statement) {
   6787             _ = try addEnsureResult(&else_scope, else_result, else_node);
   6788         }
   6789 
   6790         try checkUsed(parent_gz, &else_scope.base, sub_scope);
   6791         if (!else_scope.endsWithNoReturn()) {
   6792             _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node);
   6793         }
   6794     } else {
   6795         const result = try rvalue(&else_scope, ri, .void_value, node);
   6796         _ = try else_scope.addBreak(break_tag, loop_block, result);
   6797     }
   6798 
   6799     if (loop_scope.label) |some| {
   6800         if (!some.used) {
   6801             try astgen.appendErrorTok(some.token, "unused while loop label", .{});
   6802         }
   6803     }
   6804 
   6805     try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope);
   6806 
   6807     const result = if (need_result_rvalue)
   6808         try rvalue(parent_gz, ri, loop_block.toRef(), node)
   6809     else
   6810         loop_block.toRef();
   6811 
   6812     if (is_statement) {
   6813         _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
   6814     }
   6815 
   6816     return result;
   6817 }
   6818 
   6819 fn forExpr(
   6820     parent_gz: *GenZir,
   6821     scope: *Scope,
   6822     ri: ResultInfo,
   6823     node: Ast.Node.Index,
   6824     for_full: Ast.full.For,
   6825     is_statement: bool,
   6826 ) InnerError!Zir.Inst.Ref {
   6827     const astgen = parent_gz.astgen;
   6828 
   6829     if (for_full.label_token) |label_token| {
   6830         try astgen.checkLabelRedefinition(scope, label_token);
   6831     }
   6832 
   6833     const need_rl = astgen.nodes_need_rl.contains(node);
   6834     const block_ri: ResultInfo = if (need_rl) ri else .{
   6835         .rl = switch (ri.rl) {
   6836             .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
   6837             .inferred_ptr => .none,
   6838             else => ri.rl,
   6839         },
   6840         .ctx = ri.ctx,
   6841     };
   6842     // We need to call `rvalue` to write through to the pointer only if we had a
   6843     // result pointer and aren't forwarding it.
   6844     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   6845     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   6846 
   6847     const is_inline = for_full.inline_token != null;
   6848     if (parent_gz.is_comptime and is_inline) {
   6849         try astgen.appendErrorTok(for_full.inline_token.?, "redundant inline keyword in comptime scope", .{});
   6850     }
   6851     const tree = astgen.tree;
   6852     const gpa = astgen.gpa;
   6853 
   6854     // For counters, this is the start value; for indexables, this is the base
   6855     // pointer that can be used with elem_ptr and similar instructions.
   6856     // Special value `none` means that this is a counter and its start value is
   6857     // zero, indicating that the main index counter can be used directly.
   6858     const indexables = try gpa.alloc(Zir.Inst.Ref, for_full.ast.inputs.len);
   6859     defer gpa.free(indexables);
   6860     // elements of this array can be `none`, indicating no length check.
   6861     const lens = try gpa.alloc([2]Zir.Inst.Ref, for_full.ast.inputs.len);
   6862     defer gpa.free(lens);
   6863 
   6864     // We will use a single zero-based counter no matter how many indexables there are.
   6865     const index_ptr = blk: {
   6866         const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime_mut else .alloc;
   6867         const index_ptr = try parent_gz.addUnNode(alloc_tag, .usize_type, node);
   6868         // initialize to zero
   6869         _ = try parent_gz.addPlNode(.store_node, node, Zir.Inst.Bin{
   6870             .lhs = index_ptr,
   6871             .rhs = .zero_usize,
   6872         });
   6873         break :blk index_ptr;
   6874     };
   6875 
   6876     var any_len_checks = false;
   6877 
   6878     {
   6879         var capture_token = for_full.payload_token;
   6880         for (for_full.ast.inputs, indexables, lens) |input, *indexable_ref, *len_refs| {
   6881             const capture_is_ref = tree.tokenTag(capture_token) == .asterisk;
   6882             const ident_tok = capture_token + @intFromBool(capture_is_ref);
   6883             const is_discard = mem.eql(u8, tree.tokenSlice(ident_tok), "_");
   6884 
   6885             if (is_discard and capture_is_ref) {
   6886                 return astgen.failTok(capture_token, "pointer modifier invalid on discard", .{});
   6887             }
   6888             // Skip over the comma, and on to the next capture (or the ending pipe character).
   6889             capture_token = ident_tok + 2;
   6890 
   6891             try emitDbgNode(parent_gz, input);
   6892             if (tree.nodeTag(input) == .for_range) {
   6893                 if (capture_is_ref) {
   6894                     return astgen.failTok(ident_tok, "cannot capture reference to range", .{});
   6895                 }
   6896                 const start_node, const end_node = tree.nodeData(input).node_and_opt_node;
   6897                 const start_val = try expr(parent_gz, scope, .{ .rl = .{ .ty = .usize_type } }, start_node);
   6898 
   6899                 const end_val = if (end_node.unwrap()) |end|
   6900                     try expr(parent_gz, scope, .{ .rl = .{ .ty = .usize_type } }, end)
   6901                 else
   6902                     .none;
   6903 
   6904                 if (end_val == .none and is_discard) {
   6905                     try astgen.appendErrorTok(ident_tok, "discard of unbounded counter", .{});
   6906                 }
   6907 
   6908                 if (end_val == .none) {
   6909                     len_refs.* = .{ .none, .none };
   6910                 } else {
   6911                     any_len_checks = true;
   6912                     len_refs.* = .{ start_val, end_val };
   6913                 }
   6914 
   6915                 const start_is_zero = nodeIsTriviallyZero(tree, start_node);
   6916                 indexable_ref.* = if (start_is_zero) .none else start_val;
   6917             } else {
   6918                 const indexable = try expr(parent_gz, scope, .{ .rl = .none }, input);
   6919 
   6920                 any_len_checks = true;
   6921                 indexable_ref.* = indexable;
   6922                 len_refs.* = .{ indexable, .none };
   6923             }
   6924         }
   6925     }
   6926 
   6927     if (!any_len_checks) {
   6928         return astgen.failNode(node, "unbounded for loop", .{});
   6929     }
   6930 
   6931     // We use a dedicated ZIR instruction to assert the lengths to assist with
   6932     // nicer error reporting as well as fewer ZIR bytes emitted.
   6933     const len: Zir.Inst.Ref = len: {
   6934         const all_lens = @as([*]Zir.Inst.Ref, @ptrCast(lens))[0 .. lens.len * 2];
   6935         const lens_len: u32 = @intCast(all_lens.len);
   6936         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.MultiOp).@"struct".fields.len + lens_len);
   6937         const len = try parent_gz.addPlNode(.for_len, node, Zir.Inst.MultiOp{
   6938             .operands_len = lens_len,
   6939         });
   6940         appendRefsAssumeCapacity(astgen, all_lens);
   6941         break :len len;
   6942     };
   6943 
   6944     const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop;
   6945     const loop_block = try parent_gz.makeBlockInst(loop_tag, node);
   6946     try parent_gz.instructions.append(gpa, loop_block);
   6947 
   6948     var loop_scope = parent_gz.makeSubBlock(scope);
   6949     loop_scope.is_inline = is_inline;
   6950     loop_scope.setBreakResultInfo(block_ri);
   6951     defer loop_scope.unstack();
   6952 
   6953     // We need to finish loop_scope later once we have the deferred refs from then_scope. However, the
   6954     // load must be removed from instructions in the meantime or it appears to be part of parent_gz.
   6955     const index = try loop_scope.addUnNode(.load, index_ptr, node);
   6956     _ = loop_scope.instructions.pop();
   6957 
   6958     var cond_scope = parent_gz.makeSubBlock(&loop_scope.base);
   6959     defer cond_scope.unstack();
   6960 
   6961     // Check the condition.
   6962     const cond = try cond_scope.addPlNode(.cmp_lt, node, Zir.Inst.Bin{
   6963         .lhs = index,
   6964         .rhs = len,
   6965     });
   6966 
   6967     const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
   6968     const condbr = try cond_scope.addCondBr(condbr_tag, node);
   6969     const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block;
   6970     const cond_block = try loop_scope.makeBlockInst(block_tag, node);
   6971     try cond_scope.setBlockBody(cond_block);
   6972 
   6973     loop_scope.break_block = loop_block.toOptional();
   6974     loop_scope.continue_block = cond_block.toOptional();
   6975     if (for_full.label_token) |label_token| {
   6976         loop_scope.label = .{
   6977             .token = label_token,
   6978             .block_inst = loop_block,
   6979         };
   6980     }
   6981 
   6982     const then_node = for_full.ast.then_expr;
   6983     var then_scope = parent_gz.makeSubBlock(&cond_scope.base);
   6984     defer then_scope.unstack();
   6985 
   6986     const capture_scopes = try gpa.alloc(Scope.LocalVal, for_full.ast.inputs.len);
   6987     defer gpa.free(capture_scopes);
   6988 
   6989     const then_sub_scope = blk: {
   6990         var capture_token = for_full.payload_token;
   6991         var capture_sub_scope: *Scope = &then_scope.base;
   6992         for (for_full.ast.inputs, indexables, capture_scopes) |input, indexable_ref, *capture_scope| {
   6993             const capture_is_ref = tree.tokenTag(capture_token) == .asterisk;
   6994             const ident_tok = capture_token + @intFromBool(capture_is_ref);
   6995             const capture_name = tree.tokenSlice(ident_tok);
   6996             // Skip over the comma, and on to the next capture (or the ending pipe character).
   6997             capture_token = ident_tok + 2;
   6998 
   6999             if (mem.eql(u8, capture_name, "_")) continue;
   7000 
   7001             const name_str_index = try astgen.identAsString(ident_tok);
   7002             try astgen.detectLocalShadowing(capture_sub_scope, name_str_index, ident_tok, capture_name, .capture);
   7003 
   7004             const capture_inst = inst: {
   7005                 const is_counter = tree.nodeTag(input) == .for_range;
   7006 
   7007                 if (indexable_ref == .none) {
   7008                     // Special case: the main index can be used directly.
   7009                     assert(is_counter);
   7010                     assert(!capture_is_ref);
   7011                     break :inst index;
   7012                 }
   7013 
   7014                 // For counters, we add the index variable to the start value; for
   7015                 // indexables, we use it as an element index. This is so similar
   7016                 // that they can share the same code paths, branching only on the
   7017                 // ZIR tag.
   7018                 const switch_cond = (@as(u2, @intFromBool(capture_is_ref)) << 1) | @intFromBool(is_counter);
   7019                 const tag: Zir.Inst.Tag = switch (switch_cond) {
   7020                     0b00 => .elem_val,
   7021                     0b01 => .add,
   7022                     0b10 => .elem_ptr,
   7023                     0b11 => unreachable, // compile error emitted already
   7024                 };
   7025                 break :inst try then_scope.addPlNode(tag, input, Zir.Inst.Bin{
   7026                     .lhs = indexable_ref,
   7027                     .rhs = index,
   7028                 });
   7029             };
   7030 
   7031             capture_scope.* = .{
   7032                 .parent = capture_sub_scope,
   7033                 .gen_zir = &then_scope,
   7034                 .name = name_str_index,
   7035                 .inst = capture_inst,
   7036                 .token_src = ident_tok,
   7037                 .id_cat = .capture,
   7038             };
   7039 
   7040             try then_scope.addDbgVar(.dbg_var_val, name_str_index, capture_inst);
   7041             capture_sub_scope = &capture_scope.base;
   7042         }
   7043 
   7044         break :blk capture_sub_scope;
   7045     };
   7046 
   7047     const then_result = try fullBodyExpr(&then_scope, then_sub_scope, .{ .rl = .none }, then_node, .allow_branch_hint);
   7048     _ = try addEnsureResult(&then_scope, then_result, then_node);
   7049 
   7050     try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
   7051 
   7052     astgen.advanceSourceCursor(tree.tokenStart(tree.lastToken(then_node)));
   7053     try emitDbgStmt(parent_gz, .{ astgen.source_line - parent_gz.decl_line, astgen.source_column });
   7054     _ = try parent_gz.add(.{
   7055         .tag = .extended,
   7056         .data = .{ .extended = .{
   7057             .opcode = .dbg_empty_stmt,
   7058             .small = undefined,
   7059             .operand = undefined,
   7060         } },
   7061     });
   7062 
   7063     const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
   7064     _ = try then_scope.addBreak(break_tag, cond_block, .void_value);
   7065 
   7066     var else_scope = parent_gz.makeSubBlock(&cond_scope.base);
   7067     defer else_scope.unstack();
   7068 
   7069     if (for_full.ast.else_expr.unwrap()) |else_node| {
   7070         const sub_scope = &else_scope.base;
   7071         // Remove the continue block and break block so that `continue` and `break`
   7072         // control flow apply to outer loops; not this one.
   7073         loop_scope.continue_block = .none;
   7074         loop_scope.break_block = .none;
   7075         const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node, .allow_branch_hint);
   7076         if (is_statement) {
   7077             _ = try addEnsureResult(&else_scope, else_result, else_node);
   7078         }
   7079         if (!else_scope.endsWithNoReturn()) {
   7080             _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node);
   7081         }
   7082     } else {
   7083         const result = try rvalue(&else_scope, ri, .void_value, node);
   7084         _ = try else_scope.addBreak(break_tag, loop_block, result);
   7085     }
   7086 
   7087     if (loop_scope.label) |some| {
   7088         if (!some.used) {
   7089             try astgen.appendErrorTok(some.token, "unused for loop label", .{});
   7090         }
   7091     }
   7092 
   7093     try setCondBrPayload(condbr, cond, &then_scope, &else_scope);
   7094 
   7095     // then_block and else_block unstacked now, can resurrect loop_scope to finally finish it
   7096     {
   7097         loop_scope.instructions_top = loop_scope.instructions.items.len;
   7098         try loop_scope.instructions.appendSlice(gpa, &.{ index.toIndex().?, cond_block });
   7099 
   7100         // Increment the index variable.
   7101         const index_plus_one = try loop_scope.addPlNode(.add_unsafe, node, Zir.Inst.Bin{
   7102             .lhs = index,
   7103             .rhs = .one_usize,
   7104         });
   7105         _ = try loop_scope.addPlNode(.store_node, node, Zir.Inst.Bin{
   7106             .lhs = index_ptr,
   7107             .rhs = index_plus_one,
   7108         });
   7109 
   7110         const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
   7111         _ = try loop_scope.addNode(repeat_tag, node);
   7112 
   7113         try loop_scope.setBlockBody(loop_block);
   7114     }
   7115 
   7116     const result = if (need_result_rvalue)
   7117         try rvalue(parent_gz, ri, loop_block.toRef(), node)
   7118     else
   7119         loop_block.toRef();
   7120 
   7121     if (is_statement) {
   7122         _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
   7123     }
   7124     return result;
   7125 }
   7126 
   7127 fn switchExprErrUnion(
   7128     parent_gz: *GenZir,
   7129     scope: *Scope,
   7130     ri: ResultInfo,
   7131     catch_or_if_node: Ast.Node.Index,
   7132     node_ty: enum { @"catch", @"if" },
   7133 ) InnerError!Zir.Inst.Ref {
   7134     const astgen = parent_gz.astgen;
   7135     const gpa = astgen.gpa;
   7136     const tree = astgen.tree;
   7137 
   7138     const if_full = switch (node_ty) {
   7139         .@"catch" => undefined,
   7140         .@"if" => tree.fullIf(catch_or_if_node).?,
   7141     };
   7142 
   7143     const switch_node, const operand_node, const error_payload = switch (node_ty) {
   7144         .@"catch" => .{
   7145             tree.nodeData(catch_or_if_node).node_and_node[1],
   7146             tree.nodeData(catch_or_if_node).node_and_node[0],
   7147             tree.nodeMainToken(catch_or_if_node) + 2,
   7148         },
   7149         .@"if" => .{
   7150             if_full.ast.else_expr.unwrap().?,
   7151             if_full.ast.cond_expr,
   7152             if_full.error_token.?,
   7153         },
   7154     };
   7155     const switch_full = tree.fullSwitch(switch_node).?;
   7156 
   7157     const do_err_trace = astgen.fn_block != null;
   7158     const need_rl = astgen.nodes_need_rl.contains(catch_or_if_node);
   7159     const block_ri: ResultInfo = if (need_rl) ri else .{
   7160         .rl = switch (ri.rl) {
   7161             .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, catch_or_if_node)).? },
   7162             .inferred_ptr => .none,
   7163             else => ri.rl,
   7164         },
   7165         .ctx = ri.ctx,
   7166     };
   7167 
   7168     const payload_is_ref = switch (node_ty) {
   7169         .@"if" => if_full.payload_token != null and tree.tokenTag(if_full.payload_token.?) == .asterisk,
   7170         .@"catch" => ri.rl == .ref or ri.rl == .ref_coerced_ty,
   7171     };
   7172 
   7173     // We need to call `rvalue` to write through to the pointer only if we had a
   7174     // result pointer and aren't forwarding it.
   7175     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   7176     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   7177     var scalar_cases_len: u32 = 0;
   7178     var multi_cases_len: u32 = 0;
   7179     var inline_cases_len: u32 = 0;
   7180     var has_else = false;
   7181     var else_node: Ast.Node.OptionalIndex = .none;
   7182     var else_src: ?Ast.TokenIndex = null;
   7183     for (switch_full.ast.cases) |case_node| {
   7184         const case = tree.fullSwitchCase(case_node).?;
   7185 
   7186         if (case.ast.values.len == 0) {
   7187             const case_src = case.ast.arrow_token - 1;
   7188             if (else_src) |src| {
   7189                 return astgen.failTokNotes(
   7190                     case_src,
   7191                     "multiple else prongs in switch expression",
   7192                     .{},
   7193                     &[_]u32{
   7194                         try astgen.errNoteTok(
   7195                             src,
   7196                             "previous else prong here",
   7197                             .{},
   7198                         ),
   7199                     },
   7200                 );
   7201             }
   7202             has_else = true;
   7203             else_node = case_node.toOptional();
   7204             else_src = case_src;
   7205             continue;
   7206         } else if (case.ast.values.len == 1 and
   7207             tree.nodeTag(case.ast.values[0]) == .identifier and
   7208             mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(case.ast.values[0])), "_"))
   7209         {
   7210             const case_src = case.ast.arrow_token - 1;
   7211             return astgen.failTokNotes(
   7212                 case_src,
   7213                 "'_' prong is not allowed when switching on errors",
   7214                 .{},
   7215                 &[_]u32{
   7216                     try astgen.errNoteTok(
   7217                         case_src,
   7218                         "consider using 'else'",
   7219                         .{},
   7220                     ),
   7221                 },
   7222             );
   7223         }
   7224 
   7225         for (case.ast.values) |val| {
   7226             if (tree.nodeTag(val) == .string_literal)
   7227                 return astgen.failNode(val, "cannot switch on strings", .{});
   7228         }
   7229 
   7230         if (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) != .switch_range) {
   7231             scalar_cases_len += 1;
   7232         } else {
   7233             multi_cases_len += 1;
   7234         }
   7235         if (case.inline_token != null) {
   7236             inline_cases_len += 1;
   7237         }
   7238     }
   7239 
   7240     const operand_ri: ResultInfo = .{
   7241         .rl = if (payload_is_ref) .ref else .none,
   7242         .ctx = .error_handling_expr,
   7243     };
   7244 
   7245     astgen.advanceSourceCursorToNode(operand_node);
   7246     const operand_lc: LineColumn = .{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
   7247 
   7248     const raw_operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, switch_node);
   7249     const item_ri: ResultInfo = .{ .rl = .none };
   7250 
   7251     // This contains the data that goes into the `extra` array for the SwitchBlockErrUnion, except
   7252     // the first cases_nodes.len slots are a table that indexes payloads later in the array,
   7253     // with the non-error and else case indices coming first, then scalar_cases_len indexes, then
   7254     // multi_cases_len indexes
   7255     const payloads = &astgen.scratch;
   7256     const scratch_top = astgen.scratch.items.len;
   7257     const case_table_start = scratch_top;
   7258     const scalar_case_table = case_table_start + 1 + @intFromBool(has_else);
   7259     const multi_case_table = scalar_case_table + scalar_cases_len;
   7260     const case_table_end = multi_case_table + multi_cases_len;
   7261 
   7262     try astgen.scratch.resize(gpa, case_table_end);
   7263     defer astgen.scratch.items.len = scratch_top;
   7264 
   7265     var block_scope = parent_gz.makeSubBlock(scope);
   7266     // block_scope not used for collecting instructions
   7267     block_scope.instructions_top = GenZir.unstacked_top;
   7268     block_scope.setBreakResultInfo(block_ri);
   7269 
   7270     // Sema expects a dbg_stmt immediately before switch_block_err_union
   7271     try emitDbgStmtForceCurrentIndex(parent_gz, operand_lc);
   7272     // This gets added to the parent block later, after the item expressions.
   7273     const switch_block = try parent_gz.makeBlockInst(.switch_block_err_union, switch_node);
   7274 
   7275     // We re-use this same scope for all cases, including the special prong, if any.
   7276     var case_scope = parent_gz.makeSubBlock(&block_scope.base);
   7277     case_scope.instructions_top = GenZir.unstacked_top;
   7278 
   7279     {
   7280         const body_len_index: u32 = @intCast(payloads.items.len);
   7281         payloads.items[case_table_start] = body_len_index;
   7282         try payloads.resize(gpa, body_len_index + 1); // body_len
   7283 
   7284         case_scope.instructions_top = parent_gz.instructions.items.len;
   7285         defer case_scope.unstack();
   7286 
   7287         const unwrap_payload_tag: Zir.Inst.Tag = if (payload_is_ref)
   7288             .err_union_payload_unsafe_ptr
   7289         else
   7290             .err_union_payload_unsafe;
   7291 
   7292         const unwrapped_payload = try case_scope.addUnNode(
   7293             unwrap_payload_tag,
   7294             raw_operand,
   7295             catch_or_if_node,
   7296         );
   7297 
   7298         switch (node_ty) {
   7299             .@"catch" => {
   7300                 const case_result = switch (ri.rl) {
   7301                     .ref, .ref_coerced_ty => unwrapped_payload,
   7302                     else => try rvalue(
   7303                         &case_scope,
   7304                         block_scope.break_result_info,
   7305                         unwrapped_payload,
   7306                         catch_or_if_node,
   7307                     ),
   7308                 };
   7309                 _ = try case_scope.addBreakWithSrcNode(
   7310                     .@"break",
   7311                     switch_block,
   7312                     case_result,
   7313                     catch_or_if_node,
   7314                 );
   7315             },
   7316             .@"if" => {
   7317                 var payload_val_scope: Scope.LocalVal = undefined;
   7318 
   7319                 const then_node = if_full.ast.then_expr;
   7320                 const then_sub_scope = s: {
   7321                     assert(if_full.error_token != null);
   7322                     if (if_full.payload_token) |payload_token| {
   7323                         const token_name_index = payload_token + @intFromBool(payload_is_ref);
   7324                         const ident_name = try astgen.identAsString(token_name_index);
   7325                         const token_name_str = tree.tokenSlice(token_name_index);
   7326                         if (mem.eql(u8, "_", token_name_str))
   7327                             break :s &case_scope.base;
   7328                         try astgen.detectLocalShadowing(
   7329                             &case_scope.base,
   7330                             ident_name,
   7331                             token_name_index,
   7332                             token_name_str,
   7333                             .capture,
   7334                         );
   7335                         payload_val_scope = .{
   7336                             .parent = &case_scope.base,
   7337                             .gen_zir = &case_scope,
   7338                             .name = ident_name,
   7339                             .inst = unwrapped_payload,
   7340                             .token_src = token_name_index,
   7341                             .id_cat = .capture,
   7342                         };
   7343                         try case_scope.addDbgVar(.dbg_var_val, ident_name, unwrapped_payload);
   7344                         break :s &payload_val_scope.base;
   7345                     } else {
   7346                         _ = try case_scope.addUnNode(
   7347                             .ensure_err_union_payload_void,
   7348                             raw_operand,
   7349                             catch_or_if_node,
   7350                         );
   7351                         break :s &case_scope.base;
   7352                     }
   7353                 };
   7354                 const then_result = try expr(
   7355                     &case_scope,
   7356                     then_sub_scope,
   7357                     block_scope.break_result_info,
   7358                     then_node,
   7359                 );
   7360                 try checkUsed(parent_gz, &case_scope.base, then_sub_scope);
   7361                 if (!case_scope.endsWithNoReturn()) {
   7362                     _ = try case_scope.addBreakWithSrcNode(
   7363                         .@"break",
   7364                         switch_block,
   7365                         then_result,
   7366                         then_node,
   7367                     );
   7368                 }
   7369             },
   7370         }
   7371 
   7372         const case_slice = case_scope.instructionsSlice();
   7373         const body_len = astgen.countBodyLenAfterFixupsExtraRefs(case_slice, &.{switch_block});
   7374         try payloads.ensureUnusedCapacity(gpa, body_len);
   7375         const capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = switch (node_ty) {
   7376             .@"catch" => .none,
   7377             .@"if" => if (if_full.payload_token == null)
   7378                 .none
   7379             else if (payload_is_ref)
   7380                 .by_ref
   7381             else
   7382                 .by_val,
   7383         };
   7384         payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
   7385             .body_len = @intCast(body_len),
   7386             .capture = capture,
   7387             .is_inline = false,
   7388             .has_tag_capture = false,
   7389         });
   7390         appendBodyWithFixupsExtraRefsArrayList(astgen, payloads, case_slice, &.{switch_block});
   7391     }
   7392 
   7393     const err_name = blk: {
   7394         const err_str = tree.tokenSlice(error_payload);
   7395         if (mem.eql(u8, err_str, "_")) {
   7396             // This is fatal because we already know we're switching on the captured error.
   7397             return astgen.failTok(error_payload, "discard of error capture; omit it instead", .{});
   7398         }
   7399         const err_name = try astgen.identAsString(error_payload);
   7400         try astgen.detectLocalShadowing(scope, err_name, error_payload, err_str, .capture);
   7401 
   7402         break :blk err_name;
   7403     };
   7404 
   7405     // allocate a shared dummy instruction for the error capture
   7406     const err_inst = err_inst: {
   7407         const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
   7408         try astgen.instructions.append(astgen.gpa, .{
   7409             .tag = .extended,
   7410             .data = .{ .extended = .{
   7411                 .opcode = .value_placeholder,
   7412                 .small = undefined,
   7413                 .operand = undefined,
   7414             } },
   7415         });
   7416         break :err_inst inst;
   7417     };
   7418 
   7419     // In this pass we generate all the item and prong expressions for error cases.
   7420     var multi_case_index: u32 = 0;
   7421     var scalar_case_index: u32 = 0;
   7422     var any_uses_err_capture = false;
   7423     for (switch_full.ast.cases) |case_node| {
   7424         const case = tree.fullSwitchCase(case_node).?;
   7425 
   7426         const is_multi_case = case.ast.values.len > 1 or
   7427             (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) == .switch_range);
   7428 
   7429         var dbg_var_name: Zir.NullTerminatedString = .empty;
   7430         var dbg_var_inst: Zir.Inst.Ref = undefined;
   7431         var err_scope: Scope.LocalVal = undefined;
   7432         var capture_scope: Scope.LocalVal = undefined;
   7433 
   7434         const sub_scope = blk: {
   7435             err_scope = .{
   7436                 .parent = &case_scope.base,
   7437                 .gen_zir = &case_scope,
   7438                 .name = err_name,
   7439                 .inst = err_inst.toRef(),
   7440                 .token_src = error_payload,
   7441                 .id_cat = .capture,
   7442             };
   7443 
   7444             const capture_token = case.payload_token orelse break :blk &err_scope.base;
   7445             if (tree.tokenTag(capture_token) != .identifier) {
   7446                 return astgen.failTok(capture_token + 1, "error set cannot be captured by reference", .{});
   7447             }
   7448 
   7449             const capture_slice = tree.tokenSlice(capture_token);
   7450             if (mem.eql(u8, capture_slice, "_")) {
   7451                 try astgen.appendErrorTok(capture_token, "discard of error capture; omit it instead", .{});
   7452             }
   7453             const tag_name = try astgen.identAsString(capture_token);
   7454             try astgen.detectLocalShadowing(&case_scope.base, tag_name, capture_token, capture_slice, .capture);
   7455 
   7456             capture_scope = .{
   7457                 .parent = &case_scope.base,
   7458                 .gen_zir = &case_scope,
   7459                 .name = tag_name,
   7460                 .inst = switch_block.toRef(),
   7461                 .token_src = capture_token,
   7462                 .id_cat = .capture,
   7463             };
   7464             dbg_var_name = tag_name;
   7465             dbg_var_inst = switch_block.toRef();
   7466 
   7467             err_scope.parent = &capture_scope.base;
   7468 
   7469             break :blk &err_scope.base;
   7470         };
   7471 
   7472         const header_index: u32 = @intCast(payloads.items.len);
   7473         const body_len_index = if (is_multi_case) blk: {
   7474             payloads.items[multi_case_table + multi_case_index] = header_index;
   7475             multi_case_index += 1;
   7476             try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len
   7477 
   7478             // items
   7479             var items_len: u32 = 0;
   7480             for (case.ast.values) |item_node| {
   7481                 if (tree.nodeTag(item_node) == .switch_range) continue;
   7482                 items_len += 1;
   7483 
   7484                 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item);
   7485                 try payloads.append(gpa, @intFromEnum(item_inst));
   7486             }
   7487 
   7488             // ranges
   7489             var ranges_len: u32 = 0;
   7490             for (case.ast.values) |range| {
   7491                 if (tree.nodeTag(range) != .switch_range) continue;
   7492                 ranges_len += 1;
   7493 
   7494                 const first_node, const last_node = tree.nodeData(range).node_and_node;
   7495                 const first = try comptimeExpr(parent_gz, scope, item_ri, first_node, .switch_item);
   7496                 const last = try comptimeExpr(parent_gz, scope, item_ri, last_node, .switch_item);
   7497                 try payloads.appendSlice(gpa, &[_]u32{
   7498                     @intFromEnum(first), @intFromEnum(last),
   7499                 });
   7500             }
   7501 
   7502             payloads.items[header_index] = items_len;
   7503             payloads.items[header_index + 1] = ranges_len;
   7504             break :blk header_index + 2;
   7505         } else if (case_node.toOptional() == else_node) blk: {
   7506             payloads.items[case_table_start + 1] = header_index;
   7507             try payloads.resize(gpa, header_index + 1); // body_len
   7508             break :blk header_index;
   7509         } else blk: {
   7510             payloads.items[scalar_case_table + scalar_case_index] = header_index;
   7511             scalar_case_index += 1;
   7512             try payloads.resize(gpa, header_index + 2); // item, body_len
   7513             const item_node = case.ast.values[0];
   7514             const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item);
   7515             payloads.items[header_index] = @intFromEnum(item_inst);
   7516             break :blk header_index + 1;
   7517         };
   7518 
   7519         {
   7520             // temporarily stack case_scope on parent_gz
   7521             case_scope.instructions_top = parent_gz.instructions.items.len;
   7522             defer case_scope.unstack();
   7523 
   7524             if (do_err_trace and nodeMayAppendToErrorTrace(tree, operand_node))
   7525                 _ = try case_scope.addSaveErrRetIndex(.always);
   7526 
   7527             if (dbg_var_name != .empty) {
   7528                 try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst);
   7529             }
   7530 
   7531             const target_expr_node = case.ast.target_expr;
   7532             const case_result = try fullBodyExpr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node, .allow_branch_hint);
   7533             // check capture_scope, not err_scope to avoid false positive unused error capture
   7534             try checkUsed(parent_gz, &case_scope.base, err_scope.parent);
   7535             const uses_err = err_scope.used != .none or err_scope.discarded != .none;
   7536             if (uses_err) {
   7537                 try case_scope.addDbgVar(.dbg_var_val, err_name, err_inst.toRef());
   7538                 any_uses_err_capture = true;
   7539             }
   7540 
   7541             if (!parent_gz.refIsNoReturn(case_result)) {
   7542                 if (do_err_trace)
   7543                     try restoreErrRetIndex(
   7544                         &case_scope,
   7545                         .{ .block = switch_block },
   7546                         block_scope.break_result_info,
   7547                         target_expr_node,
   7548                         case_result,
   7549                     );
   7550 
   7551                 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node);
   7552             }
   7553 
   7554             const case_slice = case_scope.instructionsSlice();
   7555             const extra_insts: []const Zir.Inst.Index = if (uses_err) &.{ switch_block, err_inst } else &.{switch_block};
   7556             const body_len = astgen.countBodyLenAfterFixupsExtraRefs(case_slice, extra_insts);
   7557             try payloads.ensureUnusedCapacity(gpa, body_len);
   7558             payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
   7559                 .body_len = @intCast(body_len),
   7560                 .capture = if (case.payload_token != null) .by_val else .none,
   7561                 .is_inline = case.inline_token != null,
   7562                 .has_tag_capture = false,
   7563             });
   7564             appendBodyWithFixupsExtraRefsArrayList(astgen, payloads, case_slice, extra_insts);
   7565         }
   7566     }
   7567     // Now that the item expressions are generated we can add this.
   7568     try parent_gz.instructions.append(gpa, switch_block);
   7569 
   7570     try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlockErrUnion).@"struct".fields.len +
   7571         @intFromBool(multi_cases_len != 0) +
   7572         payloads.items.len - case_table_end +
   7573         (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).@"struct".fields.len);
   7574 
   7575     const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlockErrUnion{
   7576         .operand = raw_operand,
   7577         .bits = Zir.Inst.SwitchBlockErrUnion.Bits{
   7578             .has_multi_cases = multi_cases_len != 0,
   7579             .has_else = has_else,
   7580             .scalar_cases_len = @intCast(scalar_cases_len),
   7581             .any_uses_err_capture = any_uses_err_capture,
   7582             .payload_is_ref = payload_is_ref,
   7583         },
   7584         .main_src_node_offset = parent_gz.nodeIndexToRelative(catch_or_if_node),
   7585     });
   7586 
   7587     if (multi_cases_len != 0) {
   7588         astgen.extra.appendAssumeCapacity(multi_cases_len);
   7589     }
   7590 
   7591     if (any_uses_err_capture) {
   7592         astgen.extra.appendAssumeCapacity(@intFromEnum(err_inst));
   7593     }
   7594 
   7595     const zir_datas = astgen.instructions.items(.data);
   7596     zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;
   7597 
   7598     for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| {
   7599         var body_len_index = start_index;
   7600         var end_index = start_index;
   7601         const table_index = case_table_start + i;
   7602         if (table_index < scalar_case_table) {
   7603             end_index += 1;
   7604         } else if (table_index < multi_case_table) {
   7605             body_len_index += 1;
   7606             end_index += 2;
   7607         } else {
   7608             body_len_index += 2;
   7609             const items_len = payloads.items[start_index];
   7610             const ranges_len = payloads.items[start_index + 1];
   7611             end_index += 3 + items_len + 2 * ranges_len;
   7612         }
   7613         const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
   7614         end_index += prong_info.body_len;
   7615         astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
   7616     }
   7617 
   7618     if (need_result_rvalue) {
   7619         return rvalue(parent_gz, ri, switch_block.toRef(), switch_node);
   7620     } else {
   7621         return switch_block.toRef();
   7622     }
   7623 }
   7624 
   7625 fn switchExpr(
   7626     parent_gz: *GenZir,
   7627     scope: *Scope,
   7628     ri: ResultInfo,
   7629     node: Ast.Node.Index,
   7630     switch_full: Ast.full.Switch,
   7631 ) InnerError!Zir.Inst.Ref {
   7632     const astgen = parent_gz.astgen;
   7633     const gpa = astgen.gpa;
   7634     const tree = astgen.tree;
   7635     const operand_node = switch_full.ast.condition;
   7636     const case_nodes = switch_full.ast.cases;
   7637 
   7638     const need_rl = astgen.nodes_need_rl.contains(node);
   7639     const block_ri: ResultInfo = if (need_rl) ri else .{
   7640         .rl = switch (ri.rl) {
   7641             .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
   7642             .inferred_ptr => .none,
   7643             else => ri.rl,
   7644         },
   7645         .ctx = ri.ctx,
   7646     };
   7647     // We need to call `rvalue` to write through to the pointer only if we had a
   7648     // result pointer and aren't forwarding it.
   7649     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   7650     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   7651 
   7652     if (switch_full.label_token) |label_token| {
   7653         try astgen.checkLabelRedefinition(scope, label_token);
   7654     }
   7655 
   7656     // We perform two passes over the AST. This first pass is to collect information
   7657     // for the following variables, make note of the special prong AST node index,
   7658     // and bail out with a compile error if there are multiple special prongs present.
   7659     var any_payload_is_ref = false;
   7660     var any_has_tag_capture = false;
   7661     var any_non_inline_capture = false;
   7662     var scalar_cases_len: u32 = 0;
   7663     var multi_cases_len: u32 = 0;
   7664     var inline_cases_len: u32 = 0;
   7665     var else_case_node: Ast.Node.OptionalIndex = .none;
   7666     var else_src: ?Ast.TokenIndex = null;
   7667     var underscore_case_node: Ast.Node.OptionalIndex = .none;
   7668     var underscore_node: Ast.Node.OptionalIndex = .none;
   7669     var underscore_src: ?Ast.TokenIndex = null;
   7670     var underscore_additional_items: Zir.SpecialProngs.AdditionalItems = .none;
   7671     for (case_nodes) |case_node| {
   7672         const case = tree.fullSwitchCase(case_node).?;
   7673         if (case.payload_token) |payload_token| {
   7674             const ident = if (tree.tokenTag(payload_token) == .asterisk) blk: {
   7675                 any_payload_is_ref = true;
   7676                 break :blk payload_token + 1;
   7677             } else payload_token;
   7678             if (tree.tokenTag(ident + 1) == .comma) {
   7679                 any_has_tag_capture = true;
   7680             }
   7681 
   7682             // If the first capture is ignored, then there is no runtime-known
   7683             // capture, as the tag capture must be for an inline prong.
   7684             // This check isn't perfect, because for things like enums, the
   7685             // first prong *is* comptime-known for inline prongs! But such
   7686             // knowledge requires semantic analysis.
   7687             if (!mem.eql(u8, tree.tokenSlice(ident), "_")) {
   7688                 any_non_inline_capture = true;
   7689             }
   7690         }
   7691 
   7692         // Check for else prong.
   7693         if (case.ast.values.len == 0) {
   7694             const case_src = case.ast.arrow_token - 1;
   7695             if (else_src) |src| {
   7696                 return astgen.failTokNotes(
   7697                     case_src,
   7698                     "multiple else prongs in switch expression",
   7699                     .{},
   7700                     &[_]u32{
   7701                         try astgen.errNoteTok(
   7702                             src,
   7703                             "previous else prong here",
   7704                             .{},
   7705                         ),
   7706                     },
   7707                 );
   7708             }
   7709             else_case_node = case_node.toOptional();
   7710             else_src = case_src;
   7711             continue;
   7712         }
   7713 
   7714         // Check for '_' prong.
   7715         var case_has_underscore = false;
   7716         for (case.ast.values) |val| {
   7717             switch (tree.nodeTag(val)) {
   7718                 .identifier => if (mem.eql(u8, tree.tokenSlice(tree.nodeMainToken(val)), "_")) {
   7719                     const val_src = tree.nodeMainToken(val);
   7720                     if (underscore_src) |src| {
   7721                         return astgen.failTokNotes(
   7722                             val_src,
   7723                             "multiple '_' prongs in switch expression",
   7724                             .{},
   7725                             &[_]u32{
   7726                                 try astgen.errNoteTok(
   7727                                     src,
   7728                                     "previous '_' prong here",
   7729                                     .{},
   7730                                 ),
   7731                             },
   7732                         );
   7733                     }
   7734                     if (case.inline_token != null) {
   7735                         return astgen.failTok(val_src, "cannot inline '_' prong", .{});
   7736                     }
   7737                     underscore_case_node = case_node.toOptional();
   7738                     underscore_src = val_src;
   7739                     underscore_node = val.toOptional();
   7740                     underscore_additional_items = switch (case.ast.values.len) {
   7741                         0 => unreachable,
   7742                         1 => .none,
   7743                         2 => .one,
   7744                         else => .many,
   7745                     };
   7746                     case_has_underscore = true;
   7747                 },
   7748                 .string_literal => return astgen.failNode(val, "cannot switch on strings", .{}),
   7749                 else => {},
   7750             }
   7751         }
   7752         if (case_has_underscore) continue;
   7753 
   7754         if (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) != .switch_range) {
   7755             scalar_cases_len += 1;
   7756         } else {
   7757             multi_cases_len += 1;
   7758         }
   7759         if (case.inline_token != null) {
   7760             inline_cases_len += 1;
   7761         }
   7762     }
   7763 
   7764     const special_prongs: Zir.SpecialProngs = .init(
   7765         else_src != null,
   7766         underscore_src != null,
   7767         underscore_additional_items,
   7768     );
   7769     const has_else = special_prongs.hasElse();
   7770     const has_under = special_prongs.hasUnder();
   7771 
   7772     const operand_ri: ResultInfo = .{ .rl = if (any_payload_is_ref) .ref else .none };
   7773 
   7774     astgen.advanceSourceCursorToNode(operand_node);
   7775     const operand_lc: LineColumn = .{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
   7776 
   7777     const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node);
   7778     const item_ri: ResultInfo = .{ .rl = .none };
   7779 
   7780     // If this switch is labeled, it may have `continue`s targeting it, and thus we need the operand type
   7781     // to provide a result type.
   7782     const raw_operand_ty_ref = if (switch_full.label_token != null) t: {
   7783         break :t try parent_gz.addUnNode(.typeof, raw_operand, operand_node);
   7784     } else undefined;
   7785 
   7786     // This contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti,
   7787     // except the first cases_nodes.len slots are a table that indexes payloads later in the array, with
   7788     // the special case index coming first, then scalar_case_len indexes, then multi_cases_len indexes
   7789     const payloads = &astgen.scratch;
   7790     const scratch_top = astgen.scratch.items.len;
   7791     const case_table_start = scratch_top;
   7792     const else_case_index = if (has_else) case_table_start else undefined;
   7793     const under_case_index = if (has_under) case_table_start + @intFromBool(has_else) else undefined;
   7794     const scalar_case_table = case_table_start + @intFromBool(has_else) + @intFromBool(has_under);
   7795     const multi_case_table = scalar_case_table + scalar_cases_len;
   7796     const case_table_end = multi_case_table + multi_cases_len;
   7797     try astgen.scratch.resize(gpa, case_table_end);
   7798     defer astgen.scratch.items.len = scratch_top;
   7799 
   7800     var block_scope = parent_gz.makeSubBlock(scope);
   7801     // block_scope not used for collecting instructions
   7802     block_scope.instructions_top = GenZir.unstacked_top;
   7803     block_scope.setBreakResultInfo(block_ri);
   7804 
   7805     // Sema expects a dbg_stmt immediately before switch_block(_ref)
   7806     try emitDbgStmtForceCurrentIndex(parent_gz, operand_lc);
   7807     // This gets added to the parent block later, after the item expressions.
   7808     const switch_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_block_ref else .switch_block;
   7809     const switch_block = try parent_gz.makeBlockInst(switch_tag, node);
   7810 
   7811     if (switch_full.label_token) |label_token| {
   7812         block_scope.continue_block = switch_block.toOptional();
   7813         block_scope.continue_result_info = .{
   7814             .rl = if (any_payload_is_ref)
   7815                 .{ .ref_coerced_ty = raw_operand_ty_ref }
   7816             else
   7817                 .{ .coerced_ty = raw_operand_ty_ref },
   7818         };
   7819 
   7820         block_scope.label = .{
   7821             .token = label_token,
   7822             .block_inst = switch_block,
   7823         };
   7824         // `break` can target this via `label.block_inst`
   7825         // `break_result_info` already set by `setBreakResultInfo`
   7826     }
   7827 
   7828     // We re-use this same scope for all cases, including the special prong, if any.
   7829     var case_scope = parent_gz.makeSubBlock(&block_scope.base);
   7830     case_scope.instructions_top = GenZir.unstacked_top;
   7831 
   7832     // If any prong has an inline tag capture, allocate a shared dummy instruction for it
   7833     const tag_inst = if (any_has_tag_capture) tag_inst: {
   7834         const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
   7835         try astgen.instructions.append(astgen.gpa, .{
   7836             .tag = .extended,
   7837             .data = .{ .extended = .{
   7838                 .opcode = .value_placeholder,
   7839                 .small = undefined,
   7840                 .operand = undefined,
   7841             } },
   7842         });
   7843         break :tag_inst inst;
   7844     } else undefined;
   7845 
   7846     // In this pass we generate all the item and prong expressions.
   7847     var multi_case_index: u32 = 0;
   7848     var scalar_case_index: u32 = 0;
   7849     for (case_nodes) |case_node| {
   7850         const case = tree.fullSwitchCase(case_node).?;
   7851 
   7852         const is_multi_case = case.ast.values.len > 1 or
   7853             (case.ast.values.len == 1 and tree.nodeTag(case.ast.values[0]) == .switch_range);
   7854 
   7855         var dbg_var_name: Zir.NullTerminatedString = .empty;
   7856         var dbg_var_inst: Zir.Inst.Ref = undefined;
   7857         var dbg_var_tag_name: Zir.NullTerminatedString = .empty;
   7858         var dbg_var_tag_inst: Zir.Inst.Ref = undefined;
   7859         var has_tag_capture = false;
   7860         var capture_val_scope: Scope.LocalVal = undefined;
   7861         var tag_scope: Scope.LocalVal = undefined;
   7862 
   7863         var capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = .none;
   7864 
   7865         const sub_scope = blk: {
   7866             const payload_token = case.payload_token orelse break :blk &case_scope.base;
   7867             const capture_is_ref = tree.tokenTag(payload_token) == .asterisk;
   7868             const ident = payload_token + @intFromBool(capture_is_ref);
   7869 
   7870             capture = if (capture_is_ref) .by_ref else .by_val;
   7871 
   7872             const ident_slice = tree.tokenSlice(ident);
   7873             var payload_sub_scope: *Scope = undefined;
   7874             if (mem.eql(u8, ident_slice, "_")) {
   7875                 if (capture_is_ref) {
   7876                     return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   7877                 }
   7878                 payload_sub_scope = &case_scope.base;
   7879             } else {
   7880                 const capture_name = try astgen.identAsString(ident);
   7881                 try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice, .capture);
   7882                 capture_val_scope = .{
   7883                     .parent = &case_scope.base,
   7884                     .gen_zir = &case_scope,
   7885                     .name = capture_name,
   7886                     .inst = switch_block.toRef(),
   7887                     .token_src = ident,
   7888                     .id_cat = .capture,
   7889                 };
   7890                 dbg_var_name = capture_name;
   7891                 dbg_var_inst = switch_block.toRef();
   7892                 payload_sub_scope = &capture_val_scope.base;
   7893             }
   7894 
   7895             const tag_token = if (tree.tokenTag(ident + 1) == .comma)
   7896                 ident + 2
   7897             else
   7898                 break :blk payload_sub_scope;
   7899             const tag_slice = tree.tokenSlice(tag_token);
   7900             if (mem.eql(u8, tag_slice, "_")) {
   7901                 try astgen.appendErrorTok(tag_token, "discard of tag capture; omit it instead", .{});
   7902             } else if (case.inline_token == null) {
   7903                 return astgen.failTok(tag_token, "tag capture on non-inline prong", .{});
   7904             }
   7905             const tag_name = try astgen.identAsString(tag_token);
   7906             try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice, .@"switch tag capture");
   7907 
   7908             assert(any_has_tag_capture);
   7909             has_tag_capture = true;
   7910 
   7911             tag_scope = .{
   7912                 .parent = payload_sub_scope,
   7913                 .gen_zir = &case_scope,
   7914                 .name = tag_name,
   7915                 .inst = tag_inst.toRef(),
   7916                 .token_src = tag_token,
   7917                 .id_cat = .@"switch tag capture",
   7918             };
   7919             dbg_var_tag_name = tag_name;
   7920             dbg_var_tag_inst = tag_inst.toRef();
   7921             break :blk &tag_scope.base;
   7922         };
   7923 
   7924         const header_index: u32 = @intCast(payloads.items.len);
   7925         const body_len_index = if (is_multi_case) blk: {
   7926             if (case_node.toOptional() == underscore_case_node) {
   7927                 payloads.items[under_case_index] = header_index;
   7928                 if (special_prongs.hasOneAdditionalItem()) {
   7929                     try payloads.resize(gpa, header_index + 2); // item, body_len
   7930                     const maybe_item_node = case.ast.values[0];
   7931                     const item_node = if (maybe_item_node.toOptional() == underscore_node)
   7932                         case.ast.values[1]
   7933                     else
   7934                         maybe_item_node;
   7935                     const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item);
   7936                     payloads.items[header_index] = @intFromEnum(item_inst);
   7937                     break :blk header_index + 1;
   7938                 }
   7939             } else {
   7940                 payloads.items[multi_case_table + multi_case_index] = header_index;
   7941                 multi_case_index += 1;
   7942             }
   7943             try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len
   7944 
   7945             // items
   7946             var items_len: u32 = 0;
   7947             for (case.ast.values) |item_node| {
   7948                 if (item_node.toOptional() == underscore_node or
   7949                     tree.nodeTag(item_node) == .switch_range)
   7950                 {
   7951                     continue;
   7952                 }
   7953                 items_len += 1;
   7954 
   7955                 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item);
   7956                 try payloads.append(gpa, @intFromEnum(item_inst));
   7957             }
   7958 
   7959             // ranges
   7960             var ranges_len: u32 = 0;
   7961             for (case.ast.values) |range| {
   7962                 if (tree.nodeTag(range) != .switch_range) {
   7963                     continue;
   7964                 }
   7965                 ranges_len += 1;
   7966 
   7967                 const first_node, const last_node = tree.nodeData(range).node_and_node;
   7968                 const first = try comptimeExpr(parent_gz, scope, item_ri, first_node, .switch_item);
   7969                 const last = try comptimeExpr(parent_gz, scope, item_ri, last_node, .switch_item);
   7970                 try payloads.appendSlice(gpa, &[_]u32{
   7971                     @intFromEnum(first), @intFromEnum(last),
   7972                 });
   7973             }
   7974 
   7975             payloads.items[header_index] = items_len;
   7976             payloads.items[header_index + 1] = ranges_len;
   7977             break :blk header_index + 2;
   7978         } else if (case_node.toOptional() == else_case_node) blk: {
   7979             payloads.items[else_case_index] = header_index;
   7980             try payloads.resize(gpa, header_index + 1); // body_len
   7981             break :blk header_index;
   7982         } else if (case_node.toOptional() == underscore_case_node) blk: {
   7983             assert(!special_prongs.hasAdditionalItems());
   7984             payloads.items[under_case_index] = header_index;
   7985             try payloads.resize(gpa, header_index + 1); // body_len
   7986             break :blk header_index;
   7987         } else blk: {
   7988             payloads.items[scalar_case_table + scalar_case_index] = header_index;
   7989             scalar_case_index += 1;
   7990             try payloads.resize(gpa, header_index + 2); // item, body_len
   7991             const item_node = case.ast.values[0];
   7992             const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node, .switch_item);
   7993             payloads.items[header_index] = @intFromEnum(item_inst);
   7994             break :blk header_index + 1;
   7995         };
   7996 
   7997         {
   7998             // temporarily stack case_scope on parent_gz
   7999             case_scope.instructions_top = parent_gz.instructions.items.len;
   8000             defer case_scope.unstack();
   8001 
   8002             if (dbg_var_name != .empty) {
   8003                 try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst);
   8004             }
   8005             if (dbg_var_tag_name != .empty) {
   8006                 try case_scope.addDbgVar(.dbg_var_val, dbg_var_tag_name, dbg_var_tag_inst);
   8007             }
   8008             const target_expr_node = case.ast.target_expr;
   8009             const case_result = try fullBodyExpr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node, .allow_branch_hint);
   8010             try checkUsed(parent_gz, &case_scope.base, sub_scope);
   8011             if (!parent_gz.refIsNoReturn(case_result)) {
   8012                 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node);
   8013             }
   8014 
   8015             const case_slice = case_scope.instructionsSlice();
   8016             const extra_insts: []const Zir.Inst.Index = if (has_tag_capture) &.{ switch_block, tag_inst } else &.{switch_block};
   8017             const body_len = astgen.countBodyLenAfterFixupsExtraRefs(case_slice, extra_insts);
   8018             try payloads.ensureUnusedCapacity(gpa, body_len);
   8019             payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
   8020                 .body_len = @intCast(body_len),
   8021                 .capture = capture,
   8022                 .is_inline = case.inline_token != null,
   8023                 .has_tag_capture = has_tag_capture,
   8024             });
   8025             appendBodyWithFixupsExtraRefsArrayList(astgen, payloads, case_slice, extra_insts);
   8026         }
   8027     }
   8028 
   8029     if (switch_full.label_token) |label_token| if (!block_scope.label.?.used) {
   8030         try astgen.appendErrorTok(label_token, "unused switch label", .{});
   8031     };
   8032 
   8033     // Now that the item expressions are generated we can add this.
   8034     try parent_gz.instructions.append(gpa, switch_block);
   8035 
   8036     try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).@"struct".fields.len +
   8037         @intFromBool(multi_cases_len != 0) +
   8038         @intFromBool(any_has_tag_capture) +
   8039         payloads.items.len - scratch_top);
   8040 
   8041     const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
   8042         .operand = raw_operand,
   8043         .bits = Zir.Inst.SwitchBlock.Bits{
   8044             .has_multi_cases = multi_cases_len != 0,
   8045             .special_prongs = special_prongs,
   8046             .any_has_tag_capture = any_has_tag_capture,
   8047             .any_non_inline_capture = any_non_inline_capture,
   8048             .has_continue = switch_full.label_token != null and block_scope.label.?.used_for_continue,
   8049             .scalar_cases_len = @intCast(scalar_cases_len),
   8050         },
   8051     });
   8052 
   8053     if (multi_cases_len != 0) {
   8054         astgen.extra.appendAssumeCapacity(multi_cases_len);
   8055     }
   8056 
   8057     if (any_has_tag_capture) {
   8058         astgen.extra.appendAssumeCapacity(@intFromEnum(tag_inst));
   8059     }
   8060 
   8061     const zir_datas = astgen.instructions.items(.data);
   8062     zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;
   8063 
   8064     if (has_else) {
   8065         const start_index = payloads.items[else_case_index];
   8066         var end_index = start_index + 1;
   8067         const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[start_index]);
   8068         end_index += prong_info.body_len;
   8069         astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
   8070     }
   8071     if (has_under) {
   8072         const start_index = payloads.items[under_case_index];
   8073         var body_len_index = start_index;
   8074         var end_index = start_index;
   8075         switch (underscore_additional_items) {
   8076             .none => {
   8077                 end_index += 1;
   8078             },
   8079             .one => {
   8080                 body_len_index += 1;
   8081                 end_index += 2;
   8082             },
   8083             .many => {
   8084                 body_len_index += 2;
   8085                 const items_len = payloads.items[start_index];
   8086                 const ranges_len = payloads.items[start_index + 1];
   8087                 end_index += 3 + items_len + 2 * ranges_len;
   8088             },
   8089         }
   8090         const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
   8091         end_index += prong_info.body_len;
   8092         astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
   8093     }
   8094     for (payloads.items[scalar_case_table..case_table_end], 0..) |start_index, i| {
   8095         var body_len_index = start_index;
   8096         var end_index = start_index;
   8097         const table_index = scalar_case_table + i;
   8098         if (table_index < multi_case_table) {
   8099             body_len_index += 1;
   8100             end_index += 2;
   8101         } else {
   8102             body_len_index += 2;
   8103             const items_len = payloads.items[start_index];
   8104             const ranges_len = payloads.items[start_index + 1];
   8105             end_index += 3 + items_len + 2 * ranges_len;
   8106         }
   8107         const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
   8108         end_index += prong_info.body_len;
   8109         astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
   8110     }
   8111 
   8112     if (need_result_rvalue) {
   8113         return rvalue(parent_gz, ri, switch_block.toRef(), node);
   8114     } else {
   8115         return switch_block.toRef();
   8116     }
   8117 }
   8118 
   8119 fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   8120     const astgen = gz.astgen;
   8121     const tree = astgen.tree;
   8122 
   8123     if (astgen.fn_block == null) {
   8124         return astgen.failNode(node, "'return' outside function scope", .{});
   8125     }
   8126 
   8127     if (gz.any_defer_node.unwrap()) |any_defer_node| {
   8128         return astgen.failNodeNotes(node, "cannot return from defer expression", .{}, &.{
   8129             try astgen.errNoteNode(
   8130                 any_defer_node,
   8131                 "defer expression here",
   8132                 .{},
   8133             ),
   8134         });
   8135     }
   8136 
   8137     // Ensure debug line/column information is emitted for this return expression.
   8138     // Then we will save the line/column so that we can emit another one that goes
   8139     // "backwards" because we want to evaluate the operand, but then put the debug
   8140     // info back at the return keyword for error return tracing.
   8141     if (!gz.is_comptime) {
   8142         try emitDbgNode(gz, node);
   8143     }
   8144     const ret_lc: LineColumn = .{ astgen.source_line - gz.decl_line, astgen.source_column };
   8145 
   8146     const defer_outer = &astgen.fn_block.?.base;
   8147 
   8148     const operand_node = tree.nodeData(node).opt_node.unwrap() orelse {
   8149         // Returning a void value; skip error defers.
   8150         try genDefers(gz, defer_outer, scope, .normal_only);
   8151 
   8152         // As our last action before the return, "pop" the error trace if needed
   8153         _ = try gz.addRestoreErrRetIndex(.ret, .always, node);
   8154 
   8155         _ = try gz.addUnNode(.ret_node, .void_value, node);
   8156         return Zir.Inst.Ref.unreachable_value;
   8157     };
   8158 
   8159     if (tree.nodeTag(operand_node) == .error_value) {
   8160         // Hot path for `return error.Foo`. This bypasses result location logic as well as logic
   8161         // for detecting whether to add something to the function's inferred error set.
   8162         const ident_token = tree.nodeMainToken(operand_node) + 2;
   8163         const err_name_str_index = try astgen.identAsString(ident_token);
   8164         const defer_counts = countDefers(defer_outer, scope);
   8165         if (!defer_counts.need_err_code) {
   8166             try genDefers(gz, defer_outer, scope, .both_sans_err);
   8167             try emitDbgStmt(gz, ret_lc);
   8168             _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token);
   8169             return Zir.Inst.Ref.unreachable_value;
   8170         }
   8171         const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token);
   8172         try genDefers(gz, defer_outer, scope, .{ .both = err_code });
   8173         try emitDbgStmt(gz, ret_lc);
   8174         _ = try gz.addUnNode(.ret_node, err_code, node);
   8175         return Zir.Inst.Ref.unreachable_value;
   8176     }
   8177 
   8178     const ri: ResultInfo = if (astgen.nodes_need_rl.contains(node)) .{
   8179         .rl = .{ .ptr = .{ .inst = try gz.addNode(.ret_ptr, node) } },
   8180         .ctx = .@"return",
   8181     } else .{
   8182         .rl = .{ .coerced_ty = astgen.fn_ret_ty },
   8183         .ctx = .@"return",
   8184     };
   8185     const operand: Zir.Inst.Ref = try nameStratExpr(gz, scope, ri, operand_node, .func) orelse
   8186         try reachableExpr(gz, scope, ri, operand_node, node);
   8187 
   8188     switch (nodeMayEvalToError(tree, operand_node)) {
   8189         .never => {
   8190             // Returning a value that cannot be an error; skip error defers.
   8191             try genDefers(gz, defer_outer, scope, .normal_only);
   8192 
   8193             // As our last action before the return, "pop" the error trace if needed
   8194             _ = try gz.addRestoreErrRetIndex(.ret, .always, node);
   8195 
   8196             try emitDbgStmt(gz, ret_lc);
   8197             try gz.addRet(ri, operand, node);
   8198             return Zir.Inst.Ref.unreachable_value;
   8199         },
   8200         .always => {
   8201             // Value is always an error. Emit both error defers and regular defers.
   8202             const err_code = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
   8203             try genDefers(gz, defer_outer, scope, .{ .both = err_code });
   8204             try emitDbgStmt(gz, ret_lc);
   8205             try gz.addRet(ri, operand, node);
   8206             return Zir.Inst.Ref.unreachable_value;
   8207         },
   8208         .maybe => {
   8209             const defer_counts = countDefers(defer_outer, scope);
   8210             if (!defer_counts.have_err) {
   8211                 // Only regular defers; no branch needed.
   8212                 try genDefers(gz, defer_outer, scope, .normal_only);
   8213                 try emitDbgStmt(gz, ret_lc);
   8214 
   8215                 // As our last action before the return, "pop" the error trace if needed
   8216                 const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
   8217                 _ = try gz.addRestoreErrRetIndex(.ret, .{ .if_non_error = result }, node);
   8218 
   8219                 try gz.addRet(ri, operand, node);
   8220                 return Zir.Inst.Ref.unreachable_value;
   8221             }
   8222 
   8223             // Emit conditional branch for generating errdefers.
   8224             const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
   8225             const is_non_err = try gz.addUnNode(.ret_is_non_err, result, node);
   8226             const condbr = try gz.addCondBr(.condbr, node);
   8227 
   8228             var then_scope = gz.makeSubBlock(scope);
   8229             defer then_scope.unstack();
   8230 
   8231             try genDefers(&then_scope, defer_outer, scope, .normal_only);
   8232 
   8233             // As our last action before the return, "pop" the error trace if needed
   8234             _ = try then_scope.addRestoreErrRetIndex(.ret, .always, node);
   8235 
   8236             try emitDbgStmt(&then_scope, ret_lc);
   8237             try then_scope.addRet(ri, operand, node);
   8238 
   8239             var else_scope = gz.makeSubBlock(scope);
   8240             defer else_scope.unstack();
   8241 
   8242             const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{
   8243                 .both = try else_scope.addUnNode(.err_union_code, result, node),
   8244             };
   8245             try genDefers(&else_scope, defer_outer, scope, which_ones);
   8246             try emitDbgStmt(&else_scope, ret_lc);
   8247             try else_scope.addRet(ri, operand, node);
   8248 
   8249             try setCondBrPayload(condbr, is_non_err, &then_scope, &else_scope);
   8250 
   8251             return Zir.Inst.Ref.unreachable_value;
   8252         },
   8253     }
   8254 }
   8255 
   8256 /// Parses the string `buf` as a base 10 integer of type `u16`.
   8257 ///
   8258 /// Unlike std.fmt.parseInt, does not allow the '_' character in `buf`.
   8259 fn parseBitCount(buf: []const u8) std.fmt.ParseIntError!u16 {
   8260     if (buf.len == 0) return error.InvalidCharacter;
   8261 
   8262     var x: u16 = 0;
   8263 
   8264     for (buf) |c| {
   8265         const digit = switch (c) {
   8266             '0'...'9' => c - '0',
   8267             else => return error.InvalidCharacter,
   8268         };
   8269 
   8270         if (x != 0) x = try std.math.mul(u16, x, 10);
   8271         x = try std.math.add(u16, x, digit);
   8272     }
   8273 
   8274     return x;
   8275 }
   8276 
   8277 const ComptimeBlockInfo = struct {
   8278     src_node: Ast.Node.Index,
   8279     reason: std.zig.SimpleComptimeReason,
   8280 };
   8281 
   8282 fn identifier(
   8283     gz: *GenZir,
   8284     scope: *Scope,
   8285     ri: ResultInfo,
   8286     ident: Ast.Node.Index,
   8287     force_comptime: ?ComptimeBlockInfo,
   8288 ) InnerError!Zir.Inst.Ref {
   8289     const astgen = gz.astgen;
   8290     const tree = astgen.tree;
   8291 
   8292     const ident_token = tree.nodeMainToken(ident);
   8293     const ident_name_raw = tree.tokenSlice(ident_token);
   8294     if (mem.eql(u8, ident_name_raw, "_")) {
   8295         return astgen.failNode(ident, "'_' used as an identifier without @\"_\" syntax", .{});
   8296     }
   8297 
   8298     // if not @"" syntax, just use raw token slice
   8299     if (ident_name_raw[0] != '@') {
   8300         if (primitive_instrs.get(ident_name_raw)) |zir_const_ref| {
   8301             return rvalue(gz, ri, zir_const_ref, ident);
   8302         }
   8303 
   8304         if (ident_name_raw.len >= 2) integer: {
   8305             // Keep in sync with logic in `comptimeExpr2`.
   8306             const first_c = ident_name_raw[0];
   8307             if (first_c == 'i' or first_c == 'u') {
   8308                 const signedness: std.builtin.Signedness = switch (first_c == 'i') {
   8309                     true => .signed,
   8310                     false => .unsigned,
   8311                 };
   8312                 if (ident_name_raw.len >= 3 and ident_name_raw[1] == '0') {
   8313                     return astgen.failNode(
   8314                         ident,
   8315                         "primitive integer type '{s}' has leading zero",
   8316                         .{ident_name_raw},
   8317                     );
   8318                 }
   8319                 const bit_count = parseBitCount(ident_name_raw[1..]) catch |err| switch (err) {
   8320                     error.Overflow => return astgen.failNode(
   8321                         ident,
   8322                         "primitive integer type '{s}' exceeds maximum bit width of 65535",
   8323                         .{ident_name_raw},
   8324                     ),
   8325                     error.InvalidCharacter => break :integer,
   8326                 };
   8327                 const result = try gz.add(.{
   8328                     .tag = .int_type,
   8329                     .data = .{ .int_type = .{
   8330                         .src_node = gz.nodeIndexToRelative(ident),
   8331                         .signedness = signedness,
   8332                         .bit_count = bit_count,
   8333                     } },
   8334                 });
   8335                 return rvalue(gz, ri, result, ident);
   8336             }
   8337         }
   8338     }
   8339 
   8340     // Local variables, including function parameters, and container-level declarations.
   8341 
   8342     if (force_comptime) |fc| {
   8343         // Mirrors the logic at the end of `comptimeExpr2`.
   8344         const block_inst = try gz.makeBlockInst(.block_comptime, fc.src_node);
   8345 
   8346         var comptime_gz = gz.makeSubBlock(scope);
   8347         comptime_gz.is_comptime = true;
   8348         defer comptime_gz.unstack();
   8349 
   8350         const sub_ri: ResultInfo = .{
   8351             .ctx = ri.ctx,
   8352             .rl = .none, // no point providing a result type, it won't change anything
   8353         };
   8354         const block_result = try localVarRef(&comptime_gz, scope, sub_ri, ident, ident_token);
   8355         assert(!comptime_gz.endsWithNoReturn());
   8356         _ = try comptime_gz.addBreak(.break_inline, block_inst, block_result);
   8357 
   8358         try comptime_gz.setBlockComptimeBody(block_inst, fc.reason);
   8359         try gz.instructions.append(astgen.gpa, block_inst);
   8360 
   8361         return rvalue(gz, ri, block_inst.toRef(), fc.src_node);
   8362     } else {
   8363         return localVarRef(gz, scope, ri, ident, ident_token);
   8364     }
   8365 }
   8366 
   8367 fn localVarRef(
   8368     gz: *GenZir,
   8369     scope: *Scope,
   8370     ri: ResultInfo,
   8371     ident: Ast.Node.Index,
   8372     ident_token: Ast.TokenIndex,
   8373 ) InnerError!Zir.Inst.Ref {
   8374     const astgen = gz.astgen;
   8375     const name_str_index = try astgen.identAsString(ident_token);
   8376     var s = scope;
   8377     var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
   8378     var found_needs_tunnel: bool = undefined; // defined when `found_already != null`
   8379     var found_namespaces_out: u32 = undefined; // defined when `found_already != null`
   8380 
   8381     // The number of namespaces above `gz` we currently are
   8382     var num_namespaces_out: u32 = 0;
   8383     // defined by `num_namespaces_out != 0`
   8384     var capturing_namespace: *Scope.Namespace = undefined;
   8385 
   8386     while (true) switch (s.tag) {
   8387         .local_val => {
   8388             const local_val = s.cast(Scope.LocalVal).?;
   8389 
   8390             if (local_val.name == name_str_index) {
   8391                 // Locals cannot shadow anything, so we do not need to look for ambiguous
   8392                 // references in this case.
   8393                 if (ri.rl == .discard and ri.ctx == .assignment) {
   8394                     local_val.discarded = .fromToken(ident_token);
   8395                 } else {
   8396                     local_val.used = .fromToken(ident_token);
   8397                 }
   8398 
   8399                 if (local_val.is_used_or_discarded) |ptr| ptr.* = true;
   8400 
   8401                 const value_inst = if (num_namespaces_out != 0) try tunnelThroughClosure(
   8402                     gz,
   8403                     ident,
   8404                     num_namespaces_out,
   8405                     .{ .ref = local_val.inst },
   8406                     .{ .token = local_val.token_src },
   8407                     name_str_index,
   8408                 ) else local_val.inst;
   8409 
   8410                 return rvalueNoCoercePreRef(gz, ri, value_inst, ident);
   8411             }
   8412             s = local_val.parent;
   8413         },
   8414         .local_ptr => {
   8415             const local_ptr = s.cast(Scope.LocalPtr).?;
   8416             if (local_ptr.name == name_str_index) {
   8417                 if (ri.rl == .discard and ri.ctx == .assignment) {
   8418                     local_ptr.discarded = .fromToken(ident_token);
   8419                 } else {
   8420                     local_ptr.used = .fromToken(ident_token);
   8421                 }
   8422 
   8423                 // Can't close over a runtime variable
   8424                 if (num_namespaces_out != 0 and !local_ptr.maybe_comptime and !gz.is_typeof) {
   8425                     const ident_name = try astgen.identifierTokenString(ident_token);
   8426                     return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{
   8427                         try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}),
   8428                         try astgen.errNoteNode(capturing_namespace.node, "crosses namespace boundary here", .{}),
   8429                     });
   8430                 }
   8431 
   8432                 switch (ri.rl) {
   8433                     .ref, .ref_coerced_ty => {
   8434                         const ptr_inst = if (num_namespaces_out != 0) try tunnelThroughClosure(
   8435                             gz,
   8436                             ident,
   8437                             num_namespaces_out,
   8438                             .{ .ref = local_ptr.ptr },
   8439                             .{ .token = local_ptr.token_src },
   8440                             name_str_index,
   8441                         ) else local_ptr.ptr;
   8442                         local_ptr.used_as_lvalue = true;
   8443                         return ptr_inst;
   8444                     },
   8445                     else => {
   8446                         const val_inst = if (num_namespaces_out != 0) try tunnelThroughClosure(
   8447                             gz,
   8448                             ident,
   8449                             num_namespaces_out,
   8450                             .{ .ref_load = local_ptr.ptr },
   8451                             .{ .token = local_ptr.token_src },
   8452                             name_str_index,
   8453                         ) else try gz.addUnNode(.load, local_ptr.ptr, ident);
   8454                         return rvalueNoCoercePreRef(gz, ri, val_inst, ident);
   8455                     },
   8456                 }
   8457             }
   8458             s = local_ptr.parent;
   8459         },
   8460         .gen_zir => s = s.cast(GenZir).?.parent,
   8461         .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
   8462         .namespace => {
   8463             const ns = s.cast(Scope.Namespace).?;
   8464             if (ns.decls.get(name_str_index)) |i| {
   8465                 if (found_already) |f| {
   8466                     return astgen.failNodeNotes(ident, "ambiguous reference", .{}, &.{
   8467                         try astgen.errNoteNode(f, "declared here", .{}),
   8468                         try astgen.errNoteNode(i, "also declared here", .{}),
   8469                     });
   8470                 }
   8471                 // We found a match but must continue looking for ambiguous references to decls.
   8472                 found_already = i;
   8473                 found_needs_tunnel = ns.maybe_generic;
   8474                 found_namespaces_out = num_namespaces_out;
   8475             }
   8476             num_namespaces_out += 1;
   8477             capturing_namespace = ns;
   8478             s = ns.parent;
   8479         },
   8480         .top => break,
   8481     };
   8482     if (found_already == null) {
   8483         const ident_name = try astgen.identifierTokenString(ident_token);
   8484         return astgen.failNode(ident, "use of undeclared identifier '{s}'", .{ident_name});
   8485     }
   8486 
   8487     // Decl references happen by name rather than ZIR index so that when unrelated
   8488     // decls are modified, ZIR code containing references to them can be unmodified.
   8489 
   8490     if (found_namespaces_out > 0 and found_needs_tunnel) {
   8491         switch (ri.rl) {
   8492             .ref, .ref_coerced_ty => return tunnelThroughClosure(
   8493                 gz,
   8494                 ident,
   8495                 found_namespaces_out,
   8496                 .{ .decl_ref = name_str_index },
   8497                 .{ .node = found_already.? },
   8498                 name_str_index,
   8499             ),
   8500             else => {
   8501                 const result = try tunnelThroughClosure(
   8502                     gz,
   8503                     ident,
   8504                     found_namespaces_out,
   8505                     .{ .decl_val = name_str_index },
   8506                     .{ .node = found_already.? },
   8507                     name_str_index,
   8508                 );
   8509                 return rvalueNoCoercePreRef(gz, ri, result, ident);
   8510             },
   8511         }
   8512     }
   8513 
   8514     switch (ri.rl) {
   8515         .ref, .ref_coerced_ty => return gz.addStrTok(.decl_ref, name_str_index, ident_token),
   8516         else => {
   8517             const result = try gz.addStrTok(.decl_val, name_str_index, ident_token);
   8518             return rvalueNoCoercePreRef(gz, ri, result, ident);
   8519         },
   8520     }
   8521 }
   8522 
   8523 /// Access a ZIR instruction through closure. May tunnel through arbitrarily
   8524 /// many namespaces, adding closure captures as required.
   8525 /// Returns the index of the `closure_get` instruction added to `gz`.
   8526 fn tunnelThroughClosure(
   8527     gz: *GenZir,
   8528     /// The node which references the value to be captured.
   8529     inner_ref_node: Ast.Node.Index,
   8530     /// The number of namespaces being tunnelled through. At least 1.
   8531     num_tunnels: u32,
   8532     /// The value being captured.
   8533     value: union(enum) {
   8534         ref: Zir.Inst.Ref,
   8535         ref_load: Zir.Inst.Ref,
   8536         decl_val: Zir.NullTerminatedString,
   8537         decl_ref: Zir.NullTerminatedString,
   8538     },
   8539     /// The location of the value's declaration.
   8540     decl_src: union(enum) {
   8541         token: Ast.TokenIndex,
   8542         node: Ast.Node.Index,
   8543     },
   8544     name_str_index: Zir.NullTerminatedString,
   8545 ) !Zir.Inst.Ref {
   8546     switch (value) {
   8547         .ref => |v| if (v.toIndex() == null) return v, // trivial value; do not need tunnel
   8548         .ref_load => |v| assert(v.toIndex() != null), // there are no constant pointer refs
   8549         .decl_val, .decl_ref => {},
   8550     }
   8551 
   8552     const astgen = gz.astgen;
   8553     const gpa = astgen.gpa;
   8554 
   8555     // Otherwise we need a tunnel. First, figure out the path of namespaces we
   8556     // are tunneling through. This is usually only going to be one or two, so
   8557     // use an SFBA to optimize for the common case.
   8558     var sfba = std.heap.stackFallback(@sizeOf(usize) * 2, astgen.arena);
   8559     var intermediate_tunnels = try sfba.get().alloc(*Scope.Namespace, num_tunnels - 1);
   8560 
   8561     const root_ns = ns: {
   8562         var i: usize = num_tunnels - 1;
   8563         var scope: *Scope = gz.parent;
   8564         while (i > 0) {
   8565             if (scope.cast(Scope.Namespace)) |mid_ns| {
   8566                 i -= 1;
   8567                 intermediate_tunnels[i] = mid_ns;
   8568             }
   8569             scope = scope.parent().?;
   8570         }
   8571         while (true) {
   8572             if (scope.cast(Scope.Namespace)) |ns| break :ns ns;
   8573             scope = scope.parent().?;
   8574         }
   8575     };
   8576 
   8577     // Now that we know the scopes we're tunneling through, begin adding
   8578     // captures as required, starting with the outermost namespace.
   8579     const root_capture: Zir.Inst.Capture = .wrap(switch (value) {
   8580         .ref => |v| .{ .instruction = v.toIndex().? },
   8581         .ref_load => |v| .{ .instruction_load = v.toIndex().? },
   8582         .decl_val => |str| .{ .decl_val = str },
   8583         .decl_ref => |str| .{ .decl_ref = str },
   8584     });
   8585 
   8586     const root_gop = try root_ns.captures.getOrPut(gpa, root_capture);
   8587     root_gop.value_ptr.* = name_str_index;
   8588     var cur_capture_index = std.math.cast(u16, root_gop.index) orelse return astgen.failNodeNotes(
   8589         root_ns.node,
   8590         "this compiler implementation only supports up to 65536 captures per namespace",
   8591         .{},
   8592         &.{
   8593             switch (decl_src) {
   8594                 .token => |t| try astgen.errNoteTok(t, "captured value here", .{}),
   8595                 .node => |n| try astgen.errNoteNode(n, "captured value here", .{}),
   8596             },
   8597             try astgen.errNoteNode(inner_ref_node, "value used here", .{}),
   8598         },
   8599     );
   8600 
   8601     for (intermediate_tunnels) |tunnel_ns| {
   8602         const tunnel_gop = try tunnel_ns.captures.getOrPut(gpa, .wrap(.{ .nested = cur_capture_index }));
   8603         tunnel_gop.value_ptr.* = name_str_index;
   8604         cur_capture_index = std.math.cast(u16, tunnel_gop.index) orelse return astgen.failNodeNotes(
   8605             tunnel_ns.node,
   8606             "this compiler implementation only supports up to 65536 captures per namespace",
   8607             .{},
   8608             &.{
   8609                 switch (decl_src) {
   8610                     .token => |t| try astgen.errNoteTok(t, "captured value here", .{}),
   8611                     .node => |n| try astgen.errNoteNode(n, "captured value here", .{}),
   8612                 },
   8613                 try astgen.errNoteNode(inner_ref_node, "value used here", .{}),
   8614             },
   8615         );
   8616     }
   8617 
   8618     // Incorporate the capture index into the source hash, so that changes in
   8619     // the order of captures cause suitable re-analysis.
   8620     astgen.src_hasher.update(std.mem.asBytes(&cur_capture_index));
   8621 
   8622     // Add an instruction to get the value from the closure.
   8623     return gz.addExtendedNodeSmall(.closure_get, inner_ref_node, cur_capture_index);
   8624 }
   8625 
   8626 fn stringLiteral(
   8627     gz: *GenZir,
   8628     ri: ResultInfo,
   8629     node: Ast.Node.Index,
   8630 ) InnerError!Zir.Inst.Ref {
   8631     const astgen = gz.astgen;
   8632     const tree = astgen.tree;
   8633     const str_lit_token = tree.nodeMainToken(node);
   8634     const str = try astgen.strLitAsString(str_lit_token);
   8635     const result = try gz.add(.{
   8636         .tag = .str,
   8637         .data = .{ .str = .{
   8638             .start = str.index,
   8639             .len = str.len,
   8640         } },
   8641     });
   8642     return rvalue(gz, ri, result, node);
   8643 }
   8644 
   8645 fn multilineStringLiteral(
   8646     gz: *GenZir,
   8647     ri: ResultInfo,
   8648     node: Ast.Node.Index,
   8649 ) InnerError!Zir.Inst.Ref {
   8650     const astgen = gz.astgen;
   8651     const str = try astgen.strLitNodeAsString(node);
   8652     const result = try gz.add(.{
   8653         .tag = .str,
   8654         .data = .{ .str = .{
   8655             .start = str.index,
   8656             .len = str.len,
   8657         } },
   8658     });
   8659     return rvalue(gz, ri, result, node);
   8660 }
   8661 
   8662 fn charLiteral(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   8663     const astgen = gz.astgen;
   8664     const tree = astgen.tree;
   8665     const main_token = tree.nodeMainToken(node);
   8666     const slice = tree.tokenSlice(main_token);
   8667 
   8668     switch (std.zig.parseCharLiteral(slice)) {
   8669         .success => |codepoint| {
   8670             const result = try gz.addInt(codepoint);
   8671             return rvalue(gz, ri, result, node);
   8672         },
   8673         .failure => |err| return astgen.failWithStrLitError(err, main_token, slice, 0),
   8674     }
   8675 }
   8676 
   8677 const Sign = enum { negative, positive };
   8678 
   8679 fn numberLiteral(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index, source_node: Ast.Node.Index, sign: Sign) InnerError!Zir.Inst.Ref {
   8680     const astgen = gz.astgen;
   8681     const tree = astgen.tree;
   8682     const num_token = tree.nodeMainToken(node);
   8683     const bytes = tree.tokenSlice(num_token);
   8684 
   8685     const result: Zir.Inst.Ref = switch (std.zig.parseNumberLiteral(bytes)) {
   8686         .int => |num| switch (num) {
   8687             0 => if (sign == .positive) .zero else return astgen.failTokNotes(
   8688                 num_token,
   8689                 "integer literal '-0' is ambiguous",
   8690                 .{},
   8691                 &.{
   8692                     try astgen.errNoteTok(num_token, "use '0' for an integer zero", .{}),
   8693                     try astgen.errNoteTok(num_token, "use '-0.0' for a floating-point signed zero", .{}),
   8694                 },
   8695             ),
   8696             1 => {
   8697                 // Handle the negation here!
   8698                 const result: Zir.Inst.Ref = switch (sign) {
   8699                     .positive => .one,
   8700                     .negative => .negative_one,
   8701                 };
   8702                 return rvalue(gz, ri, result, source_node);
   8703             },
   8704             else => try gz.addInt(num),
   8705         },
   8706         .big_int => |base| big: {
   8707             const gpa = astgen.gpa;
   8708             var big_int = try std.math.big.int.Managed.init(gpa);
   8709             defer big_int.deinit();
   8710             const prefix_offset: usize = if (base == .decimal) 0 else 2;
   8711             big_int.setString(@intFromEnum(base), bytes[prefix_offset..]) catch |err| switch (err) {
   8712                 error.InvalidCharacter => unreachable, // caught in `parseNumberLiteral`
   8713                 error.InvalidBase => unreachable, // we only pass 16, 8, 2, see above
   8714                 error.OutOfMemory => return error.OutOfMemory,
   8715             };
   8716 
   8717             const limbs = big_int.limbs[0..big_int.len()];
   8718             assert(big_int.isPositive());
   8719             break :big try gz.addIntBig(limbs);
   8720         },
   8721         .float => {
   8722             const unsigned_float_number = std.fmt.parseFloat(f128, bytes) catch |err| switch (err) {
   8723                 error.InvalidCharacter => unreachable, // validated by tokenizer
   8724             };
   8725             const float_number = switch (sign) {
   8726                 .negative => -unsigned_float_number,
   8727                 .positive => unsigned_float_number,
   8728             };
   8729             // If the value fits into a f64 without losing any precision, store it that way.
   8730             @setFloatMode(.strict);
   8731             const smaller_float: f64 = @floatCast(float_number);
   8732             const bigger_again: f128 = smaller_float;
   8733             if (bigger_again == float_number) {
   8734                 const result = try gz.addFloat(smaller_float);
   8735                 return rvalue(gz, ri, result, source_node);
   8736             }
   8737             // We need to use 128 bits. Break the float into 4 u32 values so we can
   8738             // put it into the `extra` array.
   8739             const int_bits: u128 = @bitCast(float_number);
   8740             const result = try gz.addPlNode(.float128, node, Zir.Inst.Float128{
   8741                 .piece0 = @truncate(int_bits),
   8742                 .piece1 = @truncate(int_bits >> 32),
   8743                 .piece2 = @truncate(int_bits >> 64),
   8744                 .piece3 = @truncate(int_bits >> 96),
   8745             });
   8746             return rvalue(gz, ri, result, source_node);
   8747         },
   8748         .failure => |err| return astgen.failWithNumberError(err, num_token, bytes),
   8749     };
   8750 
   8751     if (sign == .positive) {
   8752         return rvalue(gz, ri, result, source_node);
   8753     } else {
   8754         const negated = try gz.addUnNode(.negate, result, source_node);
   8755         return rvalue(gz, ri, negated, source_node);
   8756     }
   8757 }
   8758 
   8759 fn failWithNumberError(astgen: *AstGen, err: std.zig.number_literal.Error, token: Ast.TokenIndex, bytes: []const u8) InnerError {
   8760     const is_float = std.mem.indexOfScalar(u8, bytes, '.') != null;
   8761     switch (err) {
   8762         .leading_zero => if (is_float) {
   8763             return astgen.failTok(token, "number '{s}' has leading zero", .{bytes});
   8764         } else {
   8765             return astgen.failTokNotes(token, "number '{s}' has leading zero", .{bytes}, &.{
   8766                 try astgen.errNoteTok(token, "use '0o' prefix for octal literals", .{}),
   8767             });
   8768         },
   8769         .digit_after_base => return astgen.failTok(token, "expected a digit after base prefix", .{}),
   8770         .upper_case_base => |i| return astgen.failOff(token, @intCast(i), "base prefix must be lowercase", .{}),
   8771         .invalid_float_base => |i| return astgen.failOff(token, @intCast(i), "invalid base for float literal", .{}),
   8772         .repeated_underscore => |i| return astgen.failOff(token, @intCast(i), "repeated digit separator", .{}),
   8773         .invalid_underscore_after_special => |i| return astgen.failOff(token, @intCast(i), "expected digit before digit separator", .{}),
   8774         .invalid_digit => |info| return astgen.failOff(token, @intCast(info.i), "invalid digit '{c}' for {s} base", .{ bytes[info.i], @tagName(info.base) }),
   8775         .invalid_digit_exponent => |i| return astgen.failOff(token, @intCast(i), "invalid digit '{c}' in exponent", .{bytes[i]}),
   8776         .duplicate_exponent => |i| return astgen.failOff(token, @intCast(i), "duplicate exponent", .{}),
   8777         .exponent_after_underscore => |i| return astgen.failOff(token, @intCast(i), "expected digit before exponent", .{}),
   8778         .special_after_underscore => |i| return astgen.failOff(token, @intCast(i), "expected digit before '{c}'", .{bytes[i]}),
   8779         .trailing_special => |i| return astgen.failOff(token, @intCast(i), "expected digit after '{c}'", .{bytes[i - 1]}),
   8780         .trailing_underscore => |i| return astgen.failOff(token, @intCast(i), "trailing digit separator", .{}),
   8781         .duplicate_period => unreachable, // Validated by tokenizer
   8782         .invalid_character => unreachable, // Validated by tokenizer
   8783         .invalid_exponent_sign => |i| {
   8784             assert(bytes.len >= 2 and bytes[0] == '0' and bytes[1] == 'x'); // Validated by tokenizer
   8785             return astgen.failOff(token, @intCast(i), "sign '{c}' cannot follow digit '{c}' in hex base", .{ bytes[i], bytes[i - 1] });
   8786         },
   8787         .period_after_exponent => |i| return astgen.failOff(token, @intCast(i), "unexpected period after exponent", .{}),
   8788     }
   8789 }
   8790 
   8791 fn asmExpr(
   8792     gz: *GenZir,
   8793     scope: *Scope,
   8794     ri: ResultInfo,
   8795     node: Ast.Node.Index,
   8796     full: Ast.full.Asm,
   8797 ) InnerError!Zir.Inst.Ref {
   8798     const astgen = gz.astgen;
   8799     const tree = astgen.tree;
   8800 
   8801     const TagAndTmpl = struct { tag: Zir.Inst.Extended, tmpl: Zir.NullTerminatedString };
   8802     const tag_and_tmpl: TagAndTmpl = switch (tree.nodeTag(full.ast.template)) {
   8803         .string_literal => .{
   8804             .tag = .@"asm",
   8805             .tmpl = (try astgen.strLitAsString(tree.nodeMainToken(full.ast.template))).index,
   8806         },
   8807         .multiline_string_literal => .{
   8808             .tag = .@"asm",
   8809             .tmpl = (try astgen.strLitNodeAsString(full.ast.template)).index,
   8810         },
   8811         else => .{
   8812             .tag = .asm_expr,
   8813             .tmpl = @enumFromInt(@intFromEnum(try comptimeExpr(gz, scope, .{ .rl = .none }, full.ast.template, .inline_assembly_code))),
   8814         },
   8815     };
   8816 
   8817     // See https://github.com/ziglang/zig/issues/215 and related issues discussing
   8818     // possible inline assembly improvements. Until then here is status quo AstGen
   8819     // for assembly syntax. It's used by std lib crypto aesni.zig.
   8820     const is_container_asm = astgen.fn_block == null;
   8821     if (is_container_asm) {
   8822         if (full.volatile_token) |t|
   8823             return astgen.failTok(t, "volatile is meaningless on global assembly", .{});
   8824         if (full.outputs.len != 0 or full.inputs.len != 0 or full.ast.clobbers != .none)
   8825             return astgen.failNode(node, "global assembly cannot have inputs, outputs, or clobbers", .{});
   8826     } else {
   8827         if (full.outputs.len == 0 and full.volatile_token == null) {
   8828             return astgen.failNode(node, "assembly expression with no output must be marked volatile", .{});
   8829         }
   8830     }
   8831     if (full.outputs.len >= 16) {
   8832         return astgen.failNode(full.outputs[16], "too many asm outputs", .{});
   8833     }
   8834     var outputs_buffer: [15]Zir.Inst.Asm.Output = undefined;
   8835     const outputs = outputs_buffer[0..full.outputs.len];
   8836 
   8837     var output_type_bits: u32 = 0;
   8838 
   8839     for (full.outputs, 0..) |output_node, i| {
   8840         const symbolic_name = tree.nodeMainToken(output_node);
   8841         const name = try astgen.identAsString(symbolic_name);
   8842         const constraint_token = symbolic_name + 2;
   8843         const constraint = (try astgen.strLitAsString(constraint_token)).index;
   8844         const has_arrow = tree.tokenTag(symbolic_name + 4) == .arrow;
   8845         if (has_arrow) {
   8846             if (output_type_bits != 0) {
   8847                 return astgen.failNode(output_node, "inline assembly allows up to one output value", .{});
   8848             }
   8849             output_type_bits |= @as(u32, 1) << @intCast(i);
   8850             const out_type_node = tree.nodeData(output_node).opt_node_and_token[0].unwrap().?;
   8851             const out_type_inst = try typeExpr(gz, scope, out_type_node);
   8852             outputs[i] = .{
   8853                 .name = name,
   8854                 .constraint = constraint,
   8855                 .operand = out_type_inst,
   8856             };
   8857         } else {
   8858             const ident_token = symbolic_name + 4;
   8859             // TODO have a look at #215 and related issues and decide how to
   8860             // handle outputs. Do we want this to be identifiers?
   8861             // Or maybe we want to force this to be expressions with a pointer type.
   8862             outputs[i] = .{
   8863                 .name = name,
   8864                 .constraint = constraint,
   8865                 .operand = try localVarRef(gz, scope, .{ .rl = .ref }, node, ident_token),
   8866             };
   8867         }
   8868     }
   8869 
   8870     if (full.inputs.len >= 32) {
   8871         return astgen.failNode(full.inputs[32], "too many asm inputs", .{});
   8872     }
   8873     var inputs_buffer: [31]Zir.Inst.Asm.Input = undefined;
   8874     const inputs = inputs_buffer[0..full.inputs.len];
   8875 
   8876     for (full.inputs, 0..) |input_node, i| {
   8877         const symbolic_name = tree.nodeMainToken(input_node);
   8878         const name = try astgen.identAsString(symbolic_name);
   8879         const constraint_token = symbolic_name + 2;
   8880         const constraint = (try astgen.strLitAsString(constraint_token)).index;
   8881         const operand = try expr(gz, scope, .{ .rl = .none }, tree.nodeData(input_node).node_and_token[0]);
   8882         inputs[i] = .{
   8883             .name = name,
   8884             .constraint = constraint,
   8885             .operand = operand,
   8886         };
   8887     }
   8888 
   8889     const clobbers: Zir.Inst.Ref = if (full.ast.clobbers.unwrap()) |clobbers_node|
   8890         try comptimeExpr(gz, scope, .{ .rl = .{
   8891             .coerced_ty = try gz.addBuiltinValue(clobbers_node, .clobbers),
   8892         } }, clobbers_node, .clobber)
   8893     else
   8894         .none;
   8895 
   8896     const result = try gz.addAsm(.{
   8897         .tag = tag_and_tmpl.tag,
   8898         .node = node,
   8899         .asm_source = tag_and_tmpl.tmpl,
   8900         .is_volatile = full.volatile_token != null,
   8901         .output_type_bits = output_type_bits,
   8902         .outputs = outputs,
   8903         .inputs = inputs,
   8904         .clobbers = clobbers,
   8905     });
   8906     return rvalue(gz, ri, result, node);
   8907 }
   8908 
   8909 fn as(
   8910     gz: *GenZir,
   8911     scope: *Scope,
   8912     ri: ResultInfo,
   8913     node: Ast.Node.Index,
   8914     lhs: Ast.Node.Index,
   8915     rhs: Ast.Node.Index,
   8916 ) InnerError!Zir.Inst.Ref {
   8917     const dest_type = try typeExpr(gz, scope, lhs);
   8918     const result = try reachableExpr(gz, scope, .{ .rl = .{ .ty = dest_type } }, rhs, node);
   8919     return rvalue(gz, ri, result, node);
   8920 }
   8921 
   8922 fn unionInit(
   8923     gz: *GenZir,
   8924     scope: *Scope,
   8925     ri: ResultInfo,
   8926     node: Ast.Node.Index,
   8927     params: []const Ast.Node.Index,
   8928 ) InnerError!Zir.Inst.Ref {
   8929     const union_type = try typeExpr(gz, scope, params[0]);
   8930     const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .union_field_name);
   8931     const field_type = try gz.addPlNode(.field_type_ref, node, Zir.Inst.FieldTypeRef{
   8932         .container_type = union_type,
   8933         .field_name = field_name,
   8934     });
   8935     const init = try reachableExpr(gz, scope, .{ .rl = .{ .ty = field_type } }, params[2], node);
   8936     const result = try gz.addPlNode(.union_init, node, Zir.Inst.UnionInit{
   8937         .union_type = union_type,
   8938         .init = init,
   8939         .field_name = field_name,
   8940     });
   8941     return rvalue(gz, ri, result, node);
   8942 }
   8943 
   8944 fn bitCast(
   8945     gz: *GenZir,
   8946     scope: *Scope,
   8947     ri: ResultInfo,
   8948     node: Ast.Node.Index,
   8949     operand_node: Ast.Node.Index,
   8950 ) InnerError!Zir.Inst.Ref {
   8951     const dest_type = try ri.rl.resultTypeForCast(gz, node, "@bitCast");
   8952     const operand = try reachableExpr(gz, scope, .{ .rl = .none }, operand_node, node);
   8953     const result = try gz.addPlNode(.bitcast, node, Zir.Inst.Bin{
   8954         .lhs = dest_type,
   8955         .rhs = operand,
   8956     });
   8957     return rvalue(gz, ri, result, node);
   8958 }
   8959 
   8960 /// Handle one or more nested pointer cast builtins:
   8961 /// * @ptrCast
   8962 /// * @alignCast
   8963 /// * @addrSpaceCast
   8964 /// * @constCast
   8965 /// * @volatileCast
   8966 /// Any sequence of such builtins is treated as a single operation. This allowed
   8967 /// for sequences like `@ptrCast(@alignCast(ptr))` to work correctly despite the
   8968 /// intermediate result type being unknown.
   8969 fn ptrCast(
   8970     gz: *GenZir,
   8971     scope: *Scope,
   8972     ri: ResultInfo,
   8973     root_node: Ast.Node.Index,
   8974 ) InnerError!Zir.Inst.Ref {
   8975     const astgen = gz.astgen;
   8976     const tree = astgen.tree;
   8977 
   8978     const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?;
   8979     var flags: Zir.Inst.FullPtrCastFlags = .{};
   8980 
   8981     // Note that all pointer cast builtins have one parameter, so we only need
   8982     // to handle `builtin_call_two`.
   8983     var node = root_node;
   8984     while (true) {
   8985         switch (tree.nodeTag(node)) {
   8986             .builtin_call_two, .builtin_call_two_comma => {},
   8987             .grouped_expression => {
   8988                 // Handle the chaining even with redundant parentheses
   8989                 node = tree.nodeData(node).node_and_token[0];
   8990                 continue;
   8991             },
   8992             else => break,
   8993         }
   8994 
   8995         var buf: [2]Ast.Node.Index = undefined;
   8996         const args = tree.builtinCallParams(&buf, node).?;
   8997         std.debug.assert(args.len <= 2);
   8998 
   8999         if (args.len == 0) break; // 0 args
   9000 
   9001         const builtin_token = tree.nodeMainToken(node);
   9002         const builtin_name = tree.tokenSlice(builtin_token);
   9003         const info = BuiltinFn.list.get(builtin_name) orelse break;
   9004         if (args.len == 1) {
   9005             if (info.param_count != 1) break;
   9006 
   9007             switch (info.tag) {
   9008                 else => break,
   9009                 inline .ptr_cast,
   9010                 .align_cast,
   9011                 .addrspace_cast,
   9012                 .const_cast,
   9013                 .volatile_cast,
   9014                 => |tag| {
   9015                     if (@field(flags, @tagName(tag))) {
   9016                         return astgen.failNode(node, "redundant {s}", .{builtin_name});
   9017                     }
   9018                     @field(flags, @tagName(tag)) = true;
   9019                 },
   9020             }
   9021 
   9022             node = args[0];
   9023         } else {
   9024             std.debug.assert(args.len == 2);
   9025             if (info.param_count != 2) break;
   9026 
   9027             switch (info.tag) {
   9028                 else => break,
   9029                 .field_parent_ptr => {
   9030                     if (flags.ptr_cast) break;
   9031 
   9032                     const flags_int: FlagsInt = @bitCast(flags);
   9033                     const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node);
   9034                     const parent_ptr_type = try ri.rl.resultTypeForCast(gz, root_node, "@alignCast");
   9035                     const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, args[0], .field_name);
   9036                     const field_ptr = try expr(gz, scope, .{ .rl = .none }, args[1]);
   9037                     try emitDbgStmt(gz, cursor);
   9038                     const result = try gz.addExtendedPayloadSmall(.field_parent_ptr, flags_int, Zir.Inst.FieldParentPtr{
   9039                         .src_node = gz.nodeIndexToRelative(node),
   9040                         .parent_ptr_type = parent_ptr_type,
   9041                         .field_name = field_name,
   9042                         .field_ptr = field_ptr,
   9043                     });
   9044                     return rvalue(gz, ri, result, root_node);
   9045                 },
   9046             }
   9047         }
   9048     }
   9049 
   9050     const flags_int: FlagsInt = @bitCast(flags);
   9051     assert(flags_int != 0);
   9052 
   9053     const ptr_only: Zir.Inst.FullPtrCastFlags = .{ .ptr_cast = true };
   9054     if (flags_int == @as(FlagsInt, @bitCast(ptr_only))) {
   9055         // Special case: simpler representation
   9056         return typeCast(gz, scope, ri, root_node, node, .ptr_cast, "@ptrCast");
   9057     }
   9058 
   9059     const no_result_ty_flags: Zir.Inst.FullPtrCastFlags = .{
   9060         .const_cast = true,
   9061         .volatile_cast = true,
   9062     };
   9063     if ((flags_int & ~@as(FlagsInt, @bitCast(no_result_ty_flags))) == 0) {
   9064         // Result type not needed
   9065         const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node);
   9066         const operand = try expr(gz, scope, .{ .rl = .none }, node);
   9067         try emitDbgStmt(gz, cursor);
   9068         const result = try gz.addExtendedPayloadSmall(.ptr_cast_no_dest, flags_int, Zir.Inst.UnNode{
   9069             .node = gz.nodeIndexToRelative(root_node),
   9070             .operand = operand,
   9071         });
   9072         return rvalue(gz, ri, result, root_node);
   9073     }
   9074 
   9075     // Full cast including result type
   9076 
   9077     const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node);
   9078     const result_type = try ri.rl.resultTypeForCast(gz, root_node, flags.needResultTypeBuiltinName());
   9079     const operand = try expr(gz, scope, .{ .rl = .none }, node);
   9080     try emitDbgStmt(gz, cursor);
   9081     const result = try gz.addExtendedPayloadSmall(.ptr_cast_full, flags_int, Zir.Inst.BinNode{
   9082         .node = gz.nodeIndexToRelative(root_node),
   9083         .lhs = result_type,
   9084         .rhs = operand,
   9085     });
   9086     return rvalue(gz, ri, result, root_node);
   9087 }
   9088 
   9089 fn typeOf(
   9090     gz: *GenZir,
   9091     scope: *Scope,
   9092     ri: ResultInfo,
   9093     node: Ast.Node.Index,
   9094     args: []const Ast.Node.Index,
   9095 ) InnerError!Zir.Inst.Ref {
   9096     const astgen = gz.astgen;
   9097     if (args.len < 1) {
   9098         return astgen.failNode(node, "expected at least 1 argument, found 0", .{});
   9099     }
   9100     const gpa = astgen.gpa;
   9101     if (args.len == 1) {
   9102         const typeof_inst = try gz.makeBlockInst(.typeof_builtin, node);
   9103 
   9104         var typeof_scope = gz.makeSubBlock(scope);
   9105         typeof_scope.is_comptime = false;
   9106         typeof_scope.is_typeof = true;
   9107         typeof_scope.c_import = false;
   9108         defer typeof_scope.unstack();
   9109 
   9110         const ty_expr = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, args[0], node);
   9111         if (!gz.refIsNoReturn(ty_expr)) {
   9112             _ = try typeof_scope.addBreak(.break_inline, typeof_inst, ty_expr);
   9113         }
   9114         try typeof_scope.setBlockBody(typeof_inst);
   9115 
   9116         // typeof_scope unstacked now, can add new instructions to gz
   9117         try gz.instructions.append(gpa, typeof_inst);
   9118         return rvalue(gz, ri, typeof_inst.toRef(), node);
   9119     }
   9120     const payload_size: u32 = std.meta.fields(Zir.Inst.TypeOfPeer).len;
   9121     const payload_index = try reserveExtra(astgen, payload_size + args.len);
   9122     const args_index = payload_index + payload_size;
   9123 
   9124     const typeof_inst = try gz.addExtendedMultiOpPayloadIndex(.typeof_peer, payload_index, args.len);
   9125 
   9126     var typeof_scope = gz.makeSubBlock(scope);
   9127     typeof_scope.is_comptime = false;
   9128 
   9129     for (args, 0..) |arg, i| {
   9130         const param_ref = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, arg, node);
   9131         astgen.extra.items[args_index + i] = @intFromEnum(param_ref);
   9132     }
   9133     _ = try typeof_scope.addBreak(.break_inline, typeof_inst.toIndex().?, .void_value);
   9134 
   9135     const body = typeof_scope.instructionsSlice();
   9136     const body_len = astgen.countBodyLenAfterFixups(body);
   9137     astgen.setExtra(payload_index, Zir.Inst.TypeOfPeer{
   9138         .body_len = @intCast(body_len),
   9139         .body_index = @intCast(astgen.extra.items.len),
   9140         .src_node = gz.nodeIndexToRelative(node),
   9141     });
   9142     try astgen.extra.ensureUnusedCapacity(gpa, body_len);
   9143     astgen.appendBodyWithFixups(body);
   9144     typeof_scope.unstack();
   9145 
   9146     return rvalue(gz, ri, typeof_inst, node);
   9147 }
   9148 
   9149 fn minMax(
   9150     gz: *GenZir,
   9151     scope: *Scope,
   9152     ri: ResultInfo,
   9153     node: Ast.Node.Index,
   9154     args: []const Ast.Node.Index,
   9155     comptime op: enum { min, max },
   9156 ) InnerError!Zir.Inst.Ref {
   9157     const astgen = gz.astgen;
   9158     if (args.len < 2) {
   9159         return astgen.failNode(node, "expected at least 2 arguments, found {}", .{args.len});
   9160     }
   9161     if (args.len == 2) {
   9162         const tag: Zir.Inst.Tag = switch (op) {
   9163             .min => .min,
   9164             .max => .max,
   9165         };
   9166         const a = try expr(gz, scope, .{ .rl = .none }, args[0]);
   9167         const b = try expr(gz, scope, .{ .rl = .none }, args[1]);
   9168         const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   9169             .lhs = a,
   9170             .rhs = b,
   9171         });
   9172         return rvalue(gz, ri, result, node);
   9173     }
   9174     const payload_index = try addExtra(astgen, Zir.Inst.NodeMultiOp{
   9175         .src_node = gz.nodeIndexToRelative(node),
   9176     });
   9177     var extra_index = try reserveExtra(gz.astgen, args.len);
   9178     for (args) |arg| {
   9179         const arg_ref = try expr(gz, scope, .{ .rl = .none }, arg);
   9180         astgen.extra.items[extra_index] = @intFromEnum(arg_ref);
   9181         extra_index += 1;
   9182     }
   9183     const tag: Zir.Inst.Extended = switch (op) {
   9184         .min => .min_multi,
   9185         .max => .max_multi,
   9186     };
   9187     const result = try gz.addExtendedMultiOpPayloadIndex(tag, payload_index, args.len);
   9188     return rvalue(gz, ri, result, node);
   9189 }
   9190 
   9191 fn builtinCall(
   9192     gz: *GenZir,
   9193     scope: *Scope,
   9194     ri: ResultInfo,
   9195     node: Ast.Node.Index,
   9196     params: []const Ast.Node.Index,
   9197     allow_branch_hint: bool,
   9198 ) InnerError!Zir.Inst.Ref {
   9199     const astgen = gz.astgen;
   9200     const tree = astgen.tree;
   9201 
   9202     const builtin_token = tree.nodeMainToken(node);
   9203     const builtin_name = tree.tokenSlice(builtin_token);
   9204 
   9205     // We handle the different builtins manually because they have different semantics depending
   9206     // on the function. For example, `@as` and others participate in result location semantics,
   9207     // and `@cImport` creates a special scope that collects a .c source code text buffer.
   9208     // Also, some builtins have a variable number of parameters.
   9209 
   9210     const info = BuiltinFn.list.get(builtin_name) orelse {
   9211         return astgen.failNode(node, "invalid builtin function: '{s}'", .{
   9212             builtin_name,
   9213         });
   9214     };
   9215     if (info.param_count) |expected| {
   9216         if (expected != params.len) {
   9217             const s = if (expected == 1) "" else "s";
   9218             return astgen.failNode(node, "expected {d} argument{s}, found {d}", .{
   9219                 expected, s, params.len,
   9220             });
   9221         }
   9222     }
   9223 
   9224     // Check function scope-only builtins
   9225 
   9226     if (astgen.fn_block == null and info.illegal_outside_function)
   9227         return astgen.failNode(node, "'{s}' outside function scope", .{builtin_name});
   9228 
   9229     switch (info.tag) {
   9230         .branch_hint => {
   9231             if (!allow_branch_hint) {
   9232                 return astgen.failNode(node, "'@branchHint' must appear as the first statement in a function or conditional branch", .{});
   9233             }
   9234             const hint_ty = try gz.addBuiltinValue(node, .branch_hint);
   9235             const hint_val = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = hint_ty } }, params[0], .operand_branchHint);
   9236             _ = try gz.addExtendedPayload(.branch_hint, Zir.Inst.UnNode{
   9237                 .node = gz.nodeIndexToRelative(node),
   9238                 .operand = hint_val,
   9239             });
   9240             return rvalue(gz, ri, .void_value, node);
   9241         },
   9242         .import => {
   9243             const operand_node = params[0];
   9244 
   9245             if (tree.nodeTag(operand_node) != .string_literal) {
   9246                 // Spec reference: https://github.com/ziglang/zig/issues/2206
   9247                 return astgen.failNode(operand_node, "@import operand must be a string literal", .{});
   9248             }
   9249             const str_lit_token = tree.nodeMainToken(operand_node);
   9250             const str = try astgen.strLitAsString(str_lit_token);
   9251             const str_slice = astgen.string_bytes.items[@intFromEnum(str.index)..][0..str.len];
   9252             if (mem.indexOfScalar(u8, str_slice, 0) != null) {
   9253                 return astgen.failTok(str_lit_token, "import path cannot contain null bytes", .{});
   9254             } else if (str.len == 0) {
   9255                 return astgen.failTok(str_lit_token, "import path cannot be empty", .{});
   9256             }
   9257             const res_ty = try ri.rl.resultType(gz, node) orelse .none;
   9258             const payload_index = try addExtra(gz.astgen, Zir.Inst.Import{
   9259                 .res_ty = res_ty,
   9260                 .path = str.index,
   9261             });
   9262             const result = try gz.add(.{
   9263                 .tag = .import,
   9264                 .data = .{ .pl_tok = .{
   9265                     .src_tok = gz.tokenIndexToRelative(str_lit_token),
   9266                     .payload_index = payload_index,
   9267                 } },
   9268             });
   9269             const gop = try astgen.imports.getOrPut(astgen.gpa, str.index);
   9270             if (!gop.found_existing) {
   9271                 gop.value_ptr.* = str_lit_token;
   9272             }
   9273             return rvalue(gz, ri, result, node);
   9274         },
   9275         .compile_log => {
   9276             const payload_index = try addExtra(gz.astgen, Zir.Inst.NodeMultiOp{
   9277                 .src_node = gz.nodeIndexToRelative(node),
   9278             });
   9279             var extra_index = try reserveExtra(gz.astgen, params.len);
   9280             for (params) |param| {
   9281                 const param_ref = try expr(gz, scope, .{ .rl = .none }, param);
   9282                 astgen.extra.items[extra_index] = @intFromEnum(param_ref);
   9283                 extra_index += 1;
   9284             }
   9285             const result = try gz.addExtendedMultiOpPayloadIndex(.compile_log, payload_index, params.len);
   9286             return rvalue(gz, ri, result, node);
   9287         },
   9288         .field => {
   9289             if (ri.rl == .ref or ri.rl == .ref_coerced_ty) {
   9290                 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{
   9291                     .lhs = try expr(gz, scope, .{ .rl = .ref }, params[0]),
   9292                     .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name),
   9293                 });
   9294             }
   9295             const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{
   9296                 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
   9297                 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name),
   9298             });
   9299             return rvalue(gz, ri, result, node);
   9300         },
   9301         .FieldType => {
   9302             const ty_inst = try typeExpr(gz, scope, params[0]);
   9303             const name_inst = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1], .field_name);
   9304             const result = try gz.addPlNode(.field_type_ref, node, Zir.Inst.FieldTypeRef{
   9305                 .container_type = ty_inst,
   9306                 .field_name = name_inst,
   9307             });
   9308             return rvalue(gz, ri, result, node);
   9309         },
   9310 
   9311         // zig fmt: off
   9312         .as         => return as(       gz, scope, ri, node, params[0], params[1]),
   9313         .bit_cast   => return bitCast(  gz, scope, ri, node, params[0]),
   9314         .TypeOf     => return typeOf(   gz, scope, ri, node, params),
   9315         .union_init => return unionInit(gz, scope, ri, node, params),
   9316         .c_import   => return cImport(  gz, scope,     node, params[0]),
   9317         .min        => return minMax(   gz, scope, ri, node, params, .min),
   9318         .max        => return minMax(   gz, scope, ri, node, params, .max),
   9319         // zig fmt: on
   9320 
   9321         .@"export" => {
   9322             const exported = try expr(gz, scope, .{ .rl = .none }, params[0]);
   9323             const export_options_ty = try gz.addBuiltinValue(node, .export_options);
   9324             const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = export_options_ty } }, params[1], .export_options);
   9325             _ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{
   9326                 .exported = exported,
   9327                 .options = options,
   9328             });
   9329             return rvalue(gz, ri, .void_value, node);
   9330         },
   9331         .@"extern" => {
   9332             const type_inst = try typeExpr(gz, scope, params[0]);
   9333             const extern_options_ty = try gz.addBuiltinValue(node, .extern_options);
   9334             const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = extern_options_ty } }, params[1], .extern_options);
   9335             const result = try gz.addExtendedPayload(.builtin_extern, Zir.Inst.BinNode{
   9336                 .node = gz.nodeIndexToRelative(node),
   9337                 .lhs = type_inst,
   9338                 .rhs = options,
   9339             });
   9340             return rvalue(gz, ri, result, node);
   9341         },
   9342         .set_float_mode => {
   9343             const float_mode_ty = try gz.addBuiltinValue(node, .float_mode);
   9344             const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_mode_ty } }, params[0]);
   9345             _ = try gz.addExtendedPayload(.set_float_mode, Zir.Inst.UnNode{
   9346                 .node = gz.nodeIndexToRelative(node),
   9347                 .operand = order,
   9348             });
   9349             return rvalue(gz, ri, .void_value, node);
   9350         },
   9351 
   9352         .src => {
   9353             // Incorporate the source location into the source hash, so that
   9354             // changes in the source location of `@src()` result in re-analysis.
   9355             astgen.src_hasher.update(
   9356                 std.mem.asBytes(&astgen.source_line) ++
   9357                     std.mem.asBytes(&astgen.source_column),
   9358             );
   9359 
   9360             const node_start = tree.tokenStart(tree.firstToken(node));
   9361             astgen.advanceSourceCursor(node_start);
   9362             const result = try gz.addExtendedPayload(.builtin_src, Zir.Inst.Src{
   9363                 .node = gz.nodeIndexToRelative(node),
   9364                 .line = astgen.source_line,
   9365                 .column = astgen.source_column,
   9366             });
   9367             return rvalue(gz, ri, result, node);
   9368         },
   9369 
   9370         // zig fmt: off
   9371         .This                    => return rvalue(gz, ri, try gz.addNodeExtended(.this,                    node), node),
   9372         .return_address          => return rvalue(gz, ri, try gz.addNodeExtended(.ret_addr,                node), node),
   9373         .error_return_trace      => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace,      node), node),
   9374         .frame                   => return rvalue(gz, ri, try gz.addNodeExtended(.frame,                   node), node),
   9375         .frame_address           => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address,           node), node),
   9376         .breakpoint              => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint,              node), node),
   9377         .disable_instrumentation => return rvalue(gz, ri, try gz.addNodeExtended(.disable_instrumentation, node), node),
   9378         .disable_intrinsics      => return rvalue(gz, ri, try gz.addNodeExtended(.disable_intrinsics,      node), node),
   9379 
   9380         .type_info   => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info),
   9381         .size_of     => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of),
   9382         .bit_size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .bit_size_of),
   9383         .align_of    => return simpleUnOpType(gz, scope, ri, node, params[0], .align_of),
   9384 
   9385         .int_from_ptr          => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .int_from_ptr),
   9386         .compile_error         => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } },   params[0], .compile_error),
   9387         .set_eval_branch_quota => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .u32_type } },              params[0], .set_eval_branch_quota),
   9388         .int_from_enum         => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .int_from_enum),
   9389         .int_from_bool         => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .int_from_bool),
   9390         .embed_file            => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } },   params[0], .embed_file),
   9391         .error_name            => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .anyerror_type } },         params[0], .error_name),
   9392         .set_runtime_safety    => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri,                                      params[0], .set_runtime_safety),
   9393         .sqrt                  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .sqrt),
   9394         .sin                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .sin),
   9395         .cos                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .cos),
   9396         .tan                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .tan),
   9397         .exp                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .exp),
   9398         .exp2                  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .exp2),
   9399         .log                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .log),
   9400         .log2                  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .log2),
   9401         .log10                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .log10),
   9402         .abs                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .abs),
   9403         .floor                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .floor),
   9404         .ceil                  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .ceil),
   9405         .trunc                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .trunc),
   9406         .round                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .round),
   9407         .tag_name              => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .tag_name),
   9408         .type_name             => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .type_name),
   9409         .Frame                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .frame_type),
   9410 
   9411         .int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name),
   9412         .float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name),
   9413         .ptr_from_int   => return typeCast(gz, scope, ri, node, params[0], .ptr_from_int, builtin_name),
   9414         .enum_from_int  => return typeCast(gz, scope, ri, node, params[0], .enum_from_int, builtin_name),
   9415         .float_cast     => return typeCast(gz, scope, ri, node, params[0], .float_cast, builtin_name),
   9416         .int_cast       => return typeCast(gz, scope, ri, node, params[0], .int_cast, builtin_name),
   9417         .truncate       => return typeCast(gz, scope, ri, node, params[0], .truncate, builtin_name),
   9418         // zig fmt: on
   9419 
   9420         .in_comptime => if (gz.is_comptime) {
   9421             return astgen.failNode(node, "redundant '@inComptime' in comptime scope", .{});
   9422         } else {
   9423             return rvalue(gz, ri, try gz.addNodeExtended(.in_comptime, node), node);
   9424         },
   9425 
   9426         .Type => {
   9427             return builtinReify(gz, scope, ri, node, params[0], .anon);
   9428         },
   9429         .panic => {
   9430             try emitDbgNode(gz, node);
   9431             return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .panic);
   9432         },
   9433         .trap => {
   9434             try emitDbgNode(gz, node);
   9435             _ = try gz.addNode(.trap, node);
   9436             return rvalue(gz, ri, .unreachable_value, node);
   9437         },
   9438         .int_from_error => {
   9439             const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
   9440             const result = try gz.addExtendedPayload(.int_from_error, Zir.Inst.UnNode{
   9441                 .node = gz.nodeIndexToRelative(node),
   9442                 .operand = operand,
   9443             });
   9444             return rvalue(gz, ri, result, node);
   9445         },
   9446         .error_from_int => {
   9447             const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
   9448             const result = try gz.addExtendedPayload(.error_from_int, Zir.Inst.UnNode{
   9449                 .node = gz.nodeIndexToRelative(node),
   9450                 .operand = operand,
   9451             });
   9452             return rvalue(gz, ri, result, node);
   9453         },
   9454         .error_cast => {
   9455             try emitDbgNode(gz, node);
   9456 
   9457             const result = try gz.addExtendedPayload(.error_cast, Zir.Inst.BinNode{
   9458                 .lhs = try ri.rl.resultTypeForCast(gz, node, builtin_name),
   9459                 .rhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
   9460                 .node = gz.nodeIndexToRelative(node),
   9461             });
   9462             return rvalue(gz, ri, result, node);
   9463         },
   9464         .ptr_cast,
   9465         .align_cast,
   9466         .addrspace_cast,
   9467         .const_cast,
   9468         .volatile_cast,
   9469         => return ptrCast(gz, scope, ri, node),
   9470 
   9471         // zig fmt: off
   9472         .has_decl  => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl),
   9473         .has_field => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_field),
   9474 
   9475         .clz         => return bitBuiltin(gz, scope, ri, node, params[0], .clz),
   9476         .ctz         => return bitBuiltin(gz, scope, ri, node, params[0], .ctz),
   9477         .pop_count   => return bitBuiltin(gz, scope, ri, node, params[0], .pop_count),
   9478         .byte_swap   => return bitBuiltin(gz, scope, ri, node, params[0], .byte_swap),
   9479         .bit_reverse => return bitBuiltin(gz, scope, ri, node, params[0], .bit_reverse),
   9480 
   9481         .div_exact => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_exact),
   9482         .div_floor => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_floor),
   9483         .div_trunc => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_trunc),
   9484         .mod       => return divBuiltin(gz, scope, ri, node, params[0], params[1], .mod),
   9485         .rem       => return divBuiltin(gz, scope, ri, node, params[0], params[1], .rem),
   9486 
   9487         .shl_exact => return shiftOp(gz, scope, ri, node, params[0], params[1], .shl_exact),
   9488         .shr_exact => return shiftOp(gz, scope, ri, node, params[0], params[1], .shr_exact),
   9489 
   9490         .bit_offset_of => return offsetOf(gz, scope, ri, node, params[0], params[1], .bit_offset_of),
   9491         .offset_of     => return offsetOf(gz, scope, ri, node, params[0], params[1], .offset_of),
   9492 
   9493         .c_undef   => return simpleCBuiltin(gz, scope, ri, node, params[0], .c_undef),
   9494         .c_include => return simpleCBuiltin(gz, scope, ri, node, params[0], .c_include),
   9495 
   9496         .cmpxchg_strong => return cmpxchg(gz, scope, ri, node, params, 1),
   9497         .cmpxchg_weak   => return cmpxchg(gz, scope, ri, node, params, 0),
   9498         // zig fmt: on
   9499 
   9500         .wasm_memory_size => {
   9501             const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .wasm_memory_index);
   9502             const result = try gz.addExtendedPayload(.wasm_memory_size, Zir.Inst.UnNode{
   9503                 .node = gz.nodeIndexToRelative(node),
   9504                 .operand = operand,
   9505             });
   9506             return rvalue(gz, ri, result, node);
   9507         },
   9508         .wasm_memory_grow => {
   9509             const index_arg = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .wasm_memory_index);
   9510             const delta_arg = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[1]);
   9511             const result = try gz.addExtendedPayload(.wasm_memory_grow, Zir.Inst.BinNode{
   9512                 .node = gz.nodeIndexToRelative(node),
   9513                 .lhs = index_arg,
   9514                 .rhs = delta_arg,
   9515             });
   9516             return rvalue(gz, ri, result, node);
   9517         },
   9518         .c_define => {
   9519             if (!gz.c_import) return gz.astgen.failNode(node, "C define valid only inside C import block", .{});
   9520             const name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .operand_cDefine_macro_name);
   9521             const value = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1], .operand_cDefine_macro_value);
   9522             const result = try gz.addExtendedPayload(.c_define, Zir.Inst.BinNode{
   9523                 .node = gz.nodeIndexToRelative(node),
   9524                 .lhs = name,
   9525                 .rhs = value,
   9526             });
   9527             return rvalue(gz, ri, result, node);
   9528         },
   9529 
   9530         .splat => {
   9531             const result_type = try ri.rl.resultTypeForCast(gz, node, builtin_name);
   9532             const elem_type = try gz.addUnNode(.vec_arr_elem_type, result_type, node);
   9533             const scalar = try expr(gz, scope, .{ .rl = .{ .ty = elem_type } }, params[0]);
   9534             const result = try gz.addPlNode(.splat, node, Zir.Inst.Bin{
   9535                 .lhs = result_type,
   9536                 .rhs = scalar,
   9537             });
   9538             return rvalue(gz, ri, result, node);
   9539         },
   9540         .reduce => {
   9541             const reduce_op_ty = try gz.addBuiltinValue(node, .reduce_op);
   9542             const op = try expr(gz, scope, .{ .rl = .{ .coerced_ty = reduce_op_ty } }, params[0]);
   9543             const scalar = try expr(gz, scope, .{ .rl = .none }, params[1]);
   9544             const result = try gz.addPlNode(.reduce, node, Zir.Inst.Bin{
   9545                 .lhs = op,
   9546                 .rhs = scalar,
   9547             });
   9548             return rvalue(gz, ri, result, node);
   9549         },
   9550 
   9551         .add_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .add_with_overflow),
   9552         .sub_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .sub_with_overflow),
   9553         .mul_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .mul_with_overflow),
   9554         .shl_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .shl_with_overflow),
   9555 
   9556         .atomic_load => {
   9557             const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order);
   9558             const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.AtomicLoad{
   9559                 // zig fmt: off
   9560                 .elem_type = try typeExpr(gz, scope,                                                  params[0]),
   9561                 .ptr       = try expr    (gz, scope, .{ .rl = .none },                                params[1]),
   9562                 .ordering  = try expr    (gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[2]),
   9563                 // zig fmt: on
   9564             });
   9565             return rvalue(gz, ri, result, node);
   9566         },
   9567         .atomic_rmw => {
   9568             const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order);
   9569             const atomic_rmw_op_type = try gz.addBuiltinValue(node, .atomic_rmw_op);
   9570             const int_type = try typeExpr(gz, scope, params[0]);
   9571             const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{
   9572                 // zig fmt: off
   9573                 .ptr       = try expr(gz, scope, .{ .rl = .none },                                 params[1]),
   9574                 .operation = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_rmw_op_type } }, params[2]),
   9575                 .operand   = try expr(gz, scope, .{ .rl = .{ .ty = int_type } },                   params[3]),
   9576                 .ordering  = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } },  params[4]),
   9577                 // zig fmt: on
   9578             });
   9579             return rvalue(gz, ri, result, node);
   9580         },
   9581         .atomic_store => {
   9582             const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order);
   9583             const int_type = try typeExpr(gz, scope, params[0]);
   9584             _ = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{
   9585                 // zig fmt: off
   9586                 .ptr      = try expr(gz, scope, .{ .rl = .none },                                params[1]),
   9587                 .operand  = try expr(gz, scope, .{ .rl = .{ .ty = int_type } },                  params[2]),
   9588                 .ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[3]),
   9589                 // zig fmt: on
   9590             });
   9591             return rvalue(gz, ri, .void_value, node);
   9592         },
   9593         .mul_add => {
   9594             const float_type = try typeExpr(gz, scope, params[0]);
   9595             const mulend1 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_type } }, params[1]);
   9596             const mulend2 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_type } }, params[2]);
   9597             const addend = try expr(gz, scope, .{ .rl = .{ .ty = float_type } }, params[3]);
   9598             const result = try gz.addPlNode(.mul_add, node, Zir.Inst.MulAdd{
   9599                 .mulend1 = mulend1,
   9600                 .mulend2 = mulend2,
   9601                 .addend = addend,
   9602             });
   9603             return rvalue(gz, ri, result, node);
   9604         },
   9605         .call => {
   9606             const call_modifier_ty = try gz.addBuiltinValue(node, .call_modifier);
   9607             const modifier = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = call_modifier_ty } }, params[0], .call_modifier);
   9608             const callee = try expr(gz, scope, .{ .rl = .none }, params[1]);
   9609             const args = try expr(gz, scope, .{ .rl = .none }, params[2]);
   9610             const result = try gz.addPlNode(.builtin_call, node, Zir.Inst.BuiltinCall{
   9611                 .modifier = modifier,
   9612                 .callee = callee,
   9613                 .args = args,
   9614                 .flags = .{
   9615                     .is_nosuspend = gz.nosuspend_node != .none,
   9616                     .ensure_result_used = false,
   9617                 },
   9618             });
   9619             return rvalue(gz, ri, result, node);
   9620         },
   9621         .field_parent_ptr => {
   9622             const parent_ptr_type = try ri.rl.resultTypeForCast(gz, node, builtin_name);
   9623             const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[0], .field_name);
   9624             const result = try gz.addExtendedPayloadSmall(.field_parent_ptr, 0, Zir.Inst.FieldParentPtr{
   9625                 .src_node = gz.nodeIndexToRelative(node),
   9626                 .parent_ptr_type = parent_ptr_type,
   9627                 .field_name = field_name,
   9628                 .field_ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),
   9629             });
   9630             return rvalue(gz, ri, result, node);
   9631         },
   9632         .memcpy => {
   9633             _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Bin{
   9634                 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
   9635                 .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]),
   9636             });
   9637             return rvalue(gz, ri, .void_value, node);
   9638         },
   9639         .memset => {
   9640             const lhs = try expr(gz, scope, .{ .rl = .none }, params[0]);
   9641             const lhs_ty = try gz.addUnNode(.typeof, lhs, params[0]);
   9642             const elem_ty = try gz.addUnNode(.indexable_ptr_elem_type, lhs_ty, params[0]);
   9643             _ = try gz.addPlNode(.memset, node, Zir.Inst.Bin{
   9644                 .lhs = lhs,
   9645                 .rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = elem_ty } }, params[1]),
   9646             });
   9647             return rvalue(gz, ri, .void_value, node);
   9648         },
   9649         .memmove => {
   9650             _ = try gz.addPlNode(.memmove, node, Zir.Inst.Bin{
   9651                 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
   9652                 .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]),
   9653             });
   9654             return rvalue(gz, ri, .void_value, node);
   9655         },
   9656         .shuffle => {
   9657             const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{
   9658                 .elem_type = try typeExpr(gz, scope, params[0]),
   9659                 .a = try expr(gz, scope, .{ .rl = .none }, params[1]),
   9660                 .b = try expr(gz, scope, .{ .rl = .none }, params[2]),
   9661                 .mask = try comptimeExpr(gz, scope, .{ .rl = .none }, params[3], .operand_shuffle_mask),
   9662             });
   9663             return rvalue(gz, ri, result, node);
   9664         },
   9665         .select => {
   9666             const result = try gz.addExtendedPayload(.select, Zir.Inst.Select{
   9667                 .node = gz.nodeIndexToRelative(node),
   9668                 .elem_type = try typeExpr(gz, scope, params[0]),
   9669                 .pred = try expr(gz, scope, .{ .rl = .none }, params[1]),
   9670                 .a = try expr(gz, scope, .{ .rl = .none }, params[2]),
   9671                 .b = try expr(gz, scope, .{ .rl = .none }, params[3]),
   9672             });
   9673             return rvalue(gz, ri, result, node);
   9674         },
   9675         .Vector => {
   9676             const result = try gz.addPlNode(.vector_type, node, Zir.Inst.Bin{
   9677                 .lhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .type),
   9678                 .rhs = try typeExpr(gz, scope, params[1]),
   9679             });
   9680             return rvalue(gz, ri, result, node);
   9681         },
   9682         .prefetch => {
   9683             const prefetch_options_ty = try gz.addBuiltinValue(node, .prefetch_options);
   9684             const ptr = try expr(gz, scope, .{ .rl = .none }, params[0]);
   9685             const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = prefetch_options_ty } }, params[1], .prefetch_options);
   9686             _ = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{
   9687                 .node = gz.nodeIndexToRelative(node),
   9688                 .lhs = ptr,
   9689                 .rhs = options,
   9690             });
   9691             return rvalue(gz, ri, .void_value, node);
   9692         },
   9693         .c_va_arg => {
   9694             const result = try gz.addExtendedPayload(.c_va_arg, Zir.Inst.BinNode{
   9695                 .node = gz.nodeIndexToRelative(node),
   9696                 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
   9697                 .rhs = try typeExpr(gz, scope, params[1]),
   9698             });
   9699             return rvalue(gz, ri, result, node);
   9700         },
   9701         .c_va_copy => {
   9702             const result = try gz.addExtendedPayload(.c_va_copy, Zir.Inst.UnNode{
   9703                 .node = gz.nodeIndexToRelative(node),
   9704                 .operand = try expr(gz, scope, .{ .rl = .none }, params[0]),
   9705             });
   9706             return rvalue(gz, ri, result, node);
   9707         },
   9708         .c_va_end => {
   9709             const result = try gz.addExtendedPayload(.c_va_end, Zir.Inst.UnNode{
   9710                 .node = gz.nodeIndexToRelative(node),
   9711                 .operand = try expr(gz, scope, .{ .rl = .none }, params[0]),
   9712             });
   9713             return rvalue(gz, ri, result, node);
   9714         },
   9715         .c_va_start => {
   9716             if (!astgen.fn_var_args) {
   9717                 return astgen.failNode(node, "'@cVaStart' in a non-variadic function", .{});
   9718             }
   9719             return rvalue(gz, ri, try gz.addNodeExtended(.c_va_start, node), node);
   9720         },
   9721 
   9722         .work_item_id => {
   9723             const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .work_group_dim_index);
   9724             const result = try gz.addExtendedPayload(.work_item_id, Zir.Inst.UnNode{
   9725                 .node = gz.nodeIndexToRelative(node),
   9726                 .operand = operand,
   9727             });
   9728             return rvalue(gz, ri, result, node);
   9729         },
   9730         .work_group_size => {
   9731             const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .work_group_dim_index);
   9732             const result = try gz.addExtendedPayload(.work_group_size, Zir.Inst.UnNode{
   9733                 .node = gz.nodeIndexToRelative(node),
   9734                 .operand = operand,
   9735             });
   9736             return rvalue(gz, ri, result, node);
   9737         },
   9738         .work_group_id => {
   9739             const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0], .work_group_dim_index);
   9740             const result = try gz.addExtendedPayload(.work_group_id, Zir.Inst.UnNode{
   9741                 .node = gz.nodeIndexToRelative(node),
   9742                 .operand = operand,
   9743             });
   9744             return rvalue(gz, ri, result, node);
   9745         },
   9746     }
   9747 }
   9748 fn builtinReify(
   9749     gz: *GenZir,
   9750     scope: *Scope,
   9751     ri: ResultInfo,
   9752     node: Ast.Node.Index,
   9753     arg_node: Ast.Node.Index,
   9754     name_strat: Zir.Inst.NameStrategy,
   9755 ) InnerError!Zir.Inst.Ref {
   9756     const astgen = gz.astgen;
   9757     const gpa = astgen.gpa;
   9758 
   9759     const type_info_ty = try gz.addBuiltinValue(node, .type_info);
   9760     const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = type_info_ty } }, arg_node);
   9761 
   9762     try gz.instructions.ensureUnusedCapacity(gpa, 1);
   9763     try astgen.instructions.ensureUnusedCapacity(gpa, 1);
   9764 
   9765     const payload_index = try astgen.addExtra(Zir.Inst.Reify{
   9766         .node = node, // Absolute node index -- see the definition of `Reify`.
   9767         .operand = operand,
   9768         .src_line = astgen.source_line,
   9769     });
   9770     const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
   9771     astgen.instructions.appendAssumeCapacity(.{
   9772         .tag = .extended,
   9773         .data = .{ .extended = .{
   9774             .opcode = .reify,
   9775             .small = @intFromEnum(name_strat),
   9776             .operand = payload_index,
   9777         } },
   9778     });
   9779     gz.instructions.appendAssumeCapacity(new_index);
   9780     const result = new_index.toRef();
   9781     return rvalue(gz, ri, result, node);
   9782 }
   9783 
   9784 fn hasDeclOrField(
   9785     gz: *GenZir,
   9786     scope: *Scope,
   9787     ri: ResultInfo,
   9788     node: Ast.Node.Index,
   9789     lhs_node: Ast.Node.Index,
   9790     rhs_node: Ast.Node.Index,
   9791     tag: Zir.Inst.Tag,
   9792 ) InnerError!Zir.Inst.Ref {
   9793     const container_type = try typeExpr(gz, scope, lhs_node);
   9794     const name = try comptimeExpr(
   9795         gz,
   9796         scope,
   9797         .{ .rl = .{ .coerced_ty = .slice_const_u8_type } },
   9798         rhs_node,
   9799         if (tag == .has_decl) .decl_name else .field_name,
   9800     );
   9801     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   9802         .lhs = container_type,
   9803         .rhs = name,
   9804     });
   9805     return rvalue(gz, ri, result, node);
   9806 }
   9807 
   9808 fn typeCast(
   9809     gz: *GenZir,
   9810     scope: *Scope,
   9811     ri: ResultInfo,
   9812     node: Ast.Node.Index,
   9813     operand_node: Ast.Node.Index,
   9814     tag: Zir.Inst.Tag,
   9815     builtin_name: []const u8,
   9816 ) InnerError!Zir.Inst.Ref {
   9817     const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   9818     const result_type = try ri.rl.resultTypeForCast(gz, node, builtin_name);
   9819     const operand = try expr(gz, scope, .{ .rl = .none }, operand_node);
   9820 
   9821     try emitDbgStmt(gz, cursor);
   9822     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   9823         .lhs = result_type,
   9824         .rhs = operand,
   9825     });
   9826     return rvalue(gz, ri, result, node);
   9827 }
   9828 
   9829 fn simpleUnOpType(
   9830     gz: *GenZir,
   9831     scope: *Scope,
   9832     ri: ResultInfo,
   9833     node: Ast.Node.Index,
   9834     operand_node: Ast.Node.Index,
   9835     tag: Zir.Inst.Tag,
   9836 ) InnerError!Zir.Inst.Ref {
   9837     const operand = try typeExpr(gz, scope, operand_node);
   9838     const result = try gz.addUnNode(tag, operand, node);
   9839     return rvalue(gz, ri, result, node);
   9840 }
   9841 
   9842 fn simpleUnOp(
   9843     gz: *GenZir,
   9844     scope: *Scope,
   9845     ri: ResultInfo,
   9846     node: Ast.Node.Index,
   9847     operand_ri: ResultInfo,
   9848     operand_node: Ast.Node.Index,
   9849     tag: Zir.Inst.Tag,
   9850 ) InnerError!Zir.Inst.Ref {
   9851     const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   9852     const operand = if (tag == .compile_error)
   9853         try comptimeExpr(gz, scope, operand_ri, operand_node, .compile_error_string)
   9854     else
   9855         try expr(gz, scope, operand_ri, operand_node);
   9856     switch (tag) {
   9857         .tag_name, .error_name, .int_from_ptr => try emitDbgStmt(gz, cursor),
   9858         else => {},
   9859     }
   9860     const result = try gz.addUnNode(tag, operand, node);
   9861     return rvalue(gz, ri, result, node);
   9862 }
   9863 
   9864 fn negation(
   9865     gz: *GenZir,
   9866     scope: *Scope,
   9867     ri: ResultInfo,
   9868     node: Ast.Node.Index,
   9869 ) InnerError!Zir.Inst.Ref {
   9870     const astgen = gz.astgen;
   9871     const tree = astgen.tree;
   9872 
   9873     // Check for float literal as the sub-expression because we want to preserve
   9874     // its negativity rather than having it go through comptime subtraction.
   9875     const operand_node = tree.nodeData(node).node;
   9876     if (tree.nodeTag(operand_node) == .number_literal) {
   9877         return numberLiteral(gz, ri, operand_node, node, .negative);
   9878     }
   9879 
   9880     const operand = try expr(gz, scope, .{ .rl = .none }, operand_node);
   9881     const result = try gz.addUnNode(.negate, operand, node);
   9882     return rvalue(gz, ri, result, node);
   9883 }
   9884 
   9885 fn cmpxchg(
   9886     gz: *GenZir,
   9887     scope: *Scope,
   9888     ri: ResultInfo,
   9889     node: Ast.Node.Index,
   9890     params: []const Ast.Node.Index,
   9891     small: u16,
   9892 ) InnerError!Zir.Inst.Ref {
   9893     const int_type = try typeExpr(gz, scope, params[0]);
   9894     const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order);
   9895     const result = try gz.addExtendedPayloadSmall(.cmpxchg, small, Zir.Inst.Cmpxchg{
   9896         // zig fmt: off
   9897         .node           = gz.nodeIndexToRelative(node),
   9898         .ptr            = try expr(gz, scope, .{ .rl = .none },                                params[1]),
   9899         .expected_value = try expr(gz, scope, .{ .rl = .{ .ty = int_type } },                  params[2]),
   9900         .new_value      = try expr(gz, scope, .{ .rl = .{ .coerced_ty = int_type } },          params[3]),
   9901         .success_order  = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[4]),
   9902         .failure_order  = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[5]),
   9903         // zig fmt: on
   9904     });
   9905     return rvalue(gz, ri, result, node);
   9906 }
   9907 
   9908 fn bitBuiltin(
   9909     gz: *GenZir,
   9910     scope: *Scope,
   9911     ri: ResultInfo,
   9912     node: Ast.Node.Index,
   9913     operand_node: Ast.Node.Index,
   9914     tag: Zir.Inst.Tag,
   9915 ) InnerError!Zir.Inst.Ref {
   9916     const operand = try expr(gz, scope, .{ .rl = .none }, operand_node);
   9917     const result = try gz.addUnNode(tag, operand, node);
   9918     return rvalue(gz, ri, result, node);
   9919 }
   9920 
   9921 fn divBuiltin(
   9922     gz: *GenZir,
   9923     scope: *Scope,
   9924     ri: ResultInfo,
   9925     node: Ast.Node.Index,
   9926     lhs_node: Ast.Node.Index,
   9927     rhs_node: Ast.Node.Index,
   9928     tag: Zir.Inst.Tag,
   9929 ) InnerError!Zir.Inst.Ref {
   9930     const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   9931     const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node);
   9932     const rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node);
   9933 
   9934     try emitDbgStmt(gz, cursor);
   9935     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
   9936     return rvalue(gz, ri, result, node);
   9937 }
   9938 
   9939 fn simpleCBuiltin(
   9940     gz: *GenZir,
   9941     scope: *Scope,
   9942     ri: ResultInfo,
   9943     node: Ast.Node.Index,
   9944     operand_node: Ast.Node.Index,
   9945     tag: Zir.Inst.Extended,
   9946 ) InnerError!Zir.Inst.Ref {
   9947     const name: []const u8 = if (tag == .c_undef) "C undef" else "C include";
   9948     if (!gz.c_import) return gz.astgen.failNode(node, "{s} valid only inside C import block", .{name});
   9949     const operand = try comptimeExpr(
   9950         gz,
   9951         scope,
   9952         .{ .rl = .{ .coerced_ty = .slice_const_u8_type } },
   9953         operand_node,
   9954         if (tag == .c_undef) .operand_cUndef_macro_name else .operand_cInclude_file_name,
   9955     );
   9956     _ = try gz.addExtendedPayload(tag, Zir.Inst.UnNode{
   9957         .node = gz.nodeIndexToRelative(node),
   9958         .operand = operand,
   9959     });
   9960     return rvalue(gz, ri, .void_value, node);
   9961 }
   9962 
   9963 fn offsetOf(
   9964     gz: *GenZir,
   9965     scope: *Scope,
   9966     ri: ResultInfo,
   9967     node: Ast.Node.Index,
   9968     lhs_node: Ast.Node.Index,
   9969     rhs_node: Ast.Node.Index,
   9970     tag: Zir.Inst.Tag,
   9971 ) InnerError!Zir.Inst.Ref {
   9972     const type_inst = try typeExpr(gz, scope, lhs_node);
   9973     const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, rhs_node, .field_name);
   9974     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   9975         .lhs = type_inst,
   9976         .rhs = field_name,
   9977     });
   9978     return rvalue(gz, ri, result, node);
   9979 }
   9980 
   9981 fn shiftOp(
   9982     gz: *GenZir,
   9983     scope: *Scope,
   9984     ri: ResultInfo,
   9985     node: Ast.Node.Index,
   9986     lhs_node: Ast.Node.Index,
   9987     rhs_node: Ast.Node.Index,
   9988     tag: Zir.Inst.Tag,
   9989 ) InnerError!Zir.Inst.Ref {
   9990     const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node);
   9991 
   9992     const cursor = switch (gz.astgen.tree.nodeTag(node)) {
   9993         .shl, .shr => maybeAdvanceSourceCursorToMainToken(gz, node),
   9994         else => undefined,
   9995     };
   9996 
   9997     const log2_int_type = try gz.addUnNode(.typeof_log2_int_type, lhs, lhs_node);
   9998     const rhs = try expr(gz, scope, .{ .rl = .{ .ty = log2_int_type }, .ctx = .shift_op }, rhs_node);
   9999 
  10000     switch (gz.astgen.tree.nodeTag(node)) {
  10001         .shl, .shr => try emitDbgStmt(gz, cursor),
  10002         else => undefined,
  10003     }
  10004 
  10005     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
  10006         .lhs = lhs,
  10007         .rhs = rhs,
  10008     });
  10009     return rvalue(gz, ri, result, node);
  10010 }
  10011 
  10012 fn cImport(
  10013     gz: *GenZir,
  10014     scope: *Scope,
  10015     node: Ast.Node.Index,
  10016     body_node: Ast.Node.Index,
  10017 ) InnerError!Zir.Inst.Ref {
  10018     const astgen = gz.astgen;
  10019     const gpa = astgen.gpa;
  10020 
  10021     if (gz.c_import) return gz.astgen.failNode(node, "cannot nest @cImport", .{});
  10022 
  10023     var block_scope = gz.makeSubBlock(scope);
  10024     block_scope.is_comptime = true;
  10025     block_scope.c_import = true;
  10026     defer block_scope.unstack();
  10027 
  10028     const block_inst = try gz.makeBlockInst(.c_import, node);
  10029     const block_result = try fullBodyExpr(&block_scope, &block_scope.base, .{ .rl = .none }, body_node, .normal);
  10030     _ = try gz.addUnNode(.ensure_result_used, block_result, node);
  10031     if (!gz.refIsNoReturn(block_result)) {
  10032         _ = try block_scope.addBreak(.break_inline, block_inst, .void_value);
  10033     }
  10034     try block_scope.setBlockBody(block_inst);
  10035     // block_scope unstacked now, can add new instructions to gz
  10036     try gz.instructions.append(gpa, block_inst);
  10037 
  10038     return block_inst.toRef();
  10039 }
  10040 
  10041 fn overflowArithmetic(
  10042     gz: *GenZir,
  10043     scope: *Scope,
  10044     ri: ResultInfo,
  10045     node: Ast.Node.Index,
  10046     params: []const Ast.Node.Index,
  10047     tag: Zir.Inst.Extended,
  10048 ) InnerError!Zir.Inst.Ref {
  10049     const lhs = try expr(gz, scope, .{ .rl = .none }, params[0]);
  10050     const rhs = try expr(gz, scope, .{ .rl = .none }, params[1]);
  10051     const result = try gz.addExtendedPayload(tag, Zir.Inst.BinNode{
  10052         .node = gz.nodeIndexToRelative(node),
  10053         .lhs = lhs,
  10054         .rhs = rhs,
  10055     });
  10056     return rvalue(gz, ri, result, node);
  10057 }
  10058 
  10059 fn callExpr(
  10060     gz: *GenZir,
  10061     scope: *Scope,
  10062     ri: ResultInfo,
  10063     /// If this is not `.none` and this call is a decl literal form (`.foo(...)`), then this
  10064     /// type is used as the decl literal result type instead of the result type from `ri.rl`.
  10065     override_decl_literal_type: Zir.Inst.Ref,
  10066     node: Ast.Node.Index,
  10067     call: Ast.full.Call,
  10068 ) InnerError!Zir.Inst.Ref {
  10069     const astgen = gz.astgen;
  10070 
  10071     const callee = try calleeExpr(gz, scope, ri.rl, override_decl_literal_type, call.ast.fn_expr);
  10072     const modifier: std.builtin.CallModifier = blk: {
  10073         if (gz.nosuspend_node != .none) {
  10074             break :blk .no_suspend;
  10075         }
  10076         break :blk .auto;
  10077     };
  10078 
  10079     {
  10080         astgen.advanceSourceCursor(astgen.tree.tokenStart(call.ast.lparen));
  10081         const line = astgen.source_line - gz.decl_line;
  10082         const column = astgen.source_column;
  10083         // Sema expects a dbg_stmt immediately before call,
  10084         try emitDbgStmtForceCurrentIndex(gz, .{ line, column });
  10085     }
  10086 
  10087     switch (callee) {
  10088         .direct => |obj| assert(obj != .none),
  10089         .field => |field| assert(field.obj_ptr != .none),
  10090     }
  10091 
  10092     const call_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  10093     const call_inst = call_index.toRef();
  10094     try gz.astgen.instructions.append(astgen.gpa, undefined);
  10095     try gz.instructions.append(astgen.gpa, call_index);
  10096 
  10097     const scratch_top = astgen.scratch.items.len;
  10098     defer astgen.scratch.items.len = scratch_top;
  10099 
  10100     var scratch_index = scratch_top;
  10101     try astgen.scratch.resize(astgen.gpa, scratch_top + call.ast.params.len);
  10102 
  10103     for (call.ast.params) |param_node| {
  10104         var arg_block = gz.makeSubBlock(scope);
  10105         defer arg_block.unstack();
  10106 
  10107         // `call_inst` is reused to provide the param type.
  10108         const arg_ref = try fullBodyExpr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node, .normal);
  10109         _ = try arg_block.addBreakWithSrcNode(.break_inline, call_index, arg_ref, param_node);
  10110 
  10111         const body = arg_block.instructionsSlice();
  10112         try astgen.scratch.ensureUnusedCapacity(astgen.gpa, countBodyLenAfterFixups(astgen, body));
  10113         appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
  10114 
  10115         astgen.scratch.items[scratch_index] = @intCast(astgen.scratch.items.len - scratch_top);
  10116         scratch_index += 1;
  10117     }
  10118 
  10119     // If our result location is a try/catch/error-union-if/return, a function argument,
  10120     // or an initializer for a `const` variable, the error trace propagates.
  10121     // Otherwise, it should always be popped (handled in Sema).
  10122     const propagate_error_trace = switch (ri.ctx) {
  10123         .error_handling_expr, .@"return", .fn_arg, .const_init => true,
  10124         else => false,
  10125     };
  10126 
  10127     switch (callee) {
  10128         .direct => |callee_obj| {
  10129             const payload_index = try addExtra(astgen, Zir.Inst.Call{
  10130                 .callee = callee_obj,
  10131                 .flags = .{
  10132                     .pop_error_return_trace = !propagate_error_trace,
  10133                     .packed_modifier = @intCast(@intFromEnum(modifier)),
  10134                     .args_len = @intCast(call.ast.params.len),
  10135                 },
  10136             });
  10137             if (call.ast.params.len != 0) {
  10138                 try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]);
  10139             }
  10140             gz.astgen.instructions.set(@intFromEnum(call_index), .{
  10141                 .tag = .call,
  10142                 .data = .{ .pl_node = .{
  10143                     .src_node = gz.nodeIndexToRelative(node),
  10144                     .payload_index = payload_index,
  10145                 } },
  10146             });
  10147         },
  10148         .field => |callee_field| {
  10149             const payload_index = try addExtra(astgen, Zir.Inst.FieldCall{
  10150                 .obj_ptr = callee_field.obj_ptr,
  10151                 .field_name_start = callee_field.field_name_start,
  10152                 .flags = .{
  10153                     .pop_error_return_trace = !propagate_error_trace,
  10154                     .packed_modifier = @intCast(@intFromEnum(modifier)),
  10155                     .args_len = @intCast(call.ast.params.len),
  10156                 },
  10157             });
  10158             if (call.ast.params.len != 0) {
  10159                 try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]);
  10160             }
  10161             gz.astgen.instructions.set(@intFromEnum(call_index), .{
  10162                 .tag = .field_call,
  10163                 .data = .{ .pl_node = .{
  10164                     .src_node = gz.nodeIndexToRelative(node),
  10165                     .payload_index = payload_index,
  10166                 } },
  10167             });
  10168         },
  10169     }
  10170     return rvalue(gz, ri, call_inst, node); // TODO function call with result location
  10171 }
  10172 
  10173 const Callee = union(enum) {
  10174     field: struct {
  10175         /// A *pointer* to the object the field is fetched on, so that we can
  10176         /// promote the lvalue to an address if the first parameter requires it.
  10177         obj_ptr: Zir.Inst.Ref,
  10178         /// Offset into `string_bytes`.
  10179         field_name_start: Zir.NullTerminatedString,
  10180     },
  10181     direct: Zir.Inst.Ref,
  10182 };
  10183 
  10184 /// calleeExpr generates the function part of a call expression (f in f(x)), but
  10185 /// *not* the callee argument to the @call() builtin. Its purpose is to
  10186 /// distinguish between standard calls and method call syntax `a.b()`. Thus, if
  10187 /// the lhs is a field access, we return using the `field` union field;
  10188 /// otherwise, we use the `direct` union field.
  10189 fn calleeExpr(
  10190     gz: *GenZir,
  10191     scope: *Scope,
  10192     call_rl: ResultInfo.Loc,
  10193     /// If this is not `.none` and this call is a decl literal form (`.foo(...)`), then this
  10194     /// type is used as the decl literal result type instead of the result type from `call_rl`.
  10195     override_decl_literal_type: Zir.Inst.Ref,
  10196     node: Ast.Node.Index,
  10197 ) InnerError!Callee {
  10198     const astgen = gz.astgen;
  10199     const tree = astgen.tree;
  10200 
  10201     const tag = tree.nodeTag(node);
  10202     switch (tag) {
  10203         .field_access => {
  10204             const object_node, const field_ident = tree.nodeData(node).node_and_token;
  10205             const str_index = try astgen.identAsString(field_ident);
  10206             // Capture the object by reference so we can promote it to an
  10207             // address in Sema if needed.
  10208             const lhs = try expr(gz, scope, .{ .rl = .ref }, object_node);
  10209 
  10210             const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
  10211             try emitDbgStmt(gz, cursor);
  10212 
  10213             return .{ .field = .{
  10214                 .obj_ptr = lhs,
  10215                 .field_name_start = str_index,
  10216             } };
  10217         },
  10218         .enum_literal => {
  10219             const res_ty = res_ty: {
  10220                 if (override_decl_literal_type != .none) break :res_ty override_decl_literal_type;
  10221                 break :res_ty try call_rl.resultType(gz, node) orelse {
  10222                     // No result type; lower to a literal call of an enum literal.
  10223                     return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) };
  10224                 };
  10225             };
  10226             // Decl literal call syntax, e.g.
  10227             // `const foo: T = .init();`
  10228             // Look up `init` in `T`, but don't try and coerce it.
  10229             const str_index = try astgen.identAsString(tree.nodeMainToken(node));
  10230             const callee = try gz.addPlNode(.decl_literal_no_coerce, node, Zir.Inst.Field{
  10231                 .lhs = res_ty,
  10232                 .field_name_start = str_index,
  10233             });
  10234             return .{ .direct = callee };
  10235         },
  10236         else => return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) },
  10237     }
  10238 }
  10239 
  10240 const primitive_instrs = std.StaticStringMap(Zir.Inst.Ref).initComptime(.{
  10241     .{ "anyerror", .anyerror_type },
  10242     .{ "anyframe", .anyframe_type },
  10243     .{ "anyopaque", .anyopaque_type },
  10244     .{ "bool", .bool_type },
  10245     .{ "c_int", .c_int_type },
  10246     .{ "c_long", .c_long_type },
  10247     .{ "c_longdouble", .c_longdouble_type },
  10248     .{ "c_longlong", .c_longlong_type },
  10249     .{ "c_char", .c_char_type },
  10250     .{ "c_short", .c_short_type },
  10251     .{ "c_uint", .c_uint_type },
  10252     .{ "c_ulong", .c_ulong_type },
  10253     .{ "c_ulonglong", .c_ulonglong_type },
  10254     .{ "c_ushort", .c_ushort_type },
  10255     .{ "comptime_float", .comptime_float_type },
  10256     .{ "comptime_int", .comptime_int_type },
  10257     .{ "f128", .f128_type },
  10258     .{ "f16", .f16_type },
  10259     .{ "f32", .f32_type },
  10260     .{ "f64", .f64_type },
  10261     .{ "f80", .f80_type },
  10262     .{ "false", .bool_false },
  10263     .{ "i16", .i16_type },
  10264     .{ "i32", .i32_type },
  10265     .{ "i64", .i64_type },
  10266     .{ "i128", .i128_type },
  10267     .{ "i8", .i8_type },
  10268     .{ "isize", .isize_type },
  10269     .{ "noreturn", .noreturn_type },
  10270     .{ "null", .null_value },
  10271     .{ "true", .bool_true },
  10272     .{ "type", .type_type },
  10273     .{ "u16", .u16_type },
  10274     .{ "u29", .u29_type },
  10275     .{ "u32", .u32_type },
  10276     .{ "u64", .u64_type },
  10277     .{ "u128", .u128_type },
  10278     .{ "u1", .u1_type },
  10279     .{ "u8", .u8_type },
  10280     .{ "undefined", .undef },
  10281     .{ "usize", .usize_type },
  10282     .{ "void", .void_type },
  10283 });
  10284 
  10285 comptime {
  10286     // These checks ensure that std.zig.primitives stays in sync with the primitive->Zir map.
  10287     const primitives = std.zig.primitives;
  10288     for (primitive_instrs.keys(), primitive_instrs.values()) |key, value| {
  10289         if (!primitives.isPrimitive(key)) {
  10290             @compileError("std.zig.isPrimitive() is not aware of Zir instr '" ++ @tagName(value) ++ "'");
  10291         }
  10292     }
  10293     for (primitives.names.keys()) |key| {
  10294         if (primitive_instrs.get(key) == null) {
  10295             @compileError("std.zig.primitives entry '" ++ key ++ "' does not have a corresponding Zir instr");
  10296         }
  10297     }
  10298 }
  10299 
  10300 fn nodeIsTriviallyZero(tree: *const Ast, node: Ast.Node.Index) bool {
  10301     switch (tree.nodeTag(node)) {
  10302         .number_literal => {
  10303             const ident = tree.nodeMainToken(node);
  10304             return switch (std.zig.parseNumberLiteral(tree.tokenSlice(ident))) {
  10305                 .int => |number| switch (number) {
  10306                     0 => true,
  10307                     else => false,
  10308                 },
  10309                 else => false,
  10310             };
  10311         },
  10312         else => return false,
  10313     }
  10314 }
  10315 
  10316 fn nodeMayAppendToErrorTrace(tree: *const Ast, start_node: Ast.Node.Index) bool {
  10317     var node = start_node;
  10318     while (true) {
  10319         switch (tree.nodeTag(node)) {
  10320             // These don't have the opportunity to call any runtime functions.
  10321             .error_value,
  10322             .identifier,
  10323             .@"comptime",
  10324             => return false,
  10325 
  10326             // Forward the question to the LHS sub-expression.
  10327             .@"try",
  10328             .@"nosuspend",
  10329             => node = tree.nodeData(node).node,
  10330             .grouped_expression,
  10331             .unwrap_optional,
  10332             => node = tree.nodeData(node).node_and_token[0],
  10333 
  10334             // Anything that does not eval to an error is guaranteed to pop any
  10335             // additions to the error trace, so it effectively does not append.
  10336             else => return nodeMayEvalToError(tree, start_node) != .never,
  10337         }
  10338     }
  10339 }
  10340 
  10341 fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.EvalToError {
  10342     var node = start_node;
  10343     while (true) {
  10344         switch (tree.nodeTag(node)) {
  10345             .root,
  10346             .test_decl,
  10347             .switch_case,
  10348             .switch_case_inline,
  10349             .switch_case_one,
  10350             .switch_case_inline_one,
  10351             .container_field_init,
  10352             .container_field_align,
  10353             .container_field,
  10354             .asm_output,
  10355             .asm_input,
  10356             => unreachable,
  10357 
  10358             .error_value => return .always,
  10359 
  10360             .@"asm",
  10361             .asm_simple,
  10362             .asm_legacy,
  10363             .identifier,
  10364             .field_access,
  10365             .deref,
  10366             .array_access,
  10367             .while_simple,
  10368             .while_cont,
  10369             .for_simple,
  10370             .if_simple,
  10371             .@"while",
  10372             .@"if",
  10373             .@"for",
  10374             .@"switch",
  10375             .switch_comma,
  10376             .call_one,
  10377             .call_one_comma,
  10378             .call,
  10379             .call_comma,
  10380             => return .maybe,
  10381 
  10382             .@"return",
  10383             .@"break",
  10384             .@"continue",
  10385             .bit_not,
  10386             .bool_not,
  10387             .global_var_decl,
  10388             .local_var_decl,
  10389             .simple_var_decl,
  10390             .aligned_var_decl,
  10391             .@"defer",
  10392             .@"errdefer",
  10393             .address_of,
  10394             .optional_type,
  10395             .negation,
  10396             .negation_wrap,
  10397             .@"resume",
  10398             .array_type,
  10399             .array_type_sentinel,
  10400             .ptr_type_aligned,
  10401             .ptr_type_sentinel,
  10402             .ptr_type,
  10403             .ptr_type_bit_range,
  10404             .@"suspend",
  10405             .fn_proto_simple,
  10406             .fn_proto_multi,
  10407             .fn_proto_one,
  10408             .fn_proto,
  10409             .fn_decl,
  10410             .anyframe_type,
  10411             .anyframe_literal,
  10412             .number_literal,
  10413             .enum_literal,
  10414             .string_literal,
  10415             .multiline_string_literal,
  10416             .char_literal,
  10417             .unreachable_literal,
  10418             .error_set_decl,
  10419             .container_decl,
  10420             .container_decl_trailing,
  10421             .container_decl_two,
  10422             .container_decl_two_trailing,
  10423             .container_decl_arg,
  10424             .container_decl_arg_trailing,
  10425             .tagged_union,
  10426             .tagged_union_trailing,
  10427             .tagged_union_two,
  10428             .tagged_union_two_trailing,
  10429             .tagged_union_enum_tag,
  10430             .tagged_union_enum_tag_trailing,
  10431             .add,
  10432             .add_wrap,
  10433             .add_sat,
  10434             .array_cat,
  10435             .array_mult,
  10436             .assign,
  10437             .assign_destructure,
  10438             .assign_bit_and,
  10439             .assign_bit_or,
  10440             .assign_shl,
  10441             .assign_shl_sat,
  10442             .assign_shr,
  10443             .assign_bit_xor,
  10444             .assign_div,
  10445             .assign_sub,
  10446             .assign_sub_wrap,
  10447             .assign_sub_sat,
  10448             .assign_mod,
  10449             .assign_add,
  10450             .assign_add_wrap,
  10451             .assign_add_sat,
  10452             .assign_mul,
  10453             .assign_mul_wrap,
  10454             .assign_mul_sat,
  10455             .bang_equal,
  10456             .bit_and,
  10457             .bit_or,
  10458             .shl,
  10459             .shl_sat,
  10460             .shr,
  10461             .bit_xor,
  10462             .bool_and,
  10463             .bool_or,
  10464             .div,
  10465             .equal_equal,
  10466             .error_union,
  10467             .greater_or_equal,
  10468             .greater_than,
  10469             .less_or_equal,
  10470             .less_than,
  10471             .merge_error_sets,
  10472             .mod,
  10473             .mul,
  10474             .mul_wrap,
  10475             .mul_sat,
  10476             .switch_range,
  10477             .for_range,
  10478             .sub,
  10479             .sub_wrap,
  10480             .sub_sat,
  10481             .slice,
  10482             .slice_open,
  10483             .slice_sentinel,
  10484             .array_init_one,
  10485             .array_init_one_comma,
  10486             .array_init_dot_two,
  10487             .array_init_dot_two_comma,
  10488             .array_init_dot,
  10489             .array_init_dot_comma,
  10490             .array_init,
  10491             .array_init_comma,
  10492             .struct_init_one,
  10493             .struct_init_one_comma,
  10494             .struct_init_dot_two,
  10495             .struct_init_dot_two_comma,
  10496             .struct_init_dot,
  10497             .struct_init_dot_comma,
  10498             .struct_init,
  10499             .struct_init_comma,
  10500             => return .never,
  10501 
  10502             // Forward the question to the LHS sub-expression.
  10503             .@"try",
  10504             .@"comptime",
  10505             .@"nosuspend",
  10506             => node = tree.nodeData(node).node,
  10507             .grouped_expression,
  10508             .unwrap_optional,
  10509             => node = tree.nodeData(node).node_and_token[0],
  10510 
  10511             // LHS sub-expression may still be an error under the outer optional or error union
  10512             .@"catch",
  10513             .@"orelse",
  10514             => return .maybe,
  10515 
  10516             .block_two,
  10517             .block_two_semicolon,
  10518             .block,
  10519             .block_semicolon,
  10520             => {
  10521                 const lbrace = tree.nodeMainToken(node);
  10522                 if (tree.tokenTag(lbrace - 1) == .colon) {
  10523                     // Labeled blocks may need a memory location to forward
  10524                     // to their break statements.
  10525                     return .maybe;
  10526                 } else {
  10527                     return .never;
  10528                 }
  10529             },
  10530 
  10531             .builtin_call,
  10532             .builtin_call_comma,
  10533             .builtin_call_two,
  10534             .builtin_call_two_comma,
  10535             => {
  10536                 const builtin_token = tree.nodeMainToken(node);
  10537                 const builtin_name = tree.tokenSlice(builtin_token);
  10538                 // If the builtin is an invalid name, we don't cause an error here; instead
  10539                 // let it pass, and the error will be "invalid builtin function" later.
  10540                 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return .maybe;
  10541                 return builtin_info.eval_to_error;
  10542             },
  10543         }
  10544     }
  10545 }
  10546 
  10547 /// Returns `true` if it is known the type expression has more than one possible value;
  10548 /// `false` otherwise.
  10549 fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.Index) bool {
  10550     var node = start_node;
  10551     while (true) {
  10552         switch (tree.nodeTag(node)) {
  10553             .root,
  10554             .test_decl,
  10555             .switch_case,
  10556             .switch_case_inline,
  10557             .switch_case_one,
  10558             .switch_case_inline_one,
  10559             .container_field_init,
  10560             .container_field_align,
  10561             .container_field,
  10562             .asm_output,
  10563             .asm_input,
  10564             .global_var_decl,
  10565             .local_var_decl,
  10566             .simple_var_decl,
  10567             .aligned_var_decl,
  10568             => unreachable,
  10569 
  10570             .@"return",
  10571             .@"break",
  10572             .@"continue",
  10573             .bit_not,
  10574             .bool_not,
  10575             .@"defer",
  10576             .@"errdefer",
  10577             .address_of,
  10578             .negation,
  10579             .negation_wrap,
  10580             .@"resume",
  10581             .array_type,
  10582             .@"suspend",
  10583             .fn_decl,
  10584             .anyframe_literal,
  10585             .number_literal,
  10586             .enum_literal,
  10587             .string_literal,
  10588             .multiline_string_literal,
  10589             .char_literal,
  10590             .unreachable_literal,
  10591             .error_set_decl,
  10592             .container_decl,
  10593             .container_decl_trailing,
  10594             .container_decl_two,
  10595             .container_decl_two_trailing,
  10596             .container_decl_arg,
  10597             .container_decl_arg_trailing,
  10598             .tagged_union,
  10599             .tagged_union_trailing,
  10600             .tagged_union_two,
  10601             .tagged_union_two_trailing,
  10602             .tagged_union_enum_tag,
  10603             .tagged_union_enum_tag_trailing,
  10604             .@"asm",
  10605             .asm_simple,
  10606             .asm_legacy,
  10607             .add,
  10608             .add_wrap,
  10609             .add_sat,
  10610             .array_cat,
  10611             .array_mult,
  10612             .assign,
  10613             .assign_destructure,
  10614             .assign_bit_and,
  10615             .assign_bit_or,
  10616             .assign_shl,
  10617             .assign_shl_sat,
  10618             .assign_shr,
  10619             .assign_bit_xor,
  10620             .assign_div,
  10621             .assign_sub,
  10622             .assign_sub_wrap,
  10623             .assign_sub_sat,
  10624             .assign_mod,
  10625             .assign_add,
  10626             .assign_add_wrap,
  10627             .assign_add_sat,
  10628             .assign_mul,
  10629             .assign_mul_wrap,
  10630             .assign_mul_sat,
  10631             .bang_equal,
  10632             .bit_and,
  10633             .bit_or,
  10634             .shl,
  10635             .shl_sat,
  10636             .shr,
  10637             .bit_xor,
  10638             .bool_and,
  10639             .bool_or,
  10640             .div,
  10641             .equal_equal,
  10642             .error_union,
  10643             .greater_or_equal,
  10644             .greater_than,
  10645             .less_or_equal,
  10646             .less_than,
  10647             .merge_error_sets,
  10648             .mod,
  10649             .mul,
  10650             .mul_wrap,
  10651             .mul_sat,
  10652             .switch_range,
  10653             .for_range,
  10654             .field_access,
  10655             .sub,
  10656             .sub_wrap,
  10657             .sub_sat,
  10658             .slice,
  10659             .slice_open,
  10660             .slice_sentinel,
  10661             .deref,
  10662             .array_access,
  10663             .error_value,
  10664             .while_simple,
  10665             .while_cont,
  10666             .for_simple,
  10667             .if_simple,
  10668             .@"catch",
  10669             .@"orelse",
  10670             .array_init_one,
  10671             .array_init_one_comma,
  10672             .array_init_dot_two,
  10673             .array_init_dot_two_comma,
  10674             .array_init_dot,
  10675             .array_init_dot_comma,
  10676             .array_init,
  10677             .array_init_comma,
  10678             .struct_init_one,
  10679             .struct_init_one_comma,
  10680             .struct_init_dot_two,
  10681             .struct_init_dot_two_comma,
  10682             .struct_init_dot,
  10683             .struct_init_dot_comma,
  10684             .struct_init,
  10685             .struct_init_comma,
  10686             .@"while",
  10687             .@"if",
  10688             .@"for",
  10689             .@"switch",
  10690             .switch_comma,
  10691             .call_one,
  10692             .call_one_comma,
  10693             .call,
  10694             .call_comma,
  10695             .block_two,
  10696             .block_two_semicolon,
  10697             .block,
  10698             .block_semicolon,
  10699             .builtin_call,
  10700             .builtin_call_comma,
  10701             .builtin_call_two,
  10702             .builtin_call_two_comma,
  10703             // these are function bodies, not pointers
  10704             .fn_proto_simple,
  10705             .fn_proto_multi,
  10706             .fn_proto_one,
  10707             .fn_proto,
  10708             => return false,
  10709 
  10710             // Forward the question to the LHS sub-expression.
  10711             .@"try",
  10712             .@"comptime",
  10713             .@"nosuspend",
  10714             => node = tree.nodeData(node).node,
  10715             .grouped_expression,
  10716             .unwrap_optional,
  10717             => node = tree.nodeData(node).node_and_token[0],
  10718 
  10719             .ptr_type_aligned,
  10720             .ptr_type_sentinel,
  10721             .ptr_type,
  10722             .ptr_type_bit_range,
  10723             .optional_type,
  10724             .anyframe_type,
  10725             .array_type_sentinel,
  10726             => return true,
  10727 
  10728             .identifier => {
  10729                 const ident_bytes = tree.tokenSlice(tree.nodeMainToken(node));
  10730                 if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) {
  10731                     .anyerror_type,
  10732                     .anyframe_type,
  10733                     .anyopaque_type,
  10734                     .bool_type,
  10735                     .c_int_type,
  10736                     .c_long_type,
  10737                     .c_longdouble_type,
  10738                     .c_longlong_type,
  10739                     .c_char_type,
  10740                     .c_short_type,
  10741                     .c_uint_type,
  10742                     .c_ulong_type,
  10743                     .c_ulonglong_type,
  10744                     .c_ushort_type,
  10745                     .comptime_float_type,
  10746                     .comptime_int_type,
  10747                     .f16_type,
  10748                     .f32_type,
  10749                     .f64_type,
  10750                     .f80_type,
  10751                     .f128_type,
  10752                     .i16_type,
  10753                     .i32_type,
  10754                     .i64_type,
  10755                     .i128_type,
  10756                     .i8_type,
  10757                     .isize_type,
  10758                     .type_type,
  10759                     .u16_type,
  10760                     .u29_type,
  10761                     .u32_type,
  10762                     .u64_type,
  10763                     .u128_type,
  10764                     .u1_type,
  10765                     .u8_type,
  10766                     .usize_type,
  10767                     => return true,
  10768 
  10769                     .void_type,
  10770                     .bool_false,
  10771                     .bool_true,
  10772                     .null_value,
  10773                     .undef,
  10774                     .noreturn_type,
  10775                     => return false,
  10776 
  10777                     else => unreachable, // that's all the values from `primitives`.
  10778                 } else {
  10779                     return false;
  10780                 }
  10781             },
  10782         }
  10783     }
  10784 }
  10785 
  10786 /// Returns `true` if it is known the expression is a type that cannot be used at runtime;
  10787 /// `false` otherwise.
  10788 fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
  10789     var node = start_node;
  10790     while (true) {
  10791         switch (tree.nodeTag(node)) {
  10792             .root,
  10793             .test_decl,
  10794             .switch_case,
  10795             .switch_case_inline,
  10796             .switch_case_one,
  10797             .switch_case_inline_one,
  10798             .container_field_init,
  10799             .container_field_align,
  10800             .container_field,
  10801             .asm_output,
  10802             .asm_input,
  10803             .global_var_decl,
  10804             .local_var_decl,
  10805             .simple_var_decl,
  10806             .aligned_var_decl,
  10807             => unreachable,
  10808 
  10809             .@"return",
  10810             .@"break",
  10811             .@"continue",
  10812             .bit_not,
  10813             .bool_not,
  10814             .@"defer",
  10815             .@"errdefer",
  10816             .address_of,
  10817             .negation,
  10818             .negation_wrap,
  10819             .@"resume",
  10820             .array_type,
  10821             .@"suspend",
  10822             .fn_decl,
  10823             .anyframe_literal,
  10824             .number_literal,
  10825             .enum_literal,
  10826             .string_literal,
  10827             .multiline_string_literal,
  10828             .char_literal,
  10829             .unreachable_literal,
  10830             .error_set_decl,
  10831             .container_decl,
  10832             .container_decl_trailing,
  10833             .container_decl_two,
  10834             .container_decl_two_trailing,
  10835             .container_decl_arg,
  10836             .container_decl_arg_trailing,
  10837             .tagged_union,
  10838             .tagged_union_trailing,
  10839             .tagged_union_two,
  10840             .tagged_union_two_trailing,
  10841             .tagged_union_enum_tag,
  10842             .tagged_union_enum_tag_trailing,
  10843             .@"asm",
  10844             .asm_simple,
  10845             .asm_legacy,
  10846             .add,
  10847             .add_wrap,
  10848             .add_sat,
  10849             .array_cat,
  10850             .array_mult,
  10851             .assign,
  10852             .assign_destructure,
  10853             .assign_bit_and,
  10854             .assign_bit_or,
  10855             .assign_shl,
  10856             .assign_shl_sat,
  10857             .assign_shr,
  10858             .assign_bit_xor,
  10859             .assign_div,
  10860             .assign_sub,
  10861             .assign_sub_wrap,
  10862             .assign_sub_sat,
  10863             .assign_mod,
  10864             .assign_add,
  10865             .assign_add_wrap,
  10866             .assign_add_sat,
  10867             .assign_mul,
  10868             .assign_mul_wrap,
  10869             .assign_mul_sat,
  10870             .bang_equal,
  10871             .bit_and,
  10872             .bit_or,
  10873             .shl,
  10874             .shl_sat,
  10875             .shr,
  10876             .bit_xor,
  10877             .bool_and,
  10878             .bool_or,
  10879             .div,
  10880             .equal_equal,
  10881             .error_union,
  10882             .greater_or_equal,
  10883             .greater_than,
  10884             .less_or_equal,
  10885             .less_than,
  10886             .merge_error_sets,
  10887             .mod,
  10888             .mul,
  10889             .mul_wrap,
  10890             .mul_sat,
  10891             .switch_range,
  10892             .for_range,
  10893             .field_access,
  10894             .sub,
  10895             .sub_wrap,
  10896             .sub_sat,
  10897             .slice,
  10898             .slice_open,
  10899             .slice_sentinel,
  10900             .deref,
  10901             .array_access,
  10902             .error_value,
  10903             .while_simple,
  10904             .while_cont,
  10905             .for_simple,
  10906             .if_simple,
  10907             .@"catch",
  10908             .@"orelse",
  10909             .array_init_one,
  10910             .array_init_one_comma,
  10911             .array_init_dot_two,
  10912             .array_init_dot_two_comma,
  10913             .array_init_dot,
  10914             .array_init_dot_comma,
  10915             .array_init,
  10916             .array_init_comma,
  10917             .struct_init_one,
  10918             .struct_init_one_comma,
  10919             .struct_init_dot_two,
  10920             .struct_init_dot_two_comma,
  10921             .struct_init_dot,
  10922             .struct_init_dot_comma,
  10923             .struct_init,
  10924             .struct_init_comma,
  10925             .@"while",
  10926             .@"if",
  10927             .@"for",
  10928             .@"switch",
  10929             .switch_comma,
  10930             .call_one,
  10931             .call_one_comma,
  10932             .call,
  10933             .call_comma,
  10934             .block_two,
  10935             .block_two_semicolon,
  10936             .block,
  10937             .block_semicolon,
  10938             .builtin_call,
  10939             .builtin_call_comma,
  10940             .builtin_call_two,
  10941             .builtin_call_two_comma,
  10942             .ptr_type_aligned,
  10943             .ptr_type_sentinel,
  10944             .ptr_type,
  10945             .ptr_type_bit_range,
  10946             .optional_type,
  10947             .anyframe_type,
  10948             .array_type_sentinel,
  10949             => return false,
  10950 
  10951             // these are function bodies, not pointers
  10952             .fn_proto_simple,
  10953             .fn_proto_multi,
  10954             .fn_proto_one,
  10955             .fn_proto,
  10956             => return true,
  10957 
  10958             // Forward the question to the LHS sub-expression.
  10959             .@"try",
  10960             .@"comptime",
  10961             .@"nosuspend",
  10962             => node = tree.nodeData(node).node,
  10963             .grouped_expression,
  10964             .unwrap_optional,
  10965             => node = tree.nodeData(node).node_and_token[0],
  10966 
  10967             .identifier => {
  10968                 const ident_bytes = tree.tokenSlice(tree.nodeMainToken(node));
  10969                 if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) {
  10970                     .anyerror_type,
  10971                     .anyframe_type,
  10972                     .anyopaque_type,
  10973                     .bool_type,
  10974                     .c_int_type,
  10975                     .c_long_type,
  10976                     .c_longdouble_type,
  10977                     .c_longlong_type,
  10978                     .c_char_type,
  10979                     .c_short_type,
  10980                     .c_uint_type,
  10981                     .c_ulong_type,
  10982                     .c_ulonglong_type,
  10983                     .c_ushort_type,
  10984                     .f16_type,
  10985                     .f32_type,
  10986                     .f64_type,
  10987                     .f80_type,
  10988                     .f128_type,
  10989                     .i16_type,
  10990                     .i32_type,
  10991                     .i64_type,
  10992                     .i128_type,
  10993                     .i8_type,
  10994                     .isize_type,
  10995                     .u16_type,
  10996                     .u29_type,
  10997                     .u32_type,
  10998                     .u64_type,
  10999                     .u128_type,
  11000                     .u1_type,
  11001                     .u8_type,
  11002                     .usize_type,
  11003                     .void_type,
  11004                     .bool_false,
  11005                     .bool_true,
  11006                     .null_value,
  11007                     .undef,
  11008                     .noreturn_type,
  11009                     => return false,
  11010 
  11011                     .comptime_float_type,
  11012                     .comptime_int_type,
  11013                     .type_type,
  11014                     => return true,
  11015 
  11016                     else => unreachable, // that's all the values from `primitives`.
  11017                 } else {
  11018                     return false;
  11019                 }
  11020             },
  11021         }
  11022     }
  11023 }
  11024 
  11025 /// Applies `rl` semantics to `result`. Expressions which do not do their own handling of
  11026 /// result locations must call this function on their result.
  11027 /// As an example, if `ri.rl` is `.ptr`, it will write the result to the pointer.
  11028 /// If `ri.rl` is `.ty`, it will coerce the result to the type.
  11029 /// Assumes nothing stacked on `gz`.
  11030 fn rvalue(
  11031     gz: *GenZir,
  11032     ri: ResultInfo,
  11033     raw_result: Zir.Inst.Ref,
  11034     src_node: Ast.Node.Index,
  11035 ) InnerError!Zir.Inst.Ref {
  11036     return rvalueInner(gz, ri, raw_result, src_node, true);
  11037 }
  11038 
  11039 /// Like `rvalue`, but refuses to perform coercions before taking references for
  11040 /// the `ref_coerced_ty` result type. This is used for local variables which do
  11041 /// not have `alloc`s, because we want variables to have consistent addresses,
  11042 /// i.e. we want them to act like lvalues.
  11043 fn rvalueNoCoercePreRef(
  11044     gz: *GenZir,
  11045     ri: ResultInfo,
  11046     raw_result: Zir.Inst.Ref,
  11047     src_node: Ast.Node.Index,
  11048 ) InnerError!Zir.Inst.Ref {
  11049     return rvalueInner(gz, ri, raw_result, src_node, false);
  11050 }
  11051 
  11052 fn rvalueInner(
  11053     gz: *GenZir,
  11054     ri: ResultInfo,
  11055     raw_result: Zir.Inst.Ref,
  11056     src_node: Ast.Node.Index,
  11057     allow_coerce_pre_ref: bool,
  11058 ) InnerError!Zir.Inst.Ref {
  11059     const result = r: {
  11060         if (raw_result.toIndex()) |result_index| {
  11061             const zir_tags = gz.astgen.instructions.items(.tag);
  11062             const data = gz.astgen.instructions.items(.data)[@intFromEnum(result_index)];
  11063             if (zir_tags[@intFromEnum(result_index)].isAlwaysVoid(data)) {
  11064                 break :r Zir.Inst.Ref.void_value;
  11065             }
  11066         }
  11067         break :r raw_result;
  11068     };
  11069     if (gz.endsWithNoReturn()) return result;
  11070     switch (ri.rl) {
  11071         .none, .coerced_ty => return result,
  11072         .discard => {
  11073             // Emit a compile error for discarding error values.
  11074             _ = try gz.addUnNode(.ensure_result_non_error, result, src_node);
  11075             return .void_value;
  11076         },
  11077         .ref, .ref_coerced_ty => {
  11078             const coerced_result = if (allow_coerce_pre_ref and ri.rl == .ref_coerced_ty) res: {
  11079                 const ptr_ty = ri.rl.ref_coerced_ty;
  11080                 break :res try gz.addPlNode(.coerce_ptr_elem_ty, src_node, Zir.Inst.Bin{
  11081                     .lhs = ptr_ty,
  11082                     .rhs = result,
  11083                 });
  11084             } else result;
  11085             // We need a pointer but we have a value.
  11086             // Unfortunately it's not quite as simple as directly emitting a ref
  11087             // instruction here because we need subsequent address-of operator on
  11088             // const locals to return the same address.
  11089             const astgen = gz.astgen;
  11090             const tree = astgen.tree;
  11091             const src_token = tree.firstToken(src_node);
  11092             const result_index = coerced_result.toIndex() orelse
  11093                 return gz.addUnTok(.ref, coerced_result, src_token);
  11094             const gop = try astgen.ref_table.getOrPut(astgen.gpa, result_index);
  11095             if (!gop.found_existing) {
  11096                 gop.value_ptr.* = try gz.makeUnTok(.ref, coerced_result, src_token);
  11097             }
  11098             return gop.value_ptr.*.toRef();
  11099         },
  11100         .ty => |ty_inst| {
  11101             // Quickly eliminate some common, unnecessary type coercion.
  11102             const as_ty = @as(u64, @intFromEnum(Zir.Inst.Ref.type_type)) << 32;
  11103             const as_bool = @as(u64, @intFromEnum(Zir.Inst.Ref.bool_type)) << 32;
  11104             const as_void = @as(u64, @intFromEnum(Zir.Inst.Ref.void_type)) << 32;
  11105             const as_comptime_int = @as(u64, @intFromEnum(Zir.Inst.Ref.comptime_int_type)) << 32;
  11106             const as_usize = @as(u64, @intFromEnum(Zir.Inst.Ref.usize_type)) << 32;
  11107             const as_u1 = @as(u64, @intFromEnum(Zir.Inst.Ref.u1_type)) << 32;
  11108             const as_u8 = @as(u64, @intFromEnum(Zir.Inst.Ref.u8_type)) << 32;
  11109             switch ((@as(u64, @intFromEnum(ty_inst)) << 32) | @as(u64, @intFromEnum(result))) {
  11110                 as_ty | @intFromEnum(Zir.Inst.Ref.u1_type),
  11111                 as_ty | @intFromEnum(Zir.Inst.Ref.u8_type),
  11112                 as_ty | @intFromEnum(Zir.Inst.Ref.i8_type),
  11113                 as_ty | @intFromEnum(Zir.Inst.Ref.u16_type),
  11114                 as_ty | @intFromEnum(Zir.Inst.Ref.u29_type),
  11115                 as_ty | @intFromEnum(Zir.Inst.Ref.i16_type),
  11116                 as_ty | @intFromEnum(Zir.Inst.Ref.u32_type),
  11117                 as_ty | @intFromEnum(Zir.Inst.Ref.i32_type),
  11118                 as_ty | @intFromEnum(Zir.Inst.Ref.u64_type),
  11119                 as_ty | @intFromEnum(Zir.Inst.Ref.i64_type),
  11120                 as_ty | @intFromEnum(Zir.Inst.Ref.u128_type),
  11121                 as_ty | @intFromEnum(Zir.Inst.Ref.i128_type),
  11122                 as_ty | @intFromEnum(Zir.Inst.Ref.usize_type),
  11123                 as_ty | @intFromEnum(Zir.Inst.Ref.isize_type),
  11124                 as_ty | @intFromEnum(Zir.Inst.Ref.c_char_type),
  11125                 as_ty | @intFromEnum(Zir.Inst.Ref.c_short_type),
  11126                 as_ty | @intFromEnum(Zir.Inst.Ref.c_ushort_type),
  11127                 as_ty | @intFromEnum(Zir.Inst.Ref.c_int_type),
  11128                 as_ty | @intFromEnum(Zir.Inst.Ref.c_uint_type),
  11129                 as_ty | @intFromEnum(Zir.Inst.Ref.c_long_type),
  11130                 as_ty | @intFromEnum(Zir.Inst.Ref.c_ulong_type),
  11131                 as_ty | @intFromEnum(Zir.Inst.Ref.c_longlong_type),
  11132                 as_ty | @intFromEnum(Zir.Inst.Ref.c_ulonglong_type),
  11133                 as_ty | @intFromEnum(Zir.Inst.Ref.c_longdouble_type),
  11134                 as_ty | @intFromEnum(Zir.Inst.Ref.f16_type),
  11135                 as_ty | @intFromEnum(Zir.Inst.Ref.f32_type),
  11136                 as_ty | @intFromEnum(Zir.Inst.Ref.f64_type),
  11137                 as_ty | @intFromEnum(Zir.Inst.Ref.f80_type),
  11138                 as_ty | @intFromEnum(Zir.Inst.Ref.f128_type),
  11139                 as_ty | @intFromEnum(Zir.Inst.Ref.anyopaque_type),
  11140                 as_ty | @intFromEnum(Zir.Inst.Ref.bool_type),
  11141                 as_ty | @intFromEnum(Zir.Inst.Ref.void_type),
  11142                 as_ty | @intFromEnum(Zir.Inst.Ref.type_type),
  11143                 as_ty | @intFromEnum(Zir.Inst.Ref.anyerror_type),
  11144                 as_ty | @intFromEnum(Zir.Inst.Ref.comptime_int_type),
  11145                 as_ty | @intFromEnum(Zir.Inst.Ref.comptime_float_type),
  11146                 as_ty | @intFromEnum(Zir.Inst.Ref.noreturn_type),
  11147                 as_ty | @intFromEnum(Zir.Inst.Ref.anyframe_type),
  11148                 as_ty | @intFromEnum(Zir.Inst.Ref.null_type),
  11149                 as_ty | @intFromEnum(Zir.Inst.Ref.undefined_type),
  11150                 as_ty | @intFromEnum(Zir.Inst.Ref.enum_literal_type),
  11151                 as_ty | @intFromEnum(Zir.Inst.Ref.ptr_usize_type),
  11152                 as_ty | @intFromEnum(Zir.Inst.Ref.ptr_const_comptime_int_type),
  11153                 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_u8_type),
  11154                 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_const_u8_type),
  11155                 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_const_u8_sentinel_0_type),
  11156                 as_ty | @intFromEnum(Zir.Inst.Ref.slice_const_u8_type),
  11157                 as_ty | @intFromEnum(Zir.Inst.Ref.slice_const_u8_sentinel_0_type),
  11158                 as_ty | @intFromEnum(Zir.Inst.Ref.anyerror_void_error_union_type),
  11159                 as_ty | @intFromEnum(Zir.Inst.Ref.generic_poison_type),
  11160                 as_ty | @intFromEnum(Zir.Inst.Ref.empty_tuple_type),
  11161                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero),
  11162                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one),
  11163                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.negative_one),
  11164                 as_usize | @intFromEnum(Zir.Inst.Ref.undef_usize),
  11165                 as_usize | @intFromEnum(Zir.Inst.Ref.zero_usize),
  11166                 as_usize | @intFromEnum(Zir.Inst.Ref.one_usize),
  11167                 as_u1 | @intFromEnum(Zir.Inst.Ref.undef_u1),
  11168                 as_u1 | @intFromEnum(Zir.Inst.Ref.zero_u1),
  11169                 as_u1 | @intFromEnum(Zir.Inst.Ref.one_u1),
  11170                 as_u8 | @intFromEnum(Zir.Inst.Ref.zero_u8),
  11171                 as_u8 | @intFromEnum(Zir.Inst.Ref.one_u8),
  11172                 as_u8 | @intFromEnum(Zir.Inst.Ref.four_u8),
  11173                 as_bool | @intFromEnum(Zir.Inst.Ref.undef_bool),
  11174                 as_bool | @intFromEnum(Zir.Inst.Ref.bool_true),
  11175                 as_bool | @intFromEnum(Zir.Inst.Ref.bool_false),
  11176                 as_void | @intFromEnum(Zir.Inst.Ref.void_value),
  11177                 => return result, // type of result is already correct
  11178 
  11179                 as_bool | @intFromEnum(Zir.Inst.Ref.undef) => return .undef_bool,
  11180                 as_usize | @intFromEnum(Zir.Inst.Ref.undef) => return .undef_usize,
  11181                 as_usize | @intFromEnum(Zir.Inst.Ref.undef_u1) => return .undef_usize,
  11182                 as_u1 | @intFromEnum(Zir.Inst.Ref.undef) => return .undef_u1,
  11183 
  11184                 as_usize | @intFromEnum(Zir.Inst.Ref.zero) => return .zero_usize,
  11185                 as_u1 | @intFromEnum(Zir.Inst.Ref.zero) => return .zero_u1,
  11186                 as_u8 | @intFromEnum(Zir.Inst.Ref.zero) => return .zero_u8,
  11187                 as_usize | @intFromEnum(Zir.Inst.Ref.one) => return .one_usize,
  11188                 as_u1 | @intFromEnum(Zir.Inst.Ref.one) => return .one_u1,
  11189                 as_u8 | @intFromEnum(Zir.Inst.Ref.one) => return .one_u8,
  11190                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero_usize) => return .zero,
  11191                 as_u1 | @intFromEnum(Zir.Inst.Ref.zero_usize) => return .zero_u1,
  11192                 as_u8 | @intFromEnum(Zir.Inst.Ref.zero_usize) => return .zero_u8,
  11193                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one_usize) => return .one,
  11194                 as_u1 | @intFromEnum(Zir.Inst.Ref.one_usize) => return .one_u1,
  11195                 as_u8 | @intFromEnum(Zir.Inst.Ref.one_usize) => return .one_u8,
  11196                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero_u1) => return .zero,
  11197                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero_u8) => return .zero,
  11198                 as_usize | @intFromEnum(Zir.Inst.Ref.zero_u1) => return .zero_usize,
  11199                 as_usize | @intFromEnum(Zir.Inst.Ref.zero_u8) => return .zero_usize,
  11200                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one_u1) => return .one,
  11201                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one_u8) => return .one,
  11202                 as_usize | @intFromEnum(Zir.Inst.Ref.one_u1) => return .one_usize,
  11203                 as_usize | @intFromEnum(Zir.Inst.Ref.one_u8) => return .one_usize,
  11204 
  11205                 // Need an explicit type coercion instruction.
  11206                 else => return gz.addPlNode(ri.zirTag(), src_node, Zir.Inst.As{
  11207                     .dest_type = ty_inst,
  11208                     .operand = result,
  11209                 }),
  11210             }
  11211         },
  11212         .ptr => |ptr_res| {
  11213             _ = try gz.addPlNode(.store_node, ptr_res.src_node orelse src_node, Zir.Inst.Bin{
  11214                 .lhs = ptr_res.inst,
  11215                 .rhs = result,
  11216             });
  11217             return .void_value;
  11218         },
  11219         .inferred_ptr => |alloc| {
  11220             _ = try gz.addPlNode(.store_to_inferred_ptr, src_node, Zir.Inst.Bin{
  11221                 .lhs = alloc,
  11222                 .rhs = result,
  11223             });
  11224             return .void_value;
  11225         },
  11226         .destructure => |destructure| {
  11227             const components = destructure.components;
  11228             _ = try gz.addPlNode(.validate_destructure, src_node, Zir.Inst.ValidateDestructure{
  11229                 .operand = result,
  11230                 .destructure_node = gz.nodeIndexToRelative(destructure.src_node),
  11231                 .expect_len = @intCast(components.len),
  11232             });
  11233             for (components, 0..) |component, i| {
  11234                 if (component == .discard) continue;
  11235                 const elem_val = try gz.add(.{
  11236                     .tag = .elem_val_imm,
  11237                     .data = .{ .elem_val_imm = .{
  11238                         .operand = result,
  11239                         .idx = @intCast(i),
  11240                     } },
  11241                 });
  11242                 switch (component) {
  11243                     .typed_ptr => |ptr_res| {
  11244                         _ = try gz.addPlNode(.store_node, ptr_res.src_node orelse src_node, Zir.Inst.Bin{
  11245                             .lhs = ptr_res.inst,
  11246                             .rhs = elem_val,
  11247                         });
  11248                     },
  11249                     .inferred_ptr => |ptr_inst| {
  11250                         _ = try gz.addPlNode(.store_to_inferred_ptr, src_node, Zir.Inst.Bin{
  11251                             .lhs = ptr_inst,
  11252                             .rhs = elem_val,
  11253                         });
  11254                     },
  11255                     .discard => unreachable,
  11256                 }
  11257             }
  11258             return .void_value;
  11259         },
  11260     }
  11261 }
  11262 
  11263 /// Given an identifier token, obtain the string for it.
  11264 /// If the token uses @"" syntax, parses as a string, reports errors if applicable,
  11265 /// and allocates the result within `astgen.arena`.
  11266 /// Otherwise, returns a reference to the source code bytes directly.
  11267 /// See also `appendIdentStr` and `parseStrLit`.
  11268 fn identifierTokenString(astgen: *AstGen, token: Ast.TokenIndex) InnerError![]const u8 {
  11269     const tree = astgen.tree;
  11270     assert(tree.tokenTag(token) == .identifier);
  11271     const ident_name = tree.tokenSlice(token);
  11272     if (!mem.startsWith(u8, ident_name, "@")) {
  11273         return ident_name;
  11274     }
  11275     var buf: ArrayListUnmanaged(u8) = .empty;
  11276     defer buf.deinit(astgen.gpa);
  11277     try astgen.parseStrLit(token, &buf, ident_name, 1);
  11278     if (mem.indexOfScalar(u8, buf.items, 0) != null) {
  11279         return astgen.failTok(token, "identifier cannot contain null bytes", .{});
  11280     } else if (buf.items.len == 0) {
  11281         return astgen.failTok(token, "identifier cannot be empty", .{});
  11282     }
  11283     const duped = try astgen.arena.dupe(u8, buf.items);
  11284     return duped;
  11285 }
  11286 
  11287 /// Given an identifier token, obtain the string for it (possibly parsing as a string
  11288 /// literal if it is @"" syntax), and append the string to `buf`.
  11289 /// See also `identifierTokenString` and `parseStrLit`.
  11290 fn appendIdentStr(
  11291     astgen: *AstGen,
  11292     token: Ast.TokenIndex,
  11293     buf: *ArrayListUnmanaged(u8),
  11294 ) InnerError!void {
  11295     const tree = astgen.tree;
  11296     assert(tree.tokenTag(token) == .identifier);
  11297     const ident_name = tree.tokenSlice(token);
  11298     if (!mem.startsWith(u8, ident_name, "@")) {
  11299         return buf.appendSlice(astgen.gpa, ident_name);
  11300     } else {
  11301         const start = buf.items.len;
  11302         try astgen.parseStrLit(token, buf, ident_name, 1);
  11303         const slice = buf.items[start..];
  11304         if (mem.indexOfScalar(u8, slice, 0) != null) {
  11305             return astgen.failTok(token, "identifier cannot contain null bytes", .{});
  11306         } else if (slice.len == 0) {
  11307             return astgen.failTok(token, "identifier cannot be empty", .{});
  11308         }
  11309     }
  11310 }
  11311 
  11312 /// Appends the result to `buf`.
  11313 fn parseStrLit(
  11314     astgen: *AstGen,
  11315     token: Ast.TokenIndex,
  11316     buf: *ArrayListUnmanaged(u8),
  11317     bytes: []const u8,
  11318     offset: u32,
  11319 ) InnerError!void {
  11320     const raw_string = bytes[offset..];
  11321     const result = r: {
  11322         var aw: std.io.Writer.Allocating = .fromArrayList(astgen.gpa, buf);
  11323         defer buf.* = aw.toArrayList();
  11324         break :r std.zig.string_literal.parseWrite(&aw.writer, raw_string) catch |err| switch (err) {
  11325             error.WriteFailed => return error.OutOfMemory,
  11326         };
  11327     };
  11328     switch (result) {
  11329         .success => return,
  11330         .failure => |err| return astgen.failWithStrLitError(err, token, bytes, offset),
  11331     }
  11332 }
  11333 
  11334 fn failWithStrLitError(
  11335     astgen: *AstGen,
  11336     err: std.zig.string_literal.Error,
  11337     token: Ast.TokenIndex,
  11338     bytes: []const u8,
  11339     offset: u32,
  11340 ) InnerError {
  11341     const raw_string = bytes[offset..];
  11342     return failOff(astgen, token, @intCast(offset + err.offset()), "{f}", .{err.fmt(raw_string)});
  11343 }
  11344 
  11345 fn failNode(
  11346     astgen: *AstGen,
  11347     node: Ast.Node.Index,
  11348     comptime format: []const u8,
  11349     args: anytype,
  11350 ) InnerError {
  11351     return astgen.failNodeNotes(node, format, args, &[0]u32{});
  11352 }
  11353 
  11354 fn appendErrorNode(
  11355     astgen: *AstGen,
  11356     node: Ast.Node.Index,
  11357     comptime format: []const u8,
  11358     args: anytype,
  11359 ) Allocator.Error!void {
  11360     try astgen.appendErrorNodeNotes(node, format, args, &[0]u32{});
  11361 }
  11362 
  11363 fn appendErrorNodeNotes(
  11364     astgen: *AstGen,
  11365     node: Ast.Node.Index,
  11366     comptime format: []const u8,
  11367     args: anytype,
  11368     notes: []const u32,
  11369 ) Allocator.Error!void {
  11370     @branchHint(.cold);
  11371     const gpa = astgen.gpa;
  11372     const string_bytes = &astgen.string_bytes;
  11373     const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
  11374     try string_bytes.print(gpa, format ++ "\x00", args);
  11375     const notes_index: u32 = if (notes.len != 0) blk: {
  11376         const notes_start = astgen.extra.items.len;
  11377         try astgen.extra.ensureTotalCapacity(gpa, notes_start + 1 + notes.len);
  11378         astgen.extra.appendAssumeCapacity(@intCast(notes.len));
  11379         astgen.extra.appendSliceAssumeCapacity(notes);
  11380         break :blk @intCast(notes_start);
  11381     } else 0;
  11382     try astgen.compile_errors.append(gpa, .{
  11383         .msg = msg,
  11384         .node = node.toOptional(),
  11385         .token = .none,
  11386         .byte_offset = 0,
  11387         .notes = notes_index,
  11388     });
  11389 }
  11390 
  11391 fn failNodeNotes(
  11392     astgen: *AstGen,
  11393     node: Ast.Node.Index,
  11394     comptime format: []const u8,
  11395     args: anytype,
  11396     notes: []const u32,
  11397 ) InnerError {
  11398     try appendErrorNodeNotes(astgen, node, format, args, notes);
  11399     return error.AnalysisFail;
  11400 }
  11401 
  11402 fn failTok(
  11403     astgen: *AstGen,
  11404     token: Ast.TokenIndex,
  11405     comptime format: []const u8,
  11406     args: anytype,
  11407 ) InnerError {
  11408     return astgen.failTokNotes(token, format, args, &[0]u32{});
  11409 }
  11410 
  11411 fn appendErrorTok(
  11412     astgen: *AstGen,
  11413     token: Ast.TokenIndex,
  11414     comptime format: []const u8,
  11415     args: anytype,
  11416 ) !void {
  11417     try astgen.appendErrorTokNotesOff(token, 0, format, args, &[0]u32{});
  11418 }
  11419 
  11420 fn failTokNotes(
  11421     astgen: *AstGen,
  11422     token: Ast.TokenIndex,
  11423     comptime format: []const u8,
  11424     args: anytype,
  11425     notes: []const u32,
  11426 ) InnerError {
  11427     try appendErrorTokNotesOff(astgen, token, 0, format, args, notes);
  11428     return error.AnalysisFail;
  11429 }
  11430 
  11431 fn appendErrorTokNotes(
  11432     astgen: *AstGen,
  11433     token: Ast.TokenIndex,
  11434     comptime format: []const u8,
  11435     args: anytype,
  11436     notes: []const u32,
  11437 ) !void {
  11438     return appendErrorTokNotesOff(astgen, token, 0, format, args, notes);
  11439 }
  11440 
  11441 /// Same as `fail`, except given a token plus an offset from its starting byte
  11442 /// offset.
  11443 fn failOff(
  11444     astgen: *AstGen,
  11445     token: Ast.TokenIndex,
  11446     byte_offset: u32,
  11447     comptime format: []const u8,
  11448     args: anytype,
  11449 ) InnerError {
  11450     try appendErrorTokNotesOff(astgen, token, byte_offset, format, args, &.{});
  11451     return error.AnalysisFail;
  11452 }
  11453 
  11454 fn appendErrorTokNotesOff(
  11455     astgen: *AstGen,
  11456     token: Ast.TokenIndex,
  11457     byte_offset: u32,
  11458     comptime format: []const u8,
  11459     args: anytype,
  11460     notes: []const u32,
  11461 ) !void {
  11462     @branchHint(.cold);
  11463     const gpa = astgen.gpa;
  11464     const string_bytes = &astgen.string_bytes;
  11465     const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
  11466     try string_bytes.print(gpa, format ++ "\x00", args);
  11467     const notes_index: u32 = if (notes.len != 0) blk: {
  11468         const notes_start = astgen.extra.items.len;
  11469         try astgen.extra.ensureTotalCapacity(gpa, notes_start + 1 + notes.len);
  11470         astgen.extra.appendAssumeCapacity(@intCast(notes.len));
  11471         astgen.extra.appendSliceAssumeCapacity(notes);
  11472         break :blk @intCast(notes_start);
  11473     } else 0;
  11474     try astgen.compile_errors.append(gpa, .{
  11475         .msg = msg,
  11476         .node = .none,
  11477         .token = .fromToken(token),
  11478         .byte_offset = byte_offset,
  11479         .notes = notes_index,
  11480     });
  11481 }
  11482 
  11483 fn errNoteTok(
  11484     astgen: *AstGen,
  11485     token: Ast.TokenIndex,
  11486     comptime format: []const u8,
  11487     args: anytype,
  11488 ) Allocator.Error!u32 {
  11489     return errNoteTokOff(astgen, token, 0, format, args);
  11490 }
  11491 
  11492 fn errNoteTokOff(
  11493     astgen: *AstGen,
  11494     token: Ast.TokenIndex,
  11495     byte_offset: u32,
  11496     comptime format: []const u8,
  11497     args: anytype,
  11498 ) Allocator.Error!u32 {
  11499     @branchHint(.cold);
  11500     const string_bytes = &astgen.string_bytes;
  11501     const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
  11502     try string_bytes.print(astgen.gpa, format ++ "\x00", args);
  11503     return astgen.addExtra(Zir.Inst.CompileErrors.Item{
  11504         .msg = msg,
  11505         .node = .none,
  11506         .token = .fromToken(token),
  11507         .byte_offset = byte_offset,
  11508         .notes = 0,
  11509     });
  11510 }
  11511 
  11512 fn errNoteNode(
  11513     astgen: *AstGen,
  11514     node: Ast.Node.Index,
  11515     comptime format: []const u8,
  11516     args: anytype,
  11517 ) Allocator.Error!u32 {
  11518     @branchHint(.cold);
  11519     const string_bytes = &astgen.string_bytes;
  11520     const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
  11521     try string_bytes.print(astgen.gpa, format ++ "\x00", args);
  11522     return astgen.addExtra(Zir.Inst.CompileErrors.Item{
  11523         .msg = msg,
  11524         .node = node.toOptional(),
  11525         .token = .none,
  11526         .byte_offset = 0,
  11527         .notes = 0,
  11528     });
  11529 }
  11530 
  11531 fn identAsString(astgen: *AstGen, ident_token: Ast.TokenIndex) !Zir.NullTerminatedString {
  11532     const gpa = astgen.gpa;
  11533     const string_bytes = &astgen.string_bytes;
  11534     const str_index: u32 = @intCast(string_bytes.items.len);
  11535     try astgen.appendIdentStr(ident_token, string_bytes);
  11536     const key: []const u8 = string_bytes.items[str_index..];
  11537     const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
  11538         .bytes = string_bytes,
  11539     }, StringIndexContext{
  11540         .bytes = string_bytes,
  11541     });
  11542     if (gop.found_existing) {
  11543         string_bytes.shrinkRetainingCapacity(str_index);
  11544         return @enumFromInt(gop.key_ptr.*);
  11545     } else {
  11546         gop.key_ptr.* = str_index;
  11547         try string_bytes.append(gpa, 0);
  11548         return @enumFromInt(str_index);
  11549     }
  11550 }
  11551 
  11552 const IndexSlice = struct { index: Zir.NullTerminatedString, len: u32 };
  11553 
  11554 fn strLitAsString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !IndexSlice {
  11555     const gpa = astgen.gpa;
  11556     const string_bytes = &astgen.string_bytes;
  11557     const str_index: u32 = @intCast(string_bytes.items.len);
  11558     const token_bytes = astgen.tree.tokenSlice(str_lit_token);
  11559     try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0);
  11560     const key: []const u8 = string_bytes.items[str_index..];
  11561     if (std.mem.indexOfScalar(u8, key, 0)) |_| return .{
  11562         .index = @enumFromInt(str_index),
  11563         .len = @intCast(key.len),
  11564     };
  11565     const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
  11566         .bytes = string_bytes,
  11567     }, StringIndexContext{
  11568         .bytes = string_bytes,
  11569     });
  11570     if (gop.found_existing) {
  11571         string_bytes.shrinkRetainingCapacity(str_index);
  11572         return .{
  11573             .index = @enumFromInt(gop.key_ptr.*),
  11574             .len = @intCast(key.len),
  11575         };
  11576     } else {
  11577         gop.key_ptr.* = str_index;
  11578         // Still need a null byte because we are using the same table
  11579         // to lookup null terminated strings, so if we get a match, it has to
  11580         // be null terminated for that to work.
  11581         try string_bytes.append(gpa, 0);
  11582         return .{
  11583             .index = @enumFromInt(str_index),
  11584             .len = @intCast(key.len),
  11585         };
  11586     }
  11587 }
  11588 
  11589 fn strLitNodeAsString(astgen: *AstGen, node: Ast.Node.Index) !IndexSlice {
  11590     const tree = astgen.tree;
  11591 
  11592     const start, const end = tree.nodeData(node).token_and_token;
  11593 
  11594     const gpa = astgen.gpa;
  11595     const string_bytes = &astgen.string_bytes;
  11596     const str_index = string_bytes.items.len;
  11597 
  11598     // First line: do not append a newline.
  11599     var tok_i = start;
  11600     {
  11601         const slice = tree.tokenSlice(tok_i);
  11602         const line_bytes = slice[2..];
  11603         try string_bytes.appendSlice(gpa, line_bytes);
  11604         tok_i += 1;
  11605     }
  11606     // Following lines: each line prepends a newline.
  11607     while (tok_i <= end) : (tok_i += 1) {
  11608         const slice = tree.tokenSlice(tok_i);
  11609         const line_bytes = slice[2..];
  11610         try string_bytes.ensureUnusedCapacity(gpa, line_bytes.len + 1);
  11611         string_bytes.appendAssumeCapacity('\n');
  11612         string_bytes.appendSliceAssumeCapacity(line_bytes);
  11613     }
  11614     const len = string_bytes.items.len - str_index;
  11615     try string_bytes.append(gpa, 0);
  11616     return IndexSlice{
  11617         .index = @enumFromInt(str_index),
  11618         .len = @intCast(len),
  11619     };
  11620 }
  11621 
  11622 const Scope = struct {
  11623     tag: Tag,
  11624 
  11625     fn cast(base: *Scope, comptime T: type) ?*T {
  11626         if (T == Defer) {
  11627             switch (base.tag) {
  11628                 .defer_normal, .defer_error => return @alignCast(@fieldParentPtr("base", base)),
  11629                 else => return null,
  11630             }
  11631         }
  11632         if (T == Namespace) {
  11633             switch (base.tag) {
  11634                 .namespace => return @alignCast(@fieldParentPtr("base", base)),
  11635                 else => return null,
  11636             }
  11637         }
  11638         if (base.tag != T.base_tag)
  11639             return null;
  11640 
  11641         return @alignCast(@fieldParentPtr("base", base));
  11642     }
  11643 
  11644     fn parent(base: *Scope) ?*Scope {
  11645         return switch (base.tag) {
  11646             .gen_zir => base.cast(GenZir).?.parent,
  11647             .local_val => base.cast(LocalVal).?.parent,
  11648             .local_ptr => base.cast(LocalPtr).?.parent,
  11649             .defer_normal, .defer_error => base.cast(Defer).?.parent,
  11650             .namespace => base.cast(Namespace).?.parent,
  11651             .top => null,
  11652         };
  11653     }
  11654 
  11655     const Tag = enum {
  11656         gen_zir,
  11657         local_val,
  11658         local_ptr,
  11659         defer_normal,
  11660         defer_error,
  11661         namespace,
  11662         top,
  11663     };
  11664 
  11665     /// The category of identifier. These tag names are user-visible in compile errors.
  11666     const IdCat = enum {
  11667         @"function parameter",
  11668         @"local constant",
  11669         @"local variable",
  11670         @"switch tag capture",
  11671         capture,
  11672     };
  11673 
  11674     /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.
  11675     /// This structure lives as long as the AST generation of the Block
  11676     /// node that contains the variable.
  11677     const LocalVal = struct {
  11678         const base_tag: Tag = .local_val;
  11679         base: Scope = Scope{ .tag = base_tag },
  11680         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
  11681         parent: *Scope,
  11682         gen_zir: *GenZir,
  11683         inst: Zir.Inst.Ref,
  11684         /// Source location of the corresponding variable declaration.
  11685         token_src: Ast.TokenIndex,
  11686         /// Track the first identifier where it is referenced.
  11687         /// .none means never referenced.
  11688         used: Ast.OptionalTokenIndex = .none,
  11689         /// Track the identifier where it is discarded, like this `_ = foo;`.
  11690         /// .none means never discarded.
  11691         discarded: Ast.OptionalTokenIndex = .none,
  11692         is_used_or_discarded: ?*bool = null,
  11693         /// String table index.
  11694         name: Zir.NullTerminatedString,
  11695         id_cat: IdCat,
  11696     };
  11697 
  11698     /// This could be a `const` or `var` local. It has a pointer instead of a value.
  11699     /// This structure lives as long as the AST generation of the Block
  11700     /// node that contains the variable.
  11701     const LocalPtr = struct {
  11702         const base_tag: Tag = .local_ptr;
  11703         base: Scope = Scope{ .tag = base_tag },
  11704         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
  11705         parent: *Scope,
  11706         gen_zir: *GenZir,
  11707         ptr: Zir.Inst.Ref,
  11708         /// Source location of the corresponding variable declaration.
  11709         token_src: Ast.TokenIndex,
  11710         /// Track the first identifier where it is referenced.
  11711         /// .none means never referenced.
  11712         used: Ast.OptionalTokenIndex = .none,
  11713         /// Track the identifier where it is discarded, like this `_ = foo;`.
  11714         /// .none means never discarded.
  11715         discarded: Ast.OptionalTokenIndex = .none,
  11716         /// Whether this value is used as an lvalue after initialization.
  11717         /// If not, we know it can be `const`, so will emit a compile error if it is `var`.
  11718         used_as_lvalue: bool = false,
  11719         /// String table index.
  11720         name: Zir.NullTerminatedString,
  11721         id_cat: IdCat,
  11722         /// true means we find out during Sema whether the value is comptime.
  11723         /// false means it is already known at AstGen the value is runtime-known.
  11724         maybe_comptime: bool,
  11725     };
  11726 
  11727     const Defer = struct {
  11728         base: Scope,
  11729         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
  11730         parent: *Scope,
  11731         index: u32,
  11732         len: u32,
  11733         remapped_err_code: Zir.Inst.OptionalIndex = .none,
  11734     };
  11735 
  11736     /// Represents a global scope that has any number of declarations in it.
  11737     /// Each declaration has this as the parent scope.
  11738     const Namespace = struct {
  11739         const base_tag: Tag = .namespace;
  11740         base: Scope = Scope{ .tag = base_tag },
  11741 
  11742         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
  11743         parent: *Scope,
  11744         /// Maps string table index to the source location of declaration,
  11745         /// for the purposes of reporting name shadowing compile errors.
  11746         decls: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.Node.Index) = .empty,
  11747         node: Ast.Node.Index,
  11748         inst: Zir.Inst.Index,
  11749         maybe_generic: bool,
  11750 
  11751         /// The astgen scope containing this namespace.
  11752         /// Only valid during astgen.
  11753         declaring_gz: ?*GenZir,
  11754 
  11755         /// Set of captures used by this namespace.
  11756         captures: std.AutoArrayHashMapUnmanaged(Zir.Inst.Capture, Zir.NullTerminatedString) = .empty,
  11757 
  11758         fn deinit(self: *Namespace, gpa: Allocator) void {
  11759             self.decls.deinit(gpa);
  11760             self.captures.deinit(gpa);
  11761             self.* = undefined;
  11762         }
  11763     };
  11764 
  11765     const Top = struct {
  11766         const base_tag: Scope.Tag = .top;
  11767         base: Scope = Scope{ .tag = base_tag },
  11768     };
  11769 };
  11770 
  11771 /// This is a temporary structure; references to it are valid only
  11772 /// while constructing a `Zir`.
  11773 const GenZir = struct {
  11774     const base_tag: Scope.Tag = .gen_zir;
  11775     base: Scope = Scope{ .tag = base_tag },
  11776     /// Whether we're already in a scope known to be comptime. This is set
  11777     /// whenever we know Sema will analyze the current block with `is_comptime`,
  11778     /// for instance when we're within a `struct_decl` or a `block_comptime`.
  11779     is_comptime: bool,
  11780     /// Whether we're in an expression within a `@TypeOf` operand. In this case, closure of runtime
  11781     /// variables is permitted where it is usually not.
  11782     is_typeof: bool = false,
  11783     /// This is set to true for a `GenZir` of a `block_inline`, indicating that
  11784     /// exits from this block should use `break_inline` rather than `break`.
  11785     is_inline: bool = false,
  11786     c_import: bool = false,
  11787     /// The containing decl AST node.
  11788     decl_node_index: Ast.Node.Index,
  11789     /// The containing decl line index, absolute.
  11790     decl_line: u32,
  11791     /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
  11792     parent: *Scope,
  11793     /// All `GenZir` scopes for the same ZIR share this.
  11794     astgen: *AstGen,
  11795     /// Keeps track of the list of instructions in this scope. Possibly shared.
  11796     /// Indexes to instructions in `astgen`.
  11797     instructions: *ArrayListUnmanaged(Zir.Inst.Index),
  11798     /// A sub-block may share its instructions ArrayList with containing GenZir,
  11799     /// if use is strictly nested. This saves prior size of list for unstacking.
  11800     instructions_top: usize,
  11801     label: ?Label = null,
  11802     break_block: Zir.Inst.OptionalIndex = .none,
  11803     continue_block: Zir.Inst.OptionalIndex = .none,
  11804     /// Only valid when setBreakResultInfo is called.
  11805     break_result_info: AstGen.ResultInfo = undefined,
  11806     continue_result_info: AstGen.ResultInfo = undefined,
  11807 
  11808     suspend_node: Ast.Node.OptionalIndex = .none,
  11809     nosuspend_node: Ast.Node.OptionalIndex = .none,
  11810     /// Set if this GenZir is a defer.
  11811     cur_defer_node: Ast.Node.OptionalIndex = .none,
  11812     // Set if this GenZir is a defer or it is inside a defer.
  11813     any_defer_node: Ast.Node.OptionalIndex = .none,
  11814 
  11815     const unstacked_top = std.math.maxInt(usize);
  11816     /// Call unstack before adding any new instructions to containing GenZir.
  11817     fn unstack(self: *GenZir) void {
  11818         if (self.instructions_top != unstacked_top) {
  11819             self.instructions.items.len = self.instructions_top;
  11820             self.instructions_top = unstacked_top;
  11821         }
  11822     }
  11823 
  11824     fn isEmpty(self: *const GenZir) bool {
  11825         return (self.instructions_top == unstacked_top) or
  11826             (self.instructions.items.len == self.instructions_top);
  11827     }
  11828 
  11829     fn instructionsSlice(self: *const GenZir) []Zir.Inst.Index {
  11830         return if (self.instructions_top == unstacked_top)
  11831             &[0]Zir.Inst.Index{}
  11832         else
  11833             self.instructions.items[self.instructions_top..];
  11834     }
  11835 
  11836     fn instructionsSliceUpto(self: *const GenZir, stacked_gz: *GenZir) []Zir.Inst.Index {
  11837         return if (self.instructions_top == unstacked_top)
  11838             &[0]Zir.Inst.Index{}
  11839         else if (self.instructions == stacked_gz.instructions and stacked_gz.instructions_top != unstacked_top)
  11840             self.instructions.items[self.instructions_top..stacked_gz.instructions_top]
  11841         else
  11842             self.instructions.items[self.instructions_top..];
  11843     }
  11844 
  11845     fn instructionsSliceUptoOpt(gz: *const GenZir, maybe_stacked_gz: ?*GenZir) []Zir.Inst.Index {
  11846         if (maybe_stacked_gz) |stacked_gz| {
  11847             return gz.instructionsSliceUpto(stacked_gz);
  11848         } else {
  11849             return gz.instructionsSlice();
  11850         }
  11851     }
  11852 
  11853     fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {
  11854         return .{
  11855             .is_comptime = gz.is_comptime,
  11856             .is_typeof = gz.is_typeof,
  11857             .c_import = gz.c_import,
  11858             .decl_node_index = gz.decl_node_index,
  11859             .decl_line = gz.decl_line,
  11860             .parent = scope,
  11861             .astgen = gz.astgen,
  11862             .suspend_node = gz.suspend_node,
  11863             .nosuspend_node = gz.nosuspend_node,
  11864             .any_defer_node = gz.any_defer_node,
  11865             .instructions = gz.instructions,
  11866             .instructions_top = gz.instructions.items.len,
  11867         };
  11868     }
  11869 
  11870     const Label = struct {
  11871         token: Ast.TokenIndex,
  11872         block_inst: Zir.Inst.Index,
  11873         used: bool = false,
  11874         used_for_continue: bool = false,
  11875     };
  11876 
  11877     /// Assumes nothing stacked on `gz`.
  11878     fn endsWithNoReturn(gz: GenZir) bool {
  11879         if (gz.isEmpty()) return false;
  11880         const tags = gz.astgen.instructions.items(.tag);
  11881         const last_inst = gz.instructions.items[gz.instructions.items.len - 1];
  11882         return tags[@intFromEnum(last_inst)].isNoReturn();
  11883     }
  11884 
  11885     /// TODO all uses of this should be replaced with uses of `endsWithNoReturn`.
  11886     fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool {
  11887         if (inst_ref == .unreachable_value) return true;
  11888         if (inst_ref.toIndex()) |inst_index| {
  11889             return gz.astgen.instructions.items(.tag)[@intFromEnum(inst_index)].isNoReturn();
  11890         }
  11891         return false;
  11892     }
  11893 
  11894     fn nodeIndexToRelative(gz: GenZir, node_index: Ast.Node.Index) Ast.Node.Offset {
  11895         return gz.decl_node_index.toOffset(node_index);
  11896     }
  11897 
  11898     fn tokenIndexToRelative(gz: GenZir, token: Ast.TokenIndex) Ast.TokenOffset {
  11899         return .init(gz.srcToken(), token);
  11900     }
  11901 
  11902     fn srcToken(gz: GenZir) Ast.TokenIndex {
  11903         return gz.astgen.tree.firstToken(gz.decl_node_index);
  11904     }
  11905 
  11906     fn setBreakResultInfo(gz: *GenZir, parent_ri: AstGen.ResultInfo) void {
  11907         // Depending on whether the result location is a pointer or value, different
  11908         // ZIR needs to be generated. In the former case we rely on storing to the
  11909         // pointer to communicate the result, and use breakvoid; in the latter case
  11910         // the block break instructions will have the result values.
  11911         switch (parent_ri.rl) {
  11912             .coerced_ty => |ty_inst| {
  11913                 // Type coercion needs to happen before breaks.
  11914                 gz.break_result_info = .{ .rl = .{ .ty = ty_inst }, .ctx = parent_ri.ctx };
  11915             },
  11916             .discard => {
  11917                 // We don't forward the result context here. This prevents
  11918                 // "unnecessary discard" errors from being caused by expressions
  11919                 // far from the actual discard, such as a `break` from a
  11920                 // discarded block.
  11921                 gz.break_result_info = .{ .rl = .discard };
  11922             },
  11923             else => {
  11924                 gz.break_result_info = parent_ri;
  11925             },
  11926         }
  11927     }
  11928 
  11929     /// Assumes nothing stacked on `gz`. Unstacks `gz`.
  11930     fn setBoolBrBody(gz: *GenZir, bool_br: Zir.Inst.Index, bool_br_lhs: Zir.Inst.Ref) !void {
  11931         const astgen = gz.astgen;
  11932         const gpa = astgen.gpa;
  11933         const body = gz.instructionsSlice();
  11934         const body_len = astgen.countBodyLenAfterFixups(body);
  11935         try astgen.extra.ensureUnusedCapacity(
  11936             gpa,
  11937             @typeInfo(Zir.Inst.BoolBr).@"struct".fields.len + body_len,
  11938         );
  11939         const zir_datas = astgen.instructions.items(.data);
  11940         zir_datas[@intFromEnum(bool_br)].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.BoolBr{
  11941             .lhs = bool_br_lhs,
  11942             .body_len = body_len,
  11943         });
  11944         astgen.appendBodyWithFixups(body);
  11945         gz.unstack();
  11946     }
  11947 
  11948     /// Assumes nothing stacked on `gz`. Unstacks `gz`.
  11949     /// Asserts `inst` is not a `block_comptime`.
  11950     fn setBlockBody(gz: *GenZir, inst: Zir.Inst.Index) !void {
  11951         const astgen = gz.astgen;
  11952         const gpa = astgen.gpa;
  11953         const body = gz.instructionsSlice();
  11954         const body_len = astgen.countBodyLenAfterFixups(body);
  11955 
  11956         const zir_tags = astgen.instructions.items(.tag);
  11957         assert(zir_tags[@intFromEnum(inst)] != .block_comptime); // use `setComptimeBlockBody` instead
  11958 
  11959         try astgen.extra.ensureUnusedCapacity(
  11960             gpa,
  11961             @typeInfo(Zir.Inst.Block).@"struct".fields.len + body_len,
  11962         );
  11963         const zir_datas = astgen.instructions.items(.data);
  11964         zir_datas[@intFromEnum(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity(
  11965             Zir.Inst.Block{ .body_len = body_len },
  11966         );
  11967         astgen.appendBodyWithFixups(body);
  11968         gz.unstack();
  11969     }
  11970 
  11971     /// Assumes nothing stacked on `gz`. Unstacks `gz`.
  11972     /// Asserts `inst` is a `block_comptime`.
  11973     fn setBlockComptimeBody(gz: *GenZir, inst: Zir.Inst.Index, comptime_reason: std.zig.SimpleComptimeReason) !void {
  11974         const astgen = gz.astgen;
  11975         const gpa = astgen.gpa;
  11976         const body = gz.instructionsSlice();
  11977         const body_len = astgen.countBodyLenAfterFixups(body);
  11978 
  11979         const zir_tags = astgen.instructions.items(.tag);
  11980         assert(zir_tags[@intFromEnum(inst)] == .block_comptime); // use `setBlockBody` instead
  11981 
  11982         try astgen.extra.ensureUnusedCapacity(
  11983             gpa,
  11984             @typeInfo(Zir.Inst.BlockComptime).@"struct".fields.len + body_len,
  11985         );
  11986         const zir_datas = astgen.instructions.items(.data);
  11987         zir_datas[@intFromEnum(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity(
  11988             Zir.Inst.BlockComptime{
  11989                 .reason = comptime_reason,
  11990                 .body_len = body_len,
  11991             },
  11992         );
  11993         astgen.appendBodyWithFixups(body);
  11994         gz.unstack();
  11995     }
  11996 
  11997     /// Assumes nothing stacked on `gz`. Unstacks `gz`.
  11998     fn setTryBody(gz: *GenZir, inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !void {
  11999         const astgen = gz.astgen;
  12000         const gpa = astgen.gpa;
  12001         const body = gz.instructionsSlice();
  12002         const body_len = astgen.countBodyLenAfterFixups(body);
  12003         try astgen.extra.ensureUnusedCapacity(
  12004             gpa,
  12005             @typeInfo(Zir.Inst.Try).@"struct".fields.len + body_len,
  12006         );
  12007         const zir_datas = astgen.instructions.items(.data);
  12008         zir_datas[@intFromEnum(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity(
  12009             Zir.Inst.Try{
  12010                 .operand = operand,
  12011                 .body_len = body_len,
  12012             },
  12013         );
  12014         astgen.appendBodyWithFixups(body);
  12015         gz.unstack();
  12016     }
  12017 
  12018     /// Must be called with the following stack set up:
  12019     ///  * gz (bottom)
  12020     ///  * ret_gz
  12021     ///  * cc_gz
  12022     ///  * body_gz (top)
  12023     /// Unstacks all of those except for `gz`.
  12024     fn addFunc(
  12025         gz: *GenZir,
  12026         args: struct {
  12027             src_node: Ast.Node.Index,
  12028             lbrace_line: u32 = 0,
  12029             lbrace_column: u32 = 0,
  12030             param_block: Zir.Inst.Index,
  12031 
  12032             ret_gz: ?*GenZir,
  12033             body_gz: ?*GenZir,
  12034             cc_gz: ?*GenZir,
  12035 
  12036             ret_param_refs: []Zir.Inst.Index,
  12037             param_insts: []Zir.Inst.Index, // refs to params in `body_gz` should still be in `astgen.ref_table`
  12038             ret_ty_is_generic: bool,
  12039 
  12040             cc_ref: Zir.Inst.Ref,
  12041             ret_ref: Zir.Inst.Ref,
  12042 
  12043             noalias_bits: u32,
  12044             is_var_args: bool,
  12045             is_inferred_error: bool,
  12046             is_noinline: bool,
  12047 
  12048             /// Ignored if `body_gz == null`.
  12049             proto_hash: std.zig.SrcHash,
  12050         },
  12051     ) !Zir.Inst.Ref {
  12052         assert(args.src_node != .root);
  12053         const astgen = gz.astgen;
  12054         const gpa = astgen.gpa;
  12055         const ret_ref = if (args.ret_ref == .void_type) .none else args.ret_ref;
  12056         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12057 
  12058         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12059         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12060 
  12061         const body, const cc_body, const ret_body = bodies: {
  12062             var stacked_gz: ?*GenZir = null;
  12063             const body: []const Zir.Inst.Index = if (args.body_gz) |body_gz| body: {
  12064                 const body = body_gz.instructionsSliceUptoOpt(stacked_gz);
  12065                 stacked_gz = body_gz;
  12066                 break :body body;
  12067             } else &.{};
  12068             const cc_body: []const Zir.Inst.Index = if (args.cc_gz) |cc_gz| body: {
  12069                 const cc_body = cc_gz.instructionsSliceUptoOpt(stacked_gz);
  12070                 stacked_gz = cc_gz;
  12071                 break :body cc_body;
  12072             } else &.{};
  12073             const ret_body: []const Zir.Inst.Index = if (args.ret_gz) |ret_gz| body: {
  12074                 const ret_body = ret_gz.instructionsSliceUptoOpt(stacked_gz);
  12075                 stacked_gz = ret_gz;
  12076                 break :body ret_body;
  12077             } else &.{};
  12078             break :bodies .{ body, cc_body, ret_body };
  12079         };
  12080 
  12081         var src_locs_and_hash_buffer: [7]u32 = undefined;
  12082         const src_locs_and_hash: []const u32 = if (args.body_gz != null) src_locs_and_hash: {
  12083             const tree = astgen.tree;
  12084             const fn_decl = args.src_node;
  12085             const block = switch (tree.nodeTag(fn_decl)) {
  12086                 .fn_decl => tree.nodeData(fn_decl).node_and_node[1],
  12087                 .test_decl => tree.nodeData(fn_decl).opt_token_and_node[1],
  12088                 else => unreachable,
  12089             };
  12090             const rbrace_start = tree.tokenStart(tree.lastToken(block));
  12091             astgen.advanceSourceCursor(rbrace_start);
  12092             const rbrace_line: u32 = @intCast(astgen.source_line - gz.decl_line);
  12093             const rbrace_column: u32 = @intCast(astgen.source_column);
  12094 
  12095             const columns = args.lbrace_column | (rbrace_column << 16);
  12096 
  12097             const proto_hash_arr: [4]u32 = @bitCast(args.proto_hash);
  12098 
  12099             src_locs_and_hash_buffer = .{
  12100                 args.lbrace_line,
  12101                 rbrace_line,
  12102                 columns,
  12103                 proto_hash_arr[0],
  12104                 proto_hash_arr[1],
  12105                 proto_hash_arr[2],
  12106                 proto_hash_arr[3],
  12107             };
  12108             break :src_locs_and_hash &src_locs_and_hash_buffer;
  12109         } else &.{};
  12110 
  12111         const body_len = astgen.countBodyLenAfterFixupsExtraRefs(body, args.param_insts);
  12112 
  12113         const tag: Zir.Inst.Tag, const payload_index: u32 = if (args.cc_ref != .none or
  12114             args.is_var_args or args.noalias_bits != 0 or args.is_noinline)
  12115         inst_info: {
  12116             try astgen.extra.ensureUnusedCapacity(
  12117                 gpa,
  12118                 @typeInfo(Zir.Inst.FuncFancy).@"struct".fields.len +
  12119                     fancyFnExprExtraLen(astgen, &.{}, cc_body, args.cc_ref) +
  12120                     fancyFnExprExtraLen(astgen, args.ret_param_refs, ret_body, ret_ref) +
  12121                     body_len + src_locs_and_hash.len +
  12122                     @intFromBool(args.noalias_bits != 0),
  12123             );
  12124             const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.FuncFancy{
  12125                 .param_block = args.param_block,
  12126                 .body_len = body_len,
  12127                 .bits = .{
  12128                     .is_var_args = args.is_var_args,
  12129                     .is_inferred_error = args.is_inferred_error,
  12130                     .is_noinline = args.is_noinline,
  12131                     .has_any_noalias = args.noalias_bits != 0,
  12132 
  12133                     .has_cc_ref = args.cc_ref != .none,
  12134                     .has_ret_ty_ref = ret_ref != .none,
  12135 
  12136                     .has_cc_body = cc_body.len != 0,
  12137                     .has_ret_ty_body = ret_body.len != 0,
  12138 
  12139                     .ret_ty_is_generic = args.ret_ty_is_generic,
  12140                 },
  12141             });
  12142 
  12143             const zir_datas = astgen.instructions.items(.data);
  12144             if (cc_body.len != 0) {
  12145                 astgen.extra.appendAssumeCapacity(astgen.countBodyLenAfterFixups(cc_body));
  12146                 astgen.appendBodyWithFixups(cc_body);
  12147                 const break_extra = zir_datas[@intFromEnum(cc_body[cc_body.len - 1])].@"break".payload_index;
  12148                 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
  12149                     @intFromEnum(new_index);
  12150             } else if (args.cc_ref != .none) {
  12151                 astgen.extra.appendAssumeCapacity(@intFromEnum(args.cc_ref));
  12152             }
  12153             if (ret_body.len != 0) {
  12154                 astgen.extra.appendAssumeCapacity(
  12155                     astgen.countBodyLenAfterFixups(args.ret_param_refs) +
  12156                         astgen.countBodyLenAfterFixups(ret_body),
  12157                 );
  12158                 astgen.appendBodyWithFixups(args.ret_param_refs);
  12159                 astgen.appendBodyWithFixups(ret_body);
  12160                 const break_extra = zir_datas[@intFromEnum(ret_body[ret_body.len - 1])].@"break".payload_index;
  12161                 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
  12162                     @intFromEnum(new_index);
  12163             } else if (ret_ref != .none) {
  12164                 astgen.extra.appendAssumeCapacity(@intFromEnum(ret_ref));
  12165             }
  12166 
  12167             if (args.noalias_bits != 0) {
  12168                 astgen.extra.appendAssumeCapacity(args.noalias_bits);
  12169             }
  12170 
  12171             astgen.appendBodyWithFixupsExtraRefsArrayList(&astgen.extra, body, args.param_insts);
  12172             astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash);
  12173 
  12174             break :inst_info .{ .func_fancy, payload_index };
  12175         } else inst_info: {
  12176             try astgen.extra.ensureUnusedCapacity(
  12177                 gpa,
  12178                 @typeInfo(Zir.Inst.Func).@"struct".fields.len + 1 +
  12179                     fancyFnExprExtraLen(astgen, args.ret_param_refs, ret_body, ret_ref) +
  12180                     body_len + src_locs_and_hash.len,
  12181             );
  12182 
  12183             const ret_body_len = if (ret_body.len != 0)
  12184                 countBodyLenAfterFixups(astgen, args.ret_param_refs) + countBodyLenAfterFixups(astgen, ret_body)
  12185             else
  12186                 @intFromBool(ret_ref != .none);
  12187 
  12188             const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.Func{
  12189                 .param_block = args.param_block,
  12190                 .ret_ty = .{
  12191                     .body_len = @intCast(ret_body_len),
  12192                     .is_generic = args.ret_ty_is_generic,
  12193                 },
  12194                 .body_len = body_len,
  12195             });
  12196             const zir_datas = astgen.instructions.items(.data);
  12197             if (ret_body.len != 0) {
  12198                 astgen.appendBodyWithFixups(args.ret_param_refs);
  12199                 astgen.appendBodyWithFixups(ret_body);
  12200 
  12201                 const break_extra = zir_datas[@intFromEnum(ret_body[ret_body.len - 1])].@"break".payload_index;
  12202                 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
  12203                     @intFromEnum(new_index);
  12204             } else if (ret_ref != .none) {
  12205                 astgen.extra.appendAssumeCapacity(@intFromEnum(ret_ref));
  12206             }
  12207             astgen.appendBodyWithFixupsExtraRefsArrayList(&astgen.extra, body, args.param_insts);
  12208             astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash);
  12209 
  12210             break :inst_info .{
  12211                 if (args.is_inferred_error) .func_inferred else .func,
  12212                 payload_index,
  12213             };
  12214         };
  12215 
  12216         // Order is important when unstacking.
  12217         if (args.body_gz) |body_gz| body_gz.unstack();
  12218         if (args.cc_gz) |cc_gz| cc_gz.unstack();
  12219         if (args.ret_gz) |ret_gz| ret_gz.unstack();
  12220 
  12221         astgen.instructions.appendAssumeCapacity(.{
  12222             .tag = tag,
  12223             .data = .{ .pl_node = .{
  12224                 .src_node = gz.nodeIndexToRelative(args.src_node),
  12225                 .payload_index = payload_index,
  12226             } },
  12227         });
  12228         gz.instructions.appendAssumeCapacity(new_index);
  12229         return new_index.toRef();
  12230     }
  12231 
  12232     fn fancyFnExprExtraLen(astgen: *AstGen, param_refs_body: []const Zir.Inst.Index, main_body: []const Zir.Inst.Index, ref: Zir.Inst.Ref) u32 {
  12233         return countBodyLenAfterFixups(astgen, param_refs_body) +
  12234             countBodyLenAfterFixups(astgen, main_body) +
  12235             // If there is a body, we need an element for its length; otherwise, if there is a ref, we need to include that.
  12236             @intFromBool(main_body.len > 0 or ref != .none);
  12237     }
  12238 
  12239     fn addInt(gz: *GenZir, integer: u64) !Zir.Inst.Ref {
  12240         return gz.add(.{
  12241             .tag = .int,
  12242             .data = .{ .int = integer },
  12243         });
  12244     }
  12245 
  12246     fn addIntBig(gz: *GenZir, limbs: []const std.math.big.Limb) !Zir.Inst.Ref {
  12247         const astgen = gz.astgen;
  12248         const gpa = astgen.gpa;
  12249         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12250         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12251         try astgen.string_bytes.ensureUnusedCapacity(gpa, @sizeOf(std.math.big.Limb) * limbs.len);
  12252 
  12253         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12254         astgen.instructions.appendAssumeCapacity(.{
  12255             .tag = .int_big,
  12256             .data = .{ .str = .{
  12257                 .start = @enumFromInt(astgen.string_bytes.items.len),
  12258                 .len = @intCast(limbs.len),
  12259             } },
  12260         });
  12261         gz.instructions.appendAssumeCapacity(new_index);
  12262         astgen.string_bytes.appendSliceAssumeCapacity(mem.sliceAsBytes(limbs));
  12263         return new_index.toRef();
  12264     }
  12265 
  12266     fn addFloat(gz: *GenZir, number: f64) !Zir.Inst.Ref {
  12267         return gz.add(.{
  12268             .tag = .float,
  12269             .data = .{ .float = number },
  12270         });
  12271     }
  12272 
  12273     fn addUnNode(
  12274         gz: *GenZir,
  12275         tag: Zir.Inst.Tag,
  12276         operand: Zir.Inst.Ref,
  12277         /// Absolute node index. This function does the conversion to offset from Decl.
  12278         src_node: Ast.Node.Index,
  12279     ) !Zir.Inst.Ref {
  12280         assert(operand != .none);
  12281         return gz.add(.{
  12282             .tag = tag,
  12283             .data = .{ .un_node = .{
  12284                 .operand = operand,
  12285                 .src_node = gz.nodeIndexToRelative(src_node),
  12286             } },
  12287         });
  12288     }
  12289 
  12290     fn makeUnNode(
  12291         gz: *GenZir,
  12292         tag: Zir.Inst.Tag,
  12293         operand: Zir.Inst.Ref,
  12294         /// Absolute node index. This function does the conversion to offset from Decl.
  12295         src_node: Ast.Node.Index,
  12296     ) !Zir.Inst.Index {
  12297         assert(operand != .none);
  12298         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  12299         try gz.astgen.instructions.append(gz.astgen.gpa, .{
  12300             .tag = tag,
  12301             .data = .{ .un_node = .{
  12302                 .operand = operand,
  12303                 .src_node = gz.nodeIndexToRelative(src_node),
  12304             } },
  12305         });
  12306         return new_index;
  12307     }
  12308 
  12309     fn addPlNode(
  12310         gz: *GenZir,
  12311         tag: Zir.Inst.Tag,
  12312         /// Absolute node index. This function does the conversion to offset from Decl.
  12313         src_node: Ast.Node.Index,
  12314         extra: anytype,
  12315     ) !Zir.Inst.Ref {
  12316         const gpa = gz.astgen.gpa;
  12317         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12318         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12319 
  12320         const payload_index = try gz.astgen.addExtra(extra);
  12321         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  12322         gz.astgen.instructions.appendAssumeCapacity(.{
  12323             .tag = tag,
  12324             .data = .{ .pl_node = .{
  12325                 .src_node = gz.nodeIndexToRelative(src_node),
  12326                 .payload_index = payload_index,
  12327             } },
  12328         });
  12329         gz.instructions.appendAssumeCapacity(new_index);
  12330         return new_index.toRef();
  12331     }
  12332 
  12333     fn addPlNodePayloadIndex(
  12334         gz: *GenZir,
  12335         tag: Zir.Inst.Tag,
  12336         /// Absolute node index. This function does the conversion to offset from Decl.
  12337         src_node: Ast.Node.Index,
  12338         payload_index: u32,
  12339     ) !Zir.Inst.Ref {
  12340         return try gz.add(.{
  12341             .tag = tag,
  12342             .data = .{ .pl_node = .{
  12343                 .src_node = gz.nodeIndexToRelative(src_node),
  12344                 .payload_index = payload_index,
  12345             } },
  12346         });
  12347     }
  12348 
  12349     /// Supports `param_gz` stacked on `gz`. Assumes nothing stacked on `param_gz`. Unstacks `param_gz`.
  12350     fn addParam(
  12351         gz: *GenZir,
  12352         param_gz: *GenZir,
  12353         /// Previous parameters, which might be referenced in `param_gz` (the new parameter type).
  12354         /// `ref`s of these instructions will be put into this param's type body, and removed from `AstGen.ref_table`.
  12355         prev_param_insts: []const Zir.Inst.Index,
  12356         ty_is_generic: bool,
  12357         tag: Zir.Inst.Tag,
  12358         /// Absolute token index. This function does the conversion to Decl offset.
  12359         abs_tok_index: Ast.TokenIndex,
  12360         name: Zir.NullTerminatedString,
  12361     ) !Zir.Inst.Index {
  12362         const gpa = gz.astgen.gpa;
  12363         const param_body = param_gz.instructionsSlice();
  12364         const body_len = gz.astgen.countBodyLenAfterFixupsExtraRefs(param_body, prev_param_insts);
  12365         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12366         try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Param).@"struct".fields.len + body_len);
  12367 
  12368         const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Param{
  12369             .name = name,
  12370             .type = .{
  12371                 .body_len = @intCast(body_len),
  12372                 .is_generic = ty_is_generic,
  12373             },
  12374         });
  12375         gz.astgen.appendBodyWithFixupsExtraRefsArrayList(&gz.astgen.extra, param_body, prev_param_insts);
  12376         param_gz.unstack();
  12377 
  12378         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  12379         gz.astgen.instructions.appendAssumeCapacity(.{
  12380             .tag = tag,
  12381             .data = .{ .pl_tok = .{
  12382                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  12383                 .payload_index = payload_index,
  12384             } },
  12385         });
  12386         gz.instructions.appendAssumeCapacity(new_index);
  12387         return new_index;
  12388     }
  12389 
  12390     fn addBuiltinValue(gz: *GenZir, src_node: Ast.Node.Index, val: Zir.Inst.BuiltinValue) !Zir.Inst.Ref {
  12391         return addExtendedNodeSmall(gz, .builtin_value, src_node, @intFromEnum(val));
  12392     }
  12393 
  12394     fn addExtendedPayload(gz: *GenZir, opcode: Zir.Inst.Extended, extra: anytype) !Zir.Inst.Ref {
  12395         return addExtendedPayloadSmall(gz, opcode, undefined, extra);
  12396     }
  12397 
  12398     fn addExtendedPayloadSmall(
  12399         gz: *GenZir,
  12400         opcode: Zir.Inst.Extended,
  12401         small: u16,
  12402         extra: anytype,
  12403     ) !Zir.Inst.Ref {
  12404         const gpa = gz.astgen.gpa;
  12405 
  12406         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12407         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12408 
  12409         const payload_index = try gz.astgen.addExtra(extra);
  12410         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  12411         gz.astgen.instructions.appendAssumeCapacity(.{
  12412             .tag = .extended,
  12413             .data = .{ .extended = .{
  12414                 .opcode = opcode,
  12415                 .small = small,
  12416                 .operand = payload_index,
  12417             } },
  12418         });
  12419         gz.instructions.appendAssumeCapacity(new_index);
  12420         return new_index.toRef();
  12421     }
  12422 
  12423     fn addExtendedMultiOp(
  12424         gz: *GenZir,
  12425         opcode: Zir.Inst.Extended,
  12426         node: Ast.Node.Index,
  12427         operands: []const Zir.Inst.Ref,
  12428     ) !Zir.Inst.Ref {
  12429         const astgen = gz.astgen;
  12430         const gpa = astgen.gpa;
  12431 
  12432         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12433         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12434         try astgen.extra.ensureUnusedCapacity(
  12435             gpa,
  12436             @typeInfo(Zir.Inst.NodeMultiOp).@"struct".fields.len + operands.len,
  12437         );
  12438 
  12439         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.NodeMultiOp{
  12440             .src_node = gz.nodeIndexToRelative(node),
  12441         });
  12442         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12443         astgen.instructions.appendAssumeCapacity(.{
  12444             .tag = .extended,
  12445             .data = .{ .extended = .{
  12446                 .opcode = opcode,
  12447                 .small = @intCast(operands.len),
  12448                 .operand = payload_index,
  12449             } },
  12450         });
  12451         gz.instructions.appendAssumeCapacity(new_index);
  12452         astgen.appendRefsAssumeCapacity(operands);
  12453         return new_index.toRef();
  12454     }
  12455 
  12456     fn addExtendedMultiOpPayloadIndex(
  12457         gz: *GenZir,
  12458         opcode: Zir.Inst.Extended,
  12459         payload_index: u32,
  12460         trailing_len: usize,
  12461     ) !Zir.Inst.Ref {
  12462         const astgen = gz.astgen;
  12463         const gpa = astgen.gpa;
  12464 
  12465         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12466         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12467         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12468         astgen.instructions.appendAssumeCapacity(.{
  12469             .tag = .extended,
  12470             .data = .{ .extended = .{
  12471                 .opcode = opcode,
  12472                 .small = @intCast(trailing_len),
  12473                 .operand = payload_index,
  12474             } },
  12475         });
  12476         gz.instructions.appendAssumeCapacity(new_index);
  12477         return new_index.toRef();
  12478     }
  12479 
  12480     fn addExtendedNodeSmall(
  12481         gz: *GenZir,
  12482         opcode: Zir.Inst.Extended,
  12483         src_node: Ast.Node.Index,
  12484         small: u16,
  12485     ) !Zir.Inst.Ref {
  12486         const astgen = gz.astgen;
  12487         const gpa = astgen.gpa;
  12488 
  12489         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12490         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12491         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12492         astgen.instructions.appendAssumeCapacity(.{
  12493             .tag = .extended,
  12494             .data = .{ .extended = .{
  12495                 .opcode = opcode,
  12496                 .small = small,
  12497                 .operand = @bitCast(@intFromEnum(gz.nodeIndexToRelative(src_node))),
  12498             } },
  12499         });
  12500         gz.instructions.appendAssumeCapacity(new_index);
  12501         return new_index.toRef();
  12502     }
  12503 
  12504     fn addUnTok(
  12505         gz: *GenZir,
  12506         tag: Zir.Inst.Tag,
  12507         operand: Zir.Inst.Ref,
  12508         /// Absolute token index. This function does the conversion to Decl offset.
  12509         abs_tok_index: Ast.TokenIndex,
  12510     ) !Zir.Inst.Ref {
  12511         assert(operand != .none);
  12512         return gz.add(.{
  12513             .tag = tag,
  12514             .data = .{ .un_tok = .{
  12515                 .operand = operand,
  12516                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  12517             } },
  12518         });
  12519     }
  12520 
  12521     fn makeUnTok(
  12522         gz: *GenZir,
  12523         tag: Zir.Inst.Tag,
  12524         operand: Zir.Inst.Ref,
  12525         /// Absolute token index. This function does the conversion to Decl offset.
  12526         abs_tok_index: Ast.TokenIndex,
  12527     ) !Zir.Inst.Index {
  12528         const astgen = gz.astgen;
  12529         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12530         assert(operand != .none);
  12531         try astgen.instructions.append(astgen.gpa, .{
  12532             .tag = tag,
  12533             .data = .{ .un_tok = .{
  12534                 .operand = operand,
  12535                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  12536             } },
  12537         });
  12538         return new_index;
  12539     }
  12540 
  12541     fn addStrTok(
  12542         gz: *GenZir,
  12543         tag: Zir.Inst.Tag,
  12544         str_index: Zir.NullTerminatedString,
  12545         /// Absolute token index. This function does the conversion to Decl offset.
  12546         abs_tok_index: Ast.TokenIndex,
  12547     ) !Zir.Inst.Ref {
  12548         return gz.add(.{
  12549             .tag = tag,
  12550             .data = .{ .str_tok = .{
  12551                 .start = str_index,
  12552                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  12553             } },
  12554         });
  12555     }
  12556 
  12557     fn addSaveErrRetIndex(
  12558         gz: *GenZir,
  12559         cond: union(enum) {
  12560             always: void,
  12561             if_of_error_type: Zir.Inst.Ref,
  12562         },
  12563     ) !Zir.Inst.Index {
  12564         return gz.addAsIndex(.{
  12565             .tag = .save_err_ret_index,
  12566             .data = .{ .save_err_ret_index = .{
  12567                 .operand = switch (cond) {
  12568                     .if_of_error_type => |x| x,
  12569                     else => .none,
  12570                 },
  12571             } },
  12572         });
  12573     }
  12574 
  12575     const BranchTarget = union(enum) {
  12576         ret,
  12577         block: Zir.Inst.Index,
  12578     };
  12579 
  12580     fn addRestoreErrRetIndex(
  12581         gz: *GenZir,
  12582         bt: BranchTarget,
  12583         cond: union(enum) {
  12584             always: void,
  12585             if_non_error: Zir.Inst.Ref,
  12586         },
  12587         src_node: Ast.Node.Index,
  12588     ) !Zir.Inst.Index {
  12589         switch (cond) {
  12590             .always => return gz.addAsIndex(.{
  12591                 .tag = .restore_err_ret_index_unconditional,
  12592                 .data = .{ .un_node = .{
  12593                     .operand = switch (bt) {
  12594                         .ret => .none,
  12595                         .block => |b| b.toRef(),
  12596                     },
  12597                     .src_node = gz.nodeIndexToRelative(src_node),
  12598                 } },
  12599             }),
  12600             .if_non_error => |operand| switch (bt) {
  12601                 .ret => return gz.addAsIndex(.{
  12602                     .tag = .restore_err_ret_index_fn_entry,
  12603                     .data = .{ .un_node = .{
  12604                         .operand = operand,
  12605                         .src_node = gz.nodeIndexToRelative(src_node),
  12606                     } },
  12607                 }),
  12608                 .block => |block| return (try gz.addExtendedPayload(
  12609                     .restore_err_ret_index,
  12610                     Zir.Inst.RestoreErrRetIndex{
  12611                         .src_node = gz.nodeIndexToRelative(src_node),
  12612                         .block = block.toRef(),
  12613                         .operand = operand,
  12614                     },
  12615                 )).toIndex().?,
  12616             },
  12617         }
  12618     }
  12619 
  12620     fn addBreak(
  12621         gz: *GenZir,
  12622         tag: Zir.Inst.Tag,
  12623         block_inst: Zir.Inst.Index,
  12624         operand: Zir.Inst.Ref,
  12625     ) !Zir.Inst.Index {
  12626         const gpa = gz.astgen.gpa;
  12627         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12628 
  12629         const new_index = try gz.makeBreak(tag, block_inst, operand);
  12630         gz.instructions.appendAssumeCapacity(new_index);
  12631         return new_index;
  12632     }
  12633 
  12634     fn makeBreak(
  12635         gz: *GenZir,
  12636         tag: Zir.Inst.Tag,
  12637         block_inst: Zir.Inst.Index,
  12638         operand: Zir.Inst.Ref,
  12639     ) !Zir.Inst.Index {
  12640         return gz.makeBreakCommon(tag, block_inst, operand, null);
  12641     }
  12642 
  12643     fn addBreakWithSrcNode(
  12644         gz: *GenZir,
  12645         tag: Zir.Inst.Tag,
  12646         block_inst: Zir.Inst.Index,
  12647         operand: Zir.Inst.Ref,
  12648         operand_src_node: Ast.Node.Index,
  12649     ) !Zir.Inst.Index {
  12650         const gpa = gz.astgen.gpa;
  12651         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12652 
  12653         const new_index = try gz.makeBreakWithSrcNode(tag, block_inst, operand, operand_src_node);
  12654         gz.instructions.appendAssumeCapacity(new_index);
  12655         return new_index;
  12656     }
  12657 
  12658     fn makeBreakWithSrcNode(
  12659         gz: *GenZir,
  12660         tag: Zir.Inst.Tag,
  12661         block_inst: Zir.Inst.Index,
  12662         operand: Zir.Inst.Ref,
  12663         operand_src_node: Ast.Node.Index,
  12664     ) !Zir.Inst.Index {
  12665         return gz.makeBreakCommon(tag, block_inst, operand, operand_src_node);
  12666     }
  12667 
  12668     fn makeBreakCommon(
  12669         gz: *GenZir,
  12670         tag: Zir.Inst.Tag,
  12671         block_inst: Zir.Inst.Index,
  12672         operand: Zir.Inst.Ref,
  12673         operand_src_node: ?Ast.Node.Index,
  12674     ) !Zir.Inst.Index {
  12675         const gpa = gz.astgen.gpa;
  12676         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12677         try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Break).@"struct".fields.len);
  12678 
  12679         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  12680         gz.astgen.instructions.appendAssumeCapacity(.{
  12681             .tag = tag,
  12682             .data = .{ .@"break" = .{
  12683                 .operand = operand,
  12684                 .payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Break{
  12685                     .operand_src_node = if (operand_src_node) |src_node|
  12686                         gz.nodeIndexToRelative(src_node).toOptional()
  12687                     else
  12688                         .none,
  12689                     .block_inst = block_inst,
  12690                 }),
  12691             } },
  12692         });
  12693         return new_index;
  12694     }
  12695 
  12696     fn addBin(
  12697         gz: *GenZir,
  12698         tag: Zir.Inst.Tag,
  12699         lhs: Zir.Inst.Ref,
  12700         rhs: Zir.Inst.Ref,
  12701     ) !Zir.Inst.Ref {
  12702         assert(lhs != .none);
  12703         assert(rhs != .none);
  12704         return gz.add(.{
  12705             .tag = tag,
  12706             .data = .{ .bin = .{
  12707                 .lhs = lhs,
  12708                 .rhs = rhs,
  12709             } },
  12710         });
  12711     }
  12712 
  12713     fn addDefer(gz: *GenZir, index: u32, len: u32) !void {
  12714         _ = try gz.add(.{
  12715             .tag = .@"defer",
  12716             .data = .{ .@"defer" = .{
  12717                 .index = index,
  12718                 .len = len,
  12719             } },
  12720         });
  12721     }
  12722 
  12723     fn addDecl(
  12724         gz: *GenZir,
  12725         tag: Zir.Inst.Tag,
  12726         decl_index: u32,
  12727         src_node: Ast.Node.Index,
  12728     ) !Zir.Inst.Ref {
  12729         return gz.add(.{
  12730             .tag = tag,
  12731             .data = .{ .pl_node = .{
  12732                 .src_node = gz.nodeIndexToRelative(src_node),
  12733                 .payload_index = decl_index,
  12734             } },
  12735         });
  12736     }
  12737 
  12738     fn addNode(
  12739         gz: *GenZir,
  12740         tag: Zir.Inst.Tag,
  12741         /// Absolute node index. This function does the conversion to offset from Decl.
  12742         src_node: Ast.Node.Index,
  12743     ) !Zir.Inst.Ref {
  12744         return gz.add(.{
  12745             .tag = tag,
  12746             .data = .{ .node = gz.nodeIndexToRelative(src_node) },
  12747         });
  12748     }
  12749 
  12750     fn addInstNode(
  12751         gz: *GenZir,
  12752         tag: Zir.Inst.Tag,
  12753         inst: Zir.Inst.Index,
  12754         /// Absolute node index. This function does the conversion to offset from Decl.
  12755         src_node: Ast.Node.Index,
  12756     ) !Zir.Inst.Ref {
  12757         return gz.add(.{
  12758             .tag = tag,
  12759             .data = .{ .inst_node = .{
  12760                 .inst = inst,
  12761                 .src_node = gz.nodeIndexToRelative(src_node),
  12762             } },
  12763         });
  12764     }
  12765 
  12766     fn addNodeExtended(
  12767         gz: *GenZir,
  12768         opcode: Zir.Inst.Extended,
  12769         /// Absolute node index. This function does the conversion to offset from Decl.
  12770         src_node: Ast.Node.Index,
  12771     ) !Zir.Inst.Ref {
  12772         return gz.add(.{
  12773             .tag = .extended,
  12774             .data = .{ .extended = .{
  12775                 .opcode = opcode,
  12776                 .small = undefined,
  12777                 .operand = @bitCast(@intFromEnum(gz.nodeIndexToRelative(src_node))),
  12778             } },
  12779         });
  12780     }
  12781 
  12782     fn addAllocExtended(
  12783         gz: *GenZir,
  12784         args: struct {
  12785             /// Absolute node index. This function does the conversion to offset from Decl.
  12786             node: Ast.Node.Index,
  12787             type_inst: Zir.Inst.Ref,
  12788             align_inst: Zir.Inst.Ref,
  12789             is_const: bool,
  12790             is_comptime: bool,
  12791         },
  12792     ) !Zir.Inst.Ref {
  12793         const astgen = gz.astgen;
  12794         const gpa = astgen.gpa;
  12795 
  12796         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12797         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12798         try astgen.extra.ensureUnusedCapacity(
  12799             gpa,
  12800             @typeInfo(Zir.Inst.AllocExtended).@"struct".fields.len +
  12801                 @intFromBool(args.type_inst != .none) +
  12802                 @intFromBool(args.align_inst != .none),
  12803         );
  12804         const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.AllocExtended{
  12805             .src_node = gz.nodeIndexToRelative(args.node),
  12806         });
  12807         if (args.type_inst != .none) {
  12808             astgen.extra.appendAssumeCapacity(@intFromEnum(args.type_inst));
  12809         }
  12810         if (args.align_inst != .none) {
  12811             astgen.extra.appendAssumeCapacity(@intFromEnum(args.align_inst));
  12812         }
  12813 
  12814         const has_type: u4 = @intFromBool(args.type_inst != .none);
  12815         const has_align: u4 = @intFromBool(args.align_inst != .none);
  12816         const is_const: u4 = @intFromBool(args.is_const);
  12817         const is_comptime: u4 = @intFromBool(args.is_comptime);
  12818         const small: u16 = has_type | (has_align << 1) | (is_const << 2) | (is_comptime << 3);
  12819 
  12820         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12821         astgen.instructions.appendAssumeCapacity(.{
  12822             .tag = .extended,
  12823             .data = .{ .extended = .{
  12824                 .opcode = .alloc,
  12825                 .small = small,
  12826                 .operand = payload_index,
  12827             } },
  12828         });
  12829         gz.instructions.appendAssumeCapacity(new_index);
  12830         return new_index.toRef();
  12831     }
  12832 
  12833     fn addAsm(
  12834         gz: *GenZir,
  12835         args: struct {
  12836             tag: Zir.Inst.Extended,
  12837             /// Absolute node index. This function does the conversion to offset from Decl.
  12838             node: Ast.Node.Index,
  12839             asm_source: Zir.NullTerminatedString,
  12840             output_type_bits: u32,
  12841             is_volatile: bool,
  12842             outputs: []const Zir.Inst.Asm.Output,
  12843             inputs: []const Zir.Inst.Asm.Input,
  12844             clobbers: Zir.Inst.Ref,
  12845         },
  12846     ) !Zir.Inst.Ref {
  12847         const astgen = gz.astgen;
  12848         const gpa = astgen.gpa;
  12849 
  12850         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12851         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12852         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Asm).@"struct".fields.len +
  12853             args.outputs.len * @typeInfo(Zir.Inst.Asm.Output).@"struct".fields.len +
  12854             args.inputs.len * @typeInfo(Zir.Inst.Asm.Input).@"struct".fields.len);
  12855 
  12856         const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Asm{
  12857             .src_node = gz.nodeIndexToRelative(args.node),
  12858             .asm_source = args.asm_source,
  12859             .output_type_bits = args.output_type_bits,
  12860             .clobbers = args.clobbers,
  12861         });
  12862         for (args.outputs) |output| {
  12863             _ = gz.astgen.addExtraAssumeCapacity(output);
  12864         }
  12865         for (args.inputs) |input| {
  12866             _ = gz.astgen.addExtraAssumeCapacity(input);
  12867         }
  12868 
  12869         const small: Zir.Inst.Asm.Small = .{
  12870             .outputs_len = @intCast(args.outputs.len),
  12871             .inputs_len = @intCast(args.inputs.len),
  12872             .is_volatile = args.is_volatile,
  12873         };
  12874 
  12875         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12876         astgen.instructions.appendAssumeCapacity(.{
  12877             .tag = .extended,
  12878             .data = .{ .extended = .{
  12879                 .opcode = args.tag,
  12880                 .small = @bitCast(small),
  12881                 .operand = payload_index,
  12882             } },
  12883         });
  12884         gz.instructions.appendAssumeCapacity(new_index);
  12885         return new_index.toRef();
  12886     }
  12887 
  12888     /// Note that this returns a `Zir.Inst.Index` not a ref.
  12889     /// Does *not* append the block instruction to the scope.
  12890     /// Leaves the `payload_index` field undefined.
  12891     fn makeBlockInst(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
  12892         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  12893         const gpa = gz.astgen.gpa;
  12894         try gz.astgen.instructions.append(gpa, .{
  12895             .tag = tag,
  12896             .data = .{ .pl_node = .{
  12897                 .src_node = gz.nodeIndexToRelative(node),
  12898                 .payload_index = undefined,
  12899             } },
  12900         });
  12901         return new_index;
  12902     }
  12903 
  12904     /// Note that this returns a `Zir.Inst.Index` not a ref.
  12905     /// Does *not* append the block instruction to the scope.
  12906     /// Leaves the `payload_index` field undefined. Use `setDeclaration` to finalize.
  12907     fn makeDeclaration(gz: *GenZir, node: Ast.Node.Index) !Zir.Inst.Index {
  12908         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  12909         try gz.astgen.instructions.append(gz.astgen.gpa, .{
  12910             .tag = .declaration,
  12911             .data = .{ .declaration = .{
  12912                 .src_node = node,
  12913                 .payload_index = undefined,
  12914             } },
  12915         });
  12916         return new_index;
  12917     }
  12918 
  12919     /// Note that this returns a `Zir.Inst.Index` not a ref.
  12920     /// Leaves the `payload_index` field undefined.
  12921     fn addCondBr(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
  12922         const gpa = gz.astgen.gpa;
  12923         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12924         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  12925         try gz.astgen.instructions.append(gpa, .{
  12926             .tag = tag,
  12927             .data = .{ .pl_node = .{
  12928                 .src_node = gz.nodeIndexToRelative(node),
  12929                 .payload_index = undefined,
  12930             } },
  12931         });
  12932         gz.instructions.appendAssumeCapacity(new_index);
  12933         return new_index;
  12934     }
  12935 
  12936     fn setStruct(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  12937         src_node: Ast.Node.Index,
  12938         captures_len: u32,
  12939         fields_len: u32,
  12940         decls_len: u32,
  12941         has_backing_int: bool,
  12942         layout: std.builtin.Type.ContainerLayout,
  12943         known_non_opv: bool,
  12944         known_comptime_only: bool,
  12945         any_comptime_fields: bool,
  12946         any_default_inits: bool,
  12947         any_aligned_fields: bool,
  12948         fields_hash: std.zig.SrcHash,
  12949         name_strat: Zir.Inst.NameStrategy,
  12950     }) !void {
  12951         const astgen = gz.astgen;
  12952         const gpa = astgen.gpa;
  12953 
  12954         // Node .root is valid for the root `struct_decl` of a file!
  12955         assert(args.src_node != .root or gz.parent.tag == .top);
  12956 
  12957         const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);
  12958 
  12959         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len + 3);
  12960         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.StructDecl{
  12961             .fields_hash_0 = fields_hash_arr[0],
  12962             .fields_hash_1 = fields_hash_arr[1],
  12963             .fields_hash_2 = fields_hash_arr[2],
  12964             .fields_hash_3 = fields_hash_arr[3],
  12965             .src_line = astgen.source_line,
  12966             .src_node = args.src_node,
  12967         });
  12968 
  12969         if (args.captures_len != 0) {
  12970             astgen.extra.appendAssumeCapacity(args.captures_len);
  12971         }
  12972         if (args.fields_len != 0) {
  12973             astgen.extra.appendAssumeCapacity(args.fields_len);
  12974         }
  12975         if (args.decls_len != 0) {
  12976             astgen.extra.appendAssumeCapacity(args.decls_len);
  12977         }
  12978         astgen.instructions.set(@intFromEnum(inst), .{
  12979             .tag = .extended,
  12980             .data = .{ .extended = .{
  12981                 .opcode = .struct_decl,
  12982                 .small = @bitCast(Zir.Inst.StructDecl.Small{
  12983                     .has_captures_len = args.captures_len != 0,
  12984                     .has_fields_len = args.fields_len != 0,
  12985                     .has_decls_len = args.decls_len != 0,
  12986                     .has_backing_int = args.has_backing_int,
  12987                     .known_non_opv = args.known_non_opv,
  12988                     .known_comptime_only = args.known_comptime_only,
  12989                     .name_strategy = args.name_strat,
  12990                     .layout = args.layout,
  12991                     .any_comptime_fields = args.any_comptime_fields,
  12992                     .any_default_inits = args.any_default_inits,
  12993                     .any_aligned_fields = args.any_aligned_fields,
  12994                 }),
  12995                 .operand = payload_index,
  12996             } },
  12997         });
  12998     }
  12999 
  13000     fn setUnion(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  13001         src_node: Ast.Node.Index,
  13002         tag_type: Zir.Inst.Ref,
  13003         captures_len: u32,
  13004         body_len: u32,
  13005         fields_len: u32,
  13006         decls_len: u32,
  13007         layout: std.builtin.Type.ContainerLayout,
  13008         auto_enum_tag: bool,
  13009         any_aligned_fields: bool,
  13010         fields_hash: std.zig.SrcHash,
  13011         name_strat: Zir.Inst.NameStrategy,
  13012     }) !void {
  13013         const astgen = gz.astgen;
  13014         const gpa = astgen.gpa;
  13015 
  13016         assert(args.src_node != .root);
  13017 
  13018         const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);
  13019 
  13020         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.UnionDecl).@"struct".fields.len + 5);
  13021         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.UnionDecl{
  13022             .fields_hash_0 = fields_hash_arr[0],
  13023             .fields_hash_1 = fields_hash_arr[1],
  13024             .fields_hash_2 = fields_hash_arr[2],
  13025             .fields_hash_3 = fields_hash_arr[3],
  13026             .src_line = astgen.source_line,
  13027             .src_node = args.src_node,
  13028         });
  13029 
  13030         if (args.tag_type != .none) {
  13031             astgen.extra.appendAssumeCapacity(@intFromEnum(args.tag_type));
  13032         }
  13033         if (args.captures_len != 0) {
  13034             astgen.extra.appendAssumeCapacity(args.captures_len);
  13035         }
  13036         if (args.body_len != 0) {
  13037             astgen.extra.appendAssumeCapacity(args.body_len);
  13038         }
  13039         if (args.fields_len != 0) {
  13040             astgen.extra.appendAssumeCapacity(args.fields_len);
  13041         }
  13042         if (args.decls_len != 0) {
  13043             astgen.extra.appendAssumeCapacity(args.decls_len);
  13044         }
  13045         astgen.instructions.set(@intFromEnum(inst), .{
  13046             .tag = .extended,
  13047             .data = .{ .extended = .{
  13048                 .opcode = .union_decl,
  13049                 .small = @bitCast(Zir.Inst.UnionDecl.Small{
  13050                     .has_tag_type = args.tag_type != .none,
  13051                     .has_captures_len = args.captures_len != 0,
  13052                     .has_body_len = args.body_len != 0,
  13053                     .has_fields_len = args.fields_len != 0,
  13054                     .has_decls_len = args.decls_len != 0,
  13055                     .name_strategy = args.name_strat,
  13056                     .layout = args.layout,
  13057                     .auto_enum_tag = args.auto_enum_tag,
  13058                     .any_aligned_fields = args.any_aligned_fields,
  13059                 }),
  13060                 .operand = payload_index,
  13061             } },
  13062         });
  13063     }
  13064 
  13065     fn setEnum(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  13066         src_node: Ast.Node.Index,
  13067         tag_type: Zir.Inst.Ref,
  13068         captures_len: u32,
  13069         body_len: u32,
  13070         fields_len: u32,
  13071         decls_len: u32,
  13072         nonexhaustive: bool,
  13073         fields_hash: std.zig.SrcHash,
  13074         name_strat: Zir.Inst.NameStrategy,
  13075     }) !void {
  13076         const astgen = gz.astgen;
  13077         const gpa = astgen.gpa;
  13078 
  13079         assert(args.src_node != .root);
  13080 
  13081         const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);
  13082 
  13083         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.EnumDecl).@"struct".fields.len + 5);
  13084         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.EnumDecl{
  13085             .fields_hash_0 = fields_hash_arr[0],
  13086             .fields_hash_1 = fields_hash_arr[1],
  13087             .fields_hash_2 = fields_hash_arr[2],
  13088             .fields_hash_3 = fields_hash_arr[3],
  13089             .src_line = astgen.source_line,
  13090             .src_node = args.src_node,
  13091         });
  13092 
  13093         if (args.tag_type != .none) {
  13094             astgen.extra.appendAssumeCapacity(@intFromEnum(args.tag_type));
  13095         }
  13096         if (args.captures_len != 0) {
  13097             astgen.extra.appendAssumeCapacity(args.captures_len);
  13098         }
  13099         if (args.body_len != 0) {
  13100             astgen.extra.appendAssumeCapacity(args.body_len);
  13101         }
  13102         if (args.fields_len != 0) {
  13103             astgen.extra.appendAssumeCapacity(args.fields_len);
  13104         }
  13105         if (args.decls_len != 0) {
  13106             astgen.extra.appendAssumeCapacity(args.decls_len);
  13107         }
  13108         astgen.instructions.set(@intFromEnum(inst), .{
  13109             .tag = .extended,
  13110             .data = .{ .extended = .{
  13111                 .opcode = .enum_decl,
  13112                 .small = @bitCast(Zir.Inst.EnumDecl.Small{
  13113                     .has_tag_type = args.tag_type != .none,
  13114                     .has_captures_len = args.captures_len != 0,
  13115                     .has_body_len = args.body_len != 0,
  13116                     .has_fields_len = args.fields_len != 0,
  13117                     .has_decls_len = args.decls_len != 0,
  13118                     .name_strategy = args.name_strat,
  13119                     .nonexhaustive = args.nonexhaustive,
  13120                 }),
  13121                 .operand = payload_index,
  13122             } },
  13123         });
  13124     }
  13125 
  13126     fn setOpaque(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  13127         src_node: Ast.Node.Index,
  13128         captures_len: u32,
  13129         decls_len: u32,
  13130         name_strat: Zir.Inst.NameStrategy,
  13131     }) !void {
  13132         const astgen = gz.astgen;
  13133         const gpa = astgen.gpa;
  13134 
  13135         assert(args.src_node != .root);
  13136 
  13137         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.OpaqueDecl).@"struct".fields.len + 2);
  13138         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.OpaqueDecl{
  13139             .src_line = astgen.source_line,
  13140             .src_node = args.src_node,
  13141         });
  13142 
  13143         if (args.captures_len != 0) {
  13144             astgen.extra.appendAssumeCapacity(args.captures_len);
  13145         }
  13146         if (args.decls_len != 0) {
  13147             astgen.extra.appendAssumeCapacity(args.decls_len);
  13148         }
  13149         astgen.instructions.set(@intFromEnum(inst), .{
  13150             .tag = .extended,
  13151             .data = .{ .extended = .{
  13152                 .opcode = .opaque_decl,
  13153                 .small = @bitCast(Zir.Inst.OpaqueDecl.Small{
  13154                     .has_captures_len = args.captures_len != 0,
  13155                     .has_decls_len = args.decls_len != 0,
  13156                     .name_strategy = args.name_strat,
  13157                 }),
  13158                 .operand = payload_index,
  13159             } },
  13160         });
  13161     }
  13162 
  13163     fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref {
  13164         return (try gz.addAsIndex(inst)).toRef();
  13165     }
  13166 
  13167     fn addAsIndex(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Index {
  13168         const gpa = gz.astgen.gpa;
  13169         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  13170         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  13171 
  13172         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  13173         gz.astgen.instructions.appendAssumeCapacity(inst);
  13174         gz.instructions.appendAssumeCapacity(new_index);
  13175         return new_index;
  13176     }
  13177 
  13178     fn reserveInstructionIndex(gz: *GenZir) !Zir.Inst.Index {
  13179         const gpa = gz.astgen.gpa;
  13180         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  13181         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  13182 
  13183         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  13184         gz.astgen.instructions.len += 1;
  13185         gz.instructions.appendAssumeCapacity(new_index);
  13186         return new_index;
  13187     }
  13188 
  13189     fn addRet(gz: *GenZir, ri: ResultInfo, operand: Zir.Inst.Ref, node: Ast.Node.Index) !void {
  13190         switch (ri.rl) {
  13191             .ptr => |ptr_res| _ = try gz.addUnNode(.ret_load, ptr_res.inst, node),
  13192             .coerced_ty => _ = try gz.addUnNode(.ret_node, operand, node),
  13193             else => unreachable,
  13194         }
  13195     }
  13196 
  13197     fn addDbgVar(gz: *GenZir, tag: Zir.Inst.Tag, name: Zir.NullTerminatedString, inst: Zir.Inst.Ref) !void {
  13198         if (gz.is_comptime) return;
  13199 
  13200         _ = try gz.add(.{ .tag = tag, .data = .{
  13201             .str_op = .{
  13202                 .str = name,
  13203                 .operand = inst,
  13204             },
  13205         } });
  13206     }
  13207 };
  13208 
  13209 /// This can only be for short-lived references; the memory becomes invalidated
  13210 /// when another string is added.
  13211 fn nullTerminatedString(astgen: AstGen, index: Zir.NullTerminatedString) [*:0]const u8 {
  13212     return @ptrCast(astgen.string_bytes.items[@intFromEnum(index)..]);
  13213 }
  13214 
  13215 /// Local variables shadowing detection, including function parameters.
  13216 fn detectLocalShadowing(
  13217     astgen: *AstGen,
  13218     scope: *Scope,
  13219     ident_name: Zir.NullTerminatedString,
  13220     name_token: Ast.TokenIndex,
  13221     token_bytes: []const u8,
  13222     id_cat: Scope.IdCat,
  13223 ) !void {
  13224     const gpa = astgen.gpa;
  13225     if (token_bytes[0] != '@' and isPrimitive(token_bytes)) {
  13226         return astgen.failTokNotes(name_token, "name shadows primitive '{s}'", .{
  13227             token_bytes,
  13228         }, &[_]u32{
  13229             try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{
  13230                 token_bytes,
  13231             }),
  13232         });
  13233     }
  13234 
  13235     var s = scope;
  13236     var outer_scope = false;
  13237     while (true) switch (s.tag) {
  13238         .local_val => {
  13239             const local_val = s.cast(Scope.LocalVal).?;
  13240             if (local_val.name == ident_name) {
  13241                 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
  13242                 const name = try gpa.dupe(u8, name_slice);
  13243                 defer gpa.free(name);
  13244                 if (outer_scope) {
  13245                     return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{
  13246                         @tagName(id_cat), name, @tagName(local_val.id_cat),
  13247                     }, &[_]u32{
  13248                         try astgen.errNoteTok(
  13249                             local_val.token_src,
  13250                             "previous declaration here",
  13251                             .{},
  13252                         ),
  13253                     });
  13254                 }
  13255                 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{
  13256                     @tagName(local_val.id_cat), name,
  13257                 }, &[_]u32{
  13258                     try astgen.errNoteTok(
  13259                         local_val.token_src,
  13260                         "previous declaration here",
  13261                         .{},
  13262                     ),
  13263                 });
  13264             }
  13265             s = local_val.parent;
  13266         },
  13267         .local_ptr => {
  13268             const local_ptr = s.cast(Scope.LocalPtr).?;
  13269             if (local_ptr.name == ident_name) {
  13270                 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
  13271                 const name = try gpa.dupe(u8, name_slice);
  13272                 defer gpa.free(name);
  13273                 if (outer_scope) {
  13274                     return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{
  13275                         @tagName(id_cat), name, @tagName(local_ptr.id_cat),
  13276                     }, &[_]u32{
  13277                         try astgen.errNoteTok(
  13278                             local_ptr.token_src,
  13279                             "previous declaration here",
  13280                             .{},
  13281                         ),
  13282                     });
  13283                 }
  13284                 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{
  13285                     @tagName(local_ptr.id_cat), name,
  13286                 }, &[_]u32{
  13287                     try astgen.errNoteTok(
  13288                         local_ptr.token_src,
  13289                         "previous declaration here",
  13290                         .{},
  13291                     ),
  13292                 });
  13293             }
  13294             s = local_ptr.parent;
  13295         },
  13296         .namespace => {
  13297             outer_scope = true;
  13298             const ns = s.cast(Scope.Namespace).?;
  13299             const decl_node = ns.decls.get(ident_name) orelse {
  13300                 s = ns.parent;
  13301                 continue;
  13302             };
  13303             const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
  13304             const name = try gpa.dupe(u8, name_slice);
  13305             defer gpa.free(name);
  13306             return astgen.failTokNotes(name_token, "{s} shadows declaration of '{s}'", .{
  13307                 @tagName(id_cat), name,
  13308             }, &[_]u32{
  13309                 try astgen.errNoteNode(decl_node, "declared here", .{}),
  13310             });
  13311         },
  13312         .gen_zir => {
  13313             s = s.cast(GenZir).?.parent;
  13314             outer_scope = true;
  13315         },
  13316         .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
  13317         .top => break,
  13318     };
  13319 }
  13320 
  13321 const LineColumn = struct { u32, u32 };
  13322 
  13323 /// Advances the source cursor to the main token of `node` if not in comptime scope.
  13324 /// Usually paired with `emitDbgStmt`.
  13325 fn maybeAdvanceSourceCursorToMainToken(gz: *GenZir, node: Ast.Node.Index) LineColumn {
  13326     if (gz.is_comptime) return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column };
  13327 
  13328     const tree = gz.astgen.tree;
  13329     const node_start = tree.tokenStart(tree.nodeMainToken(node));
  13330     gz.astgen.advanceSourceCursor(node_start);
  13331 
  13332     return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column };
  13333 }
  13334 
  13335 /// Advances the source cursor to the beginning of `node`.
  13336 fn advanceSourceCursorToNode(astgen: *AstGen, node: Ast.Node.Index) void {
  13337     const tree = astgen.tree;
  13338     const node_start = tree.tokenStart(tree.firstToken(node));
  13339     astgen.advanceSourceCursor(node_start);
  13340 }
  13341 
  13342 /// Advances the source cursor to an absolute byte offset `end` in the file.
  13343 fn advanceSourceCursor(astgen: *AstGen, end: usize) void {
  13344     const source = astgen.tree.source;
  13345     var i = astgen.source_offset;
  13346     var line = astgen.source_line;
  13347     var column = astgen.source_column;
  13348     assert(i <= end);
  13349     while (i < end) : (i += 1) {
  13350         if (source[i] == '\n') {
  13351             line += 1;
  13352             column = 0;
  13353         } else {
  13354             column += 1;
  13355         }
  13356     }
  13357     astgen.source_offset = i;
  13358     astgen.source_line = line;
  13359     astgen.source_column = column;
  13360 }
  13361 
  13362 const SourceCursor = struct {
  13363     offset: u32,
  13364     line: u32,
  13365     column: u32,
  13366 };
  13367 
  13368 /// Get the current source cursor, to be restored later with `restoreSourceCursor`.
  13369 /// This is useful when analyzing source code out-of-order.
  13370 fn saveSourceCursor(astgen: *const AstGen) SourceCursor {
  13371     return .{
  13372         .offset = astgen.source_offset,
  13373         .line = astgen.source_line,
  13374         .column = astgen.source_column,
  13375     };
  13376 }
  13377 fn restoreSourceCursor(astgen: *AstGen, cursor: SourceCursor) void {
  13378     astgen.source_offset = cursor.offset;
  13379     astgen.source_line = cursor.line;
  13380     astgen.source_column = cursor.column;
  13381 }
  13382 
  13383 /// Detects name conflicts for decls and fields, and populates `namespace.decls` with all named declarations.
  13384 /// Returns the number of declarations in the namespace, including unnamed declarations (e.g. `comptime` decls).
  13385 fn scanContainer(
  13386     astgen: *AstGen,
  13387     namespace: *Scope.Namespace,
  13388     members: []const Ast.Node.Index,
  13389     container_kind: enum { @"struct", @"union", @"enum", @"opaque" },
  13390 ) !u32 {
  13391     const gpa = astgen.gpa;
  13392     const tree = astgen.tree;
  13393 
  13394     var any_invalid_declarations = false;
  13395 
  13396     // This type forms a linked list of source tokens declaring the same name.
  13397     const NameEntry = struct {
  13398         tok: Ast.TokenIndex,
  13399         /// Using a linked list here simplifies memory management, and is acceptable since
  13400         ///ewntries are only allocated in error situations. The entries are allocated into the
  13401         /// AstGen arena.
  13402         next: ?*@This(),
  13403     };
  13404 
  13405     // The maps below are allocated into this SFBA to avoid using the GPA for small namespaces.
  13406     var sfba_state = std.heap.stackFallback(512, astgen.gpa);
  13407     const sfba = sfba_state.get();
  13408 
  13409     var names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .empty;
  13410     var test_names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .empty;
  13411     var decltest_names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .empty;
  13412     defer {
  13413         names.deinit(sfba);
  13414         test_names.deinit(sfba);
  13415         decltest_names.deinit(sfba);
  13416     }
  13417 
  13418     var any_duplicates = false;
  13419     var decl_count: u32 = 0;
  13420     for (members) |member_node| {
  13421         const Kind = enum { decl, field };
  13422         const kind: Kind, const name_token = switch (tree.nodeTag(member_node)) {
  13423             .container_field_init,
  13424             .container_field_align,
  13425             .container_field,
  13426             => blk: {
  13427                 var full = tree.fullContainerField(member_node).?;
  13428                 switch (container_kind) {
  13429                     .@"struct", .@"opaque" => {},
  13430                     .@"union", .@"enum" => full.convertToNonTupleLike(astgen.tree),
  13431                 }
  13432                 if (full.ast.tuple_like) continue;
  13433                 break :blk .{ .field, full.ast.main_token };
  13434             },
  13435 
  13436             .global_var_decl,
  13437             .local_var_decl,
  13438             .simple_var_decl,
  13439             .aligned_var_decl,
  13440             => blk: {
  13441                 decl_count += 1;
  13442                 break :blk .{ .decl, tree.nodeMainToken(member_node) + 1 };
  13443             },
  13444 
  13445             .fn_proto_simple,
  13446             .fn_proto_multi,
  13447             .fn_proto_one,
  13448             .fn_proto,
  13449             .fn_decl,
  13450             => blk: {
  13451                 decl_count += 1;
  13452                 const ident = tree.nodeMainToken(member_node) + 1;
  13453                 if (tree.tokenTag(ident) != .identifier) {
  13454                     try astgen.appendErrorNode(member_node, "missing function name", .{});
  13455                     any_invalid_declarations = true;
  13456                     continue;
  13457                 }
  13458                 break :blk .{ .decl, ident };
  13459             },
  13460 
  13461             .@"comptime" => {
  13462                 decl_count += 1;
  13463                 continue;
  13464             },
  13465 
  13466             .test_decl => {
  13467                 decl_count += 1;
  13468                 // We don't want shadowing detection here, and test names work a bit differently, so
  13469                 // we must do the redeclaration detection ourselves.
  13470                 const test_name_token = tree.nodeMainToken(member_node) + 1;
  13471                 const new_ent: NameEntry = .{
  13472                     .tok = test_name_token,
  13473                     .next = null,
  13474                 };
  13475                 switch (tree.tokenTag(test_name_token)) {
  13476                     else => {}, // unnamed test
  13477                     .string_literal => {
  13478                         const name = try astgen.strLitAsString(test_name_token);
  13479                         const gop = try test_names.getOrPut(sfba, name.index);
  13480                         if (gop.found_existing) {
  13481                             var e = gop.value_ptr;
  13482                             while (e.next) |n| e = n;
  13483                             e.next = try astgen.arena.create(NameEntry);
  13484                             e.next.?.* = new_ent;
  13485                             any_duplicates = true;
  13486                         } else {
  13487                             gop.value_ptr.* = new_ent;
  13488                         }
  13489                     },
  13490                     .identifier => {
  13491                         const name = try astgen.identAsString(test_name_token);
  13492                         const gop = try decltest_names.getOrPut(sfba, name);
  13493                         if (gop.found_existing) {
  13494                             var e = gop.value_ptr;
  13495                             while (e.next) |n| e = n;
  13496                             e.next = try astgen.arena.create(NameEntry);
  13497                             e.next.?.* = new_ent;
  13498                             any_duplicates = true;
  13499                         } else {
  13500                             gop.value_ptr.* = new_ent;
  13501                         }
  13502                     },
  13503                 }
  13504                 continue;
  13505             },
  13506 
  13507             else => unreachable,
  13508         };
  13509 
  13510         const name_str_index = try astgen.identAsString(name_token);
  13511 
  13512         if (kind == .decl) {
  13513             // Put the name straight into `decls`, even if there are compile errors.
  13514             // This avoids incorrect "undeclared identifier" errors later on.
  13515             try namespace.decls.put(gpa, name_str_index, member_node);
  13516         }
  13517 
  13518         {
  13519             const gop = try names.getOrPut(sfba, name_str_index);
  13520             const new_ent: NameEntry = .{
  13521                 .tok = name_token,
  13522                 .next = null,
  13523             };
  13524             if (gop.found_existing) {
  13525                 var e = gop.value_ptr;
  13526                 while (e.next) |n| e = n;
  13527                 e.next = try astgen.arena.create(NameEntry);
  13528                 e.next.?.* = new_ent;
  13529                 any_duplicates = true;
  13530                 continue;
  13531             } else {
  13532                 gop.value_ptr.* = new_ent;
  13533             }
  13534         }
  13535 
  13536         // For fields, we only needed the duplicate check! Decls have some more checks to do, though.
  13537         switch (kind) {
  13538             .decl => {},
  13539             .field => continue,
  13540         }
  13541 
  13542         const token_bytes = astgen.tree.tokenSlice(name_token);
  13543         if (token_bytes[0] != '@' and isPrimitive(token_bytes)) {
  13544             try astgen.appendErrorTokNotes(name_token, "name shadows primitive '{s}'", .{
  13545                 token_bytes,
  13546             }, &.{
  13547                 try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{
  13548                     token_bytes,
  13549                 }),
  13550             });
  13551             any_invalid_declarations = true;
  13552             continue;
  13553         }
  13554 
  13555         var s = namespace.parent;
  13556         while (true) switch (s.tag) {
  13557             .local_val => {
  13558                 const local_val = s.cast(Scope.LocalVal).?;
  13559                 if (local_val.name == name_str_index) {
  13560                     try astgen.appendErrorTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{
  13561                         token_bytes, @tagName(local_val.id_cat),
  13562                     }, &.{
  13563                         try astgen.errNoteTok(
  13564                             local_val.token_src,
  13565                             "previous declaration here",
  13566                             .{},
  13567                         ),
  13568                     });
  13569                     any_invalid_declarations = true;
  13570                     break;
  13571                 }
  13572                 s = local_val.parent;
  13573             },
  13574             .local_ptr => {
  13575                 const local_ptr = s.cast(Scope.LocalPtr).?;
  13576                 if (local_ptr.name == name_str_index) {
  13577                     try astgen.appendErrorTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{
  13578                         token_bytes, @tagName(local_ptr.id_cat),
  13579                     }, &.{
  13580                         try astgen.errNoteTok(
  13581                             local_ptr.token_src,
  13582                             "previous declaration here",
  13583                             .{},
  13584                         ),
  13585                     });
  13586                     any_invalid_declarations = true;
  13587                     break;
  13588                 }
  13589                 s = local_ptr.parent;
  13590             },
  13591             .namespace => s = s.cast(Scope.Namespace).?.parent,
  13592             .gen_zir => s = s.cast(GenZir).?.parent,
  13593             .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
  13594             .top => break,
  13595         };
  13596     }
  13597 
  13598     if (!any_duplicates) {
  13599         if (any_invalid_declarations) return error.AnalysisFail;
  13600         return decl_count;
  13601     }
  13602 
  13603     for (names.keys(), names.values()) |name, first| {
  13604         if (first.next == null) continue;
  13605         var notes: std.ArrayListUnmanaged(u32) = .empty;
  13606         var prev: NameEntry = first;
  13607         while (prev.next) |cur| : (prev = cur.*) {
  13608             try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate name here", .{}));
  13609         }
  13610         try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
  13611         const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
  13612         try astgen.appendErrorTokNotes(first.tok, "duplicate {s} member name '{s}'", .{ @tagName(container_kind), name_duped }, notes.items);
  13613         any_invalid_declarations = true;
  13614     }
  13615 
  13616     for (test_names.keys(), test_names.values()) |name, first| {
  13617         if (first.next == null) continue;
  13618         var notes: std.ArrayListUnmanaged(u32) = .empty;
  13619         var prev: NameEntry = first;
  13620         while (prev.next) |cur| : (prev = cur.*) {
  13621             try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate test here", .{}));
  13622         }
  13623         try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
  13624         const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
  13625         try astgen.appendErrorTokNotes(first.tok, "duplicate test name '{s}'", .{name_duped}, notes.items);
  13626         any_invalid_declarations = true;
  13627     }
  13628 
  13629     for (decltest_names.keys(), decltest_names.values()) |name, first| {
  13630         if (first.next == null) continue;
  13631         var notes: std.ArrayListUnmanaged(u32) = .empty;
  13632         var prev: NameEntry = first;
  13633         while (prev.next) |cur| : (prev = cur.*) {
  13634             try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate decltest here", .{}));
  13635         }
  13636         try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
  13637         const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
  13638         try astgen.appendErrorTokNotes(first.tok, "duplicate decltest '{s}'", .{name_duped}, notes.items);
  13639         any_invalid_declarations = true;
  13640     }
  13641 
  13642     assert(any_invalid_declarations);
  13643     return error.AnalysisFail;
  13644 }
  13645 
  13646 /// Assumes capacity for body has already been added. Needed capacity taking into
  13647 /// account fixups can be found with `countBodyLenAfterFixups`.
  13648 fn appendBodyWithFixups(astgen: *AstGen, body: []const Zir.Inst.Index) void {
  13649     return appendBodyWithFixupsArrayList(astgen, &astgen.extra, body);
  13650 }
  13651 
  13652 fn appendBodyWithFixupsArrayList(
  13653     astgen: *AstGen,
  13654     list: *std.ArrayListUnmanaged(u32),
  13655     body: []const Zir.Inst.Index,
  13656 ) void {
  13657     astgen.appendBodyWithFixupsExtraRefsArrayList(list, body, &.{});
  13658 }
  13659 
  13660 fn appendBodyWithFixupsExtraRefsArrayList(
  13661     astgen: *AstGen,
  13662     list: *std.ArrayListUnmanaged(u32),
  13663     body: []const Zir.Inst.Index,
  13664     extra_refs: []const Zir.Inst.Index,
  13665 ) void {
  13666     for (extra_refs) |extra_inst| {
  13667         if (astgen.ref_table.fetchRemove(extra_inst)) |kv| {
  13668             appendPossiblyRefdBodyInst(astgen, list, kv.value);
  13669         }
  13670     }
  13671     for (body) |body_inst| {
  13672         appendPossiblyRefdBodyInst(astgen, list, body_inst);
  13673     }
  13674 }
  13675 
  13676 fn appendPossiblyRefdBodyInst(
  13677     astgen: *AstGen,
  13678     list: *std.ArrayListUnmanaged(u32),
  13679     body_inst: Zir.Inst.Index,
  13680 ) void {
  13681     list.appendAssumeCapacity(@intFromEnum(body_inst));
  13682     const kv = astgen.ref_table.fetchRemove(body_inst) orelse return;
  13683     const ref_inst = kv.value;
  13684     return appendPossiblyRefdBodyInst(astgen, list, ref_inst);
  13685 }
  13686 
  13687 fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 {
  13688     return astgen.countBodyLenAfterFixupsExtraRefs(body, &.{});
  13689 }
  13690 
  13691 /// Return the number of instructions in `body` after prepending the `ref` instructions in `ref_table`.
  13692 /// As well as all instructions in `body`, we also prepend `ref`s of any instruction in `extra_refs`.
  13693 /// For instance, if an index has been reserved with a special meaning to a child block, it must be
  13694 /// passed to `extra_refs` to ensure `ref`s of that index are added correctly.
  13695 fn countBodyLenAfterFixupsExtraRefs(astgen: *AstGen, body: []const Zir.Inst.Index, extra_refs: []const Zir.Inst.Index) u32 {
  13696     var count = body.len;
  13697     for (body) |body_inst| {
  13698         var check_inst = body_inst;
  13699         while (astgen.ref_table.get(check_inst)) |ref_inst| {
  13700             count += 1;
  13701             check_inst = ref_inst;
  13702         }
  13703     }
  13704     for (extra_refs) |extra_inst| {
  13705         var check_inst = extra_inst;
  13706         while (astgen.ref_table.get(check_inst)) |ref_inst| {
  13707             count += 1;
  13708             check_inst = ref_inst;
  13709         }
  13710     }
  13711     return @intCast(count);
  13712 }
  13713 
  13714 fn emitDbgStmt(gz: *GenZir, lc: LineColumn) !void {
  13715     if (gz.is_comptime) return;
  13716     if (gz.instructions.items.len > gz.instructions_top) {
  13717         const astgen = gz.astgen;
  13718         const last = gz.instructions.items[gz.instructions.items.len - 1];
  13719         if (astgen.instructions.items(.tag)[@intFromEnum(last)] == .dbg_stmt) {
  13720             astgen.instructions.items(.data)[@intFromEnum(last)].dbg_stmt = .{
  13721                 .line = lc[0],
  13722                 .column = lc[1],
  13723             };
  13724             return;
  13725         }
  13726     }
  13727 
  13728     _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
  13729         .dbg_stmt = .{
  13730             .line = lc[0],
  13731             .column = lc[1],
  13732         },
  13733     } });
  13734 }
  13735 
  13736 /// In some cases, Sema expects us to generate a `dbg_stmt` at the instruction
  13737 /// *index* directly preceding the next instruction (e.g. if a call is %10, it
  13738 /// expects a dbg_stmt at %9). TODO: this logic may allow redundant dbg_stmt
  13739 /// instructions; fix up Sema so we don't need it!
  13740 fn emitDbgStmtForceCurrentIndex(gz: *GenZir, lc: LineColumn) !void {
  13741     const astgen = gz.astgen;
  13742     if (gz.instructions.items.len > gz.instructions_top and
  13743         @intFromEnum(gz.instructions.items[gz.instructions.items.len - 1]) == astgen.instructions.len - 1)
  13744     {
  13745         const last = astgen.instructions.len - 1;
  13746         if (astgen.instructions.items(.tag)[last] == .dbg_stmt) {
  13747             astgen.instructions.items(.data)[last].dbg_stmt = .{
  13748                 .line = lc[0],
  13749                 .column = lc[1],
  13750             };
  13751             return;
  13752         }
  13753     }
  13754 
  13755     _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
  13756         .dbg_stmt = .{
  13757             .line = lc[0],
  13758             .column = lc[1],
  13759         },
  13760     } });
  13761 }
  13762 
  13763 fn lowerAstErrors(astgen: *AstGen) error{OutOfMemory}!void {
  13764     const gpa = astgen.gpa;
  13765     const tree = astgen.tree;
  13766     assert(tree.errors.len > 0);
  13767 
  13768     var msg: std.io.Writer.Allocating = .init(gpa);
  13769     defer msg.deinit();
  13770     const msg_w = &msg.writer;
  13771 
  13772     var notes: std.ArrayListUnmanaged(u32) = .empty;
  13773     defer notes.deinit(gpa);
  13774 
  13775     const token_starts = tree.tokens.items(.start);
  13776     const token_tags = tree.tokens.items(.tag);
  13777     const parse_err = tree.errors[0];
  13778     const tok = parse_err.token + @intFromBool(parse_err.token_is_prev);
  13779     const tok_start = token_starts[tok];
  13780     const start_char = tree.source[tok_start];
  13781 
  13782     if (token_tags[tok] == .invalid and
  13783         (start_char == '\"' or start_char == '\'' or start_char == '/' or mem.startsWith(u8, tree.source[tok_start..], "\\\\")))
  13784     {
  13785         const tok_len: u32 = @intCast(tree.tokenSlice(tok).len);
  13786         const tok_end = tok_start + tok_len;
  13787         const bad_off = blk: {
  13788             var idx = tok_start;
  13789             while (idx < tok_end) : (idx += 1) {
  13790                 switch (tree.source[idx]) {
  13791                     0x00...0x09, 0x0b...0x1f, 0x7f => break,
  13792                     else => {},
  13793                 }
  13794             }
  13795             break :blk idx - tok_start;
  13796         };
  13797 
  13798         const ast_err: Ast.Error = .{
  13799             .tag = Ast.Error.Tag.invalid_byte,
  13800             .token = tok,
  13801             .extra = .{ .offset = bad_off },
  13802         };
  13803         msg.clearRetainingCapacity();
  13804         tree.renderError(ast_err, msg_w) catch return error.OutOfMemory;
  13805         return try astgen.appendErrorTokNotesOff(tok, bad_off, "{s}", .{msg.getWritten()}, notes.items);
  13806     }
  13807 
  13808     var cur_err = tree.errors[0];
  13809     for (tree.errors[1..]) |err| {
  13810         if (err.is_note) {
  13811             tree.renderError(err, msg_w) catch return error.OutOfMemory;
  13812             try notes.append(gpa, try astgen.errNoteTok(err.token, "{s}", .{msg.getWritten()}));
  13813         } else {
  13814             // Flush error
  13815             const extra_offset = tree.errorOffset(cur_err);
  13816             tree.renderError(cur_err, msg_w) catch return error.OutOfMemory;
  13817             try astgen.appendErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.getWritten()}, notes.items);
  13818             notes.clearRetainingCapacity();
  13819             cur_err = err;
  13820 
  13821             // TODO: `Parse` currently does not have good error recovery mechanisms, so the remaining errors could be bogus.
  13822             // As such, we'll ignore all remaining errors for now. We should improve `Parse` so that we can report all the errors.
  13823             return;
  13824         }
  13825         msg.clearRetainingCapacity();
  13826     }
  13827 
  13828     // Flush error
  13829     const extra_offset = tree.errorOffset(cur_err);
  13830     tree.renderError(cur_err, msg_w) catch return error.OutOfMemory;
  13831     try astgen.appendErrorTokNotesOff(cur_err.token, extra_offset, "{s}", .{msg.getWritten()}, notes.items);
  13832 }
  13833 
  13834 const DeclarationName = union(enum) {
  13835     named: Ast.TokenIndex,
  13836     named_test: Ast.TokenIndex,
  13837     decltest: Ast.TokenIndex,
  13838     unnamed_test,
  13839     @"comptime",
  13840 };
  13841 
  13842 fn addFailedDeclaration(
  13843     wip_members: *WipMembers,
  13844     gz: *GenZir,
  13845     kind: Zir.Inst.Declaration.Unwrapped.Kind,
  13846     name: Zir.NullTerminatedString,
  13847     src_node: Ast.Node.Index,
  13848     is_pub: bool,
  13849 ) !void {
  13850     const decl_inst = try gz.makeDeclaration(src_node);
  13851     wip_members.nextDecl(decl_inst);
  13852 
  13853     var dummy_gz = gz.makeSubBlock(&gz.base);
  13854 
  13855     var value_gz = gz.makeSubBlock(&gz.base); // scope doesn't matter here
  13856     _ = try value_gz.add(.{
  13857         .tag = .extended,
  13858         .data = .{ .extended = .{
  13859             .opcode = .astgen_error,
  13860             .small = undefined,
  13861             .operand = undefined,
  13862         } },
  13863     });
  13864 
  13865     try setDeclaration(decl_inst, .{
  13866         .src_hash = @splat(0), // use a fixed hash to represent an AstGen failure; we don't care about source changes if AstGen still failed!
  13867         .src_line = gz.astgen.source_line,
  13868         .src_column = gz.astgen.source_column,
  13869         .kind = kind,
  13870         .name = name,
  13871         .is_pub = is_pub,
  13872         .is_threadlocal = false,
  13873         .linkage = .normal,
  13874         .type_gz = &dummy_gz,
  13875         .align_gz = &dummy_gz,
  13876         .linksection_gz = &dummy_gz,
  13877         .addrspace_gz = &dummy_gz,
  13878         .value_gz = &value_gz,
  13879     });
  13880 }
  13881 
  13882 /// Sets all extra data for a `declaration` instruction.
  13883 /// Unstacks `type_gz`, `align_gz`, `linksection_gz`, `addrspace_gz`, and `value_gz`.
  13884 fn setDeclaration(
  13885     decl_inst: Zir.Inst.Index,
  13886     args: struct {
  13887         src_hash: std.zig.SrcHash,
  13888         src_line: u32,
  13889         src_column: u32,
  13890 
  13891         kind: Zir.Inst.Declaration.Unwrapped.Kind,
  13892         name: Zir.NullTerminatedString,
  13893         is_pub: bool,
  13894         is_threadlocal: bool,
  13895         linkage: Zir.Inst.Declaration.Unwrapped.Linkage,
  13896         lib_name: Zir.NullTerminatedString = .empty,
  13897 
  13898         type_gz: *GenZir,
  13899         /// Must be stacked on `type_gz`.
  13900         align_gz: *GenZir,
  13901         /// Must be stacked on `align_gz`.
  13902         linksection_gz: *GenZir,
  13903         /// Must be stacked on `linksection_gz`.
  13904         addrspace_gz: *GenZir,
  13905         /// Must be stacked on `addrspace_gz` and have nothing stacked on top of it.
  13906         value_gz: *GenZir,
  13907     },
  13908 ) !void {
  13909     const astgen = args.value_gz.astgen;
  13910     const gpa = astgen.gpa;
  13911 
  13912     const type_body = args.type_gz.instructionsSliceUpto(args.align_gz);
  13913     const align_body = args.align_gz.instructionsSliceUpto(args.linksection_gz);
  13914     const linksection_body = args.linksection_gz.instructionsSliceUpto(args.addrspace_gz);
  13915     const addrspace_body = args.addrspace_gz.instructionsSliceUpto(args.value_gz);
  13916     const value_body = args.value_gz.instructionsSlice();
  13917 
  13918     const has_name = args.name != .empty;
  13919     const has_lib_name = args.lib_name != .empty;
  13920     const has_type_body = type_body.len != 0;
  13921     const has_special_body = align_body.len != 0 or linksection_body.len != 0 or addrspace_body.len != 0;
  13922     const has_value_body = value_body.len != 0;
  13923 
  13924     const id: Zir.Inst.Declaration.Flags.Id = switch (args.kind) {
  13925         .unnamed_test => .unnamed_test,
  13926         .@"test" => .@"test",
  13927         .decltest => .decltest,
  13928         .@"comptime" => .@"comptime",
  13929         .@"const" => switch (args.linkage) {
  13930             .normal => if (args.is_pub) id: {
  13931                 if (has_special_body) break :id .pub_const;
  13932                 if (has_type_body) break :id .pub_const_typed;
  13933                 break :id .pub_const_simple;
  13934             } else id: {
  13935                 if (has_special_body) break :id .@"const";
  13936                 if (has_type_body) break :id .const_typed;
  13937                 break :id .const_simple;
  13938             },
  13939             .@"extern" => if (args.is_pub) id: {
  13940                 if (has_lib_name) break :id .pub_extern_const;
  13941                 if (has_special_body) break :id .pub_extern_const;
  13942                 break :id .pub_extern_const_simple;
  13943             } else id: {
  13944                 if (has_lib_name) break :id .extern_const;
  13945                 if (has_special_body) break :id .extern_const;
  13946                 break :id .extern_const_simple;
  13947             },
  13948             .@"export" => if (args.is_pub) .pub_export_const else .export_const,
  13949         },
  13950         .@"var" => switch (args.linkage) {
  13951             .normal => if (args.is_pub) id: {
  13952                 if (args.is_threadlocal) break :id .pub_var_threadlocal;
  13953                 if (has_special_body) break :id .pub_var;
  13954                 if (has_type_body) break :id .pub_var;
  13955                 break :id .pub_var_simple;
  13956             } else id: {
  13957                 if (args.is_threadlocal) break :id .var_threadlocal;
  13958                 if (has_special_body) break :id .@"var";
  13959                 if (has_type_body) break :id .@"var";
  13960                 break :id .var_simple;
  13961             },
  13962             .@"extern" => if (args.is_pub) id: {
  13963                 if (args.is_threadlocal) break :id .pub_extern_var_threadlocal;
  13964                 break :id .pub_extern_var;
  13965             } else id: {
  13966                 if (args.is_threadlocal) break :id .extern_var_threadlocal;
  13967                 break :id .extern_var;
  13968             },
  13969             .@"export" => if (args.is_pub) id: {
  13970                 if (args.is_threadlocal) break :id .pub_export_var_threadlocal;
  13971                 break :id .pub_export_var;
  13972             } else id: {
  13973                 if (args.is_threadlocal) break :id .export_var_threadlocal;
  13974                 break :id .export_var;
  13975             },
  13976         },
  13977     };
  13978 
  13979     assert(id.hasTypeBody() or !has_type_body);
  13980     assert(id.hasSpecialBodies() or !has_special_body);
  13981     assert(id.hasValueBody() == has_value_body);
  13982     assert(id.linkage() == args.linkage);
  13983     assert(id.hasName() == has_name);
  13984     assert(id.hasLibName() or !has_lib_name);
  13985     assert(id.isPub() == args.is_pub);
  13986     assert(id.isThreadlocal() == args.is_threadlocal);
  13987 
  13988     const type_len = astgen.countBodyLenAfterFixups(type_body);
  13989     const align_len = astgen.countBodyLenAfterFixups(align_body);
  13990     const linksection_len = astgen.countBodyLenAfterFixups(linksection_body);
  13991     const addrspace_len = astgen.countBodyLenAfterFixups(addrspace_body);
  13992     const value_len = astgen.countBodyLenAfterFixups(value_body);
  13993 
  13994     const src_hash_arr: [4]u32 = @bitCast(args.src_hash);
  13995     const flags: Zir.Inst.Declaration.Flags = .{
  13996         .src_line = @intCast(args.src_line),
  13997         .src_column = @intCast(args.src_column),
  13998         .id = id,
  13999     };
  14000     const flags_arr: [2]u32 = @bitCast(flags);
  14001 
  14002     const need_extra: usize =
  14003         @typeInfo(Zir.Inst.Declaration).@"struct".fields.len +
  14004         @as(usize, @intFromBool(id.hasName())) +
  14005         @as(usize, @intFromBool(id.hasLibName())) +
  14006         @as(usize, @intFromBool(id.hasTypeBody())) +
  14007         3 * @as(usize, @intFromBool(id.hasSpecialBodies())) +
  14008         @as(usize, @intFromBool(id.hasValueBody())) +
  14009         type_len + align_len + linksection_len + addrspace_len + value_len;
  14010 
  14011     try astgen.extra.ensureUnusedCapacity(gpa, need_extra);
  14012 
  14013     const extra: Zir.Inst.Declaration = .{
  14014         .src_hash_0 = src_hash_arr[0],
  14015         .src_hash_1 = src_hash_arr[1],
  14016         .src_hash_2 = src_hash_arr[2],
  14017         .src_hash_3 = src_hash_arr[3],
  14018         .flags_0 = flags_arr[0],
  14019         .flags_1 = flags_arr[1],
  14020     };
  14021     astgen.instructions.items(.data)[@intFromEnum(decl_inst)].declaration.payload_index =
  14022         astgen.addExtraAssumeCapacity(extra);
  14023 
  14024     if (id.hasName()) {
  14025         astgen.extra.appendAssumeCapacity(@intFromEnum(args.name));
  14026     }
  14027     if (id.hasLibName()) {
  14028         astgen.extra.appendAssumeCapacity(@intFromEnum(args.lib_name));
  14029     }
  14030     if (id.hasTypeBody()) {
  14031         astgen.extra.appendAssumeCapacity(type_len);
  14032     }
  14033     if (id.hasSpecialBodies()) {
  14034         astgen.extra.appendSliceAssumeCapacity(&.{
  14035             align_len,
  14036             linksection_len,
  14037             addrspace_len,
  14038         });
  14039     }
  14040     if (id.hasValueBody()) {
  14041         astgen.extra.appendAssumeCapacity(value_len);
  14042     }
  14043 
  14044     astgen.appendBodyWithFixups(type_body);
  14045     astgen.appendBodyWithFixups(align_body);
  14046     astgen.appendBodyWithFixups(linksection_body);
  14047     astgen.appendBodyWithFixups(addrspace_body);
  14048     astgen.appendBodyWithFixups(value_body);
  14049 
  14050     args.value_gz.unstack();
  14051     args.addrspace_gz.unstack();
  14052     args.linksection_gz.unstack();
  14053     args.align_gz.unstack();
  14054     args.type_gz.unstack();
  14055 }
  14056 
  14057 /// Given a list of instructions, returns a list of all instructions which are a `ref` of one of the originals,
  14058 /// from `astgen.ref_table`, non-recursively. The entries are removed from `astgen.ref_table`, and the returned
  14059 /// slice can then be treated as its own body, to append `ref` instructions to a body other than the one they
  14060 /// would normally exist in.
  14061 ///
  14062 /// This is used when lowering functions. Very rarely, the callconv expression, align expression, etc may reference
  14063 /// function parameters via `&param`; in this case, we need to lower to a `ref` instruction in the callconv/align/etc
  14064 /// body, rather than in the declaration body. However, we don't append these bodies to `extra` until we've evaluated
  14065 /// *all* of the bodies into a big `GenZir` stack. Therefore, we use this function to pull out these per-body `ref`
  14066 /// instructions which must be emitted.
  14067 fn fetchRemoveRefEntries(astgen: *AstGen, param_insts: []const Zir.Inst.Index) ![]Zir.Inst.Index {
  14068     var refs: std.ArrayListUnmanaged(Zir.Inst.Index) = .empty;
  14069     for (param_insts) |param_inst| {
  14070         if (astgen.ref_table.fetchRemove(param_inst)) |kv| {
  14071             try refs.append(astgen.arena, kv.value);
  14072         }
  14073     }
  14074     return refs.items;
  14075 }
  14076 
  14077 test {
  14078     _ = &generate;
  14079 }