motiejus/zig

fork of https://codeberg.org/ziglang/zig
git clone https://git.jakstys.lt/motiejus/zig.git
Log | Tree | Refs | README | LICENSE

lib/std/zig/AstGen.zig (566809B) - Raw


      1 //! Ingests an AST and produces ZIR code.
      2 const AstGen = @This();
      3 
      4 const std = @import("std");
      5 const Ast = std.zig.Ast;
      6 const mem = std.mem;
      7 const Allocator = std.mem.Allocator;
      8 const assert = std.debug.assert;
      9 const ArrayListUnmanaged = std.ArrayListUnmanaged;
     10 const StringIndexAdapter = std.hash_map.StringIndexAdapter;
     11 const StringIndexContext = std.hash_map.StringIndexContext;
     12 
     13 const isPrimitive = std.zig.primitives.isPrimitive;
     14 
     15 const Zir = std.zig.Zir;
     16 const BuiltinFn = std.zig.BuiltinFn;
     17 const AstRlAnnotate = std.zig.AstRlAnnotate;
     18 
     19 gpa: Allocator,
     20 tree: *const Ast,
     21 /// The set of nodes which, given the choice, must expose a result pointer to
     22 /// sub-expressions. See `AstRlAnnotate` for details.
     23 nodes_need_rl: *const AstRlAnnotate.RlNeededSet,
     24 instructions: std.MultiArrayList(Zir.Inst) = .{},
     25 extra: ArrayListUnmanaged(u32) = .empty,
     26 string_bytes: ArrayListUnmanaged(u8) = .empty,
     27 /// Tracks the current byte offset within the source file.
     28 /// Used to populate line deltas in the ZIR. AstGen maintains
     29 /// this "cursor" throughout the entire AST lowering process in order
     30 /// to avoid starting over the line/column scan for every declaration, which
     31 /// would be O(N^2).
     32 source_offset: u32 = 0,
     33 /// Tracks the corresponding line of `source_offset`.
     34 /// This value is absolute.
     35 source_line: u32 = 0,
     36 /// Tracks the corresponding column of `source_offset`.
     37 /// This value is absolute.
     38 source_column: u32 = 0,
     39 /// Used for temporary allocations; freed after AstGen is complete.
     40 /// The resulting ZIR code has no references to anything in this arena.
     41 arena: Allocator,
     42 string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .empty,
     43 compile_errors: ArrayListUnmanaged(Zir.Inst.CompileErrors.Item) = .empty,
     44 /// The topmost block of the current function.
     45 fn_block: ?*GenZir = null,
     46 fn_var_args: bool = false,
     47 /// Whether we are somewhere within a function. If `true`, any container decls may be
     48 /// generic and thus must be tunneled through closure.
     49 within_fn: bool = false,
     50 /// The return type of the current function. This may be a trivial `Ref`, or
     51 /// otherwise it refers to a `ret_type` instruction.
     52 fn_ret_ty: Zir.Inst.Ref = .none,
     53 /// Maps string table indexes to the first `@import` ZIR instruction
     54 /// that uses this string as the operand.
     55 imports: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, Ast.TokenIndex) = .empty,
     56 /// Used for temporary storage when building payloads.
     57 scratch: std.ArrayListUnmanaged(u32) = .empty,
     58 /// Whenever a `ref` instruction is needed, it is created and saved in this
     59 /// table instead of being immediately appended to the current block body.
     60 /// Then, when the instruction is being added to the parent block (typically from
     61 /// setBlockBody), if it has a ref_table entry, then the ref instruction is added
     62 /// there. This makes sure two properties are upheld:
     63 /// 1. All pointers to the same locals return the same address. This is required
     64 ///    to be compliant with the language specification.
     65 /// 2. `ref` instructions will dominate their uses. This is a required property
     66 ///    of ZIR.
     67 /// The key is the ref operand; the value is the ref instruction.
     68 ref_table: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .empty,
     69 /// Any information which should trigger invalidation of incremental compilation
     70 /// data should be used to update this hasher. The result is the final source
     71 /// hash of the enclosing declaration/etc.
     72 src_hasher: std.zig.SrcHasher,
     73 
     74 const InnerError = error{ OutOfMemory, AnalysisFail };
     75 
     76 fn addExtra(astgen: *AstGen, extra: anytype) Allocator.Error!u32 {
     77     const fields = std.meta.fields(@TypeOf(extra));
     78     try astgen.extra.ensureUnusedCapacity(astgen.gpa, fields.len);
     79     return addExtraAssumeCapacity(astgen, extra);
     80 }
     81 
     82 fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {
     83     const fields = std.meta.fields(@TypeOf(extra));
     84     const extra_index: u32 = @intCast(astgen.extra.items.len);
     85     astgen.extra.items.len += fields.len;
     86     setExtra(astgen, extra_index, extra);
     87     return extra_index;
     88 }
     89 
     90 fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void {
     91     const fields = std.meta.fields(@TypeOf(extra));
     92     var i = index;
     93     inline for (fields) |field| {
     94         astgen.extra.items[i] = switch (field.type) {
     95             u32 => @field(extra, field.name),
     96 
     97             Zir.Inst.Ref,
     98             Zir.Inst.Index,
     99             Zir.Inst.Declaration.Name,
    100             Zir.NullTerminatedString,
    101             => @intFromEnum(@field(extra, field.name)),
    102 
    103             i32,
    104             Zir.Inst.Call.Flags,
    105             Zir.Inst.BuiltinCall.Flags,
    106             Zir.Inst.SwitchBlock.Bits,
    107             Zir.Inst.SwitchBlockErrUnion.Bits,
    108             Zir.Inst.FuncFancy.Bits,
    109             Zir.Inst.Declaration.Flags,
    110             => @bitCast(@field(extra, field.name)),
    111 
    112             else => @compileError("bad field type"),
    113         };
    114         i += 1;
    115     }
    116 }
    117 
    118 fn reserveExtra(astgen: *AstGen, size: usize) Allocator.Error!u32 {
    119     const extra_index: u32 = @intCast(astgen.extra.items.len);
    120     try astgen.extra.resize(astgen.gpa, extra_index + size);
    121     return extra_index;
    122 }
    123 
    124 fn appendRefs(astgen: *AstGen, refs: []const Zir.Inst.Ref) !void {
    125     return astgen.extra.appendSlice(astgen.gpa, @ptrCast(refs));
    126 }
    127 
    128 fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const Zir.Inst.Ref) void {
    129     astgen.extra.appendSliceAssumeCapacity(@ptrCast(refs));
    130 }
    131 
    132 pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
    133     var arena = std.heap.ArenaAllocator.init(gpa);
    134     defer arena.deinit();
    135 
    136     var nodes_need_rl = try AstRlAnnotate.annotate(gpa, arena.allocator(), tree);
    137     defer nodes_need_rl.deinit(gpa);
    138 
    139     var astgen: AstGen = .{
    140         .gpa = gpa,
    141         .arena = arena.allocator(),
    142         .tree = &tree,
    143         .nodes_need_rl = &nodes_need_rl,
    144         .src_hasher = undefined, // `structDeclInner` for the root struct will set this
    145     };
    146     defer astgen.deinit(gpa);
    147 
    148     // String table index 0 is reserved for `NullTerminatedString.empty`.
    149     try astgen.string_bytes.append(gpa, 0);
    150 
    151     // We expect at least as many ZIR instructions and extra data items
    152     // as AST nodes.
    153     try astgen.instructions.ensureTotalCapacity(gpa, tree.nodes.len);
    154 
    155     // First few indexes of extra are reserved and set at the end.
    156     const reserved_count = @typeInfo(Zir.ExtraIndex).@"enum".fields.len;
    157     try astgen.extra.ensureTotalCapacity(gpa, tree.nodes.len + reserved_count);
    158     astgen.extra.items.len += reserved_count;
    159 
    160     var top_scope: Scope.Top = .{};
    161 
    162     var gz_instructions: std.ArrayListUnmanaged(Zir.Inst.Index) = .empty;
    163     var gen_scope: GenZir = .{
    164         .is_comptime = true,
    165         .parent = &top_scope.base,
    166         .anon_name_strategy = .parent,
    167         .decl_node_index = 0,
    168         .decl_line = 0,
    169         .astgen = &astgen,
    170         .instructions = &gz_instructions,
    171         .instructions_top = 0,
    172     };
    173     defer gz_instructions.deinit(gpa);
    174 
    175     // The AST -> ZIR lowering process assumes an AST that does not have any
    176     // parse errors.
    177     if (tree.errors.len == 0) {
    178         if (AstGen.structDeclInner(
    179             &gen_scope,
    180             &gen_scope.base,
    181             0,
    182             tree.containerDeclRoot(),
    183             .auto,
    184             0,
    185         )) |struct_decl_ref| {
    186             assert(struct_decl_ref.toIndex().? == .main_struct_inst);
    187         } else |err| switch (err) {
    188             error.OutOfMemory => return error.OutOfMemory,
    189             error.AnalysisFail => {}, // Handled via compile_errors below.
    190         }
    191     } else {
    192         try lowerAstErrors(&astgen);
    193     }
    194 
    195     const err_index = @intFromEnum(Zir.ExtraIndex.compile_errors);
    196     if (astgen.compile_errors.items.len == 0) {
    197         astgen.extra.items[err_index] = 0;
    198     } else {
    199         try astgen.extra.ensureUnusedCapacity(gpa, 1 + astgen.compile_errors.items.len *
    200             @typeInfo(Zir.Inst.CompileErrors.Item).@"struct".fields.len);
    201 
    202         astgen.extra.items[err_index] = astgen.addExtraAssumeCapacity(Zir.Inst.CompileErrors{
    203             .items_len = @intCast(astgen.compile_errors.items.len),
    204         });
    205 
    206         for (astgen.compile_errors.items) |item| {
    207             _ = astgen.addExtraAssumeCapacity(item);
    208         }
    209     }
    210 
    211     const imports_index = @intFromEnum(Zir.ExtraIndex.imports);
    212     if (astgen.imports.count() == 0) {
    213         astgen.extra.items[imports_index] = 0;
    214     } else {
    215         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Imports).@"struct".fields.len +
    216             astgen.imports.count() * @typeInfo(Zir.Inst.Imports.Item).@"struct".fields.len);
    217 
    218         astgen.extra.items[imports_index] = astgen.addExtraAssumeCapacity(Zir.Inst.Imports{
    219             .imports_len = @intCast(astgen.imports.count()),
    220         });
    221 
    222         var it = astgen.imports.iterator();
    223         while (it.next()) |entry| {
    224             _ = astgen.addExtraAssumeCapacity(Zir.Inst.Imports.Item{
    225                 .name = entry.key_ptr.*,
    226                 .token = entry.value_ptr.*,
    227             });
    228         }
    229     }
    230 
    231     return Zir{
    232         .instructions = astgen.instructions.toOwnedSlice(),
    233         .string_bytes = try astgen.string_bytes.toOwnedSlice(gpa),
    234         .extra = try astgen.extra.toOwnedSlice(gpa),
    235     };
    236 }
    237 
    238 fn deinit(astgen: *AstGen, gpa: Allocator) void {
    239     astgen.instructions.deinit(gpa);
    240     astgen.extra.deinit(gpa);
    241     astgen.string_table.deinit(gpa);
    242     astgen.string_bytes.deinit(gpa);
    243     astgen.compile_errors.deinit(gpa);
    244     astgen.imports.deinit(gpa);
    245     astgen.scratch.deinit(gpa);
    246     astgen.ref_table.deinit(gpa);
    247 }
    248 
    249 const ResultInfo = struct {
    250     /// The semantics requested for the result location
    251     rl: Loc,
    252 
    253     /// The "operator" consuming the result location
    254     ctx: Context = .none,
    255 
    256     /// Turns a `coerced_ty` back into a `ty`. Should be called at branch points
    257     /// such as if and switch expressions.
    258     fn br(ri: ResultInfo) ResultInfo {
    259         return switch (ri.rl) {
    260             .coerced_ty => |ty| .{
    261                 .rl = .{ .ty = ty },
    262                 .ctx = ri.ctx,
    263             },
    264             else => ri,
    265         };
    266     }
    267 
    268     fn zirTag(ri: ResultInfo) Zir.Inst.Tag {
    269         switch (ri.rl) {
    270             .ty => return switch (ri.ctx) {
    271                 .shift_op => .as_shift_operand,
    272                 else => .as_node,
    273             },
    274             else => unreachable,
    275         }
    276     }
    277 
    278     const Loc = union(enum) {
    279         /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
    280         /// expression should be generated. The result instruction from the expression must
    281         /// be ignored.
    282         discard,
    283         /// The expression has an inferred type, and it will be evaluated as an rvalue.
    284         none,
    285         /// The expression will be coerced into this type, but it will be evaluated as an rvalue.
    286         ty: Zir.Inst.Ref,
    287         /// Same as `ty` but it is guaranteed that Sema will additionally perform the coercion,
    288         /// so no `as` instruction needs to be emitted.
    289         coerced_ty: Zir.Inst.Ref,
    290         /// The expression must generate a pointer rather than a value. For example, the left hand side
    291         /// of an assignment uses this kind of result location.
    292         ref,
    293         /// The expression must generate a pointer rather than a value, and the pointer will be coerced
    294         /// by other code to this type, which is guaranteed by earlier instructions to be a pointer type.
    295         ref_coerced_ty: Zir.Inst.Ref,
    296         /// The expression must store its result into this typed pointer. The result instruction
    297         /// from the expression must be ignored.
    298         ptr: PtrResultLoc,
    299         /// The expression must store its result into this allocation, which has an inferred type.
    300         /// The result instruction from the expression must be ignored.
    301         /// Always an instruction with tag `alloc_inferred`.
    302         inferred_ptr: Zir.Inst.Ref,
    303         /// The expression has a sequence of pointers to store its results into due to a destructure
    304         /// operation. Each of these pointers may or may not have an inferred type.
    305         destructure: struct {
    306             /// The AST node of the destructure operation itself.
    307             src_node: Ast.Node.Index,
    308             /// The pointers to store results into.
    309             components: []const DestructureComponent,
    310         },
    311 
    312         const DestructureComponent = union(enum) {
    313             typed_ptr: PtrResultLoc,
    314             inferred_ptr: Zir.Inst.Ref,
    315             discard,
    316         };
    317 
    318         const PtrResultLoc = struct {
    319             inst: Zir.Inst.Ref,
    320             src_node: ?Ast.Node.Index = null,
    321         };
    322 
    323         /// Find the result type for a cast builtin given the result location.
    324         /// If the location does not have a known result type, returns `null`.
    325         fn resultType(rl: Loc, gz: *GenZir, node: Ast.Node.Index) !?Zir.Inst.Ref {
    326             return switch (rl) {
    327                 .discard, .none, .ref, .inferred_ptr, .destructure => null,
    328                 .ty, .coerced_ty => |ty_ref| ty_ref,
    329                 .ref_coerced_ty => |ptr_ty| try gz.addUnNode(.elem_type, ptr_ty, node),
    330                 .ptr => |ptr| {
    331                     const ptr_ty = try gz.addUnNode(.typeof, ptr.inst, node);
    332                     return try gz.addUnNode(.elem_type, ptr_ty, node);
    333                 },
    334             };
    335         }
    336 
    337         /// Find the result type for a cast builtin given the result location.
    338         /// If the location does not have a known result type, emits an error on
    339         /// the given node.
    340         fn resultTypeForCast(rl: Loc, gz: *GenZir, node: Ast.Node.Index, builtin_name: []const u8) !Zir.Inst.Ref {
    341             const astgen = gz.astgen;
    342             if (try rl.resultType(gz, node)) |ty| return ty;
    343             switch (rl) {
    344                 .destructure => |destructure| return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{
    345                     try astgen.errNoteNode(destructure.src_node, "destructure expressions do not provide a single result type", .{}),
    346                     try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}),
    347                 }),
    348                 else => return astgen.failNodeNotes(node, "{s} must have a known result type", .{builtin_name}, &.{
    349                     try astgen.errNoteNode(node, "use @as to provide explicit result type", .{}),
    350                 }),
    351             }
    352         }
    353     };
    354 
    355     const Context = enum {
    356         /// The expression is the operand to a return expression.
    357         @"return",
    358         /// The expression is the input to an error-handling operator (if-else, try, or catch).
    359         error_handling_expr,
    360         /// The expression is the right-hand side of a shift operation.
    361         shift_op,
    362         /// The expression is an argument in a function call.
    363         fn_arg,
    364         /// The expression is the right-hand side of an initializer for a `const` variable
    365         const_init,
    366         /// The expression is the right-hand side of an assignment expression.
    367         assignment,
    368         /// No specific operator in particular.
    369         none,
    370     };
    371 };
    372 
    373 const coerced_align_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .u29_type } };
    374 const coerced_linksection_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .slice_const_u8_type } };
    375 const coerced_type_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .type_type } };
    376 const coerced_bool_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .bool_type } };
    377 
    378 fn typeExpr(gz: *GenZir, scope: *Scope, type_node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
    379     return comptimeExpr(gz, scope, coerced_type_ri, type_node);
    380 }
    381 
    382 fn reachableTypeExpr(
    383     gz: *GenZir,
    384     scope: *Scope,
    385     type_node: Ast.Node.Index,
    386     reachable_node: Ast.Node.Index,
    387 ) InnerError!Zir.Inst.Ref {
    388     return reachableExprComptime(gz, scope, coerced_type_ri, type_node, reachable_node, true);
    389 }
    390 
    391 /// Same as `expr` but fails with a compile error if the result type is `noreturn`.
    392 fn reachableExpr(
    393     gz: *GenZir,
    394     scope: *Scope,
    395     ri: ResultInfo,
    396     node: Ast.Node.Index,
    397     reachable_node: Ast.Node.Index,
    398 ) InnerError!Zir.Inst.Ref {
    399     return reachableExprComptime(gz, scope, ri, node, reachable_node, false);
    400 }
    401 
    402 fn reachableExprComptime(
    403     gz: *GenZir,
    404     scope: *Scope,
    405     ri: ResultInfo,
    406     node: Ast.Node.Index,
    407     reachable_node: Ast.Node.Index,
    408     force_comptime: bool,
    409 ) InnerError!Zir.Inst.Ref {
    410     const result_inst = if (force_comptime)
    411         try comptimeExpr(gz, scope, ri, node)
    412     else
    413         try expr(gz, scope, ri, node);
    414 
    415     if (gz.refIsNoReturn(result_inst)) {
    416         try gz.astgen.appendErrorNodeNotes(reachable_node, "unreachable code", .{}, &[_]u32{
    417             try gz.astgen.errNoteNode(node, "control flow is diverted here", .{}),
    418         });
    419     }
    420     return result_inst;
    421 }
    422 
    423 fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
    424     const astgen = gz.astgen;
    425     const tree = astgen.tree;
    426     const node_tags = tree.nodes.items(.tag);
    427     const main_tokens = tree.nodes.items(.main_token);
    428     switch (node_tags[node]) {
    429         .root => unreachable,
    430         .@"usingnamespace" => unreachable,
    431         .test_decl => unreachable,
    432         .global_var_decl => unreachable,
    433         .local_var_decl => unreachable,
    434         .simple_var_decl => unreachable,
    435         .aligned_var_decl => unreachable,
    436         .switch_case => unreachable,
    437         .switch_case_inline => unreachable,
    438         .switch_case_one => unreachable,
    439         .switch_case_inline_one => unreachable,
    440         .container_field_init => unreachable,
    441         .container_field_align => unreachable,
    442         .container_field => unreachable,
    443         .asm_output => unreachable,
    444         .asm_input => unreachable,
    445 
    446         .assign,
    447         .assign_destructure,
    448         .assign_bit_and,
    449         .assign_bit_or,
    450         .assign_shl,
    451         .assign_shl_sat,
    452         .assign_shr,
    453         .assign_bit_xor,
    454         .assign_div,
    455         .assign_sub,
    456         .assign_sub_wrap,
    457         .assign_sub_sat,
    458         .assign_mod,
    459         .assign_add,
    460         .assign_add_wrap,
    461         .assign_add_sat,
    462         .assign_mul,
    463         .assign_mul_wrap,
    464         .assign_mul_sat,
    465         .add,
    466         .add_wrap,
    467         .add_sat,
    468         .sub,
    469         .sub_wrap,
    470         .sub_sat,
    471         .mul,
    472         .mul_wrap,
    473         .mul_sat,
    474         .div,
    475         .mod,
    476         .bit_and,
    477         .bit_or,
    478         .shl,
    479         .shl_sat,
    480         .shr,
    481         .bit_xor,
    482         .bang_equal,
    483         .equal_equal,
    484         .greater_than,
    485         .greater_or_equal,
    486         .less_than,
    487         .less_or_equal,
    488         .array_cat,
    489         .array_mult,
    490         .bool_and,
    491         .bool_or,
    492         .@"asm",
    493         .asm_simple,
    494         .string_literal,
    495         .number_literal,
    496         .call,
    497         .call_comma,
    498         .async_call,
    499         .async_call_comma,
    500         .call_one,
    501         .call_one_comma,
    502         .async_call_one,
    503         .async_call_one_comma,
    504         .unreachable_literal,
    505         .@"return",
    506         .@"if",
    507         .if_simple,
    508         .@"while",
    509         .while_simple,
    510         .while_cont,
    511         .bool_not,
    512         .address_of,
    513         .optional_type,
    514         .block,
    515         .block_semicolon,
    516         .block_two,
    517         .block_two_semicolon,
    518         .@"break",
    519         .ptr_type_aligned,
    520         .ptr_type_sentinel,
    521         .ptr_type,
    522         .ptr_type_bit_range,
    523         .array_type,
    524         .array_type_sentinel,
    525         .enum_literal,
    526         .multiline_string_literal,
    527         .char_literal,
    528         .@"defer",
    529         .@"errdefer",
    530         .@"catch",
    531         .error_union,
    532         .merge_error_sets,
    533         .switch_range,
    534         .for_range,
    535         .@"await",
    536         .bit_not,
    537         .negation,
    538         .negation_wrap,
    539         .@"resume",
    540         .@"try",
    541         .slice,
    542         .slice_open,
    543         .slice_sentinel,
    544         .array_init_one,
    545         .array_init_one_comma,
    546         .array_init_dot_two,
    547         .array_init_dot_two_comma,
    548         .array_init_dot,
    549         .array_init_dot_comma,
    550         .array_init,
    551         .array_init_comma,
    552         .struct_init_one,
    553         .struct_init_one_comma,
    554         .struct_init_dot_two,
    555         .struct_init_dot_two_comma,
    556         .struct_init_dot,
    557         .struct_init_dot_comma,
    558         .struct_init,
    559         .struct_init_comma,
    560         .@"switch",
    561         .switch_comma,
    562         .@"for",
    563         .for_simple,
    564         .@"suspend",
    565         .@"continue",
    566         .fn_proto_simple,
    567         .fn_proto_multi,
    568         .fn_proto_one,
    569         .fn_proto,
    570         .fn_decl,
    571         .anyframe_type,
    572         .anyframe_literal,
    573         .error_set_decl,
    574         .container_decl,
    575         .container_decl_trailing,
    576         .container_decl_two,
    577         .container_decl_two_trailing,
    578         .container_decl_arg,
    579         .container_decl_arg_trailing,
    580         .tagged_union,
    581         .tagged_union_trailing,
    582         .tagged_union_two,
    583         .tagged_union_two_trailing,
    584         .tagged_union_enum_tag,
    585         .tagged_union_enum_tag_trailing,
    586         .@"comptime",
    587         .@"nosuspend",
    588         .error_value,
    589         => return astgen.failNode(node, "invalid left-hand side to assignment", .{}),
    590 
    591         .builtin_call,
    592         .builtin_call_comma,
    593         .builtin_call_two,
    594         .builtin_call_two_comma,
    595         => {
    596             const builtin_token = main_tokens[node];
    597             const builtin_name = tree.tokenSlice(builtin_token);
    598             // If the builtin is an invalid name, we don't cause an error here; instead
    599             // let it pass, and the error will be "invalid builtin function" later.
    600             if (BuiltinFn.list.get(builtin_name)) |info| {
    601                 if (!info.allows_lvalue) {
    602                     return astgen.failNode(node, "invalid left-hand side to assignment", .{});
    603                 }
    604             }
    605         },
    606 
    607         // These can be assigned to.
    608         .unwrap_optional,
    609         .deref,
    610         .field_access,
    611         .array_access,
    612         .identifier,
    613         .grouped_expression,
    614         .@"orelse",
    615         => {},
    616     }
    617     return expr(gz, scope, .{ .rl = .ref }, node);
    618 }
    619 
    620 /// Turn Zig AST into untyped ZIR instructions.
    621 /// When `rl` is discard, ptr, inferred_ptr, or inferred_ptr, the
    622 /// result instruction can be used to inspect whether it is isNoReturn() but that is it,
    623 /// it must otherwise not be used.
    624 fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
    625     const astgen = gz.astgen;
    626     const tree = astgen.tree;
    627     const main_tokens = tree.nodes.items(.main_token);
    628     const token_tags = tree.tokens.items(.tag);
    629     const node_datas = tree.nodes.items(.data);
    630     const node_tags = tree.nodes.items(.tag);
    631 
    632     const prev_anon_name_strategy = gz.anon_name_strategy;
    633     defer gz.anon_name_strategy = prev_anon_name_strategy;
    634     if (!nodeUsesAnonNameStrategy(tree, node)) {
    635         gz.anon_name_strategy = .anon;
    636     }
    637 
    638     switch (node_tags[node]) {
    639         .root => unreachable, // Top-level declaration.
    640         .@"usingnamespace" => unreachable, // Top-level declaration.
    641         .test_decl => unreachable, // Top-level declaration.
    642         .container_field_init => unreachable, // Top-level declaration.
    643         .container_field_align => unreachable, // Top-level declaration.
    644         .container_field => unreachable, // Top-level declaration.
    645         .fn_decl => unreachable, // Top-level declaration.
    646 
    647         .global_var_decl => unreachable, // Handled in `blockExpr`.
    648         .local_var_decl => unreachable, // Handled in `blockExpr`.
    649         .simple_var_decl => unreachable, // Handled in `blockExpr`.
    650         .aligned_var_decl => unreachable, // Handled in `blockExpr`.
    651         .@"defer" => unreachable, // Handled in `blockExpr`.
    652         .@"errdefer" => unreachable, // Handled in `blockExpr`.
    653 
    654         .switch_case => unreachable, // Handled in `switchExpr`.
    655         .switch_case_inline => unreachable, // Handled in `switchExpr`.
    656         .switch_case_one => unreachable, // Handled in `switchExpr`.
    657         .switch_case_inline_one => unreachable, // Handled in `switchExpr`.
    658         .switch_range => unreachable, // Handled in `switchExpr`.
    659 
    660         .asm_output => unreachable, // Handled in `asmExpr`.
    661         .asm_input => unreachable, // Handled in `asmExpr`.
    662 
    663         .for_range => unreachable, // Handled in `forExpr`.
    664 
    665         .assign => {
    666             try assign(gz, scope, node);
    667             return rvalue(gz, ri, .void_value, node);
    668         },
    669 
    670         .assign_destructure => {
    671             // Note that this variant does not declare any new var/const: that
    672             // variant is handled by `blockExprStmts`.
    673             try assignDestructure(gz, scope, node);
    674             return rvalue(gz, ri, .void_value, node);
    675         },
    676 
    677         .assign_shl => {
    678             try assignShift(gz, scope, node, .shl);
    679             return rvalue(gz, ri, .void_value, node);
    680         },
    681         .assign_shl_sat => {
    682             try assignShiftSat(gz, scope, node);
    683             return rvalue(gz, ri, .void_value, node);
    684         },
    685         .assign_shr => {
    686             try assignShift(gz, scope, node, .shr);
    687             return rvalue(gz, ri, .void_value, node);
    688         },
    689 
    690         .assign_bit_and => {
    691             try assignOp(gz, scope, node, .bit_and);
    692             return rvalue(gz, ri, .void_value, node);
    693         },
    694         .assign_bit_or => {
    695             try assignOp(gz, scope, node, .bit_or);
    696             return rvalue(gz, ri, .void_value, node);
    697         },
    698         .assign_bit_xor => {
    699             try assignOp(gz, scope, node, .xor);
    700             return rvalue(gz, ri, .void_value, node);
    701         },
    702         .assign_div => {
    703             try assignOp(gz, scope, node, .div);
    704             return rvalue(gz, ri, .void_value, node);
    705         },
    706         .assign_sub => {
    707             try assignOp(gz, scope, node, .sub);
    708             return rvalue(gz, ri, .void_value, node);
    709         },
    710         .assign_sub_wrap => {
    711             try assignOp(gz, scope, node, .subwrap);
    712             return rvalue(gz, ri, .void_value, node);
    713         },
    714         .assign_sub_sat => {
    715             try assignOp(gz, scope, node, .sub_sat);
    716             return rvalue(gz, ri, .void_value, node);
    717         },
    718         .assign_mod => {
    719             try assignOp(gz, scope, node, .mod_rem);
    720             return rvalue(gz, ri, .void_value, node);
    721         },
    722         .assign_add => {
    723             try assignOp(gz, scope, node, .add);
    724             return rvalue(gz, ri, .void_value, node);
    725         },
    726         .assign_add_wrap => {
    727             try assignOp(gz, scope, node, .addwrap);
    728             return rvalue(gz, ri, .void_value, node);
    729         },
    730         .assign_add_sat => {
    731             try assignOp(gz, scope, node, .add_sat);
    732             return rvalue(gz, ri, .void_value, node);
    733         },
    734         .assign_mul => {
    735             try assignOp(gz, scope, node, .mul);
    736             return rvalue(gz, ri, .void_value, node);
    737         },
    738         .assign_mul_wrap => {
    739             try assignOp(gz, scope, node, .mulwrap);
    740             return rvalue(gz, ri, .void_value, node);
    741         },
    742         .assign_mul_sat => {
    743             try assignOp(gz, scope, node, .mul_sat);
    744             return rvalue(gz, ri, .void_value, node);
    745         },
    746 
    747         // zig fmt: off
    748         .shl => return shiftOp(gz, scope, ri, node, node_datas[node].lhs, node_datas[node].rhs, .shl),
    749         .shr => return shiftOp(gz, scope, ri, node, node_datas[node].lhs, node_datas[node].rhs, .shr),
    750 
    751         .add      => return simpleBinOp(gz, scope, ri, node, .add),
    752         .add_wrap => return simpleBinOp(gz, scope, ri, node, .addwrap),
    753         .add_sat  => return simpleBinOp(gz, scope, ri, node, .add_sat),
    754         .sub      => return simpleBinOp(gz, scope, ri, node, .sub),
    755         .sub_wrap => return simpleBinOp(gz, scope, ri, node, .subwrap),
    756         .sub_sat  => return simpleBinOp(gz, scope, ri, node, .sub_sat),
    757         .mul      => return simpleBinOp(gz, scope, ri, node, .mul),
    758         .mul_wrap => return simpleBinOp(gz, scope, ri, node, .mulwrap),
    759         .mul_sat  => return simpleBinOp(gz, scope, ri, node, .mul_sat),
    760         .div      => return simpleBinOp(gz, scope, ri, node, .div),
    761         .mod      => return simpleBinOp(gz, scope, ri, node, .mod_rem),
    762         .shl_sat  => return simpleBinOp(gz, scope, ri, node, .shl_sat),
    763 
    764         .bit_and          => return simpleBinOp(gz, scope, ri, node, .bit_and),
    765         .bit_or           => return simpleBinOp(gz, scope, ri, node, .bit_or),
    766         .bit_xor          => return simpleBinOp(gz, scope, ri, node, .xor),
    767         .bang_equal       => return simpleBinOp(gz, scope, ri, node, .cmp_neq),
    768         .equal_equal      => return simpleBinOp(gz, scope, ri, node, .cmp_eq),
    769         .greater_than     => return simpleBinOp(gz, scope, ri, node, .cmp_gt),
    770         .greater_or_equal => return simpleBinOp(gz, scope, ri, node, .cmp_gte),
    771         .less_than        => return simpleBinOp(gz, scope, ri, node, .cmp_lt),
    772         .less_or_equal    => return simpleBinOp(gz, scope, ri, node, .cmp_lte),
    773         .array_cat        => return simpleBinOp(gz, scope, ri, node, .array_cat),
    774 
    775         .array_mult => {
    776             // This syntax form does not currently use the result type in the language specification.
    777             // However, the result type can be used to emit more optimal code for large multiplications by
    778             // having Sema perform a coercion before the multiplication operation.
    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 }, node_datas[node].lhs),
    782                 .rhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs),
    783             });
    784             return rvalue(gz, ri, result, node);
    785         },
    786 
    787         .error_union      => return simpleBinOp(gz, scope, ri, node, .error_union_type),
    788         .merge_error_sets => return simpleBinOp(gz, scope, ri, node, .merge_error_sets),
    789 
    790         .bool_and => return boolBinOp(gz, scope, ri, node, .bool_br_and),
    791         .bool_or  => return boolBinOp(gz, scope, ri, node, .bool_br_or),
    792 
    793         .bool_not => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri, node_datas[node].lhs, .bool_not),
    794         .bit_not  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, node_datas[node].lhs, .bit_not),
    795 
    796         .negation      => return   negation(gz, scope, ri, node),
    797         .negation_wrap => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, node_datas[node].lhs, .negate_wrap),
    798 
    799         .identifier => return identifier(gz, scope, ri, node),
    800 
    801         .asm_simple,
    802         .@"asm",
    803         => return asmExpr(gz, scope, ri, node, tree.fullAsm(node).?),
    804 
    805         .string_literal           => return stringLiteral(gz, ri, node),
    806         .multiline_string_literal => return multilineStringLiteral(gz, ri, node),
    807 
    808         .number_literal => return numberLiteral(gz, ri, node, node, .positive),
    809         // zig fmt: on
    810 
    811         .builtin_call_two, .builtin_call_two_comma => {
    812             if (node_datas[node].lhs == 0) {
    813                 const params = [_]Ast.Node.Index{};
    814                 return builtinCall(gz, scope, ri, node, &params, false);
    815             } else if (node_datas[node].rhs == 0) {
    816                 const params = [_]Ast.Node.Index{node_datas[node].lhs};
    817                 return builtinCall(gz, scope, ri, node, &params, false);
    818             } else {
    819                 const params = [_]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
    820                 return builtinCall(gz, scope, ri, node, &params, false);
    821             }
    822         },
    823         .builtin_call, .builtin_call_comma => {
    824             const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
    825             return builtinCall(gz, scope, ri, node, params, false);
    826         },
    827 
    828         .call_one,
    829         .call_one_comma,
    830         .async_call_one,
    831         .async_call_one_comma,
    832         .call,
    833         .call_comma,
    834         .async_call,
    835         .async_call_comma,
    836         => {
    837             var buf: [1]Ast.Node.Index = undefined;
    838             return callExpr(gz, scope, ri, node, tree.fullCall(&buf, node).?);
    839         },
    840 
    841         .unreachable_literal => {
    842             try emitDbgNode(gz, node);
    843             _ = try gz.addAsIndex(.{
    844                 .tag = .@"unreachable",
    845                 .data = .{ .@"unreachable" = .{
    846                     .src_node = gz.nodeIndexToRelative(node),
    847                 } },
    848             });
    849             return Zir.Inst.Ref.unreachable_value;
    850         },
    851         .@"return" => return ret(gz, scope, node),
    852         .field_access => return fieldAccess(gz, scope, ri, node),
    853 
    854         .if_simple,
    855         .@"if",
    856         => {
    857             const if_full = tree.fullIf(node).?;
    858             no_switch_on_err: {
    859                 const error_token = if_full.error_token orelse break :no_switch_on_err;
    860                 const full_switch = tree.fullSwitch(if_full.ast.else_expr) orelse break :no_switch_on_err;
    861                 if (full_switch.label_token != null) break :no_switch_on_err;
    862                 if (node_tags[full_switch.ast.condition] != .identifier) break :no_switch_on_err;
    863                 if (!mem.eql(u8, tree.tokenSlice(error_token), tree.tokenSlice(main_tokens[full_switch.ast.condition]))) break :no_switch_on_err;
    864                 return switchExprErrUnion(gz, scope, ri.br(), node, .@"if");
    865             }
    866             return ifExpr(gz, scope, ri.br(), node, if_full);
    867         },
    868 
    869         .while_simple,
    870         .while_cont,
    871         .@"while",
    872         => return whileExpr(gz, scope, ri.br(), node, tree.fullWhile(node).?, false),
    873 
    874         .for_simple, .@"for" => return forExpr(gz, scope, ri.br(), node, tree.fullFor(node).?, false),
    875 
    876         .slice_open => {
    877             const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
    878 
    879             const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
    880             const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs);
    881             try emitDbgStmt(gz, cursor);
    882             const result = try gz.addPlNode(.slice_start, node, Zir.Inst.SliceStart{
    883                 .lhs = lhs,
    884                 .start = start,
    885             });
    886             return rvalue(gz, ri, result, node);
    887         },
    888         .slice => {
    889             const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);
    890             const lhs_node = node_datas[node].lhs;
    891             const lhs_tag = node_tags[lhs_node];
    892             const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel;
    893             const lhs_is_open_slice = lhs_tag == .slice_open or
    894                 (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0);
    895             if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) {
    896                 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs);
    897 
    898                 const start = if (lhs_is_slice_sentinel) start: {
    899                     const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel);
    900                     break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start);
    901                 } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs);
    902 
    903                 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
    904                 const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
    905                 try emitDbgStmt(gz, cursor);
    906                 const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{
    907                     .lhs = lhs,
    908                     .start = start,
    909                     .len = len,
    910                     .start_src_node_offset = gz.nodeIndexToRelative(lhs_node),
    911                     .sentinel = .none,
    912                 });
    913                 return rvalue(gz, ri, result, node);
    914             }
    915             const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
    916 
    917             const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
    918             const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
    919             const end = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end);
    920             try emitDbgStmt(gz, cursor);
    921             const result = try gz.addPlNode(.slice_end, node, Zir.Inst.SliceEnd{
    922                 .lhs = lhs,
    923                 .start = start,
    924                 .end = end,
    925             });
    926             return rvalue(gz, ri, result, node);
    927         },
    928         .slice_sentinel => {
    929             const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
    930             const lhs_node = node_datas[node].lhs;
    931             const lhs_tag = node_tags[lhs_node];
    932             const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel;
    933             const lhs_is_open_slice = lhs_tag == .slice_open or
    934                 (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0);
    935             if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) {
    936                 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs);
    937 
    938                 const start = if (lhs_is_slice_sentinel) start: {
    939                     const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel);
    940                     break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start);
    941                 } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs);
    942 
    943                 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
    944                 const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
    945                 const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);
    946                 try emitDbgStmt(gz, cursor);
    947                 const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{
    948                     .lhs = lhs,
    949                     .start = start,
    950                     .len = len,
    951                     .start_src_node_offset = gz.nodeIndexToRelative(lhs_node),
    952                     .sentinel = sentinel,
    953                 });
    954                 return rvalue(gz, ri, result, node);
    955             }
    956             const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
    957 
    958             const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
    959             const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
    960             const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
    961             const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);
    962             try emitDbgStmt(gz, cursor);
    963             const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{
    964                 .lhs = lhs,
    965                 .start = start,
    966                 .end = end,
    967                 .sentinel = sentinel,
    968             });
    969             return rvalue(gz, ri, result, node);
    970         },
    971 
    972         .deref => {
    973             const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs);
    974             _ = try gz.addUnNode(.validate_deref, lhs, node);
    975             switch (ri.rl) {
    976                 .ref, .ref_coerced_ty => return lhs,
    977                 else => {
    978                     const result = try gz.addUnNode(.load, lhs, node);
    979                     return rvalue(gz, ri, result, node);
    980                 },
    981             }
    982         },
    983         .address_of => {
    984             const operand_rl: ResultInfo.Loc = if (try ri.rl.resultType(gz, node)) |res_ty_inst| rl: {
    985                 _ = try gz.addUnTok(.validate_ref_ty, res_ty_inst, tree.firstToken(node));
    986                 break :rl .{ .ref_coerced_ty = res_ty_inst };
    987             } else .ref;
    988             const result = try expr(gz, scope, .{ .rl = operand_rl }, node_datas[node].lhs);
    989             return rvalue(gz, ri, result, node);
    990         },
    991         .optional_type => {
    992             const operand = try typeExpr(gz, scope, node_datas[node].lhs);
    993             const result = try gz.addUnNode(.optional_type, operand, node);
    994             return rvalue(gz, ri, result, node);
    995         },
    996         .unwrap_optional => switch (ri.rl) {
    997             .ref, .ref_coerced_ty => {
    998                 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
    999 
   1000                 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   1001                 try emitDbgStmt(gz, cursor);
   1002 
   1003                 return gz.addUnNode(.optional_payload_safe_ptr, lhs, node);
   1004             },
   1005             else => {
   1006                 const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs);
   1007 
   1008                 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   1009                 try emitDbgStmt(gz, cursor);
   1010 
   1011                 return rvalue(gz, ri, try gz.addUnNode(.optional_payload_safe, lhs, node), node);
   1012             },
   1013         },
   1014         .block_two, .block_two_semicolon => {
   1015             const statements = [2]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
   1016             if (node_datas[node].lhs == 0) {
   1017                 return blockExpr(gz, scope, ri, node, statements[0..0], .normal);
   1018             } else if (node_datas[node].rhs == 0) {
   1019                 return blockExpr(gz, scope, ri, node, statements[0..1], .normal);
   1020             } else {
   1021                 return blockExpr(gz, scope, ri, node, statements[0..2], .normal);
   1022             }
   1023         },
   1024         .block, .block_semicolon => {
   1025             const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
   1026             return blockExpr(gz, scope, ri, node, statements, .normal);
   1027         },
   1028         .enum_literal => if (try ri.rl.resultType(gz, node)) |res_ty| {
   1029             const str_index = try astgen.identAsString(main_tokens[node]);
   1030             const res = try gz.addPlNode(.decl_literal, node, Zir.Inst.Field{
   1031                 .lhs = res_ty,
   1032                 .field_name_start = str_index,
   1033             });
   1034             switch (ri.rl) {
   1035                 .discard, .none, .ref => unreachable, // no result type
   1036                 .ty, .coerced_ty => return res, // `decl_literal` does the coercion for us
   1037                 .ref_coerced_ty, .ptr, .inferred_ptr, .destructure => return rvalue(gz, ri, res, node),
   1038             }
   1039         } else return simpleStrTok(gz, ri, main_tokens[node], node, .enum_literal),
   1040         .error_value => return simpleStrTok(gz, ri, node_datas[node].rhs, node, .error_value),
   1041         // TODO restore this when implementing https://github.com/ziglang/zig/issues/6025
   1042         // .anyframe_literal => return rvalue(gz, ri, .anyframe_type, node),
   1043         .anyframe_literal => {
   1044             const result = try gz.addUnNode(.anyframe_type, .void_type, node);
   1045             return rvalue(gz, ri, result, node);
   1046         },
   1047         .anyframe_type => {
   1048             const return_type = try typeExpr(gz, scope, node_datas[node].rhs);
   1049             const result = try gz.addUnNode(.anyframe_type, return_type, node);
   1050             return rvalue(gz, ri, result, node);
   1051         },
   1052         .@"catch" => {
   1053             const catch_token = main_tokens[node];
   1054             const payload_token: ?Ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe)
   1055                 catch_token + 2
   1056             else
   1057                 null;
   1058             no_switch_on_err: {
   1059                 const capture_token = payload_token orelse break :no_switch_on_err;
   1060                 const full_switch = tree.fullSwitch(node_datas[node].rhs) orelse break :no_switch_on_err;
   1061                 if (full_switch.label_token != null) break :no_switch_on_err;
   1062                 if (node_tags[full_switch.ast.condition] != .identifier) break :no_switch_on_err;
   1063                 if (!mem.eql(u8, tree.tokenSlice(capture_token), tree.tokenSlice(main_tokens[full_switch.ast.condition]))) break :no_switch_on_err;
   1064                 return switchExprErrUnion(gz, scope, ri.br(), node, .@"catch");
   1065             }
   1066             switch (ri.rl) {
   1067                 .ref, .ref_coerced_ty => return orelseCatchExpr(
   1068                     gz,
   1069                     scope,
   1070                     ri,
   1071                     node,
   1072                     node_datas[node].lhs,
   1073                     .is_non_err_ptr,
   1074                     .err_union_payload_unsafe_ptr,
   1075                     .err_union_code_ptr,
   1076                     node_datas[node].rhs,
   1077                     payload_token,
   1078                 ),
   1079                 else => return orelseCatchExpr(
   1080                     gz,
   1081                     scope,
   1082                     ri,
   1083                     node,
   1084                     node_datas[node].lhs,
   1085                     .is_non_err,
   1086                     .err_union_payload_unsafe,
   1087                     .err_union_code,
   1088                     node_datas[node].rhs,
   1089                     payload_token,
   1090                 ),
   1091             }
   1092         },
   1093         .@"orelse" => switch (ri.rl) {
   1094             .ref, .ref_coerced_ty => return orelseCatchExpr(
   1095                 gz,
   1096                 scope,
   1097                 ri,
   1098                 node,
   1099                 node_datas[node].lhs,
   1100                 .is_non_null_ptr,
   1101                 .optional_payload_unsafe_ptr,
   1102                 undefined,
   1103                 node_datas[node].rhs,
   1104                 null,
   1105             ),
   1106             else => return orelseCatchExpr(
   1107                 gz,
   1108                 scope,
   1109                 ri,
   1110                 node,
   1111                 node_datas[node].lhs,
   1112                 .is_non_null,
   1113                 .optional_payload_unsafe,
   1114                 undefined,
   1115                 node_datas[node].rhs,
   1116                 null,
   1117             ),
   1118         },
   1119 
   1120         .ptr_type_aligned,
   1121         .ptr_type_sentinel,
   1122         .ptr_type,
   1123         .ptr_type_bit_range,
   1124         => return ptrType(gz, scope, ri, node, tree.fullPtrType(node).?),
   1125 
   1126         .container_decl,
   1127         .container_decl_trailing,
   1128         .container_decl_arg,
   1129         .container_decl_arg_trailing,
   1130         .container_decl_two,
   1131         .container_decl_two_trailing,
   1132         .tagged_union,
   1133         .tagged_union_trailing,
   1134         .tagged_union_enum_tag,
   1135         .tagged_union_enum_tag_trailing,
   1136         .tagged_union_two,
   1137         .tagged_union_two_trailing,
   1138         => {
   1139             var buf: [2]Ast.Node.Index = undefined;
   1140             return containerDecl(gz, scope, ri, node, tree.fullContainerDecl(&buf, node).?);
   1141         },
   1142 
   1143         .@"break" => return breakExpr(gz, scope, node),
   1144         .@"continue" => return continueExpr(gz, scope, node),
   1145         .grouped_expression => return expr(gz, scope, ri, node_datas[node].lhs),
   1146         .array_type => return arrayType(gz, scope, ri, node),
   1147         .array_type_sentinel => return arrayTypeSentinel(gz, scope, ri, node),
   1148         .char_literal => return charLiteral(gz, ri, node),
   1149         .error_set_decl => return errorSetDecl(gz, ri, node),
   1150         .array_access => return arrayAccess(gz, scope, ri, node),
   1151         .@"comptime" => return comptimeExprAst(gz, scope, ri, node),
   1152         .@"switch", .switch_comma => return switchExpr(gz, scope, ri.br(), node, tree.fullSwitch(node).?),
   1153 
   1154         .@"nosuspend" => return nosuspendExpr(gz, scope, ri, node),
   1155         .@"suspend" => return suspendExpr(gz, scope, node),
   1156         .@"await" => return awaitExpr(gz, scope, ri, node),
   1157         .@"resume" => return resumeExpr(gz, scope, ri, node),
   1158 
   1159         .@"try" => return tryExpr(gz, scope, ri, node, node_datas[node].lhs),
   1160 
   1161         .array_init_one,
   1162         .array_init_one_comma,
   1163         .array_init_dot_two,
   1164         .array_init_dot_two_comma,
   1165         .array_init_dot,
   1166         .array_init_dot_comma,
   1167         .array_init,
   1168         .array_init_comma,
   1169         => {
   1170             var buf: [2]Ast.Node.Index = undefined;
   1171             return arrayInitExpr(gz, scope, ri, node, tree.fullArrayInit(&buf, node).?);
   1172         },
   1173 
   1174         .struct_init_one,
   1175         .struct_init_one_comma,
   1176         .struct_init_dot_two,
   1177         .struct_init_dot_two_comma,
   1178         .struct_init_dot,
   1179         .struct_init_dot_comma,
   1180         .struct_init,
   1181         .struct_init_comma,
   1182         => {
   1183             var buf: [2]Ast.Node.Index = undefined;
   1184             return structInitExpr(gz, scope, ri, node, tree.fullStructInit(&buf, node).?);
   1185         },
   1186 
   1187         .fn_proto_simple,
   1188         .fn_proto_multi,
   1189         .fn_proto_one,
   1190         .fn_proto,
   1191         => {
   1192             var buf: [1]Ast.Node.Index = undefined;
   1193             return fnProtoExpr(gz, scope, ri, node, tree.fullFnProto(&buf, node).?);
   1194         },
   1195     }
   1196 }
   1197 
   1198 fn nosuspendExpr(
   1199     gz: *GenZir,
   1200     scope: *Scope,
   1201     ri: ResultInfo,
   1202     node: Ast.Node.Index,
   1203 ) InnerError!Zir.Inst.Ref {
   1204     const astgen = gz.astgen;
   1205     const tree = astgen.tree;
   1206     const node_datas = tree.nodes.items(.data);
   1207     const body_node = node_datas[node].lhs;
   1208     assert(body_node != 0);
   1209     if (gz.nosuspend_node != 0) {
   1210         try astgen.appendErrorNodeNotes(node, "redundant nosuspend block", .{}, &[_]u32{
   1211             try astgen.errNoteNode(gz.nosuspend_node, "other nosuspend block here", .{}),
   1212         });
   1213     }
   1214     gz.nosuspend_node = node;
   1215     defer gz.nosuspend_node = 0;
   1216     return expr(gz, scope, ri, body_node);
   1217 }
   1218 
   1219 fn suspendExpr(
   1220     gz: *GenZir,
   1221     scope: *Scope,
   1222     node: Ast.Node.Index,
   1223 ) InnerError!Zir.Inst.Ref {
   1224     const astgen = gz.astgen;
   1225     const gpa = astgen.gpa;
   1226     const tree = astgen.tree;
   1227     const node_datas = tree.nodes.items(.data);
   1228     const body_node = node_datas[node].lhs;
   1229 
   1230     if (gz.nosuspend_node != 0) {
   1231         return astgen.failNodeNotes(node, "suspend inside nosuspend block", .{}, &[_]u32{
   1232             try astgen.errNoteNode(gz.nosuspend_node, "nosuspend block here", .{}),
   1233         });
   1234     }
   1235     if (gz.suspend_node != 0) {
   1236         return astgen.failNodeNotes(node, "cannot suspend inside suspend block", .{}, &[_]u32{
   1237             try astgen.errNoteNode(gz.suspend_node, "other suspend block here", .{}),
   1238         });
   1239     }
   1240     assert(body_node != 0);
   1241 
   1242     const suspend_inst = try gz.makeBlockInst(.suspend_block, node);
   1243     try gz.instructions.append(gpa, suspend_inst);
   1244 
   1245     var suspend_scope = gz.makeSubBlock(scope);
   1246     suspend_scope.suspend_node = node;
   1247     defer suspend_scope.unstack();
   1248 
   1249     const body_result = try fullBodyExpr(&suspend_scope, &suspend_scope.base, .{ .rl = .none }, body_node, .normal);
   1250     if (!gz.refIsNoReturn(body_result)) {
   1251         _ = try suspend_scope.addBreak(.break_inline, suspend_inst, .void_value);
   1252     }
   1253     try suspend_scope.setBlockBody(suspend_inst);
   1254 
   1255     return suspend_inst.toRef();
   1256 }
   1257 
   1258 fn awaitExpr(
   1259     gz: *GenZir,
   1260     scope: *Scope,
   1261     ri: ResultInfo,
   1262     node: Ast.Node.Index,
   1263 ) InnerError!Zir.Inst.Ref {
   1264     const astgen = gz.astgen;
   1265     const tree = astgen.tree;
   1266     const node_datas = tree.nodes.items(.data);
   1267     const rhs_node = node_datas[node].lhs;
   1268 
   1269     if (gz.suspend_node != 0) {
   1270         return astgen.failNodeNotes(node, "cannot await inside suspend block", .{}, &[_]u32{
   1271             try astgen.errNoteNode(gz.suspend_node, "suspend block here", .{}),
   1272         });
   1273     }
   1274     const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node);
   1275     const result = if (gz.nosuspend_node != 0)
   1276         try gz.addExtendedPayload(.await_nosuspend, Zir.Inst.UnNode{
   1277             .node = gz.nodeIndexToRelative(node),
   1278             .operand = operand,
   1279         })
   1280     else
   1281         try gz.addUnNode(.@"await", operand, node);
   1282 
   1283     return rvalue(gz, ri, result, node);
   1284 }
   1285 
   1286 fn resumeExpr(
   1287     gz: *GenZir,
   1288     scope: *Scope,
   1289     ri: ResultInfo,
   1290     node: Ast.Node.Index,
   1291 ) InnerError!Zir.Inst.Ref {
   1292     const astgen = gz.astgen;
   1293     const tree = astgen.tree;
   1294     const node_datas = tree.nodes.items(.data);
   1295     const rhs_node = node_datas[node].lhs;
   1296     const operand = try expr(gz, scope, .{ .rl = .ref }, rhs_node);
   1297     const result = try gz.addUnNode(.@"resume", operand, node);
   1298     return rvalue(gz, ri, result, node);
   1299 }
   1300 
   1301 fn fnProtoExpr(
   1302     gz: *GenZir,
   1303     scope: *Scope,
   1304     ri: ResultInfo,
   1305     node: Ast.Node.Index,
   1306     fn_proto: Ast.full.FnProto,
   1307 ) InnerError!Zir.Inst.Ref {
   1308     const astgen = gz.astgen;
   1309     const tree = astgen.tree;
   1310     const token_tags = tree.tokens.items(.tag);
   1311 
   1312     if (fn_proto.name_token) |some| {
   1313         return astgen.failTok(some, "function type cannot have a name", .{});
   1314     }
   1315 
   1316     const is_extern = blk: {
   1317         const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false;
   1318         break :blk token_tags[maybe_extern_token] == .keyword_extern;
   1319     };
   1320     assert(!is_extern);
   1321 
   1322     var block_scope = gz.makeSubBlock(scope);
   1323     defer block_scope.unstack();
   1324 
   1325     const block_inst = try gz.makeBlockInst(.block_inline, node);
   1326 
   1327     var noalias_bits: u32 = 0;
   1328     const is_var_args = is_var_args: {
   1329         var param_type_i: usize = 0;
   1330         var it = fn_proto.iterate(tree);
   1331         while (it.next()) |param| : (param_type_i += 1) {
   1332             const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) {
   1333                 .keyword_noalias => is_comptime: {
   1334                     noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse
   1335                         return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
   1336                     break :is_comptime false;
   1337                 },
   1338                 .keyword_comptime => true,
   1339                 else => false,
   1340             } else false;
   1341 
   1342             const is_anytype = if (param.anytype_ellipsis3) |token| blk: {
   1343                 switch (token_tags[token]) {
   1344                     .keyword_anytype => break :blk true,
   1345                     .ellipsis3 => break :is_var_args true,
   1346                     else => unreachable,
   1347                 }
   1348             } else false;
   1349 
   1350             const param_name = if (param.name_token) |name_token| blk: {
   1351                 if (mem.eql(u8, "_", tree.tokenSlice(name_token)))
   1352                     break :blk .empty;
   1353 
   1354                 break :blk try astgen.identAsString(name_token);
   1355             } else .empty;
   1356 
   1357             if (is_anytype) {
   1358                 const name_token = param.name_token orelse param.anytype_ellipsis3.?;
   1359 
   1360                 const tag: Zir.Inst.Tag = if (is_comptime)
   1361                     .param_anytype_comptime
   1362                 else
   1363                     .param_anytype;
   1364                 _ = try block_scope.addStrTok(tag, param_name, name_token);
   1365             } else {
   1366                 const param_type_node = param.type_expr;
   1367                 assert(param_type_node != 0);
   1368                 var param_gz = block_scope.makeSubBlock(scope);
   1369                 defer param_gz.unstack();
   1370                 const param_type = try fullBodyExpr(&param_gz, scope, coerced_type_ri, param_type_node, .normal);
   1371                 const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1);
   1372                 _ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node);
   1373                 const main_tokens = tree.nodes.items(.main_token);
   1374                 const name_token = param.name_token orelse main_tokens[param_type_node];
   1375                 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param;
   1376                 const param_inst = try block_scope.addParam(&param_gz, tag, name_token, param_name, param.first_doc_comment);
   1377                 assert(param_inst_expected == param_inst);
   1378             }
   1379         }
   1380         break :is_var_args false;
   1381     };
   1382 
   1383     if (fn_proto.ast.align_expr != 0) {
   1384         return astgen.failNode(fn_proto.ast.align_expr, "function type cannot have an alignment", .{});
   1385     }
   1386 
   1387     if (fn_proto.ast.addrspace_expr != 0) {
   1388         return astgen.failNode(fn_proto.ast.addrspace_expr, "function type cannot have an addrspace", .{});
   1389     }
   1390 
   1391     if (fn_proto.ast.section_expr != 0) {
   1392         return astgen.failNode(fn_proto.ast.section_expr, "function type cannot have a linksection", .{});
   1393     }
   1394 
   1395     const cc: Zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0)
   1396         try expr(
   1397             &block_scope,
   1398             scope,
   1399             .{ .rl = .{ .coerced_ty = try block_scope.addBuiltinValue(fn_proto.ast.callconv_expr, .calling_convention) } },
   1400             fn_proto.ast.callconv_expr,
   1401         )
   1402     else
   1403         Zir.Inst.Ref.none;
   1404 
   1405     const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
   1406     const is_inferred_error = token_tags[maybe_bang] == .bang;
   1407     if (is_inferred_error) {
   1408         return astgen.failTok(maybe_bang, "function type cannot have an inferred error set", .{});
   1409     }
   1410     const ret_ty = try expr(&block_scope, scope, coerced_type_ri, fn_proto.ast.return_type);
   1411 
   1412     const result = try block_scope.addFunc(.{
   1413         .src_node = fn_proto.ast.proto_node,
   1414 
   1415         .cc_ref = cc,
   1416         .cc_gz = null,
   1417         .align_ref = .none,
   1418         .align_gz = null,
   1419         .ret_ref = ret_ty,
   1420         .ret_gz = null,
   1421         .section_ref = .none,
   1422         .section_gz = null,
   1423         .addrspace_ref = .none,
   1424         .addrspace_gz = null,
   1425 
   1426         .param_block = block_inst,
   1427         .body_gz = null,
   1428         .lib_name = .empty,
   1429         .is_var_args = is_var_args,
   1430         .is_inferred_error = false,
   1431         .is_test = false,
   1432         .is_extern = false,
   1433         .is_noinline = false,
   1434         .noalias_bits = noalias_bits,
   1435 
   1436         .proto_hash = undefined, // ignored for `body_gz == null`
   1437     });
   1438 
   1439     _ = try block_scope.addBreak(.break_inline, block_inst, result);
   1440     try block_scope.setBlockBody(block_inst);
   1441     try gz.instructions.append(astgen.gpa, block_inst);
   1442 
   1443     return rvalue(gz, ri, block_inst.toRef(), fn_proto.ast.proto_node);
   1444 }
   1445 
   1446 fn arrayInitExpr(
   1447     gz: *GenZir,
   1448     scope: *Scope,
   1449     ri: ResultInfo,
   1450     node: Ast.Node.Index,
   1451     array_init: Ast.full.ArrayInit,
   1452 ) InnerError!Zir.Inst.Ref {
   1453     const astgen = gz.astgen;
   1454     const tree = astgen.tree;
   1455     const node_tags = tree.nodes.items(.tag);
   1456     const main_tokens = tree.nodes.items(.main_token);
   1457 
   1458     assert(array_init.ast.elements.len != 0); // Otherwise it would be struct init.
   1459 
   1460     const array_ty: Zir.Inst.Ref, const elem_ty: Zir.Inst.Ref = inst: {
   1461         if (array_init.ast.type_expr == 0) break :inst .{ .none, .none };
   1462 
   1463         infer: {
   1464             const array_type: Ast.full.ArrayType = tree.fullArrayType(array_init.ast.type_expr) orelse break :infer;
   1465             // This intentionally does not support `@"_"` syntax.
   1466             if (node_tags[array_type.ast.elem_count] == .identifier and
   1467                 mem.eql(u8, tree.tokenSlice(main_tokens[array_type.ast.elem_count]), "_"))
   1468             {
   1469                 const len_inst = try gz.addInt(array_init.ast.elements.len);
   1470                 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type);
   1471                 if (array_type.ast.sentinel == 0) {
   1472                     const array_type_inst = try gz.addPlNode(.array_type, array_init.ast.type_expr, Zir.Inst.Bin{
   1473                         .lhs = len_inst,
   1474                         .rhs = elem_type,
   1475                     });
   1476                     break :inst .{ array_type_inst, elem_type };
   1477                 } else {
   1478                     const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, array_type.ast.sentinel);
   1479                     const array_type_inst = try gz.addPlNode(
   1480                         .array_type_sentinel,
   1481                         array_init.ast.type_expr,
   1482                         Zir.Inst.ArrayTypeSentinel{
   1483                             .len = len_inst,
   1484                             .elem_type = elem_type,
   1485                             .sentinel = sentinel,
   1486                         },
   1487                     );
   1488                     break :inst .{ array_type_inst, elem_type };
   1489                 }
   1490             }
   1491         }
   1492         const array_type_inst = try typeExpr(gz, scope, array_init.ast.type_expr);
   1493         _ = try gz.addPlNode(.validate_array_init_ty, node, Zir.Inst.ArrayInit{
   1494             .ty = array_type_inst,
   1495             .init_count = @intCast(array_init.ast.elements.len),
   1496         });
   1497         break :inst .{ array_type_inst, .none };
   1498     };
   1499 
   1500     if (array_ty != .none) {
   1501         // Typed inits do not use RLS for language simplicity.
   1502         switch (ri.rl) {
   1503             .discard => {
   1504                 if (elem_ty != .none) {
   1505                     const elem_ri: ResultInfo = .{ .rl = .{ .ty = elem_ty } };
   1506                     for (array_init.ast.elements) |elem_init| {
   1507                         _ = try expr(gz, scope, elem_ri, elem_init);
   1508                     }
   1509                 } else {
   1510                     for (array_init.ast.elements, 0..) |elem_init, i| {
   1511                         const this_elem_ty = try gz.add(.{
   1512                             .tag = .array_init_elem_type,
   1513                             .data = .{ .bin = .{
   1514                                 .lhs = array_ty,
   1515                                 .rhs = @enumFromInt(i),
   1516                             } },
   1517                         });
   1518                         _ = try expr(gz, scope, .{ .rl = .{ .ty = this_elem_ty } }, elem_init);
   1519                     }
   1520                 }
   1521                 return .void_value;
   1522             },
   1523             .ref => return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, true),
   1524             else => {
   1525                 const array_inst = try arrayInitExprTyped(gz, scope, node, array_init.ast.elements, array_ty, elem_ty, false);
   1526                 return rvalue(gz, ri, array_inst, node);
   1527             },
   1528         }
   1529     }
   1530 
   1531     switch (ri.rl) {
   1532         .none => return arrayInitExprAnon(gz, scope, node, array_init.ast.elements),
   1533         .discard => {
   1534             for (array_init.ast.elements) |elem_init| {
   1535                 _ = try expr(gz, scope, .{ .rl = .discard }, elem_init);
   1536             }
   1537             return Zir.Inst.Ref.void_value;
   1538         },
   1539         .ref => {
   1540             const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements);
   1541             return gz.addUnTok(.ref, result, tree.firstToken(node));
   1542         },
   1543         .ref_coerced_ty => |ptr_ty_inst| {
   1544             const dest_arr_ty_inst = try gz.addPlNode(.validate_array_init_ref_ty, node, Zir.Inst.ArrayInitRefTy{
   1545                 .ptr_ty = ptr_ty_inst,
   1546                 .elem_count = @intCast(array_init.ast.elements.len),
   1547             });
   1548             return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, dest_arr_ty_inst, .none, true);
   1549         },
   1550         .ty, .coerced_ty => |result_ty_inst| {
   1551             _ = try gz.addPlNode(.validate_array_init_result_ty, node, Zir.Inst.ArrayInit{
   1552                 .ty = result_ty_inst,
   1553                 .init_count = @intCast(array_init.ast.elements.len),
   1554             });
   1555             return arrayInitExprTyped(gz, scope, node, array_init.ast.elements, result_ty_inst, .none, false);
   1556         },
   1557         .ptr => |ptr| {
   1558             try arrayInitExprPtr(gz, scope, node, array_init.ast.elements, ptr.inst);
   1559             return .void_value;
   1560         },
   1561         .inferred_ptr => {
   1562             // We can't get elem pointers of an untyped inferred alloc, so must perform a
   1563             // standard anonymous initialization followed by an rvalue store.
   1564             // See corresponding logic in structInitExpr.
   1565             const result = try arrayInitExprAnon(gz, scope, node, array_init.ast.elements);
   1566             return rvalue(gz, ri, result, node);
   1567         },
   1568         .destructure => |destructure| {
   1569             // Untyped init - destructure directly into result pointers
   1570             if (array_init.ast.elements.len != destructure.components.len) {
   1571                 return astgen.failNodeNotes(node, "expected {} elements for destructure, found {}", .{
   1572                     destructure.components.len,
   1573                     array_init.ast.elements.len,
   1574                 }, &.{
   1575                     try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}),
   1576                 });
   1577             }
   1578             for (array_init.ast.elements, destructure.components) |elem_init, ds_comp| {
   1579                 const elem_ri: ResultInfo = .{ .rl = switch (ds_comp) {
   1580                     .typed_ptr => |ptr_rl| .{ .ptr = ptr_rl },
   1581                     .inferred_ptr => |ptr_inst| .{ .inferred_ptr = ptr_inst },
   1582                     .discard => .discard,
   1583                 } };
   1584                 _ = try expr(gz, scope, elem_ri, elem_init);
   1585             }
   1586             return .void_value;
   1587         },
   1588     }
   1589 }
   1590 
   1591 /// An array initialization expression using an `array_init_anon` instruction.
   1592 fn arrayInitExprAnon(
   1593     gz: *GenZir,
   1594     scope: *Scope,
   1595     node: Ast.Node.Index,
   1596     elements: []const Ast.Node.Index,
   1597 ) InnerError!Zir.Inst.Ref {
   1598     const astgen = gz.astgen;
   1599 
   1600     const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{
   1601         .operands_len = @intCast(elements.len),
   1602     });
   1603     var extra_index = try reserveExtra(astgen, elements.len);
   1604 
   1605     for (elements) |elem_init| {
   1606         const elem_ref = try expr(gz, scope, .{ .rl = .none }, elem_init);
   1607         astgen.extra.items[extra_index] = @intFromEnum(elem_ref);
   1608         extra_index += 1;
   1609     }
   1610     return try gz.addPlNodePayloadIndex(.array_init_anon, node, payload_index);
   1611 }
   1612 
   1613 /// An array initialization expression using an `array_init` or `array_init_ref` instruction.
   1614 fn arrayInitExprTyped(
   1615     gz: *GenZir,
   1616     scope: *Scope,
   1617     node: Ast.Node.Index,
   1618     elements: []const Ast.Node.Index,
   1619     ty_inst: Zir.Inst.Ref,
   1620     maybe_elem_ty_inst: Zir.Inst.Ref,
   1621     is_ref: bool,
   1622 ) InnerError!Zir.Inst.Ref {
   1623     const astgen = gz.astgen;
   1624 
   1625     const len = elements.len + 1; // +1 for type
   1626     const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{
   1627         .operands_len = @intCast(len),
   1628     });
   1629     var extra_index = try reserveExtra(astgen, len);
   1630     astgen.extra.items[extra_index] = @intFromEnum(ty_inst);
   1631     extra_index += 1;
   1632 
   1633     if (maybe_elem_ty_inst != .none) {
   1634         const elem_ri: ResultInfo = .{ .rl = .{ .coerced_ty = maybe_elem_ty_inst } };
   1635         for (elements) |elem_init| {
   1636             const elem_inst = try expr(gz, scope, elem_ri, elem_init);
   1637             astgen.extra.items[extra_index] = @intFromEnum(elem_inst);
   1638             extra_index += 1;
   1639         }
   1640     } else {
   1641         for (elements, 0..) |elem_init, i| {
   1642             const ri: ResultInfo = .{ .rl = .{ .coerced_ty = try gz.add(.{
   1643                 .tag = .array_init_elem_type,
   1644                 .data = .{ .bin = .{
   1645                     .lhs = ty_inst,
   1646                     .rhs = @enumFromInt(i),
   1647                 } },
   1648             }) } };
   1649 
   1650             const elem_inst = try expr(gz, scope, ri, elem_init);
   1651             astgen.extra.items[extra_index] = @intFromEnum(elem_inst);
   1652             extra_index += 1;
   1653         }
   1654     }
   1655 
   1656     const tag: Zir.Inst.Tag = if (is_ref) .array_init_ref else .array_init;
   1657     return try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1658 }
   1659 
   1660 /// An array initialization expression using element pointers.
   1661 fn arrayInitExprPtr(
   1662     gz: *GenZir,
   1663     scope: *Scope,
   1664     node: Ast.Node.Index,
   1665     elements: []const Ast.Node.Index,
   1666     ptr_inst: Zir.Inst.Ref,
   1667 ) InnerError!void {
   1668     const astgen = gz.astgen;
   1669 
   1670     const array_ptr_inst = try gz.addUnNode(.opt_eu_base_ptr_init, ptr_inst, node);
   1671 
   1672     const payload_index = try addExtra(astgen, Zir.Inst.Block{
   1673         .body_len = @intCast(elements.len),
   1674     });
   1675     var extra_index = try reserveExtra(astgen, elements.len);
   1676 
   1677     for (elements, 0..) |elem_init, i| {
   1678         const elem_ptr_inst = try gz.addPlNode(.array_init_elem_ptr, elem_init, Zir.Inst.ElemPtrImm{
   1679             .ptr = array_ptr_inst,
   1680             .index = @intCast(i),
   1681         });
   1682         astgen.extra.items[extra_index] = @intFromEnum(elem_ptr_inst.toIndex().?);
   1683         extra_index += 1;
   1684         _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = elem_ptr_inst } } }, elem_init);
   1685     }
   1686 
   1687     _ = try gz.addPlNodePayloadIndex(.validate_ptr_array_init, node, payload_index);
   1688 }
   1689 
   1690 fn structInitExpr(
   1691     gz: *GenZir,
   1692     scope: *Scope,
   1693     ri: ResultInfo,
   1694     node: Ast.Node.Index,
   1695     struct_init: Ast.full.StructInit,
   1696 ) InnerError!Zir.Inst.Ref {
   1697     const astgen = gz.astgen;
   1698     const tree = astgen.tree;
   1699 
   1700     if (struct_init.ast.type_expr == 0) {
   1701         if (struct_init.ast.fields.len == 0) {
   1702             // Anonymous init with no fields.
   1703             switch (ri.rl) {
   1704                 .discard => return .void_value,
   1705                 .ref_coerced_ty => |ptr_ty_inst| return gz.addUnNode(.struct_init_empty_ref_result, ptr_ty_inst, node),
   1706                 .ty, .coerced_ty => |ty_inst| return gz.addUnNode(.struct_init_empty_result, ty_inst, node),
   1707                 .ptr => {
   1708                     // TODO: should we modify this to use RLS for the field stores here?
   1709                     const ty_inst = (try ri.rl.resultType(gz, node)).?;
   1710                     const val = try gz.addUnNode(.struct_init_empty_result, ty_inst, node);
   1711                     return rvalue(gz, ri, val, node);
   1712                 },
   1713                 .none, .ref, .inferred_ptr => {
   1714                     return rvalue(gz, ri, .empty_struct, node);
   1715                 },
   1716                 .destructure => |destructure| {
   1717                     return astgen.failNodeNotes(node, "empty initializer cannot be destructured", .{}, &.{
   1718                         try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}),
   1719                     });
   1720                 },
   1721             }
   1722         }
   1723     } else array: {
   1724         const node_tags = tree.nodes.items(.tag);
   1725         const main_tokens = tree.nodes.items(.main_token);
   1726         const array_type: Ast.full.ArrayType = tree.fullArrayType(struct_init.ast.type_expr) orelse {
   1727             if (struct_init.ast.fields.len == 0) {
   1728                 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1729                 const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
   1730                 return rvalue(gz, ri, result, node);
   1731             }
   1732             break :array;
   1733         };
   1734         const is_inferred_array_len = node_tags[array_type.ast.elem_count] == .identifier and
   1735             // This intentionally does not support `@"_"` syntax.
   1736             mem.eql(u8, tree.tokenSlice(main_tokens[array_type.ast.elem_count]), "_");
   1737         if (struct_init.ast.fields.len == 0) {
   1738             if (is_inferred_array_len) {
   1739                 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type);
   1740                 const array_type_inst = if (array_type.ast.sentinel == 0) blk: {
   1741                     break :blk try gz.addPlNode(.array_type, struct_init.ast.type_expr, Zir.Inst.Bin{
   1742                         .lhs = .zero_usize,
   1743                         .rhs = elem_type,
   1744                     });
   1745                 } else blk: {
   1746                     const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, array_type.ast.sentinel);
   1747                     break :blk try gz.addPlNode(
   1748                         .array_type_sentinel,
   1749                         struct_init.ast.type_expr,
   1750                         Zir.Inst.ArrayTypeSentinel{
   1751                             .len = .zero_usize,
   1752                             .elem_type = elem_type,
   1753                             .sentinel = sentinel,
   1754                         },
   1755                     );
   1756                 };
   1757                 const result = try gz.addUnNode(.struct_init_empty, array_type_inst, node);
   1758                 return rvalue(gz, ri, result, node);
   1759             }
   1760             const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1761             const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
   1762             return rvalue(gz, ri, result, node);
   1763         } else {
   1764             return astgen.failNode(
   1765                 struct_init.ast.type_expr,
   1766                 "initializing array with struct syntax",
   1767                 .{},
   1768             );
   1769         }
   1770     }
   1771 
   1772     {
   1773         var sfba = std.heap.stackFallback(256, astgen.arena);
   1774         const sfba_allocator = sfba.get();
   1775 
   1776         var duplicate_names = std.AutoArrayHashMap(Zir.NullTerminatedString, ArrayListUnmanaged(Ast.TokenIndex)).init(sfba_allocator);
   1777         try duplicate_names.ensureTotalCapacity(@intCast(struct_init.ast.fields.len));
   1778 
   1779         // When there aren't errors, use this to avoid a second iteration.
   1780         var any_duplicate = false;
   1781 
   1782         for (struct_init.ast.fields) |field| {
   1783             const name_token = tree.firstToken(field) - 2;
   1784             const name_index = try astgen.identAsString(name_token);
   1785 
   1786             const gop = try duplicate_names.getOrPut(name_index);
   1787 
   1788             if (gop.found_existing) {
   1789                 try gop.value_ptr.append(sfba_allocator, name_token);
   1790                 any_duplicate = true;
   1791             } else {
   1792                 gop.value_ptr.* = .{};
   1793                 try gop.value_ptr.append(sfba_allocator, name_token);
   1794             }
   1795         }
   1796 
   1797         if (any_duplicate) {
   1798             var it = duplicate_names.iterator();
   1799 
   1800             while (it.next()) |entry| {
   1801                 const record = entry.value_ptr.*;
   1802                 if (record.items.len > 1) {
   1803                     var error_notes = std.ArrayList(u32).init(astgen.arena);
   1804 
   1805                     for (record.items[1..]) |duplicate| {
   1806                         try error_notes.append(try astgen.errNoteTok(duplicate, "duplicate name here", .{}));
   1807                     }
   1808 
   1809                     try error_notes.append(try astgen.errNoteNode(node, "struct declared here", .{}));
   1810 
   1811                     try astgen.appendErrorTokNotes(
   1812                         record.items[0],
   1813                         "duplicate struct field name",
   1814                         .{},
   1815                         error_notes.items,
   1816                     );
   1817                 }
   1818             }
   1819 
   1820             return error.AnalysisFail;
   1821         }
   1822     }
   1823 
   1824     if (struct_init.ast.type_expr != 0) {
   1825         // Typed inits do not use RLS for language simplicity.
   1826         const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1827         _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
   1828         switch (ri.rl) {
   1829             .ref => return structInitExprTyped(gz, scope, node, struct_init, ty_inst, true),
   1830             else => {
   1831                 const struct_inst = try structInitExprTyped(gz, scope, node, struct_init, ty_inst, false);
   1832                 return rvalue(gz, ri, struct_inst, node);
   1833             },
   1834         }
   1835     }
   1836 
   1837     switch (ri.rl) {
   1838         .none => return structInitExprAnon(gz, scope, node, struct_init),
   1839         .discard => {
   1840             // Even if discarding we must perform side-effects.
   1841             for (struct_init.ast.fields) |field_init| {
   1842                 _ = try expr(gz, scope, .{ .rl = .discard }, field_init);
   1843             }
   1844             return .void_value;
   1845         },
   1846         .ref => {
   1847             const result = try structInitExprAnon(gz, scope, node, struct_init);
   1848             return gz.addUnTok(.ref, result, tree.firstToken(node));
   1849         },
   1850         .ref_coerced_ty => |ptr_ty_inst| {
   1851             const result_ty_inst = try gz.addUnNode(.elem_type, ptr_ty_inst, node);
   1852             _ = try gz.addUnNode(.validate_struct_init_result_ty, result_ty_inst, node);
   1853             return structInitExprTyped(gz, scope, node, struct_init, result_ty_inst, true);
   1854         },
   1855         .ty, .coerced_ty => |result_ty_inst| {
   1856             _ = try gz.addUnNode(.validate_struct_init_result_ty, result_ty_inst, node);
   1857             return structInitExprTyped(gz, scope, node, struct_init, result_ty_inst, false);
   1858         },
   1859         .ptr => |ptr| {
   1860             try structInitExprPtr(gz, scope, node, struct_init, ptr.inst);
   1861             return .void_value;
   1862         },
   1863         .inferred_ptr => {
   1864             // We can't get field pointers of an untyped inferred alloc, so must perform a
   1865             // standard anonymous initialization followed by an rvalue store.
   1866             // See corresponding logic in arrayInitExpr.
   1867             const struct_inst = try structInitExprAnon(gz, scope, node, struct_init);
   1868             return rvalue(gz, ri, struct_inst, node);
   1869         },
   1870         .destructure => |destructure| {
   1871             // This is an untyped init, so is an actual struct, which does
   1872             // not support destructuring.
   1873             return astgen.failNodeNotes(node, "struct value cannot be destructured", .{}, &.{
   1874                 try astgen.errNoteNode(destructure.src_node, "result destructured here", .{}),
   1875             });
   1876         },
   1877     }
   1878 }
   1879 
   1880 /// A struct initialization expression using a `struct_init_anon` instruction.
   1881 fn structInitExprAnon(
   1882     gz: *GenZir,
   1883     scope: *Scope,
   1884     node: Ast.Node.Index,
   1885     struct_init: Ast.full.StructInit,
   1886 ) InnerError!Zir.Inst.Ref {
   1887     const astgen = gz.astgen;
   1888     const tree = astgen.tree;
   1889 
   1890     const payload_index = try addExtra(astgen, Zir.Inst.StructInitAnon{
   1891         .fields_len = @intCast(struct_init.ast.fields.len),
   1892     });
   1893     const field_size = @typeInfo(Zir.Inst.StructInitAnon.Item).@"struct".fields.len;
   1894     var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size);
   1895 
   1896     for (struct_init.ast.fields) |field_init| {
   1897         const name_token = tree.firstToken(field_init) - 2;
   1898         const str_index = try astgen.identAsString(name_token);
   1899         setExtra(astgen, extra_index, Zir.Inst.StructInitAnon.Item{
   1900             .field_name = str_index,
   1901             .init = try expr(gz, scope, .{ .rl = .none }, field_init),
   1902         });
   1903         extra_index += field_size;
   1904     }
   1905 
   1906     return gz.addPlNodePayloadIndex(.struct_init_anon, node, payload_index);
   1907 }
   1908 
   1909 /// A struct initialization expression using a `struct_init` or `struct_init_ref` instruction.
   1910 fn structInitExprTyped(
   1911     gz: *GenZir,
   1912     scope: *Scope,
   1913     node: Ast.Node.Index,
   1914     struct_init: Ast.full.StructInit,
   1915     ty_inst: Zir.Inst.Ref,
   1916     is_ref: bool,
   1917 ) InnerError!Zir.Inst.Ref {
   1918     const astgen = gz.astgen;
   1919     const tree = astgen.tree;
   1920 
   1921     const payload_index = try addExtra(astgen, Zir.Inst.StructInit{
   1922         .fields_len = @intCast(struct_init.ast.fields.len),
   1923     });
   1924     const field_size = @typeInfo(Zir.Inst.StructInit.Item).@"struct".fields.len;
   1925     var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size);
   1926 
   1927     for (struct_init.ast.fields) |field_init| {
   1928         const name_token = tree.firstToken(field_init) - 2;
   1929         const str_index = try astgen.identAsString(name_token);
   1930         const field_ty_inst = try gz.addPlNode(.struct_init_field_type, field_init, Zir.Inst.FieldType{
   1931             .container_type = ty_inst,
   1932             .name_start = str_index,
   1933         });
   1934         setExtra(astgen, extra_index, Zir.Inst.StructInit.Item{
   1935             .field_type = field_ty_inst.toIndex().?,
   1936             .init = try expr(gz, scope, .{ .rl = .{ .coerced_ty = field_ty_inst } }, field_init),
   1937         });
   1938         extra_index += field_size;
   1939     }
   1940 
   1941     const tag: Zir.Inst.Tag = if (is_ref) .struct_init_ref else .struct_init;
   1942     return gz.addPlNodePayloadIndex(tag, node, payload_index);
   1943 }
   1944 
   1945 /// A struct initialization expression using field pointers.
   1946 fn structInitExprPtr(
   1947     gz: *GenZir,
   1948     scope: *Scope,
   1949     node: Ast.Node.Index,
   1950     struct_init: Ast.full.StructInit,
   1951     ptr_inst: Zir.Inst.Ref,
   1952 ) InnerError!void {
   1953     const astgen = gz.astgen;
   1954     const tree = astgen.tree;
   1955 
   1956     const struct_ptr_inst = try gz.addUnNode(.opt_eu_base_ptr_init, ptr_inst, node);
   1957 
   1958     const payload_index = try addExtra(astgen, Zir.Inst.Block{
   1959         .body_len = @intCast(struct_init.ast.fields.len),
   1960     });
   1961     var extra_index = try reserveExtra(astgen, struct_init.ast.fields.len);
   1962 
   1963     for (struct_init.ast.fields) |field_init| {
   1964         const name_token = tree.firstToken(field_init) - 2;
   1965         const str_index = try astgen.identAsString(name_token);
   1966         const field_ptr = try gz.addPlNode(.struct_init_field_ptr, field_init, Zir.Inst.Field{
   1967             .lhs = struct_ptr_inst,
   1968             .field_name_start = str_index,
   1969         });
   1970         astgen.extra.items[extra_index] = @intFromEnum(field_ptr.toIndex().?);
   1971         extra_index += 1;
   1972         _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = field_ptr } } }, field_init);
   1973     }
   1974 
   1975     _ = try gz.addPlNodePayloadIndex(.validate_ptr_struct_init, node, payload_index);
   1976 }
   1977 
   1978 /// This explicitly calls expr in a comptime scope by wrapping it in a `block_comptime` if
   1979 /// necessary. It should be used whenever we need to force compile-time evaluation of something,
   1980 /// such as a type.
   1981 /// The function corresponding to `comptime` expression syntax is `comptimeExprAst`.
   1982 fn comptimeExpr(
   1983     gz: *GenZir,
   1984     scope: *Scope,
   1985     ri: ResultInfo,
   1986     node: Ast.Node.Index,
   1987 ) InnerError!Zir.Inst.Ref {
   1988     if (gz.is_comptime) {
   1989         // No need to change anything!
   1990         return expr(gz, scope, ri, node);
   1991     }
   1992 
   1993     // There's an optimization here: if the body will be evaluated at comptime regardless, there's
   1994     // no need to wrap it in a block. This is hard to determine in general, but we can identify a
   1995     // common subset of trivially comptime expressions to take down the size of the ZIR a bit.
   1996     const tree = gz.astgen.tree;
   1997     const main_tokens = tree.nodes.items(.main_token);
   1998     const node_tags = tree.nodes.items(.tag);
   1999     switch (node_tags[node]) {
   2000         // Any identifier in `primitive_instrs` is trivially comptime. In particular, this includes
   2001         // some common types, so we can elide `block_comptime` for a few common type annotations.
   2002         .identifier => {
   2003             const ident_token = main_tokens[node];
   2004             const ident_name_raw = tree.tokenSlice(ident_token);
   2005             if (primitive_instrs.get(ident_name_raw)) |zir_const_ref| {
   2006                 // No need to worry about result location here, we're not creating a comptime block!
   2007                 return rvalue(gz, ri, zir_const_ref, node);
   2008             }
   2009         },
   2010 
   2011         // We can also avoid the block for a few trivial AST tags which are always comptime-known.
   2012         .number_literal, .string_literal, .multiline_string_literal, .enum_literal, .error_value => {
   2013             // No need to worry about result location here, we're not creating a comptime block!
   2014             return expr(gz, scope, ri, node);
   2015         },
   2016 
   2017         // Lastly, for labelled blocks, avoid emitting a labelled block directly inside this
   2018         // comptime block, because that would be silly! Note that we don't bother doing this for
   2019         // unlabelled blocks, since they don't generate blocks at comptime anyway (see `blockExpr`).
   2020         .block_two, .block_two_semicolon, .block, .block_semicolon => {
   2021             const token_tags = tree.tokens.items(.tag);
   2022             const lbrace = main_tokens[node];
   2023             // Careful! We can't pass in the real result location here, since it may
   2024             // refer to runtime memory. A runtime-to-comptime boundary has to remove
   2025             // result location information, compute the result, and copy it to the true
   2026             // result location at runtime. We do this below as well.
   2027             const ty_only_ri: ResultInfo = .{
   2028                 .ctx = ri.ctx,
   2029                 .rl = if (try ri.rl.resultType(gz, node)) |res_ty|
   2030                     .{ .coerced_ty = res_ty }
   2031                 else
   2032                     .none,
   2033             };
   2034             if (token_tags[lbrace - 1] == .colon and
   2035                 token_tags[lbrace - 2] == .identifier)
   2036             {
   2037                 const node_datas = tree.nodes.items(.data);
   2038                 switch (node_tags[node]) {
   2039                     .block_two, .block_two_semicolon => {
   2040                         const stmts: [2]Ast.Node.Index = .{ node_datas[node].lhs, node_datas[node].rhs };
   2041                         const stmt_slice = if (stmts[0] == 0)
   2042                             stmts[0..0]
   2043                         else if (stmts[1] == 0)
   2044                             stmts[0..1]
   2045                         else
   2046                             stmts[0..2];
   2047 
   2048                         const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmt_slice, true, .normal);
   2049                         return rvalue(gz, ri, block_ref, node);
   2050                     },
   2051                     .block, .block_semicolon => {
   2052                         const stmts = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
   2053                         // Replace result location and copy back later - see above.
   2054                         const block_ref = try labeledBlockExpr(gz, scope, ty_only_ri, node, stmts, true, .normal);
   2055                         return rvalue(gz, ri, block_ref, node);
   2056                     },
   2057                     else => unreachable,
   2058                 }
   2059             }
   2060         },
   2061 
   2062         // In other cases, we don't optimize anything - we need a wrapper comptime block.
   2063         else => {},
   2064     }
   2065 
   2066     var block_scope = gz.makeSubBlock(scope);
   2067     block_scope.is_comptime = true;
   2068     defer block_scope.unstack();
   2069 
   2070     const block_inst = try gz.makeBlockInst(.block_comptime, node);
   2071     // Replace result location and copy back later - see above.
   2072     const ty_only_ri: ResultInfo = .{
   2073         .ctx = ri.ctx,
   2074         .rl = if (try ri.rl.resultType(gz, node)) |res_ty|
   2075             .{ .coerced_ty = res_ty }
   2076         else
   2077             .none,
   2078     };
   2079     const block_result = try fullBodyExpr(&block_scope, scope, ty_only_ri, node, .normal);
   2080     if (!gz.refIsNoReturn(block_result)) {
   2081         _ = try block_scope.addBreak(.@"break", block_inst, block_result);
   2082     }
   2083     try block_scope.setBlockBody(block_inst);
   2084     try gz.instructions.append(gz.astgen.gpa, block_inst);
   2085 
   2086     return rvalue(gz, ri, block_inst.toRef(), node);
   2087 }
   2088 
   2089 /// This one is for an actual `comptime` syntax, and will emit a compile error if
   2090 /// the scope is already known to be comptime-evaluated.
   2091 /// See `comptimeExpr` for the helper function for calling expr in a comptime scope.
   2092 fn comptimeExprAst(
   2093     gz: *GenZir,
   2094     scope: *Scope,
   2095     ri: ResultInfo,
   2096     node: Ast.Node.Index,
   2097 ) InnerError!Zir.Inst.Ref {
   2098     const astgen = gz.astgen;
   2099     if (gz.is_comptime) {
   2100         return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
   2101     }
   2102     const tree = astgen.tree;
   2103     const node_datas = tree.nodes.items(.data);
   2104     const body_node = node_datas[node].lhs;
   2105     return comptimeExpr(gz, scope, ri, body_node);
   2106 }
   2107 
   2108 /// Restore the error return trace index. Performs the restore only if the result is a non-error or
   2109 /// if the result location is a non-error-handling expression.
   2110 fn restoreErrRetIndex(
   2111     gz: *GenZir,
   2112     bt: GenZir.BranchTarget,
   2113     ri: ResultInfo,
   2114     node: Ast.Node.Index,
   2115     result: Zir.Inst.Ref,
   2116 ) !void {
   2117     const op = switch (nodeMayEvalToError(gz.astgen.tree, node)) {
   2118         .always => return, // never restore/pop
   2119         .never => .none, // always restore/pop
   2120         .maybe => switch (ri.ctx) {
   2121             .error_handling_expr, .@"return", .fn_arg, .const_init => switch (ri.rl) {
   2122                 .ptr => |ptr_res| try gz.addUnNode(.load, ptr_res.inst, node),
   2123                 .inferred_ptr => blk: {
   2124                     // This is a terrible workaround for Sema's inability to load from a .alloc_inferred ptr
   2125                     // before its type has been resolved. There is no valid operand to use here, so error
   2126                     // traces will be popped prematurely.
   2127                     // TODO: Update this to do a proper load from the rl_ptr, once Sema can support it.
   2128                     break :blk .none;
   2129                 },
   2130                 .destructure => return, // value must be a tuple or array, so never restore/pop
   2131                 else => result,
   2132             },
   2133             else => .none, // always restore/pop
   2134         },
   2135     };
   2136     _ = try gz.addRestoreErrRetIndex(bt, .{ .if_non_error = op }, node);
   2137 }
   2138 
   2139 fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   2140     const astgen = parent_gz.astgen;
   2141     const tree = astgen.tree;
   2142     const node_datas = tree.nodes.items(.data);
   2143     const break_label = node_datas[node].lhs;
   2144     const rhs = node_datas[node].rhs;
   2145 
   2146     // Look for the label in the scope.
   2147     var scope = parent_scope;
   2148     while (true) {
   2149         switch (scope.tag) {
   2150             .gen_zir => {
   2151                 const block_gz = scope.cast(GenZir).?;
   2152 
   2153                 if (block_gz.cur_defer_node != 0) {
   2154                     // We are breaking out of a `defer` block.
   2155                     return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{
   2156                         try astgen.errNoteNode(
   2157                             block_gz.cur_defer_node,
   2158                             "defer expression here",
   2159                             .{},
   2160                         ),
   2161                     });
   2162                 }
   2163 
   2164                 const block_inst = blk: {
   2165                     if (break_label != 0) {
   2166                         if (block_gz.label) |*label| {
   2167                             if (try astgen.tokenIdentEql(label.token, break_label)) {
   2168                                 label.used = true;
   2169                                 break :blk label.block_inst;
   2170                             }
   2171                         }
   2172                     } else if (block_gz.break_block.unwrap()) |i| {
   2173                         break :blk i;
   2174                     }
   2175                     // If not the target, start over with the parent
   2176                     scope = block_gz.parent;
   2177                     continue;
   2178                 };
   2179                 // If we made it here, this block is the target of the break expr
   2180 
   2181                 const break_tag: Zir.Inst.Tag = if (block_gz.is_inline)
   2182                     .break_inline
   2183                 else
   2184                     .@"break";
   2185 
   2186                 if (rhs == 0) {
   2187                     _ = try rvalue(parent_gz, block_gz.break_result_info, .void_value, node);
   2188 
   2189                     try genDefers(parent_gz, scope, parent_scope, .normal_only);
   2190 
   2191                     // As our last action before the break, "pop" the error trace if needed
   2192                     if (!block_gz.is_comptime)
   2193                         _ = try parent_gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, node);
   2194 
   2195                     _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
   2196                     return Zir.Inst.Ref.unreachable_value;
   2197                 }
   2198 
   2199                 const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_info, rhs, node);
   2200 
   2201                 try genDefers(parent_gz, scope, parent_scope, .normal_only);
   2202 
   2203                 // As our last action before the break, "pop" the error trace if needed
   2204                 if (!block_gz.is_comptime)
   2205                     try restoreErrRetIndex(parent_gz, .{ .block = block_inst }, block_gz.break_result_info, rhs, operand);
   2206 
   2207                 switch (block_gz.break_result_info.rl) {
   2208                     .ptr => {
   2209                         // In this case we don't have any mechanism to intercept it;
   2210                         // we assume the result location is written, and we break with void.
   2211                         _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
   2212                     },
   2213                     .discard => {
   2214                         _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
   2215                     },
   2216                     else => {
   2217                         _ = try parent_gz.addBreakWithSrcNode(break_tag, block_inst, operand, rhs);
   2218                     },
   2219                 }
   2220                 return Zir.Inst.Ref.unreachable_value;
   2221             },
   2222             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   2223             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   2224             .namespace => break,
   2225             .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   2226             .top => unreachable,
   2227         }
   2228     }
   2229     if (break_label != 0) {
   2230         const label_name = try astgen.identifierTokenString(break_label);
   2231         return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
   2232     } else {
   2233         return astgen.failNode(node, "break expression outside loop", .{});
   2234     }
   2235 }
   2236 
   2237 fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   2238     const astgen = parent_gz.astgen;
   2239     const tree = astgen.tree;
   2240     const node_datas = tree.nodes.items(.data);
   2241     const break_label = node_datas[node].lhs;
   2242     const rhs = node_datas[node].rhs;
   2243 
   2244     if (break_label == 0 and rhs != 0) {
   2245         return astgen.failNode(node, "cannot continue with operand without label", .{});
   2246     }
   2247 
   2248     // Look for the label in the scope.
   2249     var scope = parent_scope;
   2250     while (true) {
   2251         switch (scope.tag) {
   2252             .gen_zir => {
   2253                 const gen_zir = scope.cast(GenZir).?;
   2254 
   2255                 if (gen_zir.cur_defer_node != 0) {
   2256                     return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{
   2257                         try astgen.errNoteNode(
   2258                             gen_zir.cur_defer_node,
   2259                             "defer expression here",
   2260                             .{},
   2261                         ),
   2262                     });
   2263                 }
   2264                 const continue_block = gen_zir.continue_block.unwrap() orelse {
   2265                     scope = gen_zir.parent;
   2266                     continue;
   2267                 };
   2268                 if (break_label != 0) blk: {
   2269                     if (gen_zir.label) |*label| {
   2270                         if (try astgen.tokenIdentEql(label.token, break_label)) {
   2271                             const maybe_switch_tag = astgen.instructions.items(.tag)[@intFromEnum(label.block_inst)];
   2272                             if (rhs != 0) switch (maybe_switch_tag) {
   2273                                 .switch_block, .switch_block_ref => {},
   2274                                 else => return astgen.failNode(node, "cannot continue loop with operand", .{}),
   2275                             } else switch (maybe_switch_tag) {
   2276                                 .switch_block, .switch_block_ref => return astgen.failNode(node, "cannot continue switch without operand", .{}),
   2277                                 else => {},
   2278                             }
   2279 
   2280                             label.used = true;
   2281                             label.used_for_continue = true;
   2282                             break :blk;
   2283                         }
   2284                     }
   2285                     // found continue but either it has a different label, or no label
   2286                     scope = gen_zir.parent;
   2287                     continue;
   2288                 } else if (gen_zir.label) |label| {
   2289                     // This `continue` is unlabeled. If the gz we've found corresponds to a labeled
   2290                     // `switch`, ignore it and continue to parent scopes.
   2291                     switch (astgen.instructions.items(.tag)[@intFromEnum(label.block_inst)]) {
   2292                         .switch_block, .switch_block_ref => {
   2293                             scope = gen_zir.parent;
   2294                             continue;
   2295                         },
   2296                         else => {},
   2297                     }
   2298                 }
   2299 
   2300                 if (rhs != 0) {
   2301                     // We need to figure out the result info to use.
   2302                     // The type should match
   2303                     const operand = try reachableExpr(parent_gz, parent_scope, gen_zir.continue_result_info, rhs, node);
   2304 
   2305                     try genDefers(parent_gz, scope, parent_scope, .normal_only);
   2306 
   2307                     // As our last action before the continue, "pop" the error trace if needed
   2308                     if (!gen_zir.is_comptime)
   2309                         _ = try parent_gz.addRestoreErrRetIndex(.{ .block = continue_block }, .always, node);
   2310 
   2311                     _ = try parent_gz.addBreakWithSrcNode(.switch_continue, continue_block, operand, rhs);
   2312                     return Zir.Inst.Ref.unreachable_value;
   2313                 }
   2314 
   2315                 try genDefers(parent_gz, scope, parent_scope, .normal_only);
   2316 
   2317                 const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline)
   2318                     .break_inline
   2319                 else
   2320                     .@"break";
   2321                 if (break_tag == .break_inline) {
   2322                     _ = try parent_gz.addUnNode(.check_comptime_control_flow, continue_block.toRef(), node);
   2323                 }
   2324 
   2325                 // As our last action before the continue, "pop" the error trace if needed
   2326                 if (!gen_zir.is_comptime)
   2327                     _ = try parent_gz.addRestoreErrRetIndex(.{ .block = continue_block }, .always, node);
   2328 
   2329                 _ = try parent_gz.addBreak(break_tag, continue_block, .void_value);
   2330                 return Zir.Inst.Ref.unreachable_value;
   2331             },
   2332             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   2333             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   2334             .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   2335             .namespace => break,
   2336             .top => unreachable,
   2337         }
   2338     }
   2339     if (break_label != 0) {
   2340         const label_name = try astgen.identifierTokenString(break_label);
   2341         return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
   2342     } else {
   2343         return astgen.failNode(node, "continue expression outside loop", .{});
   2344     }
   2345 }
   2346 
   2347 /// Similar to `expr`, but intended for use when `gz` corresponds to a body
   2348 /// which will contain only this node's code. Differs from `expr` in that if the
   2349 /// root expression is an unlabeled block, does not emit an actual block.
   2350 /// Instead, the block contents are emitted directly into `gz`.
   2351 fn fullBodyExpr(
   2352     gz: *GenZir,
   2353     scope: *Scope,
   2354     ri: ResultInfo,
   2355     node: Ast.Node.Index,
   2356     block_kind: BlockKind,
   2357 ) InnerError!Zir.Inst.Ref {
   2358     const tree = gz.astgen.tree;
   2359     const node_tags = tree.nodes.items(.tag);
   2360     const node_datas = tree.nodes.items(.data);
   2361     const main_tokens = tree.nodes.items(.main_token);
   2362     const token_tags = tree.tokens.items(.tag);
   2363     var stmt_buf: [2]Ast.Node.Index = undefined;
   2364     const statements: []const Ast.Node.Index = switch (node_tags[node]) {
   2365         else => return expr(gz, scope, ri, node),
   2366         .block_two, .block_two_semicolon => if (node_datas[node].lhs == 0) s: {
   2367             break :s &.{};
   2368         } else if (node_datas[node].rhs == 0) s: {
   2369             stmt_buf[0] = node_datas[node].lhs;
   2370             break :s stmt_buf[0..1];
   2371         } else s: {
   2372             stmt_buf[0] = node_datas[node].lhs;
   2373             stmt_buf[1] = node_datas[node].rhs;
   2374             break :s stmt_buf[0..2];
   2375         },
   2376         .block, .block_semicolon => tree.extra_data[node_datas[node].lhs..node_datas[node].rhs],
   2377     };
   2378 
   2379     const lbrace = main_tokens[node];
   2380     if (token_tags[lbrace - 1] == .colon and
   2381         token_tags[lbrace - 2] == .identifier)
   2382     {
   2383         // Labeled blocks are tricky - forwarding result location information properly is non-trivial,
   2384         // plus if this block is exited with a `break_inline` we aren't allowed multiple breaks. This
   2385         // case is rare, so just treat it as a normal expression and create a nested block.
   2386         return blockExpr(gz, scope, ri, node, statements, block_kind);
   2387     }
   2388 
   2389     var sub_gz = gz.makeSubBlock(scope);
   2390     try blockExprStmts(&sub_gz, &sub_gz.base, statements, block_kind);
   2391 
   2392     return rvalue(gz, ri, .void_value, node);
   2393 }
   2394 
   2395 const BlockKind = enum { normal, allow_branch_hint };
   2396 
   2397 fn blockExpr(
   2398     gz: *GenZir,
   2399     scope: *Scope,
   2400     ri: ResultInfo,
   2401     block_node: Ast.Node.Index,
   2402     statements: []const Ast.Node.Index,
   2403     kind: BlockKind,
   2404 ) InnerError!Zir.Inst.Ref {
   2405     const astgen = gz.astgen;
   2406     const tree = astgen.tree;
   2407     const main_tokens = tree.nodes.items(.main_token);
   2408     const token_tags = tree.tokens.items(.tag);
   2409 
   2410     const lbrace = main_tokens[block_node];
   2411     if (token_tags[lbrace - 1] == .colon and
   2412         token_tags[lbrace - 2] == .identifier)
   2413     {
   2414         return labeledBlockExpr(gz, scope, ri, block_node, statements, false, kind);
   2415     }
   2416 
   2417     if (!gz.is_comptime) {
   2418         // Since this block is unlabeled, its control flow is effectively linear and we
   2419         // can *almost* get away with inlining the block here. However, we actually need
   2420         // to preserve the .block for Sema, to properly pop the error return trace.
   2421 
   2422         const block_tag: Zir.Inst.Tag = .block;
   2423         const block_inst = try gz.makeBlockInst(block_tag, block_node);
   2424         try gz.instructions.append(astgen.gpa, block_inst);
   2425 
   2426         var block_scope = gz.makeSubBlock(scope);
   2427         defer block_scope.unstack();
   2428 
   2429         try blockExprStmts(&block_scope, &block_scope.base, statements, kind);
   2430 
   2431         if (!block_scope.endsWithNoReturn()) {
   2432             // As our last action before the break, "pop" the error trace if needed
   2433             _ = try gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, block_node);
   2434             _ = try block_scope.addBreak(.@"break", block_inst, .void_value);
   2435         }
   2436 
   2437         try block_scope.setBlockBody(block_inst);
   2438     } else {
   2439         var sub_gz = gz.makeSubBlock(scope);
   2440         try blockExprStmts(&sub_gz, &sub_gz.base, statements, kind);
   2441     }
   2442 
   2443     return rvalue(gz, ri, .void_value, block_node);
   2444 }
   2445 
   2446 fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.TokenIndex) !void {
   2447     // Look for the label in the scope.
   2448     var scope = parent_scope;
   2449     while (true) {
   2450         switch (scope.tag) {
   2451             .gen_zir => {
   2452                 const gen_zir = scope.cast(GenZir).?;
   2453                 if (gen_zir.label) |prev_label| {
   2454                     if (try astgen.tokenIdentEql(label, prev_label.token)) {
   2455                         const label_name = try astgen.identifierTokenString(label);
   2456                         return astgen.failTokNotes(label, "redefinition of label '{s}'", .{
   2457                             label_name,
   2458                         }, &[_]u32{
   2459                             try astgen.errNoteTok(
   2460                                 prev_label.token,
   2461                                 "previous definition here",
   2462                                 .{},
   2463                             ),
   2464                         });
   2465                     }
   2466                 }
   2467                 scope = gen_zir.parent;
   2468             },
   2469             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   2470             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   2471             .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   2472             .namespace => break,
   2473             .top => unreachable,
   2474         }
   2475     }
   2476 }
   2477 
   2478 fn labeledBlockExpr(
   2479     gz: *GenZir,
   2480     parent_scope: *Scope,
   2481     ri: ResultInfo,
   2482     block_node: Ast.Node.Index,
   2483     statements: []const Ast.Node.Index,
   2484     force_comptime: bool,
   2485     block_kind: BlockKind,
   2486 ) InnerError!Zir.Inst.Ref {
   2487     const astgen = gz.astgen;
   2488     const tree = astgen.tree;
   2489     const main_tokens = tree.nodes.items(.main_token);
   2490     const token_tags = tree.tokens.items(.tag);
   2491 
   2492     const lbrace = main_tokens[block_node];
   2493     const label_token = lbrace - 2;
   2494     assert(token_tags[label_token] == .identifier);
   2495 
   2496     try astgen.checkLabelRedefinition(parent_scope, label_token);
   2497 
   2498     const need_rl = astgen.nodes_need_rl.contains(block_node);
   2499     const block_ri: ResultInfo = if (need_rl) ri else .{
   2500         .rl = switch (ri.rl) {
   2501             .ptr => .{ .ty = (try ri.rl.resultType(gz, block_node)).? },
   2502             .inferred_ptr => .none,
   2503             else => ri.rl,
   2504         },
   2505         .ctx = ri.ctx,
   2506     };
   2507     // We need to call `rvalue` to write through to the pointer only if we had a
   2508     // result pointer and aren't forwarding it.
   2509     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   2510     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   2511 
   2512     // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct
   2513     // so that break statements can reference it.
   2514     const block_tag: Zir.Inst.Tag = if (force_comptime) .block_comptime else .block;
   2515     const block_inst = try gz.makeBlockInst(block_tag, block_node);
   2516     try gz.instructions.append(astgen.gpa, block_inst);
   2517     var block_scope = gz.makeSubBlock(parent_scope);
   2518     block_scope.label = GenZir.Label{
   2519         .token = label_token,
   2520         .block_inst = block_inst,
   2521     };
   2522     block_scope.setBreakResultInfo(block_ri);
   2523     if (force_comptime) block_scope.is_comptime = true;
   2524     defer block_scope.unstack();
   2525 
   2526     try blockExprStmts(&block_scope, &block_scope.base, statements, block_kind);
   2527     if (!block_scope.endsWithNoReturn()) {
   2528         // As our last action before the return, "pop" the error trace if needed
   2529         _ = try gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always, block_node);
   2530         _ = try block_scope.addBreak(.@"break", block_inst, .void_value);
   2531     }
   2532 
   2533     if (!block_scope.label.?.used) {
   2534         try astgen.appendErrorTok(label_token, "unused block label", .{});
   2535     }
   2536 
   2537     try block_scope.setBlockBody(block_inst);
   2538     if (need_result_rvalue) {
   2539         return rvalue(gz, ri, block_inst.toRef(), block_node);
   2540     } else {
   2541         return block_inst.toRef();
   2542     }
   2543 }
   2544 
   2545 fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Node.Index, block_kind: BlockKind) !void {
   2546     const astgen = gz.astgen;
   2547     const tree = astgen.tree;
   2548     const node_tags = tree.nodes.items(.tag);
   2549     const node_data = tree.nodes.items(.data);
   2550 
   2551     if (statements.len == 0) return;
   2552 
   2553     var block_arena = std.heap.ArenaAllocator.init(gz.astgen.gpa);
   2554     defer block_arena.deinit();
   2555     const block_arena_allocator = block_arena.allocator();
   2556 
   2557     var noreturn_src_node: Ast.Node.Index = 0;
   2558     var scope = parent_scope;
   2559     for (statements, 0..) |statement, stmt_idx| {
   2560         if (noreturn_src_node != 0) {
   2561             try astgen.appendErrorNodeNotes(
   2562                 statement,
   2563                 "unreachable code",
   2564                 .{},
   2565                 &[_]u32{
   2566                     try astgen.errNoteNode(
   2567                         noreturn_src_node,
   2568                         "control flow is diverted here",
   2569                         .{},
   2570                     ),
   2571                 },
   2572             );
   2573         }
   2574         const allow_branch_hint = switch (block_kind) {
   2575             .normal => false,
   2576             .allow_branch_hint => stmt_idx == 0,
   2577         };
   2578         var inner_node = statement;
   2579         while (true) {
   2580             switch (node_tags[inner_node]) {
   2581                 // zig fmt: off
   2582                 .global_var_decl,
   2583                 .local_var_decl,
   2584                 .simple_var_decl,
   2585                 .aligned_var_decl, => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.fullVarDecl(statement).?),
   2586 
   2587                 .assign_destructure => scope = try assignDestructureMaybeDecls(gz, scope, statement, block_arena_allocator),
   2588 
   2589                 .@"defer"    => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_normal),
   2590                 .@"errdefer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_error),
   2591 
   2592                 .assign => try assign(gz, scope, statement),
   2593 
   2594                 .assign_shl => try assignShift(gz, scope, statement, .shl),
   2595                 .assign_shr => try assignShift(gz, scope, statement, .shr),
   2596 
   2597                 .assign_bit_and  => try assignOp(gz, scope, statement, .bit_and),
   2598                 .assign_bit_or   => try assignOp(gz, scope, statement, .bit_or),
   2599                 .assign_bit_xor  => try assignOp(gz, scope, statement, .xor),
   2600                 .assign_div      => try assignOp(gz, scope, statement, .div),
   2601                 .assign_sub      => try assignOp(gz, scope, statement, .sub),
   2602                 .assign_sub_wrap => try assignOp(gz, scope, statement, .subwrap),
   2603                 .assign_mod      => try assignOp(gz, scope, statement, .mod_rem),
   2604                 .assign_add      => try assignOp(gz, scope, statement, .add),
   2605                 .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap),
   2606                 .assign_mul      => try assignOp(gz, scope, statement, .mul),
   2607                 .assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap),
   2608 
   2609                 .grouped_expression => {
   2610                     inner_node = node_data[statement].lhs;
   2611                     continue;
   2612                 },
   2613 
   2614                 .while_simple,
   2615                 .while_cont,
   2616                 .@"while", => _ = try whileExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullWhile(inner_node).?, true),
   2617 
   2618                 .for_simple,
   2619                 .@"for", => _ = try forExpr(gz, scope, .{ .rl = .none }, inner_node, tree.fullFor(inner_node).?, true),
   2620 
   2621                 // These cases are here to allow branch hints.
   2622                 .builtin_call_two, .builtin_call_two_comma => {
   2623                     try emitDbgNode(gz, inner_node);
   2624                     const ri: ResultInfo = .{ .rl = .none };
   2625                     const result = if (node_data[inner_node].lhs == 0) r: {
   2626                         break :r try builtinCall(gz, scope, ri, inner_node, &.{}, allow_branch_hint);
   2627                     } else if (node_data[inner_node].rhs == 0) r: {
   2628                         break :r try builtinCall(gz, scope, ri, inner_node, &.{node_data[inner_node].lhs}, allow_branch_hint);
   2629                     } else r: {
   2630                         break :r try builtinCall(gz, scope, ri, inner_node, &.{
   2631                             node_data[inner_node].lhs,
   2632                             node_data[inner_node].rhs,
   2633                         }, allow_branch_hint);
   2634                     };
   2635                     noreturn_src_node = try addEnsureResult(gz, result, inner_node);
   2636                 },
   2637                 .builtin_call, .builtin_call_comma => {
   2638                     try emitDbgNode(gz, inner_node);
   2639                     const ri: ResultInfo = .{ .rl = .none };
   2640                     const params = tree.extra_data[node_data[inner_node].lhs..node_data[inner_node].rhs];
   2641                     const result = try builtinCall(gz, scope, ri, inner_node, params, allow_branch_hint);
   2642                     noreturn_src_node = try addEnsureResult(gz, result, inner_node);
   2643                 },
   2644 
   2645                 else => noreturn_src_node = try unusedResultExpr(gz, scope, inner_node),
   2646                 // zig fmt: on
   2647             }
   2648             break;
   2649         }
   2650     }
   2651 
   2652     if (noreturn_src_node == 0) {
   2653         try genDefers(gz, parent_scope, scope, .normal_only);
   2654     }
   2655     try checkUsed(gz, parent_scope, scope);
   2656 }
   2657 
   2658 /// Returns AST source node of the thing that is noreturn if the statement is
   2659 /// definitely `noreturn`. Otherwise returns 0.
   2660 fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) InnerError!Ast.Node.Index {
   2661     try emitDbgNode(gz, statement);
   2662     // We need to emit an error if the result is not `noreturn` or `void`, but
   2663     // we want to avoid adding the ZIR instruction if possible for performance.
   2664     const maybe_unused_result = try expr(gz, scope, .{ .rl = .none }, statement);
   2665     return addEnsureResult(gz, maybe_unused_result, statement);
   2666 }
   2667 
   2668 fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: Ast.Node.Index) InnerError!Ast.Node.Index {
   2669     var noreturn_src_node: Ast.Node.Index = 0;
   2670     const elide_check = if (maybe_unused_result.toIndex()) |inst| b: {
   2671         // Note that this array becomes invalid after appending more items to it
   2672         // in the above while loop.
   2673         const zir_tags = gz.astgen.instructions.items(.tag);
   2674         switch (zir_tags[@intFromEnum(inst)]) {
   2675             // For some instructions, modify the zir data
   2676             // so we can avoid a separate ensure_result_used instruction.
   2677             .call, .field_call => {
   2678                 const break_extra = gz.astgen.instructions.items(.data)[@intFromEnum(inst)].pl_node.payload_index;
   2679                 comptime assert(std.meta.fieldIndex(Zir.Inst.Call, "flags") ==
   2680                     std.meta.fieldIndex(Zir.Inst.FieldCall, "flags"));
   2681                 const flags: *Zir.Inst.Call.Flags = @ptrCast(&gz.astgen.extra.items[
   2682                     break_extra + std.meta.fieldIndex(Zir.Inst.Call, "flags").?
   2683                 ]);
   2684                 flags.ensure_result_used = true;
   2685                 break :b true;
   2686             },
   2687             .builtin_call => {
   2688                 const break_extra = gz.astgen.instructions.items(.data)[@intFromEnum(inst)].pl_node.payload_index;
   2689                 const flags: *Zir.Inst.BuiltinCall.Flags = @ptrCast(&gz.astgen.extra.items[
   2690                     break_extra + std.meta.fieldIndex(Zir.Inst.BuiltinCall, "flags").?
   2691                 ]);
   2692                 flags.ensure_result_used = true;
   2693                 break :b true;
   2694             },
   2695 
   2696             // ZIR instructions that might be a type other than `noreturn` or `void`.
   2697             .add,
   2698             .addwrap,
   2699             .add_sat,
   2700             .add_unsafe,
   2701             .param,
   2702             .param_comptime,
   2703             .param_anytype,
   2704             .param_anytype_comptime,
   2705             .alloc,
   2706             .alloc_mut,
   2707             .alloc_comptime_mut,
   2708             .alloc_inferred,
   2709             .alloc_inferred_mut,
   2710             .alloc_inferred_comptime,
   2711             .alloc_inferred_comptime_mut,
   2712             .make_ptr_const,
   2713             .array_cat,
   2714             .array_mul,
   2715             .array_type,
   2716             .array_type_sentinel,
   2717             .elem_type,
   2718             .indexable_ptr_elem_type,
   2719             .vec_arr_elem_type,
   2720             .vector_type,
   2721             .indexable_ptr_len,
   2722             .anyframe_type,
   2723             .as_node,
   2724             .as_shift_operand,
   2725             .bit_and,
   2726             .bitcast,
   2727             .bit_or,
   2728             .block,
   2729             .block_comptime,
   2730             .block_inline,
   2731             .declaration,
   2732             .suspend_block,
   2733             .loop,
   2734             .bool_br_and,
   2735             .bool_br_or,
   2736             .bool_not,
   2737             .cmp_lt,
   2738             .cmp_lte,
   2739             .cmp_eq,
   2740             .cmp_gte,
   2741             .cmp_gt,
   2742             .cmp_neq,
   2743             .decl_ref,
   2744             .decl_val,
   2745             .load,
   2746             .div,
   2747             .elem_ptr,
   2748             .elem_val,
   2749             .elem_ptr_node,
   2750             .elem_val_node,
   2751             .elem_val_imm,
   2752             .field_ptr,
   2753             .field_val,
   2754             .field_ptr_named,
   2755             .field_val_named,
   2756             .func,
   2757             .func_inferred,
   2758             .func_fancy,
   2759             .int,
   2760             .int_big,
   2761             .float,
   2762             .float128,
   2763             .int_type,
   2764             .is_non_null,
   2765             .is_non_null_ptr,
   2766             .is_non_err,
   2767             .is_non_err_ptr,
   2768             .ret_is_non_err,
   2769             .mod_rem,
   2770             .mul,
   2771             .mulwrap,
   2772             .mul_sat,
   2773             .ref,
   2774             .shl,
   2775             .shl_sat,
   2776             .shr,
   2777             .str,
   2778             .sub,
   2779             .subwrap,
   2780             .sub_sat,
   2781             .negate,
   2782             .negate_wrap,
   2783             .typeof,
   2784             .typeof_builtin,
   2785             .xor,
   2786             .optional_type,
   2787             .optional_payload_safe,
   2788             .optional_payload_unsafe,
   2789             .optional_payload_safe_ptr,
   2790             .optional_payload_unsafe_ptr,
   2791             .err_union_payload_unsafe,
   2792             .err_union_payload_unsafe_ptr,
   2793             .err_union_code,
   2794             .err_union_code_ptr,
   2795             .ptr_type,
   2796             .enum_literal,
   2797             .decl_literal,
   2798             .decl_literal_no_coerce,
   2799             .merge_error_sets,
   2800             .error_union_type,
   2801             .bit_not,
   2802             .error_value,
   2803             .slice_start,
   2804             .slice_end,
   2805             .slice_sentinel,
   2806             .slice_length,
   2807             .import,
   2808             .switch_block,
   2809             .switch_block_ref,
   2810             .switch_block_err_union,
   2811             .union_init,
   2812             .field_type_ref,
   2813             .error_set_decl,
   2814             .enum_from_int,
   2815             .int_from_enum,
   2816             .type_info,
   2817             .size_of,
   2818             .bit_size_of,
   2819             .typeof_log2_int_type,
   2820             .int_from_ptr,
   2821             .align_of,
   2822             .int_from_bool,
   2823             .embed_file,
   2824             .error_name,
   2825             .sqrt,
   2826             .sin,
   2827             .cos,
   2828             .tan,
   2829             .exp,
   2830             .exp2,
   2831             .log,
   2832             .log2,
   2833             .log10,
   2834             .abs,
   2835             .floor,
   2836             .ceil,
   2837             .trunc,
   2838             .round,
   2839             .tag_name,
   2840             .type_name,
   2841             .frame_type,
   2842             .frame_size,
   2843             .int_from_float,
   2844             .float_from_int,
   2845             .ptr_from_int,
   2846             .float_cast,
   2847             .int_cast,
   2848             .ptr_cast,
   2849             .truncate,
   2850             .has_decl,
   2851             .has_field,
   2852             .clz,
   2853             .ctz,
   2854             .pop_count,
   2855             .byte_swap,
   2856             .bit_reverse,
   2857             .div_exact,
   2858             .div_floor,
   2859             .div_trunc,
   2860             .mod,
   2861             .rem,
   2862             .shl_exact,
   2863             .shr_exact,
   2864             .bit_offset_of,
   2865             .offset_of,
   2866             .splat,
   2867             .reduce,
   2868             .shuffle,
   2869             .atomic_load,
   2870             .atomic_rmw,
   2871             .mul_add,
   2872             .max,
   2873             .min,
   2874             .c_import,
   2875             .@"resume",
   2876             .@"await",
   2877             .ret_err_value_code,
   2878             .ret_ptr,
   2879             .ret_type,
   2880             .for_len,
   2881             .@"try",
   2882             .try_ptr,
   2883             .opt_eu_base_ptr_init,
   2884             .coerce_ptr_elem_ty,
   2885             .struct_init_empty,
   2886             .struct_init_empty_result,
   2887             .struct_init_empty_ref_result,
   2888             .struct_init_anon,
   2889             .struct_init,
   2890             .struct_init_ref,
   2891             .struct_init_field_type,
   2892             .struct_init_field_ptr,
   2893             .array_init_anon,
   2894             .array_init,
   2895             .array_init_ref,
   2896             .validate_array_init_ref_ty,
   2897             .array_init_elem_type,
   2898             .array_init_elem_ptr,
   2899             => break :b false,
   2900 
   2901             .extended => switch (gz.astgen.instructions.items(.data)[@intFromEnum(inst)].extended.opcode) {
   2902                 .breakpoint,
   2903                 .disable_instrumentation,
   2904                 .set_float_mode,
   2905                 .set_align_stack,
   2906                 .branch_hint,
   2907                 => break :b true,
   2908                 else => break :b false,
   2909             },
   2910 
   2911             // ZIR instructions that are always `noreturn`.
   2912             .@"break",
   2913             .break_inline,
   2914             .condbr,
   2915             .condbr_inline,
   2916             .compile_error,
   2917             .ret_node,
   2918             .ret_load,
   2919             .ret_implicit,
   2920             .ret_err_value,
   2921             .@"unreachable",
   2922             .repeat,
   2923             .repeat_inline,
   2924             .panic,
   2925             .trap,
   2926             .check_comptime_control_flow,
   2927             .switch_continue,
   2928             => {
   2929                 noreturn_src_node = statement;
   2930                 break :b true;
   2931             },
   2932 
   2933             // ZIR instructions that are always `void`.
   2934             .dbg_stmt,
   2935             .dbg_var_ptr,
   2936             .dbg_var_val,
   2937             .ensure_result_used,
   2938             .ensure_result_non_error,
   2939             .ensure_err_union_payload_void,
   2940             .@"export",
   2941             .set_eval_branch_quota,
   2942             .atomic_store,
   2943             .store_node,
   2944             .store_to_inferred_ptr,
   2945             .resolve_inferred_alloc,
   2946             .set_runtime_safety,
   2947             .memcpy,
   2948             .memset,
   2949             .validate_deref,
   2950             .validate_destructure,
   2951             .save_err_ret_index,
   2952             .restore_err_ret_index_unconditional,
   2953             .restore_err_ret_index_fn_entry,
   2954             .validate_struct_init_ty,
   2955             .validate_struct_init_result_ty,
   2956             .validate_ptr_struct_init,
   2957             .validate_array_init_ty,
   2958             .validate_array_init_result_ty,
   2959             .validate_ptr_array_init,
   2960             .validate_ref_ty,
   2961             .try_operand_ty,
   2962             .try_ref_operand_ty,
   2963             => break :b true,
   2964 
   2965             .@"defer" => unreachable,
   2966             .defer_err_code => unreachable,
   2967         }
   2968     } else switch (maybe_unused_result) {
   2969         .none => unreachable,
   2970 
   2971         .unreachable_value => b: {
   2972             noreturn_src_node = statement;
   2973             break :b true;
   2974         },
   2975 
   2976         .void_value => true,
   2977 
   2978         else => false,
   2979     };
   2980     if (!elide_check) {
   2981         _ = try gz.addUnNode(.ensure_result_used, maybe_unused_result, statement);
   2982     }
   2983     return noreturn_src_node;
   2984 }
   2985 
   2986 fn countDefers(outer_scope: *Scope, inner_scope: *Scope) struct {
   2987     have_any: bool,
   2988     have_normal: bool,
   2989     have_err: bool,
   2990     need_err_code: bool,
   2991 } {
   2992     var have_normal = false;
   2993     var have_err = false;
   2994     var need_err_code = false;
   2995     var scope = inner_scope;
   2996     while (scope != outer_scope) {
   2997         switch (scope.tag) {
   2998             .gen_zir => scope = scope.cast(GenZir).?.parent,
   2999             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   3000             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   3001             .defer_normal => {
   3002                 const defer_scope = scope.cast(Scope.Defer).?;
   3003                 scope = defer_scope.parent;
   3004 
   3005                 have_normal = true;
   3006             },
   3007             .defer_error => {
   3008                 const defer_scope = scope.cast(Scope.Defer).?;
   3009                 scope = defer_scope.parent;
   3010 
   3011                 have_err = true;
   3012 
   3013                 const have_err_payload = defer_scope.remapped_err_code != .none;
   3014                 need_err_code = need_err_code or have_err_payload;
   3015             },
   3016             .namespace => unreachable,
   3017             .top => unreachable,
   3018         }
   3019     }
   3020     return .{
   3021         .have_any = have_normal or have_err,
   3022         .have_normal = have_normal,
   3023         .have_err = have_err,
   3024         .need_err_code = need_err_code,
   3025     };
   3026 }
   3027 
   3028 const DefersToEmit = union(enum) {
   3029     both: Zir.Inst.Ref, // err code
   3030     both_sans_err,
   3031     normal_only,
   3032 };
   3033 
   3034 fn genDefers(
   3035     gz: *GenZir,
   3036     outer_scope: *Scope,
   3037     inner_scope: *Scope,
   3038     which_ones: DefersToEmit,
   3039 ) InnerError!void {
   3040     const gpa = gz.astgen.gpa;
   3041 
   3042     var scope = inner_scope;
   3043     while (scope != outer_scope) {
   3044         switch (scope.tag) {
   3045             .gen_zir => scope = scope.cast(GenZir).?.parent,
   3046             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   3047             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   3048             .defer_normal => {
   3049                 const defer_scope = scope.cast(Scope.Defer).?;
   3050                 scope = defer_scope.parent;
   3051                 try gz.addDefer(defer_scope.index, defer_scope.len);
   3052             },
   3053             .defer_error => {
   3054                 const defer_scope = scope.cast(Scope.Defer).?;
   3055                 scope = defer_scope.parent;
   3056                 switch (which_ones) {
   3057                     .both_sans_err => {
   3058                         try gz.addDefer(defer_scope.index, defer_scope.len);
   3059                     },
   3060                     .both => |err_code| {
   3061                         if (defer_scope.remapped_err_code.unwrap()) |remapped_err_code| {
   3062                             try gz.instructions.ensureUnusedCapacity(gpa, 1);
   3063                             try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
   3064 
   3065                             const payload_index = try gz.astgen.addExtra(Zir.Inst.DeferErrCode{
   3066                                 .remapped_err_code = remapped_err_code,
   3067                                 .index = defer_scope.index,
   3068                                 .len = defer_scope.len,
   3069                             });
   3070                             const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
   3071                             gz.astgen.instructions.appendAssumeCapacity(.{
   3072                                 .tag = .defer_err_code,
   3073                                 .data = .{ .defer_err_code = .{
   3074                                     .err_code = err_code,
   3075                                     .payload_index = payload_index,
   3076                                 } },
   3077                             });
   3078                             gz.instructions.appendAssumeCapacity(new_index);
   3079                         } else {
   3080                             try gz.addDefer(defer_scope.index, defer_scope.len);
   3081                         }
   3082                     },
   3083                     .normal_only => continue,
   3084                 }
   3085             },
   3086             .namespace => unreachable,
   3087             .top => unreachable,
   3088         }
   3089     }
   3090 }
   3091 
   3092 fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!void {
   3093     const astgen = gz.astgen;
   3094 
   3095     var scope = inner_scope;
   3096     while (scope != outer_scope) {
   3097         switch (scope.tag) {
   3098             .gen_zir => scope = scope.cast(GenZir).?.parent,
   3099             .local_val => {
   3100                 const s = scope.cast(Scope.LocalVal).?;
   3101                 if (s.used == 0 and s.discarded == 0) {
   3102                     try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
   3103                 } else if (s.used != 0 and s.discarded != 0) {
   3104                     try astgen.appendErrorTokNotes(s.discarded, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{
   3105                         try gz.astgen.errNoteTok(s.used, "used here", .{}),
   3106                     });
   3107                 }
   3108                 scope = s.parent;
   3109             },
   3110             .local_ptr => {
   3111                 const s = scope.cast(Scope.LocalPtr).?;
   3112                 if (s.used == 0 and s.discarded == 0) {
   3113                     try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
   3114                 } else {
   3115                     if (s.used != 0 and s.discarded != 0) {
   3116                         try astgen.appendErrorTokNotes(s.discarded, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{
   3117                             try astgen.errNoteTok(s.used, "used here", .{}),
   3118                         });
   3119                     }
   3120                     if (s.id_cat == .@"local variable" and !s.used_as_lvalue) {
   3121                         try astgen.appendErrorTokNotes(s.token_src, "local variable is never mutated", .{}, &.{
   3122                             try astgen.errNoteTok(s.token_src, "consider using 'const'", .{}),
   3123                         });
   3124                     }
   3125                 }
   3126 
   3127                 scope = s.parent;
   3128             },
   3129             .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   3130             .namespace => unreachable,
   3131             .top => unreachable,
   3132         }
   3133     }
   3134 }
   3135 
   3136 fn deferStmt(
   3137     gz: *GenZir,
   3138     scope: *Scope,
   3139     node: Ast.Node.Index,
   3140     block_arena: Allocator,
   3141     scope_tag: Scope.Tag,
   3142 ) InnerError!*Scope {
   3143     var defer_gen = gz.makeSubBlock(scope);
   3144     defer_gen.cur_defer_node = node;
   3145     defer_gen.any_defer_node = node;
   3146     defer defer_gen.unstack();
   3147 
   3148     const tree = gz.astgen.tree;
   3149     const node_datas = tree.nodes.items(.data);
   3150     const expr_node = node_datas[node].rhs;
   3151 
   3152     const payload_token = node_datas[node].lhs;
   3153     var local_val_scope: Scope.LocalVal = undefined;
   3154     var opt_remapped_err_code: Zir.Inst.OptionalIndex = .none;
   3155     const have_err_code = scope_tag == .defer_error and payload_token != 0;
   3156     const sub_scope = if (!have_err_code) &defer_gen.base else blk: {
   3157         const ident_name = try gz.astgen.identAsString(payload_token);
   3158         if (std.mem.eql(u8, tree.tokenSlice(payload_token), "_")) {
   3159             return gz.astgen.failTok(payload_token, "discard of error capture; omit it instead", .{});
   3160         }
   3161         const remapped_err_code: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
   3162         opt_remapped_err_code = remapped_err_code.toOptional();
   3163         try gz.astgen.instructions.append(gz.astgen.gpa, .{
   3164             .tag = .extended,
   3165             .data = .{ .extended = .{
   3166                 .opcode = .value_placeholder,
   3167                 .small = undefined,
   3168                 .operand = undefined,
   3169             } },
   3170         });
   3171         const remapped_err_code_ref = remapped_err_code.toRef();
   3172         local_val_scope = .{
   3173             .parent = &defer_gen.base,
   3174             .gen_zir = gz,
   3175             .name = ident_name,
   3176             .inst = remapped_err_code_ref,
   3177             .token_src = payload_token,
   3178             .id_cat = .capture,
   3179         };
   3180         try gz.addDbgVar(.dbg_var_val, ident_name, remapped_err_code_ref);
   3181         break :blk &local_val_scope.base;
   3182     };
   3183     _ = try unusedResultExpr(&defer_gen, sub_scope, expr_node);
   3184     try checkUsed(gz, scope, sub_scope);
   3185     _ = try defer_gen.addBreak(.break_inline, @enumFromInt(0), .void_value);
   3186 
   3187     // We must handle ref_table for remapped_err_code manually.
   3188     const body = defer_gen.instructionsSlice();
   3189     const body_len = blk: {
   3190         var refs: u32 = 0;
   3191         if (opt_remapped_err_code.unwrap()) |remapped_err_code| {
   3192             var cur_inst = remapped_err_code;
   3193             while (gz.astgen.ref_table.get(cur_inst)) |ref_inst| {
   3194                 refs += 1;
   3195                 cur_inst = ref_inst;
   3196             }
   3197         }
   3198         break :blk gz.astgen.countBodyLenAfterFixups(body) + refs;
   3199     };
   3200 
   3201     const index: u32 = @intCast(gz.astgen.extra.items.len);
   3202     try gz.astgen.extra.ensureUnusedCapacity(gz.astgen.gpa, body_len);
   3203     if (opt_remapped_err_code.unwrap()) |remapped_err_code| {
   3204         if (gz.astgen.ref_table.fetchRemove(remapped_err_code)) |kv| {
   3205             gz.astgen.appendPossiblyRefdBodyInst(&gz.astgen.extra, kv.value);
   3206         }
   3207     }
   3208     gz.astgen.appendBodyWithFixups(body);
   3209 
   3210     const defer_scope = try block_arena.create(Scope.Defer);
   3211 
   3212     defer_scope.* = .{
   3213         .base = .{ .tag = scope_tag },
   3214         .parent = scope,
   3215         .index = index,
   3216         .len = body_len,
   3217         .remapped_err_code = opt_remapped_err_code,
   3218     };
   3219     return &defer_scope.base;
   3220 }
   3221 
   3222 fn varDecl(
   3223     gz: *GenZir,
   3224     scope: *Scope,
   3225     node: Ast.Node.Index,
   3226     block_arena: Allocator,
   3227     var_decl: Ast.full.VarDecl,
   3228 ) InnerError!*Scope {
   3229     try emitDbgNode(gz, node);
   3230     const astgen = gz.astgen;
   3231     const tree = astgen.tree;
   3232     const token_tags = tree.tokens.items(.tag);
   3233     const main_tokens = tree.nodes.items(.main_token);
   3234 
   3235     const name_token = var_decl.ast.mut_token + 1;
   3236     const ident_name_raw = tree.tokenSlice(name_token);
   3237     if (mem.eql(u8, ident_name_raw, "_")) {
   3238         return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{});
   3239     }
   3240     const ident_name = try astgen.identAsString(name_token);
   3241 
   3242     try astgen.detectLocalShadowing(
   3243         scope,
   3244         ident_name,
   3245         name_token,
   3246         ident_name_raw,
   3247         if (token_tags[var_decl.ast.mut_token] == .keyword_const) .@"local constant" else .@"local variable",
   3248     );
   3249 
   3250     if (var_decl.ast.init_node == 0) {
   3251         return astgen.failNode(node, "variables must be initialized", .{});
   3252     }
   3253 
   3254     if (var_decl.ast.addrspace_node != 0) {
   3255         return astgen.failTok(main_tokens[var_decl.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw});
   3256     }
   3257 
   3258     if (var_decl.ast.section_node != 0) {
   3259         return astgen.failTok(main_tokens[var_decl.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw});
   3260     }
   3261 
   3262     const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node != 0)
   3263         try expr(gz, scope, coerced_align_ri, var_decl.ast.align_node)
   3264     else
   3265         .none;
   3266 
   3267     switch (token_tags[var_decl.ast.mut_token]) {
   3268         .keyword_const => {
   3269             if (var_decl.comptime_token) |comptime_token| {
   3270                 try astgen.appendErrorTok(comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
   3271             }
   3272 
   3273             // Depending on the type of AST the initialization expression is, we may need an lvalue
   3274             // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
   3275             // the variable, no memory location needed.
   3276             const type_node = var_decl.ast.type_node;
   3277             if (align_inst == .none and
   3278                 !astgen.nodes_need_rl.contains(node))
   3279             {
   3280                 const result_info: ResultInfo = if (type_node != 0) .{
   3281                     .rl = .{ .ty = try typeExpr(gz, scope, type_node) },
   3282                     .ctx = .const_init,
   3283                 } else .{ .rl = .none, .ctx = .const_init };
   3284                 const prev_anon_name_strategy = gz.anon_name_strategy;
   3285                 gz.anon_name_strategy = .dbg_var;
   3286                 const init_inst = try reachableExpr(gz, scope, result_info, var_decl.ast.init_node, node);
   3287                 gz.anon_name_strategy = prev_anon_name_strategy;
   3288 
   3289                 try gz.addDbgVar(.dbg_var_val, ident_name, init_inst);
   3290 
   3291                 // The const init expression may have modified the error return trace, so signal
   3292                 // to Sema that it should save the new index for restoring later.
   3293                 if (nodeMayAppendToErrorTrace(tree, var_decl.ast.init_node))
   3294                     _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst });
   3295 
   3296                 const sub_scope = try block_arena.create(Scope.LocalVal);
   3297                 sub_scope.* = .{
   3298                     .parent = scope,
   3299                     .gen_zir = gz,
   3300                     .name = ident_name,
   3301                     .inst = init_inst,
   3302                     .token_src = name_token,
   3303                     .id_cat = .@"local constant",
   3304                 };
   3305                 return &sub_scope.base;
   3306             }
   3307 
   3308             const is_comptime = gz.is_comptime or
   3309                 tree.nodes.items(.tag)[var_decl.ast.init_node] == .@"comptime";
   3310 
   3311             var resolve_inferred_alloc: Zir.Inst.Ref = .none;
   3312             var opt_type_inst: Zir.Inst.Ref = .none;
   3313             const init_rl: ResultInfo.Loc = if (type_node != 0) init_rl: {
   3314                 const type_inst = try typeExpr(gz, scope, type_node);
   3315                 opt_type_inst = type_inst;
   3316                 if (align_inst == .none) {
   3317                     break :init_rl .{ .ptr = .{ .inst = try gz.addUnNode(.alloc, type_inst, node) } };
   3318                 } else {
   3319                     break :init_rl .{ .ptr = .{ .inst = try gz.addAllocExtended(.{
   3320                         .node = node,
   3321                         .type_inst = type_inst,
   3322                         .align_inst = align_inst,
   3323                         .is_const = true,
   3324                         .is_comptime = is_comptime,
   3325                     }) } };
   3326                 }
   3327             } else init_rl: {
   3328                 const alloc_inst = if (align_inst == .none) ptr: {
   3329                     const tag: Zir.Inst.Tag = if (is_comptime)
   3330                         .alloc_inferred_comptime
   3331                     else
   3332                         .alloc_inferred;
   3333                     break :ptr try gz.addNode(tag, node);
   3334                 } else ptr: {
   3335                     break :ptr try gz.addAllocExtended(.{
   3336                         .node = node,
   3337                         .type_inst = .none,
   3338                         .align_inst = align_inst,
   3339                         .is_const = true,
   3340                         .is_comptime = is_comptime,
   3341                     });
   3342                 };
   3343                 resolve_inferred_alloc = alloc_inst;
   3344                 break :init_rl .{ .inferred_ptr = alloc_inst };
   3345             };
   3346             const var_ptr = switch (init_rl) {
   3347                 .ptr => |ptr| ptr.inst,
   3348                 .inferred_ptr => |inst| inst,
   3349                 else => unreachable,
   3350             };
   3351             const init_result_info: ResultInfo = .{ .rl = init_rl, .ctx = .const_init };
   3352 
   3353             const prev_anon_name_strategy = gz.anon_name_strategy;
   3354             gz.anon_name_strategy = .dbg_var;
   3355             defer gz.anon_name_strategy = prev_anon_name_strategy;
   3356             const init_inst = try reachableExpr(gz, scope, init_result_info, var_decl.ast.init_node, node);
   3357 
   3358             // The const init expression may have modified the error return trace, so signal
   3359             // to Sema that it should save the new index for restoring later.
   3360             if (nodeMayAppendToErrorTrace(tree, var_decl.ast.init_node))
   3361                 _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst });
   3362 
   3363             const const_ptr = if (resolve_inferred_alloc != .none) p: {
   3364                 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
   3365                 break :p var_ptr;
   3366             } else try gz.addUnNode(.make_ptr_const, var_ptr, node);
   3367 
   3368             try gz.addDbgVar(.dbg_var_ptr, ident_name, const_ptr);
   3369 
   3370             const sub_scope = try block_arena.create(Scope.LocalPtr);
   3371             sub_scope.* = .{
   3372                 .parent = scope,
   3373                 .gen_zir = gz,
   3374                 .name = ident_name,
   3375                 .ptr = const_ptr,
   3376                 .token_src = name_token,
   3377                 .maybe_comptime = true,
   3378                 .id_cat = .@"local constant",
   3379             };
   3380             return &sub_scope.base;
   3381         },
   3382         .keyword_var => {
   3383             if (var_decl.comptime_token != null and gz.is_comptime)
   3384                 return astgen.failTok(var_decl.comptime_token.?, "'comptime var' is redundant in comptime scope", .{});
   3385             const is_comptime = var_decl.comptime_token != null or gz.is_comptime;
   3386             var resolve_inferred_alloc: Zir.Inst.Ref = .none;
   3387             const alloc: Zir.Inst.Ref, const result_info: ResultInfo = if (var_decl.ast.type_node != 0) a: {
   3388                 const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node);
   3389                 const alloc = alloc: {
   3390                     if (align_inst == .none) {
   3391                         const tag: Zir.Inst.Tag = if (is_comptime)
   3392                             .alloc_comptime_mut
   3393                         else
   3394                             .alloc_mut;
   3395                         break :alloc try gz.addUnNode(tag, type_inst, node);
   3396                     } else {
   3397                         break :alloc try gz.addAllocExtended(.{
   3398                             .node = node,
   3399                             .type_inst = type_inst,
   3400                             .align_inst = align_inst,
   3401                             .is_const = false,
   3402                             .is_comptime = is_comptime,
   3403                         });
   3404                     }
   3405                 };
   3406                 break :a .{ alloc, .{ .rl = .{ .ptr = .{ .inst = alloc } } } };
   3407             } else a: {
   3408                 const alloc = alloc: {
   3409                     if (align_inst == .none) {
   3410                         const tag: Zir.Inst.Tag = if (is_comptime)
   3411                             .alloc_inferred_comptime_mut
   3412                         else
   3413                             .alloc_inferred_mut;
   3414                         break :alloc try gz.addNode(tag, node);
   3415                     } else {
   3416                         break :alloc try gz.addAllocExtended(.{
   3417                             .node = node,
   3418                             .type_inst = .none,
   3419                             .align_inst = align_inst,
   3420                             .is_const = false,
   3421                             .is_comptime = is_comptime,
   3422                         });
   3423                     }
   3424                 };
   3425                 resolve_inferred_alloc = alloc;
   3426                 break :a .{ alloc, .{ .rl = .{ .inferred_ptr = alloc } } };
   3427             };
   3428             const prev_anon_name_strategy = gz.anon_name_strategy;
   3429             gz.anon_name_strategy = .dbg_var;
   3430             _ = try reachableExprComptime(gz, scope, result_info, var_decl.ast.init_node, node, is_comptime);
   3431             gz.anon_name_strategy = prev_anon_name_strategy;
   3432             if (resolve_inferred_alloc != .none) {
   3433                 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
   3434             }
   3435 
   3436             try gz.addDbgVar(.dbg_var_ptr, ident_name, alloc);
   3437 
   3438             const sub_scope = try block_arena.create(Scope.LocalPtr);
   3439             sub_scope.* = .{
   3440                 .parent = scope,
   3441                 .gen_zir = gz,
   3442                 .name = ident_name,
   3443                 .ptr = alloc,
   3444                 .token_src = name_token,
   3445                 .maybe_comptime = is_comptime,
   3446                 .id_cat = .@"local variable",
   3447             };
   3448             return &sub_scope.base;
   3449         },
   3450         else => unreachable,
   3451     }
   3452 }
   3453 
   3454 fn emitDbgNode(gz: *GenZir, node: Ast.Node.Index) !void {
   3455     // The instruction emitted here is for debugging runtime code.
   3456     // If the current block will be evaluated only during semantic analysis
   3457     // then no dbg_stmt ZIR instruction is needed.
   3458     if (gz.is_comptime) return;
   3459     const astgen = gz.astgen;
   3460     astgen.advanceSourceCursorToNode(node);
   3461     const line = astgen.source_line - gz.decl_line;
   3462     const column = astgen.source_column;
   3463     try emitDbgStmt(gz, .{ line, column });
   3464 }
   3465 
   3466 fn assign(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void {
   3467     try emitDbgNode(gz, infix_node);
   3468     const astgen = gz.astgen;
   3469     const tree = astgen.tree;
   3470     const node_datas = tree.nodes.items(.data);
   3471     const main_tokens = tree.nodes.items(.main_token);
   3472     const node_tags = tree.nodes.items(.tag);
   3473 
   3474     const lhs = node_datas[infix_node].lhs;
   3475     const rhs = node_datas[infix_node].rhs;
   3476     if (node_tags[lhs] == .identifier) {
   3477         // This intentionally does not support `@"_"` syntax.
   3478         const ident_name = tree.tokenSlice(main_tokens[lhs]);
   3479         if (mem.eql(u8, ident_name, "_")) {
   3480             _ = try expr(gz, scope, .{ .rl = .discard, .ctx = .assignment }, rhs);
   3481             return;
   3482         }
   3483     }
   3484     const lvalue = try lvalExpr(gz, scope, lhs);
   3485     _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{
   3486         .inst = lvalue,
   3487         .src_node = infix_node,
   3488     } } }, rhs);
   3489 }
   3490 
   3491 /// Handles destructure assignments where no LHS is a `const` or `var` decl.
   3492 fn assignDestructure(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!void {
   3493     try emitDbgNode(gz, node);
   3494     const astgen = gz.astgen;
   3495     const tree = astgen.tree;
   3496     const main_tokens = tree.nodes.items(.main_token);
   3497     const node_tags = tree.nodes.items(.tag);
   3498 
   3499     const full = tree.assignDestructure(node);
   3500     if (full.comptime_token != null and gz.is_comptime) {
   3501         return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
   3502     }
   3503 
   3504     // If this expression is marked comptime, we must wrap the whole thing in a comptime block.
   3505     var gz_buf: GenZir = undefined;
   3506     const inner_gz = if (full.comptime_token) |_| bs: {
   3507         gz_buf = gz.makeSubBlock(scope);
   3508         gz_buf.is_comptime = true;
   3509         break :bs &gz_buf;
   3510     } else gz;
   3511     defer if (full.comptime_token) |_| inner_gz.unstack();
   3512 
   3513     const rl_components = try astgen.arena.alloc(ResultInfo.Loc.DestructureComponent, full.ast.variables.len);
   3514     for (rl_components, full.ast.variables) |*variable_rl, variable_node| {
   3515         if (node_tags[variable_node] == .identifier) {
   3516             // This intentionally does not support `@"_"` syntax.
   3517             const ident_name = tree.tokenSlice(main_tokens[variable_node]);
   3518             if (mem.eql(u8, ident_name, "_")) {
   3519                 variable_rl.* = .discard;
   3520                 continue;
   3521             }
   3522         }
   3523         variable_rl.* = .{ .typed_ptr = .{
   3524             .inst = try lvalExpr(inner_gz, scope, variable_node),
   3525             .src_node = variable_node,
   3526         } };
   3527     }
   3528 
   3529     const ri: ResultInfo = .{ .rl = .{ .destructure = .{
   3530         .src_node = node,
   3531         .components = rl_components,
   3532     } } };
   3533 
   3534     _ = try expr(inner_gz, scope, ri, full.ast.value_expr);
   3535 
   3536     if (full.comptime_token) |_| {
   3537         const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node);
   3538         _ = try inner_gz.addBreak(.@"break", comptime_block_inst, .void_value);
   3539         try inner_gz.setBlockBody(comptime_block_inst);
   3540         try gz.instructions.append(gz.astgen.gpa, comptime_block_inst);
   3541     }
   3542 }
   3543 
   3544 /// Handles destructure assignments where the LHS may contain `const` or `var` decls.
   3545 fn assignDestructureMaybeDecls(
   3546     gz: *GenZir,
   3547     scope: *Scope,
   3548     node: Ast.Node.Index,
   3549     block_arena: Allocator,
   3550 ) InnerError!*Scope {
   3551     try emitDbgNode(gz, node);
   3552     const astgen = gz.astgen;
   3553     const tree = astgen.tree;
   3554     const token_tags = tree.tokens.items(.tag);
   3555     const main_tokens = tree.nodes.items(.main_token);
   3556     const node_tags = tree.nodes.items(.tag);
   3557 
   3558     const full = tree.assignDestructure(node);
   3559     if (full.comptime_token != null and gz.is_comptime) {
   3560         return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
   3561     }
   3562 
   3563     const is_comptime = full.comptime_token != null or gz.is_comptime;
   3564     const value_is_comptime = node_tags[full.ast.value_expr] == .@"comptime";
   3565 
   3566     // When declaring consts via a destructure, we always use a result pointer.
   3567     // This avoids the need to create tuple types, and is also likely easier to
   3568     // optimize, since it's a bit tricky for the optimizer to "split up" the
   3569     // value into individual pointer writes down the line.
   3570 
   3571     // We know this rl information won't live past the evaluation of this
   3572     // expression, so it may as well go in the block arena.
   3573     const rl_components = try block_arena.alloc(ResultInfo.Loc.DestructureComponent, full.ast.variables.len);
   3574     var any_non_const_variables = false;
   3575     var any_lvalue_expr = false;
   3576     for (rl_components, full.ast.variables) |*variable_rl, variable_node| {
   3577         switch (node_tags[variable_node]) {
   3578             .identifier => {
   3579                 // This intentionally does not support `@"_"` syntax.
   3580                 const ident_name = tree.tokenSlice(main_tokens[variable_node]);
   3581                 if (mem.eql(u8, ident_name, "_")) {
   3582                     any_non_const_variables = true;
   3583                     variable_rl.* = .discard;
   3584                     continue;
   3585                 }
   3586             },
   3587             .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => {
   3588                 const full_var_decl = tree.fullVarDecl(variable_node).?;
   3589 
   3590                 const name_token = full_var_decl.ast.mut_token + 1;
   3591                 const ident_name_raw = tree.tokenSlice(name_token);
   3592                 if (mem.eql(u8, ident_name_raw, "_")) {
   3593                     return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{});
   3594                 }
   3595 
   3596                 // We detect shadowing in the second pass over these, while we're creating scopes.
   3597 
   3598                 if (full_var_decl.ast.addrspace_node != 0) {
   3599                     return astgen.failTok(main_tokens[full_var_decl.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw});
   3600                 }
   3601                 if (full_var_decl.ast.section_node != 0) {
   3602                     return astgen.failTok(main_tokens[full_var_decl.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw});
   3603                 }
   3604 
   3605                 const is_const = switch (token_tags[full_var_decl.ast.mut_token]) {
   3606                     .keyword_var => false,
   3607                     .keyword_const => true,
   3608                     else => unreachable,
   3609                 };
   3610                 if (!is_const) any_non_const_variables = true;
   3611 
   3612                 // We also mark `const`s as comptime if the RHS is definitely comptime-known.
   3613                 const this_variable_comptime = is_comptime or (is_const and value_is_comptime);
   3614 
   3615                 const align_inst: Zir.Inst.Ref = if (full_var_decl.ast.align_node != 0)
   3616                     try expr(gz, scope, coerced_align_ri, full_var_decl.ast.align_node)
   3617                 else
   3618                     .none;
   3619 
   3620                 if (full_var_decl.ast.type_node != 0) {
   3621                     // Typed alloc
   3622                     const type_inst = try typeExpr(gz, scope, full_var_decl.ast.type_node);
   3623                     const ptr = if (align_inst == .none) ptr: {
   3624                         const tag: Zir.Inst.Tag = if (is_const)
   3625                             .alloc
   3626                         else if (this_variable_comptime)
   3627                             .alloc_comptime_mut
   3628                         else
   3629                             .alloc_mut;
   3630                         break :ptr try gz.addUnNode(tag, type_inst, node);
   3631                     } else try gz.addAllocExtended(.{
   3632                         .node = node,
   3633                         .type_inst = type_inst,
   3634                         .align_inst = align_inst,
   3635                         .is_const = is_const,
   3636                         .is_comptime = this_variable_comptime,
   3637                     });
   3638                     variable_rl.* = .{ .typed_ptr = .{ .inst = ptr } };
   3639                 } else {
   3640                     // Inferred alloc
   3641                     const ptr = if (align_inst == .none) ptr: {
   3642                         const tag: Zir.Inst.Tag = if (is_const) tag: {
   3643                             break :tag if (this_variable_comptime) .alloc_inferred_comptime else .alloc_inferred;
   3644                         } else tag: {
   3645                             break :tag if (this_variable_comptime) .alloc_inferred_comptime_mut else .alloc_inferred_mut;
   3646                         };
   3647                         break :ptr try gz.addNode(tag, node);
   3648                     } else try gz.addAllocExtended(.{
   3649                         .node = node,
   3650                         .type_inst = .none,
   3651                         .align_inst = align_inst,
   3652                         .is_const = is_const,
   3653                         .is_comptime = this_variable_comptime,
   3654                     });
   3655                     variable_rl.* = .{ .inferred_ptr = ptr };
   3656                 }
   3657 
   3658                 continue;
   3659             },
   3660             else => {},
   3661         }
   3662         // This variable is just an lvalue expression.
   3663         // We will fill in its result pointer later, inside a comptime block.
   3664         any_non_const_variables = true;
   3665         any_lvalue_expr = true;
   3666         variable_rl.* = .{ .typed_ptr = .{
   3667             .inst = undefined,
   3668             .src_node = variable_node,
   3669         } };
   3670     }
   3671 
   3672     if (full.comptime_token != null and !any_non_const_variables) {
   3673         try astgen.appendErrorTok(full.comptime_token.?, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
   3674     }
   3675 
   3676     // If this expression is marked comptime, we must wrap it in a comptime block.
   3677     var gz_buf: GenZir = undefined;
   3678     const inner_gz = if (full.comptime_token) |_| bs: {
   3679         gz_buf = gz.makeSubBlock(scope);
   3680         gz_buf.is_comptime = true;
   3681         break :bs &gz_buf;
   3682     } else gz;
   3683     defer if (full.comptime_token) |_| inner_gz.unstack();
   3684 
   3685     if (any_lvalue_expr) {
   3686         // At least one variable was an lvalue expr. Iterate again in order to
   3687         // evaluate the lvalues from within the possible block_comptime.
   3688         for (rl_components, full.ast.variables) |*variable_rl, variable_node| {
   3689             if (variable_rl.* != .typed_ptr) continue;
   3690             switch (node_tags[variable_node]) {
   3691                 .global_var_decl, .local_var_decl, .simple_var_decl, .aligned_var_decl => continue,
   3692                 else => {},
   3693             }
   3694             variable_rl.typed_ptr.inst = try lvalExpr(inner_gz, scope, variable_node);
   3695         }
   3696     }
   3697 
   3698     // We can't give a reasonable anon name strategy for destructured inits, so
   3699     // leave it at its default of `.anon`.
   3700     _ = try reachableExpr(inner_gz, scope, .{ .rl = .{ .destructure = .{
   3701         .src_node = node,
   3702         .components = rl_components,
   3703     } } }, full.ast.value_expr, node);
   3704 
   3705     if (full.comptime_token) |_| {
   3706         // Finish the block_comptime. Inferred alloc resolution etc will occur
   3707         // in the parent block.
   3708         const comptime_block_inst = try gz.makeBlockInst(.block_comptime, node);
   3709         _ = try inner_gz.addBreak(.@"break", comptime_block_inst, .void_value);
   3710         try inner_gz.setBlockBody(comptime_block_inst);
   3711         try gz.instructions.append(gz.astgen.gpa, comptime_block_inst);
   3712     }
   3713 
   3714     // Now, iterate over the variable exprs to construct any new scopes.
   3715     // If there were any inferred allocations, resolve them.
   3716     // If there were any `const` decls, make the pointer constant.
   3717     var cur_scope = scope;
   3718     for (rl_components, full.ast.variables) |variable_rl, variable_node| {
   3719         switch (node_tags[variable_node]) {
   3720             .local_var_decl, .simple_var_decl, .aligned_var_decl => {},
   3721             else => continue, // We were mutating an existing lvalue - nothing to do
   3722         }
   3723         const full_var_decl = tree.fullVarDecl(variable_node).?;
   3724         const raw_ptr = switch (variable_rl) {
   3725             .discard => unreachable,
   3726             .typed_ptr => |typed_ptr| typed_ptr.inst,
   3727             .inferred_ptr => |ptr_inst| ptr_inst,
   3728         };
   3729         // If the alloc was inferred, resolve it.
   3730         if (full_var_decl.ast.type_node == 0) {
   3731             _ = try gz.addUnNode(.resolve_inferred_alloc, raw_ptr, variable_node);
   3732         }
   3733         const is_const = switch (token_tags[full_var_decl.ast.mut_token]) {
   3734             .keyword_var => false,
   3735             .keyword_const => true,
   3736             else => unreachable,
   3737         };
   3738         // If the alloc was const, make it const.
   3739         const var_ptr = if (is_const and full_var_decl.ast.type_node != 0) make_const: {
   3740             // Note that we don't do this if type_node == 0 since `resolve_inferred_alloc`
   3741             // handles it for us.
   3742             break :make_const try gz.addUnNode(.make_ptr_const, raw_ptr, node);
   3743         } else raw_ptr;
   3744         const name_token = full_var_decl.ast.mut_token + 1;
   3745         const ident_name_raw = tree.tokenSlice(name_token);
   3746         const ident_name = try astgen.identAsString(name_token);
   3747         try astgen.detectLocalShadowing(
   3748             cur_scope,
   3749             ident_name,
   3750             name_token,
   3751             ident_name_raw,
   3752             if (is_const) .@"local constant" else .@"local variable",
   3753         );
   3754         try gz.addDbgVar(.dbg_var_ptr, ident_name, var_ptr);
   3755         // Finally, create the scope.
   3756         const sub_scope = try block_arena.create(Scope.LocalPtr);
   3757         sub_scope.* = .{
   3758             .parent = cur_scope,
   3759             .gen_zir = gz,
   3760             .name = ident_name,
   3761             .ptr = var_ptr,
   3762             .token_src = name_token,
   3763             .maybe_comptime = is_const or is_comptime,
   3764             .id_cat = if (is_const) .@"local constant" else .@"local variable",
   3765         };
   3766         cur_scope = &sub_scope.base;
   3767     }
   3768 
   3769     return cur_scope;
   3770 }
   3771 
   3772 fn assignOp(
   3773     gz: *GenZir,
   3774     scope: *Scope,
   3775     infix_node: Ast.Node.Index,
   3776     op_inst_tag: Zir.Inst.Tag,
   3777 ) InnerError!void {
   3778     try emitDbgNode(gz, infix_node);
   3779     const astgen = gz.astgen;
   3780     const tree = astgen.tree;
   3781     const node_datas = tree.nodes.items(.data);
   3782 
   3783     const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
   3784 
   3785     const cursor = switch (op_inst_tag) {
   3786         .add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, infix_node),
   3787         else => undefined,
   3788     };
   3789     const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
   3790 
   3791     const rhs_res_ty = switch (op_inst_tag) {
   3792         .add,
   3793         .sub,
   3794         => try gz.add(.{
   3795             .tag = .extended,
   3796             .data = .{ .extended = .{
   3797                 .opcode = .inplace_arith_result_ty,
   3798                 .small = @intFromEnum(@as(Zir.Inst.InplaceOp, switch (op_inst_tag) {
   3799                     .add => .add_eq,
   3800                     .sub => .sub_eq,
   3801                     else => unreachable,
   3802                 })),
   3803                 .operand = @intFromEnum(lhs),
   3804             } },
   3805         }),
   3806         else => try gz.addUnNode(.typeof, lhs, infix_node), // same as LHS type
   3807     };
   3808     // Not `coerced_ty` since `add`/etc won't coerce to this type.
   3809     const rhs = try expr(gz, scope, .{ .rl = .{ .ty = rhs_res_ty } }, node_datas[infix_node].rhs);
   3810 
   3811     switch (op_inst_tag) {
   3812         .add, .sub, .mul, .div, .mod_rem => {
   3813             try emitDbgStmt(gz, cursor);
   3814         },
   3815         else => {},
   3816     }
   3817     const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{
   3818         .lhs = lhs,
   3819         .rhs = rhs,
   3820     });
   3821     _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{
   3822         .lhs = lhs_ptr,
   3823         .rhs = result,
   3824     });
   3825 }
   3826 
   3827 fn assignShift(
   3828     gz: *GenZir,
   3829     scope: *Scope,
   3830     infix_node: Ast.Node.Index,
   3831     op_inst_tag: Zir.Inst.Tag,
   3832 ) InnerError!void {
   3833     try emitDbgNode(gz, infix_node);
   3834     const astgen = gz.astgen;
   3835     const tree = astgen.tree;
   3836     const node_datas = tree.nodes.items(.data);
   3837 
   3838     const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
   3839     const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
   3840     const rhs_type = try gz.addUnNode(.typeof_log2_int_type, lhs, infix_node);
   3841     const rhs = try expr(gz, scope, .{ .rl = .{ .ty = rhs_type } }, node_datas[infix_node].rhs);
   3842 
   3843     const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{
   3844         .lhs = lhs,
   3845         .rhs = rhs,
   3846     });
   3847     _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{
   3848         .lhs = lhs_ptr,
   3849         .rhs = result,
   3850     });
   3851 }
   3852 
   3853 fn assignShiftSat(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void {
   3854     try emitDbgNode(gz, infix_node);
   3855     const astgen = gz.astgen;
   3856     const tree = astgen.tree;
   3857     const node_datas = tree.nodes.items(.data);
   3858 
   3859     const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
   3860     const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
   3861     // Saturating shift-left allows any integer type for both the LHS and RHS.
   3862     const rhs = try expr(gz, scope, .{ .rl = .none }, node_datas[infix_node].rhs);
   3863 
   3864     const result = try gz.addPlNode(.shl_sat, infix_node, Zir.Inst.Bin{
   3865         .lhs = lhs,
   3866         .rhs = rhs,
   3867     });
   3868     _ = try gz.addPlNode(.store_node, infix_node, Zir.Inst.Bin{
   3869         .lhs = lhs_ptr,
   3870         .rhs = result,
   3871     });
   3872 }
   3873 
   3874 fn ptrType(
   3875     gz: *GenZir,
   3876     scope: *Scope,
   3877     ri: ResultInfo,
   3878     node: Ast.Node.Index,
   3879     ptr_info: Ast.full.PtrType,
   3880 ) InnerError!Zir.Inst.Ref {
   3881     if (ptr_info.size == .C and ptr_info.allowzero_token != null) {
   3882         return gz.astgen.failTok(ptr_info.allowzero_token.?, "C pointers always allow address zero", .{});
   3883     }
   3884 
   3885     const source_offset = gz.astgen.source_offset;
   3886     const source_line = gz.astgen.source_line;
   3887     const source_column = gz.astgen.source_column;
   3888     const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type);
   3889 
   3890     var sentinel_ref: Zir.Inst.Ref = .none;
   3891     var align_ref: Zir.Inst.Ref = .none;
   3892     var addrspace_ref: Zir.Inst.Ref = .none;
   3893     var bit_start_ref: Zir.Inst.Ref = .none;
   3894     var bit_end_ref: Zir.Inst.Ref = .none;
   3895     var trailing_count: u32 = 0;
   3896 
   3897     if (ptr_info.ast.sentinel != 0) {
   3898         // These attributes can appear in any order and they all come before the
   3899         // element type so we need to reset the source cursor before generating them.
   3900         gz.astgen.source_offset = source_offset;
   3901         gz.astgen.source_line = source_line;
   3902         gz.astgen.source_column = source_column;
   3903 
   3904         sentinel_ref = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, ptr_info.ast.sentinel);
   3905         trailing_count += 1;
   3906     }
   3907     if (ptr_info.ast.addrspace_node != 0) {
   3908         gz.astgen.source_offset = source_offset;
   3909         gz.astgen.source_line = source_line;
   3910         gz.astgen.source_column = source_column;
   3911 
   3912         const addrspace_ty = try gz.addBuiltinValue(ptr_info.ast.addrspace_node, .address_space);
   3913         addrspace_ref = try expr(gz, scope, .{ .rl = .{ .coerced_ty = addrspace_ty } }, ptr_info.ast.addrspace_node);
   3914         trailing_count += 1;
   3915     }
   3916     if (ptr_info.ast.align_node != 0) {
   3917         gz.astgen.source_offset = source_offset;
   3918         gz.astgen.source_line = source_line;
   3919         gz.astgen.source_column = source_column;
   3920 
   3921         align_ref = try expr(gz, scope, coerced_align_ri, ptr_info.ast.align_node);
   3922         trailing_count += 1;
   3923     }
   3924     if (ptr_info.ast.bit_range_start != 0) {
   3925         assert(ptr_info.ast.bit_range_end != 0);
   3926         bit_start_ref = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, ptr_info.ast.bit_range_start);
   3927         bit_end_ref = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, ptr_info.ast.bit_range_end);
   3928         trailing_count += 2;
   3929     }
   3930 
   3931     const gpa = gz.astgen.gpa;
   3932     try gz.instructions.ensureUnusedCapacity(gpa, 1);
   3933     try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
   3934     try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.PtrType).@"struct".fields.len +
   3935         trailing_count);
   3936 
   3937     const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.PtrType{
   3938         .elem_type = elem_type,
   3939         .src_node = gz.nodeIndexToRelative(node),
   3940     });
   3941     if (sentinel_ref != .none) {
   3942         gz.astgen.extra.appendAssumeCapacity(@intFromEnum(sentinel_ref));
   3943     }
   3944     if (align_ref != .none) {
   3945         gz.astgen.extra.appendAssumeCapacity(@intFromEnum(align_ref));
   3946     }
   3947     if (addrspace_ref != .none) {
   3948         gz.astgen.extra.appendAssumeCapacity(@intFromEnum(addrspace_ref));
   3949     }
   3950     if (bit_start_ref != .none) {
   3951         gz.astgen.extra.appendAssumeCapacity(@intFromEnum(bit_start_ref));
   3952         gz.astgen.extra.appendAssumeCapacity(@intFromEnum(bit_end_ref));
   3953     }
   3954 
   3955     const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
   3956     const result = new_index.toRef();
   3957     gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{
   3958         .ptr_type = .{
   3959             .flags = .{
   3960                 .is_allowzero = ptr_info.allowzero_token != null,
   3961                 .is_mutable = ptr_info.const_token == null,
   3962                 .is_volatile = ptr_info.volatile_token != null,
   3963                 .has_sentinel = sentinel_ref != .none,
   3964                 .has_align = align_ref != .none,
   3965                 .has_addrspace = addrspace_ref != .none,
   3966                 .has_bit_range = bit_start_ref != .none,
   3967             },
   3968             .size = ptr_info.size,
   3969             .payload_index = payload_index,
   3970         },
   3971     } });
   3972     gz.instructions.appendAssumeCapacity(new_index);
   3973 
   3974     return rvalue(gz, ri, result, node);
   3975 }
   3976 
   3977 fn arrayType(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) !Zir.Inst.Ref {
   3978     const astgen = gz.astgen;
   3979     const tree = astgen.tree;
   3980     const node_datas = tree.nodes.items(.data);
   3981     const node_tags = tree.nodes.items(.tag);
   3982     const main_tokens = tree.nodes.items(.main_token);
   3983 
   3984     const len_node = node_datas[node].lhs;
   3985     if (node_tags[len_node] == .identifier and
   3986         mem.eql(u8, tree.tokenSlice(main_tokens[len_node]), "_"))
   3987     {
   3988         return astgen.failNode(len_node, "unable to infer array size", .{});
   3989     }
   3990     const len = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, len_node);
   3991     const elem_type = try typeExpr(gz, scope, node_datas[node].rhs);
   3992 
   3993     const result = try gz.addPlNode(.array_type, node, Zir.Inst.Bin{
   3994         .lhs = len,
   3995         .rhs = elem_type,
   3996     });
   3997     return rvalue(gz, ri, result, node);
   3998 }
   3999 
   4000 fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) !Zir.Inst.Ref {
   4001     const astgen = gz.astgen;
   4002     const tree = astgen.tree;
   4003     const node_datas = tree.nodes.items(.data);
   4004     const node_tags = tree.nodes.items(.tag);
   4005     const main_tokens = tree.nodes.items(.main_token);
   4006     const extra = tree.extraData(node_datas[node].rhs, Ast.Node.ArrayTypeSentinel);
   4007 
   4008     const len_node = node_datas[node].lhs;
   4009     if (node_tags[len_node] == .identifier and
   4010         mem.eql(u8, tree.tokenSlice(main_tokens[len_node]), "_"))
   4011     {
   4012         return astgen.failNode(len_node, "unable to infer array size", .{});
   4013     }
   4014     const len = try reachableExpr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, len_node, node);
   4015     const elem_type = try typeExpr(gz, scope, extra.elem_type);
   4016     const sentinel = try reachableExprComptime(gz, scope, .{ .rl = .{ .coerced_ty = elem_type } }, extra.sentinel, node, true);
   4017 
   4018     const result = try gz.addPlNode(.array_type_sentinel, node, Zir.Inst.ArrayTypeSentinel{
   4019         .len = len,
   4020         .elem_type = elem_type,
   4021         .sentinel = sentinel,
   4022     });
   4023     return rvalue(gz, ri, result, node);
   4024 }
   4025 
   4026 const WipMembers = struct {
   4027     payload: *ArrayListUnmanaged(u32),
   4028     payload_top: usize,
   4029     field_bits_start: u32,
   4030     fields_start: u32,
   4031     fields_end: u32,
   4032     decl_index: u32 = 0,
   4033     field_index: u32 = 0,
   4034 
   4035     const Self = @This();
   4036 
   4037     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 {
   4038         const payload_top: u32 = @intCast(payload.items.len);
   4039         const field_bits_start = payload_top + decl_count;
   4040         const fields_start = field_bits_start + if (bits_per_field > 0) blk: {
   4041             const fields_per_u32 = 32 / bits_per_field;
   4042             break :blk (field_count + fields_per_u32 - 1) / fields_per_u32;
   4043         } else 0;
   4044         const payload_end = fields_start + field_count * max_field_size;
   4045         try payload.resize(gpa, payload_end);
   4046         return .{
   4047             .payload = payload,
   4048             .payload_top = payload_top,
   4049             .field_bits_start = field_bits_start,
   4050             .fields_start = fields_start,
   4051             .fields_end = fields_start,
   4052         };
   4053     }
   4054 
   4055     fn nextDecl(self: *Self, decl_inst: Zir.Inst.Index) void {
   4056         self.payload.items[self.payload_top + self.decl_index] = @intFromEnum(decl_inst);
   4057         self.decl_index += 1;
   4058     }
   4059 
   4060     fn nextField(self: *Self, comptime bits_per_field: u32, bits: [bits_per_field]bool) void {
   4061         const fields_per_u32 = 32 / bits_per_field;
   4062         const index = self.field_bits_start + self.field_index / fields_per_u32;
   4063         assert(index < self.fields_start);
   4064         var bit_bag: u32 = if (self.field_index % fields_per_u32 == 0) 0 else self.payload.items[index];
   4065         bit_bag >>= bits_per_field;
   4066         comptime var i = 0;
   4067         inline while (i < bits_per_field) : (i += 1) {
   4068             bit_bag |= @as(u32, @intFromBool(bits[i])) << (32 - bits_per_field + i);
   4069         }
   4070         self.payload.items[index] = bit_bag;
   4071         self.field_index += 1;
   4072     }
   4073 
   4074     fn appendToField(self: *Self, data: u32) void {
   4075         assert(self.fields_end < self.payload.items.len);
   4076         self.payload.items[self.fields_end] = data;
   4077         self.fields_end += 1;
   4078     }
   4079 
   4080     fn finishBits(self: *Self, comptime bits_per_field: u32) void {
   4081         if (bits_per_field > 0) {
   4082             const fields_per_u32 = 32 / bits_per_field;
   4083             const empty_field_slots = fields_per_u32 - (self.field_index % fields_per_u32);
   4084             if (self.field_index > 0 and empty_field_slots < fields_per_u32) {
   4085                 const index = self.field_bits_start + self.field_index / fields_per_u32;
   4086                 self.payload.items[index] >>= @intCast(empty_field_slots * bits_per_field);
   4087             }
   4088         }
   4089     }
   4090 
   4091     fn declsSlice(self: *Self) []u32 {
   4092         return self.payload.items[self.payload_top..][0..self.decl_index];
   4093     }
   4094 
   4095     fn fieldsSlice(self: *Self) []u32 {
   4096         return self.payload.items[self.field_bits_start..self.fields_end];
   4097     }
   4098 
   4099     fn deinit(self: *Self) void {
   4100         self.payload.items.len = self.payload_top;
   4101     }
   4102 };
   4103 
   4104 fn fnDecl(
   4105     astgen: *AstGen,
   4106     gz: *GenZir,
   4107     scope: *Scope,
   4108     wip_members: *WipMembers,
   4109     decl_node: Ast.Node.Index,
   4110     body_node: Ast.Node.Index,
   4111     fn_proto: Ast.full.FnProto,
   4112 ) InnerError!void {
   4113     const tree = astgen.tree;
   4114     const token_tags = tree.tokens.items(.tag);
   4115 
   4116     const old_hasher = astgen.src_hasher;
   4117     defer astgen.src_hasher = old_hasher;
   4118     astgen.src_hasher = std.zig.SrcHasher.init(.{});
   4119     // We don't add the full source yet, because we also need the prototype hash!
   4120     // The source slice is added towards the *end* of this function.
   4121     astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
   4122 
   4123     // missing function name already happened in scanContainer()
   4124     const fn_name_token = fn_proto.name_token orelse return error.AnalysisFail;
   4125 
   4126     // We insert this at the beginning so that its instruction index marks the
   4127     // start of the top level declaration.
   4128     const decl_inst = try gz.makeDeclaration(fn_proto.ast.proto_node);
   4129     astgen.advanceSourceCursorToNode(decl_node);
   4130 
   4131     var decl_gz: GenZir = .{
   4132         .is_comptime = true,
   4133         .decl_node_index = fn_proto.ast.proto_node,
   4134         .decl_line = astgen.source_line,
   4135         .parent = scope,
   4136         .astgen = astgen,
   4137         .instructions = gz.instructions,
   4138         .instructions_top = gz.instructions.items.len,
   4139     };
   4140     defer decl_gz.unstack();
   4141 
   4142     var fn_gz: GenZir = .{
   4143         .is_comptime = false,
   4144         .decl_node_index = fn_proto.ast.proto_node,
   4145         .decl_line = decl_gz.decl_line,
   4146         .parent = &decl_gz.base,
   4147         .astgen = astgen,
   4148         .instructions = gz.instructions,
   4149         .instructions_top = GenZir.unstacked_top,
   4150     };
   4151     defer fn_gz.unstack();
   4152 
   4153     // Set this now, since parameter types, return type, etc may be generic.
   4154     const prev_within_fn = astgen.within_fn;
   4155     defer astgen.within_fn = prev_within_fn;
   4156     astgen.within_fn = true;
   4157 
   4158     const is_pub = fn_proto.visib_token != null;
   4159     const is_export = blk: {
   4160         const maybe_export_token = fn_proto.extern_export_inline_token orelse break :blk false;
   4161         break :blk token_tags[maybe_export_token] == .keyword_export;
   4162     };
   4163     const is_extern = blk: {
   4164         const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false;
   4165         break :blk token_tags[maybe_extern_token] == .keyword_extern;
   4166     };
   4167     const has_inline_keyword = blk: {
   4168         const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;
   4169         break :blk token_tags[maybe_inline_token] == .keyword_inline;
   4170     };
   4171     const is_noinline = blk: {
   4172         const maybe_noinline_token = fn_proto.extern_export_inline_token orelse break :blk false;
   4173         break :blk token_tags[maybe_noinline_token] == .keyword_noinline;
   4174     };
   4175 
   4176     const doc_comment_index = try astgen.docCommentAsString(fn_proto.firstToken());
   4177 
   4178     wip_members.nextDecl(decl_inst);
   4179 
   4180     var noalias_bits: u32 = 0;
   4181     var params_scope = &fn_gz.base;
   4182     const is_var_args = is_var_args: {
   4183         var param_type_i: usize = 0;
   4184         var it = fn_proto.iterate(tree);
   4185         while (it.next()) |param| : (param_type_i += 1) {
   4186             const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) {
   4187                 .keyword_noalias => is_comptime: {
   4188                     noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse
   4189                         return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
   4190                     break :is_comptime false;
   4191                 },
   4192                 .keyword_comptime => true,
   4193                 else => false,
   4194             } else false;
   4195 
   4196             const is_anytype = if (param.anytype_ellipsis3) |token| blk: {
   4197                 switch (token_tags[token]) {
   4198                     .keyword_anytype => break :blk true,
   4199                     .ellipsis3 => break :is_var_args true,
   4200                     else => unreachable,
   4201                 }
   4202             } else false;
   4203 
   4204             const param_name: Zir.NullTerminatedString = if (param.name_token) |name_token| blk: {
   4205                 const name_bytes = tree.tokenSlice(name_token);
   4206                 if (mem.eql(u8, "_", name_bytes))
   4207                     break :blk .empty;
   4208 
   4209                 const param_name = try astgen.identAsString(name_token);
   4210                 if (!is_extern) {
   4211                     try astgen.detectLocalShadowing(params_scope, param_name, name_token, name_bytes, .@"function parameter");
   4212                 }
   4213                 break :blk param_name;
   4214             } else if (!is_extern) {
   4215                 if (param.anytype_ellipsis3) |tok| {
   4216                     return astgen.failTok(tok, "missing parameter name", .{});
   4217                 } else {
   4218                     ambiguous: {
   4219                         if (tree.nodes.items(.tag)[param.type_expr] != .identifier) break :ambiguous;
   4220                         const main_token = tree.nodes.items(.main_token)[param.type_expr];
   4221                         const identifier_str = tree.tokenSlice(main_token);
   4222                         if (isPrimitive(identifier_str)) break :ambiguous;
   4223                         return astgen.failNodeNotes(
   4224                             param.type_expr,
   4225                             "missing parameter name or type",
   4226                             .{},
   4227                             &[_]u32{
   4228                                 try astgen.errNoteNode(
   4229                                     param.type_expr,
   4230                                     "if this is a name, annotate its type '{s}: T'",
   4231                                     .{identifier_str},
   4232                                 ),
   4233                                 try astgen.errNoteNode(
   4234                                     param.type_expr,
   4235                                     "if this is a type, give it a name '<name>: {s}'",
   4236                                     .{identifier_str},
   4237                                 ),
   4238                             },
   4239                         );
   4240                     }
   4241                     return astgen.failNode(param.type_expr, "missing parameter name", .{});
   4242                 }
   4243             } else .empty;
   4244 
   4245             const param_inst = if (is_anytype) param: {
   4246                 const name_token = param.name_token orelse param.anytype_ellipsis3.?;
   4247                 const tag: Zir.Inst.Tag = if (is_comptime)
   4248                     .param_anytype_comptime
   4249                 else
   4250                     .param_anytype;
   4251                 break :param try decl_gz.addStrTok(tag, param_name, name_token);
   4252             } else param: {
   4253                 const param_type_node = param.type_expr;
   4254                 assert(param_type_node != 0);
   4255                 var param_gz = decl_gz.makeSubBlock(scope);
   4256                 defer param_gz.unstack();
   4257                 const param_type = try fullBodyExpr(&param_gz, params_scope, coerced_type_ri, param_type_node, .normal);
   4258                 const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1);
   4259                 _ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node);
   4260 
   4261                 const main_tokens = tree.nodes.items(.main_token);
   4262                 const name_token = param.name_token orelse main_tokens[param_type_node];
   4263                 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param;
   4264                 const param_inst = try decl_gz.addParam(&param_gz, tag, name_token, param_name, param.first_doc_comment);
   4265                 assert(param_inst_expected == param_inst);
   4266                 break :param param_inst.toRef();
   4267             };
   4268 
   4269             if (param_name == .empty or is_extern) continue;
   4270 
   4271             const sub_scope = try astgen.arena.create(Scope.LocalVal);
   4272             sub_scope.* = .{
   4273                 .parent = params_scope,
   4274                 .gen_zir = &decl_gz,
   4275                 .name = param_name,
   4276                 .inst = param_inst,
   4277                 .token_src = param.name_token.?,
   4278                 .id_cat = .@"function parameter",
   4279             };
   4280             params_scope = &sub_scope.base;
   4281         }
   4282         break :is_var_args false;
   4283     };
   4284 
   4285     const lib_name = if (fn_proto.lib_name) |lib_name_token| blk: {
   4286         const lib_name_str = try astgen.strLitAsString(lib_name_token);
   4287         const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len];
   4288         if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) {
   4289             return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{});
   4290         } else if (lib_name_str.len == 0) {
   4291             return astgen.failTok(lib_name_token, "library name cannot be empty", .{});
   4292         }
   4293         break :blk lib_name_str.index;
   4294     } else .empty;
   4295 
   4296     const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
   4297     const is_inferred_error = token_tags[maybe_bang] == .bang;
   4298 
   4299     // After creating the function ZIR instruction, it will need to update the break
   4300     // instructions inside the expression blocks for align, addrspace, cc, and ret_ty
   4301     // to use the function instruction as the "block" to break from.
   4302 
   4303     var align_gz = decl_gz.makeSubBlock(params_scope);
   4304     defer align_gz.unstack();
   4305     const align_ref: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
   4306         const inst = try expr(&decl_gz, params_scope, coerced_align_ri, fn_proto.ast.align_expr);
   4307         if (align_gz.instructionsSlice().len == 0) {
   4308             // In this case we will send a len=0 body which can be encoded more efficiently.
   4309             break :inst inst;
   4310         }
   4311         _ = try align_gz.addBreak(.break_inline, @enumFromInt(0), inst);
   4312         break :inst inst;
   4313     };
   4314 
   4315     var addrspace_gz = decl_gz.makeSubBlock(params_scope);
   4316     defer addrspace_gz.unstack();
   4317     const addrspace_ref: Zir.Inst.Ref = if (fn_proto.ast.addrspace_expr == 0) .none else inst: {
   4318         const addrspace_ty = try decl_gz.addBuiltinValue(fn_proto.ast.addrspace_expr, .address_space);
   4319         const inst = try expr(&decl_gz, params_scope, .{ .rl = .{ .coerced_ty = addrspace_ty } }, fn_proto.ast.addrspace_expr);
   4320         if (addrspace_gz.instructionsSlice().len == 0) {
   4321             // In this case we will send a len=0 body which can be encoded more efficiently.
   4322             break :inst inst;
   4323         }
   4324         _ = try addrspace_gz.addBreak(.break_inline, @enumFromInt(0), inst);
   4325         break :inst inst;
   4326     };
   4327 
   4328     var section_gz = decl_gz.makeSubBlock(params_scope);
   4329     defer section_gz.unstack();
   4330     const section_ref: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: {
   4331         const inst = try expr(&decl_gz, params_scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, fn_proto.ast.section_expr);
   4332         if (section_gz.instructionsSlice().len == 0) {
   4333             // In this case we will send a len=0 body which can be encoded more efficiently.
   4334             break :inst inst;
   4335         }
   4336         _ = try section_gz.addBreak(.break_inline, @enumFromInt(0), inst);
   4337         break :inst inst;
   4338     };
   4339 
   4340     var cc_gz = decl_gz.makeSubBlock(params_scope);
   4341     defer cc_gz.unstack();
   4342     const cc_ref: Zir.Inst.Ref = blk: {
   4343         if (fn_proto.ast.callconv_expr != 0) {
   4344             if (has_inline_keyword) {
   4345                 return astgen.failNode(
   4346                     fn_proto.ast.callconv_expr,
   4347                     "explicit callconv incompatible with inline keyword",
   4348                     .{},
   4349                 );
   4350             }
   4351             const inst = try expr(
   4352                 &cc_gz,
   4353                 params_scope,
   4354                 .{ .rl = .{ .coerced_ty = try cc_gz.addBuiltinValue(fn_proto.ast.callconv_expr, .calling_convention) } },
   4355                 fn_proto.ast.callconv_expr,
   4356             );
   4357             if (cc_gz.instructionsSlice().len == 0) {
   4358                 // In this case we will send a len=0 body which can be encoded more efficiently.
   4359                 break :blk inst;
   4360             }
   4361             _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst);
   4362             break :blk inst;
   4363         } else if (is_extern) {
   4364             const inst = try cc_gz.addBuiltinValue(decl_node, .calling_convention_c);
   4365             _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst);
   4366             break :blk inst;
   4367         } else if (has_inline_keyword) {
   4368             const inst = try cc_gz.addBuiltinValue(decl_node, .calling_convention_inline);
   4369             _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst);
   4370             break :blk inst;
   4371         } else {
   4372             break :blk .none;
   4373         }
   4374     };
   4375 
   4376     var ret_gz = decl_gz.makeSubBlock(params_scope);
   4377     defer ret_gz.unstack();
   4378     const ret_ref: Zir.Inst.Ref = inst: {
   4379         const inst = try fullBodyExpr(&ret_gz, params_scope, coerced_type_ri, fn_proto.ast.return_type, .normal);
   4380         if (ret_gz.instructionsSlice().len == 0) {
   4381             // In this case we will send a len=0 body which can be encoded more efficiently.
   4382             break :inst inst;
   4383         }
   4384         _ = try ret_gz.addBreak(.break_inline, @enumFromInt(0), inst);
   4385         break :inst inst;
   4386     };
   4387 
   4388     const func_inst: Zir.Inst.Ref = if (body_node == 0) func: {
   4389         if (!is_extern) {
   4390             return astgen.failTok(fn_proto.ast.fn_token, "non-extern function has no body", .{});
   4391         }
   4392         if (is_inferred_error) {
   4393             return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{});
   4394         }
   4395         break :func try decl_gz.addFunc(.{
   4396             .src_node = decl_node,
   4397             .cc_ref = cc_ref,
   4398             .cc_gz = &cc_gz,
   4399             .align_ref = align_ref,
   4400             .align_gz = &align_gz,
   4401             .ret_ref = ret_ref,
   4402             .ret_gz = &ret_gz,
   4403             .section_ref = section_ref,
   4404             .section_gz = &section_gz,
   4405             .addrspace_ref = addrspace_ref,
   4406             .addrspace_gz = &addrspace_gz,
   4407             .param_block = decl_inst,
   4408             .body_gz = null,
   4409             .lib_name = lib_name,
   4410             .is_var_args = is_var_args,
   4411             .is_inferred_error = false,
   4412             .is_test = false,
   4413             .is_extern = true,
   4414             .is_noinline = is_noinline,
   4415             .noalias_bits = noalias_bits,
   4416             .proto_hash = undefined, // ignored for `body_gz == null`
   4417         });
   4418     } else func: {
   4419         // as a scope, fn_gz encloses ret_gz, but for instruction list, fn_gz stacks on ret_gz
   4420         fn_gz.instructions_top = ret_gz.instructions.items.len;
   4421 
   4422         // Construct the prototype hash.
   4423         // Leave `astgen.src_hasher` unmodified; this will be used for hashing
   4424         // the *whole* function declaration, including its body.
   4425         var proto_hasher = astgen.src_hasher;
   4426         const proto_node = tree.nodes.items(.data)[decl_node].lhs;
   4427         proto_hasher.update(tree.getNodeSource(proto_node));
   4428         var proto_hash: std.zig.SrcHash = undefined;
   4429         proto_hasher.final(&proto_hash);
   4430 
   4431         const prev_fn_block = astgen.fn_block;
   4432         const prev_fn_ret_ty = astgen.fn_ret_ty;
   4433         defer {
   4434             astgen.fn_block = prev_fn_block;
   4435             astgen.fn_ret_ty = prev_fn_ret_ty;
   4436         }
   4437         astgen.fn_block = &fn_gz;
   4438         astgen.fn_ret_ty = if (is_inferred_error or ret_ref.toIndex() != null) r: {
   4439             // We're essentially guaranteed to need the return type at some point,
   4440             // since the return type is likely not `void` or `noreturn` so there
   4441             // will probably be an explicit return requiring RLS. Fetch this
   4442             // return type now so the rest of the function can use it.
   4443             break :r try fn_gz.addNode(.ret_type, decl_node);
   4444         } else ret_ref;
   4445 
   4446         const prev_var_args = astgen.fn_var_args;
   4447         astgen.fn_var_args = is_var_args;
   4448         defer astgen.fn_var_args = prev_var_args;
   4449 
   4450         astgen.advanceSourceCursorToNode(body_node);
   4451         const lbrace_line = astgen.source_line - decl_gz.decl_line;
   4452         const lbrace_column = astgen.source_column;
   4453 
   4454         _ = try fullBodyExpr(&fn_gz, params_scope, .{ .rl = .none }, body_node, .allow_branch_hint);
   4455         try checkUsed(gz, &fn_gz.base, params_scope);
   4456 
   4457         if (!fn_gz.endsWithNoReturn()) {
   4458             // As our last action before the return, "pop" the error trace if needed
   4459             _ = try fn_gz.addRestoreErrRetIndex(.ret, .always, decl_node);
   4460 
   4461             // Add implicit return at end of function.
   4462             _ = try fn_gz.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node));
   4463         }
   4464 
   4465         break :func try decl_gz.addFunc(.{
   4466             .src_node = decl_node,
   4467             .cc_ref = cc_ref,
   4468             .cc_gz = &cc_gz,
   4469             .align_ref = align_ref,
   4470             .align_gz = &align_gz,
   4471             .ret_ref = ret_ref,
   4472             .ret_gz = &ret_gz,
   4473             .section_ref = section_ref,
   4474             .section_gz = &section_gz,
   4475             .addrspace_ref = addrspace_ref,
   4476             .addrspace_gz = &addrspace_gz,
   4477             .lbrace_line = lbrace_line,
   4478             .lbrace_column = lbrace_column,
   4479             .param_block = decl_inst,
   4480             .body_gz = &fn_gz,
   4481             .lib_name = lib_name,
   4482             .is_var_args = is_var_args,
   4483             .is_inferred_error = is_inferred_error,
   4484             .is_test = false,
   4485             .is_extern = false,
   4486             .is_noinline = is_noinline,
   4487             .noalias_bits = noalias_bits,
   4488             .proto_hash = proto_hash,
   4489         });
   4490     };
   4491 
   4492     // *Now* we can incorporate the full source code into the hasher.
   4493     astgen.src_hasher.update(tree.getNodeSource(decl_node));
   4494 
   4495     // We add this at the end so that its instruction index marks the end range
   4496     // of the top level declaration. addFunc already unstacked fn_gz and ret_gz.
   4497     _ = try decl_gz.addBreak(.break_inline, decl_inst, func_inst);
   4498 
   4499     var hash: std.zig.SrcHash = undefined;
   4500     astgen.src_hasher.final(&hash);
   4501     try setDeclaration(
   4502         decl_inst,
   4503         hash,
   4504         .{ .named = fn_name_token },
   4505         decl_gz.decl_line,
   4506         is_pub,
   4507         is_export,
   4508         doc_comment_index,
   4509         &decl_gz,
   4510         // align, linksection, and addrspace are passed in the func instruction in this case.
   4511         // TODO: move them from the function instruction to the declaration instruction?
   4512         null,
   4513     );
   4514 }
   4515 
   4516 fn globalVarDecl(
   4517     astgen: *AstGen,
   4518     gz: *GenZir,
   4519     scope: *Scope,
   4520     wip_members: *WipMembers,
   4521     node: Ast.Node.Index,
   4522     var_decl: Ast.full.VarDecl,
   4523 ) InnerError!void {
   4524     const tree = astgen.tree;
   4525     const token_tags = tree.tokens.items(.tag);
   4526 
   4527     const old_hasher = astgen.src_hasher;
   4528     defer astgen.src_hasher = old_hasher;
   4529     astgen.src_hasher = std.zig.SrcHasher.init(.{});
   4530     astgen.src_hasher.update(tree.getNodeSource(node));
   4531     astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
   4532 
   4533     const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var;
   4534     // We do this at the beginning so that the instruction index marks the range start
   4535     // of the top level declaration.
   4536     const decl_inst = try gz.makeDeclaration(node);
   4537 
   4538     const name_token = var_decl.ast.mut_token + 1;
   4539     astgen.advanceSourceCursorToNode(node);
   4540 
   4541     var block_scope: GenZir = .{
   4542         .parent = scope,
   4543         .decl_node_index = node,
   4544         .decl_line = astgen.source_line,
   4545         .astgen = astgen,
   4546         .is_comptime = true,
   4547         .instructions = gz.instructions,
   4548         .instructions_top = gz.instructions.items.len,
   4549     };
   4550     defer block_scope.unstack();
   4551 
   4552     const is_pub = var_decl.visib_token != null;
   4553     const is_export = blk: {
   4554         const maybe_export_token = var_decl.extern_export_token orelse break :blk false;
   4555         break :blk token_tags[maybe_export_token] == .keyword_export;
   4556     };
   4557     const is_extern = blk: {
   4558         const maybe_extern_token = var_decl.extern_export_token orelse break :blk false;
   4559         break :blk token_tags[maybe_extern_token] == .keyword_extern;
   4560     };
   4561     wip_members.nextDecl(decl_inst);
   4562 
   4563     const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: {
   4564         if (!is_mutable) {
   4565             return astgen.failTok(tok, "threadlocal variable cannot be constant", .{});
   4566         }
   4567         break :blk true;
   4568     } else false;
   4569 
   4570     const lib_name = if (var_decl.lib_name) |lib_name_token| blk: {
   4571         const lib_name_str = try astgen.strLitAsString(lib_name_token);
   4572         const lib_name_slice = astgen.string_bytes.items[@intFromEnum(lib_name_str.index)..][0..lib_name_str.len];
   4573         if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) {
   4574             return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{});
   4575         } else if (lib_name_str.len == 0) {
   4576             return astgen.failTok(lib_name_token, "library name cannot be empty", .{});
   4577         }
   4578         break :blk lib_name_str.index;
   4579     } else .empty;
   4580 
   4581     const doc_comment_index = try astgen.docCommentAsString(var_decl.firstToken());
   4582 
   4583     assert(var_decl.comptime_token == null); // handled by parser
   4584 
   4585     const var_inst: Zir.Inst.Ref = if (var_decl.ast.init_node != 0) vi: {
   4586         if (is_extern) {
   4587             return astgen.failNode(
   4588                 var_decl.ast.init_node,
   4589                 "extern variables have no initializers",
   4590                 .{},
   4591             );
   4592         }
   4593 
   4594         const type_inst: Zir.Inst.Ref = if (var_decl.ast.type_node != 0)
   4595             try expr(
   4596                 &block_scope,
   4597                 &block_scope.base,
   4598                 coerced_type_ri,
   4599                 var_decl.ast.type_node,
   4600             )
   4601         else
   4602             .none;
   4603 
   4604         block_scope.anon_name_strategy = .parent;
   4605 
   4606         const init_inst = try expr(
   4607             &block_scope,
   4608             &block_scope.base,
   4609             if (type_inst != .none) .{ .rl = .{ .ty = type_inst } } else .{ .rl = .none },
   4610             var_decl.ast.init_node,
   4611         );
   4612 
   4613         if (is_mutable) {
   4614             const var_inst = try block_scope.addVar(.{
   4615                 .var_type = type_inst,
   4616                 .lib_name = .empty,
   4617                 .align_inst = .none, // passed via the decls data
   4618                 .init = init_inst,
   4619                 .is_extern = false,
   4620                 .is_const = !is_mutable,
   4621                 .is_threadlocal = is_threadlocal,
   4622             });
   4623             break :vi var_inst;
   4624         } else {
   4625             break :vi init_inst;
   4626         }
   4627     } else if (!is_extern) {
   4628         return astgen.failNode(node, "variables must be initialized", .{});
   4629     } else if (var_decl.ast.type_node != 0) vi: {
   4630         // Extern variable which has an explicit type.
   4631         const type_inst = try typeExpr(&block_scope, &block_scope.base, var_decl.ast.type_node);
   4632 
   4633         block_scope.anon_name_strategy = .parent;
   4634 
   4635         const var_inst = try block_scope.addVar(.{
   4636             .var_type = type_inst,
   4637             .lib_name = lib_name,
   4638             .align_inst = .none, // passed via the decls data
   4639             .init = .none,
   4640             .is_extern = true,
   4641             .is_const = !is_mutable,
   4642             .is_threadlocal = is_threadlocal,
   4643         });
   4644         break :vi var_inst;
   4645     } else {
   4646         return astgen.failNode(node, "unable to infer variable type", .{});
   4647     };
   4648 
   4649     // We do this at the end so that the instruction index marks the end
   4650     // range of a top level declaration.
   4651     _ = try block_scope.addBreakWithSrcNode(.break_inline, decl_inst, var_inst, node);
   4652 
   4653     var align_gz = block_scope.makeSubBlock(scope);
   4654     if (var_decl.ast.align_node != 0) {
   4655         const align_inst = try fullBodyExpr(&align_gz, &align_gz.base, coerced_align_ri, var_decl.ast.align_node, .normal);
   4656         _ = try align_gz.addBreakWithSrcNode(.break_inline, decl_inst, align_inst, node);
   4657     }
   4658 
   4659     var linksection_gz = align_gz.makeSubBlock(scope);
   4660     if (var_decl.ast.section_node != 0) {
   4661         const linksection_inst = try fullBodyExpr(&linksection_gz, &linksection_gz.base, coerced_linksection_ri, var_decl.ast.section_node, .normal);
   4662         _ = try linksection_gz.addBreakWithSrcNode(.break_inline, decl_inst, linksection_inst, node);
   4663     }
   4664 
   4665     var addrspace_gz = linksection_gz.makeSubBlock(scope);
   4666     if (var_decl.ast.addrspace_node != 0) {
   4667         const addrspace_ty = try addrspace_gz.addBuiltinValue(var_decl.ast.addrspace_node, .address_space);
   4668         const addrspace_inst = try fullBodyExpr(&addrspace_gz, &addrspace_gz.base, .{ .rl = .{ .coerced_ty = addrspace_ty } }, var_decl.ast.addrspace_node, .normal);
   4669         _ = try addrspace_gz.addBreakWithSrcNode(.break_inline, decl_inst, addrspace_inst, node);
   4670     }
   4671 
   4672     var hash: std.zig.SrcHash = undefined;
   4673     astgen.src_hasher.final(&hash);
   4674     try setDeclaration(
   4675         decl_inst,
   4676         hash,
   4677         .{ .named = name_token },
   4678         block_scope.decl_line,
   4679         is_pub,
   4680         is_export,
   4681         doc_comment_index,
   4682         &block_scope,
   4683         .{
   4684             .align_gz = &align_gz,
   4685             .linksection_gz = &linksection_gz,
   4686             .addrspace_gz = &addrspace_gz,
   4687         },
   4688     );
   4689 }
   4690 
   4691 fn comptimeDecl(
   4692     astgen: *AstGen,
   4693     gz: *GenZir,
   4694     scope: *Scope,
   4695     wip_members: *WipMembers,
   4696     node: Ast.Node.Index,
   4697 ) InnerError!void {
   4698     const tree = astgen.tree;
   4699     const node_datas = tree.nodes.items(.data);
   4700     const body_node = node_datas[node].lhs;
   4701 
   4702     const old_hasher = astgen.src_hasher;
   4703     defer astgen.src_hasher = old_hasher;
   4704     astgen.src_hasher = std.zig.SrcHasher.init(.{});
   4705     astgen.src_hasher.update(tree.getNodeSource(node));
   4706     astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
   4707 
   4708     // Up top so the ZIR instruction index marks the start range of this
   4709     // top-level declaration.
   4710     const decl_inst = try gz.makeDeclaration(node);
   4711     wip_members.nextDecl(decl_inst);
   4712     astgen.advanceSourceCursorToNode(node);
   4713 
   4714     var decl_block: GenZir = .{
   4715         .is_comptime = true,
   4716         .decl_node_index = node,
   4717         .decl_line = astgen.source_line,
   4718         .parent = scope,
   4719         .astgen = astgen,
   4720         .instructions = gz.instructions,
   4721         .instructions_top = gz.instructions.items.len,
   4722     };
   4723     defer decl_block.unstack();
   4724 
   4725     const block_result = try fullBodyExpr(&decl_block, &decl_block.base, .{ .rl = .none }, body_node, .normal);
   4726     if (decl_block.isEmpty() or !decl_block.refIsNoReturn(block_result)) {
   4727         _ = try decl_block.addBreak(.break_inline, decl_inst, .void_value);
   4728     }
   4729 
   4730     var hash: std.zig.SrcHash = undefined;
   4731     astgen.src_hasher.final(&hash);
   4732     try setDeclaration(
   4733         decl_inst,
   4734         hash,
   4735         .@"comptime",
   4736         decl_block.decl_line,
   4737         false,
   4738         false,
   4739         .empty,
   4740         &decl_block,
   4741         null,
   4742     );
   4743 }
   4744 
   4745 fn usingnamespaceDecl(
   4746     astgen: *AstGen,
   4747     gz: *GenZir,
   4748     scope: *Scope,
   4749     wip_members: *WipMembers,
   4750     node: Ast.Node.Index,
   4751 ) InnerError!void {
   4752     const tree = astgen.tree;
   4753     const node_datas = tree.nodes.items(.data);
   4754 
   4755     const old_hasher = astgen.src_hasher;
   4756     defer astgen.src_hasher = old_hasher;
   4757     astgen.src_hasher = std.zig.SrcHasher.init(.{});
   4758     astgen.src_hasher.update(tree.getNodeSource(node));
   4759     astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
   4760 
   4761     const type_expr = node_datas[node].lhs;
   4762     const is_pub = blk: {
   4763         const main_tokens = tree.nodes.items(.main_token);
   4764         const token_tags = tree.tokens.items(.tag);
   4765         const main_token = main_tokens[node];
   4766         break :blk (main_token > 0 and token_tags[main_token - 1] == .keyword_pub);
   4767     };
   4768     // Up top so the ZIR instruction index marks the start range of this
   4769     // top-level declaration.
   4770     const decl_inst = try gz.makeDeclaration(node);
   4771     wip_members.nextDecl(decl_inst);
   4772     astgen.advanceSourceCursorToNode(node);
   4773 
   4774     var decl_block: GenZir = .{
   4775         .is_comptime = true,
   4776         .decl_node_index = node,
   4777         .decl_line = astgen.source_line,
   4778         .parent = scope,
   4779         .astgen = astgen,
   4780         .instructions = gz.instructions,
   4781         .instructions_top = gz.instructions.items.len,
   4782     };
   4783     defer decl_block.unstack();
   4784 
   4785     const namespace_inst = try typeExpr(&decl_block, &decl_block.base, type_expr);
   4786     _ = try decl_block.addBreak(.break_inline, decl_inst, namespace_inst);
   4787 
   4788     var hash: std.zig.SrcHash = undefined;
   4789     astgen.src_hasher.final(&hash);
   4790     try setDeclaration(
   4791         decl_inst,
   4792         hash,
   4793         .@"usingnamespace",
   4794         decl_block.decl_line,
   4795         is_pub,
   4796         false,
   4797         .empty,
   4798         &decl_block,
   4799         null,
   4800     );
   4801 }
   4802 
   4803 fn testDecl(
   4804     astgen: *AstGen,
   4805     gz: *GenZir,
   4806     scope: *Scope,
   4807     wip_members: *WipMembers,
   4808     node: Ast.Node.Index,
   4809 ) InnerError!void {
   4810     const tree = astgen.tree;
   4811     const node_datas = tree.nodes.items(.data);
   4812     const body_node = node_datas[node].rhs;
   4813 
   4814     const old_hasher = astgen.src_hasher;
   4815     defer astgen.src_hasher = old_hasher;
   4816     astgen.src_hasher = std.zig.SrcHasher.init(.{});
   4817     astgen.src_hasher.update(tree.getNodeSource(node));
   4818     astgen.src_hasher.update(std.mem.asBytes(&astgen.source_column));
   4819 
   4820     // Up top so the ZIR instruction index marks the start range of this
   4821     // top-level declaration.
   4822     const decl_inst = try gz.makeDeclaration(node);
   4823 
   4824     wip_members.nextDecl(decl_inst);
   4825     astgen.advanceSourceCursorToNode(node);
   4826 
   4827     var decl_block: GenZir = .{
   4828         .is_comptime = true,
   4829         .decl_node_index = node,
   4830         .decl_line = astgen.source_line,
   4831         .parent = scope,
   4832         .astgen = astgen,
   4833         .instructions = gz.instructions,
   4834         .instructions_top = gz.instructions.items.len,
   4835     };
   4836     defer decl_block.unstack();
   4837 
   4838     const main_tokens = tree.nodes.items(.main_token);
   4839     const token_tags = tree.tokens.items(.tag);
   4840     const test_token = main_tokens[node];
   4841     const test_name_token = test_token + 1;
   4842     const test_name: DeclarationName = switch (token_tags[test_name_token]) {
   4843         else => .unnamed_test,
   4844         .string_literal => .{ .named_test = test_name_token },
   4845         .identifier => blk: {
   4846             const ident_name_raw = tree.tokenSlice(test_name_token);
   4847 
   4848             if (mem.eql(u8, ident_name_raw, "_")) return astgen.failTok(test_name_token, "'_' used as an identifier without @\"_\" syntax", .{});
   4849 
   4850             // if not @"" syntax, just use raw token slice
   4851             if (ident_name_raw[0] != '@') {
   4852                 if (isPrimitive(ident_name_raw)) return astgen.failTok(test_name_token, "cannot test a primitive", .{});
   4853             }
   4854 
   4855             // Local variables, including function parameters.
   4856             const name_str_index = try astgen.identAsString(test_name_token);
   4857             var s = scope;
   4858             var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
   4859             var num_namespaces_out: u32 = 0;
   4860             var capturing_namespace: ?*Scope.Namespace = null;
   4861             while (true) switch (s.tag) {
   4862                 .local_val => {
   4863                     const local_val = s.cast(Scope.LocalVal).?;
   4864                     if (local_val.name == name_str_index) {
   4865                         local_val.used = test_name_token;
   4866                         return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{
   4867                             @tagName(local_val.id_cat),
   4868                         }, &[_]u32{
   4869                             try astgen.errNoteTok(local_val.token_src, "{s} declared here", .{
   4870                                 @tagName(local_val.id_cat),
   4871                             }),
   4872                         });
   4873                     }
   4874                     s = local_val.parent;
   4875                 },
   4876                 .local_ptr => {
   4877                     const local_ptr = s.cast(Scope.LocalPtr).?;
   4878                     if (local_ptr.name == name_str_index) {
   4879                         local_ptr.used = test_name_token;
   4880                         return astgen.failTokNotes(test_name_token, "cannot test a {s}", .{
   4881                             @tagName(local_ptr.id_cat),
   4882                         }, &[_]u32{
   4883                             try astgen.errNoteTok(local_ptr.token_src, "{s} declared here", .{
   4884                                 @tagName(local_ptr.id_cat),
   4885                             }),
   4886                         });
   4887                     }
   4888                     s = local_ptr.parent;
   4889                 },
   4890                 .gen_zir => s = s.cast(GenZir).?.parent,
   4891                 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
   4892                 .namespace => {
   4893                     const ns = s.cast(Scope.Namespace).?;
   4894                     if (ns.decls.get(name_str_index)) |i| {
   4895                         if (found_already) |f| {
   4896                             return astgen.failTokNotes(test_name_token, "ambiguous reference", .{}, &.{
   4897                                 try astgen.errNoteNode(f, "declared here", .{}),
   4898                                 try astgen.errNoteNode(i, "also declared here", .{}),
   4899                             });
   4900                         }
   4901                         // We found a match but must continue looking for ambiguous references to decls.
   4902                         found_already = i;
   4903                     }
   4904                     num_namespaces_out += 1;
   4905                     capturing_namespace = ns;
   4906                     s = ns.parent;
   4907                 },
   4908                 .top => break,
   4909             };
   4910             if (found_already == null) {
   4911                 const ident_name = try astgen.identifierTokenString(test_name_token);
   4912                 return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name});
   4913             }
   4914 
   4915             break :blk .{ .decltest = name_str_index };
   4916         },
   4917     };
   4918 
   4919     var fn_block: GenZir = .{
   4920         .is_comptime = false,
   4921         .decl_node_index = node,
   4922         .decl_line = decl_block.decl_line,
   4923         .parent = &decl_block.base,
   4924         .astgen = astgen,
   4925         .instructions = decl_block.instructions,
   4926         .instructions_top = decl_block.instructions.items.len,
   4927     };
   4928     defer fn_block.unstack();
   4929 
   4930     const prev_within_fn = astgen.within_fn;
   4931     const prev_fn_block = astgen.fn_block;
   4932     const prev_fn_ret_ty = astgen.fn_ret_ty;
   4933     astgen.within_fn = true;
   4934     astgen.fn_block = &fn_block;
   4935     astgen.fn_ret_ty = .anyerror_void_error_union_type;
   4936     defer {
   4937         astgen.within_fn = prev_within_fn;
   4938         astgen.fn_block = prev_fn_block;
   4939         astgen.fn_ret_ty = prev_fn_ret_ty;
   4940     }
   4941 
   4942     astgen.advanceSourceCursorToNode(body_node);
   4943     const lbrace_line = astgen.source_line - decl_block.decl_line;
   4944     const lbrace_column = astgen.source_column;
   4945 
   4946     const block_result = try fullBodyExpr(&fn_block, &fn_block.base, .{ .rl = .none }, body_node, .normal);
   4947     if (fn_block.isEmpty() or !fn_block.refIsNoReturn(block_result)) {
   4948 
   4949         // As our last action before the return, "pop" the error trace if needed
   4950         _ = try fn_block.addRestoreErrRetIndex(.ret, .always, node);
   4951 
   4952         // Add implicit return at end of function.
   4953         _ = try fn_block.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node));
   4954     }
   4955 
   4956     const func_inst = try decl_block.addFunc(.{
   4957         .src_node = node,
   4958 
   4959         .cc_ref = .none,
   4960         .cc_gz = null,
   4961         .align_ref = .none,
   4962         .align_gz = null,
   4963         .ret_ref = .anyerror_void_error_union_type,
   4964         .ret_gz = null,
   4965         .section_ref = .none,
   4966         .section_gz = null,
   4967         .addrspace_ref = .none,
   4968         .addrspace_gz = null,
   4969 
   4970         .lbrace_line = lbrace_line,
   4971         .lbrace_column = lbrace_column,
   4972         .param_block = decl_inst,
   4973         .body_gz = &fn_block,
   4974         .lib_name = .empty,
   4975         .is_var_args = false,
   4976         .is_inferred_error = false,
   4977         .is_test = true,
   4978         .is_extern = false,
   4979         .is_noinline = false,
   4980         .noalias_bits = 0,
   4981 
   4982         // Tests don't have a prototype that needs hashing
   4983         .proto_hash = .{0} ** 16,
   4984     });
   4985 
   4986     _ = try decl_block.addBreak(.break_inline, decl_inst, func_inst);
   4987 
   4988     var hash: std.zig.SrcHash = undefined;
   4989     astgen.src_hasher.final(&hash);
   4990     try setDeclaration(
   4991         decl_inst,
   4992         hash,
   4993         test_name,
   4994         decl_block.decl_line,
   4995         false,
   4996         false,
   4997         .empty,
   4998         &decl_block,
   4999         null,
   5000     );
   5001 }
   5002 
   5003 fn structDeclInner(
   5004     gz: *GenZir,
   5005     scope: *Scope,
   5006     node: Ast.Node.Index,
   5007     container_decl: Ast.full.ContainerDecl,
   5008     layout: std.builtin.Type.ContainerLayout,
   5009     backing_int_node: Ast.Node.Index,
   5010 ) InnerError!Zir.Inst.Ref {
   5011     const decl_inst = try gz.reserveInstructionIndex();
   5012 
   5013     if (container_decl.ast.members.len == 0 and backing_int_node == 0) {
   5014         try gz.setStruct(decl_inst, .{
   5015             .src_node = node,
   5016             .layout = layout,
   5017             .captures_len = 0,
   5018             .fields_len = 0,
   5019             .decls_len = 0,
   5020             .has_backing_int = false,
   5021             .known_non_opv = false,
   5022             .known_comptime_only = false,
   5023             .is_tuple = false,
   5024             .any_comptime_fields = false,
   5025             .any_default_inits = false,
   5026             .any_aligned_fields = false,
   5027             .fields_hash = std.zig.hashSrc(@tagName(layout)),
   5028         });
   5029         return decl_inst.toRef();
   5030     }
   5031 
   5032     const astgen = gz.astgen;
   5033     const gpa = astgen.gpa;
   5034     const tree = astgen.tree;
   5035 
   5036     var namespace: Scope.Namespace = .{
   5037         .parent = scope,
   5038         .node = node,
   5039         .inst = decl_inst,
   5040         .declaring_gz = gz,
   5041         .maybe_generic = astgen.within_fn,
   5042     };
   5043     defer namespace.deinit(gpa);
   5044 
   5045     // The struct_decl instruction introduces a scope in which the decls of the struct
   5046     // are in scope, so that field types, alignments, and default value expressions
   5047     // can refer to decls within the struct itself.
   5048     astgen.advanceSourceCursorToNode(node);
   5049     var block_scope: GenZir = .{
   5050         .parent = &namespace.base,
   5051         .decl_node_index = node,
   5052         .decl_line = gz.decl_line,
   5053         .astgen = astgen,
   5054         .is_comptime = true,
   5055         .instructions = gz.instructions,
   5056         .instructions_top = gz.instructions.items.len,
   5057     };
   5058     defer block_scope.unstack();
   5059 
   5060     const scratch_top = astgen.scratch.items.len;
   5061     defer astgen.scratch.items.len = scratch_top;
   5062 
   5063     var backing_int_body_len: usize = 0;
   5064     const backing_int_ref: Zir.Inst.Ref = blk: {
   5065         if (backing_int_node != 0) {
   5066             if (layout != .@"packed") {
   5067                 return astgen.failNode(backing_int_node, "non-packed struct does not support backing integer type", .{});
   5068             } else {
   5069                 const backing_int_ref = try typeExpr(&block_scope, &namespace.base, backing_int_node);
   5070                 if (!block_scope.isEmpty()) {
   5071                     if (!block_scope.endsWithNoReturn()) {
   5072                         _ = try block_scope.addBreak(.break_inline, decl_inst, backing_int_ref);
   5073                     }
   5074 
   5075                     const body = block_scope.instructionsSlice();
   5076                     const old_scratch_len = astgen.scratch.items.len;
   5077                     try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
   5078                     appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
   5079                     backing_int_body_len = astgen.scratch.items.len - old_scratch_len;
   5080                     block_scope.instructions.items.len = block_scope.instructions_top;
   5081                 }
   5082                 break :blk backing_int_ref;
   5083             }
   5084         } else {
   5085             break :blk .none;
   5086         }
   5087     };
   5088 
   5089     const decl_count = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"struct");
   5090     const field_count: u32 = @intCast(container_decl.ast.members.len - decl_count);
   5091 
   5092     const bits_per_field = 4;
   5093     const max_field_size = 5;
   5094     var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size);
   5095     defer wip_members.deinit();
   5096 
   5097     // We will use the scratch buffer, starting here, for the bodies:
   5098     //    bodies: { // for every fields_len
   5099     //        field_type_body_inst: Inst, // for each field_type_body_len
   5100     //        align_body_inst: Inst, // for each align_body_len
   5101     //        init_body_inst: Inst, // for each init_body_len
   5102     //    }
   5103     // Note that the scratch buffer is simultaneously being used by WipMembers, however
   5104     // it will not access any elements beyond this point in the ArrayList. It also
   5105     // accesses via the ArrayList items field so it can handle the scratch buffer being
   5106     // reallocated.
   5107     // No defer needed here because it is handled by `wip_members.deinit()` above.
   5108     const bodies_start = astgen.scratch.items.len;
   5109 
   5110     const node_tags = tree.nodes.items(.tag);
   5111     const is_tuple = for (container_decl.ast.members) |member_node| {
   5112         const container_field = tree.fullContainerField(member_node) orelse continue;
   5113         if (container_field.ast.tuple_like) break true;
   5114     } else false;
   5115 
   5116     if (is_tuple) switch (layout) {
   5117         .auto => {},
   5118         .@"extern" => return astgen.failNode(node, "extern tuples are not supported", .{}),
   5119         .@"packed" => return astgen.failNode(node, "packed tuples are not supported", .{}),
   5120     };
   5121 
   5122     if (is_tuple) for (container_decl.ast.members) |member_node| {
   5123         switch (node_tags[member_node]) {
   5124             .container_field_init,
   5125             .container_field_align,
   5126             .container_field,
   5127             .@"comptime",
   5128             .test_decl,
   5129             => continue,
   5130             else => {
   5131                 const tuple_member = for (container_decl.ast.members) |maybe_tuple| switch (node_tags[maybe_tuple]) {
   5132                     .container_field_init,
   5133                     .container_field_align,
   5134                     .container_field,
   5135                     => break maybe_tuple,
   5136                     else => {},
   5137                 } else unreachable;
   5138                 return astgen.failNodeNotes(
   5139                     member_node,
   5140                     "tuple declarations cannot contain declarations",
   5141                     .{},
   5142                     &[_]u32{
   5143                         try astgen.errNoteNode(tuple_member, "tuple field here", .{}),
   5144                     },
   5145                 );
   5146             },
   5147         }
   5148     };
   5149 
   5150     const old_hasher = astgen.src_hasher;
   5151     defer astgen.src_hasher = old_hasher;
   5152     astgen.src_hasher = std.zig.SrcHasher.init(.{});
   5153     astgen.src_hasher.update(@tagName(layout));
   5154     if (backing_int_node != 0) {
   5155         astgen.src_hasher.update(tree.getNodeSource(backing_int_node));
   5156     }
   5157 
   5158     var known_non_opv = false;
   5159     var known_comptime_only = false;
   5160     var any_comptime_fields = false;
   5161     var any_aligned_fields = false;
   5162     var any_default_inits = false;
   5163     for (container_decl.ast.members) |member_node| {
   5164         var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
   5165             .decl => continue,
   5166             .field => |field| field,
   5167         };
   5168 
   5169         astgen.src_hasher.update(tree.getNodeSource(member_node));
   5170 
   5171         if (!is_tuple) {
   5172             const field_name = try astgen.identAsString(member.ast.main_token);
   5173 
   5174             member.convertToNonTupleLike(astgen.tree.nodes);
   5175             assert(!member.ast.tuple_like);
   5176 
   5177             wip_members.appendToField(@intFromEnum(field_name));
   5178         } else if (!member.ast.tuple_like) {
   5179             return astgen.failTok(member.ast.main_token, "tuple field has a name", .{});
   5180         }
   5181 
   5182         const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
   5183         wip_members.appendToField(@intFromEnum(doc_comment_index));
   5184 
   5185         if (member.ast.type_expr == 0) {
   5186             return astgen.failTok(member.ast.main_token, "struct field missing type", .{});
   5187         }
   5188 
   5189         const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr);
   5190         const have_type_body = !block_scope.isEmpty();
   5191         const have_align = member.ast.align_expr != 0;
   5192         const have_value = member.ast.value_expr != 0;
   5193         const is_comptime = member.comptime_token != null;
   5194 
   5195         if (is_comptime) {
   5196             switch (layout) {
   5197                 .@"packed" => return astgen.failTok(member.comptime_token.?, "packed struct fields cannot be marked comptime", .{}),
   5198                 .@"extern" => return astgen.failTok(member.comptime_token.?, "extern struct fields cannot be marked comptime", .{}),
   5199                 .auto => any_comptime_fields = true,
   5200             }
   5201         } else {
   5202             known_non_opv = known_non_opv or
   5203                 nodeImpliesMoreThanOnePossibleValue(tree, member.ast.type_expr);
   5204             known_comptime_only = known_comptime_only or
   5205                 nodeImpliesComptimeOnly(tree, member.ast.type_expr);
   5206         }
   5207         wip_members.nextField(bits_per_field, .{ have_align, have_value, is_comptime, have_type_body });
   5208 
   5209         if (have_type_body) {
   5210             if (!block_scope.endsWithNoReturn()) {
   5211                 _ = try block_scope.addBreak(.break_inline, decl_inst, field_type);
   5212             }
   5213             const body = block_scope.instructionsSlice();
   5214             const old_scratch_len = astgen.scratch.items.len;
   5215             try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
   5216             appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
   5217             wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len));
   5218             block_scope.instructions.items.len = block_scope.instructions_top;
   5219         } else {
   5220             wip_members.appendToField(@intFromEnum(field_type));
   5221         }
   5222 
   5223         if (have_align) {
   5224             if (layout == .@"packed") {
   5225                 try astgen.appendErrorNode(member.ast.align_expr, "unable to override alignment of packed struct fields", .{});
   5226             }
   5227             any_aligned_fields = true;
   5228             const align_ref = try expr(&block_scope, &namespace.base, coerced_align_ri, member.ast.align_expr);
   5229             if (!block_scope.endsWithNoReturn()) {
   5230                 _ = try block_scope.addBreak(.break_inline, decl_inst, align_ref);
   5231             }
   5232             const body = block_scope.instructionsSlice();
   5233             const old_scratch_len = astgen.scratch.items.len;
   5234             try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
   5235             appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
   5236             wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len));
   5237             block_scope.instructions.items.len = block_scope.instructions_top;
   5238         }
   5239 
   5240         if (have_value) {
   5241             any_default_inits = true;
   5242 
   5243             // The decl_inst is used as here so that we can easily reconstruct a mapping
   5244             // between it and the field type when the fields inits are analyzed.
   5245             const ri: ResultInfo = .{ .rl = if (field_type == .none) .none else .{ .coerced_ty = decl_inst.toRef() } };
   5246 
   5247             const default_inst = try expr(&block_scope, &namespace.base, ri, member.ast.value_expr);
   5248             if (!block_scope.endsWithNoReturn()) {
   5249                 _ = try block_scope.addBreak(.break_inline, decl_inst, default_inst);
   5250             }
   5251             const body = block_scope.instructionsSlice();
   5252             const old_scratch_len = astgen.scratch.items.len;
   5253             try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
   5254             appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
   5255             wip_members.appendToField(@intCast(astgen.scratch.items.len - old_scratch_len));
   5256             block_scope.instructions.items.len = block_scope.instructions_top;
   5257         } else if (member.comptime_token) |comptime_token| {
   5258             return astgen.failTok(comptime_token, "comptime field without default initialization value", .{});
   5259         }
   5260     }
   5261 
   5262     var fields_hash: std.zig.SrcHash = undefined;
   5263     astgen.src_hasher.final(&fields_hash);
   5264 
   5265     try gz.setStruct(decl_inst, .{
   5266         .src_node = node,
   5267         .layout = layout,
   5268         .captures_len = @intCast(namespace.captures.count()),
   5269         .fields_len = field_count,
   5270         .decls_len = decl_count,
   5271         .has_backing_int = backing_int_ref != .none,
   5272         .known_non_opv = known_non_opv,
   5273         .known_comptime_only = known_comptime_only,
   5274         .is_tuple = is_tuple,
   5275         .any_comptime_fields = any_comptime_fields,
   5276         .any_default_inits = any_default_inits,
   5277         .any_aligned_fields = any_aligned_fields,
   5278         .fields_hash = fields_hash,
   5279     });
   5280 
   5281     wip_members.finishBits(bits_per_field);
   5282     const decls_slice = wip_members.declsSlice();
   5283     const fields_slice = wip_members.fieldsSlice();
   5284     const bodies_slice = astgen.scratch.items[bodies_start..];
   5285     try astgen.extra.ensureUnusedCapacity(gpa, backing_int_body_len + 2 +
   5286         decls_slice.len + namespace.captures.count() + fields_slice.len + bodies_slice.len);
   5287     astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys()));
   5288     if (backing_int_ref != .none) {
   5289         astgen.extra.appendAssumeCapacity(@intCast(backing_int_body_len));
   5290         if (backing_int_body_len == 0) {
   5291             astgen.extra.appendAssumeCapacity(@intFromEnum(backing_int_ref));
   5292         } else {
   5293             astgen.extra.appendSliceAssumeCapacity(astgen.scratch.items[scratch_top..][0..backing_int_body_len]);
   5294         }
   5295     }
   5296     astgen.extra.appendSliceAssumeCapacity(decls_slice);
   5297     astgen.extra.appendSliceAssumeCapacity(fields_slice);
   5298     astgen.extra.appendSliceAssumeCapacity(bodies_slice);
   5299 
   5300     block_scope.unstack();
   5301     return decl_inst.toRef();
   5302 }
   5303 
   5304 fn unionDeclInner(
   5305     gz: *GenZir,
   5306     scope: *Scope,
   5307     node: Ast.Node.Index,
   5308     members: []const Ast.Node.Index,
   5309     layout: std.builtin.Type.ContainerLayout,
   5310     arg_node: Ast.Node.Index,
   5311     auto_enum_tok: ?Ast.TokenIndex,
   5312 ) InnerError!Zir.Inst.Ref {
   5313     const decl_inst = try gz.reserveInstructionIndex();
   5314 
   5315     const astgen = gz.astgen;
   5316     const gpa = astgen.gpa;
   5317 
   5318     var namespace: Scope.Namespace = .{
   5319         .parent = scope,
   5320         .node = node,
   5321         .inst = decl_inst,
   5322         .declaring_gz = gz,
   5323         .maybe_generic = astgen.within_fn,
   5324     };
   5325     defer namespace.deinit(gpa);
   5326 
   5327     // The union_decl instruction introduces a scope in which the decls of the union
   5328     // are in scope, so that field types, alignments, and default value expressions
   5329     // can refer to decls within the union itself.
   5330     astgen.advanceSourceCursorToNode(node);
   5331     var block_scope: GenZir = .{
   5332         .parent = &namespace.base,
   5333         .decl_node_index = node,
   5334         .decl_line = gz.decl_line,
   5335         .astgen = astgen,
   5336         .is_comptime = true,
   5337         .instructions = gz.instructions,
   5338         .instructions_top = gz.instructions.items.len,
   5339     };
   5340     defer block_scope.unstack();
   5341 
   5342     const decl_count = try astgen.scanContainer(&namespace, members, .@"union");
   5343     const field_count: u32 = @intCast(members.len - decl_count);
   5344 
   5345     if (layout != .auto and (auto_enum_tok != null or arg_node != 0)) {
   5346         if (arg_node != 0) {
   5347             return astgen.failNode(arg_node, "{s} union does not support enum tag type", .{@tagName(layout)});
   5348         } else {
   5349             return astgen.failTok(auto_enum_tok.?, "{s} union does not support enum tag type", .{@tagName(layout)});
   5350         }
   5351     }
   5352 
   5353     const arg_inst: Zir.Inst.Ref = if (arg_node != 0)
   5354         try typeExpr(&block_scope, &namespace.base, arg_node)
   5355     else
   5356         .none;
   5357 
   5358     const bits_per_field = 4;
   5359     const max_field_size = 5;
   5360     var any_aligned_fields = false;
   5361     var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size);
   5362     defer wip_members.deinit();
   5363 
   5364     const old_hasher = astgen.src_hasher;
   5365     defer astgen.src_hasher = old_hasher;
   5366     astgen.src_hasher = std.zig.SrcHasher.init(.{});
   5367     astgen.src_hasher.update(@tagName(layout));
   5368     astgen.src_hasher.update(&.{@intFromBool(auto_enum_tok != null)});
   5369     if (arg_node != 0) {
   5370         astgen.src_hasher.update(astgen.tree.getNodeSource(arg_node));
   5371     }
   5372 
   5373     for (members) |member_node| {
   5374         var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
   5375             .decl => continue,
   5376             .field => |field| field,
   5377         };
   5378         astgen.src_hasher.update(astgen.tree.getNodeSource(member_node));
   5379         member.convertToNonTupleLike(astgen.tree.nodes);
   5380         if (member.ast.tuple_like) {
   5381             return astgen.failTok(member.ast.main_token, "union field missing name", .{});
   5382         }
   5383         if (member.comptime_token) |comptime_token| {
   5384             return astgen.failTok(comptime_token, "union fields cannot be marked comptime", .{});
   5385         }
   5386 
   5387         const field_name = try astgen.identAsString(member.ast.main_token);
   5388         wip_members.appendToField(@intFromEnum(field_name));
   5389 
   5390         const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
   5391         wip_members.appendToField(@intFromEnum(doc_comment_index));
   5392 
   5393         const have_type = member.ast.type_expr != 0;
   5394         const have_align = member.ast.align_expr != 0;
   5395         const have_value = member.ast.value_expr != 0;
   5396         const unused = false;
   5397         wip_members.nextField(bits_per_field, .{ have_type, have_align, have_value, unused });
   5398 
   5399         if (have_type) {
   5400             const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr);
   5401             wip_members.appendToField(@intFromEnum(field_type));
   5402         } else if (arg_inst == .none and auto_enum_tok == null) {
   5403             return astgen.failNode(member_node, "union field missing type", .{});
   5404         }
   5405         if (have_align) {
   5406             const align_inst = try expr(&block_scope, &block_scope.base, coerced_align_ri, member.ast.align_expr);
   5407             wip_members.appendToField(@intFromEnum(align_inst));
   5408             any_aligned_fields = true;
   5409         }
   5410         if (have_value) {
   5411             if (arg_inst == .none) {
   5412                 return astgen.failNodeNotes(
   5413                     node,
   5414                     "explicitly valued tagged union missing integer tag type",
   5415                     .{},
   5416                     &[_]u32{
   5417                         try astgen.errNoteNode(
   5418                             member.ast.value_expr,
   5419                             "tag value specified here",
   5420                             .{},
   5421                         ),
   5422                     },
   5423                 );
   5424             }
   5425             if (auto_enum_tok == null) {
   5426                 return astgen.failNodeNotes(
   5427                     node,
   5428                     "explicitly valued tagged union requires inferred enum tag type",
   5429                     .{},
   5430                     &[_]u32{
   5431                         try astgen.errNoteNode(
   5432                             member.ast.value_expr,
   5433                             "tag value specified here",
   5434                             .{},
   5435                         ),
   5436                     },
   5437                 );
   5438             }
   5439             const tag_value = try expr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = arg_inst } }, member.ast.value_expr);
   5440             wip_members.appendToField(@intFromEnum(tag_value));
   5441         }
   5442     }
   5443 
   5444     var fields_hash: std.zig.SrcHash = undefined;
   5445     astgen.src_hasher.final(&fields_hash);
   5446 
   5447     if (!block_scope.isEmpty()) {
   5448         _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
   5449     }
   5450 
   5451     const body = block_scope.instructionsSlice();
   5452     const body_len = astgen.countBodyLenAfterFixups(body);
   5453 
   5454     try gz.setUnion(decl_inst, .{
   5455         .src_node = node,
   5456         .layout = layout,
   5457         .tag_type = arg_inst,
   5458         .captures_len = @intCast(namespace.captures.count()),
   5459         .body_len = body_len,
   5460         .fields_len = field_count,
   5461         .decls_len = decl_count,
   5462         .auto_enum_tag = auto_enum_tok != null,
   5463         .any_aligned_fields = any_aligned_fields,
   5464         .fields_hash = fields_hash,
   5465     });
   5466 
   5467     wip_members.finishBits(bits_per_field);
   5468     const decls_slice = wip_members.declsSlice();
   5469     const fields_slice = wip_members.fieldsSlice();
   5470     try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() + decls_slice.len + body_len + fields_slice.len);
   5471     astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys()));
   5472     astgen.extra.appendSliceAssumeCapacity(decls_slice);
   5473     astgen.appendBodyWithFixups(body);
   5474     astgen.extra.appendSliceAssumeCapacity(fields_slice);
   5475 
   5476     block_scope.unstack();
   5477     return decl_inst.toRef();
   5478 }
   5479 
   5480 fn containerDecl(
   5481     gz: *GenZir,
   5482     scope: *Scope,
   5483     ri: ResultInfo,
   5484     node: Ast.Node.Index,
   5485     container_decl: Ast.full.ContainerDecl,
   5486 ) InnerError!Zir.Inst.Ref {
   5487     const astgen = gz.astgen;
   5488     const gpa = astgen.gpa;
   5489     const tree = astgen.tree;
   5490     const token_tags = tree.tokens.items(.tag);
   5491 
   5492     const prev_fn_block = astgen.fn_block;
   5493     astgen.fn_block = null;
   5494     defer astgen.fn_block = prev_fn_block;
   5495 
   5496     // We must not create any types until Sema. Here the goal is only to generate
   5497     // ZIR for all the field types, alignments, and default value expressions.
   5498 
   5499     switch (token_tags[container_decl.ast.main_token]) {
   5500         .keyword_struct => {
   5501             const layout: std.builtin.Type.ContainerLayout = if (container_decl.layout_token) |t| switch (token_tags[t]) {
   5502                 .keyword_packed => .@"packed",
   5503                 .keyword_extern => .@"extern",
   5504                 else => unreachable,
   5505             } else .auto;
   5506 
   5507             const result = try structDeclInner(gz, scope, node, container_decl, layout, container_decl.ast.arg);
   5508             return rvalue(gz, ri, result, node);
   5509         },
   5510         .keyword_union => {
   5511             const layout: std.builtin.Type.ContainerLayout = if (container_decl.layout_token) |t| switch (token_tags[t]) {
   5512                 .keyword_packed => .@"packed",
   5513                 .keyword_extern => .@"extern",
   5514                 else => unreachable,
   5515             } else .auto;
   5516 
   5517             const result = try unionDeclInner(gz, scope, node, container_decl.ast.members, layout, container_decl.ast.arg, container_decl.ast.enum_token);
   5518             return rvalue(gz, ri, result, node);
   5519         },
   5520         .keyword_enum => {
   5521             if (container_decl.layout_token) |t| {
   5522                 return astgen.failTok(t, "enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", .{});
   5523             }
   5524             // Count total fields as well as how many have explicitly provided tag values.
   5525             const counts = blk: {
   5526                 var values: usize = 0;
   5527                 var total_fields: usize = 0;
   5528                 var decls: usize = 0;
   5529                 var nonexhaustive_node: Ast.Node.Index = 0;
   5530                 var nonfinal_nonexhaustive = false;
   5531                 for (container_decl.ast.members) |member_node| {
   5532                     var member = tree.fullContainerField(member_node) orelse {
   5533                         decls += 1;
   5534                         continue;
   5535                     };
   5536                     member.convertToNonTupleLike(astgen.tree.nodes);
   5537                     if (member.ast.tuple_like) {
   5538                         return astgen.failTok(member.ast.main_token, "enum field missing name", .{});
   5539                     }
   5540                     if (member.comptime_token) |comptime_token| {
   5541                         return astgen.failTok(comptime_token, "enum fields cannot be marked comptime", .{});
   5542                     }
   5543                     if (member.ast.type_expr != 0) {
   5544                         return astgen.failNodeNotes(
   5545                             member.ast.type_expr,
   5546                             "enum fields do not have types",
   5547                             .{},
   5548                             &[_]u32{
   5549                                 try astgen.errNoteNode(
   5550                                     node,
   5551                                     "consider 'union(enum)' here to make it a tagged union",
   5552                                     .{},
   5553                                 ),
   5554                             },
   5555                         );
   5556                     }
   5557                     if (member.ast.align_expr != 0) {
   5558                         return astgen.failNode(member.ast.align_expr, "enum fields cannot be aligned", .{});
   5559                     }
   5560 
   5561                     const name_token = member.ast.main_token;
   5562                     if (mem.eql(u8, tree.tokenSlice(name_token), "_")) {
   5563                         if (nonexhaustive_node != 0) {
   5564                             return astgen.failNodeNotes(
   5565                                 member_node,
   5566                                 "redundant non-exhaustive enum mark",
   5567                                 .{},
   5568                                 &[_]u32{
   5569                                     try astgen.errNoteNode(
   5570                                         nonexhaustive_node,
   5571                                         "other mark here",
   5572                                         .{},
   5573                                     ),
   5574                                 },
   5575                             );
   5576                         }
   5577                         nonexhaustive_node = member_node;
   5578                         if (member.ast.value_expr != 0) {
   5579                             return astgen.failNode(member.ast.value_expr, "'_' is used to mark an enum as non-exhaustive and cannot be assigned a value", .{});
   5580                         }
   5581                         continue;
   5582                     } else if (nonexhaustive_node != 0) {
   5583                         nonfinal_nonexhaustive = true;
   5584                     }
   5585                     total_fields += 1;
   5586                     if (member.ast.value_expr != 0) {
   5587                         if (container_decl.ast.arg == 0) {
   5588                             return astgen.failNode(member.ast.value_expr, "value assigned to enum tag with inferred tag type", .{});
   5589                         }
   5590                         values += 1;
   5591                     }
   5592                 }
   5593                 if (nonfinal_nonexhaustive) {
   5594                     return astgen.failNode(nonexhaustive_node, "'_' field of non-exhaustive enum must be last", .{});
   5595                 }
   5596                 break :blk .{
   5597                     .total_fields = total_fields,
   5598                     .values = values,
   5599                     .decls = decls,
   5600                     .nonexhaustive_node = nonexhaustive_node,
   5601                 };
   5602             };
   5603             if (counts.nonexhaustive_node != 0 and container_decl.ast.arg == 0) {
   5604                 try astgen.appendErrorNodeNotes(
   5605                     node,
   5606                     "non-exhaustive enum missing integer tag type",
   5607                     .{},
   5608                     &[_]u32{
   5609                         try astgen.errNoteNode(
   5610                             counts.nonexhaustive_node,
   5611                             "marked non-exhaustive here",
   5612                             .{},
   5613                         ),
   5614                     },
   5615                 );
   5616             }
   5617             // In this case we must generate ZIR code for the tag values, similar to
   5618             // how structs are handled above.
   5619             const nonexhaustive = counts.nonexhaustive_node != 0;
   5620 
   5621             const decl_inst = try gz.reserveInstructionIndex();
   5622 
   5623             var namespace: Scope.Namespace = .{
   5624                 .parent = scope,
   5625                 .node = node,
   5626                 .inst = decl_inst,
   5627                 .declaring_gz = gz,
   5628                 .maybe_generic = astgen.within_fn,
   5629             };
   5630             defer namespace.deinit(gpa);
   5631 
   5632             // The enum_decl instruction introduces a scope in which the decls of the enum
   5633             // are in scope, so that tag values can refer to decls within the enum itself.
   5634             astgen.advanceSourceCursorToNode(node);
   5635             var block_scope: GenZir = .{
   5636                 .parent = &namespace.base,
   5637                 .decl_node_index = node,
   5638                 .decl_line = gz.decl_line,
   5639                 .astgen = astgen,
   5640                 .is_comptime = true,
   5641                 .instructions = gz.instructions,
   5642                 .instructions_top = gz.instructions.items.len,
   5643             };
   5644             defer block_scope.unstack();
   5645 
   5646             _ = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"enum");
   5647             namespace.base.tag = .namespace;
   5648 
   5649             const arg_inst: Zir.Inst.Ref = if (container_decl.ast.arg != 0)
   5650                 try comptimeExpr(&block_scope, &namespace.base, coerced_type_ri, container_decl.ast.arg)
   5651             else
   5652                 .none;
   5653 
   5654             const bits_per_field = 1;
   5655             const max_field_size = 3;
   5656             var wip_members = try WipMembers.init(gpa, &astgen.scratch, @intCast(counts.decls), @intCast(counts.total_fields), bits_per_field, max_field_size);
   5657             defer wip_members.deinit();
   5658 
   5659             const old_hasher = astgen.src_hasher;
   5660             defer astgen.src_hasher = old_hasher;
   5661             astgen.src_hasher = std.zig.SrcHasher.init(.{});
   5662             if (container_decl.ast.arg != 0) {
   5663                 astgen.src_hasher.update(tree.getNodeSource(container_decl.ast.arg));
   5664             }
   5665             astgen.src_hasher.update(&.{@intFromBool(nonexhaustive)});
   5666 
   5667             for (container_decl.ast.members) |member_node| {
   5668                 if (member_node == counts.nonexhaustive_node)
   5669                     continue;
   5670                 astgen.src_hasher.update(tree.getNodeSource(member_node));
   5671                 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
   5672                     .decl => continue,
   5673                     .field => |field| field,
   5674                 };
   5675                 member.convertToNonTupleLike(astgen.tree.nodes);
   5676                 assert(member.comptime_token == null);
   5677                 assert(member.ast.type_expr == 0);
   5678                 assert(member.ast.align_expr == 0);
   5679 
   5680                 const field_name = try astgen.identAsString(member.ast.main_token);
   5681                 wip_members.appendToField(@intFromEnum(field_name));
   5682 
   5683                 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
   5684                 wip_members.appendToField(@intFromEnum(doc_comment_index));
   5685 
   5686                 const have_value = member.ast.value_expr != 0;
   5687                 wip_members.nextField(bits_per_field, .{have_value});
   5688 
   5689                 if (have_value) {
   5690                     if (arg_inst == .none) {
   5691                         return astgen.failNodeNotes(
   5692                             node,
   5693                             "explicitly valued enum missing integer tag type",
   5694                             .{},
   5695                             &[_]u32{
   5696                                 try astgen.errNoteNode(
   5697                                     member.ast.value_expr,
   5698                                     "tag value specified here",
   5699                                     .{},
   5700                                 ),
   5701                             },
   5702                         );
   5703                     }
   5704                     const tag_value_inst = try expr(&block_scope, &namespace.base, .{ .rl = .{ .ty = arg_inst } }, member.ast.value_expr);
   5705                     wip_members.appendToField(@intFromEnum(tag_value_inst));
   5706                 }
   5707             }
   5708 
   5709             if (!block_scope.isEmpty()) {
   5710                 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
   5711             }
   5712 
   5713             var fields_hash: std.zig.SrcHash = undefined;
   5714             astgen.src_hasher.final(&fields_hash);
   5715 
   5716             const body = block_scope.instructionsSlice();
   5717             const body_len = astgen.countBodyLenAfterFixups(body);
   5718 
   5719             try gz.setEnum(decl_inst, .{
   5720                 .src_node = node,
   5721                 .nonexhaustive = nonexhaustive,
   5722                 .tag_type = arg_inst,
   5723                 .captures_len = @intCast(namespace.captures.count()),
   5724                 .body_len = body_len,
   5725                 .fields_len = @intCast(counts.total_fields),
   5726                 .decls_len = @intCast(counts.decls),
   5727                 .fields_hash = fields_hash,
   5728             });
   5729 
   5730             wip_members.finishBits(bits_per_field);
   5731             const decls_slice = wip_members.declsSlice();
   5732             const fields_slice = wip_members.fieldsSlice();
   5733             try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() + decls_slice.len + body_len + fields_slice.len);
   5734             astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys()));
   5735             astgen.extra.appendSliceAssumeCapacity(decls_slice);
   5736             astgen.appendBodyWithFixups(body);
   5737             astgen.extra.appendSliceAssumeCapacity(fields_slice);
   5738 
   5739             block_scope.unstack();
   5740             return rvalue(gz, ri, decl_inst.toRef(), node);
   5741         },
   5742         .keyword_opaque => {
   5743             assert(container_decl.ast.arg == 0);
   5744 
   5745             const decl_inst = try gz.reserveInstructionIndex();
   5746 
   5747             var namespace: Scope.Namespace = .{
   5748                 .parent = scope,
   5749                 .node = node,
   5750                 .inst = decl_inst,
   5751                 .declaring_gz = gz,
   5752                 .maybe_generic = astgen.within_fn,
   5753             };
   5754             defer namespace.deinit(gpa);
   5755 
   5756             astgen.advanceSourceCursorToNode(node);
   5757             var block_scope: GenZir = .{
   5758                 .parent = &namespace.base,
   5759                 .decl_node_index = node,
   5760                 .decl_line = gz.decl_line,
   5761                 .astgen = astgen,
   5762                 .is_comptime = true,
   5763                 .instructions = gz.instructions,
   5764                 .instructions_top = gz.instructions.items.len,
   5765             };
   5766             defer block_scope.unstack();
   5767 
   5768             const decl_count = try astgen.scanContainer(&namespace, container_decl.ast.members, .@"opaque");
   5769 
   5770             var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, 0, 0, 0);
   5771             defer wip_members.deinit();
   5772 
   5773             for (container_decl.ast.members) |member_node| {
   5774                 const res = try containerMember(&block_scope, &namespace.base, &wip_members, member_node);
   5775                 if (res == .field) {
   5776                     return astgen.failNode(member_node, "opaque types cannot have fields", .{});
   5777                 }
   5778             }
   5779 
   5780             try gz.setOpaque(decl_inst, .{
   5781                 .src_node = node,
   5782                 .captures_len = @intCast(namespace.captures.count()),
   5783                 .decls_len = decl_count,
   5784             });
   5785 
   5786             wip_members.finishBits(0);
   5787             const decls_slice = wip_members.declsSlice();
   5788             try astgen.extra.ensureUnusedCapacity(gpa, namespace.captures.count() + decls_slice.len);
   5789             astgen.extra.appendSliceAssumeCapacity(@ptrCast(namespace.captures.keys()));
   5790             astgen.extra.appendSliceAssumeCapacity(decls_slice);
   5791 
   5792             block_scope.unstack();
   5793             return rvalue(gz, ri, decl_inst.toRef(), node);
   5794         },
   5795         else => unreachable,
   5796     }
   5797 }
   5798 
   5799 const ContainerMemberResult = union(enum) { decl, field: Ast.full.ContainerField };
   5800 
   5801 fn containerMember(
   5802     gz: *GenZir,
   5803     scope: *Scope,
   5804     wip_members: *WipMembers,
   5805     member_node: Ast.Node.Index,
   5806 ) InnerError!ContainerMemberResult {
   5807     const astgen = gz.astgen;
   5808     const tree = astgen.tree;
   5809     const node_tags = tree.nodes.items(.tag);
   5810     const node_datas = tree.nodes.items(.data);
   5811     switch (node_tags[member_node]) {
   5812         .container_field_init,
   5813         .container_field_align,
   5814         .container_field,
   5815         => return ContainerMemberResult{ .field = tree.fullContainerField(member_node).? },
   5816 
   5817         .fn_proto,
   5818         .fn_proto_multi,
   5819         .fn_proto_one,
   5820         .fn_proto_simple,
   5821         .fn_decl,
   5822         => {
   5823             var buf: [1]Ast.Node.Index = undefined;
   5824             const full = tree.fullFnProto(&buf, member_node).?;
   5825             const body = if (node_tags[member_node] == .fn_decl) node_datas[member_node].rhs else 0;
   5826 
   5827             astgen.fnDecl(gz, scope, wip_members, member_node, body, full) catch |err| switch (err) {
   5828                 error.OutOfMemory => return error.OutOfMemory,
   5829                 error.AnalysisFail => {},
   5830             };
   5831         },
   5832 
   5833         .global_var_decl,
   5834         .local_var_decl,
   5835         .simple_var_decl,
   5836         .aligned_var_decl,
   5837         => {
   5838             astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.fullVarDecl(member_node).?) catch |err| switch (err) {
   5839                 error.OutOfMemory => return error.OutOfMemory,
   5840                 error.AnalysisFail => {},
   5841             };
   5842         },
   5843 
   5844         .@"comptime" => {
   5845             astgen.comptimeDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
   5846                 error.OutOfMemory => return error.OutOfMemory,
   5847                 error.AnalysisFail => {},
   5848             };
   5849         },
   5850         .@"usingnamespace" => {
   5851             astgen.usingnamespaceDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
   5852                 error.OutOfMemory => return error.OutOfMemory,
   5853                 error.AnalysisFail => {},
   5854             };
   5855         },
   5856         .test_decl => {
   5857             astgen.testDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
   5858                 error.OutOfMemory => return error.OutOfMemory,
   5859                 error.AnalysisFail => {},
   5860             };
   5861         },
   5862         else => unreachable,
   5863     }
   5864     return .decl;
   5865 }
   5866 
   5867 fn errorSetDecl(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   5868     const astgen = gz.astgen;
   5869     const gpa = astgen.gpa;
   5870     const tree = astgen.tree;
   5871     const main_tokens = tree.nodes.items(.main_token);
   5872     const token_tags = tree.tokens.items(.tag);
   5873 
   5874     const payload_index = try reserveExtra(astgen, @typeInfo(Zir.Inst.ErrorSetDecl).@"struct".fields.len);
   5875     var fields_len: usize = 0;
   5876     {
   5877         var idents: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.TokenIndex) = .empty;
   5878         defer idents.deinit(gpa);
   5879 
   5880         const error_token = main_tokens[node];
   5881         var tok_i = error_token + 2;
   5882         while (true) : (tok_i += 1) {
   5883             switch (token_tags[tok_i]) {
   5884                 .doc_comment, .comma => {},
   5885                 .identifier => {
   5886                     const str_index = try astgen.identAsString(tok_i);
   5887                     const gop = try idents.getOrPut(gpa, str_index);
   5888                     if (gop.found_existing) {
   5889                         const name = try gpa.dupe(u8, mem.span(astgen.nullTerminatedString(str_index)));
   5890                         defer gpa.free(name);
   5891                         return astgen.failTokNotes(
   5892                             tok_i,
   5893                             "duplicate error set field '{s}'",
   5894                             .{name},
   5895                             &[_]u32{
   5896                                 try astgen.errNoteTok(
   5897                                     gop.value_ptr.*,
   5898                                     "previous declaration here",
   5899                                     .{},
   5900                                 ),
   5901                             },
   5902                         );
   5903                     }
   5904                     gop.value_ptr.* = tok_i;
   5905 
   5906                     try astgen.extra.ensureUnusedCapacity(gpa, 2);
   5907                     astgen.extra.appendAssumeCapacity(@intFromEnum(str_index));
   5908                     const doc_comment_index = try astgen.docCommentAsString(tok_i);
   5909                     astgen.extra.appendAssumeCapacity(@intFromEnum(doc_comment_index));
   5910                     fields_len += 1;
   5911                 },
   5912                 .r_brace => break,
   5913                 else => unreachable,
   5914             }
   5915         }
   5916     }
   5917 
   5918     setExtra(astgen, payload_index, Zir.Inst.ErrorSetDecl{
   5919         .fields_len = @intCast(fields_len),
   5920     });
   5921     const result = try gz.addPlNodePayloadIndex(.error_set_decl, node, payload_index);
   5922     return rvalue(gz, ri, result, node);
   5923 }
   5924 
   5925 fn tryExpr(
   5926     parent_gz: *GenZir,
   5927     scope: *Scope,
   5928     ri: ResultInfo,
   5929     node: Ast.Node.Index,
   5930     operand_node: Ast.Node.Index,
   5931 ) InnerError!Zir.Inst.Ref {
   5932     const astgen = parent_gz.astgen;
   5933 
   5934     const fn_block = astgen.fn_block orelse {
   5935         return astgen.failNode(node, "'try' outside function scope", .{});
   5936     };
   5937 
   5938     if (parent_gz.any_defer_node != 0) {
   5939         return astgen.failNodeNotes(node, "'try' not allowed inside defer expression", .{}, &.{
   5940             try astgen.errNoteNode(
   5941                 parent_gz.any_defer_node,
   5942                 "defer expression here",
   5943                 .{},
   5944             ),
   5945         });
   5946     }
   5947 
   5948     // Ensure debug line/column information is emitted for this try expression.
   5949     // Then we will save the line/column so that we can emit another one that goes
   5950     // "backwards" because we want to evaluate the operand, but then put the debug
   5951     // info back at the try keyword for error return tracing.
   5952     if (!parent_gz.is_comptime) {
   5953         try emitDbgNode(parent_gz, node);
   5954     }
   5955     const try_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
   5956 
   5957     const operand_rl: ResultInfo.Loc, const block_tag: Zir.Inst.Tag = switch (ri.rl) {
   5958         .ref => .{ .ref, .try_ptr },
   5959         .ref_coerced_ty => |payload_ptr_ty| .{
   5960             .{ .ref_coerced_ty = try parent_gz.addUnNode(.try_ref_operand_ty, payload_ptr_ty, node) },
   5961             .try_ptr,
   5962         },
   5963         else => if (try ri.rl.resultType(parent_gz, node)) |payload_ty| .{
   5964             // `coerced_ty` is OK due to the `rvalue` call below
   5965             .{ .coerced_ty = try parent_gz.addUnNode(.try_operand_ty, payload_ty, node) },
   5966             .@"try",
   5967         } else .{ .none, .@"try" },
   5968     };
   5969     const operand_ri: ResultInfo = .{ .rl = operand_rl, .ctx = .error_handling_expr };
   5970     // This could be a pointer or value depending on the `ri` parameter.
   5971     const operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, node);
   5972     const try_inst = try parent_gz.makeBlockInst(block_tag, node);
   5973     try parent_gz.instructions.append(astgen.gpa, try_inst);
   5974 
   5975     var else_scope = parent_gz.makeSubBlock(scope);
   5976     defer else_scope.unstack();
   5977 
   5978     const err_tag = switch (ri.rl) {
   5979         .ref, .ref_coerced_ty => Zir.Inst.Tag.err_union_code_ptr,
   5980         else => Zir.Inst.Tag.err_union_code,
   5981     };
   5982     const err_code = try else_scope.addUnNode(err_tag, operand, node);
   5983     try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code });
   5984     try emitDbgStmt(&else_scope, try_lc);
   5985     _ = try else_scope.addUnNode(.ret_node, err_code, node);
   5986 
   5987     try else_scope.setTryBody(try_inst, operand);
   5988     const result = try_inst.toRef();
   5989     switch (ri.rl) {
   5990         .ref, .ref_coerced_ty => return result,
   5991         else => return rvalue(parent_gz, ri, result, node),
   5992     }
   5993 }
   5994 
   5995 fn orelseCatchExpr(
   5996     parent_gz: *GenZir,
   5997     scope: *Scope,
   5998     ri: ResultInfo,
   5999     node: Ast.Node.Index,
   6000     lhs: Ast.Node.Index,
   6001     cond_op: Zir.Inst.Tag,
   6002     unwrap_op: Zir.Inst.Tag,
   6003     unwrap_code_op: Zir.Inst.Tag,
   6004     rhs: Ast.Node.Index,
   6005     payload_token: ?Ast.TokenIndex,
   6006 ) InnerError!Zir.Inst.Ref {
   6007     const astgen = parent_gz.astgen;
   6008     const tree = astgen.tree;
   6009 
   6010     const need_rl = astgen.nodes_need_rl.contains(node);
   6011     const block_ri: ResultInfo = if (need_rl) ri else .{
   6012         .rl = switch (ri.rl) {
   6013             .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
   6014             .inferred_ptr => .none,
   6015             else => ri.rl,
   6016         },
   6017         .ctx = ri.ctx,
   6018     };
   6019     // We need to call `rvalue` to write through to the pointer only if we had a
   6020     // result pointer and aren't forwarding it.
   6021     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   6022     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   6023 
   6024     const do_err_trace = astgen.fn_block != null and (cond_op == .is_non_err or cond_op == .is_non_err_ptr);
   6025 
   6026     var block_scope = parent_gz.makeSubBlock(scope);
   6027     block_scope.setBreakResultInfo(block_ri);
   6028     defer block_scope.unstack();
   6029 
   6030     const operand_ri: ResultInfo = switch (block_scope.break_result_info.rl) {
   6031         .ref, .ref_coerced_ty => .{ .rl = .ref, .ctx = if (do_err_trace) .error_handling_expr else .none },
   6032         else => .{ .rl = .none, .ctx = if (do_err_trace) .error_handling_expr else .none },
   6033     };
   6034     // This could be a pointer or value depending on the `operand_ri` parameter.
   6035     // We cannot use `block_scope.break_result_info` because that has the bare
   6036     // type, whereas this expression has the optional type. Later we make
   6037     // up for this fact by calling rvalue on the else branch.
   6038     const operand = try reachableExpr(&block_scope, &block_scope.base, operand_ri, lhs, rhs);
   6039     const cond = try block_scope.addUnNode(cond_op, operand, node);
   6040     const condbr = try block_scope.addCondBr(.condbr, node);
   6041 
   6042     const block = try parent_gz.makeBlockInst(.block, node);
   6043     try block_scope.setBlockBody(block);
   6044     // block_scope unstacked now, can add new instructions to parent_gz
   6045     try parent_gz.instructions.append(astgen.gpa, block);
   6046 
   6047     var then_scope = block_scope.makeSubBlock(scope);
   6048     defer then_scope.unstack();
   6049 
   6050     // This could be a pointer or value depending on `unwrap_op`.
   6051     const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node);
   6052     const then_result = switch (ri.rl) {
   6053         .ref, .ref_coerced_ty => unwrapped_payload,
   6054         else => try rvalue(&then_scope, block_scope.break_result_info, unwrapped_payload, node),
   6055     };
   6056     _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, node);
   6057 
   6058     var else_scope = block_scope.makeSubBlock(scope);
   6059     defer else_scope.unstack();
   6060 
   6061     // We know that the operand (almost certainly) modified the error return trace,
   6062     // so signal to Sema that it should save the new index for restoring later.
   6063     if (do_err_trace and nodeMayAppendToErrorTrace(tree, lhs))
   6064         _ = try else_scope.addSaveErrRetIndex(.always);
   6065 
   6066     var err_val_scope: Scope.LocalVal = undefined;
   6067     const else_sub_scope = blk: {
   6068         const payload = payload_token orelse break :blk &else_scope.base;
   6069         const err_str = tree.tokenSlice(payload);
   6070         if (mem.eql(u8, err_str, "_")) {
   6071             return astgen.failTok(payload, "discard of error capture; omit it instead", .{});
   6072         }
   6073         const err_name = try astgen.identAsString(payload);
   6074 
   6075         try astgen.detectLocalShadowing(scope, err_name, payload, err_str, .capture);
   6076 
   6077         err_val_scope = .{
   6078             .parent = &else_scope.base,
   6079             .gen_zir = &else_scope,
   6080             .name = err_name,
   6081             .inst = try else_scope.addUnNode(unwrap_code_op, operand, node),
   6082             .token_src = payload,
   6083             .id_cat = .capture,
   6084         };
   6085         break :blk &err_val_scope.base;
   6086     };
   6087 
   6088     const else_result = try fullBodyExpr(&else_scope, else_sub_scope, block_scope.break_result_info, rhs, .allow_branch_hint);
   6089     if (!else_scope.endsWithNoReturn()) {
   6090         // As our last action before the break, "pop" the error trace if needed
   6091         if (do_err_trace)
   6092             try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, rhs, else_result);
   6093 
   6094         _ = try else_scope.addBreakWithSrcNode(.@"break", block, else_result, rhs);
   6095     }
   6096     try checkUsed(parent_gz, &else_scope.base, else_sub_scope);
   6097 
   6098     try setCondBrPayload(condbr, cond, &then_scope, &else_scope);
   6099 
   6100     if (need_result_rvalue) {
   6101         return rvalue(parent_gz, ri, block.toRef(), node);
   6102     } else {
   6103         return block.toRef();
   6104     }
   6105 }
   6106 
   6107 /// Return whether the identifier names of two tokens are equal. Resolves @""
   6108 /// tokens without allocating.
   6109 /// OK in theory it could do it without allocating. This implementation
   6110 /// allocates when the @"" form is used.
   6111 fn tokenIdentEql(astgen: *AstGen, token1: Ast.TokenIndex, token2: Ast.TokenIndex) !bool {
   6112     const ident_name_1 = try astgen.identifierTokenString(token1);
   6113     const ident_name_2 = try astgen.identifierTokenString(token2);
   6114     return mem.eql(u8, ident_name_1, ident_name_2);
   6115 }
   6116 
   6117 fn fieldAccess(
   6118     gz: *GenZir,
   6119     scope: *Scope,
   6120     ri: ResultInfo,
   6121     node: Ast.Node.Index,
   6122 ) InnerError!Zir.Inst.Ref {
   6123     switch (ri.rl) {
   6124         .ref, .ref_coerced_ty => return addFieldAccess(.field_ptr, gz, scope, .{ .rl = .ref }, node),
   6125         else => {
   6126             const access = try addFieldAccess(.field_val, gz, scope, .{ .rl = .none }, node);
   6127             return rvalue(gz, ri, access, node);
   6128         },
   6129     }
   6130 }
   6131 
   6132 fn addFieldAccess(
   6133     tag: Zir.Inst.Tag,
   6134     gz: *GenZir,
   6135     scope: *Scope,
   6136     lhs_ri: ResultInfo,
   6137     node: Ast.Node.Index,
   6138 ) InnerError!Zir.Inst.Ref {
   6139     const astgen = gz.astgen;
   6140     const tree = astgen.tree;
   6141     const main_tokens = tree.nodes.items(.main_token);
   6142     const node_datas = tree.nodes.items(.data);
   6143 
   6144     const object_node = node_datas[node].lhs;
   6145     const dot_token = main_tokens[node];
   6146     const field_ident = dot_token + 1;
   6147     const str_index = try astgen.identAsString(field_ident);
   6148     const lhs = try expr(gz, scope, lhs_ri, object_node);
   6149 
   6150     const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   6151     try emitDbgStmt(gz, cursor);
   6152 
   6153     return gz.addPlNode(tag, node, Zir.Inst.Field{
   6154         .lhs = lhs,
   6155         .field_name_start = str_index,
   6156     });
   6157 }
   6158 
   6159 fn arrayAccess(
   6160     gz: *GenZir,
   6161     scope: *Scope,
   6162     ri: ResultInfo,
   6163     node: Ast.Node.Index,
   6164 ) InnerError!Zir.Inst.Ref {
   6165     const tree = gz.astgen.tree;
   6166     const node_datas = tree.nodes.items(.data);
   6167     switch (ri.rl) {
   6168         .ref, .ref_coerced_ty => {
   6169             const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
   6170 
   6171             const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   6172 
   6173             const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs);
   6174             try emitDbgStmt(gz, cursor);
   6175 
   6176             return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
   6177         },
   6178         else => {
   6179             const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs);
   6180 
   6181             const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   6182 
   6183             const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs);
   6184             try emitDbgStmt(gz, cursor);
   6185 
   6186             return rvalue(gz, ri, try gz.addPlNode(.elem_val_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }), node);
   6187         },
   6188     }
   6189 }
   6190 
   6191 fn simpleBinOp(
   6192     gz: *GenZir,
   6193     scope: *Scope,
   6194     ri: ResultInfo,
   6195     node: Ast.Node.Index,
   6196     op_inst_tag: Zir.Inst.Tag,
   6197 ) InnerError!Zir.Inst.Ref {
   6198     const astgen = gz.astgen;
   6199     const tree = astgen.tree;
   6200     const node_datas = tree.nodes.items(.data);
   6201 
   6202     if (op_inst_tag == .cmp_neq or op_inst_tag == .cmp_eq) {
   6203         const node_tags = tree.nodes.items(.tag);
   6204         const str = if (op_inst_tag == .cmp_eq) "==" else "!=";
   6205         if (node_tags[node_datas[node].lhs] == .string_literal or
   6206             node_tags[node_datas[node].rhs] == .string_literal)
   6207             return astgen.failNode(node, "cannot compare strings with {s}", .{str});
   6208     }
   6209 
   6210     const lhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].lhs, node);
   6211     const cursor = switch (op_inst_tag) {
   6212         .add, .sub, .mul, .div, .mod_rem => maybeAdvanceSourceCursorToMainToken(gz, node),
   6213         else => undefined,
   6214     };
   6215     const rhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].rhs, node);
   6216 
   6217     switch (op_inst_tag) {
   6218         .add, .sub, .mul, .div, .mod_rem => {
   6219             try emitDbgStmt(gz, cursor);
   6220         },
   6221         else => {},
   6222     }
   6223     const result = try gz.addPlNode(op_inst_tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
   6224     return rvalue(gz, ri, result, node);
   6225 }
   6226 
   6227 fn simpleStrTok(
   6228     gz: *GenZir,
   6229     ri: ResultInfo,
   6230     ident_token: Ast.TokenIndex,
   6231     node: Ast.Node.Index,
   6232     op_inst_tag: Zir.Inst.Tag,
   6233 ) InnerError!Zir.Inst.Ref {
   6234     const astgen = gz.astgen;
   6235     const str_index = try astgen.identAsString(ident_token);
   6236     const result = try gz.addStrTok(op_inst_tag, str_index, ident_token);
   6237     return rvalue(gz, ri, result, node);
   6238 }
   6239 
   6240 fn boolBinOp(
   6241     gz: *GenZir,
   6242     scope: *Scope,
   6243     ri: ResultInfo,
   6244     node: Ast.Node.Index,
   6245     zir_tag: Zir.Inst.Tag,
   6246 ) InnerError!Zir.Inst.Ref {
   6247     const astgen = gz.astgen;
   6248     const tree = astgen.tree;
   6249     const node_datas = tree.nodes.items(.data);
   6250 
   6251     const lhs = try expr(gz, scope, coerced_bool_ri, node_datas[node].lhs);
   6252     const bool_br = (try gz.addPlNodePayloadIndex(zir_tag, node, undefined)).toIndex().?;
   6253 
   6254     var rhs_scope = gz.makeSubBlock(scope);
   6255     defer rhs_scope.unstack();
   6256     const rhs = try fullBodyExpr(&rhs_scope, &rhs_scope.base, coerced_bool_ri, node_datas[node].rhs, .allow_branch_hint);
   6257     if (!gz.refIsNoReturn(rhs)) {
   6258         _ = try rhs_scope.addBreakWithSrcNode(.break_inline, bool_br, rhs, node_datas[node].rhs);
   6259     }
   6260     try rhs_scope.setBoolBrBody(bool_br, lhs);
   6261 
   6262     const block_ref = bool_br.toRef();
   6263     return rvalue(gz, ri, block_ref, node);
   6264 }
   6265 
   6266 fn ifExpr(
   6267     parent_gz: *GenZir,
   6268     scope: *Scope,
   6269     ri: ResultInfo,
   6270     node: Ast.Node.Index,
   6271     if_full: Ast.full.If,
   6272 ) InnerError!Zir.Inst.Ref {
   6273     const astgen = parent_gz.astgen;
   6274     const tree = astgen.tree;
   6275     const token_tags = tree.tokens.items(.tag);
   6276 
   6277     const do_err_trace = astgen.fn_block != null and if_full.error_token != null;
   6278 
   6279     const need_rl = astgen.nodes_need_rl.contains(node);
   6280     const block_ri: ResultInfo = if (need_rl) ri else .{
   6281         .rl = switch (ri.rl) {
   6282             .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
   6283             .inferred_ptr => .none,
   6284             else => ri.rl,
   6285         },
   6286         .ctx = ri.ctx,
   6287     };
   6288     // We need to call `rvalue` to write through to the pointer only if we had a
   6289     // result pointer and aren't forwarding it.
   6290     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   6291     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   6292 
   6293     var block_scope = parent_gz.makeSubBlock(scope);
   6294     block_scope.setBreakResultInfo(block_ri);
   6295     defer block_scope.unstack();
   6296 
   6297     const payload_is_ref = if (if_full.payload_token) |payload_token|
   6298         token_tags[payload_token] == .asterisk
   6299     else
   6300         false;
   6301 
   6302     try emitDbgNode(parent_gz, if_full.ast.cond_expr);
   6303     const cond: struct {
   6304         inst: Zir.Inst.Ref,
   6305         bool_bit: Zir.Inst.Ref,
   6306     } = c: {
   6307         if (if_full.error_token) |_| {
   6308             const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none, .ctx = .error_handling_expr };
   6309             const err_union = try expr(&block_scope, &block_scope.base, cond_ri, if_full.ast.cond_expr);
   6310             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
   6311             break :c .{
   6312                 .inst = err_union,
   6313                 .bool_bit = try block_scope.addUnNode(tag, err_union, if_full.ast.cond_expr),
   6314             };
   6315         } else if (if_full.payload_token) |_| {
   6316             const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
   6317             const optional = try expr(&block_scope, &block_scope.base, cond_ri, if_full.ast.cond_expr);
   6318             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
   6319             break :c .{
   6320                 .inst = optional,
   6321                 .bool_bit = try block_scope.addUnNode(tag, optional, if_full.ast.cond_expr),
   6322             };
   6323         } else {
   6324             const cond = try expr(&block_scope, &block_scope.base, coerced_bool_ri, if_full.ast.cond_expr);
   6325             break :c .{
   6326                 .inst = cond,
   6327                 .bool_bit = cond,
   6328             };
   6329         }
   6330     };
   6331 
   6332     const condbr = try block_scope.addCondBr(.condbr, node);
   6333 
   6334     const block = try parent_gz.makeBlockInst(.block, node);
   6335     try block_scope.setBlockBody(block);
   6336     // block_scope unstacked now, can add new instructions to parent_gz
   6337     try parent_gz.instructions.append(astgen.gpa, block);
   6338 
   6339     var then_scope = parent_gz.makeSubBlock(scope);
   6340     defer then_scope.unstack();
   6341 
   6342     var payload_val_scope: Scope.LocalVal = undefined;
   6343 
   6344     const then_node = if_full.ast.then_expr;
   6345     const then_sub_scope = s: {
   6346         if (if_full.error_token != null) {
   6347             if (if_full.payload_token) |payload_token| {
   6348                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   6349                     .err_union_payload_unsafe_ptr
   6350                 else
   6351                     .err_union_payload_unsafe;
   6352                 const payload_inst = try then_scope.addUnNode(tag, cond.inst, then_node);
   6353                 const token_name_index = payload_token + @intFromBool(payload_is_ref);
   6354                 const ident_name = try astgen.identAsString(token_name_index);
   6355                 const token_name_str = tree.tokenSlice(token_name_index);
   6356                 if (mem.eql(u8, "_", token_name_str)) {
   6357                     if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   6358                     break :s &then_scope.base;
   6359                 }
   6360                 try astgen.detectLocalShadowing(&then_scope.base, ident_name, token_name_index, token_name_str, .capture);
   6361                 payload_val_scope = .{
   6362                     .parent = &then_scope.base,
   6363                     .gen_zir = &then_scope,
   6364                     .name = ident_name,
   6365                     .inst = payload_inst,
   6366                     .token_src = token_name_index,
   6367                     .id_cat = .capture,
   6368                 };
   6369                 try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
   6370                 break :s &payload_val_scope.base;
   6371             } else {
   6372                 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node);
   6373                 break :s &then_scope.base;
   6374             }
   6375         } else if (if_full.payload_token) |payload_token| {
   6376             const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
   6377             const tag: Zir.Inst.Tag = if (payload_is_ref)
   6378                 .optional_payload_unsafe_ptr
   6379             else
   6380                 .optional_payload_unsafe;
   6381             const ident_bytes = tree.tokenSlice(ident_token);
   6382             if (mem.eql(u8, "_", ident_bytes)) {
   6383                 if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   6384                 break :s &then_scope.base;
   6385             }
   6386             const payload_inst = try then_scope.addUnNode(tag, cond.inst, then_node);
   6387             const ident_name = try astgen.identAsString(ident_token);
   6388             try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture);
   6389             payload_val_scope = .{
   6390                 .parent = &then_scope.base,
   6391                 .gen_zir = &then_scope,
   6392                 .name = ident_name,
   6393                 .inst = payload_inst,
   6394                 .token_src = ident_token,
   6395                 .id_cat = .capture,
   6396             };
   6397             try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
   6398             break :s &payload_val_scope.base;
   6399         } else {
   6400             break :s &then_scope.base;
   6401         }
   6402     };
   6403 
   6404     const then_result = try fullBodyExpr(&then_scope, then_sub_scope, block_scope.break_result_info, then_node, .allow_branch_hint);
   6405     try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
   6406     if (!then_scope.endsWithNoReturn()) {
   6407         _ = try then_scope.addBreakWithSrcNode(.@"break", block, then_result, then_node);
   6408     }
   6409 
   6410     var else_scope = parent_gz.makeSubBlock(scope);
   6411     defer else_scope.unstack();
   6412 
   6413     // We know that the operand (almost certainly) modified the error return trace,
   6414     // so signal to Sema that it should save the new index for restoring later.
   6415     if (do_err_trace and nodeMayAppendToErrorTrace(tree, if_full.ast.cond_expr))
   6416         _ = try else_scope.addSaveErrRetIndex(.always);
   6417 
   6418     const else_node = if_full.ast.else_expr;
   6419     if (else_node != 0) {
   6420         const sub_scope = s: {
   6421             if (if_full.error_token) |error_token| {
   6422                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   6423                     .err_union_code_ptr
   6424                 else
   6425                     .err_union_code;
   6426                 const payload_inst = try else_scope.addUnNode(tag, cond.inst, if_full.ast.cond_expr);
   6427                 const ident_name = try astgen.identAsString(error_token);
   6428                 const error_token_str = tree.tokenSlice(error_token);
   6429                 if (mem.eql(u8, "_", error_token_str))
   6430                     break :s &else_scope.base;
   6431                 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, error_token_str, .capture);
   6432                 payload_val_scope = .{
   6433                     .parent = &else_scope.base,
   6434                     .gen_zir = &else_scope,
   6435                     .name = ident_name,
   6436                     .inst = payload_inst,
   6437                     .token_src = error_token,
   6438                     .id_cat = .capture,
   6439                 };
   6440                 try else_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
   6441                 break :s &payload_val_scope.base;
   6442             } else {
   6443                 break :s &else_scope.base;
   6444             }
   6445         };
   6446         const else_result = try fullBodyExpr(&else_scope, sub_scope, block_scope.break_result_info, else_node, .allow_branch_hint);
   6447         if (!else_scope.endsWithNoReturn()) {
   6448             // As our last action before the break, "pop" the error trace if needed
   6449             if (do_err_trace)
   6450                 try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, else_node, else_result);
   6451             _ = try else_scope.addBreakWithSrcNode(.@"break", block, else_result, else_node);
   6452         }
   6453         try checkUsed(parent_gz, &else_scope.base, sub_scope);
   6454     } else {
   6455         const result = try rvalue(&else_scope, ri, .void_value, node);
   6456         _ = try else_scope.addBreak(.@"break", block, result);
   6457     }
   6458 
   6459     try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope);
   6460 
   6461     if (need_result_rvalue) {
   6462         return rvalue(parent_gz, ri, block.toRef(), node);
   6463     } else {
   6464         return block.toRef();
   6465     }
   6466 }
   6467 
   6468 /// Supports `else_scope` stacked on `then_scope`. Unstacks `else_scope` then `then_scope`.
   6469 fn setCondBrPayload(
   6470     condbr: Zir.Inst.Index,
   6471     cond: Zir.Inst.Ref,
   6472     then_scope: *GenZir,
   6473     else_scope: *GenZir,
   6474 ) !void {
   6475     defer then_scope.unstack();
   6476     defer else_scope.unstack();
   6477     const astgen = then_scope.astgen;
   6478     const then_body = then_scope.instructionsSliceUpto(else_scope);
   6479     const else_body = else_scope.instructionsSlice();
   6480     const then_body_len = astgen.countBodyLenAfterFixups(then_body);
   6481     const else_body_len = astgen.countBodyLenAfterFixups(else_body);
   6482     try astgen.extra.ensureUnusedCapacity(
   6483         astgen.gpa,
   6484         @typeInfo(Zir.Inst.CondBr).@"struct".fields.len + then_body_len + else_body_len,
   6485     );
   6486 
   6487     const zir_datas = astgen.instructions.items(.data);
   6488     zir_datas[@intFromEnum(condbr)].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.CondBr{
   6489         .condition = cond,
   6490         .then_body_len = then_body_len,
   6491         .else_body_len = else_body_len,
   6492     });
   6493     astgen.appendBodyWithFixups(then_body);
   6494     astgen.appendBodyWithFixups(else_body);
   6495 }
   6496 
   6497 fn whileExpr(
   6498     parent_gz: *GenZir,
   6499     scope: *Scope,
   6500     ri: ResultInfo,
   6501     node: Ast.Node.Index,
   6502     while_full: Ast.full.While,
   6503     is_statement: bool,
   6504 ) InnerError!Zir.Inst.Ref {
   6505     const astgen = parent_gz.astgen;
   6506     const tree = astgen.tree;
   6507     const token_tags = tree.tokens.items(.tag);
   6508 
   6509     const need_rl = astgen.nodes_need_rl.contains(node);
   6510     const block_ri: ResultInfo = if (need_rl) ri else .{
   6511         .rl = switch (ri.rl) {
   6512             .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
   6513             .inferred_ptr => .none,
   6514             else => ri.rl,
   6515         },
   6516         .ctx = ri.ctx,
   6517     };
   6518     // We need to call `rvalue` to write through to the pointer only if we had a
   6519     // result pointer and aren't forwarding it.
   6520     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   6521     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   6522 
   6523     if (while_full.label_token) |label_token| {
   6524         try astgen.checkLabelRedefinition(scope, label_token);
   6525     }
   6526 
   6527     const is_inline = while_full.inline_token != null;
   6528     if (parent_gz.is_comptime and is_inline) {
   6529         return astgen.failTok(while_full.inline_token.?, "redundant inline keyword in comptime scope", .{});
   6530     }
   6531     const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop;
   6532     const loop_block = try parent_gz.makeBlockInst(loop_tag, node);
   6533     try parent_gz.instructions.append(astgen.gpa, loop_block);
   6534 
   6535     var loop_scope = parent_gz.makeSubBlock(scope);
   6536     loop_scope.is_inline = is_inline;
   6537     loop_scope.setBreakResultInfo(block_ri);
   6538     defer loop_scope.unstack();
   6539 
   6540     var cond_scope = parent_gz.makeSubBlock(&loop_scope.base);
   6541     defer cond_scope.unstack();
   6542 
   6543     const payload_is_ref = if (while_full.payload_token) |payload_token|
   6544         token_tags[payload_token] == .asterisk
   6545     else
   6546         false;
   6547 
   6548     try emitDbgNode(parent_gz, while_full.ast.cond_expr);
   6549     const cond: struct {
   6550         inst: Zir.Inst.Ref,
   6551         bool_bit: Zir.Inst.Ref,
   6552     } = c: {
   6553         if (while_full.error_token) |_| {
   6554             const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
   6555             const err_union = try fullBodyExpr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr, .normal);
   6556             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
   6557             break :c .{
   6558                 .inst = err_union,
   6559                 .bool_bit = try cond_scope.addUnNode(tag, err_union, while_full.ast.cond_expr),
   6560             };
   6561         } else if (while_full.payload_token) |_| {
   6562             const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
   6563             const optional = try fullBodyExpr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr, .normal);
   6564             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
   6565             break :c .{
   6566                 .inst = optional,
   6567                 .bool_bit = try cond_scope.addUnNode(tag, optional, while_full.ast.cond_expr),
   6568             };
   6569         } else {
   6570             const cond = try fullBodyExpr(&cond_scope, &cond_scope.base, coerced_bool_ri, while_full.ast.cond_expr, .normal);
   6571             break :c .{
   6572                 .inst = cond,
   6573                 .bool_bit = cond,
   6574             };
   6575         }
   6576     };
   6577 
   6578     const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
   6579     const condbr = try cond_scope.addCondBr(condbr_tag, node);
   6580     const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block;
   6581     const cond_block = try loop_scope.makeBlockInst(block_tag, node);
   6582     try cond_scope.setBlockBody(cond_block);
   6583     // cond_scope unstacked now, can add new instructions to loop_scope
   6584     try loop_scope.instructions.append(astgen.gpa, cond_block);
   6585 
   6586     // make scope now but don't stack on parent_gz until loop_scope
   6587     // gets unstacked after cont_expr is emitted and added below
   6588     var then_scope = parent_gz.makeSubBlock(&cond_scope.base);
   6589     then_scope.instructions_top = GenZir.unstacked_top;
   6590     defer then_scope.unstack();
   6591 
   6592     var dbg_var_name: Zir.NullTerminatedString = .empty;
   6593     var dbg_var_inst: Zir.Inst.Ref = undefined;
   6594     var opt_payload_inst: Zir.Inst.OptionalIndex = .none;
   6595     var payload_val_scope: Scope.LocalVal = undefined;
   6596     const then_sub_scope = s: {
   6597         if (while_full.error_token != null) {
   6598             if (while_full.payload_token) |payload_token| {
   6599                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   6600                     .err_union_payload_unsafe_ptr
   6601                 else
   6602                     .err_union_payload_unsafe;
   6603                 // will add this instruction to then_scope.instructions below
   6604                 const payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr);
   6605                 opt_payload_inst = payload_inst.toOptional();
   6606                 const ident_token = payload_token + @intFromBool(payload_is_ref);
   6607                 const ident_bytes = tree.tokenSlice(ident_token);
   6608                 if (mem.eql(u8, "_", ident_bytes)) {
   6609                     if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   6610                     break :s &then_scope.base;
   6611                 }
   6612                 const ident_name = try astgen.identAsString(ident_token);
   6613                 try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture);
   6614                 payload_val_scope = .{
   6615                     .parent = &then_scope.base,
   6616                     .gen_zir = &then_scope,
   6617                     .name = ident_name,
   6618                     .inst = payload_inst.toRef(),
   6619                     .token_src = ident_token,
   6620                     .id_cat = .capture,
   6621                 };
   6622                 dbg_var_name = ident_name;
   6623                 dbg_var_inst = payload_inst.toRef();
   6624                 break :s &payload_val_scope.base;
   6625             } else {
   6626                 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node);
   6627                 break :s &then_scope.base;
   6628             }
   6629         } else if (while_full.payload_token) |payload_token| {
   6630             const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
   6631             const tag: Zir.Inst.Tag = if (payload_is_ref)
   6632                 .optional_payload_unsafe_ptr
   6633             else
   6634                 .optional_payload_unsafe;
   6635             // will add this instruction to then_scope.instructions below
   6636             const payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr);
   6637             opt_payload_inst = payload_inst.toOptional();
   6638             const ident_name = try astgen.identAsString(ident_token);
   6639             const ident_bytes = tree.tokenSlice(ident_token);
   6640             if (mem.eql(u8, "_", ident_bytes)) {
   6641                 if (payload_is_ref) return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   6642                 break :s &then_scope.base;
   6643             }
   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             break :s &then_scope.base;
   6658         }
   6659     };
   6660 
   6661     var continue_scope = parent_gz.makeSubBlock(then_sub_scope);
   6662     continue_scope.instructions_top = GenZir.unstacked_top;
   6663     defer continue_scope.unstack();
   6664     const continue_block = try then_scope.makeBlockInst(block_tag, node);
   6665 
   6666     const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
   6667     _ = try loop_scope.addNode(repeat_tag, node);
   6668 
   6669     try loop_scope.setBlockBody(loop_block);
   6670     loop_scope.break_block = loop_block.toOptional();
   6671     loop_scope.continue_block = continue_block.toOptional();
   6672     if (while_full.label_token) |label_token| {
   6673         loop_scope.label = .{
   6674             .token = label_token,
   6675             .block_inst = loop_block,
   6676         };
   6677     }
   6678 
   6679     // done adding instructions to loop_scope, can now stack then_scope
   6680     then_scope.instructions_top = then_scope.instructions.items.len;
   6681 
   6682     const then_node = while_full.ast.then_expr;
   6683     if (opt_payload_inst.unwrap()) |payload_inst| {
   6684         try then_scope.instructions.append(astgen.gpa, payload_inst);
   6685     }
   6686     if (dbg_var_name != .empty) try then_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst);
   6687     try then_scope.instructions.append(astgen.gpa, continue_block);
   6688     // This code could be improved to avoid emitting the continue expr when there
   6689     // are no jumps to it. This happens when the last statement of a while body is noreturn
   6690     // and there are no `continue` statements.
   6691     // Tracking issue: https://github.com/ziglang/zig/issues/9185
   6692     if (while_full.ast.cont_expr != 0) {
   6693         _ = try unusedResultExpr(&then_scope, then_sub_scope, while_full.ast.cont_expr);
   6694     }
   6695 
   6696     continue_scope.instructions_top = continue_scope.instructions.items.len;
   6697     {
   6698         try emitDbgNode(&continue_scope, then_node);
   6699         const unused_result = try fullBodyExpr(&continue_scope, &continue_scope.base, .{ .rl = .none }, then_node, .allow_branch_hint);
   6700         _ = try addEnsureResult(&continue_scope, unused_result, then_node);
   6701     }
   6702     try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
   6703     const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
   6704     if (!continue_scope.endsWithNoReturn()) {
   6705         _ = try continue_scope.addBreak(break_tag, continue_block, .void_value);
   6706     }
   6707     try continue_scope.setBlockBody(continue_block);
   6708     _ = try then_scope.addBreak(break_tag, cond_block, .void_value);
   6709 
   6710     var else_scope = parent_gz.makeSubBlock(&cond_scope.base);
   6711     defer else_scope.unstack();
   6712 
   6713     const else_node = while_full.ast.else_expr;
   6714     if (else_node != 0) {
   6715         const sub_scope = s: {
   6716             if (while_full.error_token) |error_token| {
   6717                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   6718                     .err_union_code_ptr
   6719                 else
   6720                     .err_union_code;
   6721                 const else_payload_inst = try else_scope.addUnNode(tag, cond.inst, while_full.ast.cond_expr);
   6722                 const ident_name = try astgen.identAsString(error_token);
   6723                 const ident_bytes = tree.tokenSlice(error_token);
   6724                 if (mem.eql(u8, ident_bytes, "_"))
   6725                     break :s &else_scope.base;
   6726                 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, ident_bytes, .capture);
   6727                 payload_val_scope = .{
   6728                     .parent = &else_scope.base,
   6729                     .gen_zir = &else_scope,
   6730                     .name = ident_name,
   6731                     .inst = else_payload_inst,
   6732                     .token_src = error_token,
   6733                     .id_cat = .capture,
   6734                 };
   6735                 try else_scope.addDbgVar(.dbg_var_val, ident_name, else_payload_inst);
   6736                 break :s &payload_val_scope.base;
   6737             } else {
   6738                 break :s &else_scope.base;
   6739             }
   6740         };
   6741         // Remove the continue block and break block so that `continue` and `break`
   6742         // control flow apply to outer loops; not this one.
   6743         loop_scope.continue_block = .none;
   6744         loop_scope.break_block = .none;
   6745         const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node, .allow_branch_hint);
   6746         if (is_statement) {
   6747             _ = try addEnsureResult(&else_scope, else_result, else_node);
   6748         }
   6749 
   6750         try checkUsed(parent_gz, &else_scope.base, sub_scope);
   6751         if (!else_scope.endsWithNoReturn()) {
   6752             _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node);
   6753         }
   6754     } else {
   6755         const result = try rvalue(&else_scope, ri, .void_value, node);
   6756         _ = try else_scope.addBreak(break_tag, loop_block, result);
   6757     }
   6758 
   6759     if (loop_scope.label) |some| {
   6760         if (!some.used) {
   6761             try astgen.appendErrorTok(some.token, "unused while loop label", .{});
   6762         }
   6763     }
   6764 
   6765     try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope);
   6766 
   6767     const result = if (need_result_rvalue)
   6768         try rvalue(parent_gz, ri, loop_block.toRef(), node)
   6769     else
   6770         loop_block.toRef();
   6771 
   6772     if (is_statement) {
   6773         _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
   6774     }
   6775 
   6776     return result;
   6777 }
   6778 
   6779 fn forExpr(
   6780     parent_gz: *GenZir,
   6781     scope: *Scope,
   6782     ri: ResultInfo,
   6783     node: Ast.Node.Index,
   6784     for_full: Ast.full.For,
   6785     is_statement: bool,
   6786 ) InnerError!Zir.Inst.Ref {
   6787     const astgen = parent_gz.astgen;
   6788 
   6789     if (for_full.label_token) |label_token| {
   6790         try astgen.checkLabelRedefinition(scope, label_token);
   6791     }
   6792 
   6793     const need_rl = astgen.nodes_need_rl.contains(node);
   6794     const block_ri: ResultInfo = if (need_rl) ri else .{
   6795         .rl = switch (ri.rl) {
   6796             .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
   6797             .inferred_ptr => .none,
   6798             else => ri.rl,
   6799         },
   6800         .ctx = ri.ctx,
   6801     };
   6802     // We need to call `rvalue` to write through to the pointer only if we had a
   6803     // result pointer and aren't forwarding it.
   6804     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   6805     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   6806 
   6807     const is_inline = for_full.inline_token != null;
   6808     if (parent_gz.is_comptime and is_inline) {
   6809         return astgen.failTok(for_full.inline_token.?, "redundant inline keyword in comptime scope", .{});
   6810     }
   6811     const tree = astgen.tree;
   6812     const token_tags = tree.tokens.items(.tag);
   6813     const node_tags = tree.nodes.items(.tag);
   6814     const node_data = tree.nodes.items(.data);
   6815     const gpa = astgen.gpa;
   6816 
   6817     // For counters, this is the start value; for indexables, this is the base
   6818     // pointer that can be used with elem_ptr and similar instructions.
   6819     // Special value `none` means that this is a counter and its start value is
   6820     // zero, indicating that the main index counter can be used directly.
   6821     const indexables = try gpa.alloc(Zir.Inst.Ref, for_full.ast.inputs.len);
   6822     defer gpa.free(indexables);
   6823     // elements of this array can be `none`, indicating no length check.
   6824     const lens = try gpa.alloc(Zir.Inst.Ref, for_full.ast.inputs.len);
   6825     defer gpa.free(lens);
   6826 
   6827     // We will use a single zero-based counter no matter how many indexables there are.
   6828     const index_ptr = blk: {
   6829         const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime_mut else .alloc;
   6830         const index_ptr = try parent_gz.addUnNode(alloc_tag, .usize_type, node);
   6831         // initialize to zero
   6832         _ = try parent_gz.addPlNode(.store_node, node, Zir.Inst.Bin{
   6833             .lhs = index_ptr,
   6834             .rhs = .zero_usize,
   6835         });
   6836         break :blk index_ptr;
   6837     };
   6838 
   6839     var any_len_checks = false;
   6840 
   6841     {
   6842         var capture_token = for_full.payload_token;
   6843         for (for_full.ast.inputs, indexables, lens) |input, *indexable_ref, *len_ref| {
   6844             const capture_is_ref = token_tags[capture_token] == .asterisk;
   6845             const ident_tok = capture_token + @intFromBool(capture_is_ref);
   6846             const is_discard = mem.eql(u8, tree.tokenSlice(ident_tok), "_");
   6847 
   6848             if (is_discard and capture_is_ref) {
   6849                 return astgen.failTok(capture_token, "pointer modifier invalid on discard", .{});
   6850             }
   6851             // Skip over the comma, and on to the next capture (or the ending pipe character).
   6852             capture_token = ident_tok + 2;
   6853 
   6854             try emitDbgNode(parent_gz, input);
   6855             if (node_tags[input] == .for_range) {
   6856                 if (capture_is_ref) {
   6857                     return astgen.failTok(ident_tok, "cannot capture reference to range", .{});
   6858                 }
   6859                 const start_node = node_data[input].lhs;
   6860                 const start_val = try expr(parent_gz, scope, .{ .rl = .{ .ty = .usize_type } }, start_node);
   6861 
   6862                 const end_node = node_data[input].rhs;
   6863                 const end_val = if (end_node != 0)
   6864                     try expr(parent_gz, scope, .{ .rl = .{ .ty = .usize_type } }, node_data[input].rhs)
   6865                 else
   6866                     .none;
   6867 
   6868                 if (end_val == .none and is_discard) {
   6869                     return astgen.failTok(ident_tok, "discard of unbounded counter", .{});
   6870                 }
   6871 
   6872                 const start_is_zero = nodeIsTriviallyZero(tree, start_node);
   6873                 const range_len = if (end_val == .none or start_is_zero)
   6874                     end_val
   6875                 else
   6876                     try parent_gz.addPlNode(.sub, input, Zir.Inst.Bin{
   6877                         .lhs = end_val,
   6878                         .rhs = start_val,
   6879                     });
   6880 
   6881                 any_len_checks = any_len_checks or range_len != .none;
   6882                 indexable_ref.* = if (start_is_zero) .none else start_val;
   6883                 len_ref.* = range_len;
   6884             } else {
   6885                 const indexable = try expr(parent_gz, scope, .{ .rl = .none }, input);
   6886 
   6887                 any_len_checks = true;
   6888                 indexable_ref.* = indexable;
   6889                 len_ref.* = indexable;
   6890             }
   6891         }
   6892     }
   6893 
   6894     if (!any_len_checks) {
   6895         return astgen.failNode(node, "unbounded for loop", .{});
   6896     }
   6897 
   6898     // We use a dedicated ZIR instruction to assert the lengths to assist with
   6899     // nicer error reporting as well as fewer ZIR bytes emitted.
   6900     const len: Zir.Inst.Ref = len: {
   6901         const lens_len: u32 = @intCast(lens.len);
   6902         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.MultiOp).@"struct".fields.len + lens_len);
   6903         const len = try parent_gz.addPlNode(.for_len, node, Zir.Inst.MultiOp{
   6904             .operands_len = lens_len,
   6905         });
   6906         appendRefsAssumeCapacity(astgen, lens);
   6907         break :len len;
   6908     };
   6909 
   6910     const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop;
   6911     const loop_block = try parent_gz.makeBlockInst(loop_tag, node);
   6912     try parent_gz.instructions.append(gpa, loop_block);
   6913 
   6914     var loop_scope = parent_gz.makeSubBlock(scope);
   6915     loop_scope.is_inline = is_inline;
   6916     loop_scope.setBreakResultInfo(block_ri);
   6917     defer loop_scope.unstack();
   6918 
   6919     // We need to finish loop_scope later once we have the deferred refs from then_scope. However, the
   6920     // load must be removed from instructions in the meantime or it appears to be part of parent_gz.
   6921     const index = try loop_scope.addUnNode(.load, index_ptr, node);
   6922     _ = loop_scope.instructions.pop();
   6923 
   6924     var cond_scope = parent_gz.makeSubBlock(&loop_scope.base);
   6925     defer cond_scope.unstack();
   6926 
   6927     // Check the condition.
   6928     const cond = try cond_scope.addPlNode(.cmp_lt, node, Zir.Inst.Bin{
   6929         .lhs = index,
   6930         .rhs = len,
   6931     });
   6932 
   6933     const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
   6934     const condbr = try cond_scope.addCondBr(condbr_tag, node);
   6935     const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block;
   6936     const cond_block = try loop_scope.makeBlockInst(block_tag, node);
   6937     try cond_scope.setBlockBody(cond_block);
   6938 
   6939     loop_scope.break_block = loop_block.toOptional();
   6940     loop_scope.continue_block = cond_block.toOptional();
   6941     if (for_full.label_token) |label_token| {
   6942         loop_scope.label = .{
   6943             .token = label_token,
   6944             .block_inst = loop_block,
   6945         };
   6946     }
   6947 
   6948     const then_node = for_full.ast.then_expr;
   6949     var then_scope = parent_gz.makeSubBlock(&cond_scope.base);
   6950     defer then_scope.unstack();
   6951 
   6952     const capture_scopes = try gpa.alloc(Scope.LocalVal, for_full.ast.inputs.len);
   6953     defer gpa.free(capture_scopes);
   6954 
   6955     const then_sub_scope = blk: {
   6956         var capture_token = for_full.payload_token;
   6957         var capture_sub_scope: *Scope = &then_scope.base;
   6958         for (for_full.ast.inputs, indexables, capture_scopes) |input, indexable_ref, *capture_scope| {
   6959             const capture_is_ref = token_tags[capture_token] == .asterisk;
   6960             const ident_tok = capture_token + @intFromBool(capture_is_ref);
   6961             const capture_name = tree.tokenSlice(ident_tok);
   6962             // Skip over the comma, and on to the next capture (or the ending pipe character).
   6963             capture_token = ident_tok + 2;
   6964 
   6965             if (mem.eql(u8, capture_name, "_")) continue;
   6966 
   6967             const name_str_index = try astgen.identAsString(ident_tok);
   6968             try astgen.detectLocalShadowing(capture_sub_scope, name_str_index, ident_tok, capture_name, .capture);
   6969 
   6970             const capture_inst = inst: {
   6971                 const is_counter = node_tags[input] == .for_range;
   6972 
   6973                 if (indexable_ref == .none) {
   6974                     // Special case: the main index can be used directly.
   6975                     assert(is_counter);
   6976                     assert(!capture_is_ref);
   6977                     break :inst index;
   6978                 }
   6979 
   6980                 // For counters, we add the index variable to the start value; for
   6981                 // indexables, we use it as an element index. This is so similar
   6982                 // that they can share the same code paths, branching only on the
   6983                 // ZIR tag.
   6984                 const switch_cond = (@as(u2, @intFromBool(capture_is_ref)) << 1) | @intFromBool(is_counter);
   6985                 const tag: Zir.Inst.Tag = switch (switch_cond) {
   6986                     0b00 => .elem_val,
   6987                     0b01 => .add,
   6988                     0b10 => .elem_ptr,
   6989                     0b11 => unreachable, // compile error emitted already
   6990                 };
   6991                 break :inst try then_scope.addPlNode(tag, input, Zir.Inst.Bin{
   6992                     .lhs = indexable_ref,
   6993                     .rhs = index,
   6994                 });
   6995             };
   6996 
   6997             capture_scope.* = .{
   6998                 .parent = capture_sub_scope,
   6999                 .gen_zir = &then_scope,
   7000                 .name = name_str_index,
   7001                 .inst = capture_inst,
   7002                 .token_src = ident_tok,
   7003                 .id_cat = .capture,
   7004             };
   7005 
   7006             try then_scope.addDbgVar(.dbg_var_val, name_str_index, capture_inst);
   7007             capture_sub_scope = &capture_scope.base;
   7008         }
   7009 
   7010         break :blk capture_sub_scope;
   7011     };
   7012 
   7013     const then_result = try fullBodyExpr(&then_scope, then_sub_scope, .{ .rl = .none }, then_node, .allow_branch_hint);
   7014     _ = try addEnsureResult(&then_scope, then_result, then_node);
   7015 
   7016     try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
   7017 
   7018     const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
   7019 
   7020     _ = try then_scope.addBreak(break_tag, cond_block, .void_value);
   7021 
   7022     var else_scope = parent_gz.makeSubBlock(&cond_scope.base);
   7023     defer else_scope.unstack();
   7024 
   7025     const else_node = for_full.ast.else_expr;
   7026     if (else_node != 0) {
   7027         const sub_scope = &else_scope.base;
   7028         // Remove the continue block and break block so that `continue` and `break`
   7029         // control flow apply to outer loops; not this one.
   7030         loop_scope.continue_block = .none;
   7031         loop_scope.break_block = .none;
   7032         const else_result = try fullBodyExpr(&else_scope, sub_scope, loop_scope.break_result_info, else_node, .allow_branch_hint);
   7033         if (is_statement) {
   7034             _ = try addEnsureResult(&else_scope, else_result, else_node);
   7035         }
   7036         if (!else_scope.endsWithNoReturn()) {
   7037             _ = try else_scope.addBreakWithSrcNode(break_tag, loop_block, else_result, else_node);
   7038         }
   7039     } else {
   7040         const result = try rvalue(&else_scope, ri, .void_value, node);
   7041         _ = try else_scope.addBreak(break_tag, loop_block, result);
   7042     }
   7043 
   7044     if (loop_scope.label) |some| {
   7045         if (!some.used) {
   7046             try astgen.appendErrorTok(some.token, "unused for loop label", .{});
   7047         }
   7048     }
   7049 
   7050     try setCondBrPayload(condbr, cond, &then_scope, &else_scope);
   7051 
   7052     // then_block and else_block unstacked now, can resurrect loop_scope to finally finish it
   7053     {
   7054         loop_scope.instructions_top = loop_scope.instructions.items.len;
   7055         try loop_scope.instructions.appendSlice(gpa, &.{ index.toIndex().?, cond_block });
   7056 
   7057         // Increment the index variable.
   7058         const index_plus_one = try loop_scope.addPlNode(.add_unsafe, node, Zir.Inst.Bin{
   7059             .lhs = index,
   7060             .rhs = .one_usize,
   7061         });
   7062         _ = try loop_scope.addPlNode(.store_node, node, Zir.Inst.Bin{
   7063             .lhs = index_ptr,
   7064             .rhs = index_plus_one,
   7065         });
   7066         const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
   7067         _ = try loop_scope.addNode(repeat_tag, node);
   7068 
   7069         try loop_scope.setBlockBody(loop_block);
   7070     }
   7071 
   7072     const result = if (need_result_rvalue)
   7073         try rvalue(parent_gz, ri, loop_block.toRef(), node)
   7074     else
   7075         loop_block.toRef();
   7076 
   7077     if (is_statement) {
   7078         _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
   7079     }
   7080     return result;
   7081 }
   7082 
   7083 fn switchExprErrUnion(
   7084     parent_gz: *GenZir,
   7085     scope: *Scope,
   7086     ri: ResultInfo,
   7087     catch_or_if_node: Ast.Node.Index,
   7088     node_ty: enum { @"catch", @"if" },
   7089 ) InnerError!Zir.Inst.Ref {
   7090     const astgen = parent_gz.astgen;
   7091     const gpa = astgen.gpa;
   7092     const tree = astgen.tree;
   7093     const node_datas = tree.nodes.items(.data);
   7094     const node_tags = tree.nodes.items(.tag);
   7095     const main_tokens = tree.nodes.items(.main_token);
   7096     const token_tags = tree.tokens.items(.tag);
   7097 
   7098     const if_full = switch (node_ty) {
   7099         .@"catch" => undefined,
   7100         .@"if" => tree.fullIf(catch_or_if_node).?,
   7101     };
   7102 
   7103     const switch_node, const operand_node, const error_payload = switch (node_ty) {
   7104         .@"catch" => .{
   7105             node_datas[catch_or_if_node].rhs,
   7106             node_datas[catch_or_if_node].lhs,
   7107             main_tokens[catch_or_if_node] + 2,
   7108         },
   7109         .@"if" => .{
   7110             if_full.ast.else_expr,
   7111             if_full.ast.cond_expr,
   7112             if_full.error_token.?,
   7113         },
   7114     };
   7115     assert(node_tags[switch_node] == .@"switch" or node_tags[switch_node] == .switch_comma);
   7116 
   7117     const do_err_trace = astgen.fn_block != null;
   7118 
   7119     const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
   7120     const case_nodes = tree.extra_data[extra.start..extra.end];
   7121 
   7122     const need_rl = astgen.nodes_need_rl.contains(catch_or_if_node);
   7123     const block_ri: ResultInfo = if (need_rl) ri else .{
   7124         .rl = switch (ri.rl) {
   7125             .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, catch_or_if_node)).? },
   7126             .inferred_ptr => .none,
   7127             else => ri.rl,
   7128         },
   7129         .ctx = ri.ctx,
   7130     };
   7131 
   7132     const payload_is_ref = switch (node_ty) {
   7133         .@"if" => if_full.payload_token != null and token_tags[if_full.payload_token.?] == .asterisk,
   7134         .@"catch" => ri.rl == .ref or ri.rl == .ref_coerced_ty,
   7135     };
   7136 
   7137     // We need to call `rvalue` to write through to the pointer only if we had a
   7138     // result pointer and aren't forwarding it.
   7139     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   7140     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   7141     var scalar_cases_len: u32 = 0;
   7142     var multi_cases_len: u32 = 0;
   7143     var inline_cases_len: u32 = 0;
   7144     var has_else = false;
   7145     var else_node: Ast.Node.Index = 0;
   7146     var else_src: ?Ast.TokenIndex = null;
   7147     for (case_nodes) |case_node| {
   7148         const case = tree.fullSwitchCase(case_node).?;
   7149 
   7150         if (case.ast.values.len == 0) {
   7151             const case_src = case.ast.arrow_token - 1;
   7152             if (else_src) |src| {
   7153                 return astgen.failTokNotes(
   7154                     case_src,
   7155                     "multiple else prongs in switch expression",
   7156                     .{},
   7157                     &[_]u32{
   7158                         try astgen.errNoteTok(
   7159                             src,
   7160                             "previous else prong here",
   7161                             .{},
   7162                         ),
   7163                     },
   7164                 );
   7165             }
   7166             has_else = true;
   7167             else_node = case_node;
   7168             else_src = case_src;
   7169             continue;
   7170         } else if (case.ast.values.len == 1 and
   7171             node_tags[case.ast.values[0]] == .identifier and
   7172             mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
   7173         {
   7174             const case_src = case.ast.arrow_token - 1;
   7175             return astgen.failTokNotes(
   7176                 case_src,
   7177                 "'_' prong is not allowed when switching on errors",
   7178                 .{},
   7179                 &[_]u32{
   7180                     try astgen.errNoteTok(
   7181                         case_src,
   7182                         "consider using 'else'",
   7183                         .{},
   7184                     ),
   7185                 },
   7186             );
   7187         }
   7188 
   7189         for (case.ast.values) |val| {
   7190             if (node_tags[val] == .string_literal)
   7191                 return astgen.failNode(val, "cannot switch on strings", .{});
   7192         }
   7193 
   7194         if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] != .switch_range) {
   7195             scalar_cases_len += 1;
   7196         } else {
   7197             multi_cases_len += 1;
   7198         }
   7199         if (case.inline_token != null) {
   7200             inline_cases_len += 1;
   7201         }
   7202     }
   7203 
   7204     const operand_ri: ResultInfo = .{
   7205         .rl = if (payload_is_ref) .ref else .none,
   7206         .ctx = .error_handling_expr,
   7207     };
   7208 
   7209     astgen.advanceSourceCursorToNode(operand_node);
   7210     const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
   7211 
   7212     const raw_operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, switch_node);
   7213     const item_ri: ResultInfo = .{ .rl = .none };
   7214 
   7215     // This contains the data that goes into the `extra` array for the SwitchBlockErrUnion, except
   7216     // the first cases_nodes.len slots are a table that indexes payloads later in the array,
   7217     // with the non-error and else case indices coming first, then scalar_cases_len indexes, then
   7218     // multi_cases_len indexes
   7219     const payloads = &astgen.scratch;
   7220     const scratch_top = astgen.scratch.items.len;
   7221     const case_table_start = scratch_top;
   7222     const scalar_case_table = case_table_start + 1 + @intFromBool(has_else);
   7223     const multi_case_table = scalar_case_table + scalar_cases_len;
   7224     const case_table_end = multi_case_table + multi_cases_len;
   7225 
   7226     try astgen.scratch.resize(gpa, case_table_end);
   7227     defer astgen.scratch.items.len = scratch_top;
   7228 
   7229     var block_scope = parent_gz.makeSubBlock(scope);
   7230     // block_scope not used for collecting instructions
   7231     block_scope.instructions_top = GenZir.unstacked_top;
   7232     block_scope.setBreakResultInfo(block_ri);
   7233 
   7234     // Sema expects a dbg_stmt immediately before switch_block_err_union
   7235     try emitDbgStmtForceCurrentIndex(parent_gz, operand_lc);
   7236     // This gets added to the parent block later, after the item expressions.
   7237     const switch_block = try parent_gz.makeBlockInst(.switch_block_err_union, switch_node);
   7238 
   7239     // We re-use this same scope for all cases, including the special prong, if any.
   7240     var case_scope = parent_gz.makeSubBlock(&block_scope.base);
   7241     case_scope.instructions_top = GenZir.unstacked_top;
   7242 
   7243     {
   7244         const body_len_index: u32 = @intCast(payloads.items.len);
   7245         payloads.items[case_table_start] = body_len_index;
   7246         try payloads.resize(gpa, body_len_index + 1); // body_len
   7247 
   7248         case_scope.instructions_top = parent_gz.instructions.items.len;
   7249         defer case_scope.unstack();
   7250 
   7251         const unwrap_payload_tag: Zir.Inst.Tag = if (payload_is_ref)
   7252             .err_union_payload_unsafe_ptr
   7253         else
   7254             .err_union_payload_unsafe;
   7255 
   7256         const unwrapped_payload = try case_scope.addUnNode(
   7257             unwrap_payload_tag,
   7258             raw_operand,
   7259             catch_or_if_node,
   7260         );
   7261 
   7262         switch (node_ty) {
   7263             .@"catch" => {
   7264                 const case_result = switch (ri.rl) {
   7265                     .ref, .ref_coerced_ty => unwrapped_payload,
   7266                     else => try rvalue(
   7267                         &case_scope,
   7268                         block_scope.break_result_info,
   7269                         unwrapped_payload,
   7270                         catch_or_if_node,
   7271                     ),
   7272                 };
   7273                 _ = try case_scope.addBreakWithSrcNode(
   7274                     .@"break",
   7275                     switch_block,
   7276                     case_result,
   7277                     catch_or_if_node,
   7278                 );
   7279             },
   7280             .@"if" => {
   7281                 var payload_val_scope: Scope.LocalVal = undefined;
   7282 
   7283                 const then_node = if_full.ast.then_expr;
   7284                 const then_sub_scope = s: {
   7285                     assert(if_full.error_token != null);
   7286                     if (if_full.payload_token) |payload_token| {
   7287                         const token_name_index = payload_token + @intFromBool(payload_is_ref);
   7288                         const ident_name = try astgen.identAsString(token_name_index);
   7289                         const token_name_str = tree.tokenSlice(token_name_index);
   7290                         if (mem.eql(u8, "_", token_name_str))
   7291                             break :s &case_scope.base;
   7292                         try astgen.detectLocalShadowing(
   7293                             &case_scope.base,
   7294                             ident_name,
   7295                             token_name_index,
   7296                             token_name_str,
   7297                             .capture,
   7298                         );
   7299                         payload_val_scope = .{
   7300                             .parent = &case_scope.base,
   7301                             .gen_zir = &case_scope,
   7302                             .name = ident_name,
   7303                             .inst = unwrapped_payload,
   7304                             .token_src = token_name_index,
   7305                             .id_cat = .capture,
   7306                         };
   7307                         try case_scope.addDbgVar(.dbg_var_val, ident_name, unwrapped_payload);
   7308                         break :s &payload_val_scope.base;
   7309                     } else {
   7310                         _ = try case_scope.addUnNode(
   7311                             .ensure_err_union_payload_void,
   7312                             raw_operand,
   7313                             catch_or_if_node,
   7314                         );
   7315                         break :s &case_scope.base;
   7316                     }
   7317                 };
   7318                 const then_result = try expr(
   7319                     &case_scope,
   7320                     then_sub_scope,
   7321                     block_scope.break_result_info,
   7322                     then_node,
   7323                 );
   7324                 try checkUsed(parent_gz, &case_scope.base, then_sub_scope);
   7325                 if (!case_scope.endsWithNoReturn()) {
   7326                     _ = try case_scope.addBreakWithSrcNode(
   7327                         .@"break",
   7328                         switch_block,
   7329                         then_result,
   7330                         then_node,
   7331                     );
   7332                 }
   7333             },
   7334         }
   7335 
   7336         const case_slice = case_scope.instructionsSlice();
   7337         // Since we use the switch_block_err_union instruction itself to refer
   7338         // to the capture, which will not be added to the child block, we need
   7339         // to handle ref_table manually.
   7340         const refs_len = refs: {
   7341             var n: usize = 0;
   7342             var check_inst = switch_block;
   7343             while (astgen.ref_table.get(check_inst)) |ref_inst| {
   7344                 n += 1;
   7345                 check_inst = ref_inst;
   7346             }
   7347             break :refs n;
   7348         };
   7349         const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice);
   7350         try payloads.ensureUnusedCapacity(gpa, body_len);
   7351         const capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = switch (node_ty) {
   7352             .@"catch" => .none,
   7353             .@"if" => if (if_full.payload_token == null)
   7354                 .none
   7355             else if (payload_is_ref)
   7356                 .by_ref
   7357             else
   7358                 .by_val,
   7359         };
   7360         payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
   7361             .body_len = @intCast(body_len),
   7362             .capture = capture,
   7363             .is_inline = false,
   7364             .has_tag_capture = false,
   7365         });
   7366         if (astgen.ref_table.fetchRemove(switch_block)) |kv| {
   7367             appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
   7368         }
   7369         appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
   7370     }
   7371 
   7372     const err_name = blk: {
   7373         const err_str = tree.tokenSlice(error_payload);
   7374         if (mem.eql(u8, err_str, "_")) {
   7375             return astgen.failTok(error_payload, "discard of error capture; omit it instead", .{});
   7376         }
   7377         const err_name = try astgen.identAsString(error_payload);
   7378         try astgen.detectLocalShadowing(scope, err_name, error_payload, err_str, .capture);
   7379 
   7380         break :blk err_name;
   7381     };
   7382 
   7383     // allocate a shared dummy instruction for the error capture
   7384     const err_inst = err_inst: {
   7385         const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
   7386         try astgen.instructions.append(astgen.gpa, .{
   7387             .tag = .extended,
   7388             .data = .{ .extended = .{
   7389                 .opcode = .value_placeholder,
   7390                 .small = undefined,
   7391                 .operand = undefined,
   7392             } },
   7393         });
   7394         break :err_inst inst;
   7395     };
   7396 
   7397     // In this pass we generate all the item and prong expressions for error cases.
   7398     var multi_case_index: u32 = 0;
   7399     var scalar_case_index: u32 = 0;
   7400     var any_uses_err_capture = false;
   7401     for (case_nodes) |case_node| {
   7402         const case = tree.fullSwitchCase(case_node).?;
   7403 
   7404         const is_multi_case = case.ast.values.len > 1 or
   7405             (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .switch_range);
   7406 
   7407         var dbg_var_name: Zir.NullTerminatedString = .empty;
   7408         var dbg_var_inst: Zir.Inst.Ref = undefined;
   7409         var err_scope: Scope.LocalVal = undefined;
   7410         var capture_scope: Scope.LocalVal = undefined;
   7411 
   7412         const sub_scope = blk: {
   7413             err_scope = .{
   7414                 .parent = &case_scope.base,
   7415                 .gen_zir = &case_scope,
   7416                 .name = err_name,
   7417                 .inst = err_inst.toRef(),
   7418                 .token_src = error_payload,
   7419                 .id_cat = .capture,
   7420             };
   7421 
   7422             const capture_token = case.payload_token orelse break :blk &err_scope.base;
   7423             if (token_tags[capture_token] != .identifier) {
   7424                 return astgen.failTok(capture_token + 1, "error set cannot be captured by reference", .{});
   7425             }
   7426 
   7427             const capture_slice = tree.tokenSlice(capture_token);
   7428             if (mem.eql(u8, capture_slice, "_")) {
   7429                 return astgen.failTok(capture_token, "discard of error capture; omit it instead", .{});
   7430             }
   7431             const tag_name = try astgen.identAsString(capture_token);
   7432             try astgen.detectLocalShadowing(&case_scope.base, tag_name, capture_token, capture_slice, .capture);
   7433 
   7434             capture_scope = .{
   7435                 .parent = &case_scope.base,
   7436                 .gen_zir = &case_scope,
   7437                 .name = tag_name,
   7438                 .inst = switch_block.toRef(),
   7439                 .token_src = capture_token,
   7440                 .id_cat = .capture,
   7441             };
   7442             dbg_var_name = tag_name;
   7443             dbg_var_inst = switch_block.toRef();
   7444 
   7445             err_scope.parent = &capture_scope.base;
   7446 
   7447             break :blk &err_scope.base;
   7448         };
   7449 
   7450         const header_index: u32 = @intCast(payloads.items.len);
   7451         const body_len_index = if (is_multi_case) blk: {
   7452             payloads.items[multi_case_table + multi_case_index] = header_index;
   7453             multi_case_index += 1;
   7454             try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len
   7455 
   7456             // items
   7457             var items_len: u32 = 0;
   7458             for (case.ast.values) |item_node| {
   7459                 if (node_tags[item_node] == .switch_range) continue;
   7460                 items_len += 1;
   7461 
   7462                 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node);
   7463                 try payloads.append(gpa, @intFromEnum(item_inst));
   7464             }
   7465 
   7466             // ranges
   7467             var ranges_len: u32 = 0;
   7468             for (case.ast.values) |range| {
   7469                 if (node_tags[range] != .switch_range) continue;
   7470                 ranges_len += 1;
   7471 
   7472                 const first = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].lhs);
   7473                 const last = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].rhs);
   7474                 try payloads.appendSlice(gpa, &[_]u32{
   7475                     @intFromEnum(first), @intFromEnum(last),
   7476                 });
   7477             }
   7478 
   7479             payloads.items[header_index] = items_len;
   7480             payloads.items[header_index + 1] = ranges_len;
   7481             break :blk header_index + 2;
   7482         } else if (case_node == else_node) blk: {
   7483             payloads.items[case_table_start + 1] = header_index;
   7484             try payloads.resize(gpa, header_index + 1); // body_len
   7485             break :blk header_index;
   7486         } else blk: {
   7487             payloads.items[scalar_case_table + scalar_case_index] = header_index;
   7488             scalar_case_index += 1;
   7489             try payloads.resize(gpa, header_index + 2); // item, body_len
   7490             const item_node = case.ast.values[0];
   7491             const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node);
   7492             payloads.items[header_index] = @intFromEnum(item_inst);
   7493             break :blk header_index + 1;
   7494         };
   7495 
   7496         {
   7497             // temporarily stack case_scope on parent_gz
   7498             case_scope.instructions_top = parent_gz.instructions.items.len;
   7499             defer case_scope.unstack();
   7500 
   7501             if (do_err_trace and nodeMayAppendToErrorTrace(tree, operand_node))
   7502                 _ = try case_scope.addSaveErrRetIndex(.always);
   7503 
   7504             if (dbg_var_name != .empty) {
   7505                 try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst);
   7506             }
   7507 
   7508             const target_expr_node = case.ast.target_expr;
   7509             const case_result = try fullBodyExpr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node, .allow_branch_hint);
   7510             // check capture_scope, not err_scope to avoid false positive unused error capture
   7511             try checkUsed(parent_gz, &case_scope.base, err_scope.parent);
   7512             const uses_err = err_scope.used != 0 or err_scope.discarded != 0;
   7513             if (uses_err) {
   7514                 try case_scope.addDbgVar(.dbg_var_val, err_name, err_inst.toRef());
   7515                 any_uses_err_capture = true;
   7516             }
   7517 
   7518             if (!parent_gz.refIsNoReturn(case_result)) {
   7519                 if (do_err_trace)
   7520                     try restoreErrRetIndex(
   7521                         &case_scope,
   7522                         .{ .block = switch_block },
   7523                         block_scope.break_result_info,
   7524                         target_expr_node,
   7525                         case_result,
   7526                     );
   7527 
   7528                 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node);
   7529             }
   7530 
   7531             const case_slice = case_scope.instructionsSlice();
   7532             // Since we use the switch_block_err_union instruction itself to refer
   7533             // to the capture, which will not be added to the child block, we need
   7534             // to handle ref_table manually.
   7535             const refs_len = refs: {
   7536                 var n: usize = 0;
   7537                 var check_inst = switch_block;
   7538                 while (astgen.ref_table.get(check_inst)) |ref_inst| {
   7539                     n += 1;
   7540                     check_inst = ref_inst;
   7541                 }
   7542                 if (uses_err) {
   7543                     check_inst = err_inst;
   7544                     while (astgen.ref_table.get(check_inst)) |ref_inst| {
   7545                         n += 1;
   7546                         check_inst = ref_inst;
   7547                     }
   7548                 }
   7549                 break :refs n;
   7550             };
   7551             const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice);
   7552             try payloads.ensureUnusedCapacity(gpa, body_len);
   7553             payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
   7554                 .body_len = @intCast(body_len),
   7555                 .capture = if (case.payload_token != null) .by_val else .none,
   7556                 .is_inline = case.inline_token != null,
   7557                 .has_tag_capture = false,
   7558             });
   7559             if (astgen.ref_table.fetchRemove(switch_block)) |kv| {
   7560                 appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
   7561             }
   7562             if (uses_err) {
   7563                 if (astgen.ref_table.fetchRemove(err_inst)) |kv| {
   7564                     appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
   7565                 }
   7566             }
   7567             appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
   7568         }
   7569     }
   7570     // Now that the item expressions are generated we can add this.
   7571     try parent_gz.instructions.append(gpa, switch_block);
   7572 
   7573     try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlockErrUnion).@"struct".fields.len +
   7574         @intFromBool(multi_cases_len != 0) +
   7575         payloads.items.len - case_table_end +
   7576         (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).@"struct".fields.len);
   7577 
   7578     const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlockErrUnion{
   7579         .operand = raw_operand,
   7580         .bits = Zir.Inst.SwitchBlockErrUnion.Bits{
   7581             .has_multi_cases = multi_cases_len != 0,
   7582             .has_else = has_else,
   7583             .scalar_cases_len = @intCast(scalar_cases_len),
   7584             .any_uses_err_capture = any_uses_err_capture,
   7585             .payload_is_ref = payload_is_ref,
   7586         },
   7587         .main_src_node_offset = parent_gz.nodeIndexToRelative(catch_or_if_node),
   7588     });
   7589 
   7590     if (multi_cases_len != 0) {
   7591         astgen.extra.appendAssumeCapacity(multi_cases_len);
   7592     }
   7593 
   7594     if (any_uses_err_capture) {
   7595         astgen.extra.appendAssumeCapacity(@intFromEnum(err_inst));
   7596     }
   7597 
   7598     const zir_datas = astgen.instructions.items(.data);
   7599     zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;
   7600 
   7601     for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| {
   7602         var body_len_index = start_index;
   7603         var end_index = start_index;
   7604         const table_index = case_table_start + i;
   7605         if (table_index < scalar_case_table) {
   7606             end_index += 1;
   7607         } else if (table_index < multi_case_table) {
   7608             body_len_index += 1;
   7609             end_index += 2;
   7610         } else {
   7611             body_len_index += 2;
   7612             const items_len = payloads.items[start_index];
   7613             const ranges_len = payloads.items[start_index + 1];
   7614             end_index += 3 + items_len + 2 * ranges_len;
   7615         }
   7616         const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
   7617         end_index += prong_info.body_len;
   7618         astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
   7619     }
   7620 
   7621     if (need_result_rvalue) {
   7622         return rvalue(parent_gz, ri, switch_block.toRef(), switch_node);
   7623     } else {
   7624         return switch_block.toRef();
   7625     }
   7626 }
   7627 
   7628 fn switchExpr(
   7629     parent_gz: *GenZir,
   7630     scope: *Scope,
   7631     ri: ResultInfo,
   7632     node: Ast.Node.Index,
   7633     switch_full: Ast.full.Switch,
   7634 ) InnerError!Zir.Inst.Ref {
   7635     const astgen = parent_gz.astgen;
   7636     const gpa = astgen.gpa;
   7637     const tree = astgen.tree;
   7638     const node_datas = tree.nodes.items(.data);
   7639     const node_tags = tree.nodes.items(.tag);
   7640     const main_tokens = tree.nodes.items(.main_token);
   7641     const token_tags = tree.tokens.items(.tag);
   7642     const operand_node = switch_full.ast.condition;
   7643     const case_nodes = switch_full.ast.cases;
   7644 
   7645     const need_rl = astgen.nodes_need_rl.contains(node);
   7646     const block_ri: ResultInfo = if (need_rl) ri else .{
   7647         .rl = switch (ri.rl) {
   7648             .ptr => .{ .ty = (try ri.rl.resultType(parent_gz, node)).? },
   7649             .inferred_ptr => .none,
   7650             else => ri.rl,
   7651         },
   7652         .ctx = ri.ctx,
   7653     };
   7654     // We need to call `rvalue` to write through to the pointer only if we had a
   7655     // result pointer and aren't forwarding it.
   7656     const LocTag = @typeInfo(ResultInfo.Loc).@"union".tag_type.?;
   7657     const need_result_rvalue = @as(LocTag, block_ri.rl) != @as(LocTag, ri.rl);
   7658 
   7659     if (switch_full.label_token) |label_token| {
   7660         try astgen.checkLabelRedefinition(scope, label_token);
   7661     }
   7662 
   7663     // We perform two passes over the AST. This first pass is to collect information
   7664     // for the following variables, make note of the special prong AST node index,
   7665     // and bail out with a compile error if there are multiple special prongs present.
   7666     var any_payload_is_ref = false;
   7667     var any_has_tag_capture = false;
   7668     var any_non_inline_capture = false;
   7669     var scalar_cases_len: u32 = 0;
   7670     var multi_cases_len: u32 = 0;
   7671     var inline_cases_len: u32 = 0;
   7672     var special_prong: Zir.SpecialProng = .none;
   7673     var special_node: Ast.Node.Index = 0;
   7674     var else_src: ?Ast.TokenIndex = null;
   7675     var underscore_src: ?Ast.TokenIndex = null;
   7676     for (case_nodes) |case_node| {
   7677         const case = tree.fullSwitchCase(case_node).?;
   7678         if (case.payload_token) |payload_token| {
   7679             const ident = if (token_tags[payload_token] == .asterisk) blk: {
   7680                 any_payload_is_ref = true;
   7681                 break :blk payload_token + 1;
   7682             } else payload_token;
   7683             if (token_tags[ident + 1] == .comma) {
   7684                 any_has_tag_capture = true;
   7685             }
   7686 
   7687             // If the first capture is ignored, then there is no runtime-known
   7688             // capture, as the tag capture must be for an inline prong.
   7689             // This check isn't perfect, because for things like enums, the
   7690             // first prong *is* comptime-known for inline prongs! But such
   7691             // knowledge requires semantic analysis.
   7692             if (!mem.eql(u8, tree.tokenSlice(ident), "_")) {
   7693                 any_non_inline_capture = true;
   7694             }
   7695         }
   7696         // Check for else/`_` prong.
   7697         if (case.ast.values.len == 0) {
   7698             const case_src = case.ast.arrow_token - 1;
   7699             if (else_src) |src| {
   7700                 return astgen.failTokNotes(
   7701                     case_src,
   7702                     "multiple else prongs in switch expression",
   7703                     .{},
   7704                     &[_]u32{
   7705                         try astgen.errNoteTok(
   7706                             src,
   7707                             "previous else prong here",
   7708                             .{},
   7709                         ),
   7710                     },
   7711                 );
   7712             } else if (underscore_src) |some_underscore| {
   7713                 return astgen.failNodeNotes(
   7714                     node,
   7715                     "else and '_' prong in switch expression",
   7716                     .{},
   7717                     &[_]u32{
   7718                         try astgen.errNoteTok(
   7719                             case_src,
   7720                             "else prong here",
   7721                             .{},
   7722                         ),
   7723                         try astgen.errNoteTok(
   7724                             some_underscore,
   7725                             "'_' prong here",
   7726                             .{},
   7727                         ),
   7728                     },
   7729                 );
   7730             }
   7731             special_node = case_node;
   7732             special_prong = .@"else";
   7733             else_src = case_src;
   7734             continue;
   7735         } else if (case.ast.values.len == 1 and
   7736             node_tags[case.ast.values[0]] == .identifier and
   7737             mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
   7738         {
   7739             const case_src = case.ast.arrow_token - 1;
   7740             if (underscore_src) |src| {
   7741                 return astgen.failTokNotes(
   7742                     case_src,
   7743                     "multiple '_' prongs in switch expression",
   7744                     .{},
   7745                     &[_]u32{
   7746                         try astgen.errNoteTok(
   7747                             src,
   7748                             "previous '_' prong here",
   7749                             .{},
   7750                         ),
   7751                     },
   7752                 );
   7753             } else if (else_src) |some_else| {
   7754                 return astgen.failNodeNotes(
   7755                     node,
   7756                     "else and '_' prong in switch expression",
   7757                     .{},
   7758                     &[_]u32{
   7759                         try astgen.errNoteTok(
   7760                             some_else,
   7761                             "else prong here",
   7762                             .{},
   7763                         ),
   7764                         try astgen.errNoteTok(
   7765                             case_src,
   7766                             "'_' prong here",
   7767                             .{},
   7768                         ),
   7769                     },
   7770                 );
   7771             }
   7772             if (case.inline_token != null) {
   7773                 return astgen.failTok(case_src, "cannot inline '_' prong", .{});
   7774             }
   7775             special_node = case_node;
   7776             special_prong = .under;
   7777             underscore_src = case_src;
   7778             continue;
   7779         }
   7780 
   7781         for (case.ast.values) |val| {
   7782             if (node_tags[val] == .string_literal)
   7783                 return astgen.failNode(val, "cannot switch on strings", .{});
   7784         }
   7785 
   7786         if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] != .switch_range) {
   7787             scalar_cases_len += 1;
   7788         } else {
   7789             multi_cases_len += 1;
   7790         }
   7791         if (case.inline_token != null) {
   7792             inline_cases_len += 1;
   7793         }
   7794     }
   7795 
   7796     const operand_ri: ResultInfo = .{ .rl = if (any_payload_is_ref) .ref else .none };
   7797 
   7798     astgen.advanceSourceCursorToNode(operand_node);
   7799     const operand_lc = LineColumn{ astgen.source_line - parent_gz.decl_line, astgen.source_column };
   7800 
   7801     const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node);
   7802     const item_ri: ResultInfo = .{ .rl = .none };
   7803 
   7804     // If this switch is labeled, it may have `continue`s targeting it, and thus we need the operand type
   7805     // to provide a result type.
   7806     const raw_operand_ty_ref = if (switch_full.label_token != null) t: {
   7807         break :t try parent_gz.addUnNode(.typeof, raw_operand, operand_node);
   7808     } else undefined;
   7809 
   7810     // This contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti,
   7811     // except the first cases_nodes.len slots are a table that indexes payloads later in the array, with
   7812     // the special case index coming first, then scalar_case_len indexes, then multi_cases_len indexes
   7813     const payloads = &astgen.scratch;
   7814     const scratch_top = astgen.scratch.items.len;
   7815     const case_table_start = scratch_top;
   7816     const scalar_case_table = case_table_start + @intFromBool(special_prong != .none);
   7817     const multi_case_table = scalar_case_table + scalar_cases_len;
   7818     const case_table_end = multi_case_table + multi_cases_len;
   7819     try astgen.scratch.resize(gpa, case_table_end);
   7820     defer astgen.scratch.items.len = scratch_top;
   7821 
   7822     var block_scope = parent_gz.makeSubBlock(scope);
   7823     // block_scope not used for collecting instructions
   7824     block_scope.instructions_top = GenZir.unstacked_top;
   7825     block_scope.setBreakResultInfo(block_ri);
   7826 
   7827     // Sema expects a dbg_stmt immediately before switch_block(_ref)
   7828     try emitDbgStmtForceCurrentIndex(parent_gz, operand_lc);
   7829     // This gets added to the parent block later, after the item expressions.
   7830     const switch_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_block_ref else .switch_block;
   7831     const switch_block = try parent_gz.makeBlockInst(switch_tag, node);
   7832 
   7833     if (switch_full.label_token) |label_token| {
   7834         block_scope.continue_block = switch_block.toOptional();
   7835         block_scope.continue_result_info = .{
   7836             .rl = if (any_payload_is_ref)
   7837                 .{ .ref_coerced_ty = raw_operand_ty_ref }
   7838             else
   7839                 .{ .coerced_ty = raw_operand_ty_ref },
   7840         };
   7841 
   7842         block_scope.label = .{
   7843             .token = label_token,
   7844             .block_inst = switch_block,
   7845         };
   7846         // `break` can target this via `label.block_inst`
   7847         // `break_result_info` already set by `setBreakResultInfo`
   7848     }
   7849 
   7850     // We re-use this same scope for all cases, including the special prong, if any.
   7851     var case_scope = parent_gz.makeSubBlock(&block_scope.base);
   7852     case_scope.instructions_top = GenZir.unstacked_top;
   7853 
   7854     // If any prong has an inline tag capture, allocate a shared dummy instruction for it
   7855     const tag_inst = if (any_has_tag_capture) tag_inst: {
   7856         const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
   7857         try astgen.instructions.append(astgen.gpa, .{
   7858             .tag = .extended,
   7859             .data = .{ .extended = .{
   7860                 .opcode = .value_placeholder,
   7861                 .small = undefined,
   7862                 .operand = undefined,
   7863             } },
   7864         });
   7865         break :tag_inst inst;
   7866     } else undefined;
   7867 
   7868     // In this pass we generate all the item and prong expressions.
   7869     var multi_case_index: u32 = 0;
   7870     var scalar_case_index: u32 = 0;
   7871     for (case_nodes) |case_node| {
   7872         const case = tree.fullSwitchCase(case_node).?;
   7873 
   7874         const is_multi_case = case.ast.values.len > 1 or
   7875             (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .switch_range);
   7876 
   7877         var dbg_var_name: Zir.NullTerminatedString = .empty;
   7878         var dbg_var_inst: Zir.Inst.Ref = undefined;
   7879         var dbg_var_tag_name: Zir.NullTerminatedString = .empty;
   7880         var dbg_var_tag_inst: Zir.Inst.Ref = undefined;
   7881         var has_tag_capture = false;
   7882         var capture_val_scope: Scope.LocalVal = undefined;
   7883         var tag_scope: Scope.LocalVal = undefined;
   7884 
   7885         var capture: Zir.Inst.SwitchBlock.ProngInfo.Capture = .none;
   7886 
   7887         const sub_scope = blk: {
   7888             const payload_token = case.payload_token orelse break :blk &case_scope.base;
   7889             const ident = if (token_tags[payload_token] == .asterisk)
   7890                 payload_token + 1
   7891             else
   7892                 payload_token;
   7893 
   7894             const is_ptr = ident != payload_token;
   7895             capture = if (is_ptr) .by_ref else .by_val;
   7896 
   7897             const ident_slice = tree.tokenSlice(ident);
   7898             var payload_sub_scope: *Scope = undefined;
   7899             if (mem.eql(u8, ident_slice, "_")) {
   7900                 if (is_ptr) {
   7901                     return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   7902                 }
   7903                 payload_sub_scope = &case_scope.base;
   7904             } else {
   7905                 const capture_name = try astgen.identAsString(ident);
   7906                 try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice, .capture);
   7907                 capture_val_scope = .{
   7908                     .parent = &case_scope.base,
   7909                     .gen_zir = &case_scope,
   7910                     .name = capture_name,
   7911                     .inst = switch_block.toRef(),
   7912                     .token_src = ident,
   7913                     .id_cat = .capture,
   7914                 };
   7915                 dbg_var_name = capture_name;
   7916                 dbg_var_inst = switch_block.toRef();
   7917                 payload_sub_scope = &capture_val_scope.base;
   7918             }
   7919 
   7920             const tag_token = if (token_tags[ident + 1] == .comma)
   7921                 ident + 2
   7922             else
   7923                 break :blk payload_sub_scope;
   7924             const tag_slice = tree.tokenSlice(tag_token);
   7925             if (mem.eql(u8, tag_slice, "_")) {
   7926                 return astgen.failTok(tag_token, "discard of tag capture; omit it instead", .{});
   7927             } else if (case.inline_token == null) {
   7928                 return astgen.failTok(tag_token, "tag capture on non-inline prong", .{});
   7929             }
   7930             const tag_name = try astgen.identAsString(tag_token);
   7931             try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice, .@"switch tag capture");
   7932 
   7933             assert(any_has_tag_capture);
   7934             has_tag_capture = true;
   7935 
   7936             tag_scope = .{
   7937                 .parent = payload_sub_scope,
   7938                 .gen_zir = &case_scope,
   7939                 .name = tag_name,
   7940                 .inst = tag_inst.toRef(),
   7941                 .token_src = tag_token,
   7942                 .id_cat = .@"switch tag capture",
   7943             };
   7944             dbg_var_tag_name = tag_name;
   7945             dbg_var_tag_inst = tag_inst.toRef();
   7946             break :blk &tag_scope.base;
   7947         };
   7948 
   7949         const header_index: u32 = @intCast(payloads.items.len);
   7950         const body_len_index = if (is_multi_case) blk: {
   7951             payloads.items[multi_case_table + multi_case_index] = header_index;
   7952             multi_case_index += 1;
   7953             try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len
   7954 
   7955             // items
   7956             var items_len: u32 = 0;
   7957             for (case.ast.values) |item_node| {
   7958                 if (node_tags[item_node] == .switch_range) continue;
   7959                 items_len += 1;
   7960 
   7961                 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node);
   7962                 try payloads.append(gpa, @intFromEnum(item_inst));
   7963             }
   7964 
   7965             // ranges
   7966             var ranges_len: u32 = 0;
   7967             for (case.ast.values) |range| {
   7968                 if (node_tags[range] != .switch_range) continue;
   7969                 ranges_len += 1;
   7970 
   7971                 const first = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].lhs);
   7972                 const last = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].rhs);
   7973                 try payloads.appendSlice(gpa, &[_]u32{
   7974                     @intFromEnum(first), @intFromEnum(last),
   7975                 });
   7976             }
   7977 
   7978             payloads.items[header_index] = items_len;
   7979             payloads.items[header_index + 1] = ranges_len;
   7980             break :blk header_index + 2;
   7981         } else if (case_node == special_node) blk: {
   7982             payloads.items[case_table_start] = header_index;
   7983             try payloads.resize(gpa, header_index + 1); // body_len
   7984             break :blk header_index;
   7985         } else blk: {
   7986             payloads.items[scalar_case_table + scalar_case_index] = header_index;
   7987             scalar_case_index += 1;
   7988             try payloads.resize(gpa, header_index + 2); // item, body_len
   7989             const item_node = case.ast.values[0];
   7990             const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node);
   7991             payloads.items[header_index] = @intFromEnum(item_inst);
   7992             break :blk header_index + 1;
   7993         };
   7994 
   7995         {
   7996             // temporarily stack case_scope on parent_gz
   7997             case_scope.instructions_top = parent_gz.instructions.items.len;
   7998             defer case_scope.unstack();
   7999 
   8000             if (dbg_var_name != .empty) {
   8001                 try case_scope.addDbgVar(.dbg_var_val, dbg_var_name, dbg_var_inst);
   8002             }
   8003             if (dbg_var_tag_name != .empty) {
   8004                 try case_scope.addDbgVar(.dbg_var_val, dbg_var_tag_name, dbg_var_tag_inst);
   8005             }
   8006             const target_expr_node = case.ast.target_expr;
   8007             const case_result = try fullBodyExpr(&case_scope, sub_scope, block_scope.break_result_info, target_expr_node, .allow_branch_hint);
   8008             try checkUsed(parent_gz, &case_scope.base, sub_scope);
   8009             if (!parent_gz.refIsNoReturn(case_result)) {
   8010                 _ = try case_scope.addBreakWithSrcNode(.@"break", switch_block, case_result, target_expr_node);
   8011             }
   8012 
   8013             const case_slice = case_scope.instructionsSlice();
   8014             // Since we use the switch_block instruction itself to refer to the
   8015             // capture, which will not be added to the child block, we need to
   8016             // handle ref_table manually, and the same for the inline tag
   8017             // capture instruction.
   8018             const refs_len = refs: {
   8019                 var n: usize = 0;
   8020                 var check_inst = switch_block;
   8021                 while (astgen.ref_table.get(check_inst)) |ref_inst| {
   8022                     n += 1;
   8023                     check_inst = ref_inst;
   8024                 }
   8025                 if (has_tag_capture) {
   8026                     check_inst = tag_inst;
   8027                     while (astgen.ref_table.get(check_inst)) |ref_inst| {
   8028                         n += 1;
   8029                         check_inst = ref_inst;
   8030                     }
   8031                 }
   8032                 break :refs n;
   8033             };
   8034             const body_len = refs_len + astgen.countBodyLenAfterFixups(case_slice);
   8035             try payloads.ensureUnusedCapacity(gpa, body_len);
   8036             payloads.items[body_len_index] = @bitCast(Zir.Inst.SwitchBlock.ProngInfo{
   8037                 .body_len = @intCast(body_len),
   8038                 .capture = capture,
   8039                 .is_inline = case.inline_token != null,
   8040                 .has_tag_capture = has_tag_capture,
   8041             });
   8042             if (astgen.ref_table.fetchRemove(switch_block)) |kv| {
   8043                 appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
   8044             }
   8045             if (has_tag_capture) {
   8046                 if (astgen.ref_table.fetchRemove(tag_inst)) |kv| {
   8047                     appendPossiblyRefdBodyInst(astgen, payloads, kv.value);
   8048                 }
   8049             }
   8050             appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
   8051         }
   8052     }
   8053 
   8054     if (switch_full.label_token) |label_token| if (!block_scope.label.?.used) {
   8055         try astgen.appendErrorTok(label_token, "unused switch label", .{});
   8056     };
   8057 
   8058     // Now that the item expressions are generated we can add this.
   8059     try parent_gz.instructions.append(gpa, switch_block);
   8060 
   8061     try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).@"struct".fields.len +
   8062         @intFromBool(multi_cases_len != 0) +
   8063         @intFromBool(any_has_tag_capture) +
   8064         payloads.items.len - case_table_end +
   8065         (case_table_end - case_table_start) * @typeInfo(Zir.Inst.As).@"struct".fields.len);
   8066 
   8067     const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
   8068         .operand = raw_operand,
   8069         .bits = Zir.Inst.SwitchBlock.Bits{
   8070             .has_multi_cases = multi_cases_len != 0,
   8071             .has_else = special_prong == .@"else",
   8072             .has_under = special_prong == .under,
   8073             .any_has_tag_capture = any_has_tag_capture,
   8074             .any_non_inline_capture = any_non_inline_capture,
   8075             .has_continue = switch_full.label_token != null and block_scope.label.?.used_for_continue,
   8076             .scalar_cases_len = @intCast(scalar_cases_len),
   8077         },
   8078     });
   8079 
   8080     if (multi_cases_len != 0) {
   8081         astgen.extra.appendAssumeCapacity(multi_cases_len);
   8082     }
   8083 
   8084     if (any_has_tag_capture) {
   8085         astgen.extra.appendAssumeCapacity(@intFromEnum(tag_inst));
   8086     }
   8087 
   8088     const zir_datas = astgen.instructions.items(.data);
   8089     zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;
   8090 
   8091     for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| {
   8092         var body_len_index = start_index;
   8093         var end_index = start_index;
   8094         const table_index = case_table_start + i;
   8095         if (table_index < scalar_case_table) {
   8096             end_index += 1;
   8097         } else if (table_index < multi_case_table) {
   8098             body_len_index += 1;
   8099             end_index += 2;
   8100         } else {
   8101             body_len_index += 2;
   8102             const items_len = payloads.items[start_index];
   8103             const ranges_len = payloads.items[start_index + 1];
   8104             end_index += 3 + items_len + 2 * ranges_len;
   8105         }
   8106         const prong_info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(payloads.items[body_len_index]);
   8107         end_index += prong_info.body_len;
   8108         astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
   8109     }
   8110 
   8111     if (need_result_rvalue) {
   8112         return rvalue(parent_gz, ri, switch_block.toRef(), node);
   8113     } else {
   8114         return switch_block.toRef();
   8115     }
   8116 }
   8117 
   8118 fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   8119     const astgen = gz.astgen;
   8120     const tree = astgen.tree;
   8121     const node_datas = tree.nodes.items(.data);
   8122     const node_tags = tree.nodes.items(.tag);
   8123 
   8124     if (astgen.fn_block == null) {
   8125         return astgen.failNode(node, "'return' outside function scope", .{});
   8126     }
   8127 
   8128     if (gz.any_defer_node != 0) {
   8129         return astgen.failNodeNotes(node, "cannot return from defer expression", .{}, &.{
   8130             try astgen.errNoteNode(
   8131                 gz.any_defer_node,
   8132                 "defer expression here",
   8133                 .{},
   8134             ),
   8135         });
   8136     }
   8137 
   8138     // Ensure debug line/column information is emitted for this return expression.
   8139     // Then we will save the line/column so that we can emit another one that goes
   8140     // "backwards" because we want to evaluate the operand, but then put the debug
   8141     // info back at the return keyword for error return tracing.
   8142     if (!gz.is_comptime) {
   8143         try emitDbgNode(gz, node);
   8144     }
   8145     const ret_lc = LineColumn{ astgen.source_line - gz.decl_line, astgen.source_column };
   8146 
   8147     const defer_outer = &astgen.fn_block.?.base;
   8148 
   8149     const operand_node = node_datas[node].lhs;
   8150     if (operand_node == 0) {
   8151         // Returning a void value; skip error defers.
   8152         try genDefers(gz, defer_outer, scope, .normal_only);
   8153 
   8154         // As our last action before the return, "pop" the error trace if needed
   8155         _ = try gz.addRestoreErrRetIndex(.ret, .always, node);
   8156 
   8157         _ = try gz.addUnNode(.ret_node, .void_value, node);
   8158         return Zir.Inst.Ref.unreachable_value;
   8159     }
   8160 
   8161     if (node_tags[operand_node] == .error_value) {
   8162         // Hot path for `return error.Foo`. This bypasses result location logic as well as logic
   8163         // for detecting whether to add something to the function's inferred error set.
   8164         const ident_token = node_datas[operand_node].rhs;
   8165         const err_name_str_index = try astgen.identAsString(ident_token);
   8166         const defer_counts = countDefers(defer_outer, scope);
   8167         if (!defer_counts.need_err_code) {
   8168             try genDefers(gz, defer_outer, scope, .both_sans_err);
   8169             try emitDbgStmt(gz, ret_lc);
   8170             _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token);
   8171             return Zir.Inst.Ref.unreachable_value;
   8172         }
   8173         const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token);
   8174         try genDefers(gz, defer_outer, scope, .{ .both = err_code });
   8175         try emitDbgStmt(gz, ret_lc);
   8176         _ = try gz.addUnNode(.ret_node, err_code, node);
   8177         return Zir.Inst.Ref.unreachable_value;
   8178     }
   8179 
   8180     const ri: ResultInfo = if (astgen.nodes_need_rl.contains(node)) .{
   8181         .rl = .{ .ptr = .{ .inst = try gz.addNode(.ret_ptr, node) } },
   8182         .ctx = .@"return",
   8183     } else .{
   8184         .rl = .{ .coerced_ty = astgen.fn_ret_ty },
   8185         .ctx = .@"return",
   8186     };
   8187     const prev_anon_name_strategy = gz.anon_name_strategy;
   8188     gz.anon_name_strategy = .func;
   8189     const operand = try reachableExpr(gz, scope, ri, operand_node, node);
   8190     gz.anon_name_strategy = prev_anon_name_strategy;
   8191 
   8192     switch (nodeMayEvalToError(tree, operand_node)) {
   8193         .never => {
   8194             // Returning a value that cannot be an error; skip error defers.
   8195             try genDefers(gz, defer_outer, scope, .normal_only);
   8196 
   8197             // As our last action before the return, "pop" the error trace if needed
   8198             _ = try gz.addRestoreErrRetIndex(.ret, .always, node);
   8199 
   8200             try emitDbgStmt(gz, ret_lc);
   8201             try gz.addRet(ri, operand, node);
   8202             return Zir.Inst.Ref.unreachable_value;
   8203         },
   8204         .always => {
   8205             // Value is always an error. Emit both error defers and regular defers.
   8206             const err_code = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
   8207             try genDefers(gz, defer_outer, scope, .{ .both = err_code });
   8208             try emitDbgStmt(gz, ret_lc);
   8209             try gz.addRet(ri, operand, node);
   8210             return Zir.Inst.Ref.unreachable_value;
   8211         },
   8212         .maybe => {
   8213             const defer_counts = countDefers(defer_outer, scope);
   8214             if (!defer_counts.have_err) {
   8215                 // Only regular defers; no branch needed.
   8216                 try genDefers(gz, defer_outer, scope, .normal_only);
   8217                 try emitDbgStmt(gz, ret_lc);
   8218 
   8219                 // As our last action before the return, "pop" the error trace if needed
   8220                 const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
   8221                 _ = try gz.addRestoreErrRetIndex(.ret, .{ .if_non_error = result }, node);
   8222 
   8223                 try gz.addRet(ri, operand, node);
   8224                 return Zir.Inst.Ref.unreachable_value;
   8225             }
   8226 
   8227             // Emit conditional branch for generating errdefers.
   8228             const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
   8229             const is_non_err = try gz.addUnNode(.ret_is_non_err, result, node);
   8230             const condbr = try gz.addCondBr(.condbr, node);
   8231 
   8232             var then_scope = gz.makeSubBlock(scope);
   8233             defer then_scope.unstack();
   8234 
   8235             try genDefers(&then_scope, defer_outer, scope, .normal_only);
   8236 
   8237             // As our last action before the return, "pop" the error trace if needed
   8238             _ = try then_scope.addRestoreErrRetIndex(.ret, .always, node);
   8239 
   8240             try emitDbgStmt(&then_scope, ret_lc);
   8241             try then_scope.addRet(ri, operand, node);
   8242 
   8243             var else_scope = gz.makeSubBlock(scope);
   8244             defer else_scope.unstack();
   8245 
   8246             const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{
   8247                 .both = try else_scope.addUnNode(.err_union_code, result, node),
   8248             };
   8249             try genDefers(&else_scope, defer_outer, scope, which_ones);
   8250             try emitDbgStmt(&else_scope, ret_lc);
   8251             try else_scope.addRet(ri, operand, node);
   8252 
   8253             try setCondBrPayload(condbr, is_non_err, &then_scope, &else_scope);
   8254 
   8255             return Zir.Inst.Ref.unreachable_value;
   8256         },
   8257     }
   8258 }
   8259 
   8260 /// Parses the string `buf` as a base 10 integer of type `u16`.
   8261 ///
   8262 /// Unlike std.fmt.parseInt, does not allow the '_' character in `buf`.
   8263 fn parseBitCount(buf: []const u8) std.fmt.ParseIntError!u16 {
   8264     if (buf.len == 0) return error.InvalidCharacter;
   8265 
   8266     var x: u16 = 0;
   8267 
   8268     for (buf) |c| {
   8269         const digit = switch (c) {
   8270             '0'...'9' => c - '0',
   8271             else => return error.InvalidCharacter,
   8272         };
   8273 
   8274         if (x != 0) x = try std.math.mul(u16, x, 10);
   8275         x = try std.math.add(u16, x, digit);
   8276     }
   8277 
   8278     return x;
   8279 }
   8280 
   8281 fn identifier(
   8282     gz: *GenZir,
   8283     scope: *Scope,
   8284     ri: ResultInfo,
   8285     ident: Ast.Node.Index,
   8286 ) InnerError!Zir.Inst.Ref {
   8287     const astgen = gz.astgen;
   8288     const tree = astgen.tree;
   8289     const main_tokens = tree.nodes.items(.main_token);
   8290 
   8291     const ident_token = main_tokens[ident];
   8292     const ident_name_raw = tree.tokenSlice(ident_token);
   8293     if (mem.eql(u8, ident_name_raw, "_")) {
   8294         return astgen.failNode(ident, "'_' used as an identifier without @\"_\" syntax", .{});
   8295     }
   8296 
   8297     // if not @"" syntax, just use raw token slice
   8298     if (ident_name_raw[0] != '@') {
   8299         if (primitive_instrs.get(ident_name_raw)) |zir_const_ref| {
   8300             return rvalue(gz, ri, zir_const_ref, ident);
   8301         }
   8302 
   8303         if (ident_name_raw.len >= 2) integer: {
   8304             const first_c = ident_name_raw[0];
   8305             if (first_c == 'i' or first_c == 'u') {
   8306                 const signedness: std.builtin.Signedness = switch (first_c == 'i') {
   8307                     true => .signed,
   8308                     false => .unsigned,
   8309                 };
   8310                 if (ident_name_raw.len >= 3 and ident_name_raw[1] == '0') {
   8311                     return astgen.failNode(
   8312                         ident,
   8313                         "primitive integer type '{s}' has leading zero",
   8314                         .{ident_name_raw},
   8315                     );
   8316                 }
   8317                 const bit_count = parseBitCount(ident_name_raw[1..]) catch |err| switch (err) {
   8318                     error.Overflow => return astgen.failNode(
   8319                         ident,
   8320                         "primitive integer type '{s}' exceeds maximum bit width of 65535",
   8321                         .{ident_name_raw},
   8322                     ),
   8323                     error.InvalidCharacter => break :integer,
   8324                 };
   8325                 const result = try gz.add(.{
   8326                     .tag = .int_type,
   8327                     .data = .{ .int_type = .{
   8328                         .src_node = gz.nodeIndexToRelative(ident),
   8329                         .signedness = signedness,
   8330                         .bit_count = bit_count,
   8331                     } },
   8332                 });
   8333                 return rvalue(gz, ri, result, ident);
   8334             }
   8335         }
   8336     }
   8337 
   8338     // Local variables, including function parameters.
   8339     return localVarRef(gz, scope, ri, ident, ident_token);
   8340 }
   8341 
   8342 fn localVarRef(
   8343     gz: *GenZir,
   8344     scope: *Scope,
   8345     ri: ResultInfo,
   8346     ident: Ast.Node.Index,
   8347     ident_token: Ast.TokenIndex,
   8348 ) InnerError!Zir.Inst.Ref {
   8349     const astgen = gz.astgen;
   8350     const name_str_index = try astgen.identAsString(ident_token);
   8351     var s = scope;
   8352     var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
   8353     var found_needs_tunnel: bool = undefined; // defined when `found_already != null`
   8354     var found_namespaces_out: u32 = undefined; // defined when `found_already != null`
   8355 
   8356     // The number of namespaces above `gz` we currently are
   8357     var num_namespaces_out: u32 = 0;
   8358     // defined by `num_namespaces_out != 0`
   8359     var capturing_namespace: *Scope.Namespace = undefined;
   8360 
   8361     while (true) switch (s.tag) {
   8362         .local_val => {
   8363             const local_val = s.cast(Scope.LocalVal).?;
   8364 
   8365             if (local_val.name == name_str_index) {
   8366                 // Locals cannot shadow anything, so we do not need to look for ambiguous
   8367                 // references in this case.
   8368                 if (ri.rl == .discard and ri.ctx == .assignment) {
   8369                     local_val.discarded = ident_token;
   8370                 } else {
   8371                     local_val.used = ident_token;
   8372                 }
   8373 
   8374                 const value_inst = if (num_namespaces_out != 0) try tunnelThroughClosure(
   8375                     gz,
   8376                     ident,
   8377                     num_namespaces_out,
   8378                     .{ .ref = local_val.inst },
   8379                     .{ .token = local_val.token_src },
   8380                 ) else local_val.inst;
   8381 
   8382                 return rvalueNoCoercePreRef(gz, ri, value_inst, ident);
   8383             }
   8384             s = local_val.parent;
   8385         },
   8386         .local_ptr => {
   8387             const local_ptr = s.cast(Scope.LocalPtr).?;
   8388             if (local_ptr.name == name_str_index) {
   8389                 if (ri.rl == .discard and ri.ctx == .assignment) {
   8390                     local_ptr.discarded = ident_token;
   8391                 } else {
   8392                     local_ptr.used = ident_token;
   8393                 }
   8394 
   8395                 // Can't close over a runtime variable
   8396                 if (num_namespaces_out != 0 and !local_ptr.maybe_comptime and !gz.is_typeof) {
   8397                     const ident_name = try astgen.identifierTokenString(ident_token);
   8398                     return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{
   8399                         try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}),
   8400                         try astgen.errNoteNode(capturing_namespace.node, "crosses namespace boundary here", .{}),
   8401                     });
   8402                 }
   8403 
   8404                 switch (ri.rl) {
   8405                     .ref, .ref_coerced_ty => {
   8406                         const ptr_inst = if (num_namespaces_out != 0) try tunnelThroughClosure(
   8407                             gz,
   8408                             ident,
   8409                             num_namespaces_out,
   8410                             .{ .ref = local_ptr.ptr },
   8411                             .{ .token = local_ptr.token_src },
   8412                         ) else local_ptr.ptr;
   8413                         local_ptr.used_as_lvalue = true;
   8414                         return ptr_inst;
   8415                     },
   8416                     else => {
   8417                         const val_inst = if (num_namespaces_out != 0) try tunnelThroughClosure(
   8418                             gz,
   8419                             ident,
   8420                             num_namespaces_out,
   8421                             .{ .ref_load = local_ptr.ptr },
   8422                             .{ .token = local_ptr.token_src },
   8423                         ) else try gz.addUnNode(.load, local_ptr.ptr, ident);
   8424                         return rvalueNoCoercePreRef(gz, ri, val_inst, ident);
   8425                     },
   8426                 }
   8427             }
   8428             s = local_ptr.parent;
   8429         },
   8430         .gen_zir => s = s.cast(GenZir).?.parent,
   8431         .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
   8432         .namespace => {
   8433             const ns = s.cast(Scope.Namespace).?;
   8434             if (ns.decls.get(name_str_index)) |i| {
   8435                 if (found_already) |f| {
   8436                     return astgen.failNodeNotes(ident, "ambiguous reference", .{}, &.{
   8437                         try astgen.errNoteNode(f, "declared here", .{}),
   8438                         try astgen.errNoteNode(i, "also declared here", .{}),
   8439                     });
   8440                 }
   8441                 // We found a match but must continue looking for ambiguous references to decls.
   8442                 found_already = i;
   8443                 found_needs_tunnel = ns.maybe_generic;
   8444                 found_namespaces_out = num_namespaces_out;
   8445             }
   8446             num_namespaces_out += 1;
   8447             capturing_namespace = ns;
   8448             s = ns.parent;
   8449         },
   8450         .top => break,
   8451     };
   8452     if (found_already == null) {
   8453         const ident_name = try astgen.identifierTokenString(ident_token);
   8454         return astgen.failNode(ident, "use of undeclared identifier '{s}'", .{ident_name});
   8455     }
   8456 
   8457     // Decl references happen by name rather than ZIR index so that when unrelated
   8458     // decls are modified, ZIR code containing references to them can be unmodified.
   8459 
   8460     if (found_namespaces_out > 0 and found_needs_tunnel) {
   8461         switch (ri.rl) {
   8462             .ref, .ref_coerced_ty => return tunnelThroughClosure(
   8463                 gz,
   8464                 ident,
   8465                 found_namespaces_out,
   8466                 .{ .decl_ref = name_str_index },
   8467                 .{ .node = found_already.? },
   8468             ),
   8469             else => {
   8470                 const result = try tunnelThroughClosure(
   8471                     gz,
   8472                     ident,
   8473                     found_namespaces_out,
   8474                     .{ .decl_val = name_str_index },
   8475                     .{ .node = found_already.? },
   8476                 );
   8477                 return rvalueNoCoercePreRef(gz, ri, result, ident);
   8478             },
   8479         }
   8480     }
   8481 
   8482     switch (ri.rl) {
   8483         .ref, .ref_coerced_ty => return gz.addStrTok(.decl_ref, name_str_index, ident_token),
   8484         else => {
   8485             const result = try gz.addStrTok(.decl_val, name_str_index, ident_token);
   8486             return rvalueNoCoercePreRef(gz, ri, result, ident);
   8487         },
   8488     }
   8489 }
   8490 
   8491 /// Access a ZIR instruction through closure. May tunnel through arbitrarily
   8492 /// many namespaces, adding closure captures as required.
   8493 /// Returns the index of the `closure_get` instruction added to `gz`.
   8494 fn tunnelThroughClosure(
   8495     gz: *GenZir,
   8496     /// The node which references the value to be captured.
   8497     inner_ref_node: Ast.Node.Index,
   8498     /// The number of namespaces being tunnelled through. At least 1.
   8499     num_tunnels: u32,
   8500     /// The value being captured.
   8501     value: union(enum) {
   8502         ref: Zir.Inst.Ref,
   8503         ref_load: Zir.Inst.Ref,
   8504         decl_val: Zir.NullTerminatedString,
   8505         decl_ref: Zir.NullTerminatedString,
   8506     },
   8507     /// The location of the value's declaration.
   8508     decl_src: union(enum) {
   8509         token: Ast.TokenIndex,
   8510         node: Ast.Node.Index,
   8511     },
   8512 ) !Zir.Inst.Ref {
   8513     switch (value) {
   8514         .ref => |v| if (v.toIndex() == null) return v, // trivial value; do not need tunnel
   8515         .ref_load => |v| assert(v.toIndex() != null), // there are no constant pointer refs
   8516         .decl_val, .decl_ref => {},
   8517     }
   8518 
   8519     const astgen = gz.astgen;
   8520     const gpa = astgen.gpa;
   8521 
   8522     // Otherwise we need a tunnel. First, figure out the path of namespaces we
   8523     // are tunneling through. This is usually only going to be one or two, so
   8524     // use an SFBA to optimize for the common case.
   8525     var sfba = std.heap.stackFallback(@sizeOf(usize) * 2, astgen.arena);
   8526     var intermediate_tunnels = try sfba.get().alloc(*Scope.Namespace, num_tunnels - 1);
   8527 
   8528     const root_ns = ns: {
   8529         var i: usize = num_tunnels - 1;
   8530         var scope: *Scope = gz.parent;
   8531         while (i > 0) {
   8532             if (scope.cast(Scope.Namespace)) |mid_ns| {
   8533                 i -= 1;
   8534                 intermediate_tunnels[i] = mid_ns;
   8535             }
   8536             scope = scope.parent().?;
   8537         }
   8538         while (true) {
   8539             if (scope.cast(Scope.Namespace)) |ns| break :ns ns;
   8540             scope = scope.parent().?;
   8541         }
   8542     };
   8543 
   8544     // Now that we know the scopes we're tunneling through, begin adding
   8545     // captures as required, starting with the outermost namespace.
   8546     const root_capture = Zir.Inst.Capture.wrap(switch (value) {
   8547         .ref => |v| .{ .instruction = v.toIndex().? },
   8548         .ref_load => |v| .{ .instruction_load = v.toIndex().? },
   8549         .decl_val => |str| .{ .decl_val = str },
   8550         .decl_ref => |str| .{ .decl_ref = str },
   8551     });
   8552     var cur_capture_index = std.math.cast(
   8553         u16,
   8554         (try root_ns.captures.getOrPut(gpa, root_capture)).index,
   8555     ) orelse return astgen.failNodeNotes(root_ns.node, "this compiler implementation only supports up to 65536 captures per namespace", .{}, &.{
   8556         switch (decl_src) {
   8557             .token => |t| try astgen.errNoteTok(t, "captured value here", .{}),
   8558             .node => |n| try astgen.errNoteNode(n, "captured value here", .{}),
   8559         },
   8560         try astgen.errNoteNode(inner_ref_node, "value used here", .{}),
   8561     });
   8562 
   8563     for (intermediate_tunnels) |tunnel_ns| {
   8564         cur_capture_index = std.math.cast(
   8565             u16,
   8566             (try tunnel_ns.captures.getOrPut(gpa, Zir.Inst.Capture.wrap(.{ .nested = cur_capture_index }))).index,
   8567         ) orelse return astgen.failNodeNotes(tunnel_ns.node, "this compiler implementation only supports up to 65536 captures per namespace", .{}, &.{
   8568             switch (decl_src) {
   8569                 .token => |t| try astgen.errNoteTok(t, "captured value here", .{}),
   8570                 .node => |n| try astgen.errNoteNode(n, "captured value here", .{}),
   8571             },
   8572             try astgen.errNoteNode(inner_ref_node, "value used here", .{}),
   8573         });
   8574     }
   8575 
   8576     // Incorporate the capture index into the source hash, so that changes in
   8577     // the order of captures cause suitable re-analysis.
   8578     astgen.src_hasher.update(std.mem.asBytes(&cur_capture_index));
   8579 
   8580     // Add an instruction to get the value from the closure.
   8581     return gz.addExtendedNodeSmall(.closure_get, inner_ref_node, cur_capture_index);
   8582 }
   8583 
   8584 fn stringLiteral(
   8585     gz: *GenZir,
   8586     ri: ResultInfo,
   8587     node: Ast.Node.Index,
   8588 ) InnerError!Zir.Inst.Ref {
   8589     const astgen = gz.astgen;
   8590     const tree = astgen.tree;
   8591     const main_tokens = tree.nodes.items(.main_token);
   8592     const str_lit_token = main_tokens[node];
   8593     const str = try astgen.strLitAsString(str_lit_token);
   8594     const result = try gz.add(.{
   8595         .tag = .str,
   8596         .data = .{ .str = .{
   8597             .start = str.index,
   8598             .len = str.len,
   8599         } },
   8600     });
   8601     return rvalue(gz, ri, result, node);
   8602 }
   8603 
   8604 fn multilineStringLiteral(
   8605     gz: *GenZir,
   8606     ri: ResultInfo,
   8607     node: Ast.Node.Index,
   8608 ) InnerError!Zir.Inst.Ref {
   8609     const astgen = gz.astgen;
   8610     const str = try astgen.strLitNodeAsString(node);
   8611     const result = try gz.add(.{
   8612         .tag = .str,
   8613         .data = .{ .str = .{
   8614             .start = str.index,
   8615             .len = str.len,
   8616         } },
   8617     });
   8618     return rvalue(gz, ri, result, node);
   8619 }
   8620 
   8621 fn charLiteral(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   8622     const astgen = gz.astgen;
   8623     const tree = astgen.tree;
   8624     const main_tokens = tree.nodes.items(.main_token);
   8625     const main_token = main_tokens[node];
   8626     const slice = tree.tokenSlice(main_token);
   8627 
   8628     switch (std.zig.parseCharLiteral(slice)) {
   8629         .success => |codepoint| {
   8630             const result = try gz.addInt(codepoint);
   8631             return rvalue(gz, ri, result, node);
   8632         },
   8633         .failure => |err| return astgen.failWithStrLitError(err, main_token, slice, 0),
   8634     }
   8635 }
   8636 
   8637 const Sign = enum { negative, positive };
   8638 
   8639 fn numberLiteral(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index, source_node: Ast.Node.Index, sign: Sign) InnerError!Zir.Inst.Ref {
   8640     const astgen = gz.astgen;
   8641     const tree = astgen.tree;
   8642     const main_tokens = tree.nodes.items(.main_token);
   8643     const num_token = main_tokens[node];
   8644     const bytes = tree.tokenSlice(num_token);
   8645 
   8646     const result: Zir.Inst.Ref = switch (std.zig.parseNumberLiteral(bytes)) {
   8647         .int => |num| switch (num) {
   8648             0 => if (sign == .positive) .zero else return astgen.failTokNotes(
   8649                 num_token,
   8650                 "integer literal '-0' is ambiguous",
   8651                 .{},
   8652                 &.{
   8653                     try astgen.errNoteTok(num_token, "use '0' for an integer zero", .{}),
   8654                     try astgen.errNoteTok(num_token, "use '-0.0' for a floating-point signed zero", .{}),
   8655                 },
   8656             ),
   8657             1 => {
   8658                 // Handle the negation here!
   8659                 const result: Zir.Inst.Ref = switch (sign) {
   8660                     .positive => .one,
   8661                     .negative => .negative_one,
   8662                 };
   8663                 return rvalue(gz, ri, result, source_node);
   8664             },
   8665             else => try gz.addInt(num),
   8666         },
   8667         .big_int => |base| big: {
   8668             const gpa = astgen.gpa;
   8669             var big_int = try std.math.big.int.Managed.init(gpa);
   8670             defer big_int.deinit();
   8671             const prefix_offset: usize = if (base == .decimal) 0 else 2;
   8672             big_int.setString(@intFromEnum(base), bytes[prefix_offset..]) catch |err| switch (err) {
   8673                 error.InvalidCharacter => unreachable, // caught in `parseNumberLiteral`
   8674                 error.InvalidBase => unreachable, // we only pass 16, 8, 2, see above
   8675                 error.OutOfMemory => return error.OutOfMemory,
   8676             };
   8677 
   8678             const limbs = big_int.limbs[0..big_int.len()];
   8679             assert(big_int.isPositive());
   8680             break :big try gz.addIntBig(limbs);
   8681         },
   8682         .float => {
   8683             const unsigned_float_number = std.fmt.parseFloat(f128, bytes) catch |err| switch (err) {
   8684                 error.InvalidCharacter => unreachable, // validated by tokenizer
   8685             };
   8686             const float_number = switch (sign) {
   8687                 .negative => -unsigned_float_number,
   8688                 .positive => unsigned_float_number,
   8689             };
   8690             // If the value fits into a f64 without losing any precision, store it that way.
   8691             @setFloatMode(.strict);
   8692             const smaller_float: f64 = @floatCast(float_number);
   8693             const bigger_again: f128 = smaller_float;
   8694             if (bigger_again == float_number) {
   8695                 const result = try gz.addFloat(smaller_float);
   8696                 return rvalue(gz, ri, result, source_node);
   8697             }
   8698             // We need to use 128 bits. Break the float into 4 u32 values so we can
   8699             // put it into the `extra` array.
   8700             const int_bits: u128 = @bitCast(float_number);
   8701             const result = try gz.addPlNode(.float128, node, Zir.Inst.Float128{
   8702                 .piece0 = @truncate(int_bits),
   8703                 .piece1 = @truncate(int_bits >> 32),
   8704                 .piece2 = @truncate(int_bits >> 64),
   8705                 .piece3 = @truncate(int_bits >> 96),
   8706             });
   8707             return rvalue(gz, ri, result, source_node);
   8708         },
   8709         .failure => |err| return astgen.failWithNumberError(err, num_token, bytes),
   8710     };
   8711 
   8712     if (sign == .positive) {
   8713         return rvalue(gz, ri, result, source_node);
   8714     } else {
   8715         const negated = try gz.addUnNode(.negate, result, source_node);
   8716         return rvalue(gz, ri, negated, source_node);
   8717     }
   8718 }
   8719 
   8720 fn failWithNumberError(astgen: *AstGen, err: std.zig.number_literal.Error, token: Ast.TokenIndex, bytes: []const u8) InnerError {
   8721     const is_float = std.mem.indexOfScalar(u8, bytes, '.') != null;
   8722     switch (err) {
   8723         .leading_zero => if (is_float) {
   8724             return astgen.failTok(token, "number '{s}' has leading zero", .{bytes});
   8725         } else {
   8726             return astgen.failTokNotes(token, "number '{s}' has leading zero", .{bytes}, &.{
   8727                 try astgen.errNoteTok(token, "use '0o' prefix for octal literals", .{}),
   8728             });
   8729         },
   8730         .digit_after_base => return astgen.failTok(token, "expected a digit after base prefix", .{}),
   8731         .upper_case_base => |i| return astgen.failOff(token, @intCast(i), "base prefix must be lowercase", .{}),
   8732         .invalid_float_base => |i| return astgen.failOff(token, @intCast(i), "invalid base for float literal", .{}),
   8733         .repeated_underscore => |i| return astgen.failOff(token, @intCast(i), "repeated digit separator", .{}),
   8734         .invalid_underscore_after_special => |i| return astgen.failOff(token, @intCast(i), "expected digit before digit separator", .{}),
   8735         .invalid_digit => |info| return astgen.failOff(token, @intCast(info.i), "invalid digit '{c}' for {s} base", .{ bytes[info.i], @tagName(info.base) }),
   8736         .invalid_digit_exponent => |i| return astgen.failOff(token, @intCast(i), "invalid digit '{c}' in exponent", .{bytes[i]}),
   8737         .duplicate_exponent => |i| return astgen.failOff(token, @intCast(i), "duplicate exponent", .{}),
   8738         .exponent_after_underscore => |i| return astgen.failOff(token, @intCast(i), "expected digit before exponent", .{}),
   8739         .special_after_underscore => |i| return astgen.failOff(token, @intCast(i), "expected digit before '{c}'", .{bytes[i]}),
   8740         .trailing_special => |i| return astgen.failOff(token, @intCast(i), "expected digit after '{c}'", .{bytes[i - 1]}),
   8741         .trailing_underscore => |i| return astgen.failOff(token, @intCast(i), "trailing digit separator", .{}),
   8742         .duplicate_period => unreachable, // Validated by tokenizer
   8743         .invalid_character => unreachable, // Validated by tokenizer
   8744         .invalid_exponent_sign => |i| {
   8745             assert(bytes.len >= 2 and bytes[0] == '0' and bytes[1] == 'x'); // Validated by tokenizer
   8746             return astgen.failOff(token, @intCast(i), "sign '{c}' cannot follow digit '{c}' in hex base", .{ bytes[i], bytes[i - 1] });
   8747         },
   8748         .period_after_exponent => |i| return astgen.failOff(token, @intCast(i), "unexpected period after exponent", .{}),
   8749     }
   8750 }
   8751 
   8752 fn asmExpr(
   8753     gz: *GenZir,
   8754     scope: *Scope,
   8755     ri: ResultInfo,
   8756     node: Ast.Node.Index,
   8757     full: Ast.full.Asm,
   8758 ) InnerError!Zir.Inst.Ref {
   8759     const astgen = gz.astgen;
   8760     const tree = astgen.tree;
   8761     const main_tokens = tree.nodes.items(.main_token);
   8762     const node_datas = tree.nodes.items(.data);
   8763     const node_tags = tree.nodes.items(.tag);
   8764     const token_tags = tree.tokens.items(.tag);
   8765 
   8766     const TagAndTmpl = struct { tag: Zir.Inst.Extended, tmpl: Zir.NullTerminatedString };
   8767     const tag_and_tmpl: TagAndTmpl = switch (node_tags[full.ast.template]) {
   8768         .string_literal => .{
   8769             .tag = .@"asm",
   8770             .tmpl = (try astgen.strLitAsString(main_tokens[full.ast.template])).index,
   8771         },
   8772         .multiline_string_literal => .{
   8773             .tag = .@"asm",
   8774             .tmpl = (try astgen.strLitNodeAsString(full.ast.template)).index,
   8775         },
   8776         else => .{
   8777             .tag = .asm_expr,
   8778             .tmpl = @enumFromInt(@intFromEnum(try comptimeExpr(gz, scope, .{ .rl = .none }, full.ast.template))),
   8779         },
   8780     };
   8781 
   8782     // See https://github.com/ziglang/zig/issues/215 and related issues discussing
   8783     // possible inline assembly improvements. Until then here is status quo AstGen
   8784     // for assembly syntax. It's used by std lib crypto aesni.zig.
   8785     const is_container_asm = astgen.fn_block == null;
   8786     if (is_container_asm) {
   8787         if (full.volatile_token) |t|
   8788             return astgen.failTok(t, "volatile is meaningless on global assembly", .{});
   8789         if (full.outputs.len != 0 or full.inputs.len != 0 or full.first_clobber != null)
   8790             return astgen.failNode(node, "global assembly cannot have inputs, outputs, or clobbers", .{});
   8791     } else {
   8792         if (full.outputs.len == 0 and full.volatile_token == null) {
   8793             return astgen.failNode(node, "assembly expression with no output must be marked volatile", .{});
   8794         }
   8795     }
   8796     if (full.outputs.len > 32) {
   8797         return astgen.failNode(full.outputs[32], "too many asm outputs", .{});
   8798     }
   8799     var outputs_buffer: [32]Zir.Inst.Asm.Output = undefined;
   8800     const outputs = outputs_buffer[0..full.outputs.len];
   8801 
   8802     var output_type_bits: u32 = 0;
   8803 
   8804     for (full.outputs, 0..) |output_node, i| {
   8805         const symbolic_name = main_tokens[output_node];
   8806         const name = try astgen.identAsString(symbolic_name);
   8807         const constraint_token = symbolic_name + 2;
   8808         const constraint = (try astgen.strLitAsString(constraint_token)).index;
   8809         const has_arrow = token_tags[symbolic_name + 4] == .arrow;
   8810         if (has_arrow) {
   8811             if (output_type_bits != 0) {
   8812                 return astgen.failNode(output_node, "inline assembly allows up to one output value", .{});
   8813             }
   8814             output_type_bits |= @as(u32, 1) << @intCast(i);
   8815             const out_type_node = node_datas[output_node].lhs;
   8816             const out_type_inst = try typeExpr(gz, scope, out_type_node);
   8817             outputs[i] = .{
   8818                 .name = name,
   8819                 .constraint = constraint,
   8820                 .operand = out_type_inst,
   8821             };
   8822         } else {
   8823             const ident_token = symbolic_name + 4;
   8824             // TODO have a look at #215 and related issues and decide how to
   8825             // handle outputs. Do we want this to be identifiers?
   8826             // Or maybe we want to force this to be expressions with a pointer type.
   8827             outputs[i] = .{
   8828                 .name = name,
   8829                 .constraint = constraint,
   8830                 .operand = try localVarRef(gz, scope, .{ .rl = .ref }, node, ident_token),
   8831             };
   8832         }
   8833     }
   8834 
   8835     if (full.inputs.len > 32) {
   8836         return astgen.failNode(full.inputs[32], "too many asm inputs", .{});
   8837     }
   8838     var inputs_buffer: [32]Zir.Inst.Asm.Input = undefined;
   8839     const inputs = inputs_buffer[0..full.inputs.len];
   8840 
   8841     for (full.inputs, 0..) |input_node, i| {
   8842         const symbolic_name = main_tokens[input_node];
   8843         const name = try astgen.identAsString(symbolic_name);
   8844         const constraint_token = symbolic_name + 2;
   8845         const constraint = (try astgen.strLitAsString(constraint_token)).index;
   8846         const operand = try expr(gz, scope, .{ .rl = .none }, node_datas[input_node].lhs);
   8847         inputs[i] = .{
   8848             .name = name,
   8849             .constraint = constraint,
   8850             .operand = operand,
   8851         };
   8852     }
   8853 
   8854     var clobbers_buffer: [32]u32 = undefined;
   8855     var clobber_i: usize = 0;
   8856     if (full.first_clobber) |first_clobber| clobbers: {
   8857         // asm ("foo" ::: "a", "b")
   8858         // asm ("foo" ::: "a", "b",)
   8859         var tok_i = first_clobber;
   8860         while (true) : (tok_i += 1) {
   8861             if (clobber_i >= clobbers_buffer.len) {
   8862                 return astgen.failTok(tok_i, "too many asm clobbers", .{});
   8863             }
   8864             clobbers_buffer[clobber_i] = @intFromEnum((try astgen.strLitAsString(tok_i)).index);
   8865             clobber_i += 1;
   8866             tok_i += 1;
   8867             switch (token_tags[tok_i]) {
   8868                 .r_paren => break :clobbers,
   8869                 .comma => {
   8870                     if (token_tags[tok_i + 1] == .r_paren) {
   8871                         break :clobbers;
   8872                     } else {
   8873                         continue;
   8874                     }
   8875                 },
   8876                 else => unreachable,
   8877             }
   8878         }
   8879     }
   8880 
   8881     const result = try gz.addAsm(.{
   8882         .tag = tag_and_tmpl.tag,
   8883         .node = node,
   8884         .asm_source = tag_and_tmpl.tmpl,
   8885         .is_volatile = full.volatile_token != null,
   8886         .output_type_bits = output_type_bits,
   8887         .outputs = outputs,
   8888         .inputs = inputs,
   8889         .clobbers = clobbers_buffer[0..clobber_i],
   8890     });
   8891     return rvalue(gz, ri, result, node);
   8892 }
   8893 
   8894 fn as(
   8895     gz: *GenZir,
   8896     scope: *Scope,
   8897     ri: ResultInfo,
   8898     node: Ast.Node.Index,
   8899     lhs: Ast.Node.Index,
   8900     rhs: Ast.Node.Index,
   8901 ) InnerError!Zir.Inst.Ref {
   8902     const dest_type = try typeExpr(gz, scope, lhs);
   8903     const result = try reachableExpr(gz, scope, .{ .rl = .{ .ty = dest_type } }, rhs, node);
   8904     return rvalue(gz, ri, result, node);
   8905 }
   8906 
   8907 fn unionInit(
   8908     gz: *GenZir,
   8909     scope: *Scope,
   8910     ri: ResultInfo,
   8911     node: Ast.Node.Index,
   8912     params: []const Ast.Node.Index,
   8913 ) InnerError!Zir.Inst.Ref {
   8914     const union_type = try typeExpr(gz, scope, params[0]);
   8915     const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]);
   8916     const field_type = try gz.addPlNode(.field_type_ref, node, Zir.Inst.FieldTypeRef{
   8917         .container_type = union_type,
   8918         .field_name = field_name,
   8919     });
   8920     const init = try reachableExpr(gz, scope, .{ .rl = .{ .ty = field_type } }, params[2], node);
   8921     const result = try gz.addPlNode(.union_init, node, Zir.Inst.UnionInit{
   8922         .union_type = union_type,
   8923         .init = init,
   8924         .field_name = field_name,
   8925     });
   8926     return rvalue(gz, ri, result, node);
   8927 }
   8928 
   8929 fn bitCast(
   8930     gz: *GenZir,
   8931     scope: *Scope,
   8932     ri: ResultInfo,
   8933     node: Ast.Node.Index,
   8934     operand_node: Ast.Node.Index,
   8935 ) InnerError!Zir.Inst.Ref {
   8936     const dest_type = try ri.rl.resultTypeForCast(gz, node, "@bitCast");
   8937     const operand = try reachableExpr(gz, scope, .{ .rl = .none }, operand_node, node);
   8938     const result = try gz.addPlNode(.bitcast, node, Zir.Inst.Bin{
   8939         .lhs = dest_type,
   8940         .rhs = operand,
   8941     });
   8942     return rvalue(gz, ri, result, node);
   8943 }
   8944 
   8945 /// Handle one or more nested pointer cast builtins:
   8946 /// * @ptrCast
   8947 /// * @alignCast
   8948 /// * @addrSpaceCast
   8949 /// * @constCast
   8950 /// * @volatileCast
   8951 /// Any sequence of such builtins is treated as a single operation. This allowed
   8952 /// for sequences like `@ptrCast(@alignCast(ptr))` to work correctly despite the
   8953 /// intermediate result type being unknown.
   8954 fn ptrCast(
   8955     gz: *GenZir,
   8956     scope: *Scope,
   8957     ri: ResultInfo,
   8958     root_node: Ast.Node.Index,
   8959 ) InnerError!Zir.Inst.Ref {
   8960     const astgen = gz.astgen;
   8961     const tree = astgen.tree;
   8962     const main_tokens = tree.nodes.items(.main_token);
   8963     const node_datas = tree.nodes.items(.data);
   8964     const node_tags = tree.nodes.items(.tag);
   8965 
   8966     const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?;
   8967     var flags: Zir.Inst.FullPtrCastFlags = .{};
   8968 
   8969     // Note that all pointer cast builtins have one parameter, so we only need
   8970     // to handle `builtin_call_two`.
   8971     var node = root_node;
   8972     while (true) {
   8973         switch (node_tags[node]) {
   8974             .builtin_call_two, .builtin_call_two_comma => {},
   8975             .grouped_expression => {
   8976                 // Handle the chaining even with redundant parentheses
   8977                 node = node_datas[node].lhs;
   8978                 continue;
   8979             },
   8980             else => break,
   8981         }
   8982 
   8983         if (node_datas[node].lhs == 0) break; // 0 args
   8984 
   8985         const builtin_token = main_tokens[node];
   8986         const builtin_name = tree.tokenSlice(builtin_token);
   8987         const info = BuiltinFn.list.get(builtin_name) orelse break;
   8988         if (node_datas[node].rhs == 0) {
   8989             // 1 arg
   8990             if (info.param_count != 1) break;
   8991 
   8992             switch (info.tag) {
   8993                 else => break,
   8994                 inline .ptr_cast,
   8995                 .align_cast,
   8996                 .addrspace_cast,
   8997                 .const_cast,
   8998                 .volatile_cast,
   8999                 => |tag| {
   9000                     if (@field(flags, @tagName(tag))) {
   9001                         return astgen.failNode(node, "redundant {s}", .{builtin_name});
   9002                     }
   9003                     @field(flags, @tagName(tag)) = true;
   9004                 },
   9005             }
   9006 
   9007             node = node_datas[node].lhs;
   9008         } else {
   9009             // 2 args
   9010             if (info.param_count != 2) break;
   9011 
   9012             switch (info.tag) {
   9013                 else => break,
   9014                 .field_parent_ptr => {
   9015                     if (flags.ptr_cast) break;
   9016 
   9017                     const flags_int: FlagsInt = @bitCast(flags);
   9018                     const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node);
   9019                     const parent_ptr_type = try ri.rl.resultTypeForCast(gz, root_node, "@alignCast");
   9020                     const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, node_datas[node].lhs);
   9021                     const field_ptr = try expr(gz, scope, .{ .rl = .none }, node_datas[node].rhs);
   9022                     try emitDbgStmt(gz, cursor);
   9023                     const result = try gz.addExtendedPayloadSmall(.field_parent_ptr, flags_int, Zir.Inst.FieldParentPtr{
   9024                         .src_node = gz.nodeIndexToRelative(node),
   9025                         .parent_ptr_type = parent_ptr_type,
   9026                         .field_name = field_name,
   9027                         .field_ptr = field_ptr,
   9028                     });
   9029                     return rvalue(gz, ri, result, root_node);
   9030                 },
   9031             }
   9032         }
   9033     }
   9034 
   9035     const flags_int: FlagsInt = @bitCast(flags);
   9036     assert(flags_int != 0);
   9037 
   9038     const ptr_only: Zir.Inst.FullPtrCastFlags = .{ .ptr_cast = true };
   9039     if (flags_int == @as(FlagsInt, @bitCast(ptr_only))) {
   9040         // Special case: simpler representation
   9041         return typeCast(gz, scope, ri, root_node, node, .ptr_cast, "@ptrCast");
   9042     }
   9043 
   9044     const no_result_ty_flags: Zir.Inst.FullPtrCastFlags = .{
   9045         .const_cast = true,
   9046         .volatile_cast = true,
   9047     };
   9048     if ((flags_int & ~@as(FlagsInt, @bitCast(no_result_ty_flags))) == 0) {
   9049         // Result type not needed
   9050         const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node);
   9051         const operand = try expr(gz, scope, .{ .rl = .none }, node);
   9052         try emitDbgStmt(gz, cursor);
   9053         const result = try gz.addExtendedPayloadSmall(.ptr_cast_no_dest, flags_int, Zir.Inst.UnNode{
   9054             .node = gz.nodeIndexToRelative(root_node),
   9055             .operand = operand,
   9056         });
   9057         return rvalue(gz, ri, result, root_node);
   9058     }
   9059 
   9060     // Full cast including result type
   9061 
   9062     const cursor = maybeAdvanceSourceCursorToMainToken(gz, root_node);
   9063     const result_type = try ri.rl.resultTypeForCast(gz, root_node, flags.needResultTypeBuiltinName());
   9064     const operand = try expr(gz, scope, .{ .rl = .none }, node);
   9065     try emitDbgStmt(gz, cursor);
   9066     const result = try gz.addExtendedPayloadSmall(.ptr_cast_full, flags_int, Zir.Inst.BinNode{
   9067         .node = gz.nodeIndexToRelative(root_node),
   9068         .lhs = result_type,
   9069         .rhs = operand,
   9070     });
   9071     return rvalue(gz, ri, result, root_node);
   9072 }
   9073 
   9074 fn typeOf(
   9075     gz: *GenZir,
   9076     scope: *Scope,
   9077     ri: ResultInfo,
   9078     node: Ast.Node.Index,
   9079     args: []const Ast.Node.Index,
   9080 ) InnerError!Zir.Inst.Ref {
   9081     const astgen = gz.astgen;
   9082     if (args.len < 1) {
   9083         return astgen.failNode(node, "expected at least 1 argument, found 0", .{});
   9084     }
   9085     const gpa = astgen.gpa;
   9086     if (args.len == 1) {
   9087         const typeof_inst = try gz.makeBlockInst(.typeof_builtin, node);
   9088 
   9089         var typeof_scope = gz.makeSubBlock(scope);
   9090         typeof_scope.is_comptime = false;
   9091         typeof_scope.is_typeof = true;
   9092         typeof_scope.c_import = false;
   9093         defer typeof_scope.unstack();
   9094 
   9095         const ty_expr = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, args[0], node);
   9096         if (!gz.refIsNoReturn(ty_expr)) {
   9097             _ = try typeof_scope.addBreak(.break_inline, typeof_inst, ty_expr);
   9098         }
   9099         try typeof_scope.setBlockBody(typeof_inst);
   9100 
   9101         // typeof_scope unstacked now, can add new instructions to gz
   9102         try gz.instructions.append(gpa, typeof_inst);
   9103         return rvalue(gz, ri, typeof_inst.toRef(), node);
   9104     }
   9105     const payload_size: u32 = std.meta.fields(Zir.Inst.TypeOfPeer).len;
   9106     const payload_index = try reserveExtra(astgen, payload_size + args.len);
   9107     const args_index = payload_index + payload_size;
   9108 
   9109     const typeof_inst = try gz.addExtendedMultiOpPayloadIndex(.typeof_peer, payload_index, args.len);
   9110 
   9111     var typeof_scope = gz.makeSubBlock(scope);
   9112     typeof_scope.is_comptime = false;
   9113 
   9114     for (args, 0..) |arg, i| {
   9115         const param_ref = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, arg, node);
   9116         astgen.extra.items[args_index + i] = @intFromEnum(param_ref);
   9117     }
   9118     _ = try typeof_scope.addBreak(.break_inline, typeof_inst.toIndex().?, .void_value);
   9119 
   9120     const body = typeof_scope.instructionsSlice();
   9121     const body_len = astgen.countBodyLenAfterFixups(body);
   9122     astgen.setExtra(payload_index, Zir.Inst.TypeOfPeer{
   9123         .body_len = @intCast(body_len),
   9124         .body_index = @intCast(astgen.extra.items.len),
   9125         .src_node = gz.nodeIndexToRelative(node),
   9126     });
   9127     try astgen.extra.ensureUnusedCapacity(gpa, body_len);
   9128     astgen.appendBodyWithFixups(body);
   9129     typeof_scope.unstack();
   9130 
   9131     return rvalue(gz, ri, typeof_inst, node);
   9132 }
   9133 
   9134 fn minMax(
   9135     gz: *GenZir,
   9136     scope: *Scope,
   9137     ri: ResultInfo,
   9138     node: Ast.Node.Index,
   9139     args: []const Ast.Node.Index,
   9140     comptime op: enum { min, max },
   9141 ) InnerError!Zir.Inst.Ref {
   9142     const astgen = gz.astgen;
   9143     if (args.len < 2) {
   9144         return astgen.failNode(node, "expected at least 2 arguments, found {}", .{args.len});
   9145     }
   9146     if (args.len == 2) {
   9147         const tag: Zir.Inst.Tag = switch (op) {
   9148             .min => .min,
   9149             .max => .max,
   9150         };
   9151         const a = try expr(gz, scope, .{ .rl = .none }, args[0]);
   9152         const b = try expr(gz, scope, .{ .rl = .none }, args[1]);
   9153         const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   9154             .lhs = a,
   9155             .rhs = b,
   9156         });
   9157         return rvalue(gz, ri, result, node);
   9158     }
   9159     const payload_index = try addExtra(astgen, Zir.Inst.NodeMultiOp{
   9160         .src_node = gz.nodeIndexToRelative(node),
   9161     });
   9162     var extra_index = try reserveExtra(gz.astgen, args.len);
   9163     for (args) |arg| {
   9164         const arg_ref = try expr(gz, scope, .{ .rl = .none }, arg);
   9165         astgen.extra.items[extra_index] = @intFromEnum(arg_ref);
   9166         extra_index += 1;
   9167     }
   9168     const tag: Zir.Inst.Extended = switch (op) {
   9169         .min => .min_multi,
   9170         .max => .max_multi,
   9171     };
   9172     const result = try gz.addExtendedMultiOpPayloadIndex(tag, payload_index, args.len);
   9173     return rvalue(gz, ri, result, node);
   9174 }
   9175 
   9176 fn builtinCall(
   9177     gz: *GenZir,
   9178     scope: *Scope,
   9179     ri: ResultInfo,
   9180     node: Ast.Node.Index,
   9181     params: []const Ast.Node.Index,
   9182     allow_branch_hint: bool,
   9183 ) InnerError!Zir.Inst.Ref {
   9184     const astgen = gz.astgen;
   9185     const tree = astgen.tree;
   9186     const main_tokens = tree.nodes.items(.main_token);
   9187 
   9188     const builtin_token = main_tokens[node];
   9189     const builtin_name = tree.tokenSlice(builtin_token);
   9190 
   9191     // We handle the different builtins manually because they have different semantics depending
   9192     // on the function. For example, `@as` and others participate in result location semantics,
   9193     // and `@cImport` creates a special scope that collects a .c source code text buffer.
   9194     // Also, some builtins have a variable number of parameters.
   9195 
   9196     const info = BuiltinFn.list.get(builtin_name) orelse {
   9197         return astgen.failNode(node, "invalid builtin function: '{s}'", .{
   9198             builtin_name,
   9199         });
   9200     };
   9201     if (info.param_count) |expected| {
   9202         if (expected != params.len) {
   9203             const s = if (expected == 1) "" else "s";
   9204             return astgen.failNode(node, "expected {d} argument{s}, found {d}", .{
   9205                 expected, s, params.len,
   9206             });
   9207         }
   9208     }
   9209 
   9210     // Check function scope-only builtins
   9211 
   9212     if (astgen.fn_block == null and info.illegal_outside_function)
   9213         return astgen.failNode(node, "'{s}' outside function scope", .{builtin_name});
   9214 
   9215     switch (info.tag) {
   9216         .branch_hint => {
   9217             if (!allow_branch_hint) {
   9218                 return astgen.failNode(node, "'@branchHint' must appear as the first statement in a function or conditional branch", .{});
   9219             }
   9220             const hint_ty = try gz.addBuiltinValue(node, .branch_hint);
   9221             const hint_val = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = hint_ty } }, params[0]);
   9222             _ = try gz.addExtendedPayload(.branch_hint, Zir.Inst.UnNode{
   9223                 .node = gz.nodeIndexToRelative(node),
   9224                 .operand = hint_val,
   9225             });
   9226             return rvalue(gz, ri, .void_value, node);
   9227         },
   9228         .import => {
   9229             const node_tags = tree.nodes.items(.tag);
   9230             const operand_node = params[0];
   9231 
   9232             if (node_tags[operand_node] != .string_literal) {
   9233                 // Spec reference: https://github.com/ziglang/zig/issues/2206
   9234                 return astgen.failNode(operand_node, "@import operand must be a string literal", .{});
   9235             }
   9236             const str_lit_token = main_tokens[operand_node];
   9237             const str = try astgen.strLitAsString(str_lit_token);
   9238             const str_slice = astgen.string_bytes.items[@intFromEnum(str.index)..][0..str.len];
   9239             if (mem.indexOfScalar(u8, str_slice, 0) != null) {
   9240                 return astgen.failTok(str_lit_token, "import path cannot contain null bytes", .{});
   9241             } else if (str.len == 0) {
   9242                 return astgen.failTok(str_lit_token, "import path cannot be empty", .{});
   9243             }
   9244             const result = try gz.addStrTok(.import, str.index, str_lit_token);
   9245             const gop = try astgen.imports.getOrPut(astgen.gpa, str.index);
   9246             if (!gop.found_existing) {
   9247                 gop.value_ptr.* = str_lit_token;
   9248             }
   9249             return rvalue(gz, ri, result, node);
   9250         },
   9251         .compile_log => {
   9252             const payload_index = try addExtra(gz.astgen, Zir.Inst.NodeMultiOp{
   9253                 .src_node = gz.nodeIndexToRelative(node),
   9254             });
   9255             var extra_index = try reserveExtra(gz.astgen, params.len);
   9256             for (params) |param| {
   9257                 const param_ref = try expr(gz, scope, .{ .rl = .none }, param);
   9258                 astgen.extra.items[extra_index] = @intFromEnum(param_ref);
   9259                 extra_index += 1;
   9260             }
   9261             const result = try gz.addExtendedMultiOpPayloadIndex(.compile_log, payload_index, params.len);
   9262             return rvalue(gz, ri, result, node);
   9263         },
   9264         .field => {
   9265             if (ri.rl == .ref or ri.rl == .ref_coerced_ty) {
   9266                 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{
   9267                     .lhs = try expr(gz, scope, .{ .rl = .ref }, params[0]),
   9268                     .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]),
   9269                 });
   9270             }
   9271             const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{
   9272                 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
   9273                 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, params[1]),
   9274             });
   9275             return rvalue(gz, ri, result, node);
   9276         },
   9277 
   9278         // zig fmt: off
   9279         .as         => return as(       gz, scope, ri, node, params[0], params[1]),
   9280         .bit_cast   => return bitCast(  gz, scope, ri, node, params[0]),
   9281         .TypeOf     => return typeOf(   gz, scope, ri, node, params),
   9282         .union_init => return unionInit(gz, scope, ri, node, params),
   9283         .c_import   => return cImport(  gz, scope,     node, params[0]),
   9284         .min        => return minMax(   gz, scope, ri, node, params, .min),
   9285         .max        => return minMax(   gz, scope, ri, node, params, .max),
   9286         // zig fmt: on
   9287 
   9288         .@"export" => {
   9289             const exported = try expr(gz, scope, .{ .rl = .none }, params[0]);
   9290             const export_options_ty = try gz.addBuiltinValue(node, .export_options);
   9291             const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = export_options_ty } }, params[1]);
   9292             _ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{
   9293                 .exported = exported,
   9294                 .options = options,
   9295             });
   9296             return rvalue(gz, ri, .void_value, node);
   9297         },
   9298         .@"extern" => {
   9299             const type_inst = try typeExpr(gz, scope, params[0]);
   9300             const extern_options_ty = try gz.addBuiltinValue(node, .extern_options);
   9301             const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = extern_options_ty } }, params[1]);
   9302             const result = try gz.addExtendedPayload(.builtin_extern, Zir.Inst.BinNode{
   9303                 .node = gz.nodeIndexToRelative(node),
   9304                 .lhs = type_inst,
   9305                 .rhs = options,
   9306             });
   9307             return rvalue(gz, ri, result, node);
   9308         },
   9309         .set_float_mode => {
   9310             const float_mode_ty = try gz.addBuiltinValue(node, .float_mode);
   9311             const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_mode_ty } }, params[0]);
   9312             _ = try gz.addExtendedPayload(.set_float_mode, Zir.Inst.UnNode{
   9313                 .node = gz.nodeIndexToRelative(node),
   9314                 .operand = order,
   9315             });
   9316             return rvalue(gz, ri, .void_value, node);
   9317         },
   9318         .set_align_stack => {
   9319             const order = try expr(gz, scope, coerced_align_ri, params[0]);
   9320             _ = try gz.addExtendedPayload(.set_align_stack, Zir.Inst.UnNode{
   9321                 .node = gz.nodeIndexToRelative(node),
   9322                 .operand = order,
   9323             });
   9324             return rvalue(gz, ri, .void_value, node);
   9325         },
   9326 
   9327         .src => {
   9328             // Incorporate the source location into the source hash, so that
   9329             // changes in the source location of `@src()` result in re-analysis.
   9330             astgen.src_hasher.update(
   9331                 std.mem.asBytes(&astgen.source_line) ++
   9332                     std.mem.asBytes(&astgen.source_column),
   9333             );
   9334 
   9335             const token_starts = tree.tokens.items(.start);
   9336             const node_start = token_starts[tree.firstToken(node)];
   9337             astgen.advanceSourceCursor(node_start);
   9338             const result = try gz.addExtendedPayload(.builtin_src, Zir.Inst.Src{
   9339                 .node = gz.nodeIndexToRelative(node),
   9340                 .line = astgen.source_line,
   9341                 .column = astgen.source_column,
   9342             });
   9343             return rvalue(gz, ri, result, node);
   9344         },
   9345 
   9346         // zig fmt: off
   9347         .This                    => return rvalue(gz, ri, try gz.addNodeExtended(.this,                    node), node),
   9348         .return_address          => return rvalue(gz, ri, try gz.addNodeExtended(.ret_addr,                node), node),
   9349         .error_return_trace      => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace,      node), node),
   9350         .frame                   => return rvalue(gz, ri, try gz.addNodeExtended(.frame,                   node), node),
   9351         .frame_address           => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address,           node), node),
   9352         .breakpoint              => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint,              node), node),
   9353         .disable_instrumentation => return rvalue(gz, ri, try gz.addNodeExtended(.disable_instrumentation, node), node),
   9354 
   9355         .type_info   => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info),
   9356         .size_of     => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of),
   9357         .bit_size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .bit_size_of),
   9358         .align_of    => return simpleUnOpType(gz, scope, ri, node, params[0], .align_of),
   9359 
   9360         .int_from_ptr          => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .int_from_ptr),
   9361         .compile_error         => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } },   params[0], .compile_error),
   9362         .set_eval_branch_quota => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .u32_type } },              params[0], .set_eval_branch_quota),
   9363         .int_from_enum         => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .int_from_enum),
   9364         .int_from_bool         => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .int_from_bool),
   9365         .embed_file            => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } },   params[0], .embed_file),
   9366         .error_name            => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .anyerror_type } },         params[0], .error_name),
   9367         .set_runtime_safety    => return simpleUnOp(gz, scope, ri, node, coerced_bool_ri,                                      params[0], .set_runtime_safety),
   9368         .sqrt                  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .sqrt),
   9369         .sin                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .sin),
   9370         .cos                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .cos),
   9371         .tan                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .tan),
   9372         .exp                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .exp),
   9373         .exp2                  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .exp2),
   9374         .log                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .log),
   9375         .log2                  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .log2),
   9376         .log10                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .log10),
   9377         .abs                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .abs),
   9378         .floor                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .floor),
   9379         .ceil                  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .ceil),
   9380         .trunc                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .trunc),
   9381         .round                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .round),
   9382         .tag_name              => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .tag_name),
   9383         .type_name             => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .type_name),
   9384         .Frame                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .frame_type),
   9385         .frame_size            => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                                     params[0], .frame_size),
   9386 
   9387         .int_from_float => return typeCast(gz, scope, ri, node, params[0], .int_from_float, builtin_name),
   9388         .float_from_int => return typeCast(gz, scope, ri, node, params[0], .float_from_int, builtin_name),
   9389         .ptr_from_int   => return typeCast(gz, scope, ri, node, params[0], .ptr_from_int, builtin_name),
   9390         .enum_from_int  => return typeCast(gz, scope, ri, node, params[0], .enum_from_int, builtin_name),
   9391         .float_cast     => return typeCast(gz, scope, ri, node, params[0], .float_cast, builtin_name),
   9392         .int_cast       => return typeCast(gz, scope, ri, node, params[0], .int_cast, builtin_name),
   9393         .truncate       => return typeCast(gz, scope, ri, node, params[0], .truncate, builtin_name),
   9394         // zig fmt: on
   9395 
   9396         .in_comptime => if (gz.is_comptime) {
   9397             return astgen.failNode(node, "redundant '@inComptime' in comptime scope", .{});
   9398         } else {
   9399             return rvalue(gz, ri, try gz.addNodeExtended(.in_comptime, node), node);
   9400         },
   9401 
   9402         .Type => {
   9403             const type_info_ty = try gz.addBuiltinValue(node, .type_info);
   9404             const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = type_info_ty } }, params[0]);
   9405 
   9406             const gpa = gz.astgen.gpa;
   9407 
   9408             try gz.instructions.ensureUnusedCapacity(gpa, 1);
   9409             try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
   9410 
   9411             const payload_index = try gz.astgen.addExtra(Zir.Inst.Reify{
   9412                 .node = node, // Absolute node index -- see the definition of `Reify`.
   9413                 .operand = operand,
   9414                 .src_line = astgen.source_line,
   9415             });
   9416             const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
   9417             gz.astgen.instructions.appendAssumeCapacity(.{
   9418                 .tag = .extended,
   9419                 .data = .{ .extended = .{
   9420                     .opcode = .reify,
   9421                     .small = @intFromEnum(gz.anon_name_strategy),
   9422                     .operand = payload_index,
   9423                 } },
   9424             });
   9425             gz.instructions.appendAssumeCapacity(new_index);
   9426             const result = new_index.toRef();
   9427             return rvalue(gz, ri, result, node);
   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]);
   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]);
   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]);
   9521             const value = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]);
   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]);
   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 != 0,
   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]);
   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         .shuffle => {
   9650             const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{
   9651                 .elem_type = try typeExpr(gz, scope, params[0]),
   9652                 .a = try expr(gz, scope, .{ .rl = .none }, params[1]),
   9653                 .b = try expr(gz, scope, .{ .rl = .none }, params[2]),
   9654                 .mask = try comptimeExpr(gz, scope, .{ .rl = .none }, params[3]),
   9655             });
   9656             return rvalue(gz, ri, result, node);
   9657         },
   9658         .select => {
   9659             const result = try gz.addExtendedPayload(.select, Zir.Inst.Select{
   9660                 .node = gz.nodeIndexToRelative(node),
   9661                 .elem_type = try typeExpr(gz, scope, params[0]),
   9662                 .pred = try expr(gz, scope, .{ .rl = .none }, params[1]),
   9663                 .a = try expr(gz, scope, .{ .rl = .none }, params[2]),
   9664                 .b = try expr(gz, scope, .{ .rl = .none }, params[3]),
   9665             });
   9666             return rvalue(gz, ri, result, node);
   9667         },
   9668         .async_call => {
   9669             const result = try gz.addExtendedPayload(.builtin_async_call, Zir.Inst.AsyncCall{
   9670                 .node = gz.nodeIndexToRelative(node),
   9671                 .frame_buffer = try expr(gz, scope, .{ .rl = .none }, params[0]),
   9672                 .result_ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),
   9673                 .fn_ptr = try expr(gz, scope, .{ .rl = .none }, params[2]),
   9674                 .args = try expr(gz, scope, .{ .rl = .none }, params[3]),
   9675             });
   9676             return rvalue(gz, ri, result, node);
   9677         },
   9678         .Vector => {
   9679             const result = try gz.addPlNode(.vector_type, node, Zir.Inst.Bin{
   9680                 .lhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]),
   9681                 .rhs = try typeExpr(gz, scope, params[1]),
   9682             });
   9683             return rvalue(gz, ri, result, node);
   9684         },
   9685         .prefetch => {
   9686             const prefetch_options_ty = try gz.addBuiltinValue(node, .prefetch_options);
   9687             const ptr = try expr(gz, scope, .{ .rl = .none }, params[0]);
   9688             const options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = prefetch_options_ty } }, params[1]);
   9689             _ = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{
   9690                 .node = gz.nodeIndexToRelative(node),
   9691                 .lhs = ptr,
   9692                 .rhs = options,
   9693             });
   9694             return rvalue(gz, ri, .void_value, node);
   9695         },
   9696         .c_va_arg => {
   9697             const result = try gz.addExtendedPayload(.c_va_arg, Zir.Inst.BinNode{
   9698                 .node = gz.nodeIndexToRelative(node),
   9699                 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
   9700                 .rhs = try typeExpr(gz, scope, params[1]),
   9701             });
   9702             return rvalue(gz, ri, result, node);
   9703         },
   9704         .c_va_copy => {
   9705             const result = try gz.addExtendedPayload(.c_va_copy, Zir.Inst.UnNode{
   9706                 .node = gz.nodeIndexToRelative(node),
   9707                 .operand = try expr(gz, scope, .{ .rl = .none }, params[0]),
   9708             });
   9709             return rvalue(gz, ri, result, node);
   9710         },
   9711         .c_va_end => {
   9712             const result = try gz.addExtendedPayload(.c_va_end, Zir.Inst.UnNode{
   9713                 .node = gz.nodeIndexToRelative(node),
   9714                 .operand = try expr(gz, scope, .{ .rl = .none }, params[0]),
   9715             });
   9716             return rvalue(gz, ri, result, node);
   9717         },
   9718         .c_va_start => {
   9719             if (!astgen.fn_var_args) {
   9720                 return astgen.failNode(node, "'@cVaStart' in a non-variadic function", .{});
   9721             }
   9722             return rvalue(gz, ri, try gz.addNodeExtended(.c_va_start, node), node);
   9723         },
   9724 
   9725         .work_item_id => {
   9726             const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
   9727             const result = try gz.addExtendedPayload(.work_item_id, Zir.Inst.UnNode{
   9728                 .node = gz.nodeIndexToRelative(node),
   9729                 .operand = operand,
   9730             });
   9731             return rvalue(gz, ri, result, node);
   9732         },
   9733         .work_group_size => {
   9734             const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
   9735             const result = try gz.addExtendedPayload(.work_group_size, Zir.Inst.UnNode{
   9736                 .node = gz.nodeIndexToRelative(node),
   9737                 .operand = operand,
   9738             });
   9739             return rvalue(gz, ri, result, node);
   9740         },
   9741         .work_group_id => {
   9742             const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
   9743             const result = try gz.addExtendedPayload(.work_group_id, Zir.Inst.UnNode{
   9744                 .node = gz.nodeIndexToRelative(node),
   9745                 .operand = operand,
   9746             });
   9747             return rvalue(gz, ri, result, node);
   9748         },
   9749     }
   9750 }
   9751 
   9752 fn hasDeclOrField(
   9753     gz: *GenZir,
   9754     scope: *Scope,
   9755     ri: ResultInfo,
   9756     node: Ast.Node.Index,
   9757     lhs_node: Ast.Node.Index,
   9758     rhs_node: Ast.Node.Index,
   9759     tag: Zir.Inst.Tag,
   9760 ) InnerError!Zir.Inst.Ref {
   9761     const container_type = try typeExpr(gz, scope, lhs_node);
   9762     const name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, rhs_node);
   9763     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   9764         .lhs = container_type,
   9765         .rhs = name,
   9766     });
   9767     return rvalue(gz, ri, result, node);
   9768 }
   9769 
   9770 fn typeCast(
   9771     gz: *GenZir,
   9772     scope: *Scope,
   9773     ri: ResultInfo,
   9774     node: Ast.Node.Index,
   9775     operand_node: Ast.Node.Index,
   9776     tag: Zir.Inst.Tag,
   9777     builtin_name: []const u8,
   9778 ) InnerError!Zir.Inst.Ref {
   9779     const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   9780     const result_type = try ri.rl.resultTypeForCast(gz, node, builtin_name);
   9781     const operand = try expr(gz, scope, .{ .rl = .none }, operand_node);
   9782 
   9783     try emitDbgStmt(gz, cursor);
   9784     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   9785         .lhs = result_type,
   9786         .rhs = operand,
   9787     });
   9788     return rvalue(gz, ri, result, node);
   9789 }
   9790 
   9791 fn simpleUnOpType(
   9792     gz: *GenZir,
   9793     scope: *Scope,
   9794     ri: ResultInfo,
   9795     node: Ast.Node.Index,
   9796     operand_node: Ast.Node.Index,
   9797     tag: Zir.Inst.Tag,
   9798 ) InnerError!Zir.Inst.Ref {
   9799     const operand = try typeExpr(gz, scope, operand_node);
   9800     const result = try gz.addUnNode(tag, operand, node);
   9801     return rvalue(gz, ri, result, node);
   9802 }
   9803 
   9804 fn simpleUnOp(
   9805     gz: *GenZir,
   9806     scope: *Scope,
   9807     ri: ResultInfo,
   9808     node: Ast.Node.Index,
   9809     operand_ri: ResultInfo,
   9810     operand_node: Ast.Node.Index,
   9811     tag: Zir.Inst.Tag,
   9812 ) InnerError!Zir.Inst.Ref {
   9813     const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   9814     const operand = if (tag == .compile_error)
   9815         try comptimeExpr(gz, scope, operand_ri, operand_node)
   9816     else
   9817         try expr(gz, scope, operand_ri, operand_node);
   9818     switch (tag) {
   9819         .tag_name, .error_name, .int_from_ptr => try emitDbgStmt(gz, cursor),
   9820         else => {},
   9821     }
   9822     const result = try gz.addUnNode(tag, operand, node);
   9823     return rvalue(gz, ri, result, node);
   9824 }
   9825 
   9826 fn negation(
   9827     gz: *GenZir,
   9828     scope: *Scope,
   9829     ri: ResultInfo,
   9830     node: Ast.Node.Index,
   9831 ) InnerError!Zir.Inst.Ref {
   9832     const astgen = gz.astgen;
   9833     const tree = astgen.tree;
   9834     const node_tags = tree.nodes.items(.tag);
   9835     const node_datas = tree.nodes.items(.data);
   9836 
   9837     // Check for float literal as the sub-expression because we want to preserve
   9838     // its negativity rather than having it go through comptime subtraction.
   9839     const operand_node = node_datas[node].lhs;
   9840     if (node_tags[operand_node] == .number_literal) {
   9841         return numberLiteral(gz, ri, operand_node, node, .negative);
   9842     }
   9843 
   9844     const operand = try expr(gz, scope, .{ .rl = .none }, operand_node);
   9845     const result = try gz.addUnNode(.negate, operand, node);
   9846     return rvalue(gz, ri, result, node);
   9847 }
   9848 
   9849 fn cmpxchg(
   9850     gz: *GenZir,
   9851     scope: *Scope,
   9852     ri: ResultInfo,
   9853     node: Ast.Node.Index,
   9854     params: []const Ast.Node.Index,
   9855     small: u16,
   9856 ) InnerError!Zir.Inst.Ref {
   9857     const int_type = try typeExpr(gz, scope, params[0]);
   9858     const atomic_order_type = try gz.addBuiltinValue(node, .atomic_order);
   9859     const result = try gz.addExtendedPayloadSmall(.cmpxchg, small, Zir.Inst.Cmpxchg{
   9860         // zig fmt: off
   9861         .node           = gz.nodeIndexToRelative(node),
   9862         .ptr            = try expr(gz, scope, .{ .rl = .none },                                params[1]),
   9863         .expected_value = try expr(gz, scope, .{ .rl = .{ .ty = int_type } },                  params[2]),
   9864         .new_value      = try expr(gz, scope, .{ .rl = .{ .coerced_ty = int_type } },          params[3]),
   9865         .success_order  = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[4]),
   9866         .failure_order  = try expr(gz, scope, .{ .rl = .{ .coerced_ty = atomic_order_type } }, params[5]),
   9867         // zig fmt: on
   9868     });
   9869     return rvalue(gz, ri, result, node);
   9870 }
   9871 
   9872 fn bitBuiltin(
   9873     gz: *GenZir,
   9874     scope: *Scope,
   9875     ri: ResultInfo,
   9876     node: Ast.Node.Index,
   9877     operand_node: Ast.Node.Index,
   9878     tag: Zir.Inst.Tag,
   9879 ) InnerError!Zir.Inst.Ref {
   9880     const operand = try expr(gz, scope, .{ .rl = .none }, operand_node);
   9881     const result = try gz.addUnNode(tag, operand, node);
   9882     return rvalue(gz, ri, result, node);
   9883 }
   9884 
   9885 fn divBuiltin(
   9886     gz: *GenZir,
   9887     scope: *Scope,
   9888     ri: ResultInfo,
   9889     node: Ast.Node.Index,
   9890     lhs_node: Ast.Node.Index,
   9891     rhs_node: Ast.Node.Index,
   9892     tag: Zir.Inst.Tag,
   9893 ) InnerError!Zir.Inst.Ref {
   9894     const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
   9895     const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node);
   9896     const rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node);
   9897 
   9898     try emitDbgStmt(gz, cursor);
   9899     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
   9900     return rvalue(gz, ri, result, node);
   9901 }
   9902 
   9903 fn simpleCBuiltin(
   9904     gz: *GenZir,
   9905     scope: *Scope,
   9906     ri: ResultInfo,
   9907     node: Ast.Node.Index,
   9908     operand_node: Ast.Node.Index,
   9909     tag: Zir.Inst.Extended,
   9910 ) InnerError!Zir.Inst.Ref {
   9911     const name: []const u8 = if (tag == .c_undef) "C undef" else "C include";
   9912     if (!gz.c_import) return gz.astgen.failNode(node, "{s} valid only inside C import block", .{name});
   9913     const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, operand_node);
   9914     _ = try gz.addExtendedPayload(tag, Zir.Inst.UnNode{
   9915         .node = gz.nodeIndexToRelative(node),
   9916         .operand = operand,
   9917     });
   9918     return rvalue(gz, ri, .void_value, node);
   9919 }
   9920 
   9921 fn offsetOf(
   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 type_inst = try typeExpr(gz, scope, lhs_node);
   9931     const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .slice_const_u8_type } }, rhs_node);
   9932     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   9933         .lhs = type_inst,
   9934         .rhs = field_name,
   9935     });
   9936     return rvalue(gz, ri, result, node);
   9937 }
   9938 
   9939 fn shiftOp(
   9940     gz: *GenZir,
   9941     scope: *Scope,
   9942     ri: ResultInfo,
   9943     node: Ast.Node.Index,
   9944     lhs_node: Ast.Node.Index,
   9945     rhs_node: Ast.Node.Index,
   9946     tag: Zir.Inst.Tag,
   9947 ) InnerError!Zir.Inst.Ref {
   9948     const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node);
   9949 
   9950     const cursor = switch (gz.astgen.tree.nodes.items(.tag)[node]) {
   9951         .shl, .shr => maybeAdvanceSourceCursorToMainToken(gz, node),
   9952         else => undefined,
   9953     };
   9954 
   9955     const log2_int_type = try gz.addUnNode(.typeof_log2_int_type, lhs, lhs_node);
   9956     const rhs = try expr(gz, scope, .{ .rl = .{ .ty = log2_int_type }, .ctx = .shift_op }, rhs_node);
   9957 
   9958     switch (gz.astgen.tree.nodes.items(.tag)[node]) {
   9959         .shl, .shr => try emitDbgStmt(gz, cursor),
   9960         else => undefined,
   9961     }
   9962 
   9963     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   9964         .lhs = lhs,
   9965         .rhs = rhs,
   9966     });
   9967     return rvalue(gz, ri, result, node);
   9968 }
   9969 
   9970 fn cImport(
   9971     gz: *GenZir,
   9972     scope: *Scope,
   9973     node: Ast.Node.Index,
   9974     body_node: Ast.Node.Index,
   9975 ) InnerError!Zir.Inst.Ref {
   9976     const astgen = gz.astgen;
   9977     const gpa = astgen.gpa;
   9978 
   9979     if (gz.c_import) return gz.astgen.failNode(node, "cannot nest @cImport", .{});
   9980 
   9981     var block_scope = gz.makeSubBlock(scope);
   9982     block_scope.is_comptime = true;
   9983     block_scope.c_import = true;
   9984     defer block_scope.unstack();
   9985 
   9986     const block_inst = try gz.makeBlockInst(.c_import, node);
   9987     const block_result = try fullBodyExpr(&block_scope, &block_scope.base, .{ .rl = .none }, body_node, .normal);
   9988     _ = try gz.addUnNode(.ensure_result_used, block_result, node);
   9989     if (!gz.refIsNoReturn(block_result)) {
   9990         _ = try block_scope.addBreak(.break_inline, block_inst, .void_value);
   9991     }
   9992     try block_scope.setBlockBody(block_inst);
   9993     // block_scope unstacked now, can add new instructions to gz
   9994     try gz.instructions.append(gpa, block_inst);
   9995 
   9996     return block_inst.toRef();
   9997 }
   9998 
   9999 fn overflowArithmetic(
  10000     gz: *GenZir,
  10001     scope: *Scope,
  10002     ri: ResultInfo,
  10003     node: Ast.Node.Index,
  10004     params: []const Ast.Node.Index,
  10005     tag: Zir.Inst.Extended,
  10006 ) InnerError!Zir.Inst.Ref {
  10007     const lhs = try expr(gz, scope, .{ .rl = .none }, params[0]);
  10008     const rhs = try expr(gz, scope, .{ .rl = .none }, params[1]);
  10009     const result = try gz.addExtendedPayload(tag, Zir.Inst.BinNode{
  10010         .node = gz.nodeIndexToRelative(node),
  10011         .lhs = lhs,
  10012         .rhs = rhs,
  10013     });
  10014     return rvalue(gz, ri, result, node);
  10015 }
  10016 
  10017 fn callExpr(
  10018     gz: *GenZir,
  10019     scope: *Scope,
  10020     ri: ResultInfo,
  10021     node: Ast.Node.Index,
  10022     call: Ast.full.Call,
  10023 ) InnerError!Zir.Inst.Ref {
  10024     const astgen = gz.astgen;
  10025 
  10026     const callee = try calleeExpr(gz, scope, ri.rl, call.ast.fn_expr);
  10027     const modifier: std.builtin.CallModifier = blk: {
  10028         if (gz.is_comptime) {
  10029             break :blk .compile_time;
  10030         }
  10031         if (call.async_token != null) {
  10032             break :blk .async_kw;
  10033         }
  10034         if (gz.nosuspend_node != 0) {
  10035             break :blk .no_async;
  10036         }
  10037         break :blk .auto;
  10038     };
  10039 
  10040     {
  10041         astgen.advanceSourceCursor(astgen.tree.tokens.items(.start)[call.ast.lparen]);
  10042         const line = astgen.source_line - gz.decl_line;
  10043         const column = astgen.source_column;
  10044         // Sema expects a dbg_stmt immediately before call,
  10045         try emitDbgStmtForceCurrentIndex(gz, .{ line, column });
  10046     }
  10047 
  10048     switch (callee) {
  10049         .direct => |obj| assert(obj != .none),
  10050         .field => |field| assert(field.obj_ptr != .none),
  10051     }
  10052     assert(node != 0);
  10053 
  10054     const call_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  10055     const call_inst = call_index.toRef();
  10056     try gz.astgen.instructions.append(astgen.gpa, undefined);
  10057     try gz.instructions.append(astgen.gpa, call_index);
  10058 
  10059     const scratch_top = astgen.scratch.items.len;
  10060     defer astgen.scratch.items.len = scratch_top;
  10061 
  10062     var scratch_index = scratch_top;
  10063     try astgen.scratch.resize(astgen.gpa, scratch_top + call.ast.params.len);
  10064 
  10065     for (call.ast.params) |param_node| {
  10066         var arg_block = gz.makeSubBlock(scope);
  10067         defer arg_block.unstack();
  10068 
  10069         // `call_inst` is reused to provide the param type.
  10070         const arg_ref = try fullBodyExpr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node, .normal);
  10071         _ = try arg_block.addBreakWithSrcNode(.break_inline, call_index, arg_ref, param_node);
  10072 
  10073         const body = arg_block.instructionsSlice();
  10074         try astgen.scratch.ensureUnusedCapacity(astgen.gpa, countBodyLenAfterFixups(astgen, body));
  10075         appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
  10076 
  10077         astgen.scratch.items[scratch_index] = @intCast(astgen.scratch.items.len - scratch_top);
  10078         scratch_index += 1;
  10079     }
  10080 
  10081     // If our result location is a try/catch/error-union-if/return, a function argument,
  10082     // or an initializer for a `const` variable, the error trace propagates.
  10083     // Otherwise, it should always be popped (handled in Sema).
  10084     const propagate_error_trace = switch (ri.ctx) {
  10085         .error_handling_expr, .@"return", .fn_arg, .const_init => true,
  10086         else => false,
  10087     };
  10088 
  10089     switch (callee) {
  10090         .direct => |callee_obj| {
  10091             const payload_index = try addExtra(astgen, Zir.Inst.Call{
  10092                 .callee = callee_obj,
  10093                 .flags = .{
  10094                     .pop_error_return_trace = !propagate_error_trace,
  10095                     .packed_modifier = @intCast(@intFromEnum(modifier)),
  10096                     .args_len = @intCast(call.ast.params.len),
  10097                 },
  10098             });
  10099             if (call.ast.params.len != 0) {
  10100                 try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]);
  10101             }
  10102             gz.astgen.instructions.set(@intFromEnum(call_index), .{
  10103                 .tag = .call,
  10104                 .data = .{ .pl_node = .{
  10105                     .src_node = gz.nodeIndexToRelative(node),
  10106                     .payload_index = payload_index,
  10107                 } },
  10108             });
  10109         },
  10110         .field => |callee_field| {
  10111             const payload_index = try addExtra(astgen, Zir.Inst.FieldCall{
  10112                 .obj_ptr = callee_field.obj_ptr,
  10113                 .field_name_start = callee_field.field_name_start,
  10114                 .flags = .{
  10115                     .pop_error_return_trace = !propagate_error_trace,
  10116                     .packed_modifier = @intCast(@intFromEnum(modifier)),
  10117                     .args_len = @intCast(call.ast.params.len),
  10118                 },
  10119             });
  10120             if (call.ast.params.len != 0) {
  10121                 try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]);
  10122             }
  10123             gz.astgen.instructions.set(@intFromEnum(call_index), .{
  10124                 .tag = .field_call,
  10125                 .data = .{ .pl_node = .{
  10126                     .src_node = gz.nodeIndexToRelative(node),
  10127                     .payload_index = payload_index,
  10128                 } },
  10129             });
  10130         },
  10131     }
  10132     return rvalue(gz, ri, call_inst, node); // TODO function call with result location
  10133 }
  10134 
  10135 const Callee = union(enum) {
  10136     field: struct {
  10137         /// A *pointer* to the object the field is fetched on, so that we can
  10138         /// promote the lvalue to an address if the first parameter requires it.
  10139         obj_ptr: Zir.Inst.Ref,
  10140         /// Offset into `string_bytes`.
  10141         field_name_start: Zir.NullTerminatedString,
  10142     },
  10143     direct: Zir.Inst.Ref,
  10144 };
  10145 
  10146 /// calleeExpr generates the function part of a call expression (f in f(x)), but
  10147 /// *not* the callee argument to the @call() builtin. Its purpose is to
  10148 /// distinguish between standard calls and method call syntax `a.b()`. Thus, if
  10149 /// the lhs is a field access, we return using the `field` union field;
  10150 /// otherwise, we use the `direct` union field.
  10151 fn calleeExpr(
  10152     gz: *GenZir,
  10153     scope: *Scope,
  10154     call_rl: ResultInfo.Loc,
  10155     node: Ast.Node.Index,
  10156 ) InnerError!Callee {
  10157     const astgen = gz.astgen;
  10158     const tree = astgen.tree;
  10159 
  10160     const tag = tree.nodes.items(.tag)[node];
  10161     switch (tag) {
  10162         .field_access => {
  10163             const main_tokens = tree.nodes.items(.main_token);
  10164             const node_datas = tree.nodes.items(.data);
  10165             const object_node = node_datas[node].lhs;
  10166             const dot_token = main_tokens[node];
  10167             const field_ident = dot_token + 1;
  10168             const str_index = try astgen.identAsString(field_ident);
  10169             // Capture the object by reference so we can promote it to an
  10170             // address in Sema if needed.
  10171             const lhs = try expr(gz, scope, .{ .rl = .ref }, object_node);
  10172 
  10173             const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
  10174             try emitDbgStmt(gz, cursor);
  10175 
  10176             return .{ .field = .{
  10177                 .obj_ptr = lhs,
  10178                 .field_name_start = str_index,
  10179             } };
  10180         },
  10181         .enum_literal => if (try call_rl.resultType(gz, node)) |res_ty| {
  10182             // Decl literal call syntax, e.g.
  10183             // `const foo: T = .init();`
  10184             // Look up `init` in `T`, but don't try and coerce it.
  10185             const str_index = try astgen.identAsString(tree.nodes.items(.main_token)[node]);
  10186             const callee = try gz.addPlNode(.decl_literal_no_coerce, node, Zir.Inst.Field{
  10187                 .lhs = res_ty,
  10188                 .field_name_start = str_index,
  10189             });
  10190             return .{ .direct = callee };
  10191         } else {
  10192             return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) };
  10193         },
  10194         else => return .{ .direct = try expr(gz, scope, .{ .rl = .none }, node) },
  10195     }
  10196 }
  10197 
  10198 const primitive_instrs = std.StaticStringMap(Zir.Inst.Ref).initComptime(.{
  10199     .{ "anyerror", .anyerror_type },
  10200     .{ "anyframe", .anyframe_type },
  10201     .{ "anyopaque", .anyopaque_type },
  10202     .{ "bool", .bool_type },
  10203     .{ "c_int", .c_int_type },
  10204     .{ "c_long", .c_long_type },
  10205     .{ "c_longdouble", .c_longdouble_type },
  10206     .{ "c_longlong", .c_longlong_type },
  10207     .{ "c_char", .c_char_type },
  10208     .{ "c_short", .c_short_type },
  10209     .{ "c_uint", .c_uint_type },
  10210     .{ "c_ulong", .c_ulong_type },
  10211     .{ "c_ulonglong", .c_ulonglong_type },
  10212     .{ "c_ushort", .c_ushort_type },
  10213     .{ "comptime_float", .comptime_float_type },
  10214     .{ "comptime_int", .comptime_int_type },
  10215     .{ "f128", .f128_type },
  10216     .{ "f16", .f16_type },
  10217     .{ "f32", .f32_type },
  10218     .{ "f64", .f64_type },
  10219     .{ "f80", .f80_type },
  10220     .{ "false", .bool_false },
  10221     .{ "i16", .i16_type },
  10222     .{ "i32", .i32_type },
  10223     .{ "i64", .i64_type },
  10224     .{ "i128", .i128_type },
  10225     .{ "i8", .i8_type },
  10226     .{ "isize", .isize_type },
  10227     .{ "noreturn", .noreturn_type },
  10228     .{ "null", .null_value },
  10229     .{ "true", .bool_true },
  10230     .{ "type", .type_type },
  10231     .{ "u16", .u16_type },
  10232     .{ "u29", .u29_type },
  10233     .{ "u32", .u32_type },
  10234     .{ "u64", .u64_type },
  10235     .{ "u128", .u128_type },
  10236     .{ "u1", .u1_type },
  10237     .{ "u8", .u8_type },
  10238     .{ "undefined", .undef },
  10239     .{ "usize", .usize_type },
  10240     .{ "void", .void_type },
  10241 });
  10242 
  10243 comptime {
  10244     // These checks ensure that std.zig.primitives stays in sync with the primitive->Zir map.
  10245     const primitives = std.zig.primitives;
  10246     for (primitive_instrs.keys(), primitive_instrs.values()) |key, value| {
  10247         if (!primitives.isPrimitive(key)) {
  10248             @compileError("std.zig.isPrimitive() is not aware of Zir instr '" ++ @tagName(value) ++ "'");
  10249         }
  10250     }
  10251     for (primitives.names.keys()) |key| {
  10252         if (primitive_instrs.get(key) == null) {
  10253             @compileError("std.zig.primitives entry '" ++ key ++ "' does not have a corresponding Zir instr");
  10254         }
  10255     }
  10256 }
  10257 
  10258 fn nodeIsTriviallyZero(tree: *const Ast, node: Ast.Node.Index) bool {
  10259     const node_tags = tree.nodes.items(.tag);
  10260     const main_tokens = tree.nodes.items(.main_token);
  10261 
  10262     switch (node_tags[node]) {
  10263         .number_literal => {
  10264             const ident = main_tokens[node];
  10265             return switch (std.zig.parseNumberLiteral(tree.tokenSlice(ident))) {
  10266                 .int => |number| switch (number) {
  10267                     0 => true,
  10268                     else => false,
  10269                 },
  10270                 else => false,
  10271             };
  10272         },
  10273         else => return false,
  10274     }
  10275 }
  10276 
  10277 fn nodeMayAppendToErrorTrace(tree: *const Ast, start_node: Ast.Node.Index) bool {
  10278     const node_tags = tree.nodes.items(.tag);
  10279     const node_datas = tree.nodes.items(.data);
  10280 
  10281     var node = start_node;
  10282     while (true) {
  10283         switch (node_tags[node]) {
  10284             // These don't have the opportunity to call any runtime functions.
  10285             .error_value,
  10286             .identifier,
  10287             .@"comptime",
  10288             => return false,
  10289 
  10290             // Forward the question to the LHS sub-expression.
  10291             .grouped_expression,
  10292             .@"try",
  10293             .@"nosuspend",
  10294             .unwrap_optional,
  10295             => node = node_datas[node].lhs,
  10296 
  10297             // Anything that does not eval to an error is guaranteed to pop any
  10298             // additions to the error trace, so it effectively does not append.
  10299             else => return nodeMayEvalToError(tree, start_node) != .never,
  10300         }
  10301     }
  10302 }
  10303 
  10304 fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.EvalToError {
  10305     const node_tags = tree.nodes.items(.tag);
  10306     const node_datas = tree.nodes.items(.data);
  10307     const main_tokens = tree.nodes.items(.main_token);
  10308     const token_tags = tree.tokens.items(.tag);
  10309 
  10310     var node = start_node;
  10311     while (true) {
  10312         switch (node_tags[node]) {
  10313             .root,
  10314             .@"usingnamespace",
  10315             .test_decl,
  10316             .switch_case,
  10317             .switch_case_inline,
  10318             .switch_case_one,
  10319             .switch_case_inline_one,
  10320             .container_field_init,
  10321             .container_field_align,
  10322             .container_field,
  10323             .asm_output,
  10324             .asm_input,
  10325             => unreachable,
  10326 
  10327             .error_value => return .always,
  10328 
  10329             .@"asm",
  10330             .asm_simple,
  10331             .identifier,
  10332             .field_access,
  10333             .deref,
  10334             .array_access,
  10335             .while_simple,
  10336             .while_cont,
  10337             .for_simple,
  10338             .if_simple,
  10339             .@"while",
  10340             .@"if",
  10341             .@"for",
  10342             .@"switch",
  10343             .switch_comma,
  10344             .call_one,
  10345             .call_one_comma,
  10346             .async_call_one,
  10347             .async_call_one_comma,
  10348             .call,
  10349             .call_comma,
  10350             .async_call,
  10351             .async_call_comma,
  10352             => return .maybe,
  10353 
  10354             .@"return",
  10355             .@"break",
  10356             .@"continue",
  10357             .bit_not,
  10358             .bool_not,
  10359             .global_var_decl,
  10360             .local_var_decl,
  10361             .simple_var_decl,
  10362             .aligned_var_decl,
  10363             .@"defer",
  10364             .@"errdefer",
  10365             .address_of,
  10366             .optional_type,
  10367             .negation,
  10368             .negation_wrap,
  10369             .@"resume",
  10370             .array_type,
  10371             .array_type_sentinel,
  10372             .ptr_type_aligned,
  10373             .ptr_type_sentinel,
  10374             .ptr_type,
  10375             .ptr_type_bit_range,
  10376             .@"suspend",
  10377             .fn_proto_simple,
  10378             .fn_proto_multi,
  10379             .fn_proto_one,
  10380             .fn_proto,
  10381             .fn_decl,
  10382             .anyframe_type,
  10383             .anyframe_literal,
  10384             .number_literal,
  10385             .enum_literal,
  10386             .string_literal,
  10387             .multiline_string_literal,
  10388             .char_literal,
  10389             .unreachable_literal,
  10390             .error_set_decl,
  10391             .container_decl,
  10392             .container_decl_trailing,
  10393             .container_decl_two,
  10394             .container_decl_two_trailing,
  10395             .container_decl_arg,
  10396             .container_decl_arg_trailing,
  10397             .tagged_union,
  10398             .tagged_union_trailing,
  10399             .tagged_union_two,
  10400             .tagged_union_two_trailing,
  10401             .tagged_union_enum_tag,
  10402             .tagged_union_enum_tag_trailing,
  10403             .add,
  10404             .add_wrap,
  10405             .add_sat,
  10406             .array_cat,
  10407             .array_mult,
  10408             .assign,
  10409             .assign_destructure,
  10410             .assign_bit_and,
  10411             .assign_bit_or,
  10412             .assign_shl,
  10413             .assign_shl_sat,
  10414             .assign_shr,
  10415             .assign_bit_xor,
  10416             .assign_div,
  10417             .assign_sub,
  10418             .assign_sub_wrap,
  10419             .assign_sub_sat,
  10420             .assign_mod,
  10421             .assign_add,
  10422             .assign_add_wrap,
  10423             .assign_add_sat,
  10424             .assign_mul,
  10425             .assign_mul_wrap,
  10426             .assign_mul_sat,
  10427             .bang_equal,
  10428             .bit_and,
  10429             .bit_or,
  10430             .shl,
  10431             .shl_sat,
  10432             .shr,
  10433             .bit_xor,
  10434             .bool_and,
  10435             .bool_or,
  10436             .div,
  10437             .equal_equal,
  10438             .error_union,
  10439             .greater_or_equal,
  10440             .greater_than,
  10441             .less_or_equal,
  10442             .less_than,
  10443             .merge_error_sets,
  10444             .mod,
  10445             .mul,
  10446             .mul_wrap,
  10447             .mul_sat,
  10448             .switch_range,
  10449             .for_range,
  10450             .sub,
  10451             .sub_wrap,
  10452             .sub_sat,
  10453             .slice,
  10454             .slice_open,
  10455             .slice_sentinel,
  10456             .array_init_one,
  10457             .array_init_one_comma,
  10458             .array_init_dot_two,
  10459             .array_init_dot_two_comma,
  10460             .array_init_dot,
  10461             .array_init_dot_comma,
  10462             .array_init,
  10463             .array_init_comma,
  10464             .struct_init_one,
  10465             .struct_init_one_comma,
  10466             .struct_init_dot_two,
  10467             .struct_init_dot_two_comma,
  10468             .struct_init_dot,
  10469             .struct_init_dot_comma,
  10470             .struct_init,
  10471             .struct_init_comma,
  10472             => return .never,
  10473 
  10474             // Forward the question to the LHS sub-expression.
  10475             .grouped_expression,
  10476             .@"try",
  10477             .@"await",
  10478             .@"comptime",
  10479             .@"nosuspend",
  10480             .unwrap_optional,
  10481             => node = node_datas[node].lhs,
  10482 
  10483             // LHS sub-expression may still be an error under the outer optional or error union
  10484             .@"catch",
  10485             .@"orelse",
  10486             => return .maybe,
  10487 
  10488             .block_two,
  10489             .block_two_semicolon,
  10490             .block,
  10491             .block_semicolon,
  10492             => {
  10493                 const lbrace = main_tokens[node];
  10494                 if (token_tags[lbrace - 1] == .colon) {
  10495                     // Labeled blocks may need a memory location to forward
  10496                     // to their break statements.
  10497                     return .maybe;
  10498                 } else {
  10499                     return .never;
  10500                 }
  10501             },
  10502 
  10503             .builtin_call,
  10504             .builtin_call_comma,
  10505             .builtin_call_two,
  10506             .builtin_call_two_comma,
  10507             => {
  10508                 const builtin_token = main_tokens[node];
  10509                 const builtin_name = tree.tokenSlice(builtin_token);
  10510                 // If the builtin is an invalid name, we don't cause an error here; instead
  10511                 // let it pass, and the error will be "invalid builtin function" later.
  10512                 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return .maybe;
  10513                 return builtin_info.eval_to_error;
  10514             },
  10515         }
  10516     }
  10517 }
  10518 
  10519 /// Returns `true` if it is known the type expression has more than one possible value;
  10520 /// `false` otherwise.
  10521 fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.Index) bool {
  10522     const node_tags = tree.nodes.items(.tag);
  10523     const node_datas = tree.nodes.items(.data);
  10524 
  10525     var node = start_node;
  10526     while (true) {
  10527         switch (node_tags[node]) {
  10528             .root,
  10529             .@"usingnamespace",
  10530             .test_decl,
  10531             .switch_case,
  10532             .switch_case_inline,
  10533             .switch_case_one,
  10534             .switch_case_inline_one,
  10535             .container_field_init,
  10536             .container_field_align,
  10537             .container_field,
  10538             .asm_output,
  10539             .asm_input,
  10540             .global_var_decl,
  10541             .local_var_decl,
  10542             .simple_var_decl,
  10543             .aligned_var_decl,
  10544             => unreachable,
  10545 
  10546             .@"return",
  10547             .@"break",
  10548             .@"continue",
  10549             .bit_not,
  10550             .bool_not,
  10551             .@"defer",
  10552             .@"errdefer",
  10553             .address_of,
  10554             .negation,
  10555             .negation_wrap,
  10556             .@"resume",
  10557             .array_type,
  10558             .@"suspend",
  10559             .fn_decl,
  10560             .anyframe_literal,
  10561             .number_literal,
  10562             .enum_literal,
  10563             .string_literal,
  10564             .multiline_string_literal,
  10565             .char_literal,
  10566             .unreachable_literal,
  10567             .error_set_decl,
  10568             .container_decl,
  10569             .container_decl_trailing,
  10570             .container_decl_two,
  10571             .container_decl_two_trailing,
  10572             .container_decl_arg,
  10573             .container_decl_arg_trailing,
  10574             .tagged_union,
  10575             .tagged_union_trailing,
  10576             .tagged_union_two,
  10577             .tagged_union_two_trailing,
  10578             .tagged_union_enum_tag,
  10579             .tagged_union_enum_tag_trailing,
  10580             .@"asm",
  10581             .asm_simple,
  10582             .add,
  10583             .add_wrap,
  10584             .add_sat,
  10585             .array_cat,
  10586             .array_mult,
  10587             .assign,
  10588             .assign_destructure,
  10589             .assign_bit_and,
  10590             .assign_bit_or,
  10591             .assign_shl,
  10592             .assign_shl_sat,
  10593             .assign_shr,
  10594             .assign_bit_xor,
  10595             .assign_div,
  10596             .assign_sub,
  10597             .assign_sub_wrap,
  10598             .assign_sub_sat,
  10599             .assign_mod,
  10600             .assign_add,
  10601             .assign_add_wrap,
  10602             .assign_add_sat,
  10603             .assign_mul,
  10604             .assign_mul_wrap,
  10605             .assign_mul_sat,
  10606             .bang_equal,
  10607             .bit_and,
  10608             .bit_or,
  10609             .shl,
  10610             .shl_sat,
  10611             .shr,
  10612             .bit_xor,
  10613             .bool_and,
  10614             .bool_or,
  10615             .div,
  10616             .equal_equal,
  10617             .error_union,
  10618             .greater_or_equal,
  10619             .greater_than,
  10620             .less_or_equal,
  10621             .less_than,
  10622             .merge_error_sets,
  10623             .mod,
  10624             .mul,
  10625             .mul_wrap,
  10626             .mul_sat,
  10627             .switch_range,
  10628             .for_range,
  10629             .field_access,
  10630             .sub,
  10631             .sub_wrap,
  10632             .sub_sat,
  10633             .slice,
  10634             .slice_open,
  10635             .slice_sentinel,
  10636             .deref,
  10637             .array_access,
  10638             .error_value,
  10639             .while_simple,
  10640             .while_cont,
  10641             .for_simple,
  10642             .if_simple,
  10643             .@"catch",
  10644             .@"orelse",
  10645             .array_init_one,
  10646             .array_init_one_comma,
  10647             .array_init_dot_two,
  10648             .array_init_dot_two_comma,
  10649             .array_init_dot,
  10650             .array_init_dot_comma,
  10651             .array_init,
  10652             .array_init_comma,
  10653             .struct_init_one,
  10654             .struct_init_one_comma,
  10655             .struct_init_dot_two,
  10656             .struct_init_dot_two_comma,
  10657             .struct_init_dot,
  10658             .struct_init_dot_comma,
  10659             .struct_init,
  10660             .struct_init_comma,
  10661             .@"while",
  10662             .@"if",
  10663             .@"for",
  10664             .@"switch",
  10665             .switch_comma,
  10666             .call_one,
  10667             .call_one_comma,
  10668             .async_call_one,
  10669             .async_call_one_comma,
  10670             .call,
  10671             .call_comma,
  10672             .async_call,
  10673             .async_call_comma,
  10674             .block_two,
  10675             .block_two_semicolon,
  10676             .block,
  10677             .block_semicolon,
  10678             .builtin_call,
  10679             .builtin_call_comma,
  10680             .builtin_call_two,
  10681             .builtin_call_two_comma,
  10682             // these are function bodies, not pointers
  10683             .fn_proto_simple,
  10684             .fn_proto_multi,
  10685             .fn_proto_one,
  10686             .fn_proto,
  10687             => return false,
  10688 
  10689             // Forward the question to the LHS sub-expression.
  10690             .grouped_expression,
  10691             .@"try",
  10692             .@"await",
  10693             .@"comptime",
  10694             .@"nosuspend",
  10695             .unwrap_optional,
  10696             => node = node_datas[node].lhs,
  10697 
  10698             .ptr_type_aligned,
  10699             .ptr_type_sentinel,
  10700             .ptr_type,
  10701             .ptr_type_bit_range,
  10702             .optional_type,
  10703             .anyframe_type,
  10704             .array_type_sentinel,
  10705             => return true,
  10706 
  10707             .identifier => {
  10708                 const main_tokens = tree.nodes.items(.main_token);
  10709                 const ident_bytes = tree.tokenSlice(main_tokens[node]);
  10710                 if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) {
  10711                     .anyerror_type,
  10712                     .anyframe_type,
  10713                     .anyopaque_type,
  10714                     .bool_type,
  10715                     .c_int_type,
  10716                     .c_long_type,
  10717                     .c_longdouble_type,
  10718                     .c_longlong_type,
  10719                     .c_char_type,
  10720                     .c_short_type,
  10721                     .c_uint_type,
  10722                     .c_ulong_type,
  10723                     .c_ulonglong_type,
  10724                     .c_ushort_type,
  10725                     .comptime_float_type,
  10726                     .comptime_int_type,
  10727                     .f16_type,
  10728                     .f32_type,
  10729                     .f64_type,
  10730                     .f80_type,
  10731                     .f128_type,
  10732                     .i16_type,
  10733                     .i32_type,
  10734                     .i64_type,
  10735                     .i128_type,
  10736                     .i8_type,
  10737                     .isize_type,
  10738                     .type_type,
  10739                     .u16_type,
  10740                     .u29_type,
  10741                     .u32_type,
  10742                     .u64_type,
  10743                     .u128_type,
  10744                     .u1_type,
  10745                     .u8_type,
  10746                     .usize_type,
  10747                     => return true,
  10748 
  10749                     .void_type,
  10750                     .bool_false,
  10751                     .bool_true,
  10752                     .null_value,
  10753                     .undef,
  10754                     .noreturn_type,
  10755                     => return false,
  10756 
  10757                     else => unreachable, // that's all the values from `primitives`.
  10758                 } else {
  10759                     return false;
  10760                 }
  10761             },
  10762         }
  10763     }
  10764 }
  10765 
  10766 /// Returns `true` if it is known the expression is a type that cannot be used at runtime;
  10767 /// `false` otherwise.
  10768 fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
  10769     const node_tags = tree.nodes.items(.tag);
  10770     const node_datas = tree.nodes.items(.data);
  10771 
  10772     var node = start_node;
  10773     while (true) {
  10774         switch (node_tags[node]) {
  10775             .root,
  10776             .@"usingnamespace",
  10777             .test_decl,
  10778             .switch_case,
  10779             .switch_case_inline,
  10780             .switch_case_one,
  10781             .switch_case_inline_one,
  10782             .container_field_init,
  10783             .container_field_align,
  10784             .container_field,
  10785             .asm_output,
  10786             .asm_input,
  10787             .global_var_decl,
  10788             .local_var_decl,
  10789             .simple_var_decl,
  10790             .aligned_var_decl,
  10791             => unreachable,
  10792 
  10793             .@"return",
  10794             .@"break",
  10795             .@"continue",
  10796             .bit_not,
  10797             .bool_not,
  10798             .@"defer",
  10799             .@"errdefer",
  10800             .address_of,
  10801             .negation,
  10802             .negation_wrap,
  10803             .@"resume",
  10804             .array_type,
  10805             .@"suspend",
  10806             .fn_decl,
  10807             .anyframe_literal,
  10808             .number_literal,
  10809             .enum_literal,
  10810             .string_literal,
  10811             .multiline_string_literal,
  10812             .char_literal,
  10813             .unreachable_literal,
  10814             .error_set_decl,
  10815             .container_decl,
  10816             .container_decl_trailing,
  10817             .container_decl_two,
  10818             .container_decl_two_trailing,
  10819             .container_decl_arg,
  10820             .container_decl_arg_trailing,
  10821             .tagged_union,
  10822             .tagged_union_trailing,
  10823             .tagged_union_two,
  10824             .tagged_union_two_trailing,
  10825             .tagged_union_enum_tag,
  10826             .tagged_union_enum_tag_trailing,
  10827             .@"asm",
  10828             .asm_simple,
  10829             .add,
  10830             .add_wrap,
  10831             .add_sat,
  10832             .array_cat,
  10833             .array_mult,
  10834             .assign,
  10835             .assign_destructure,
  10836             .assign_bit_and,
  10837             .assign_bit_or,
  10838             .assign_shl,
  10839             .assign_shl_sat,
  10840             .assign_shr,
  10841             .assign_bit_xor,
  10842             .assign_div,
  10843             .assign_sub,
  10844             .assign_sub_wrap,
  10845             .assign_sub_sat,
  10846             .assign_mod,
  10847             .assign_add,
  10848             .assign_add_wrap,
  10849             .assign_add_sat,
  10850             .assign_mul,
  10851             .assign_mul_wrap,
  10852             .assign_mul_sat,
  10853             .bang_equal,
  10854             .bit_and,
  10855             .bit_or,
  10856             .shl,
  10857             .shl_sat,
  10858             .shr,
  10859             .bit_xor,
  10860             .bool_and,
  10861             .bool_or,
  10862             .div,
  10863             .equal_equal,
  10864             .error_union,
  10865             .greater_or_equal,
  10866             .greater_than,
  10867             .less_or_equal,
  10868             .less_than,
  10869             .merge_error_sets,
  10870             .mod,
  10871             .mul,
  10872             .mul_wrap,
  10873             .mul_sat,
  10874             .switch_range,
  10875             .for_range,
  10876             .field_access,
  10877             .sub,
  10878             .sub_wrap,
  10879             .sub_sat,
  10880             .slice,
  10881             .slice_open,
  10882             .slice_sentinel,
  10883             .deref,
  10884             .array_access,
  10885             .error_value,
  10886             .while_simple,
  10887             .while_cont,
  10888             .for_simple,
  10889             .if_simple,
  10890             .@"catch",
  10891             .@"orelse",
  10892             .array_init_one,
  10893             .array_init_one_comma,
  10894             .array_init_dot_two,
  10895             .array_init_dot_two_comma,
  10896             .array_init_dot,
  10897             .array_init_dot_comma,
  10898             .array_init,
  10899             .array_init_comma,
  10900             .struct_init_one,
  10901             .struct_init_one_comma,
  10902             .struct_init_dot_two,
  10903             .struct_init_dot_two_comma,
  10904             .struct_init_dot,
  10905             .struct_init_dot_comma,
  10906             .struct_init,
  10907             .struct_init_comma,
  10908             .@"while",
  10909             .@"if",
  10910             .@"for",
  10911             .@"switch",
  10912             .switch_comma,
  10913             .call_one,
  10914             .call_one_comma,
  10915             .async_call_one,
  10916             .async_call_one_comma,
  10917             .call,
  10918             .call_comma,
  10919             .async_call,
  10920             .async_call_comma,
  10921             .block_two,
  10922             .block_two_semicolon,
  10923             .block,
  10924             .block_semicolon,
  10925             .builtin_call,
  10926             .builtin_call_comma,
  10927             .builtin_call_two,
  10928             .builtin_call_two_comma,
  10929             .ptr_type_aligned,
  10930             .ptr_type_sentinel,
  10931             .ptr_type,
  10932             .ptr_type_bit_range,
  10933             .optional_type,
  10934             .anyframe_type,
  10935             .array_type_sentinel,
  10936             => return false,
  10937 
  10938             // these are function bodies, not pointers
  10939             .fn_proto_simple,
  10940             .fn_proto_multi,
  10941             .fn_proto_one,
  10942             .fn_proto,
  10943             => return true,
  10944 
  10945             // Forward the question to the LHS sub-expression.
  10946             .grouped_expression,
  10947             .@"try",
  10948             .@"await",
  10949             .@"comptime",
  10950             .@"nosuspend",
  10951             .unwrap_optional,
  10952             => node = node_datas[node].lhs,
  10953 
  10954             .identifier => {
  10955                 const main_tokens = tree.nodes.items(.main_token);
  10956                 const ident_bytes = tree.tokenSlice(main_tokens[node]);
  10957                 if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) {
  10958                     .anyerror_type,
  10959                     .anyframe_type,
  10960                     .anyopaque_type,
  10961                     .bool_type,
  10962                     .c_int_type,
  10963                     .c_long_type,
  10964                     .c_longdouble_type,
  10965                     .c_longlong_type,
  10966                     .c_char_type,
  10967                     .c_short_type,
  10968                     .c_uint_type,
  10969                     .c_ulong_type,
  10970                     .c_ulonglong_type,
  10971                     .c_ushort_type,
  10972                     .f16_type,
  10973                     .f32_type,
  10974                     .f64_type,
  10975                     .f80_type,
  10976                     .f128_type,
  10977                     .i16_type,
  10978                     .i32_type,
  10979                     .i64_type,
  10980                     .i128_type,
  10981                     .i8_type,
  10982                     .isize_type,
  10983                     .u16_type,
  10984                     .u29_type,
  10985                     .u32_type,
  10986                     .u64_type,
  10987                     .u128_type,
  10988                     .u1_type,
  10989                     .u8_type,
  10990                     .usize_type,
  10991                     .void_type,
  10992                     .bool_false,
  10993                     .bool_true,
  10994                     .null_value,
  10995                     .undef,
  10996                     .noreturn_type,
  10997                     => return false,
  10998 
  10999                     .comptime_float_type,
  11000                     .comptime_int_type,
  11001                     .type_type,
  11002                     => return true,
  11003 
  11004                     else => unreachable, // that's all the values from `primitives`.
  11005                 } else {
  11006                     return false;
  11007                 }
  11008             },
  11009         }
  11010     }
  11011 }
  11012 
  11013 /// Returns `true` if the node uses `gz.anon_name_strategy`.
  11014 fn nodeUsesAnonNameStrategy(tree: *const Ast, node: Ast.Node.Index) bool {
  11015     const node_tags = tree.nodes.items(.tag);
  11016     switch (node_tags[node]) {
  11017         .container_decl,
  11018         .container_decl_trailing,
  11019         .container_decl_two,
  11020         .container_decl_two_trailing,
  11021         .container_decl_arg,
  11022         .container_decl_arg_trailing,
  11023         .tagged_union,
  11024         .tagged_union_trailing,
  11025         .tagged_union_two,
  11026         .tagged_union_two_trailing,
  11027         .tagged_union_enum_tag,
  11028         .tagged_union_enum_tag_trailing,
  11029         => return true,
  11030         .builtin_call_two, .builtin_call_two_comma, .builtin_call, .builtin_call_comma => {
  11031             const builtin_token = tree.nodes.items(.main_token)[node];
  11032             const builtin_name = tree.tokenSlice(builtin_token);
  11033             return std.mem.eql(u8, builtin_name, "@Type");
  11034         },
  11035         else => return false,
  11036     }
  11037 }
  11038 
  11039 /// Applies `rl` semantics to `result`. Expressions which do not do their own handling of
  11040 /// result locations must call this function on their result.
  11041 /// As an example, if `ri.rl` is `.ptr`, it will write the result to the pointer.
  11042 /// If `ri.rl` is `.ty`, it will coerce the result to the type.
  11043 /// Assumes nothing stacked on `gz`.
  11044 fn rvalue(
  11045     gz: *GenZir,
  11046     ri: ResultInfo,
  11047     raw_result: Zir.Inst.Ref,
  11048     src_node: Ast.Node.Index,
  11049 ) InnerError!Zir.Inst.Ref {
  11050     return rvalueInner(gz, ri, raw_result, src_node, true);
  11051 }
  11052 
  11053 /// Like `rvalue`, but refuses to perform coercions before taking references for
  11054 /// the `ref_coerced_ty` result type. This is used for local variables which do
  11055 /// not have `alloc`s, because we want variables to have consistent addresses,
  11056 /// i.e. we want them to act like lvalues.
  11057 fn rvalueNoCoercePreRef(
  11058     gz: *GenZir,
  11059     ri: ResultInfo,
  11060     raw_result: Zir.Inst.Ref,
  11061     src_node: Ast.Node.Index,
  11062 ) InnerError!Zir.Inst.Ref {
  11063     return rvalueInner(gz, ri, raw_result, src_node, false);
  11064 }
  11065 
  11066 fn rvalueInner(
  11067     gz: *GenZir,
  11068     ri: ResultInfo,
  11069     raw_result: Zir.Inst.Ref,
  11070     src_node: Ast.Node.Index,
  11071     allow_coerce_pre_ref: bool,
  11072 ) InnerError!Zir.Inst.Ref {
  11073     const result = r: {
  11074         if (raw_result.toIndex()) |result_index| {
  11075             const zir_tags = gz.astgen.instructions.items(.tag);
  11076             const data = gz.astgen.instructions.items(.data)[@intFromEnum(result_index)];
  11077             if (zir_tags[@intFromEnum(result_index)].isAlwaysVoid(data)) {
  11078                 break :r Zir.Inst.Ref.void_value;
  11079             }
  11080         }
  11081         break :r raw_result;
  11082     };
  11083     if (gz.endsWithNoReturn()) return result;
  11084     switch (ri.rl) {
  11085         .none, .coerced_ty => return result,
  11086         .discard => {
  11087             // Emit a compile error for discarding error values.
  11088             _ = try gz.addUnNode(.ensure_result_non_error, result, src_node);
  11089             return .void_value;
  11090         },
  11091         .ref, .ref_coerced_ty => {
  11092             const coerced_result = if (allow_coerce_pre_ref and ri.rl == .ref_coerced_ty) res: {
  11093                 const ptr_ty = ri.rl.ref_coerced_ty;
  11094                 break :res try gz.addPlNode(.coerce_ptr_elem_ty, src_node, Zir.Inst.Bin{
  11095                     .lhs = ptr_ty,
  11096                     .rhs = result,
  11097                 });
  11098             } else result;
  11099             // We need a pointer but we have a value.
  11100             // Unfortunately it's not quite as simple as directly emitting a ref
  11101             // instruction here because we need subsequent address-of operator on
  11102             // const locals to return the same address.
  11103             const astgen = gz.astgen;
  11104             const tree = astgen.tree;
  11105             const src_token = tree.firstToken(src_node);
  11106             const result_index = coerced_result.toIndex() orelse
  11107                 return gz.addUnTok(.ref, coerced_result, src_token);
  11108             const zir_tags = gz.astgen.instructions.items(.tag);
  11109             if (zir_tags[@intFromEnum(result_index)].isParam() or astgen.isInferred(coerced_result))
  11110                 return gz.addUnTok(.ref, coerced_result, src_token);
  11111             const gop = try astgen.ref_table.getOrPut(astgen.gpa, result_index);
  11112             if (!gop.found_existing) {
  11113                 gop.value_ptr.* = try gz.makeUnTok(.ref, coerced_result, src_token);
  11114             }
  11115             return gop.value_ptr.*.toRef();
  11116         },
  11117         .ty => |ty_inst| {
  11118             // Quickly eliminate some common, unnecessary type coercion.
  11119             const as_ty = @as(u64, @intFromEnum(Zir.Inst.Ref.type_type)) << 32;
  11120             const as_bool = @as(u64, @intFromEnum(Zir.Inst.Ref.bool_type)) << 32;
  11121             const as_void = @as(u64, @intFromEnum(Zir.Inst.Ref.void_type)) << 32;
  11122             const as_comptime_int = @as(u64, @intFromEnum(Zir.Inst.Ref.comptime_int_type)) << 32;
  11123             const as_usize = @as(u64, @intFromEnum(Zir.Inst.Ref.usize_type)) << 32;
  11124             const as_u8 = @as(u64, @intFromEnum(Zir.Inst.Ref.u8_type)) << 32;
  11125             switch ((@as(u64, @intFromEnum(ty_inst)) << 32) | @as(u64, @intFromEnum(result))) {
  11126                 as_ty | @intFromEnum(Zir.Inst.Ref.u1_type),
  11127                 as_ty | @intFromEnum(Zir.Inst.Ref.u8_type),
  11128                 as_ty | @intFromEnum(Zir.Inst.Ref.i8_type),
  11129                 as_ty | @intFromEnum(Zir.Inst.Ref.u16_type),
  11130                 as_ty | @intFromEnum(Zir.Inst.Ref.u29_type),
  11131                 as_ty | @intFromEnum(Zir.Inst.Ref.i16_type),
  11132                 as_ty | @intFromEnum(Zir.Inst.Ref.u32_type),
  11133                 as_ty | @intFromEnum(Zir.Inst.Ref.i32_type),
  11134                 as_ty | @intFromEnum(Zir.Inst.Ref.u64_type),
  11135                 as_ty | @intFromEnum(Zir.Inst.Ref.i64_type),
  11136                 as_ty | @intFromEnum(Zir.Inst.Ref.u128_type),
  11137                 as_ty | @intFromEnum(Zir.Inst.Ref.i128_type),
  11138                 as_ty | @intFromEnum(Zir.Inst.Ref.usize_type),
  11139                 as_ty | @intFromEnum(Zir.Inst.Ref.isize_type),
  11140                 as_ty | @intFromEnum(Zir.Inst.Ref.c_char_type),
  11141                 as_ty | @intFromEnum(Zir.Inst.Ref.c_short_type),
  11142                 as_ty | @intFromEnum(Zir.Inst.Ref.c_ushort_type),
  11143                 as_ty | @intFromEnum(Zir.Inst.Ref.c_int_type),
  11144                 as_ty | @intFromEnum(Zir.Inst.Ref.c_uint_type),
  11145                 as_ty | @intFromEnum(Zir.Inst.Ref.c_long_type),
  11146                 as_ty | @intFromEnum(Zir.Inst.Ref.c_ulong_type),
  11147                 as_ty | @intFromEnum(Zir.Inst.Ref.c_longlong_type),
  11148                 as_ty | @intFromEnum(Zir.Inst.Ref.c_ulonglong_type),
  11149                 as_ty | @intFromEnum(Zir.Inst.Ref.c_longdouble_type),
  11150                 as_ty | @intFromEnum(Zir.Inst.Ref.f16_type),
  11151                 as_ty | @intFromEnum(Zir.Inst.Ref.f32_type),
  11152                 as_ty | @intFromEnum(Zir.Inst.Ref.f64_type),
  11153                 as_ty | @intFromEnum(Zir.Inst.Ref.f80_type),
  11154                 as_ty | @intFromEnum(Zir.Inst.Ref.f128_type),
  11155                 as_ty | @intFromEnum(Zir.Inst.Ref.anyopaque_type),
  11156                 as_ty | @intFromEnum(Zir.Inst.Ref.bool_type),
  11157                 as_ty | @intFromEnum(Zir.Inst.Ref.void_type),
  11158                 as_ty | @intFromEnum(Zir.Inst.Ref.type_type),
  11159                 as_ty | @intFromEnum(Zir.Inst.Ref.anyerror_type),
  11160                 as_ty | @intFromEnum(Zir.Inst.Ref.comptime_int_type),
  11161                 as_ty | @intFromEnum(Zir.Inst.Ref.comptime_float_type),
  11162                 as_ty | @intFromEnum(Zir.Inst.Ref.noreturn_type),
  11163                 as_ty | @intFromEnum(Zir.Inst.Ref.anyframe_type),
  11164                 as_ty | @intFromEnum(Zir.Inst.Ref.null_type),
  11165                 as_ty | @intFromEnum(Zir.Inst.Ref.undefined_type),
  11166                 as_ty | @intFromEnum(Zir.Inst.Ref.enum_literal_type),
  11167                 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_u8_type),
  11168                 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_const_u8_type),
  11169                 as_ty | @intFromEnum(Zir.Inst.Ref.manyptr_const_u8_sentinel_0_type),
  11170                 as_ty | @intFromEnum(Zir.Inst.Ref.single_const_pointer_to_comptime_int_type),
  11171                 as_ty | @intFromEnum(Zir.Inst.Ref.slice_const_u8_type),
  11172                 as_ty | @intFromEnum(Zir.Inst.Ref.slice_const_u8_sentinel_0_type),
  11173                 as_ty | @intFromEnum(Zir.Inst.Ref.anyerror_void_error_union_type),
  11174                 as_ty | @intFromEnum(Zir.Inst.Ref.generic_poison_type),
  11175                 as_ty | @intFromEnum(Zir.Inst.Ref.empty_struct_type),
  11176                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero),
  11177                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one),
  11178                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.negative_one),
  11179                 as_usize | @intFromEnum(Zir.Inst.Ref.zero_usize),
  11180                 as_usize | @intFromEnum(Zir.Inst.Ref.one_usize),
  11181                 as_u8 | @intFromEnum(Zir.Inst.Ref.zero_u8),
  11182                 as_u8 | @intFromEnum(Zir.Inst.Ref.one_u8),
  11183                 as_u8 | @intFromEnum(Zir.Inst.Ref.four_u8),
  11184                 as_bool | @intFromEnum(Zir.Inst.Ref.bool_true),
  11185                 as_bool | @intFromEnum(Zir.Inst.Ref.bool_false),
  11186                 as_void | @intFromEnum(Zir.Inst.Ref.void_value),
  11187                 => return result, // type of result is already correct
  11188 
  11189                 as_usize | @intFromEnum(Zir.Inst.Ref.zero) => return .zero_usize,
  11190                 as_u8 | @intFromEnum(Zir.Inst.Ref.zero) => return .zero_u8,
  11191                 as_usize | @intFromEnum(Zir.Inst.Ref.one) => return .one_usize,
  11192                 as_u8 | @intFromEnum(Zir.Inst.Ref.one) => return .one_u8,
  11193                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero_usize) => return .zero,
  11194                 as_u8 | @intFromEnum(Zir.Inst.Ref.zero_usize) => return .zero_u8,
  11195                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one_usize) => return .one,
  11196                 as_u8 | @intFromEnum(Zir.Inst.Ref.one_usize) => return .one_u8,
  11197                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.zero_u8) => return .zero,
  11198                 as_usize | @intFromEnum(Zir.Inst.Ref.zero_u8) => return .zero_usize,
  11199                 as_comptime_int | @intFromEnum(Zir.Inst.Ref.one_u8) => return .one,
  11200                 as_usize | @intFromEnum(Zir.Inst.Ref.one_u8) => return .one_usize,
  11201 
  11202                 // Need an explicit type coercion instruction.
  11203                 else => return gz.addPlNode(ri.zirTag(), src_node, Zir.Inst.As{
  11204                     .dest_type = ty_inst,
  11205                     .operand = result,
  11206                 }),
  11207             }
  11208         },
  11209         .ptr => |ptr_res| {
  11210             _ = try gz.addPlNode(.store_node, ptr_res.src_node orelse src_node, Zir.Inst.Bin{
  11211                 .lhs = ptr_res.inst,
  11212                 .rhs = result,
  11213             });
  11214             return .void_value;
  11215         },
  11216         .inferred_ptr => |alloc| {
  11217             _ = try gz.addPlNode(.store_to_inferred_ptr, src_node, Zir.Inst.Bin{
  11218                 .lhs = alloc,
  11219                 .rhs = result,
  11220             });
  11221             return .void_value;
  11222         },
  11223         .destructure => |destructure| {
  11224             const components = destructure.components;
  11225             _ = try gz.addPlNode(.validate_destructure, src_node, Zir.Inst.ValidateDestructure{
  11226                 .operand = result,
  11227                 .destructure_node = gz.nodeIndexToRelative(destructure.src_node),
  11228                 .expect_len = @intCast(components.len),
  11229             });
  11230             for (components, 0..) |component, i| {
  11231                 if (component == .discard) continue;
  11232                 const elem_val = try gz.add(.{
  11233                     .tag = .elem_val_imm,
  11234                     .data = .{ .elem_val_imm = .{
  11235                         .operand = result,
  11236                         .idx = @intCast(i),
  11237                     } },
  11238                 });
  11239                 switch (component) {
  11240                     .typed_ptr => |ptr_res| {
  11241                         _ = try gz.addPlNode(.store_node, ptr_res.src_node orelse src_node, Zir.Inst.Bin{
  11242                             .lhs = ptr_res.inst,
  11243                             .rhs = elem_val,
  11244                         });
  11245                     },
  11246                     .inferred_ptr => |ptr_inst| {
  11247                         _ = try gz.addPlNode(.store_to_inferred_ptr, src_node, Zir.Inst.Bin{
  11248                             .lhs = ptr_inst,
  11249                             .rhs = elem_val,
  11250                         });
  11251                     },
  11252                     .discard => unreachable,
  11253                 }
  11254             }
  11255             return .void_value;
  11256         },
  11257     }
  11258 }
  11259 
  11260 /// Given an identifier token, obtain the string for it.
  11261 /// If the token uses @"" syntax, parses as a string, reports errors if applicable,
  11262 /// and allocates the result within `astgen.arena`.
  11263 /// Otherwise, returns a reference to the source code bytes directly.
  11264 /// See also `appendIdentStr` and `parseStrLit`.
  11265 fn identifierTokenString(astgen: *AstGen, token: Ast.TokenIndex) InnerError![]const u8 {
  11266     const tree = astgen.tree;
  11267     const token_tags = tree.tokens.items(.tag);
  11268     assert(token_tags[token] == .identifier);
  11269     const ident_name = tree.tokenSlice(token);
  11270     if (!mem.startsWith(u8, ident_name, "@")) {
  11271         return ident_name;
  11272     }
  11273     var buf: ArrayListUnmanaged(u8) = .empty;
  11274     defer buf.deinit(astgen.gpa);
  11275     try astgen.parseStrLit(token, &buf, ident_name, 1);
  11276     if (mem.indexOfScalar(u8, buf.items, 0) != null) {
  11277         return astgen.failTok(token, "identifier cannot contain null bytes", .{});
  11278     } else if (buf.items.len == 0) {
  11279         return astgen.failTok(token, "identifier cannot be empty", .{});
  11280     }
  11281     const duped = try astgen.arena.dupe(u8, buf.items);
  11282     return duped;
  11283 }
  11284 
  11285 /// Given an identifier token, obtain the string for it (possibly parsing as a string
  11286 /// literal if it is @"" syntax), and append the string to `buf`.
  11287 /// See also `identifierTokenString` and `parseStrLit`.
  11288 fn appendIdentStr(
  11289     astgen: *AstGen,
  11290     token: Ast.TokenIndex,
  11291     buf: *ArrayListUnmanaged(u8),
  11292 ) InnerError!void {
  11293     const tree = astgen.tree;
  11294     const token_tags = tree.tokens.items(.tag);
  11295     assert(token_tags[token] == .identifier);
  11296     const ident_name = tree.tokenSlice(token);
  11297     if (!mem.startsWith(u8, ident_name, "@")) {
  11298         return buf.appendSlice(astgen.gpa, ident_name);
  11299     } else {
  11300         const start = buf.items.len;
  11301         try astgen.parseStrLit(token, buf, ident_name, 1);
  11302         const slice = buf.items[start..];
  11303         if (mem.indexOfScalar(u8, slice, 0) != null) {
  11304             return astgen.failTok(token, "identifier cannot contain null bytes", .{});
  11305         } else if (slice.len == 0) {
  11306             return astgen.failTok(token, "identifier cannot be empty", .{});
  11307         }
  11308     }
  11309 }
  11310 
  11311 /// Appends the result to `buf`.
  11312 fn parseStrLit(
  11313     astgen: *AstGen,
  11314     token: Ast.TokenIndex,
  11315     buf: *ArrayListUnmanaged(u8),
  11316     bytes: []const u8,
  11317     offset: u32,
  11318 ) InnerError!void {
  11319     const raw_string = bytes[offset..];
  11320     var buf_managed = buf.toManaged(astgen.gpa);
  11321     const result = std.zig.string_literal.parseWrite(buf_managed.writer(), raw_string);
  11322     buf.* = buf_managed.moveToUnmanaged();
  11323     switch (try result) {
  11324         .success => return,
  11325         .failure => |err| return astgen.failWithStrLitError(err, token, bytes, offset),
  11326     }
  11327 }
  11328 
  11329 fn failWithStrLitError(astgen: *AstGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, bytes: []const u8, offset: u32) InnerError {
  11330     const raw_string = bytes[offset..];
  11331     switch (err) {
  11332         .invalid_escape_character => |bad_index| {
  11333             return astgen.failOff(
  11334                 token,
  11335                 offset + @as(u32, @intCast(bad_index)),
  11336                 "invalid escape character: '{c}'",
  11337                 .{raw_string[bad_index]},
  11338             );
  11339         },
  11340         .expected_hex_digit => |bad_index| {
  11341             return astgen.failOff(
  11342                 token,
  11343                 offset + @as(u32, @intCast(bad_index)),
  11344                 "expected hex digit, found '{c}'",
  11345                 .{raw_string[bad_index]},
  11346             );
  11347         },
  11348         .empty_unicode_escape_sequence => |bad_index| {
  11349             return astgen.failOff(
  11350                 token,
  11351                 offset + @as(u32, @intCast(bad_index)),
  11352                 "empty unicode escape sequence",
  11353                 .{},
  11354             );
  11355         },
  11356         .expected_hex_digit_or_rbrace => |bad_index| {
  11357             return astgen.failOff(
  11358                 token,
  11359                 offset + @as(u32, @intCast(bad_index)),
  11360                 "expected hex digit or '}}', found '{c}'",
  11361                 .{raw_string[bad_index]},
  11362             );
  11363         },
  11364         .invalid_unicode_codepoint => |bad_index| {
  11365             return astgen.failOff(
  11366                 token,
  11367                 offset + @as(u32, @intCast(bad_index)),
  11368                 "unicode escape does not correspond to a valid unicode scalar value",
  11369                 .{},
  11370             );
  11371         },
  11372         .expected_lbrace => |bad_index| {
  11373             return astgen.failOff(
  11374                 token,
  11375                 offset + @as(u32, @intCast(bad_index)),
  11376                 "expected '{{', found '{c}",
  11377                 .{raw_string[bad_index]},
  11378             );
  11379         },
  11380         .expected_rbrace => |bad_index| {
  11381             return astgen.failOff(
  11382                 token,
  11383                 offset + @as(u32, @intCast(bad_index)),
  11384                 "expected '}}', found '{c}",
  11385                 .{raw_string[bad_index]},
  11386             );
  11387         },
  11388         .expected_single_quote => |bad_index| {
  11389             return astgen.failOff(
  11390                 token,
  11391                 offset + @as(u32, @intCast(bad_index)),
  11392                 "expected single quote ('), found '{c}",
  11393                 .{raw_string[bad_index]},
  11394             );
  11395         },
  11396         .invalid_character => |bad_index| {
  11397             return astgen.failOff(
  11398                 token,
  11399                 offset + @as(u32, @intCast(bad_index)),
  11400                 "invalid byte in string or character literal: '{c}'",
  11401                 .{raw_string[bad_index]},
  11402             );
  11403         },
  11404         .empty_char_literal => {
  11405             return astgen.failOff(token, offset, "empty character literal", .{});
  11406         },
  11407     }
  11408 }
  11409 
  11410 fn failNode(
  11411     astgen: *AstGen,
  11412     node: Ast.Node.Index,
  11413     comptime format: []const u8,
  11414     args: anytype,
  11415 ) InnerError {
  11416     return astgen.failNodeNotes(node, format, args, &[0]u32{});
  11417 }
  11418 
  11419 fn appendErrorNode(
  11420     astgen: *AstGen,
  11421     node: Ast.Node.Index,
  11422     comptime format: []const u8,
  11423     args: anytype,
  11424 ) Allocator.Error!void {
  11425     try astgen.appendErrorNodeNotes(node, format, args, &[0]u32{});
  11426 }
  11427 
  11428 fn appendErrorNodeNotes(
  11429     astgen: *AstGen,
  11430     node: Ast.Node.Index,
  11431     comptime format: []const u8,
  11432     args: anytype,
  11433     notes: []const u32,
  11434 ) Allocator.Error!void {
  11435     @branchHint(.cold);
  11436     const string_bytes = &astgen.string_bytes;
  11437     const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
  11438     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
  11439     const notes_index: u32 = if (notes.len != 0) blk: {
  11440         const notes_start = astgen.extra.items.len;
  11441         try astgen.extra.ensureTotalCapacity(astgen.gpa, notes_start + 1 + notes.len);
  11442         astgen.extra.appendAssumeCapacity(@intCast(notes.len));
  11443         astgen.extra.appendSliceAssumeCapacity(notes);
  11444         break :blk @intCast(notes_start);
  11445     } else 0;
  11446     try astgen.compile_errors.append(astgen.gpa, .{
  11447         .msg = msg,
  11448         .node = node,
  11449         .token = 0,
  11450         .byte_offset = 0,
  11451         .notes = notes_index,
  11452     });
  11453 }
  11454 
  11455 fn failNodeNotes(
  11456     astgen: *AstGen,
  11457     node: Ast.Node.Index,
  11458     comptime format: []const u8,
  11459     args: anytype,
  11460     notes: []const u32,
  11461 ) InnerError {
  11462     try appendErrorNodeNotes(astgen, node, format, args, notes);
  11463     return error.AnalysisFail;
  11464 }
  11465 
  11466 fn failTok(
  11467     astgen: *AstGen,
  11468     token: Ast.TokenIndex,
  11469     comptime format: []const u8,
  11470     args: anytype,
  11471 ) InnerError {
  11472     return astgen.failTokNotes(token, format, args, &[0]u32{});
  11473 }
  11474 
  11475 fn appendErrorTok(
  11476     astgen: *AstGen,
  11477     token: Ast.TokenIndex,
  11478     comptime format: []const u8,
  11479     args: anytype,
  11480 ) !void {
  11481     try astgen.appendErrorTokNotesOff(token, 0, format, args, &[0]u32{});
  11482 }
  11483 
  11484 fn failTokNotes(
  11485     astgen: *AstGen,
  11486     token: Ast.TokenIndex,
  11487     comptime format: []const u8,
  11488     args: anytype,
  11489     notes: []const u32,
  11490 ) InnerError {
  11491     try appendErrorTokNotesOff(astgen, token, 0, format, args, notes);
  11492     return error.AnalysisFail;
  11493 }
  11494 
  11495 fn appendErrorTokNotes(
  11496     astgen: *AstGen,
  11497     token: Ast.TokenIndex,
  11498     comptime format: []const u8,
  11499     args: anytype,
  11500     notes: []const u32,
  11501 ) !void {
  11502     return appendErrorTokNotesOff(astgen, token, 0, format, args, notes);
  11503 }
  11504 
  11505 /// Same as `fail`, except given a token plus an offset from its starting byte
  11506 /// offset.
  11507 fn failOff(
  11508     astgen: *AstGen,
  11509     token: Ast.TokenIndex,
  11510     byte_offset: u32,
  11511     comptime format: []const u8,
  11512     args: anytype,
  11513 ) InnerError {
  11514     try appendErrorTokNotesOff(astgen, token, byte_offset, format, args, &.{});
  11515     return error.AnalysisFail;
  11516 }
  11517 
  11518 fn appendErrorTokNotesOff(
  11519     astgen: *AstGen,
  11520     token: Ast.TokenIndex,
  11521     byte_offset: u32,
  11522     comptime format: []const u8,
  11523     args: anytype,
  11524     notes: []const u32,
  11525 ) !void {
  11526     @branchHint(.cold);
  11527     const gpa = astgen.gpa;
  11528     const string_bytes = &astgen.string_bytes;
  11529     const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
  11530     try string_bytes.writer(gpa).print(format ++ "\x00", args);
  11531     const notes_index: u32 = if (notes.len != 0) blk: {
  11532         const notes_start = astgen.extra.items.len;
  11533         try astgen.extra.ensureTotalCapacity(gpa, notes_start + 1 + notes.len);
  11534         astgen.extra.appendAssumeCapacity(@intCast(notes.len));
  11535         astgen.extra.appendSliceAssumeCapacity(notes);
  11536         break :blk @intCast(notes_start);
  11537     } else 0;
  11538     try astgen.compile_errors.append(gpa, .{
  11539         .msg = msg,
  11540         .node = 0,
  11541         .token = token,
  11542         .byte_offset = byte_offset,
  11543         .notes = notes_index,
  11544     });
  11545 }
  11546 
  11547 fn errNoteTok(
  11548     astgen: *AstGen,
  11549     token: Ast.TokenIndex,
  11550     comptime format: []const u8,
  11551     args: anytype,
  11552 ) Allocator.Error!u32 {
  11553     return errNoteTokOff(astgen, token, 0, format, args);
  11554 }
  11555 
  11556 fn errNoteTokOff(
  11557     astgen: *AstGen,
  11558     token: Ast.TokenIndex,
  11559     byte_offset: u32,
  11560     comptime format: []const u8,
  11561     args: anytype,
  11562 ) Allocator.Error!u32 {
  11563     @branchHint(.cold);
  11564     const string_bytes = &astgen.string_bytes;
  11565     const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
  11566     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
  11567     return astgen.addExtra(Zir.Inst.CompileErrors.Item{
  11568         .msg = msg,
  11569         .node = 0,
  11570         .token = token,
  11571         .byte_offset = byte_offset,
  11572         .notes = 0,
  11573     });
  11574 }
  11575 
  11576 fn errNoteNode(
  11577     astgen: *AstGen,
  11578     node: Ast.Node.Index,
  11579     comptime format: []const u8,
  11580     args: anytype,
  11581 ) Allocator.Error!u32 {
  11582     @branchHint(.cold);
  11583     const string_bytes = &astgen.string_bytes;
  11584     const msg: Zir.NullTerminatedString = @enumFromInt(string_bytes.items.len);
  11585     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
  11586     return astgen.addExtra(Zir.Inst.CompileErrors.Item{
  11587         .msg = msg,
  11588         .node = node,
  11589         .token = 0,
  11590         .byte_offset = 0,
  11591         .notes = 0,
  11592     });
  11593 }
  11594 
  11595 fn identAsString(astgen: *AstGen, ident_token: Ast.TokenIndex) !Zir.NullTerminatedString {
  11596     const gpa = astgen.gpa;
  11597     const string_bytes = &astgen.string_bytes;
  11598     const str_index: u32 = @intCast(string_bytes.items.len);
  11599     try astgen.appendIdentStr(ident_token, string_bytes);
  11600     const key: []const u8 = string_bytes.items[str_index..];
  11601     const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
  11602         .bytes = string_bytes,
  11603     }, StringIndexContext{
  11604         .bytes = string_bytes,
  11605     });
  11606     if (gop.found_existing) {
  11607         string_bytes.shrinkRetainingCapacity(str_index);
  11608         return @enumFromInt(gop.key_ptr.*);
  11609     } else {
  11610         gop.key_ptr.* = str_index;
  11611         try string_bytes.append(gpa, 0);
  11612         return @enumFromInt(str_index);
  11613     }
  11614 }
  11615 
  11616 /// Adds a doc comment block to `string_bytes` by walking backwards from `end_token`.
  11617 /// `end_token` must point at the first token after the last doc comment line.
  11618 /// Returns 0 if no doc comment is present.
  11619 fn docCommentAsString(astgen: *AstGen, end_token: Ast.TokenIndex) !Zir.NullTerminatedString {
  11620     if (end_token == 0) return .empty;
  11621 
  11622     const token_tags = astgen.tree.tokens.items(.tag);
  11623 
  11624     var tok = end_token - 1;
  11625     while (token_tags[tok] == .doc_comment) {
  11626         if (tok == 0) break;
  11627         tok -= 1;
  11628     } else {
  11629         tok += 1;
  11630     }
  11631 
  11632     return docCommentAsStringFromFirst(astgen, end_token, tok);
  11633 }
  11634 
  11635 /// end_token must be > the index of the last doc comment.
  11636 fn docCommentAsStringFromFirst(
  11637     astgen: *AstGen,
  11638     end_token: Ast.TokenIndex,
  11639     start_token: Ast.TokenIndex,
  11640 ) !Zir.NullTerminatedString {
  11641     if (start_token == end_token) return .empty;
  11642 
  11643     const gpa = astgen.gpa;
  11644     const string_bytes = &astgen.string_bytes;
  11645     const str_index: u32 = @intCast(string_bytes.items.len);
  11646     const token_starts = astgen.tree.tokens.items(.start);
  11647     const token_tags = astgen.tree.tokens.items(.tag);
  11648 
  11649     const total_bytes = token_starts[end_token] - token_starts[start_token];
  11650     try string_bytes.ensureUnusedCapacity(gpa, total_bytes);
  11651 
  11652     var current_token = start_token;
  11653     while (current_token < end_token) : (current_token += 1) {
  11654         switch (token_tags[current_token]) {
  11655             .doc_comment => {
  11656                 const tok_bytes = astgen.tree.tokenSlice(current_token)[3..];
  11657                 string_bytes.appendSliceAssumeCapacity(tok_bytes);
  11658                 if (current_token != end_token - 1) {
  11659                     string_bytes.appendAssumeCapacity('\n');
  11660                 }
  11661             },
  11662             else => break,
  11663         }
  11664     }
  11665 
  11666     const key: []const u8 = string_bytes.items[str_index..];
  11667     const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
  11668         .bytes = string_bytes,
  11669     }, StringIndexContext{
  11670         .bytes = string_bytes,
  11671     });
  11672 
  11673     if (gop.found_existing) {
  11674         string_bytes.shrinkRetainingCapacity(str_index);
  11675         return @enumFromInt(gop.key_ptr.*);
  11676     } else {
  11677         gop.key_ptr.* = str_index;
  11678         try string_bytes.append(gpa, 0);
  11679         return @enumFromInt(str_index);
  11680     }
  11681 }
  11682 
  11683 const IndexSlice = struct { index: Zir.NullTerminatedString, len: u32 };
  11684 
  11685 fn strLitAsString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !IndexSlice {
  11686     const gpa = astgen.gpa;
  11687     const string_bytes = &astgen.string_bytes;
  11688     const str_index: u32 = @intCast(string_bytes.items.len);
  11689     const token_bytes = astgen.tree.tokenSlice(str_lit_token);
  11690     try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0);
  11691     const key: []const u8 = string_bytes.items[str_index..];
  11692     if (std.mem.indexOfScalar(u8, key, 0)) |_| return .{
  11693         .index = @enumFromInt(str_index),
  11694         .len = @intCast(key.len),
  11695     };
  11696     const gop = try astgen.string_table.getOrPutContextAdapted(gpa, key, StringIndexAdapter{
  11697         .bytes = string_bytes,
  11698     }, StringIndexContext{
  11699         .bytes = string_bytes,
  11700     });
  11701     if (gop.found_existing) {
  11702         string_bytes.shrinkRetainingCapacity(str_index);
  11703         return .{
  11704             .index = @enumFromInt(gop.key_ptr.*),
  11705             .len = @intCast(key.len),
  11706         };
  11707     } else {
  11708         gop.key_ptr.* = str_index;
  11709         // Still need a null byte because we are using the same table
  11710         // to lookup null terminated strings, so if we get a match, it has to
  11711         // be null terminated for that to work.
  11712         try string_bytes.append(gpa, 0);
  11713         return .{
  11714             .index = @enumFromInt(str_index),
  11715             .len = @intCast(key.len),
  11716         };
  11717     }
  11718 }
  11719 
  11720 fn strLitNodeAsString(astgen: *AstGen, node: Ast.Node.Index) !IndexSlice {
  11721     const tree = astgen.tree;
  11722     const node_datas = tree.nodes.items(.data);
  11723 
  11724     const start = node_datas[node].lhs;
  11725     const end = node_datas[node].rhs;
  11726 
  11727     const gpa = astgen.gpa;
  11728     const string_bytes = &astgen.string_bytes;
  11729     const str_index = string_bytes.items.len;
  11730 
  11731     // First line: do not append a newline.
  11732     var tok_i = start;
  11733     {
  11734         const slice = tree.tokenSlice(tok_i);
  11735         const line_bytes = slice[2..];
  11736         try string_bytes.appendSlice(gpa, line_bytes);
  11737         tok_i += 1;
  11738     }
  11739     // Following lines: each line prepends a newline.
  11740     while (tok_i <= end) : (tok_i += 1) {
  11741         const slice = tree.tokenSlice(tok_i);
  11742         const line_bytes = slice[2..];
  11743         try string_bytes.ensureUnusedCapacity(gpa, line_bytes.len + 1);
  11744         string_bytes.appendAssumeCapacity('\n');
  11745         string_bytes.appendSliceAssumeCapacity(line_bytes);
  11746     }
  11747     const len = string_bytes.items.len - str_index;
  11748     try string_bytes.append(gpa, 0);
  11749     return IndexSlice{
  11750         .index = @enumFromInt(str_index),
  11751         .len = @intCast(len),
  11752     };
  11753 }
  11754 
  11755 fn testNameString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !Zir.NullTerminatedString {
  11756     const gpa = astgen.gpa;
  11757     const string_bytes = &astgen.string_bytes;
  11758     const str_index: u32 = @intCast(string_bytes.items.len);
  11759     const token_bytes = astgen.tree.tokenSlice(str_lit_token);
  11760     try string_bytes.append(gpa, 0); // Indicates this is a test.
  11761     try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0);
  11762     const slice = string_bytes.items[str_index + 1 ..];
  11763     if (mem.indexOfScalar(u8, slice, 0) != null) {
  11764         return astgen.failTok(str_lit_token, "test name cannot contain null bytes", .{});
  11765     } else if (slice.len == 0) {
  11766         return astgen.failTok(str_lit_token, "empty test name must be omitted", .{});
  11767     }
  11768     try string_bytes.append(gpa, 0);
  11769     return @enumFromInt(str_index);
  11770 }
  11771 
  11772 const Scope = struct {
  11773     tag: Tag,
  11774 
  11775     fn cast(base: *Scope, comptime T: type) ?*T {
  11776         if (T == Defer) {
  11777             switch (base.tag) {
  11778                 .defer_normal, .defer_error => return @alignCast(@fieldParentPtr("base", base)),
  11779                 else => return null,
  11780             }
  11781         }
  11782         if (T == Namespace) {
  11783             switch (base.tag) {
  11784                 .namespace => return @alignCast(@fieldParentPtr("base", base)),
  11785                 else => return null,
  11786             }
  11787         }
  11788         if (base.tag != T.base_tag)
  11789             return null;
  11790 
  11791         return @alignCast(@fieldParentPtr("base", base));
  11792     }
  11793 
  11794     fn parent(base: *Scope) ?*Scope {
  11795         return switch (base.tag) {
  11796             .gen_zir => base.cast(GenZir).?.parent,
  11797             .local_val => base.cast(LocalVal).?.parent,
  11798             .local_ptr => base.cast(LocalPtr).?.parent,
  11799             .defer_normal, .defer_error => base.cast(Defer).?.parent,
  11800             .namespace => base.cast(Namespace).?.parent,
  11801             .top => null,
  11802         };
  11803     }
  11804 
  11805     const Tag = enum {
  11806         gen_zir,
  11807         local_val,
  11808         local_ptr,
  11809         defer_normal,
  11810         defer_error,
  11811         namespace,
  11812         top,
  11813     };
  11814 
  11815     /// The category of identifier. These tag names are user-visible in compile errors.
  11816     const IdCat = enum {
  11817         @"function parameter",
  11818         @"local constant",
  11819         @"local variable",
  11820         @"switch tag capture",
  11821         capture,
  11822     };
  11823 
  11824     /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.
  11825     /// This structure lives as long as the AST generation of the Block
  11826     /// node that contains the variable.
  11827     const LocalVal = struct {
  11828         const base_tag: Tag = .local_val;
  11829         base: Scope = Scope{ .tag = base_tag },
  11830         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
  11831         parent: *Scope,
  11832         gen_zir: *GenZir,
  11833         inst: Zir.Inst.Ref,
  11834         /// Source location of the corresponding variable declaration.
  11835         token_src: Ast.TokenIndex,
  11836         /// Track the first identifier where it is referenced.
  11837         /// 0 means never referenced.
  11838         used: Ast.TokenIndex = 0,
  11839         /// Track the identifier where it is discarded, like this `_ = foo;`.
  11840         /// 0 means never discarded.
  11841         discarded: Ast.TokenIndex = 0,
  11842         /// String table index.
  11843         name: Zir.NullTerminatedString,
  11844         id_cat: IdCat,
  11845     };
  11846 
  11847     /// This could be a `const` or `var` local. It has a pointer instead of a value.
  11848     /// This structure lives as long as the AST generation of the Block
  11849     /// node that contains the variable.
  11850     const LocalPtr = struct {
  11851         const base_tag: Tag = .local_ptr;
  11852         base: Scope = Scope{ .tag = base_tag },
  11853         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
  11854         parent: *Scope,
  11855         gen_zir: *GenZir,
  11856         ptr: Zir.Inst.Ref,
  11857         /// Source location of the corresponding variable declaration.
  11858         token_src: Ast.TokenIndex,
  11859         /// Track the first identifier where it is referenced.
  11860         /// 0 means never referenced.
  11861         used: Ast.TokenIndex = 0,
  11862         /// Track the identifier where it is discarded, like this `_ = foo;`.
  11863         /// 0 means never discarded.
  11864         discarded: Ast.TokenIndex = 0,
  11865         /// Whether this value is used as an lvalue after initialization.
  11866         /// If not, we know it can be `const`, so will emit a compile error if it is `var`.
  11867         used_as_lvalue: bool = false,
  11868         /// String table index.
  11869         name: Zir.NullTerminatedString,
  11870         id_cat: IdCat,
  11871         /// true means we find out during Sema whether the value is comptime.
  11872         /// false means it is already known at AstGen the value is runtime-known.
  11873         maybe_comptime: bool,
  11874     };
  11875 
  11876     const Defer = struct {
  11877         base: Scope,
  11878         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
  11879         parent: *Scope,
  11880         index: u32,
  11881         len: u32,
  11882         remapped_err_code: Zir.Inst.OptionalIndex = .none,
  11883     };
  11884 
  11885     /// Represents a global scope that has any number of declarations in it.
  11886     /// Each declaration has this as the parent scope.
  11887     const Namespace = struct {
  11888         const base_tag: Tag = .namespace;
  11889         base: Scope = Scope{ .tag = base_tag },
  11890 
  11891         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
  11892         parent: *Scope,
  11893         /// Maps string table index to the source location of declaration,
  11894         /// for the purposes of reporting name shadowing compile errors.
  11895         decls: std.AutoHashMapUnmanaged(Zir.NullTerminatedString, Ast.Node.Index) = .empty,
  11896         node: Ast.Node.Index,
  11897         inst: Zir.Inst.Index,
  11898         maybe_generic: bool,
  11899 
  11900         /// The astgen scope containing this namespace.
  11901         /// Only valid during astgen.
  11902         declaring_gz: ?*GenZir,
  11903 
  11904         /// Set of captures used by this namespace.
  11905         captures: std.AutoArrayHashMapUnmanaged(Zir.Inst.Capture, void) = .empty,
  11906 
  11907         fn deinit(self: *Namespace, gpa: Allocator) void {
  11908             self.decls.deinit(gpa);
  11909             self.captures.deinit(gpa);
  11910             self.* = undefined;
  11911         }
  11912     };
  11913 
  11914     const Top = struct {
  11915         const base_tag: Scope.Tag = .top;
  11916         base: Scope = Scope{ .tag = base_tag },
  11917     };
  11918 };
  11919 
  11920 /// This is a temporary structure; references to it are valid only
  11921 /// while constructing a `Zir`.
  11922 const GenZir = struct {
  11923     const base_tag: Scope.Tag = .gen_zir;
  11924     base: Scope = Scope{ .tag = base_tag },
  11925     /// Whether we're already in a scope known to be comptime. This is set
  11926     /// whenever we know Sema will analyze the current block with `is_comptime`,
  11927     /// for instance when we're within a `struct_decl` or a `block_comptime`.
  11928     is_comptime: bool,
  11929     /// Whether we're in an expression within a `@TypeOf` operand. In this case, closure of runtime
  11930     /// variables is permitted where it is usually not.
  11931     is_typeof: bool = false,
  11932     /// This is set to true for a `GenZir` of a `block_inline`, indicating that
  11933     /// exits from this block should use `break_inline` rather than `break`.
  11934     is_inline: bool = false,
  11935     c_import: bool = false,
  11936     /// How decls created in this scope should be named.
  11937     anon_name_strategy: Zir.Inst.NameStrategy = .anon,
  11938     /// The containing decl AST node.
  11939     decl_node_index: Ast.Node.Index,
  11940     /// The containing decl line index, absolute.
  11941     decl_line: u32,
  11942     /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
  11943     parent: *Scope,
  11944     /// All `GenZir` scopes for the same ZIR share this.
  11945     astgen: *AstGen,
  11946     /// Keeps track of the list of instructions in this scope. Possibly shared.
  11947     /// Indexes to instructions in `astgen`.
  11948     instructions: *ArrayListUnmanaged(Zir.Inst.Index),
  11949     /// A sub-block may share its instructions ArrayList with containing GenZir,
  11950     /// if use is strictly nested. This saves prior size of list for unstacking.
  11951     instructions_top: usize,
  11952     label: ?Label = null,
  11953     break_block: Zir.Inst.OptionalIndex = .none,
  11954     continue_block: Zir.Inst.OptionalIndex = .none,
  11955     /// Only valid when setBreakResultInfo is called.
  11956     break_result_info: AstGen.ResultInfo = undefined,
  11957     continue_result_info: AstGen.ResultInfo = undefined,
  11958 
  11959     suspend_node: Ast.Node.Index = 0,
  11960     nosuspend_node: Ast.Node.Index = 0,
  11961     /// Set if this GenZir is a defer.
  11962     cur_defer_node: Ast.Node.Index = 0,
  11963     // Set if this GenZir is a defer or it is inside a defer.
  11964     any_defer_node: Ast.Node.Index = 0,
  11965 
  11966     const unstacked_top = std.math.maxInt(usize);
  11967     /// Call unstack before adding any new instructions to containing GenZir.
  11968     fn unstack(self: *GenZir) void {
  11969         if (self.instructions_top != unstacked_top) {
  11970             self.instructions.items.len = self.instructions_top;
  11971             self.instructions_top = unstacked_top;
  11972         }
  11973     }
  11974 
  11975     fn isEmpty(self: *const GenZir) bool {
  11976         return (self.instructions_top == unstacked_top) or
  11977             (self.instructions.items.len == self.instructions_top);
  11978     }
  11979 
  11980     fn instructionsSlice(self: *const GenZir) []Zir.Inst.Index {
  11981         return if (self.instructions_top == unstacked_top)
  11982             &[0]Zir.Inst.Index{}
  11983         else
  11984             self.instructions.items[self.instructions_top..];
  11985     }
  11986 
  11987     fn instructionsSliceUpto(self: *const GenZir, stacked_gz: *GenZir) []Zir.Inst.Index {
  11988         return if (self.instructions_top == unstacked_top)
  11989             &[0]Zir.Inst.Index{}
  11990         else if (self.instructions == stacked_gz.instructions and stacked_gz.instructions_top != unstacked_top)
  11991             self.instructions.items[self.instructions_top..stacked_gz.instructions_top]
  11992         else
  11993             self.instructions.items[self.instructions_top..];
  11994     }
  11995 
  11996     fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {
  11997         return .{
  11998             .is_comptime = gz.is_comptime,
  11999             .is_typeof = gz.is_typeof,
  12000             .c_import = gz.c_import,
  12001             .decl_node_index = gz.decl_node_index,
  12002             .decl_line = gz.decl_line,
  12003             .parent = scope,
  12004             .astgen = gz.astgen,
  12005             .suspend_node = gz.suspend_node,
  12006             .nosuspend_node = gz.nosuspend_node,
  12007             .any_defer_node = gz.any_defer_node,
  12008             .instructions = gz.instructions,
  12009             .instructions_top = gz.instructions.items.len,
  12010         };
  12011     }
  12012 
  12013     const Label = struct {
  12014         token: Ast.TokenIndex,
  12015         block_inst: Zir.Inst.Index,
  12016         used: bool = false,
  12017         used_for_continue: bool = false,
  12018     };
  12019 
  12020     /// Assumes nothing stacked on `gz`.
  12021     fn endsWithNoReturn(gz: GenZir) bool {
  12022         if (gz.isEmpty()) return false;
  12023         const tags = gz.astgen.instructions.items(.tag);
  12024         const last_inst = gz.instructions.items[gz.instructions.items.len - 1];
  12025         return tags[@intFromEnum(last_inst)].isNoReturn();
  12026     }
  12027 
  12028     /// TODO all uses of this should be replaced with uses of `endsWithNoReturn`.
  12029     fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool {
  12030         if (inst_ref == .unreachable_value) return true;
  12031         if (inst_ref.toIndex()) |inst_index| {
  12032             return gz.astgen.instructions.items(.tag)[@intFromEnum(inst_index)].isNoReturn();
  12033         }
  12034         return false;
  12035     }
  12036 
  12037     fn nodeIndexToRelative(gz: GenZir, node_index: Ast.Node.Index) i32 {
  12038         return @as(i32, @bitCast(node_index)) - @as(i32, @bitCast(gz.decl_node_index));
  12039     }
  12040 
  12041     fn tokenIndexToRelative(gz: GenZir, token: Ast.TokenIndex) u32 {
  12042         return token - gz.srcToken();
  12043     }
  12044 
  12045     fn srcToken(gz: GenZir) Ast.TokenIndex {
  12046         return gz.astgen.tree.firstToken(gz.decl_node_index);
  12047     }
  12048 
  12049     fn setBreakResultInfo(gz: *GenZir, parent_ri: AstGen.ResultInfo) void {
  12050         // Depending on whether the result location is a pointer or value, different
  12051         // ZIR needs to be generated. In the former case we rely on storing to the
  12052         // pointer to communicate the result, and use breakvoid; in the latter case
  12053         // the block break instructions will have the result values.
  12054         switch (parent_ri.rl) {
  12055             .coerced_ty => |ty_inst| {
  12056                 // Type coercion needs to happen before breaks.
  12057                 gz.break_result_info = .{ .rl = .{ .ty = ty_inst }, .ctx = parent_ri.ctx };
  12058             },
  12059             .discard => {
  12060                 // We don't forward the result context here. This prevents
  12061                 // "unnecessary discard" errors from being caused by expressions
  12062                 // far from the actual discard, such as a `break` from a
  12063                 // discarded block.
  12064                 gz.break_result_info = .{ .rl = .discard };
  12065             },
  12066             else => {
  12067                 gz.break_result_info = parent_ri;
  12068             },
  12069         }
  12070     }
  12071 
  12072     /// Assumes nothing stacked on `gz`. Unstacks `gz`.
  12073     fn setBoolBrBody(gz: *GenZir, bool_br: Zir.Inst.Index, bool_br_lhs: Zir.Inst.Ref) !void {
  12074         const astgen = gz.astgen;
  12075         const gpa = astgen.gpa;
  12076         const body = gz.instructionsSlice();
  12077         const body_len = astgen.countBodyLenAfterFixups(body);
  12078         try astgen.extra.ensureUnusedCapacity(
  12079             gpa,
  12080             @typeInfo(Zir.Inst.BoolBr).@"struct".fields.len + body_len,
  12081         );
  12082         const zir_datas = astgen.instructions.items(.data);
  12083         zir_datas[@intFromEnum(bool_br)].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.BoolBr{
  12084             .lhs = bool_br_lhs,
  12085             .body_len = body_len,
  12086         });
  12087         astgen.appendBodyWithFixups(body);
  12088         gz.unstack();
  12089     }
  12090 
  12091     /// Assumes nothing stacked on `gz`. Unstacks `gz`.
  12092     fn setBlockBody(gz: *GenZir, inst: Zir.Inst.Index) !void {
  12093         const astgen = gz.astgen;
  12094         const gpa = astgen.gpa;
  12095         const body = gz.instructionsSlice();
  12096         const body_len = astgen.countBodyLenAfterFixups(body);
  12097         try astgen.extra.ensureUnusedCapacity(
  12098             gpa,
  12099             @typeInfo(Zir.Inst.Block).@"struct".fields.len + body_len,
  12100         );
  12101         const zir_datas = astgen.instructions.items(.data);
  12102         zir_datas[@intFromEnum(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity(
  12103             Zir.Inst.Block{ .body_len = body_len },
  12104         );
  12105         astgen.appendBodyWithFixups(body);
  12106         gz.unstack();
  12107     }
  12108 
  12109     /// Assumes nothing stacked on `gz`. Unstacks `gz`.
  12110     fn setTryBody(gz: *GenZir, inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !void {
  12111         const astgen = gz.astgen;
  12112         const gpa = astgen.gpa;
  12113         const body = gz.instructionsSlice();
  12114         const body_len = astgen.countBodyLenAfterFixups(body);
  12115         try astgen.extra.ensureUnusedCapacity(
  12116             gpa,
  12117             @typeInfo(Zir.Inst.Try).@"struct".fields.len + body_len,
  12118         );
  12119         const zir_datas = astgen.instructions.items(.data);
  12120         zir_datas[@intFromEnum(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity(
  12121             Zir.Inst.Try{
  12122                 .operand = operand,
  12123                 .body_len = body_len,
  12124             },
  12125         );
  12126         astgen.appendBodyWithFixups(body);
  12127         gz.unstack();
  12128     }
  12129 
  12130     /// Must be called with the following stack set up:
  12131     ///  * gz (bottom)
  12132     ///  * align_gz
  12133     ///  * addrspace_gz
  12134     ///  * section_gz
  12135     ///  * cc_gz
  12136     ///  * ret_gz
  12137     ///  * body_gz (top)
  12138     /// Unstacks all of those except for `gz`.
  12139     fn addFunc(gz: *GenZir, args: struct {
  12140         src_node: Ast.Node.Index,
  12141         lbrace_line: u32 = 0,
  12142         lbrace_column: u32 = 0,
  12143         param_block: Zir.Inst.Index,
  12144 
  12145         align_gz: ?*GenZir,
  12146         addrspace_gz: ?*GenZir,
  12147         section_gz: ?*GenZir,
  12148         cc_gz: ?*GenZir,
  12149         ret_gz: ?*GenZir,
  12150         body_gz: ?*GenZir,
  12151 
  12152         align_ref: Zir.Inst.Ref,
  12153         addrspace_ref: Zir.Inst.Ref,
  12154         section_ref: Zir.Inst.Ref,
  12155         cc_ref: Zir.Inst.Ref,
  12156         ret_ref: Zir.Inst.Ref,
  12157 
  12158         lib_name: Zir.NullTerminatedString,
  12159         noalias_bits: u32,
  12160         is_var_args: bool,
  12161         is_inferred_error: bool,
  12162         is_test: bool,
  12163         is_extern: bool,
  12164         is_noinline: bool,
  12165 
  12166         /// Ignored if `body_gz == null`.
  12167         proto_hash: std.zig.SrcHash,
  12168     }) !Zir.Inst.Ref {
  12169         assert(args.src_node != 0);
  12170         const astgen = gz.astgen;
  12171         const gpa = astgen.gpa;
  12172         const ret_ref = if (args.ret_ref == .void_type) .none else args.ret_ref;
  12173         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12174 
  12175         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12176 
  12177         var body: []Zir.Inst.Index = &[0]Zir.Inst.Index{};
  12178         var ret_body: []Zir.Inst.Index = &[0]Zir.Inst.Index{};
  12179         var src_locs_and_hash_buffer: [7]u32 = undefined;
  12180         var src_locs_and_hash: []u32 = src_locs_and_hash_buffer[0..0];
  12181         if (args.body_gz) |body_gz| {
  12182             const tree = astgen.tree;
  12183             const node_tags = tree.nodes.items(.tag);
  12184             const node_datas = tree.nodes.items(.data);
  12185             const token_starts = tree.tokens.items(.start);
  12186             const fn_decl = args.src_node;
  12187             assert(node_tags[fn_decl] == .fn_decl or node_tags[fn_decl] == .test_decl);
  12188             const block = node_datas[fn_decl].rhs;
  12189             const rbrace_start = token_starts[tree.lastToken(block)];
  12190             astgen.advanceSourceCursor(rbrace_start);
  12191             const rbrace_line: u32 = @intCast(astgen.source_line - gz.decl_line);
  12192             const rbrace_column: u32 = @intCast(astgen.source_column);
  12193 
  12194             const columns = args.lbrace_column | (rbrace_column << 16);
  12195 
  12196             const proto_hash_arr: [4]u32 = @bitCast(args.proto_hash);
  12197 
  12198             src_locs_and_hash_buffer = .{
  12199                 args.lbrace_line,
  12200                 rbrace_line,
  12201                 columns,
  12202                 proto_hash_arr[0],
  12203                 proto_hash_arr[1],
  12204                 proto_hash_arr[2],
  12205                 proto_hash_arr[3],
  12206             };
  12207             src_locs_and_hash = &src_locs_and_hash_buffer;
  12208 
  12209             body = body_gz.instructionsSlice();
  12210             if (args.ret_gz) |ret_gz|
  12211                 ret_body = ret_gz.instructionsSliceUpto(body_gz);
  12212         } else {
  12213             if (args.ret_gz) |ret_gz|
  12214                 ret_body = ret_gz.instructionsSlice();
  12215         }
  12216         const body_len = astgen.countBodyLenAfterFixups(body);
  12217 
  12218         if (args.cc_ref != .none or args.lib_name != .empty or args.is_var_args or args.is_test or
  12219             args.is_extern or args.align_ref != .none or args.section_ref != .none or
  12220             args.addrspace_ref != .none or args.noalias_bits != 0 or args.is_noinline)
  12221         {
  12222             var align_body: []Zir.Inst.Index = &.{};
  12223             var addrspace_body: []Zir.Inst.Index = &.{};
  12224             var section_body: []Zir.Inst.Index = &.{};
  12225             var cc_body: []Zir.Inst.Index = &.{};
  12226             if (args.ret_gz != null) {
  12227                 align_body = args.align_gz.?.instructionsSliceUpto(args.addrspace_gz.?);
  12228                 addrspace_body = args.addrspace_gz.?.instructionsSliceUpto(args.section_gz.?);
  12229                 section_body = args.section_gz.?.instructionsSliceUpto(args.cc_gz.?);
  12230                 cc_body = args.cc_gz.?.instructionsSliceUpto(args.ret_gz.?);
  12231             }
  12232 
  12233             try astgen.extra.ensureUnusedCapacity(
  12234                 gpa,
  12235                 @typeInfo(Zir.Inst.FuncFancy).@"struct".fields.len +
  12236                     fancyFnExprExtraLen(astgen, align_body, args.align_ref) +
  12237                     fancyFnExprExtraLen(astgen, addrspace_body, args.addrspace_ref) +
  12238                     fancyFnExprExtraLen(astgen, section_body, args.section_ref) +
  12239                     fancyFnExprExtraLen(astgen, cc_body, args.cc_ref) +
  12240                     fancyFnExprExtraLen(astgen, ret_body, ret_ref) +
  12241                     body_len + src_locs_and_hash.len +
  12242                     @intFromBool(args.lib_name != .empty) +
  12243                     @intFromBool(args.noalias_bits != 0),
  12244             );
  12245             const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.FuncFancy{
  12246                 .param_block = args.param_block,
  12247                 .body_len = body_len,
  12248                 .bits = .{
  12249                     .is_var_args = args.is_var_args,
  12250                     .is_inferred_error = args.is_inferred_error,
  12251                     .is_test = args.is_test,
  12252                     .is_extern = args.is_extern,
  12253                     .is_noinline = args.is_noinline,
  12254                     .has_lib_name = args.lib_name != .empty,
  12255                     .has_any_noalias = args.noalias_bits != 0,
  12256 
  12257                     .has_align_ref = args.align_ref != .none,
  12258                     .has_addrspace_ref = args.addrspace_ref != .none,
  12259                     .has_section_ref = args.section_ref != .none,
  12260                     .has_cc_ref = args.cc_ref != .none,
  12261                     .has_ret_ty_ref = ret_ref != .none,
  12262 
  12263                     .has_align_body = align_body.len != 0,
  12264                     .has_addrspace_body = addrspace_body.len != 0,
  12265                     .has_section_body = section_body.len != 0,
  12266                     .has_cc_body = cc_body.len != 0,
  12267                     .has_ret_ty_body = ret_body.len != 0,
  12268                 },
  12269             });
  12270             if (args.lib_name != .empty) {
  12271                 astgen.extra.appendAssumeCapacity(@intFromEnum(args.lib_name));
  12272             }
  12273 
  12274             const zir_datas = astgen.instructions.items(.data);
  12275             if (align_body.len != 0) {
  12276                 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, align_body));
  12277                 astgen.appendBodyWithFixups(align_body);
  12278                 const break_extra = zir_datas[@intFromEnum(align_body[align_body.len - 1])].@"break".payload_index;
  12279                 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
  12280                     @intFromEnum(new_index);
  12281             } else if (args.align_ref != .none) {
  12282                 astgen.extra.appendAssumeCapacity(@intFromEnum(args.align_ref));
  12283             }
  12284             if (addrspace_body.len != 0) {
  12285                 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, addrspace_body));
  12286                 astgen.appendBodyWithFixups(addrspace_body);
  12287                 const break_extra =
  12288                     zir_datas[@intFromEnum(addrspace_body[addrspace_body.len - 1])].@"break".payload_index;
  12289                 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
  12290                     @intFromEnum(new_index);
  12291             } else if (args.addrspace_ref != .none) {
  12292                 astgen.extra.appendAssumeCapacity(@intFromEnum(args.addrspace_ref));
  12293             }
  12294             if (section_body.len != 0) {
  12295                 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, section_body));
  12296                 astgen.appendBodyWithFixups(section_body);
  12297                 const break_extra =
  12298                     zir_datas[@intFromEnum(section_body[section_body.len - 1])].@"break".payload_index;
  12299                 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
  12300                     @intFromEnum(new_index);
  12301             } else if (args.section_ref != .none) {
  12302                 astgen.extra.appendAssumeCapacity(@intFromEnum(args.section_ref));
  12303             }
  12304             if (cc_body.len != 0) {
  12305                 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, cc_body));
  12306                 astgen.appendBodyWithFixups(cc_body);
  12307                 const break_extra = zir_datas[@intFromEnum(cc_body[cc_body.len - 1])].@"break".payload_index;
  12308                 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
  12309                     @intFromEnum(new_index);
  12310             } else if (args.cc_ref != .none) {
  12311                 astgen.extra.appendAssumeCapacity(@intFromEnum(args.cc_ref));
  12312             }
  12313             if (ret_body.len != 0) {
  12314                 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, ret_body));
  12315                 astgen.appendBodyWithFixups(ret_body);
  12316                 const break_extra = zir_datas[@intFromEnum(ret_body[ret_body.len - 1])].@"break".payload_index;
  12317                 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
  12318                     @intFromEnum(new_index);
  12319             } else if (ret_ref != .none) {
  12320                 astgen.extra.appendAssumeCapacity(@intFromEnum(ret_ref));
  12321             }
  12322 
  12323             if (args.noalias_bits != 0) {
  12324                 astgen.extra.appendAssumeCapacity(args.noalias_bits);
  12325             }
  12326 
  12327             astgen.appendBodyWithFixups(body);
  12328             astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash);
  12329 
  12330             // Order is important when unstacking.
  12331             if (args.body_gz) |body_gz| body_gz.unstack();
  12332             if (args.ret_gz != null) {
  12333                 args.ret_gz.?.unstack();
  12334                 args.cc_gz.?.unstack();
  12335                 args.section_gz.?.unstack();
  12336                 args.addrspace_gz.?.unstack();
  12337                 args.align_gz.?.unstack();
  12338             }
  12339 
  12340             try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12341 
  12342             astgen.instructions.appendAssumeCapacity(.{
  12343                 .tag = .func_fancy,
  12344                 .data = .{ .pl_node = .{
  12345                     .src_node = gz.nodeIndexToRelative(args.src_node),
  12346                     .payload_index = payload_index,
  12347                 } },
  12348             });
  12349             gz.instructions.appendAssumeCapacity(new_index);
  12350             return new_index.toRef();
  12351         } else {
  12352             try astgen.extra.ensureUnusedCapacity(
  12353                 gpa,
  12354                 @typeInfo(Zir.Inst.Func).@"struct".fields.len + 1 +
  12355                     fancyFnExprExtraLen(astgen, ret_body, ret_ref) +
  12356                     body_len + src_locs_and_hash.len,
  12357             );
  12358 
  12359             const ret_body_len = if (ret_body.len != 0)
  12360                 countBodyLenAfterFixups(astgen, ret_body)
  12361             else
  12362                 @intFromBool(ret_ref != .none);
  12363 
  12364             const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.Func{
  12365                 .param_block = args.param_block,
  12366                 .ret_body_len = ret_body_len,
  12367                 .body_len = body_len,
  12368             });
  12369             const zir_datas = astgen.instructions.items(.data);
  12370             if (ret_body.len != 0) {
  12371                 astgen.appendBodyWithFixups(ret_body);
  12372 
  12373                 const break_extra = zir_datas[@intFromEnum(ret_body[ret_body.len - 1])].@"break".payload_index;
  12374                 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
  12375                     @intFromEnum(new_index);
  12376             } else if (ret_ref != .none) {
  12377                 astgen.extra.appendAssumeCapacity(@intFromEnum(ret_ref));
  12378             }
  12379             astgen.appendBodyWithFixups(body);
  12380             astgen.extra.appendSliceAssumeCapacity(src_locs_and_hash);
  12381 
  12382             // Order is important when unstacking.
  12383             if (args.body_gz) |body_gz| body_gz.unstack();
  12384             if (args.ret_gz) |ret_gz| ret_gz.unstack();
  12385             if (args.cc_gz) |cc_gz| cc_gz.unstack();
  12386             if (args.section_gz) |section_gz| section_gz.unstack();
  12387             if (args.addrspace_gz) |addrspace_gz| addrspace_gz.unstack();
  12388             if (args.align_gz) |align_gz| align_gz.unstack();
  12389 
  12390             try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12391 
  12392             const tag: Zir.Inst.Tag = if (args.is_inferred_error) .func_inferred else .func;
  12393             astgen.instructions.appendAssumeCapacity(.{
  12394                 .tag = tag,
  12395                 .data = .{ .pl_node = .{
  12396                     .src_node = gz.nodeIndexToRelative(args.src_node),
  12397                     .payload_index = payload_index,
  12398                 } },
  12399             });
  12400             gz.instructions.appendAssumeCapacity(new_index);
  12401             return new_index.toRef();
  12402         }
  12403     }
  12404 
  12405     fn fancyFnExprExtraLen(astgen: *AstGen, body: []Zir.Inst.Index, ref: Zir.Inst.Ref) u32 {
  12406         // In the case of non-empty body, there is one for the body length,
  12407         // and then one for each instruction.
  12408         return countBodyLenAfterFixups(astgen, body) + @intFromBool(ref != .none);
  12409     }
  12410 
  12411     fn addVar(gz: *GenZir, args: struct {
  12412         align_inst: Zir.Inst.Ref,
  12413         lib_name: Zir.NullTerminatedString,
  12414         var_type: Zir.Inst.Ref,
  12415         init: Zir.Inst.Ref,
  12416         is_extern: bool,
  12417         is_const: bool,
  12418         is_threadlocal: bool,
  12419     }) !Zir.Inst.Ref {
  12420         const astgen = gz.astgen;
  12421         const gpa = astgen.gpa;
  12422 
  12423         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12424         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12425 
  12426         try astgen.extra.ensureUnusedCapacity(
  12427             gpa,
  12428             @typeInfo(Zir.Inst.ExtendedVar).@"struct".fields.len +
  12429                 @intFromBool(args.lib_name != .empty) +
  12430                 @intFromBool(args.align_inst != .none) +
  12431                 @intFromBool(args.init != .none),
  12432         );
  12433         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.ExtendedVar{
  12434             .var_type = args.var_type,
  12435         });
  12436         if (args.lib_name != .empty) {
  12437             astgen.extra.appendAssumeCapacity(@intFromEnum(args.lib_name));
  12438         }
  12439         if (args.align_inst != .none) {
  12440             astgen.extra.appendAssumeCapacity(@intFromEnum(args.align_inst));
  12441         }
  12442         if (args.init != .none) {
  12443             astgen.extra.appendAssumeCapacity(@intFromEnum(args.init));
  12444         }
  12445 
  12446         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12447         astgen.instructions.appendAssumeCapacity(.{
  12448             .tag = .extended,
  12449             .data = .{ .extended = .{
  12450                 .opcode = .variable,
  12451                 .small = @bitCast(Zir.Inst.ExtendedVar.Small{
  12452                     .has_lib_name = args.lib_name != .empty,
  12453                     .has_align = args.align_inst != .none,
  12454                     .has_init = args.init != .none,
  12455                     .is_extern = args.is_extern,
  12456                     .is_const = args.is_const,
  12457                     .is_threadlocal = args.is_threadlocal,
  12458                 }),
  12459                 .operand = payload_index,
  12460             } },
  12461         });
  12462         gz.instructions.appendAssumeCapacity(new_index);
  12463         return new_index.toRef();
  12464     }
  12465 
  12466     fn addInt(gz: *GenZir, integer: u64) !Zir.Inst.Ref {
  12467         return gz.add(.{
  12468             .tag = .int,
  12469             .data = .{ .int = integer },
  12470         });
  12471     }
  12472 
  12473     fn addIntBig(gz: *GenZir, limbs: []const std.math.big.Limb) !Zir.Inst.Ref {
  12474         const astgen = gz.astgen;
  12475         const gpa = astgen.gpa;
  12476         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12477         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12478         try astgen.string_bytes.ensureUnusedCapacity(gpa, @sizeOf(std.math.big.Limb) * limbs.len);
  12479 
  12480         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12481         astgen.instructions.appendAssumeCapacity(.{
  12482             .tag = .int_big,
  12483             .data = .{ .str = .{
  12484                 .start = @enumFromInt(astgen.string_bytes.items.len),
  12485                 .len = @intCast(limbs.len),
  12486             } },
  12487         });
  12488         gz.instructions.appendAssumeCapacity(new_index);
  12489         astgen.string_bytes.appendSliceAssumeCapacity(mem.sliceAsBytes(limbs));
  12490         return new_index.toRef();
  12491     }
  12492 
  12493     fn addFloat(gz: *GenZir, number: f64) !Zir.Inst.Ref {
  12494         return gz.add(.{
  12495             .tag = .float,
  12496             .data = .{ .float = number },
  12497         });
  12498     }
  12499 
  12500     fn addUnNode(
  12501         gz: *GenZir,
  12502         tag: Zir.Inst.Tag,
  12503         operand: Zir.Inst.Ref,
  12504         /// Absolute node index. This function does the conversion to offset from Decl.
  12505         src_node: Ast.Node.Index,
  12506     ) !Zir.Inst.Ref {
  12507         assert(operand != .none);
  12508         return gz.add(.{
  12509             .tag = tag,
  12510             .data = .{ .un_node = .{
  12511                 .operand = operand,
  12512                 .src_node = gz.nodeIndexToRelative(src_node),
  12513             } },
  12514         });
  12515     }
  12516 
  12517     fn makeUnNode(
  12518         gz: *GenZir,
  12519         tag: Zir.Inst.Tag,
  12520         operand: Zir.Inst.Ref,
  12521         /// Absolute node index. This function does the conversion to offset from Decl.
  12522         src_node: Ast.Node.Index,
  12523     ) !Zir.Inst.Index {
  12524         assert(operand != .none);
  12525         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  12526         try gz.astgen.instructions.append(gz.astgen.gpa, .{
  12527             .tag = tag,
  12528             .data = .{ .un_node = .{
  12529                 .operand = operand,
  12530                 .src_node = gz.nodeIndexToRelative(src_node),
  12531             } },
  12532         });
  12533         return new_index;
  12534     }
  12535 
  12536     fn addPlNode(
  12537         gz: *GenZir,
  12538         tag: Zir.Inst.Tag,
  12539         /// Absolute node index. This function does the conversion to offset from Decl.
  12540         src_node: Ast.Node.Index,
  12541         extra: anytype,
  12542     ) !Zir.Inst.Ref {
  12543         const gpa = gz.astgen.gpa;
  12544         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12545         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12546 
  12547         const payload_index = try gz.astgen.addExtra(extra);
  12548         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  12549         gz.astgen.instructions.appendAssumeCapacity(.{
  12550             .tag = tag,
  12551             .data = .{ .pl_node = .{
  12552                 .src_node = gz.nodeIndexToRelative(src_node),
  12553                 .payload_index = payload_index,
  12554             } },
  12555         });
  12556         gz.instructions.appendAssumeCapacity(new_index);
  12557         return new_index.toRef();
  12558     }
  12559 
  12560     fn addPlNodePayloadIndex(
  12561         gz: *GenZir,
  12562         tag: Zir.Inst.Tag,
  12563         /// Absolute node index. This function does the conversion to offset from Decl.
  12564         src_node: Ast.Node.Index,
  12565         payload_index: u32,
  12566     ) !Zir.Inst.Ref {
  12567         return try gz.add(.{
  12568             .tag = tag,
  12569             .data = .{ .pl_node = .{
  12570                 .src_node = gz.nodeIndexToRelative(src_node),
  12571                 .payload_index = payload_index,
  12572             } },
  12573         });
  12574     }
  12575 
  12576     /// Supports `param_gz` stacked on `gz`. Assumes nothing stacked on `param_gz`. Unstacks `param_gz`.
  12577     fn addParam(
  12578         gz: *GenZir,
  12579         param_gz: *GenZir,
  12580         tag: Zir.Inst.Tag,
  12581         /// Absolute token index. This function does the conversion to Decl offset.
  12582         abs_tok_index: Ast.TokenIndex,
  12583         name: Zir.NullTerminatedString,
  12584         first_doc_comment: ?Ast.TokenIndex,
  12585     ) !Zir.Inst.Index {
  12586         const gpa = gz.astgen.gpa;
  12587         const param_body = param_gz.instructionsSlice();
  12588         const body_len = gz.astgen.countBodyLenAfterFixups(param_body);
  12589         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12590         try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Param).@"struct".fields.len + body_len);
  12591 
  12592         const doc_comment_index = if (first_doc_comment) |first|
  12593             try gz.astgen.docCommentAsStringFromFirst(abs_tok_index, first)
  12594         else
  12595             .empty;
  12596 
  12597         const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Param{
  12598             .name = name,
  12599             .doc_comment = doc_comment_index,
  12600             .body_len = @intCast(body_len),
  12601         });
  12602         gz.astgen.appendBodyWithFixups(param_body);
  12603         param_gz.unstack();
  12604 
  12605         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  12606         gz.astgen.instructions.appendAssumeCapacity(.{
  12607             .tag = tag,
  12608             .data = .{ .pl_tok = .{
  12609                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  12610                 .payload_index = payload_index,
  12611             } },
  12612         });
  12613         gz.instructions.appendAssumeCapacity(new_index);
  12614         return new_index;
  12615     }
  12616 
  12617     fn addBuiltinValue(gz: *GenZir, src_node: Ast.Node.Index, val: Zir.Inst.BuiltinValue) !Zir.Inst.Ref {
  12618         return addExtendedNodeSmall(gz, .builtin_value, src_node, @intFromEnum(val));
  12619     }
  12620 
  12621     fn addExtendedPayload(gz: *GenZir, opcode: Zir.Inst.Extended, extra: anytype) !Zir.Inst.Ref {
  12622         return addExtendedPayloadSmall(gz, opcode, undefined, extra);
  12623     }
  12624 
  12625     fn addExtendedPayloadSmall(
  12626         gz: *GenZir,
  12627         opcode: Zir.Inst.Extended,
  12628         small: u16,
  12629         extra: anytype,
  12630     ) !Zir.Inst.Ref {
  12631         const gpa = gz.astgen.gpa;
  12632 
  12633         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12634         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12635 
  12636         const payload_index = try gz.astgen.addExtra(extra);
  12637         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  12638         gz.astgen.instructions.appendAssumeCapacity(.{
  12639             .tag = .extended,
  12640             .data = .{ .extended = .{
  12641                 .opcode = opcode,
  12642                 .small = small,
  12643                 .operand = payload_index,
  12644             } },
  12645         });
  12646         gz.instructions.appendAssumeCapacity(new_index);
  12647         return new_index.toRef();
  12648     }
  12649 
  12650     fn addExtendedMultiOp(
  12651         gz: *GenZir,
  12652         opcode: Zir.Inst.Extended,
  12653         node: Ast.Node.Index,
  12654         operands: []const Zir.Inst.Ref,
  12655     ) !Zir.Inst.Ref {
  12656         const astgen = gz.astgen;
  12657         const gpa = astgen.gpa;
  12658 
  12659         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12660         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12661         try astgen.extra.ensureUnusedCapacity(
  12662             gpa,
  12663             @typeInfo(Zir.Inst.NodeMultiOp).@"struct".fields.len + operands.len,
  12664         );
  12665 
  12666         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.NodeMultiOp{
  12667             .src_node = gz.nodeIndexToRelative(node),
  12668         });
  12669         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12670         astgen.instructions.appendAssumeCapacity(.{
  12671             .tag = .extended,
  12672             .data = .{ .extended = .{
  12673                 .opcode = opcode,
  12674                 .small = @intCast(operands.len),
  12675                 .operand = payload_index,
  12676             } },
  12677         });
  12678         gz.instructions.appendAssumeCapacity(new_index);
  12679         astgen.appendRefsAssumeCapacity(operands);
  12680         return new_index.toRef();
  12681     }
  12682 
  12683     fn addExtendedMultiOpPayloadIndex(
  12684         gz: *GenZir,
  12685         opcode: Zir.Inst.Extended,
  12686         payload_index: u32,
  12687         trailing_len: usize,
  12688     ) !Zir.Inst.Ref {
  12689         const astgen = gz.astgen;
  12690         const gpa = astgen.gpa;
  12691 
  12692         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12693         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12694         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12695         astgen.instructions.appendAssumeCapacity(.{
  12696             .tag = .extended,
  12697             .data = .{ .extended = .{
  12698                 .opcode = opcode,
  12699                 .small = @intCast(trailing_len),
  12700                 .operand = payload_index,
  12701             } },
  12702         });
  12703         gz.instructions.appendAssumeCapacity(new_index);
  12704         return new_index.toRef();
  12705     }
  12706 
  12707     fn addExtendedNodeSmall(
  12708         gz: *GenZir,
  12709         opcode: Zir.Inst.Extended,
  12710         src_node: Ast.Node.Index,
  12711         small: u16,
  12712     ) !Zir.Inst.Ref {
  12713         const astgen = gz.astgen;
  12714         const gpa = astgen.gpa;
  12715 
  12716         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12717         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12718         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12719         astgen.instructions.appendAssumeCapacity(.{
  12720             .tag = .extended,
  12721             .data = .{ .extended = .{
  12722                 .opcode = opcode,
  12723                 .small = small,
  12724                 .operand = @bitCast(gz.nodeIndexToRelative(src_node)),
  12725             } },
  12726         });
  12727         gz.instructions.appendAssumeCapacity(new_index);
  12728         return new_index.toRef();
  12729     }
  12730 
  12731     fn addUnTok(
  12732         gz: *GenZir,
  12733         tag: Zir.Inst.Tag,
  12734         operand: Zir.Inst.Ref,
  12735         /// Absolute token index. This function does the conversion to Decl offset.
  12736         abs_tok_index: Ast.TokenIndex,
  12737     ) !Zir.Inst.Ref {
  12738         assert(operand != .none);
  12739         return gz.add(.{
  12740             .tag = tag,
  12741             .data = .{ .un_tok = .{
  12742                 .operand = operand,
  12743                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  12744             } },
  12745         });
  12746     }
  12747 
  12748     fn makeUnTok(
  12749         gz: *GenZir,
  12750         tag: Zir.Inst.Tag,
  12751         operand: Zir.Inst.Ref,
  12752         /// Absolute token index. This function does the conversion to Decl offset.
  12753         abs_tok_index: Ast.TokenIndex,
  12754     ) !Zir.Inst.Index {
  12755         const astgen = gz.astgen;
  12756         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  12757         assert(operand != .none);
  12758         try astgen.instructions.append(astgen.gpa, .{
  12759             .tag = tag,
  12760             .data = .{ .un_tok = .{
  12761                 .operand = operand,
  12762                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  12763             } },
  12764         });
  12765         return new_index;
  12766     }
  12767 
  12768     fn addStrTok(
  12769         gz: *GenZir,
  12770         tag: Zir.Inst.Tag,
  12771         str_index: Zir.NullTerminatedString,
  12772         /// Absolute token index. This function does the conversion to Decl offset.
  12773         abs_tok_index: Ast.TokenIndex,
  12774     ) !Zir.Inst.Ref {
  12775         return gz.add(.{
  12776             .tag = tag,
  12777             .data = .{ .str_tok = .{
  12778                 .start = str_index,
  12779                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  12780             } },
  12781         });
  12782     }
  12783 
  12784     fn addSaveErrRetIndex(
  12785         gz: *GenZir,
  12786         cond: union(enum) {
  12787             always: void,
  12788             if_of_error_type: Zir.Inst.Ref,
  12789         },
  12790     ) !Zir.Inst.Index {
  12791         return gz.addAsIndex(.{
  12792             .tag = .save_err_ret_index,
  12793             .data = .{ .save_err_ret_index = .{
  12794                 .operand = switch (cond) {
  12795                     .if_of_error_type => |x| x,
  12796                     else => .none,
  12797                 },
  12798             } },
  12799         });
  12800     }
  12801 
  12802     const BranchTarget = union(enum) {
  12803         ret,
  12804         block: Zir.Inst.Index,
  12805     };
  12806 
  12807     fn addRestoreErrRetIndex(
  12808         gz: *GenZir,
  12809         bt: BranchTarget,
  12810         cond: union(enum) {
  12811             always: void,
  12812             if_non_error: Zir.Inst.Ref,
  12813         },
  12814         src_node: Ast.Node.Index,
  12815     ) !Zir.Inst.Index {
  12816         switch (cond) {
  12817             .always => return gz.addAsIndex(.{
  12818                 .tag = .restore_err_ret_index_unconditional,
  12819                 .data = .{ .un_node = .{
  12820                     .operand = switch (bt) {
  12821                         .ret => .none,
  12822                         .block => |b| b.toRef(),
  12823                     },
  12824                     .src_node = gz.nodeIndexToRelative(src_node),
  12825                 } },
  12826             }),
  12827             .if_non_error => |operand| switch (bt) {
  12828                 .ret => return gz.addAsIndex(.{
  12829                     .tag = .restore_err_ret_index_fn_entry,
  12830                     .data = .{ .un_node = .{
  12831                         .operand = operand,
  12832                         .src_node = gz.nodeIndexToRelative(src_node),
  12833                     } },
  12834                 }),
  12835                 .block => |block| return (try gz.addExtendedPayload(
  12836                     .restore_err_ret_index,
  12837                     Zir.Inst.RestoreErrRetIndex{
  12838                         .src_node = gz.nodeIndexToRelative(src_node),
  12839                         .block = block.toRef(),
  12840                         .operand = operand,
  12841                     },
  12842                 )).toIndex().?,
  12843             },
  12844         }
  12845     }
  12846 
  12847     fn addBreak(
  12848         gz: *GenZir,
  12849         tag: Zir.Inst.Tag,
  12850         block_inst: Zir.Inst.Index,
  12851         operand: Zir.Inst.Ref,
  12852     ) !Zir.Inst.Index {
  12853         const gpa = gz.astgen.gpa;
  12854         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12855 
  12856         const new_index = try gz.makeBreak(tag, block_inst, operand);
  12857         gz.instructions.appendAssumeCapacity(new_index);
  12858         return new_index;
  12859     }
  12860 
  12861     fn makeBreak(
  12862         gz: *GenZir,
  12863         tag: Zir.Inst.Tag,
  12864         block_inst: Zir.Inst.Index,
  12865         operand: Zir.Inst.Ref,
  12866     ) !Zir.Inst.Index {
  12867         return gz.makeBreakCommon(tag, block_inst, operand, null);
  12868     }
  12869 
  12870     fn addBreakWithSrcNode(
  12871         gz: *GenZir,
  12872         tag: Zir.Inst.Tag,
  12873         block_inst: Zir.Inst.Index,
  12874         operand: Zir.Inst.Ref,
  12875         operand_src_node: Ast.Node.Index,
  12876     ) !Zir.Inst.Index {
  12877         const gpa = gz.astgen.gpa;
  12878         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12879 
  12880         const new_index = try gz.makeBreakWithSrcNode(tag, block_inst, operand, operand_src_node);
  12881         gz.instructions.appendAssumeCapacity(new_index);
  12882         return new_index;
  12883     }
  12884 
  12885     fn makeBreakWithSrcNode(
  12886         gz: *GenZir,
  12887         tag: Zir.Inst.Tag,
  12888         block_inst: Zir.Inst.Index,
  12889         operand: Zir.Inst.Ref,
  12890         operand_src_node: Ast.Node.Index,
  12891     ) !Zir.Inst.Index {
  12892         return gz.makeBreakCommon(tag, block_inst, operand, operand_src_node);
  12893     }
  12894 
  12895     fn makeBreakCommon(
  12896         gz: *GenZir,
  12897         tag: Zir.Inst.Tag,
  12898         block_inst: Zir.Inst.Index,
  12899         operand: Zir.Inst.Ref,
  12900         operand_src_node: ?Ast.Node.Index,
  12901     ) !Zir.Inst.Index {
  12902         const gpa = gz.astgen.gpa;
  12903         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12904         try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Break).@"struct".fields.len);
  12905 
  12906         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  12907         gz.astgen.instructions.appendAssumeCapacity(.{
  12908             .tag = tag,
  12909             .data = .{ .@"break" = .{
  12910                 .operand = operand,
  12911                 .payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Break{
  12912                     .operand_src_node = if (operand_src_node) |src_node|
  12913                         gz.nodeIndexToRelative(src_node)
  12914                     else
  12915                         Zir.Inst.Break.no_src_node,
  12916                     .block_inst = block_inst,
  12917                 }),
  12918             } },
  12919         });
  12920         return new_index;
  12921     }
  12922 
  12923     fn addBin(
  12924         gz: *GenZir,
  12925         tag: Zir.Inst.Tag,
  12926         lhs: Zir.Inst.Ref,
  12927         rhs: Zir.Inst.Ref,
  12928     ) !Zir.Inst.Ref {
  12929         assert(lhs != .none);
  12930         assert(rhs != .none);
  12931         return gz.add(.{
  12932             .tag = tag,
  12933             .data = .{ .bin = .{
  12934                 .lhs = lhs,
  12935                 .rhs = rhs,
  12936             } },
  12937         });
  12938     }
  12939 
  12940     fn addDefer(gz: *GenZir, index: u32, len: u32) !void {
  12941         _ = try gz.add(.{
  12942             .tag = .@"defer",
  12943             .data = .{ .@"defer" = .{
  12944                 .index = index,
  12945                 .len = len,
  12946             } },
  12947         });
  12948     }
  12949 
  12950     fn addDecl(
  12951         gz: *GenZir,
  12952         tag: Zir.Inst.Tag,
  12953         decl_index: u32,
  12954         src_node: Ast.Node.Index,
  12955     ) !Zir.Inst.Ref {
  12956         return gz.add(.{
  12957             .tag = tag,
  12958             .data = .{ .pl_node = .{
  12959                 .src_node = gz.nodeIndexToRelative(src_node),
  12960                 .payload_index = decl_index,
  12961             } },
  12962         });
  12963     }
  12964 
  12965     fn addNode(
  12966         gz: *GenZir,
  12967         tag: Zir.Inst.Tag,
  12968         /// Absolute node index. This function does the conversion to offset from Decl.
  12969         src_node: Ast.Node.Index,
  12970     ) !Zir.Inst.Ref {
  12971         return gz.add(.{
  12972             .tag = tag,
  12973             .data = .{ .node = gz.nodeIndexToRelative(src_node) },
  12974         });
  12975     }
  12976 
  12977     fn addInstNode(
  12978         gz: *GenZir,
  12979         tag: Zir.Inst.Tag,
  12980         inst: Zir.Inst.Index,
  12981         /// Absolute node index. This function does the conversion to offset from Decl.
  12982         src_node: Ast.Node.Index,
  12983     ) !Zir.Inst.Ref {
  12984         return gz.add(.{
  12985             .tag = tag,
  12986             .data = .{ .inst_node = .{
  12987                 .inst = inst,
  12988                 .src_node = gz.nodeIndexToRelative(src_node),
  12989             } },
  12990         });
  12991     }
  12992 
  12993     fn addNodeExtended(
  12994         gz: *GenZir,
  12995         opcode: Zir.Inst.Extended,
  12996         /// Absolute node index. This function does the conversion to offset from Decl.
  12997         src_node: Ast.Node.Index,
  12998     ) !Zir.Inst.Ref {
  12999         return gz.add(.{
  13000             .tag = .extended,
  13001             .data = .{ .extended = .{
  13002                 .opcode = opcode,
  13003                 .small = undefined,
  13004                 .operand = @bitCast(gz.nodeIndexToRelative(src_node)),
  13005             } },
  13006         });
  13007     }
  13008 
  13009     fn addAllocExtended(
  13010         gz: *GenZir,
  13011         args: struct {
  13012             /// Absolute node index. This function does the conversion to offset from Decl.
  13013             node: Ast.Node.Index,
  13014             type_inst: Zir.Inst.Ref,
  13015             align_inst: Zir.Inst.Ref,
  13016             is_const: bool,
  13017             is_comptime: bool,
  13018         },
  13019     ) !Zir.Inst.Ref {
  13020         const astgen = gz.astgen;
  13021         const gpa = astgen.gpa;
  13022 
  13023         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  13024         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  13025         try astgen.extra.ensureUnusedCapacity(
  13026             gpa,
  13027             @typeInfo(Zir.Inst.AllocExtended).@"struct".fields.len +
  13028                 @intFromBool(args.type_inst != .none) +
  13029                 @intFromBool(args.align_inst != .none),
  13030         );
  13031         const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.AllocExtended{
  13032             .src_node = gz.nodeIndexToRelative(args.node),
  13033         });
  13034         if (args.type_inst != .none) {
  13035             astgen.extra.appendAssumeCapacity(@intFromEnum(args.type_inst));
  13036         }
  13037         if (args.align_inst != .none) {
  13038             astgen.extra.appendAssumeCapacity(@intFromEnum(args.align_inst));
  13039         }
  13040 
  13041         const has_type: u4 = @intFromBool(args.type_inst != .none);
  13042         const has_align: u4 = @intFromBool(args.align_inst != .none);
  13043         const is_const: u4 = @intFromBool(args.is_const);
  13044         const is_comptime: u4 = @intFromBool(args.is_comptime);
  13045         const small: u16 = has_type | (has_align << 1) | (is_const << 2) | (is_comptime << 3);
  13046 
  13047         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  13048         astgen.instructions.appendAssumeCapacity(.{
  13049             .tag = .extended,
  13050             .data = .{ .extended = .{
  13051                 .opcode = .alloc,
  13052                 .small = small,
  13053                 .operand = payload_index,
  13054             } },
  13055         });
  13056         gz.instructions.appendAssumeCapacity(new_index);
  13057         return new_index.toRef();
  13058     }
  13059 
  13060     fn addAsm(
  13061         gz: *GenZir,
  13062         args: struct {
  13063             tag: Zir.Inst.Extended,
  13064             /// Absolute node index. This function does the conversion to offset from Decl.
  13065             node: Ast.Node.Index,
  13066             asm_source: Zir.NullTerminatedString,
  13067             output_type_bits: u32,
  13068             is_volatile: bool,
  13069             outputs: []const Zir.Inst.Asm.Output,
  13070             inputs: []const Zir.Inst.Asm.Input,
  13071             clobbers: []const u32,
  13072         },
  13073     ) !Zir.Inst.Ref {
  13074         const astgen = gz.astgen;
  13075         const gpa = astgen.gpa;
  13076 
  13077         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  13078         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  13079         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Asm).@"struct".fields.len +
  13080             args.outputs.len * @typeInfo(Zir.Inst.Asm.Output).@"struct".fields.len +
  13081             args.inputs.len * @typeInfo(Zir.Inst.Asm.Input).@"struct".fields.len +
  13082             args.clobbers.len);
  13083 
  13084         const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Asm{
  13085             .src_node = gz.nodeIndexToRelative(args.node),
  13086             .asm_source = args.asm_source,
  13087             .output_type_bits = args.output_type_bits,
  13088         });
  13089         for (args.outputs) |output| {
  13090             _ = gz.astgen.addExtraAssumeCapacity(output);
  13091         }
  13092         for (args.inputs) |input| {
  13093             _ = gz.astgen.addExtraAssumeCapacity(input);
  13094         }
  13095         gz.astgen.extra.appendSliceAssumeCapacity(args.clobbers);
  13096 
  13097         //  * 0b00000000_000XXXXX - `outputs_len`.
  13098         //  * 0b000000XX_XXX00000 - `inputs_len`.
  13099         //  * 0b0XXXXX00_00000000 - `clobbers_len`.
  13100         //  * 0bX0000000_00000000 - is volatile
  13101         const small: u16 = @as(u16, @intCast(args.outputs.len)) |
  13102             @as(u16, @intCast(args.inputs.len << 5)) |
  13103             @as(u16, @intCast(args.clobbers.len << 10)) |
  13104             (@as(u16, @intFromBool(args.is_volatile)) << 15);
  13105 
  13106         const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
  13107         astgen.instructions.appendAssumeCapacity(.{
  13108             .tag = .extended,
  13109             .data = .{ .extended = .{
  13110                 .opcode = args.tag,
  13111                 .small = small,
  13112                 .operand = payload_index,
  13113             } },
  13114         });
  13115         gz.instructions.appendAssumeCapacity(new_index);
  13116         return new_index.toRef();
  13117     }
  13118 
  13119     /// Note that this returns a `Zir.Inst.Index` not a ref.
  13120     /// Does *not* append the block instruction to the scope.
  13121     /// Leaves the `payload_index` field undefined.
  13122     fn makeBlockInst(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
  13123         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  13124         const gpa = gz.astgen.gpa;
  13125         try gz.astgen.instructions.append(gpa, .{
  13126             .tag = tag,
  13127             .data = .{ .pl_node = .{
  13128                 .src_node = gz.nodeIndexToRelative(node),
  13129                 .payload_index = undefined,
  13130             } },
  13131         });
  13132         return new_index;
  13133     }
  13134 
  13135     /// Note that this returns a `Zir.Inst.Index` not a ref.
  13136     /// Does *not* append the block instruction to the scope.
  13137     /// Leaves the `payload_index` field undefined. Use `setDeclaration` to finalize.
  13138     fn makeDeclaration(gz: *GenZir, node: Ast.Node.Index) !Zir.Inst.Index {
  13139         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  13140         try gz.astgen.instructions.append(gz.astgen.gpa, .{
  13141             .tag = .declaration,
  13142             .data = .{ .declaration = .{
  13143                 .src_node = node,
  13144                 .payload_index = undefined,
  13145             } },
  13146         });
  13147         return new_index;
  13148     }
  13149 
  13150     /// Note that this returns a `Zir.Inst.Index` not a ref.
  13151     /// Leaves the `payload_index` field undefined.
  13152     fn addCondBr(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
  13153         const gpa = gz.astgen.gpa;
  13154         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  13155         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  13156         try gz.astgen.instructions.append(gpa, .{
  13157             .tag = tag,
  13158             .data = .{ .pl_node = .{
  13159                 .src_node = gz.nodeIndexToRelative(node),
  13160                 .payload_index = undefined,
  13161             } },
  13162         });
  13163         gz.instructions.appendAssumeCapacity(new_index);
  13164         return new_index;
  13165     }
  13166 
  13167     fn setStruct(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  13168         src_node: Ast.Node.Index,
  13169         captures_len: u32,
  13170         fields_len: u32,
  13171         decls_len: u32,
  13172         has_backing_int: bool,
  13173         layout: std.builtin.Type.ContainerLayout,
  13174         known_non_opv: bool,
  13175         known_comptime_only: bool,
  13176         is_tuple: bool,
  13177         any_comptime_fields: bool,
  13178         any_default_inits: bool,
  13179         any_aligned_fields: bool,
  13180         fields_hash: std.zig.SrcHash,
  13181     }) !void {
  13182         const astgen = gz.astgen;
  13183         const gpa = astgen.gpa;
  13184 
  13185         // Node 0 is valid for the root `struct_decl` of a file!
  13186         assert(args.src_node != 0 or gz.parent.tag == .top);
  13187 
  13188         const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);
  13189 
  13190         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.StructDecl).@"struct".fields.len + 3);
  13191         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.StructDecl{
  13192             .fields_hash_0 = fields_hash_arr[0],
  13193             .fields_hash_1 = fields_hash_arr[1],
  13194             .fields_hash_2 = fields_hash_arr[2],
  13195             .fields_hash_3 = fields_hash_arr[3],
  13196             .src_line = astgen.source_line,
  13197             .src_node = args.src_node,
  13198         });
  13199 
  13200         if (args.captures_len != 0) {
  13201             astgen.extra.appendAssumeCapacity(args.captures_len);
  13202         }
  13203         if (args.fields_len != 0) {
  13204             astgen.extra.appendAssumeCapacity(args.fields_len);
  13205         }
  13206         if (args.decls_len != 0) {
  13207             astgen.extra.appendAssumeCapacity(args.decls_len);
  13208         }
  13209         astgen.instructions.set(@intFromEnum(inst), .{
  13210             .tag = .extended,
  13211             .data = .{ .extended = .{
  13212                 .opcode = .struct_decl,
  13213                 .small = @bitCast(Zir.Inst.StructDecl.Small{
  13214                     .has_captures_len = args.captures_len != 0,
  13215                     .has_fields_len = args.fields_len != 0,
  13216                     .has_decls_len = args.decls_len != 0,
  13217                     .has_backing_int = args.has_backing_int,
  13218                     .known_non_opv = args.known_non_opv,
  13219                     .known_comptime_only = args.known_comptime_only,
  13220                     .is_tuple = args.is_tuple,
  13221                     .name_strategy = gz.anon_name_strategy,
  13222                     .layout = args.layout,
  13223                     .any_comptime_fields = args.any_comptime_fields,
  13224                     .any_default_inits = args.any_default_inits,
  13225                     .any_aligned_fields = args.any_aligned_fields,
  13226                 }),
  13227                 .operand = payload_index,
  13228             } },
  13229         });
  13230     }
  13231 
  13232     fn setUnion(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  13233         src_node: Ast.Node.Index,
  13234         tag_type: Zir.Inst.Ref,
  13235         captures_len: u32,
  13236         body_len: u32,
  13237         fields_len: u32,
  13238         decls_len: u32,
  13239         layout: std.builtin.Type.ContainerLayout,
  13240         auto_enum_tag: bool,
  13241         any_aligned_fields: bool,
  13242         fields_hash: std.zig.SrcHash,
  13243     }) !void {
  13244         const astgen = gz.astgen;
  13245         const gpa = astgen.gpa;
  13246 
  13247         assert(args.src_node != 0);
  13248 
  13249         const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);
  13250 
  13251         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.UnionDecl).@"struct".fields.len + 5);
  13252         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.UnionDecl{
  13253             .fields_hash_0 = fields_hash_arr[0],
  13254             .fields_hash_1 = fields_hash_arr[1],
  13255             .fields_hash_2 = fields_hash_arr[2],
  13256             .fields_hash_3 = fields_hash_arr[3],
  13257             .src_line = astgen.source_line,
  13258             .src_node = args.src_node,
  13259         });
  13260 
  13261         if (args.tag_type != .none) {
  13262             astgen.extra.appendAssumeCapacity(@intFromEnum(args.tag_type));
  13263         }
  13264         if (args.captures_len != 0) {
  13265             astgen.extra.appendAssumeCapacity(args.captures_len);
  13266         }
  13267         if (args.body_len != 0) {
  13268             astgen.extra.appendAssumeCapacity(args.body_len);
  13269         }
  13270         if (args.fields_len != 0) {
  13271             astgen.extra.appendAssumeCapacity(args.fields_len);
  13272         }
  13273         if (args.decls_len != 0) {
  13274             astgen.extra.appendAssumeCapacity(args.decls_len);
  13275         }
  13276         astgen.instructions.set(@intFromEnum(inst), .{
  13277             .tag = .extended,
  13278             .data = .{ .extended = .{
  13279                 .opcode = .union_decl,
  13280                 .small = @bitCast(Zir.Inst.UnionDecl.Small{
  13281                     .has_tag_type = args.tag_type != .none,
  13282                     .has_captures_len = args.captures_len != 0,
  13283                     .has_body_len = args.body_len != 0,
  13284                     .has_fields_len = args.fields_len != 0,
  13285                     .has_decls_len = args.decls_len != 0,
  13286                     .name_strategy = gz.anon_name_strategy,
  13287                     .layout = args.layout,
  13288                     .auto_enum_tag = args.auto_enum_tag,
  13289                     .any_aligned_fields = args.any_aligned_fields,
  13290                 }),
  13291                 .operand = payload_index,
  13292             } },
  13293         });
  13294     }
  13295 
  13296     fn setEnum(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  13297         src_node: Ast.Node.Index,
  13298         tag_type: Zir.Inst.Ref,
  13299         captures_len: u32,
  13300         body_len: u32,
  13301         fields_len: u32,
  13302         decls_len: u32,
  13303         nonexhaustive: bool,
  13304         fields_hash: std.zig.SrcHash,
  13305     }) !void {
  13306         const astgen = gz.astgen;
  13307         const gpa = astgen.gpa;
  13308 
  13309         assert(args.src_node != 0);
  13310 
  13311         const fields_hash_arr: [4]u32 = @bitCast(args.fields_hash);
  13312 
  13313         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.EnumDecl).@"struct".fields.len + 5);
  13314         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.EnumDecl{
  13315             .fields_hash_0 = fields_hash_arr[0],
  13316             .fields_hash_1 = fields_hash_arr[1],
  13317             .fields_hash_2 = fields_hash_arr[2],
  13318             .fields_hash_3 = fields_hash_arr[3],
  13319             .src_line = astgen.source_line,
  13320             .src_node = args.src_node,
  13321         });
  13322 
  13323         if (args.tag_type != .none) {
  13324             astgen.extra.appendAssumeCapacity(@intFromEnum(args.tag_type));
  13325         }
  13326         if (args.captures_len != 0) {
  13327             astgen.extra.appendAssumeCapacity(args.captures_len);
  13328         }
  13329         if (args.body_len != 0) {
  13330             astgen.extra.appendAssumeCapacity(args.body_len);
  13331         }
  13332         if (args.fields_len != 0) {
  13333             astgen.extra.appendAssumeCapacity(args.fields_len);
  13334         }
  13335         if (args.decls_len != 0) {
  13336             astgen.extra.appendAssumeCapacity(args.decls_len);
  13337         }
  13338         astgen.instructions.set(@intFromEnum(inst), .{
  13339             .tag = .extended,
  13340             .data = .{ .extended = .{
  13341                 .opcode = .enum_decl,
  13342                 .small = @bitCast(Zir.Inst.EnumDecl.Small{
  13343                     .has_tag_type = args.tag_type != .none,
  13344                     .has_captures_len = args.captures_len != 0,
  13345                     .has_body_len = args.body_len != 0,
  13346                     .has_fields_len = args.fields_len != 0,
  13347                     .has_decls_len = args.decls_len != 0,
  13348                     .name_strategy = gz.anon_name_strategy,
  13349                     .nonexhaustive = args.nonexhaustive,
  13350                 }),
  13351                 .operand = payload_index,
  13352             } },
  13353         });
  13354     }
  13355 
  13356     fn setOpaque(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  13357         src_node: Ast.Node.Index,
  13358         captures_len: u32,
  13359         decls_len: u32,
  13360     }) !void {
  13361         const astgen = gz.astgen;
  13362         const gpa = astgen.gpa;
  13363 
  13364         assert(args.src_node != 0);
  13365 
  13366         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.OpaqueDecl).@"struct".fields.len + 2);
  13367         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.OpaqueDecl{
  13368             .src_line = astgen.source_line,
  13369             .src_node = args.src_node,
  13370         });
  13371 
  13372         if (args.captures_len != 0) {
  13373             astgen.extra.appendAssumeCapacity(args.captures_len);
  13374         }
  13375         if (args.decls_len != 0) {
  13376             astgen.extra.appendAssumeCapacity(args.decls_len);
  13377         }
  13378         astgen.instructions.set(@intFromEnum(inst), .{
  13379             .tag = .extended,
  13380             .data = .{ .extended = .{
  13381                 .opcode = .opaque_decl,
  13382                 .small = @bitCast(Zir.Inst.OpaqueDecl.Small{
  13383                     .has_captures_len = args.captures_len != 0,
  13384                     .has_decls_len = args.decls_len != 0,
  13385                     .name_strategy = gz.anon_name_strategy,
  13386                 }),
  13387                 .operand = payload_index,
  13388             } },
  13389         });
  13390     }
  13391 
  13392     fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref {
  13393         return (try gz.addAsIndex(inst)).toRef();
  13394     }
  13395 
  13396     fn addAsIndex(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Index {
  13397         const gpa = gz.astgen.gpa;
  13398         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  13399         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  13400 
  13401         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  13402         gz.astgen.instructions.appendAssumeCapacity(inst);
  13403         gz.instructions.appendAssumeCapacity(new_index);
  13404         return new_index;
  13405     }
  13406 
  13407     fn reserveInstructionIndex(gz: *GenZir) !Zir.Inst.Index {
  13408         const gpa = gz.astgen.gpa;
  13409         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  13410         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  13411 
  13412         const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
  13413         gz.astgen.instructions.len += 1;
  13414         gz.instructions.appendAssumeCapacity(new_index);
  13415         return new_index;
  13416     }
  13417 
  13418     fn addRet(gz: *GenZir, ri: ResultInfo, operand: Zir.Inst.Ref, node: Ast.Node.Index) !void {
  13419         switch (ri.rl) {
  13420             .ptr => |ptr_res| _ = try gz.addUnNode(.ret_load, ptr_res.inst, node),
  13421             .coerced_ty => _ = try gz.addUnNode(.ret_node, operand, node),
  13422             else => unreachable,
  13423         }
  13424     }
  13425 
  13426     fn addDbgVar(gz: *GenZir, tag: Zir.Inst.Tag, name: Zir.NullTerminatedString, inst: Zir.Inst.Ref) !void {
  13427         if (gz.is_comptime) return;
  13428 
  13429         _ = try gz.add(.{ .tag = tag, .data = .{
  13430             .str_op = .{
  13431                 .str = name,
  13432                 .operand = inst,
  13433             },
  13434         } });
  13435     }
  13436 };
  13437 
  13438 /// This can only be for short-lived references; the memory becomes invalidated
  13439 /// when another string is added.
  13440 fn nullTerminatedString(astgen: AstGen, index: Zir.NullTerminatedString) [*:0]const u8 {
  13441     return @ptrCast(astgen.string_bytes.items[@intFromEnum(index)..]);
  13442 }
  13443 
  13444 /// Local variables shadowing detection, including function parameters.
  13445 fn detectLocalShadowing(
  13446     astgen: *AstGen,
  13447     scope: *Scope,
  13448     ident_name: Zir.NullTerminatedString,
  13449     name_token: Ast.TokenIndex,
  13450     token_bytes: []const u8,
  13451     id_cat: Scope.IdCat,
  13452 ) !void {
  13453     const gpa = astgen.gpa;
  13454     if (token_bytes[0] != '@' and isPrimitive(token_bytes)) {
  13455         return astgen.failTokNotes(name_token, "name shadows primitive '{s}'", .{
  13456             token_bytes,
  13457         }, &[_]u32{
  13458             try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{
  13459                 token_bytes,
  13460             }),
  13461         });
  13462     }
  13463 
  13464     var s = scope;
  13465     var outer_scope = false;
  13466     while (true) switch (s.tag) {
  13467         .local_val => {
  13468             const local_val = s.cast(Scope.LocalVal).?;
  13469             if (local_val.name == ident_name) {
  13470                 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
  13471                 const name = try gpa.dupe(u8, name_slice);
  13472                 defer gpa.free(name);
  13473                 if (outer_scope) {
  13474                     return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{
  13475                         @tagName(id_cat), name, @tagName(local_val.id_cat),
  13476                     }, &[_]u32{
  13477                         try astgen.errNoteTok(
  13478                             local_val.token_src,
  13479                             "previous declaration here",
  13480                             .{},
  13481                         ),
  13482                     });
  13483                 }
  13484                 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{
  13485                     @tagName(local_val.id_cat), name,
  13486                 }, &[_]u32{
  13487                     try astgen.errNoteTok(
  13488                         local_val.token_src,
  13489                         "previous declaration here",
  13490                         .{},
  13491                     ),
  13492                 });
  13493             }
  13494             s = local_val.parent;
  13495         },
  13496         .local_ptr => {
  13497             const local_ptr = s.cast(Scope.LocalPtr).?;
  13498             if (local_ptr.name == ident_name) {
  13499                 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
  13500                 const name = try gpa.dupe(u8, name_slice);
  13501                 defer gpa.free(name);
  13502                 if (outer_scope) {
  13503                     return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{
  13504                         @tagName(id_cat), name, @tagName(local_ptr.id_cat),
  13505                     }, &[_]u32{
  13506                         try astgen.errNoteTok(
  13507                             local_ptr.token_src,
  13508                             "previous declaration here",
  13509                             .{},
  13510                         ),
  13511                     });
  13512                 }
  13513                 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{
  13514                     @tagName(local_ptr.id_cat), name,
  13515                 }, &[_]u32{
  13516                     try astgen.errNoteTok(
  13517                         local_ptr.token_src,
  13518                         "previous declaration here",
  13519                         .{},
  13520                     ),
  13521                 });
  13522             }
  13523             s = local_ptr.parent;
  13524         },
  13525         .namespace => {
  13526             outer_scope = true;
  13527             const ns = s.cast(Scope.Namespace).?;
  13528             const decl_node = ns.decls.get(ident_name) orelse {
  13529                 s = ns.parent;
  13530                 continue;
  13531             };
  13532             const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
  13533             const name = try gpa.dupe(u8, name_slice);
  13534             defer gpa.free(name);
  13535             return astgen.failTokNotes(name_token, "{s} shadows declaration of '{s}'", .{
  13536                 @tagName(id_cat), name,
  13537             }, &[_]u32{
  13538                 try astgen.errNoteNode(decl_node, "declared here", .{}),
  13539             });
  13540         },
  13541         .gen_zir => {
  13542             s = s.cast(GenZir).?.parent;
  13543             outer_scope = true;
  13544         },
  13545         .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
  13546         .top => break,
  13547     };
  13548 }
  13549 
  13550 const LineColumn = struct { u32, u32 };
  13551 
  13552 /// Advances the source cursor to the main token of `node` if not in comptime scope.
  13553 /// Usually paired with `emitDbgStmt`.
  13554 fn maybeAdvanceSourceCursorToMainToken(gz: *GenZir, node: Ast.Node.Index) LineColumn {
  13555     if (gz.is_comptime) return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column };
  13556 
  13557     const tree = gz.astgen.tree;
  13558     const token_starts = tree.tokens.items(.start);
  13559     const main_tokens = tree.nodes.items(.main_token);
  13560     const node_start = token_starts[main_tokens[node]];
  13561     gz.astgen.advanceSourceCursor(node_start);
  13562 
  13563     return .{ gz.astgen.source_line - gz.decl_line, gz.astgen.source_column };
  13564 }
  13565 
  13566 /// Advances the source cursor to the beginning of `node`.
  13567 fn advanceSourceCursorToNode(astgen: *AstGen, node: Ast.Node.Index) void {
  13568     const tree = astgen.tree;
  13569     const token_starts = tree.tokens.items(.start);
  13570     const node_start = token_starts[tree.firstToken(node)];
  13571     astgen.advanceSourceCursor(node_start);
  13572 }
  13573 
  13574 /// Advances the source cursor to an absolute byte offset `end` in the file.
  13575 fn advanceSourceCursor(astgen: *AstGen, end: usize) void {
  13576     const source = astgen.tree.source;
  13577     var i = astgen.source_offset;
  13578     var line = astgen.source_line;
  13579     var column = astgen.source_column;
  13580     assert(i <= end);
  13581     while (i < end) : (i += 1) {
  13582         if (source[i] == '\n') {
  13583             line += 1;
  13584             column = 0;
  13585         } else {
  13586             column += 1;
  13587         }
  13588     }
  13589     astgen.source_offset = i;
  13590     astgen.source_line = line;
  13591     astgen.source_column = column;
  13592 }
  13593 
  13594 /// Detects name conflicts for decls and fields, and populates `namespace.decls` with all named declarations.
  13595 /// Returns the number of declarations in the namespace, including unnamed declarations (e.g. `comptime` decls).
  13596 fn scanContainer(
  13597     astgen: *AstGen,
  13598     namespace: *Scope.Namespace,
  13599     members: []const Ast.Node.Index,
  13600     container_kind: enum { @"struct", @"union", @"enum", @"opaque" },
  13601 ) !u32 {
  13602     const gpa = astgen.gpa;
  13603     const tree = astgen.tree;
  13604     const node_tags = tree.nodes.items(.tag);
  13605     const main_tokens = tree.nodes.items(.main_token);
  13606     const token_tags = tree.tokens.items(.tag);
  13607 
  13608     // This type forms a linked list of source tokens declaring the same name.
  13609     const NameEntry = struct {
  13610         tok: Ast.TokenIndex,
  13611         /// Using a linked list here simplifies memory management, and is acceptable since
  13612         ///ewntries are only allocated in error situations. The entries are allocated into the
  13613         /// AstGen arena.
  13614         next: ?*@This(),
  13615     };
  13616 
  13617     // The maps below are allocated into this SFBA to avoid using the GPA for small namespaces.
  13618     var sfba_state = std.heap.stackFallback(512, astgen.gpa);
  13619     const sfba = sfba_state.get();
  13620 
  13621     var names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .empty;
  13622     var test_names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .empty;
  13623     var decltest_names: std.AutoArrayHashMapUnmanaged(Zir.NullTerminatedString, NameEntry) = .empty;
  13624     defer {
  13625         names.deinit(sfba);
  13626         test_names.deinit(sfba);
  13627         decltest_names.deinit(sfba);
  13628     }
  13629 
  13630     var any_duplicates = false;
  13631     var decl_count: u32 = 0;
  13632     for (members) |member_node| {
  13633         const Kind = enum { decl, field };
  13634         const kind: Kind, const name_token = switch (node_tags[member_node]) {
  13635             .container_field_init,
  13636             .container_field_align,
  13637             .container_field,
  13638             => blk: {
  13639                 var full = tree.fullContainerField(member_node).?;
  13640                 switch (container_kind) {
  13641                     .@"struct", .@"opaque" => {},
  13642                     .@"union", .@"enum" => full.convertToNonTupleLike(astgen.tree.nodes),
  13643                 }
  13644                 if (full.ast.tuple_like) continue;
  13645                 break :blk .{ .field, full.ast.main_token };
  13646             },
  13647 
  13648             .global_var_decl,
  13649             .local_var_decl,
  13650             .simple_var_decl,
  13651             .aligned_var_decl,
  13652             => blk: {
  13653                 decl_count += 1;
  13654                 break :blk .{ .decl, main_tokens[member_node] + 1 };
  13655             },
  13656 
  13657             .fn_proto_simple,
  13658             .fn_proto_multi,
  13659             .fn_proto_one,
  13660             .fn_proto,
  13661             .fn_decl,
  13662             => blk: {
  13663                 decl_count += 1;
  13664                 const ident = main_tokens[member_node] + 1;
  13665                 if (token_tags[ident] != .identifier) {
  13666                     try astgen.appendErrorNode(member_node, "missing function name", .{});
  13667                     continue;
  13668                 }
  13669                 break :blk .{ .decl, ident };
  13670             },
  13671 
  13672             .@"comptime", .@"usingnamespace" => {
  13673                 decl_count += 1;
  13674                 continue;
  13675             },
  13676 
  13677             .test_decl => {
  13678                 decl_count += 1;
  13679                 // We don't want shadowing detection here, and test names work a bit differently, so
  13680                 // we must do the redeclaration detection ourselves.
  13681                 const test_name_token = main_tokens[member_node] + 1;
  13682                 const new_ent: NameEntry = .{
  13683                     .tok = test_name_token,
  13684                     .next = null,
  13685                 };
  13686                 switch (token_tags[test_name_token]) {
  13687                     else => {}, // unnamed test
  13688                     .string_literal => {
  13689                         const name = try astgen.strLitAsString(test_name_token);
  13690                         const gop = try test_names.getOrPut(sfba, name.index);
  13691                         if (gop.found_existing) {
  13692                             var e = gop.value_ptr;
  13693                             while (e.next) |n| e = n;
  13694                             e.next = try astgen.arena.create(NameEntry);
  13695                             e.next.?.* = new_ent;
  13696                             any_duplicates = true;
  13697                         } else {
  13698                             gop.value_ptr.* = new_ent;
  13699                         }
  13700                     },
  13701                     .identifier => {
  13702                         const name = try astgen.identAsString(test_name_token);
  13703                         const gop = try decltest_names.getOrPut(sfba, name);
  13704                         if (gop.found_existing) {
  13705                             var e = gop.value_ptr;
  13706                             while (e.next) |n| e = n;
  13707                             e.next = try astgen.arena.create(NameEntry);
  13708                             e.next.?.* = new_ent;
  13709                             any_duplicates = true;
  13710                         } else {
  13711                             gop.value_ptr.* = new_ent;
  13712                         }
  13713                     },
  13714                 }
  13715                 continue;
  13716             },
  13717 
  13718             else => unreachable,
  13719         };
  13720 
  13721         const name_str_index = try astgen.identAsString(name_token);
  13722 
  13723         if (kind == .decl) {
  13724             // Put the name straight into `decls`, even if there are compile errors.
  13725             // This avoids incorrect "undeclared identifier" errors later on.
  13726             try namespace.decls.put(gpa, name_str_index, member_node);
  13727         }
  13728 
  13729         {
  13730             const gop = try names.getOrPut(sfba, name_str_index);
  13731             const new_ent: NameEntry = .{
  13732                 .tok = name_token,
  13733                 .next = null,
  13734             };
  13735             if (gop.found_existing) {
  13736                 var e = gop.value_ptr;
  13737                 while (e.next) |n| e = n;
  13738                 e.next = try astgen.arena.create(NameEntry);
  13739                 e.next.?.* = new_ent;
  13740                 any_duplicates = true;
  13741                 continue;
  13742             } else {
  13743                 gop.value_ptr.* = new_ent;
  13744             }
  13745         }
  13746 
  13747         // For fields, we only needed the duplicate check! Decls have some more checks to do, though.
  13748         switch (kind) {
  13749             .decl => {},
  13750             .field => continue,
  13751         }
  13752 
  13753         const token_bytes = astgen.tree.tokenSlice(name_token);
  13754         if (token_bytes[0] != '@' and isPrimitive(token_bytes)) {
  13755             try astgen.appendErrorTokNotes(name_token, "name shadows primitive '{s}'", .{
  13756                 token_bytes,
  13757             }, &.{
  13758                 try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{
  13759                     token_bytes,
  13760                 }),
  13761             });
  13762             continue;
  13763         }
  13764 
  13765         var s = namespace.parent;
  13766         while (true) switch (s.tag) {
  13767             .local_val => {
  13768                 const local_val = s.cast(Scope.LocalVal).?;
  13769                 if (local_val.name == name_str_index) {
  13770                     try astgen.appendErrorTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{
  13771                         token_bytes, @tagName(local_val.id_cat),
  13772                     }, &.{
  13773                         try astgen.errNoteTok(
  13774                             local_val.token_src,
  13775                             "previous declaration here",
  13776                             .{},
  13777                         ),
  13778                     });
  13779                     break;
  13780                 }
  13781                 s = local_val.parent;
  13782             },
  13783             .local_ptr => {
  13784                 const local_ptr = s.cast(Scope.LocalPtr).?;
  13785                 if (local_ptr.name == name_str_index) {
  13786                     try astgen.appendErrorTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{
  13787                         token_bytes, @tagName(local_ptr.id_cat),
  13788                     }, &.{
  13789                         try astgen.errNoteTok(
  13790                             local_ptr.token_src,
  13791                             "previous declaration here",
  13792                             .{},
  13793                         ),
  13794                     });
  13795                     break;
  13796                 }
  13797                 s = local_ptr.parent;
  13798             },
  13799             .namespace => s = s.cast(Scope.Namespace).?.parent,
  13800             .gen_zir => s = s.cast(GenZir).?.parent,
  13801             .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
  13802             .top => break,
  13803         };
  13804     }
  13805 
  13806     if (!any_duplicates) return decl_count;
  13807 
  13808     for (names.keys(), names.values()) |name, first| {
  13809         if (first.next == null) continue;
  13810         var notes: std.ArrayListUnmanaged(u32) = .empty;
  13811         var prev: NameEntry = first;
  13812         while (prev.next) |cur| : (prev = cur.*) {
  13813             try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate name here", .{}));
  13814         }
  13815         try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
  13816         const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
  13817         try astgen.appendErrorTokNotes(first.tok, "duplicate {s} member name '{s}'", .{ @tagName(container_kind), name_duped }, notes.items);
  13818     }
  13819 
  13820     for (test_names.keys(), test_names.values()) |name, first| {
  13821         if (first.next == null) continue;
  13822         var notes: std.ArrayListUnmanaged(u32) = .empty;
  13823         var prev: NameEntry = first;
  13824         while (prev.next) |cur| : (prev = cur.*) {
  13825             try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate test here", .{}));
  13826         }
  13827         try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
  13828         const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
  13829         try astgen.appendErrorTokNotes(first.tok, "duplicate test name '{s}'", .{name_duped}, notes.items);
  13830     }
  13831 
  13832     for (decltest_names.keys(), decltest_names.values()) |name, first| {
  13833         if (first.next == null) continue;
  13834         var notes: std.ArrayListUnmanaged(u32) = .empty;
  13835         var prev: NameEntry = first;
  13836         while (prev.next) |cur| : (prev = cur.*) {
  13837             try notes.append(astgen.arena, try astgen.errNoteTok(cur.tok, "duplicate decltest here", .{}));
  13838         }
  13839         try notes.append(astgen.arena, try astgen.errNoteNode(namespace.node, "{s} declared here", .{@tagName(container_kind)}));
  13840         const name_duped = try astgen.arena.dupe(u8, mem.span(astgen.nullTerminatedString(name)));
  13841         try astgen.appendErrorTokNotes(first.tok, "duplicate decltest '{s}'", .{name_duped}, notes.items);
  13842     }
  13843 
  13844     return decl_count;
  13845 }
  13846 
  13847 fn isInferred(astgen: *AstGen, ref: Zir.Inst.Ref) bool {
  13848     const inst = ref.toIndex() orelse return false;
  13849     const zir_tags = astgen.instructions.items(.tag);
  13850     return switch (zir_tags[@intFromEnum(inst)]) {
  13851         .alloc_inferred,
  13852         .alloc_inferred_mut,
  13853         .alloc_inferred_comptime,
  13854         .alloc_inferred_comptime_mut,
  13855         => true,
  13856 
  13857         .extended => {
  13858             const zir_data = astgen.instructions.items(.data);
  13859             if (zir_data[@intFromEnum(inst)].extended.opcode != .alloc) return false;
  13860             const small: Zir.Inst.AllocExtended.Small = @bitCast(zir_data[@intFromEnum(inst)].extended.small);
  13861             return !small.has_type;
  13862         },
  13863 
  13864         else => false,
  13865     };
  13866 }
  13867 
  13868 /// Assumes capacity for body has already been added. Needed capacity taking into
  13869 /// account fixups can be found with `countBodyLenAfterFixups`.
  13870 fn appendBodyWithFixups(astgen: *AstGen, body: []const Zir.Inst.Index) void {
  13871     return appendBodyWithFixupsArrayList(astgen, &astgen.extra, body);
  13872 }
  13873 
  13874 fn appendBodyWithFixupsArrayList(
  13875     astgen: *AstGen,
  13876     list: *std.ArrayListUnmanaged(u32),
  13877     body: []const Zir.Inst.Index,
  13878 ) void {
  13879     for (body) |body_inst| {
  13880         appendPossiblyRefdBodyInst(astgen, list, body_inst);
  13881     }
  13882 }
  13883 
  13884 fn appendPossiblyRefdBodyInst(
  13885     astgen: *AstGen,
  13886     list: *std.ArrayListUnmanaged(u32),
  13887     body_inst: Zir.Inst.Index,
  13888 ) void {
  13889     list.appendAssumeCapacity(@intFromEnum(body_inst));
  13890     const kv = astgen.ref_table.fetchRemove(body_inst) orelse return;
  13891     const ref_inst = kv.value;
  13892     return appendPossiblyRefdBodyInst(astgen, list, ref_inst);
  13893 }
  13894 
  13895 fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 {
  13896     var count = body.len;
  13897     for (body) |body_inst| {
  13898         var check_inst = body_inst;
  13899         while (astgen.ref_table.get(check_inst)) |ref_inst| {
  13900             count += 1;
  13901             check_inst = ref_inst;
  13902         }
  13903     }
  13904     return @intCast(count);
  13905 }
  13906 
  13907 fn emitDbgStmt(gz: *GenZir, lc: LineColumn) !void {
  13908     if (gz.is_comptime) return;
  13909     if (gz.instructions.items.len > gz.instructions_top) {
  13910         const astgen = gz.astgen;
  13911         const last = gz.instructions.items[gz.instructions.items.len - 1];
  13912         if (astgen.instructions.items(.tag)[@intFromEnum(last)] == .dbg_stmt) {
  13913             astgen.instructions.items(.data)[@intFromEnum(last)].dbg_stmt = .{
  13914                 .line = lc[0],
  13915                 .column = lc[1],
  13916             };
  13917             return;
  13918         }
  13919     }
  13920 
  13921     _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
  13922         .dbg_stmt = .{
  13923             .line = lc[0],
  13924             .column = lc[1],
  13925         },
  13926     } });
  13927 }
  13928 
  13929 /// In some cases, Sema expects us to generate a `dbg_stmt` at the instruction
  13930 /// *index* directly preceding the next instruction (e.g. if a call is %10, it
  13931 /// expects a dbg_stmt at %9). TODO: this logic may allow redundant dbg_stmt
  13932 /// instructions; fix up Sema so we don't need it!
  13933 fn emitDbgStmtForceCurrentIndex(gz: *GenZir, lc: LineColumn) !void {
  13934     const astgen = gz.astgen;
  13935     if (gz.instructions.items.len > gz.instructions_top and
  13936         @intFromEnum(gz.instructions.items[gz.instructions.items.len - 1]) == astgen.instructions.len - 1)
  13937     {
  13938         const last = astgen.instructions.len - 1;
  13939         if (astgen.instructions.items(.tag)[last] == .dbg_stmt) {
  13940             astgen.instructions.items(.data)[last].dbg_stmt = .{
  13941                 .line = lc[0],
  13942                 .column = lc[1],
  13943             };
  13944             return;
  13945         }
  13946     }
  13947 
  13948     _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
  13949         .dbg_stmt = .{
  13950             .line = lc[0],
  13951             .column = lc[1],
  13952         },
  13953     } });
  13954 }
  13955 
  13956 fn lowerAstErrors(astgen: *AstGen) !void {
  13957     const tree = astgen.tree;
  13958     assert(tree.errors.len > 0);
  13959 
  13960     const gpa = astgen.gpa;
  13961     const parse_err = tree.errors[0];
  13962 
  13963     var msg: std.ArrayListUnmanaged(u8) = .empty;
  13964     defer msg.deinit(gpa);
  13965 
  13966     var notes: std.ArrayListUnmanaged(u32) = .empty;
  13967     defer notes.deinit(gpa);
  13968 
  13969     for (tree.errors[1..]) |note| {
  13970         if (!note.is_note) break;
  13971 
  13972         msg.clearRetainingCapacity();
  13973         try tree.renderError(note, msg.writer(gpa));
  13974         try notes.append(gpa, try astgen.errNoteTok(note.token, "{s}", .{msg.items}));
  13975     }
  13976 
  13977     const extra_offset = tree.errorOffset(parse_err);
  13978     msg.clearRetainingCapacity();
  13979     try tree.renderError(parse_err, msg.writer(gpa));
  13980     try astgen.appendErrorTokNotesOff(parse_err.token, extra_offset, "{s}", .{msg.items}, notes.items);
  13981 }
  13982 
  13983 const DeclarationName = union(enum) {
  13984     named: Ast.TokenIndex,
  13985     named_test: Ast.TokenIndex,
  13986     unnamed_test,
  13987     decltest: Zir.NullTerminatedString,
  13988     @"comptime",
  13989     @"usingnamespace",
  13990 };
  13991 
  13992 /// Sets all extra data for a `declaration` instruction.
  13993 /// Unstacks `value_gz`, `align_gz`, `linksection_gz`, and `addrspace_gz`.
  13994 fn setDeclaration(
  13995     decl_inst: Zir.Inst.Index,
  13996     src_hash: std.zig.SrcHash,
  13997     name: DeclarationName,
  13998     src_line: u32,
  13999     is_pub: bool,
  14000     is_export: bool,
  14001     doc_comment: Zir.NullTerminatedString,
  14002     value_gz: *GenZir,
  14003     /// May be `null` if all these blocks would be empty.
  14004     /// If `null`, then `value_gz` must have nothing stacked on it.
  14005     extra_gzs: ?struct {
  14006         /// Must be stacked on `value_gz`.
  14007         align_gz: *GenZir,
  14008         /// Must be stacked on `align_gz`.
  14009         linksection_gz: *GenZir,
  14010         /// Must be stacked on `linksection_gz`, and have nothing stacked on it.
  14011         addrspace_gz: *GenZir,
  14012     },
  14013 ) !void {
  14014     const astgen = value_gz.astgen;
  14015     const gpa = astgen.gpa;
  14016 
  14017     const empty_body: []Zir.Inst.Index = &.{};
  14018     const value_body, const align_body, const linksection_body, const addrspace_body = if (extra_gzs) |e| .{
  14019         value_gz.instructionsSliceUpto(e.align_gz),
  14020         e.align_gz.instructionsSliceUpto(e.linksection_gz),
  14021         e.linksection_gz.instructionsSliceUpto(e.addrspace_gz),
  14022         e.addrspace_gz.instructionsSlice(),
  14023     } else .{ value_gz.instructionsSlice(), empty_body, empty_body, empty_body };
  14024 
  14025     const value_len = astgen.countBodyLenAfterFixups(value_body);
  14026     const align_len = astgen.countBodyLenAfterFixups(align_body);
  14027     const linksection_len = astgen.countBodyLenAfterFixups(linksection_body);
  14028     const addrspace_len = astgen.countBodyLenAfterFixups(addrspace_body);
  14029 
  14030     const true_doc_comment: Zir.NullTerminatedString = switch (name) {
  14031         .decltest => |test_name| test_name,
  14032         else => doc_comment,
  14033     };
  14034 
  14035     const src_hash_arr: [4]u32 = @bitCast(src_hash);
  14036 
  14037     const extra: Zir.Inst.Declaration = .{
  14038         .src_hash_0 = src_hash_arr[0],
  14039         .src_hash_1 = src_hash_arr[1],
  14040         .src_hash_2 = src_hash_arr[2],
  14041         .src_hash_3 = src_hash_arr[3],
  14042         .name = switch (name) {
  14043             .named => |tok| @enumFromInt(@intFromEnum(try astgen.identAsString(tok))),
  14044             .named_test => |tok| @enumFromInt(@intFromEnum(try astgen.testNameString(tok))),
  14045             .unnamed_test => .unnamed_test,
  14046             .decltest => .decltest,
  14047             .@"comptime" => .@"comptime",
  14048             .@"usingnamespace" => .@"usingnamespace",
  14049         },
  14050         .src_line = src_line,
  14051         .flags = .{
  14052             .value_body_len = @intCast(value_len),
  14053             .is_pub = is_pub,
  14054             .is_export = is_export,
  14055             .has_doc_comment = true_doc_comment != .empty,
  14056             .has_align_linksection_addrspace = align_len != 0 or linksection_len != 0 or addrspace_len != 0,
  14057         },
  14058     };
  14059     astgen.instructions.items(.data)[@intFromEnum(decl_inst)].declaration.payload_index = try astgen.addExtra(extra);
  14060     if (extra.flags.has_doc_comment) {
  14061         try astgen.extra.append(gpa, @intFromEnum(true_doc_comment));
  14062     }
  14063     if (extra.flags.has_align_linksection_addrspace) {
  14064         try astgen.extra.appendSlice(gpa, &.{
  14065             align_len,
  14066             linksection_len,
  14067             addrspace_len,
  14068         });
  14069     }
  14070     try astgen.extra.ensureUnusedCapacity(gpa, value_len + align_len + linksection_len + addrspace_len);
  14071     astgen.appendBodyWithFixups(value_body);
  14072     if (extra.flags.has_align_linksection_addrspace) {
  14073         astgen.appendBodyWithFixups(align_body);
  14074         astgen.appendBodyWithFixups(linksection_body);
  14075         astgen.appendBodyWithFixups(addrspace_body);
  14076     }
  14077 
  14078     if (extra_gzs) |e| {
  14079         e.addrspace_gz.unstack();
  14080         e.linksection_gz.unstack();
  14081         e.align_gz.unstack();
  14082     }
  14083     value_gz.unstack();
  14084 }