zig

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

blob 4e571ffd (494619B) - 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 = @import("Zir.zig");
     16 const refToIndex = Zir.refToIndex;
     17 const indexToRef = Zir.indexToRef;
     18 const trace = @import("tracy.zig").trace;
     19 const BuiltinFn = @import("BuiltinFn.zig");
     20 
     21 gpa: Allocator,
     22 tree: *const Ast,
     23 instructions: std.MultiArrayList(Zir.Inst) = .{},
     24 extra: ArrayListUnmanaged(u32) = .{},
     25 string_bytes: ArrayListUnmanaged(u8) = .{},
     26 /// Tracks the current byte offset within the source file.
     27 /// Used to populate line deltas in the ZIR. AstGen maintains
     28 /// this "cursor" throughout the entire AST lowering process in order
     29 /// to avoid starting over the line/column scan for every declaration, which
     30 /// would be O(N^2).
     31 source_offset: u32 = 0,
     32 /// Tracks the corresponding line of `source_offset`.
     33 /// This value is absolute.
     34 source_line: u32 = 0,
     35 /// Tracks the corresponding column of `source_offset`.
     36 /// This value is absolute.
     37 source_column: u32 = 0,
     38 /// Used for temporary allocations; freed after AstGen is complete.
     39 /// The resulting ZIR code has no references to anything in this arena.
     40 arena: Allocator,
     41 string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
     42 compile_errors: ArrayListUnmanaged(Zir.Inst.CompileErrors.Item) = .{},
     43 /// The topmost block of the current function.
     44 fn_block: ?*GenZir = null,
     45 /// Maps string table indexes to the first `@import` ZIR instruction
     46 /// that uses this string as the operand.
     47 imports: std.AutoArrayHashMapUnmanaged(u32, Ast.TokenIndex) = .{},
     48 /// Used for temporary storage when building payloads.
     49 scratch: std.ArrayListUnmanaged(u32) = .{},
     50 /// Whenever a `ref` instruction is needed, it is created and saved in this
     51 /// table instead of being immediately appended to the current block body.
     52 /// Then, when the instruction is being added to the parent block (typically from
     53 /// setBlockBody), if it has a ref_table entry, then the ref instruction is added
     54 /// there. This makes sure two properties are upheld:
     55 /// 1. All pointers to the same locals return the same address. This is required
     56 ///    to be compliant with the language specification.
     57 /// 2. `ref` instructions will dominate their uses. This is a required property
     58 ///    of ZIR.
     59 /// The key is the ref operand; the value is the ref instruction.
     60 ref_table: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},
     61 
     62 const InnerError = error{ OutOfMemory, AnalysisFail };
     63 
     64 fn addExtra(astgen: *AstGen, extra: anytype) Allocator.Error!u32 {
     65     const fields = std.meta.fields(@TypeOf(extra));
     66     try astgen.extra.ensureUnusedCapacity(astgen.gpa, fields.len);
     67     return addExtraAssumeCapacity(astgen, extra);
     68 }
     69 
     70 fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {
     71     const fields = std.meta.fields(@TypeOf(extra));
     72     const result = @intCast(u32, astgen.extra.items.len);
     73     astgen.extra.items.len += fields.len;
     74     setExtra(astgen, result, extra);
     75     return result;
     76 }
     77 
     78 fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void {
     79     const fields = std.meta.fields(@TypeOf(extra));
     80     var i = index;
     81     inline for (fields) |field| {
     82         astgen.extra.items[i] = switch (field.field_type) {
     83             u32 => @field(extra, field.name),
     84             Zir.Inst.Ref => @enumToInt(@field(extra, field.name)),
     85             i32 => @bitCast(u32, @field(extra, field.name)),
     86             Zir.Inst.Call.Flags => @bitCast(u32, @field(extra, field.name)),
     87             Zir.Inst.BuiltinCall.Flags => @bitCast(u32, @field(extra, field.name)),
     88             Zir.Inst.SwitchBlock.Bits => @bitCast(u32, @field(extra, field.name)),
     89             Zir.Inst.FuncFancy.Bits => @bitCast(u32, @field(extra, field.name)),
     90             else => @compileError("bad field type"),
     91         };
     92         i += 1;
     93     }
     94 }
     95 
     96 fn reserveExtra(astgen: *AstGen, size: usize) Allocator.Error!u32 {
     97     const result = @intCast(u32, astgen.extra.items.len);
     98     try astgen.extra.resize(astgen.gpa, result + size);
     99     return result;
    100 }
    101 
    102 fn appendRefs(astgen: *AstGen, refs: []const Zir.Inst.Ref) !void {
    103     const coerced = @ptrCast([]const u32, refs);
    104     return astgen.extra.appendSlice(astgen.gpa, coerced);
    105 }
    106 
    107 fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const Zir.Inst.Ref) void {
    108     const coerced = @ptrCast([]const u32, refs);
    109     astgen.extra.appendSliceAssumeCapacity(coerced);
    110 }
    111 
    112 pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
    113     var arena = std.heap.ArenaAllocator.init(gpa);
    114     defer arena.deinit();
    115 
    116     var astgen: AstGen = .{
    117         .gpa = gpa,
    118         .arena = arena.allocator(),
    119         .tree = &tree,
    120     };
    121     defer astgen.deinit(gpa);
    122 
    123     // String table indexes 0, 1, 2 are reserved for special meaning.
    124     try astgen.string_bytes.appendSlice(gpa, &[_]u8{ 0, 0, 0 });
    125 
    126     // We expect at least as many ZIR instructions and extra data items
    127     // as AST nodes.
    128     try astgen.instructions.ensureTotalCapacity(gpa, tree.nodes.len);
    129 
    130     // First few indexes of extra are reserved and set at the end.
    131     const reserved_count = @typeInfo(Zir.ExtraIndex).Enum.fields.len;
    132     try astgen.extra.ensureTotalCapacity(gpa, tree.nodes.len + reserved_count);
    133     astgen.extra.items.len += reserved_count;
    134 
    135     var top_scope: Scope.Top = .{};
    136 
    137     var gz_instructions: std.ArrayListUnmanaged(Zir.Inst.Index) = .{};
    138     var gen_scope: GenZir = .{
    139         .force_comptime = true,
    140         .parent = &top_scope.base,
    141         .anon_name_strategy = .parent,
    142         .decl_node_index = 0,
    143         .decl_line = 0,
    144         .astgen = &astgen,
    145         .instructions = &gz_instructions,
    146         .instructions_top = 0,
    147     };
    148     defer gz_instructions.deinit(gpa);
    149 
    150     if (AstGen.structDeclInner(
    151         &gen_scope,
    152         &gen_scope.base,
    153         0,
    154         tree.containerDeclRoot(),
    155         .Auto,
    156         0,
    157     )) |struct_decl_ref| {
    158         assert(refToIndex(struct_decl_ref).? == 0);
    159     } else |err| switch (err) {
    160         error.OutOfMemory => return error.OutOfMemory,
    161         error.AnalysisFail => {}, // Handled via compile_errors below.
    162     }
    163 
    164     const err_index = @enumToInt(Zir.ExtraIndex.compile_errors);
    165     if (astgen.compile_errors.items.len == 0) {
    166         astgen.extra.items[err_index] = 0;
    167     } else {
    168         try astgen.extra.ensureUnusedCapacity(gpa, 1 + astgen.compile_errors.items.len *
    169             @typeInfo(Zir.Inst.CompileErrors.Item).Struct.fields.len);
    170 
    171         astgen.extra.items[err_index] = astgen.addExtraAssumeCapacity(Zir.Inst.CompileErrors{
    172             .items_len = @intCast(u32, astgen.compile_errors.items.len),
    173         });
    174 
    175         for (astgen.compile_errors.items) |item| {
    176             _ = astgen.addExtraAssumeCapacity(item);
    177         }
    178     }
    179 
    180     const imports_index = @enumToInt(Zir.ExtraIndex.imports);
    181     if (astgen.imports.count() == 0) {
    182         astgen.extra.items[imports_index] = 0;
    183     } else {
    184         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Imports).Struct.fields.len +
    185             astgen.imports.count() * @typeInfo(Zir.Inst.Imports.Item).Struct.fields.len);
    186 
    187         astgen.extra.items[imports_index] = astgen.addExtraAssumeCapacity(Zir.Inst.Imports{
    188             .imports_len = @intCast(u32, astgen.imports.count()),
    189         });
    190 
    191         var it = astgen.imports.iterator();
    192         while (it.next()) |entry| {
    193             _ = astgen.addExtraAssumeCapacity(Zir.Inst.Imports.Item{
    194                 .name = entry.key_ptr.*,
    195                 .token = entry.value_ptr.*,
    196             });
    197         }
    198     }
    199 
    200     return Zir{
    201         .instructions = astgen.instructions.toOwnedSlice(),
    202         .string_bytes = try astgen.string_bytes.toOwnedSlice(gpa),
    203         .extra = try astgen.extra.toOwnedSlice(gpa),
    204     };
    205 }
    206 
    207 pub fn deinit(astgen: *AstGen, gpa: Allocator) void {
    208     astgen.instructions.deinit(gpa);
    209     astgen.extra.deinit(gpa);
    210     astgen.string_table.deinit(gpa);
    211     astgen.string_bytes.deinit(gpa);
    212     astgen.compile_errors.deinit(gpa);
    213     astgen.imports.deinit(gpa);
    214     astgen.scratch.deinit(gpa);
    215     astgen.ref_table.deinit(gpa);
    216 }
    217 
    218 pub const ResultInfo = struct {
    219     /// The semantics requested for the result location
    220     rl: Loc,
    221 
    222     /// The "operator" consuming the result location
    223     ctx: Context = .none,
    224 
    225     /// Turns a `coerced_ty` back into a `ty`. Should be called at branch points
    226     /// such as if and switch expressions.
    227     fn br(ri: ResultInfo) ResultInfo {
    228         return switch (ri.rl) {
    229             .coerced_ty => |ty| .{
    230                 .rl = .{ .ty = ty },
    231                 .ctx = ri.ctx,
    232             },
    233             else => ri,
    234         };
    235     }
    236 
    237     fn zirTag(ri: ResultInfo) Zir.Inst.Tag {
    238         switch (ri.rl) {
    239             .ty => return switch (ri.ctx) {
    240                 .shift_op => .as_shift_operand,
    241                 else => .as_node,
    242             },
    243             else => unreachable,
    244         }
    245     }
    246 
    247     pub const Loc = union(enum) {
    248         /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
    249         /// expression should be generated. The result instruction from the expression must
    250         /// be ignored.
    251         discard,
    252         /// The expression has an inferred type, and it will be evaluated as an rvalue.
    253         none,
    254         /// The expression must generate a pointer rather than a value. For example, the left hand side
    255         /// of an assignment uses this kind of result location.
    256         ref,
    257         /// The expression will be coerced into this type, but it will be evaluated as an rvalue.
    258         ty: Zir.Inst.Ref,
    259         /// Same as `ty` but it is guaranteed that Sema will additionally perform the coercion,
    260         /// so no `as` instruction needs to be emitted.
    261         coerced_ty: Zir.Inst.Ref,
    262         /// The expression must store its result into this typed pointer. The result instruction
    263         /// from the expression must be ignored.
    264         ptr: PtrResultLoc,
    265         /// The expression must store its result into this allocation, which has an inferred type.
    266         /// The result instruction from the expression must be ignored.
    267         /// Always an instruction with tag `alloc_inferred`.
    268         inferred_ptr: Zir.Inst.Ref,
    269         /// There is a pointer for the expression to store its result into, however, its type
    270         /// is inferred based on peer type resolution for a `Zir.Inst.Block`.
    271         /// The result instruction from the expression must be ignored.
    272         block_ptr: *GenZir,
    273 
    274         const PtrResultLoc = struct {
    275             inst: Zir.Inst.Ref,
    276             src_node: ?Ast.Node.Index = null,
    277         };
    278 
    279         pub const Strategy = struct {
    280             elide_store_to_block_ptr_instructions: bool,
    281             tag: Tag,
    282 
    283             pub const Tag = enum {
    284                 /// Both branches will use break_void; result location is used to communicate the
    285                 /// result instruction.
    286                 break_void,
    287                 /// Use break statements to pass the block result value, and call rvalue() at
    288                 /// the end depending on rl. Also elide the store_to_block_ptr instructions
    289                 /// depending on rl.
    290                 break_operand,
    291             };
    292         };
    293 
    294         fn strategy(rl: Loc, block_scope: *GenZir) Strategy {
    295             switch (rl) {
    296                 // In this branch there will not be any store_to_block_ptr instructions.
    297                 .none, .ty, .coerced_ty, .ref => return .{
    298                     .tag = .break_operand,
    299                     .elide_store_to_block_ptr_instructions = false,
    300                 },
    301                 .discard => return .{
    302                     .tag = .break_void,
    303                     .elide_store_to_block_ptr_instructions = false,
    304                 },
    305                 // The pointer got passed through to the sub-expressions, so we will use
    306                 // break_void here.
    307                 // In this branch there will not be any store_to_block_ptr instructions.
    308                 .ptr => return .{
    309                     .tag = .break_void,
    310                     .elide_store_to_block_ptr_instructions = false,
    311                 },
    312                 .inferred_ptr, .block_ptr => {
    313                     if (block_scope.rvalue_rl_count == block_scope.break_count) {
    314                         // Neither prong of the if consumed the result location, so we can
    315                         // use break instructions to create an rvalue.
    316                         return .{
    317                             .tag = .break_operand,
    318                             .elide_store_to_block_ptr_instructions = true,
    319                         };
    320                     } else {
    321                         // Allow the store_to_block_ptr instructions to remain so that
    322                         // semantic analysis can turn them into bitcasts.
    323                         return .{
    324                             .tag = .break_void,
    325                             .elide_store_to_block_ptr_instructions = false,
    326                         };
    327                     }
    328                 },
    329             }
    330         }
    331     };
    332 
    333     pub const Context = enum {
    334         /// The expression is the operand to a return expression.
    335         @"return",
    336         /// The expression is the input to an error-handling operator (if-else, try, or catch).
    337         error_handling_expr,
    338         /// The expression is the right-hand side of a shift operation.
    339         shift_op,
    340         /// The expression is an argument in a function call.
    341         fn_arg,
    342         /// The expression is the right-hand side of an initializer for a `const` variable
    343         const_init,
    344         /// The expression is the right-hand side of an assignment expression.
    345         assignment,
    346         /// No specific operator in particular.
    347         none,
    348     };
    349 };
    350 
    351 pub const align_ri: ResultInfo = .{ .rl = .{ .ty = .u29_type } };
    352 pub const coerced_align_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .u29_type } };
    353 pub const bool_ri: ResultInfo = .{ .rl = .{ .ty = .bool_type } };
    354 pub const type_ri: ResultInfo = .{ .rl = .{ .ty = .type_type } };
    355 pub const coerced_type_ri: ResultInfo = .{ .rl = .{ .coerced_ty = .type_type } };
    356 
    357 fn typeExpr(gz: *GenZir, scope: *Scope, type_node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
    358     const prev_force_comptime = gz.force_comptime;
    359     gz.force_comptime = true;
    360     defer gz.force_comptime = prev_force_comptime;
    361 
    362     return expr(gz, scope, coerced_type_ri, type_node);
    363 }
    364 
    365 fn reachableTypeExpr(
    366     gz: *GenZir,
    367     scope: *Scope,
    368     type_node: Ast.Node.Index,
    369     reachable_node: Ast.Node.Index,
    370 ) InnerError!Zir.Inst.Ref {
    371     const prev_force_comptime = gz.force_comptime;
    372     gz.force_comptime = true;
    373     defer gz.force_comptime = prev_force_comptime;
    374 
    375     return reachableExpr(gz, scope, coerced_type_ri, type_node, reachable_node);
    376 }
    377 
    378 /// Same as `expr` but fails with a compile error if the result type is `noreturn`.
    379 fn reachableExpr(
    380     gz: *GenZir,
    381     scope: *Scope,
    382     ri: ResultInfo,
    383     node: Ast.Node.Index,
    384     reachable_node: Ast.Node.Index,
    385 ) InnerError!Zir.Inst.Ref {
    386     return reachableExprComptime(gz, scope, ri, node, reachable_node, false);
    387 }
    388 
    389 fn reachableExprComptime(
    390     gz: *GenZir,
    391     scope: *Scope,
    392     ri: ResultInfo,
    393     node: Ast.Node.Index,
    394     reachable_node: Ast.Node.Index,
    395     force_comptime: bool,
    396 ) InnerError!Zir.Inst.Ref {
    397     const prev_force_comptime = gz.force_comptime;
    398     gz.force_comptime = prev_force_comptime or force_comptime;
    399     defer gz.force_comptime = prev_force_comptime;
    400 
    401     const result_inst = try expr(gz, scope, ri, node);
    402     if (gz.refIsNoReturn(result_inst)) {
    403         try gz.astgen.appendErrorNodeNotes(reachable_node, "unreachable code", .{}, &[_]u32{
    404             try gz.astgen.errNoteNode(node, "control flow is diverted here", .{}),
    405         });
    406     }
    407     return result_inst;
    408 }
    409 
    410 fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
    411     const astgen = gz.astgen;
    412     const tree = astgen.tree;
    413     const node_tags = tree.nodes.items(.tag);
    414     const main_tokens = tree.nodes.items(.main_token);
    415     switch (node_tags[node]) {
    416         .root => unreachable,
    417         .@"usingnamespace" => unreachable,
    418         .test_decl => unreachable,
    419         .global_var_decl => unreachable,
    420         .local_var_decl => unreachable,
    421         .simple_var_decl => unreachable,
    422         .aligned_var_decl => unreachable,
    423         .switch_case => unreachable,
    424         .switch_case_inline => unreachable,
    425         .switch_case_one => unreachable,
    426         .switch_case_inline_one => unreachable,
    427         .container_field_init => unreachable,
    428         .container_field_align => unreachable,
    429         .container_field => unreachable,
    430         .asm_output => unreachable,
    431         .asm_input => unreachable,
    432 
    433         .assign,
    434         .assign_bit_and,
    435         .assign_bit_or,
    436         .assign_shl,
    437         .assign_shl_sat,
    438         .assign_shr,
    439         .assign_bit_xor,
    440         .assign_div,
    441         .assign_sub,
    442         .assign_sub_wrap,
    443         .assign_sub_sat,
    444         .assign_mod,
    445         .assign_add,
    446         .assign_add_wrap,
    447         .assign_add_sat,
    448         .assign_mul,
    449         .assign_mul_wrap,
    450         .assign_mul_sat,
    451         .add,
    452         .add_wrap,
    453         .add_sat,
    454         .sub,
    455         .sub_wrap,
    456         .sub_sat,
    457         .mul,
    458         .mul_wrap,
    459         .mul_sat,
    460         .div,
    461         .mod,
    462         .bit_and,
    463         .bit_or,
    464         .shl,
    465         .shl_sat,
    466         .shr,
    467         .bit_xor,
    468         .bang_equal,
    469         .equal_equal,
    470         .greater_than,
    471         .greater_or_equal,
    472         .less_than,
    473         .less_or_equal,
    474         .array_cat,
    475         .array_mult,
    476         .bool_and,
    477         .bool_or,
    478         .@"asm",
    479         .asm_simple,
    480         .string_literal,
    481         .number_literal,
    482         .call,
    483         .call_comma,
    484         .async_call,
    485         .async_call_comma,
    486         .call_one,
    487         .call_one_comma,
    488         .async_call_one,
    489         .async_call_one_comma,
    490         .unreachable_literal,
    491         .@"return",
    492         .@"if",
    493         .if_simple,
    494         .@"while",
    495         .while_simple,
    496         .while_cont,
    497         .bool_not,
    498         .address_of,
    499         .optional_type,
    500         .block,
    501         .block_semicolon,
    502         .block_two,
    503         .block_two_semicolon,
    504         .@"break",
    505         .ptr_type_aligned,
    506         .ptr_type_sentinel,
    507         .ptr_type,
    508         .ptr_type_bit_range,
    509         .array_type,
    510         .array_type_sentinel,
    511         .enum_literal,
    512         .multiline_string_literal,
    513         .char_literal,
    514         .@"defer",
    515         .@"errdefer",
    516         .@"catch",
    517         .error_union,
    518         .merge_error_sets,
    519         .switch_range,
    520         .@"await",
    521         .bit_not,
    522         .negation,
    523         .negation_wrap,
    524         .@"resume",
    525         .@"try",
    526         .slice,
    527         .slice_open,
    528         .slice_sentinel,
    529         .array_init_one,
    530         .array_init_one_comma,
    531         .array_init_dot_two,
    532         .array_init_dot_two_comma,
    533         .array_init_dot,
    534         .array_init_dot_comma,
    535         .array_init,
    536         .array_init_comma,
    537         .struct_init_one,
    538         .struct_init_one_comma,
    539         .struct_init_dot_two,
    540         .struct_init_dot_two_comma,
    541         .struct_init_dot,
    542         .struct_init_dot_comma,
    543         .struct_init,
    544         .struct_init_comma,
    545         .@"switch",
    546         .switch_comma,
    547         .@"for",
    548         .for_simple,
    549         .@"suspend",
    550         .@"continue",
    551         .fn_proto_simple,
    552         .fn_proto_multi,
    553         .fn_proto_one,
    554         .fn_proto,
    555         .fn_decl,
    556         .anyframe_type,
    557         .anyframe_literal,
    558         .error_set_decl,
    559         .container_decl,
    560         .container_decl_trailing,
    561         .container_decl_two,
    562         .container_decl_two_trailing,
    563         .container_decl_arg,
    564         .container_decl_arg_trailing,
    565         .tagged_union,
    566         .tagged_union_trailing,
    567         .tagged_union_two,
    568         .tagged_union_two_trailing,
    569         .tagged_union_enum_tag,
    570         .tagged_union_enum_tag_trailing,
    571         .@"comptime",
    572         .@"nosuspend",
    573         .error_value,
    574         => return astgen.failNode(node, "invalid left-hand side to assignment", .{}),
    575 
    576         .builtin_call,
    577         .builtin_call_comma,
    578         .builtin_call_two,
    579         .builtin_call_two_comma,
    580         => {
    581             const builtin_token = main_tokens[node];
    582             const builtin_name = tree.tokenSlice(builtin_token);
    583             // If the builtin is an invalid name, we don't cause an error here; instead
    584             // let it pass, and the error will be "invalid builtin function" later.
    585             if (BuiltinFn.list.get(builtin_name)) |info| {
    586                 if (!info.allows_lvalue) {
    587                     return astgen.failNode(node, "invalid left-hand side to assignment", .{});
    588                 }
    589             }
    590         },
    591 
    592         // These can be assigned to.
    593         .unwrap_optional,
    594         .deref,
    595         .field_access,
    596         .array_access,
    597         .identifier,
    598         .grouped_expression,
    599         .@"orelse",
    600         => {},
    601     }
    602     return expr(gz, scope, .{ .rl = .ref }, node);
    603 }
    604 
    605 /// Turn Zig AST into untyped ZIR instructions.
    606 /// When `rl` is discard, ptr, inferred_ptr, or inferred_ptr, the
    607 /// result instruction can be used to inspect whether it is isNoReturn() but that is it,
    608 /// it must otherwise not be used.
    609 fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
    610     const astgen = gz.astgen;
    611     const tree = astgen.tree;
    612     const main_tokens = tree.nodes.items(.main_token);
    613     const token_tags = tree.tokens.items(.tag);
    614     const node_datas = tree.nodes.items(.data);
    615     const node_tags = tree.nodes.items(.tag);
    616 
    617     const prev_anon_name_strategy = gz.anon_name_strategy;
    618     defer gz.anon_name_strategy = prev_anon_name_strategy;
    619     if (!nodeUsesAnonNameStrategy(tree, node)) {
    620         gz.anon_name_strategy = .anon;
    621     }
    622 
    623     switch (node_tags[node]) {
    624         .root => unreachable, // Top-level declaration.
    625         .@"usingnamespace" => unreachable, // Top-level declaration.
    626         .test_decl => unreachable, // Top-level declaration.
    627         .container_field_init => unreachable, // Top-level declaration.
    628         .container_field_align => unreachable, // Top-level declaration.
    629         .container_field => unreachable, // Top-level declaration.
    630         .fn_decl => unreachable, // Top-level declaration.
    631 
    632         .global_var_decl => unreachable, // Handled in `blockExpr`.
    633         .local_var_decl => unreachable, // Handled in `blockExpr`.
    634         .simple_var_decl => unreachable, // Handled in `blockExpr`.
    635         .aligned_var_decl => unreachable, // Handled in `blockExpr`.
    636         .@"defer" => unreachable, // Handled in `blockExpr`.
    637         .@"errdefer" => unreachable, // Handled in `blockExpr`.
    638 
    639         .switch_case => unreachable, // Handled in `switchExpr`.
    640         .switch_case_inline => unreachable, // Handled in `switchExpr`.
    641         .switch_case_one => unreachable, // Handled in `switchExpr`.
    642         .switch_case_inline_one => unreachable, // Handled in `switchExpr`.
    643         .switch_range => unreachable, // Handled in `switchExpr`.
    644 
    645         .asm_output => unreachable, // Handled in `asmExpr`.
    646         .asm_input => unreachable, // Handled in `asmExpr`.
    647 
    648         .assign => {
    649             try assign(gz, scope, node);
    650             return rvalue(gz, ri, .void_value, node);
    651         },
    652 
    653         .assign_shl => {
    654             try assignShift(gz, scope, node, .shl);
    655             return rvalue(gz, ri, .void_value, node);
    656         },
    657         .assign_shl_sat => {
    658             try assignShiftSat(gz, scope, node);
    659             return rvalue(gz, ri, .void_value, node);
    660         },
    661         .assign_shr => {
    662             try assignShift(gz, scope, node, .shr);
    663             return rvalue(gz, ri, .void_value, node);
    664         },
    665 
    666         .assign_bit_and => {
    667             try assignOp(gz, scope, node, .bit_and);
    668             return rvalue(gz, ri, .void_value, node);
    669         },
    670         .assign_bit_or => {
    671             try assignOp(gz, scope, node, .bit_or);
    672             return rvalue(gz, ri, .void_value, node);
    673         },
    674         .assign_bit_xor => {
    675             try assignOp(gz, scope, node, .xor);
    676             return rvalue(gz, ri, .void_value, node);
    677         },
    678         .assign_div => {
    679             try assignOp(gz, scope, node, .div);
    680             return rvalue(gz, ri, .void_value, node);
    681         },
    682         .assign_sub => {
    683             try assignOp(gz, scope, node, .sub);
    684             return rvalue(gz, ri, .void_value, node);
    685         },
    686         .assign_sub_wrap => {
    687             try assignOp(gz, scope, node, .subwrap);
    688             return rvalue(gz, ri, .void_value, node);
    689         },
    690         .assign_sub_sat => {
    691             try assignOp(gz, scope, node, .sub_sat);
    692             return rvalue(gz, ri, .void_value, node);
    693         },
    694         .assign_mod => {
    695             try assignOp(gz, scope, node, .mod_rem);
    696             return rvalue(gz, ri, .void_value, node);
    697         },
    698         .assign_add => {
    699             try assignOp(gz, scope, node, .add);
    700             return rvalue(gz, ri, .void_value, node);
    701         },
    702         .assign_add_wrap => {
    703             try assignOp(gz, scope, node, .addwrap);
    704             return rvalue(gz, ri, .void_value, node);
    705         },
    706         .assign_add_sat => {
    707             try assignOp(gz, scope, node, .add_sat);
    708             return rvalue(gz, ri, .void_value, node);
    709         },
    710         .assign_mul => {
    711             try assignOp(gz, scope, node, .mul);
    712             return rvalue(gz, ri, .void_value, node);
    713         },
    714         .assign_mul_wrap => {
    715             try assignOp(gz, scope, node, .mulwrap);
    716             return rvalue(gz, ri, .void_value, node);
    717         },
    718         .assign_mul_sat => {
    719             try assignOp(gz, scope, node, .mul_sat);
    720             return rvalue(gz, ri, .void_value, node);
    721         },
    722 
    723         // zig fmt: off
    724         .shl => return shiftOp(gz, scope, ri, node, node_datas[node].lhs, node_datas[node].rhs, .shl),
    725         .shr => return shiftOp(gz, scope, ri, node, node_datas[node].lhs, node_datas[node].rhs, .shr),
    726 
    727         .add      => return simpleBinOp(gz, scope, ri, node, .add),
    728         .add_wrap => return simpleBinOp(gz, scope, ri, node, .addwrap),
    729         .add_sat  => return simpleBinOp(gz, scope, ri, node, .add_sat),
    730         .sub      => return simpleBinOp(gz, scope, ri, node, .sub),
    731         .sub_wrap => return simpleBinOp(gz, scope, ri, node, .subwrap),
    732         .sub_sat  => return simpleBinOp(gz, scope, ri, node, .sub_sat),
    733         .mul      => return simpleBinOp(gz, scope, ri, node, .mul),
    734         .mul_wrap => return simpleBinOp(gz, scope, ri, node, .mulwrap),
    735         .mul_sat  => return simpleBinOp(gz, scope, ri, node, .mul_sat),
    736         .div      => return simpleBinOp(gz, scope, ri, node, .div),
    737         .mod      => return simpleBinOp(gz, scope, ri, node, .mod_rem),
    738         .shl_sat  => return simpleBinOp(gz, scope, ri, node, .shl_sat),
    739 
    740         .bit_and          => return simpleBinOp(gz, scope, ri, node, .bit_and),
    741         .bit_or           => return simpleBinOp(gz, scope, ri, node, .bit_or),
    742         .bit_xor          => return simpleBinOp(gz, scope, ri, node, .xor),
    743         .bang_equal       => return simpleBinOp(gz, scope, ri, node, .cmp_neq),
    744         .equal_equal      => return simpleBinOp(gz, scope, ri, node, .cmp_eq),
    745         .greater_than     => return simpleBinOp(gz, scope, ri, node, .cmp_gt),
    746         .greater_or_equal => return simpleBinOp(gz, scope, ri, node, .cmp_gte),
    747         .less_than        => return simpleBinOp(gz, scope, ri, node, .cmp_lt),
    748         .less_or_equal    => return simpleBinOp(gz, scope, ri, node, .cmp_lte),
    749         .array_cat        => return simpleBinOp(gz, scope, ri, node, .array_cat),
    750 
    751         .array_mult => {
    752             const result = try gz.addPlNode(.array_mul, node, Zir.Inst.Bin{
    753                 .lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs),
    754                 .rhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs),
    755             });
    756             return rvalue(gz, ri, result, node);
    757         },
    758 
    759         .error_union      => return simpleBinOp(gz, scope, ri, node, .error_union_type),
    760         .merge_error_sets => return simpleBinOp(gz, scope, ri, node, .merge_error_sets),
    761 
    762         .bool_and => return boolBinOp(gz, scope, ri, node, .bool_br_and),
    763         .bool_or  => return boolBinOp(gz, scope, ri, node, .bool_br_or),
    764 
    765         .bool_not => return simpleUnOp(gz, scope, ri, node, bool_ri, node_datas[node].lhs, .bool_not),
    766         .bit_not  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, node_datas[node].lhs, .bit_not),
    767 
    768         .negation      => return   negation(gz, scope, ri, node),
    769         .negation_wrap => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none }, node_datas[node].lhs, .negate_wrap),
    770 
    771         .identifier => return identifier(gz, scope, ri, node),
    772 
    773         .asm_simple => return asmExpr(gz, scope, ri, node, tree.asmSimple(node)),
    774         .@"asm"     => return asmExpr(gz, scope, ri, node, tree.asmFull(node)),
    775 
    776         .string_literal           => return stringLiteral(gz, ri, node),
    777         .multiline_string_literal => return multilineStringLiteral(gz, ri, node),
    778 
    779         .number_literal => return numberLiteral(gz, ri, node, node, .positive),
    780         // zig fmt: on
    781 
    782         .builtin_call_two, .builtin_call_two_comma => {
    783             if (node_datas[node].lhs == 0) {
    784                 const params = [_]Ast.Node.Index{};
    785                 return builtinCall(gz, scope, ri, node, &params);
    786             } else if (node_datas[node].rhs == 0) {
    787                 const params = [_]Ast.Node.Index{node_datas[node].lhs};
    788                 return builtinCall(gz, scope, ri, node, &params);
    789             } else {
    790                 const params = [_]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
    791                 return builtinCall(gz, scope, ri, node, &params);
    792             }
    793         },
    794         .builtin_call, .builtin_call_comma => {
    795             const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
    796             return builtinCall(gz, scope, ri, node, params);
    797         },
    798 
    799         .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => {
    800             var params: [1]Ast.Node.Index = undefined;
    801             return callExpr(gz, scope, ri, node, tree.callOne(&params, node));
    802         },
    803         .call, .call_comma, .async_call, .async_call_comma => {
    804             return callExpr(gz, scope, ri, node, tree.callFull(node));
    805         },
    806 
    807         .unreachable_literal => {
    808             try emitDbgNode(gz, node);
    809             _ = try gz.addAsIndex(.{
    810                 .tag = .@"unreachable",
    811                 .data = .{ .@"unreachable" = .{
    812                     .force_comptime = gz.force_comptime,
    813                     .src_node = gz.nodeIndexToRelative(node),
    814                 } },
    815             });
    816             return Zir.Inst.Ref.unreachable_value;
    817         },
    818         .@"return" => return ret(gz, scope, node),
    819         .field_access => return fieldAccess(gz, scope, ri, node),
    820 
    821         .if_simple => return ifExpr(gz, scope, ri.br(), node, tree.ifSimple(node)),
    822         .@"if" => return ifExpr(gz, scope, ri.br(), node, tree.ifFull(node)),
    823 
    824         .while_simple => return whileExpr(gz, scope, ri.br(), node, tree.whileSimple(node), false),
    825         .while_cont => return whileExpr(gz, scope, ri.br(), node, tree.whileCont(node), false),
    826         .@"while" => return whileExpr(gz, scope, ri.br(), node, tree.whileFull(node), false),
    827 
    828         .for_simple => return forExpr(gz, scope, ri.br(), node, tree.forSimple(node), false),
    829         .@"for" => return forExpr(gz, scope, ri.br(), node, tree.forFull(node), false),
    830 
    831         .slice_open => {
    832             const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
    833 
    834             maybeAdvanceSourceCursorToMainToken(gz, node);
    835             const line = gz.astgen.source_line - gz.decl_line;
    836             const column = gz.astgen.source_column;
    837 
    838             const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[node].rhs);
    839             try emitDbgStmt(gz, line, column);
    840             const result = try gz.addPlNode(.slice_start, node, Zir.Inst.SliceStart{
    841                 .lhs = lhs,
    842                 .start = start,
    843             });
    844             return rvalue(gz, ri, result, node);
    845         },
    846         .slice => {
    847             const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
    848 
    849             maybeAdvanceSourceCursorToMainToken(gz, node);
    850             const line = gz.astgen.source_line - gz.decl_line;
    851             const column = gz.astgen.source_column;
    852 
    853             const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);
    854             const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
    855             const end = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end);
    856             try emitDbgStmt(gz, line, column);
    857             const result = try gz.addPlNode(.slice_end, node, Zir.Inst.SliceEnd{
    858                 .lhs = lhs,
    859                 .start = start,
    860                 .end = end,
    861             });
    862             return rvalue(gz, ri, result, node);
    863         },
    864         .slice_sentinel => {
    865             const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
    866 
    867             maybeAdvanceSourceCursorToMainToken(gz, node);
    868             const line = gz.astgen.source_line - gz.decl_line;
    869             const column = gz.astgen.source_column;
    870 
    871             const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
    872             const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
    873             const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
    874             const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);
    875             try emitDbgStmt(gz, line, column);
    876             const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{
    877                 .lhs = lhs,
    878                 .start = start,
    879                 .end = end,
    880                 .sentinel = sentinel,
    881             });
    882             return rvalue(gz, ri, result, node);
    883         },
    884 
    885         .deref => {
    886             const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs);
    887             _ = try gz.addUnNode(.validate_deref, lhs, node);
    888             switch (ri.rl) {
    889                 .ref => return lhs,
    890                 else => {
    891                     const result = try gz.addUnNode(.load, lhs, node);
    892                     return rvalue(gz, ri, result, node);
    893                 },
    894             }
    895         },
    896         .address_of => {
    897             const result = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
    898             return rvalue(gz, ri, result, node);
    899         },
    900         .optional_type => {
    901             const operand = try typeExpr(gz, scope, node_datas[node].lhs);
    902             const result = try gz.addUnNode(.optional_type, operand, node);
    903             return rvalue(gz, ri, result, node);
    904         },
    905         .unwrap_optional => switch (ri.rl) {
    906             .ref => {
    907                 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
    908 
    909                 maybeAdvanceSourceCursorToMainToken(gz, node);
    910                 const line = gz.astgen.source_line - gz.decl_line;
    911                 const column = gz.astgen.source_column;
    912                 try emitDbgStmt(gz, line, column);
    913 
    914                 return gz.addUnNode(.optional_payload_safe_ptr, lhs, node);
    915             },
    916             else => {
    917                 const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs);
    918 
    919                 maybeAdvanceSourceCursorToMainToken(gz, node);
    920                 const line = gz.astgen.source_line - gz.decl_line;
    921                 const column = gz.astgen.source_column;
    922                 try emitDbgStmt(gz, line, column);
    923 
    924                 return rvalue(gz, ri, try gz.addUnNode(.optional_payload_safe, lhs, node), node);
    925             },
    926         },
    927         .block_two, .block_two_semicolon => {
    928             const statements = [2]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
    929             if (node_datas[node].lhs == 0) {
    930                 return blockExpr(gz, scope, ri, node, statements[0..0]);
    931             } else if (node_datas[node].rhs == 0) {
    932                 return blockExpr(gz, scope, ri, node, statements[0..1]);
    933             } else {
    934                 return blockExpr(gz, scope, ri, node, statements[0..2]);
    935             }
    936         },
    937         .block, .block_semicolon => {
    938             const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
    939             return blockExpr(gz, scope, ri, node, statements);
    940         },
    941         .enum_literal => return simpleStrTok(gz, ri, main_tokens[node], node, .enum_literal),
    942         .error_value => return simpleStrTok(gz, ri, node_datas[node].rhs, node, .error_value),
    943         // TODO restore this when implementing https://github.com/ziglang/zig/issues/6025
    944         // .anyframe_literal => return rvalue(gz, ri, .anyframe_type, node),
    945         .anyframe_literal => {
    946             const result = try gz.addUnNode(.anyframe_type, .void_type, node);
    947             return rvalue(gz, ri, result, node);
    948         },
    949         .anyframe_type => {
    950             const return_type = try typeExpr(gz, scope, node_datas[node].rhs);
    951             const result = try gz.addUnNode(.anyframe_type, return_type, node);
    952             return rvalue(gz, ri, result, node);
    953         },
    954         .@"catch" => {
    955             const catch_token = main_tokens[node];
    956             const payload_token: ?Ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe)
    957                 catch_token + 2
    958             else
    959                 null;
    960             switch (ri.rl) {
    961                 .ref => return orelseCatchExpr(
    962                     gz,
    963                     scope,
    964                     ri,
    965                     node,
    966                     node_datas[node].lhs,
    967                     .is_non_err_ptr,
    968                     .err_union_payload_unsafe_ptr,
    969                     .err_union_code_ptr,
    970                     node_datas[node].rhs,
    971                     payload_token,
    972                 ),
    973                 else => return orelseCatchExpr(
    974                     gz,
    975                     scope,
    976                     ri,
    977                     node,
    978                     node_datas[node].lhs,
    979                     .is_non_err,
    980                     .err_union_payload_unsafe,
    981                     .err_union_code,
    982                     node_datas[node].rhs,
    983                     payload_token,
    984                 ),
    985             }
    986         },
    987         .@"orelse" => switch (ri.rl) {
    988             .ref => return orelseCatchExpr(
    989                 gz,
    990                 scope,
    991                 ri,
    992                 node,
    993                 node_datas[node].lhs,
    994                 .is_non_null_ptr,
    995                 .optional_payload_unsafe_ptr,
    996                 undefined,
    997                 node_datas[node].rhs,
    998                 null,
    999             ),
   1000             else => return orelseCatchExpr(
   1001                 gz,
   1002                 scope,
   1003                 ri,
   1004                 node,
   1005                 node_datas[node].lhs,
   1006                 .is_non_null,
   1007                 .optional_payload_unsafe,
   1008                 undefined,
   1009                 node_datas[node].rhs,
   1010                 null,
   1011             ),
   1012         },
   1013 
   1014         .ptr_type_aligned => return ptrType(gz, scope, ri, node, tree.ptrTypeAligned(node)),
   1015         .ptr_type_sentinel => return ptrType(gz, scope, ri, node, tree.ptrTypeSentinel(node)),
   1016         .ptr_type => return ptrType(gz, scope, ri, node, tree.ptrType(node)),
   1017         .ptr_type_bit_range => return ptrType(gz, scope, ri, node, tree.ptrTypeBitRange(node)),
   1018 
   1019         .container_decl,
   1020         .container_decl_trailing,
   1021         => return containerDecl(gz, scope, ri, node, tree.containerDecl(node)),
   1022         .container_decl_two, .container_decl_two_trailing => {
   1023             var buffer: [2]Ast.Node.Index = undefined;
   1024             return containerDecl(gz, scope, ri, node, tree.containerDeclTwo(&buffer, node));
   1025         },
   1026         .container_decl_arg,
   1027         .container_decl_arg_trailing,
   1028         => return containerDecl(gz, scope, ri, node, tree.containerDeclArg(node)),
   1029 
   1030         .tagged_union,
   1031         .tagged_union_trailing,
   1032         => return containerDecl(gz, scope, ri, node, tree.taggedUnion(node)),
   1033         .tagged_union_two, .tagged_union_two_trailing => {
   1034             var buffer: [2]Ast.Node.Index = undefined;
   1035             return containerDecl(gz, scope, ri, node, tree.taggedUnionTwo(&buffer, node));
   1036         },
   1037         .tagged_union_enum_tag,
   1038         .tagged_union_enum_tag_trailing,
   1039         => return containerDecl(gz, scope, ri, node, tree.taggedUnionEnumTag(node)),
   1040 
   1041         .@"break" => return breakExpr(gz, scope, node),
   1042         .@"continue" => return continueExpr(gz, scope, node),
   1043         .grouped_expression => return expr(gz, scope, ri, node_datas[node].lhs),
   1044         .array_type => return arrayType(gz, scope, ri, node),
   1045         .array_type_sentinel => return arrayTypeSentinel(gz, scope, ri, node),
   1046         .char_literal => return charLiteral(gz, ri, node),
   1047         .error_set_decl => return errorSetDecl(gz, ri, node),
   1048         .array_access => return arrayAccess(gz, scope, ri, node),
   1049         .@"comptime" => return comptimeExprAst(gz, scope, ri, node),
   1050         .@"switch", .switch_comma => return switchExpr(gz, scope, ri.br(), node),
   1051 
   1052         .@"nosuspend" => return nosuspendExpr(gz, scope, ri, node),
   1053         .@"suspend" => return suspendExpr(gz, scope, node),
   1054         .@"await" => return awaitExpr(gz, scope, ri, node),
   1055         .@"resume" => return resumeExpr(gz, scope, ri, node),
   1056 
   1057         .@"try" => return tryExpr(gz, scope, ri, node, node_datas[node].lhs),
   1058 
   1059         .array_init_one, .array_init_one_comma => {
   1060             var elements: [1]Ast.Node.Index = undefined;
   1061             return arrayInitExpr(gz, scope, ri, node, tree.arrayInitOne(&elements, node));
   1062         },
   1063         .array_init_dot_two, .array_init_dot_two_comma => {
   1064             var elements: [2]Ast.Node.Index = undefined;
   1065             return arrayInitExpr(gz, scope, ri, node, tree.arrayInitDotTwo(&elements, node));
   1066         },
   1067         .array_init_dot,
   1068         .array_init_dot_comma,
   1069         => return arrayInitExpr(gz, scope, ri, node, tree.arrayInitDot(node)),
   1070         .array_init,
   1071         .array_init_comma,
   1072         => return arrayInitExpr(gz, scope, ri, node, tree.arrayInit(node)),
   1073 
   1074         .struct_init_one, .struct_init_one_comma => {
   1075             var fields: [1]Ast.Node.Index = undefined;
   1076             return structInitExpr(gz, scope, ri, node, tree.structInitOne(&fields, node));
   1077         },
   1078         .struct_init_dot_two, .struct_init_dot_two_comma => {
   1079             var fields: [2]Ast.Node.Index = undefined;
   1080             return structInitExpr(gz, scope, ri, node, tree.structInitDotTwo(&fields, node));
   1081         },
   1082         .struct_init_dot,
   1083         .struct_init_dot_comma,
   1084         => return structInitExpr(gz, scope, ri, node, tree.structInitDot(node)),
   1085         .struct_init,
   1086         .struct_init_comma,
   1087         => return structInitExpr(gz, scope, ri, node, tree.structInit(node)),
   1088 
   1089         .fn_proto_simple => {
   1090             var params: [1]Ast.Node.Index = undefined;
   1091             return fnProtoExpr(gz, scope, ri, node, tree.fnProtoSimple(&params, node));
   1092         },
   1093         .fn_proto_multi => {
   1094             return fnProtoExpr(gz, scope, ri, node, tree.fnProtoMulti(node));
   1095         },
   1096         .fn_proto_one => {
   1097             var params: [1]Ast.Node.Index = undefined;
   1098             return fnProtoExpr(gz, scope, ri, node, tree.fnProtoOne(&params, node));
   1099         },
   1100         .fn_proto => {
   1101             return fnProtoExpr(gz, scope, ri, node, tree.fnProto(node));
   1102         },
   1103     }
   1104 }
   1105 
   1106 fn nosuspendExpr(
   1107     gz: *GenZir,
   1108     scope: *Scope,
   1109     ri: ResultInfo,
   1110     node: Ast.Node.Index,
   1111 ) InnerError!Zir.Inst.Ref {
   1112     const astgen = gz.astgen;
   1113     const tree = astgen.tree;
   1114     const node_datas = tree.nodes.items(.data);
   1115     const body_node = node_datas[node].lhs;
   1116     assert(body_node != 0);
   1117     if (gz.nosuspend_node != 0) {
   1118         try astgen.appendErrorNodeNotes(node, "redundant nosuspend block", .{}, &[_]u32{
   1119             try astgen.errNoteNode(gz.nosuspend_node, "other nosuspend block here", .{}),
   1120         });
   1121     }
   1122     gz.nosuspend_node = node;
   1123     defer gz.nosuspend_node = 0;
   1124     return expr(gz, scope, ri, body_node);
   1125 }
   1126 
   1127 fn suspendExpr(
   1128     gz: *GenZir,
   1129     scope: *Scope,
   1130     node: Ast.Node.Index,
   1131 ) InnerError!Zir.Inst.Ref {
   1132     const astgen = gz.astgen;
   1133     const gpa = astgen.gpa;
   1134     const tree = astgen.tree;
   1135     const node_datas = tree.nodes.items(.data);
   1136     const body_node = node_datas[node].lhs;
   1137 
   1138     if (gz.nosuspend_node != 0) {
   1139         return astgen.failNodeNotes(node, "suspend inside nosuspend block", .{}, &[_]u32{
   1140             try astgen.errNoteNode(gz.nosuspend_node, "nosuspend block here", .{}),
   1141         });
   1142     }
   1143     if (gz.suspend_node != 0) {
   1144         return astgen.failNodeNotes(node, "cannot suspend inside suspend block", .{}, &[_]u32{
   1145             try astgen.errNoteNode(gz.suspend_node, "other suspend block here", .{}),
   1146         });
   1147     }
   1148     assert(body_node != 0);
   1149 
   1150     const suspend_inst = try gz.makeBlockInst(.suspend_block, node);
   1151     try gz.instructions.append(gpa, suspend_inst);
   1152 
   1153     var suspend_scope = gz.makeSubBlock(scope);
   1154     suspend_scope.suspend_node = node;
   1155     defer suspend_scope.unstack();
   1156 
   1157     const body_result = try expr(&suspend_scope, &suspend_scope.base, .{ .rl = .none }, body_node);
   1158     if (!gz.refIsNoReturn(body_result)) {
   1159         _ = try suspend_scope.addBreak(.break_inline, suspend_inst, .void_value);
   1160     }
   1161     try suspend_scope.setBlockBody(suspend_inst);
   1162 
   1163     return indexToRef(suspend_inst);
   1164 }
   1165 
   1166 fn awaitExpr(
   1167     gz: *GenZir,
   1168     scope: *Scope,
   1169     ri: ResultInfo,
   1170     node: Ast.Node.Index,
   1171 ) InnerError!Zir.Inst.Ref {
   1172     const astgen = gz.astgen;
   1173     const tree = astgen.tree;
   1174     const node_datas = tree.nodes.items(.data);
   1175     const rhs_node = node_datas[node].lhs;
   1176 
   1177     if (gz.suspend_node != 0) {
   1178         return astgen.failNodeNotes(node, "cannot await inside suspend block", .{}, &[_]u32{
   1179             try astgen.errNoteNode(gz.suspend_node, "suspend block here", .{}),
   1180         });
   1181     }
   1182     const operand = try expr(gz, scope, .{ .rl = .none }, rhs_node);
   1183     const result = if (gz.nosuspend_node != 0)
   1184         try gz.addExtendedPayload(.await_nosuspend, Zir.Inst.UnNode{
   1185             .node = gz.nodeIndexToRelative(node),
   1186             .operand = operand,
   1187         })
   1188     else
   1189         try gz.addUnNode(.@"await", operand, node);
   1190 
   1191     return rvalue(gz, ri, result, node);
   1192 }
   1193 
   1194 fn resumeExpr(
   1195     gz: *GenZir,
   1196     scope: *Scope,
   1197     ri: ResultInfo,
   1198     node: Ast.Node.Index,
   1199 ) InnerError!Zir.Inst.Ref {
   1200     const astgen = gz.astgen;
   1201     const tree = astgen.tree;
   1202     const node_datas = tree.nodes.items(.data);
   1203     const rhs_node = node_datas[node].lhs;
   1204     const operand = try expr(gz, scope, .{ .rl = .none }, rhs_node);
   1205     const result = try gz.addUnNode(.@"resume", operand, node);
   1206     return rvalue(gz, ri, result, node);
   1207 }
   1208 
   1209 fn fnProtoExpr(
   1210     gz: *GenZir,
   1211     scope: *Scope,
   1212     ri: ResultInfo,
   1213     node: Ast.Node.Index,
   1214     fn_proto: Ast.full.FnProto,
   1215 ) InnerError!Zir.Inst.Ref {
   1216     const astgen = gz.astgen;
   1217     const tree = astgen.tree;
   1218     const token_tags = tree.tokens.items(.tag);
   1219 
   1220     if (fn_proto.name_token) |some| {
   1221         return astgen.failTok(some, "function type cannot have a name", .{});
   1222     }
   1223 
   1224     const is_extern = blk: {
   1225         const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false;
   1226         break :blk token_tags[maybe_extern_token] == .keyword_extern;
   1227     };
   1228     assert(!is_extern);
   1229 
   1230     var block_scope = gz.makeSubBlock(scope);
   1231     defer block_scope.unstack();
   1232 
   1233     const block_inst = try gz.makeBlockInst(.block_inline, node);
   1234 
   1235     var noalias_bits: u32 = 0;
   1236     const is_var_args = is_var_args: {
   1237         var param_type_i: usize = 0;
   1238         var it = fn_proto.iterate(tree);
   1239         while (it.next()) |param| : (param_type_i += 1) {
   1240             const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) {
   1241                 .keyword_noalias => is_comptime: {
   1242                     noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse
   1243                         return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
   1244                     break :is_comptime false;
   1245                 },
   1246                 .keyword_comptime => true,
   1247                 else => false,
   1248             } else false;
   1249 
   1250             const is_anytype = if (param.anytype_ellipsis3) |token| blk: {
   1251                 switch (token_tags[token]) {
   1252                     .keyword_anytype => break :blk true,
   1253                     .ellipsis3 => break :is_var_args true,
   1254                     else => unreachable,
   1255                 }
   1256             } else false;
   1257 
   1258             const param_name: u32 = if (param.name_token) |name_token| blk: {
   1259                 if (mem.eql(u8, "_", tree.tokenSlice(name_token)))
   1260                     break :blk 0;
   1261 
   1262                 break :blk try astgen.identAsString(name_token);
   1263             } else 0;
   1264 
   1265             if (is_anytype) {
   1266                 const name_token = param.name_token orelse param.anytype_ellipsis3.?;
   1267 
   1268                 const tag: Zir.Inst.Tag = if (is_comptime)
   1269                     .param_anytype_comptime
   1270                 else
   1271                     .param_anytype;
   1272                 _ = try block_scope.addStrTok(tag, param_name, name_token);
   1273             } else {
   1274                 const param_type_node = param.type_expr;
   1275                 assert(param_type_node != 0);
   1276                 var param_gz = block_scope.makeSubBlock(scope);
   1277                 defer param_gz.unstack();
   1278                 const param_type = try expr(&param_gz, scope, coerced_type_ri, param_type_node);
   1279                 const param_inst_expected = @intCast(u32, astgen.instructions.len + 1);
   1280                 _ = try param_gz.addBreak(.break_inline, param_inst_expected, param_type);
   1281                 const main_tokens = tree.nodes.items(.main_token);
   1282                 const name_token = param.name_token orelse main_tokens[param_type_node];
   1283                 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param;
   1284                 const param_inst = try block_scope.addParam(&param_gz, tag, name_token, param_name, param.first_doc_comment);
   1285                 assert(param_inst_expected == param_inst);
   1286             }
   1287         }
   1288         break :is_var_args false;
   1289     };
   1290 
   1291     const align_ref: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
   1292         break :inst try expr(&block_scope, scope, align_ri, fn_proto.ast.align_expr);
   1293     };
   1294 
   1295     if (fn_proto.ast.addrspace_expr != 0) {
   1296         return astgen.failNode(fn_proto.ast.addrspace_expr, "addrspace not allowed on function prototypes", .{});
   1297     }
   1298 
   1299     if (fn_proto.ast.section_expr != 0) {
   1300         return astgen.failNode(fn_proto.ast.section_expr, "linksection not allowed on function prototypes", .{});
   1301     }
   1302 
   1303     const cc: Zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0)
   1304         try expr(
   1305             &block_scope,
   1306             scope,
   1307             .{ .rl = .{ .ty = .calling_convention_type } },
   1308             fn_proto.ast.callconv_expr,
   1309         )
   1310     else
   1311         Zir.Inst.Ref.none;
   1312 
   1313     const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
   1314     const is_inferred_error = token_tags[maybe_bang] == .bang;
   1315     if (is_inferred_error) {
   1316         return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{});
   1317     }
   1318     const ret_ty = try expr(&block_scope, scope, coerced_type_ri, fn_proto.ast.return_type);
   1319 
   1320     const result = try block_scope.addFunc(.{
   1321         .src_node = fn_proto.ast.proto_node,
   1322 
   1323         .cc_ref = cc,
   1324         .cc_gz = null,
   1325         .align_ref = align_ref,
   1326         .align_gz = null,
   1327         .ret_ref = ret_ty,
   1328         .ret_gz = null,
   1329         .section_ref = .none,
   1330         .section_gz = null,
   1331         .addrspace_ref = .none,
   1332         .addrspace_gz = null,
   1333 
   1334         .param_block = block_inst,
   1335         .body_gz = null,
   1336         .lib_name = 0,
   1337         .is_var_args = is_var_args,
   1338         .is_inferred_error = false,
   1339         .is_test = false,
   1340         .is_extern = false,
   1341         .is_noinline = false,
   1342         .noalias_bits = noalias_bits,
   1343     });
   1344 
   1345     _ = try block_scope.addBreak(.break_inline, block_inst, result);
   1346     try block_scope.setBlockBody(block_inst);
   1347     try gz.instructions.append(astgen.gpa, block_inst);
   1348 
   1349     return rvalue(gz, ri, indexToRef(block_inst), fn_proto.ast.proto_node);
   1350 }
   1351 
   1352 fn arrayInitExpr(
   1353     gz: *GenZir,
   1354     scope: *Scope,
   1355     ri: ResultInfo,
   1356     node: Ast.Node.Index,
   1357     array_init: Ast.full.ArrayInit,
   1358 ) InnerError!Zir.Inst.Ref {
   1359     const astgen = gz.astgen;
   1360     const tree = astgen.tree;
   1361     const node_tags = tree.nodes.items(.tag);
   1362     const main_tokens = tree.nodes.items(.main_token);
   1363 
   1364     assert(array_init.ast.elements.len != 0); // Otherwise it would be struct init.
   1365 
   1366     const types: struct {
   1367         array: Zir.Inst.Ref,
   1368         elem: Zir.Inst.Ref,
   1369     } = inst: {
   1370         if (array_init.ast.type_expr == 0) break :inst .{
   1371             .array = .none,
   1372             .elem = .none,
   1373         };
   1374 
   1375         infer: {
   1376             const array_type: Ast.full.ArrayType = switch (node_tags[array_init.ast.type_expr]) {
   1377                 .array_type => tree.arrayType(array_init.ast.type_expr),
   1378                 .array_type_sentinel => tree.arrayTypeSentinel(array_init.ast.type_expr),
   1379                 else => break :infer,
   1380             };
   1381             // This intentionally does not support `@"_"` syntax.
   1382             if (node_tags[array_type.ast.elem_count] == .identifier and
   1383                 mem.eql(u8, tree.tokenSlice(main_tokens[array_type.ast.elem_count]), "_"))
   1384             {
   1385                 const len_inst = try gz.addInt(array_init.ast.elements.len);
   1386                 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type);
   1387                 if (array_type.ast.sentinel == 0) {
   1388                     const array_type_inst = try gz.addPlNode(.array_type, array_init.ast.type_expr, Zir.Inst.Bin{
   1389                         .lhs = len_inst,
   1390                         .rhs = elem_type,
   1391                     });
   1392                     break :inst .{
   1393                         .array = array_type_inst,
   1394                         .elem = elem_type,
   1395                     };
   1396                 } else {
   1397                     const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, array_type.ast.sentinel);
   1398                     const array_type_inst = try gz.addPlNode(
   1399                         .array_type_sentinel,
   1400                         array_init.ast.type_expr,
   1401                         Zir.Inst.ArrayTypeSentinel{
   1402                             .len = len_inst,
   1403                             .elem_type = elem_type,
   1404                             .sentinel = sentinel,
   1405                         },
   1406                     );
   1407                     break :inst .{
   1408                         .array = array_type_inst,
   1409                         .elem = elem_type,
   1410                     };
   1411                 }
   1412             }
   1413         }
   1414         const array_type_inst = try typeExpr(gz, scope, array_init.ast.type_expr);
   1415         _ = try gz.addPlNode(.validate_array_init_ty, node, Zir.Inst.ArrayInit{
   1416             .ty = array_type_inst,
   1417             .init_count = @intCast(u32, array_init.ast.elements.len),
   1418         });
   1419         break :inst .{
   1420             .array = array_type_inst,
   1421             .elem = .none,
   1422         };
   1423     };
   1424 
   1425     switch (ri.rl) {
   1426         .discard => {
   1427             // TODO elements should still be coerced if type is provided
   1428             for (array_init.ast.elements) |elem_init| {
   1429                 _ = try expr(gz, scope, .{ .rl = .discard }, elem_init);
   1430             }
   1431             return Zir.Inst.Ref.void_value;
   1432         },
   1433         .ref => {
   1434             const tag: Zir.Inst.Tag = if (types.array != .none) .array_init_ref else .array_init_anon_ref;
   1435             return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
   1436         },
   1437         .none => {
   1438             const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;
   1439             return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
   1440         },
   1441         .ty, .coerced_ty => {
   1442             const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;
   1443             const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
   1444             return rvalue(gz, ri, result, node);
   1445         },
   1446         .ptr => |ptr_res| {
   1447             return arrayInitExprRlPtr(gz, scope, ri, node, ptr_res.inst, array_init.ast.elements, types.array);
   1448         },
   1449         .inferred_ptr => |ptr_inst| {
   1450             if (types.array == .none) {
   1451                 // We treat this case differently so that we don't get a crash when
   1452                 // analyzing array_base_ptr against an alloc_inferred_mut.
   1453                 // See corresponding logic in structInitExpr.
   1454                 const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon);
   1455                 return rvalue(gz, ri, result, node);
   1456             } else {
   1457                 return arrayInitExprRlPtr(gz, scope, ri, node, ptr_inst, array_init.ast.elements, types.array);
   1458             }
   1459         },
   1460         .block_ptr => |block_gz| {
   1461             // This condition is here for the same reason as the above condition in `inferred_ptr`.
   1462             // See corresponding logic in structInitExpr.
   1463             if (types.array == .none and astgen.isInferred(block_gz.rl_ptr)) {
   1464                 const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon);
   1465                 return rvalue(gz, ri, result, node);
   1466             }
   1467             return arrayInitExprRlPtr(gz, scope, ri, node, block_gz.rl_ptr, array_init.ast.elements, types.array);
   1468         },
   1469     }
   1470 }
   1471 
   1472 fn arrayInitExprRlNone(
   1473     gz: *GenZir,
   1474     scope: *Scope,
   1475     node: Ast.Node.Index,
   1476     elements: []const Ast.Node.Index,
   1477     tag: Zir.Inst.Tag,
   1478 ) InnerError!Zir.Inst.Ref {
   1479     const astgen = gz.astgen;
   1480 
   1481     const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{
   1482         .operands_len = @intCast(u32, elements.len),
   1483     });
   1484     var extra_index = try reserveExtra(astgen, elements.len);
   1485 
   1486     for (elements) |elem_init| {
   1487         const elem_ref = try expr(gz, scope, .{ .rl = .none }, elem_init);
   1488         astgen.extra.items[extra_index] = @enumToInt(elem_ref);
   1489         extra_index += 1;
   1490     }
   1491     return try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1492 }
   1493 
   1494 fn arrayInitExprInner(
   1495     gz: *GenZir,
   1496     scope: *Scope,
   1497     node: Ast.Node.Index,
   1498     elements: []const Ast.Node.Index,
   1499     array_ty_inst: Zir.Inst.Ref,
   1500     elem_ty: Zir.Inst.Ref,
   1501     tag: Zir.Inst.Tag,
   1502 ) InnerError!Zir.Inst.Ref {
   1503     const astgen = gz.astgen;
   1504 
   1505     const len = elements.len + @boolToInt(array_ty_inst != .none);
   1506     const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{
   1507         .operands_len = @intCast(u32, len),
   1508     });
   1509     var extra_index = try reserveExtra(astgen, len);
   1510     if (array_ty_inst != .none) {
   1511         astgen.extra.items[extra_index] = @enumToInt(array_ty_inst);
   1512         extra_index += 1;
   1513     }
   1514 
   1515     for (elements) |elem_init, i| {
   1516         const ri = if (elem_ty != .none)
   1517             ResultInfo{ .rl = .{ .coerced_ty = elem_ty } }
   1518         else if (array_ty_inst != .none and nodeMayNeedMemoryLocation(astgen.tree, elem_init, true)) ri: {
   1519             const ty_expr = try gz.add(.{
   1520                 .tag = .elem_type_index,
   1521                 .data = .{ .bin = .{
   1522                     .lhs = array_ty_inst,
   1523                     .rhs = @intToEnum(Zir.Inst.Ref, i),
   1524                 } },
   1525             });
   1526             break :ri ResultInfo{ .rl = .{ .coerced_ty = ty_expr } };
   1527         } else ResultInfo{ .rl = .{ .none = {} } };
   1528 
   1529         const elem_ref = try expr(gz, scope, ri, elem_init);
   1530         astgen.extra.items[extra_index] = @enumToInt(elem_ref);
   1531         extra_index += 1;
   1532     }
   1533 
   1534     return try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1535 }
   1536 
   1537 fn arrayInitExprRlPtr(
   1538     gz: *GenZir,
   1539     scope: *Scope,
   1540     ri: ResultInfo,
   1541     node: Ast.Node.Index,
   1542     result_ptr: Zir.Inst.Ref,
   1543     elements: []const Ast.Node.Index,
   1544     array_ty: Zir.Inst.Ref,
   1545 ) InnerError!Zir.Inst.Ref {
   1546     if (array_ty == .none) {
   1547         const base_ptr = try gz.addUnNode(.array_base_ptr, result_ptr, node);
   1548         return arrayInitExprRlPtrInner(gz, scope, node, base_ptr, elements);
   1549     }
   1550 
   1551     var as_scope = try gz.makeCoercionScope(scope, array_ty, result_ptr, node);
   1552     defer as_scope.unstack();
   1553 
   1554     const result = try arrayInitExprRlPtrInner(&as_scope, scope, node, as_scope.rl_ptr, elements);
   1555     return as_scope.finishCoercion(gz, ri, node, result, array_ty);
   1556 }
   1557 
   1558 fn arrayInitExprRlPtrInner(
   1559     gz: *GenZir,
   1560     scope: *Scope,
   1561     node: Ast.Node.Index,
   1562     result_ptr: Zir.Inst.Ref,
   1563     elements: []const Ast.Node.Index,
   1564 ) InnerError!Zir.Inst.Ref {
   1565     const astgen = gz.astgen;
   1566 
   1567     const payload_index = try addExtra(astgen, Zir.Inst.Block{
   1568         .body_len = @intCast(u32, elements.len),
   1569     });
   1570     var extra_index = try reserveExtra(astgen, elements.len);
   1571 
   1572     for (elements) |elem_init, i| {
   1573         const elem_ptr = try gz.addPlNode(.elem_ptr_imm, elem_init, Zir.Inst.ElemPtrImm{
   1574             .ptr = result_ptr,
   1575             .index = @intCast(u32, i),
   1576         });
   1577         astgen.extra.items[extra_index] = refToIndex(elem_ptr).?;
   1578         extra_index += 1;
   1579         _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = elem_ptr } } }, elem_init);
   1580     }
   1581 
   1582     const tag: Zir.Inst.Tag = if (gz.force_comptime)
   1583         .validate_array_init_comptime
   1584     else
   1585         .validate_array_init;
   1586 
   1587     _ = try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1588     return .void_value;
   1589 }
   1590 
   1591 fn structInitExpr(
   1592     gz: *GenZir,
   1593     scope: *Scope,
   1594     ri: ResultInfo,
   1595     node: Ast.Node.Index,
   1596     struct_init: Ast.full.StructInit,
   1597 ) InnerError!Zir.Inst.Ref {
   1598     const astgen = gz.astgen;
   1599     const tree = astgen.tree;
   1600 
   1601     if (struct_init.ast.type_expr == 0) {
   1602         if (struct_init.ast.fields.len == 0) {
   1603             return rvalue(gz, ri, .empty_struct, node);
   1604         }
   1605     } else array: {
   1606         const node_tags = tree.nodes.items(.tag);
   1607         const main_tokens = tree.nodes.items(.main_token);
   1608         const array_type: Ast.full.ArrayType = switch (node_tags[struct_init.ast.type_expr]) {
   1609             .array_type => tree.arrayType(struct_init.ast.type_expr),
   1610             .array_type_sentinel => tree.arrayTypeSentinel(struct_init.ast.type_expr),
   1611             else => {
   1612                 if (struct_init.ast.fields.len == 0) {
   1613                     const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1614                     const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
   1615                     return rvalue(gz, ri, result, node);
   1616                 }
   1617                 break :array;
   1618             },
   1619         };
   1620         const is_inferred_array_len = node_tags[array_type.ast.elem_count] == .identifier and
   1621             // This intentionally does not support `@"_"` syntax.
   1622             mem.eql(u8, tree.tokenSlice(main_tokens[array_type.ast.elem_count]), "_");
   1623         if (struct_init.ast.fields.len == 0) {
   1624             if (is_inferred_array_len) {
   1625                 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type);
   1626                 const array_type_inst = if (array_type.ast.sentinel == 0) blk: {
   1627                     break :blk try gz.addPlNode(.array_type, struct_init.ast.type_expr, Zir.Inst.Bin{
   1628                         .lhs = .zero_usize,
   1629                         .rhs = elem_type,
   1630                     });
   1631                 } else blk: {
   1632                     const sentinel = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = elem_type } }, array_type.ast.sentinel);
   1633                     break :blk try gz.addPlNode(
   1634                         .array_type_sentinel,
   1635                         struct_init.ast.type_expr,
   1636                         Zir.Inst.ArrayTypeSentinel{
   1637                             .len = .zero_usize,
   1638                             .elem_type = elem_type,
   1639                             .sentinel = sentinel,
   1640                         },
   1641                     );
   1642                 };
   1643                 const result = try gz.addUnNode(.struct_init_empty, array_type_inst, node);
   1644                 return rvalue(gz, ri, result, node);
   1645             }
   1646             const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1647             const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
   1648             return rvalue(gz, ri, result, node);
   1649         } else {
   1650             return astgen.failNode(
   1651                 struct_init.ast.type_expr,
   1652                 "initializing array with struct syntax",
   1653                 .{},
   1654             );
   1655         }
   1656     }
   1657 
   1658     switch (ri.rl) {
   1659         .discard => {
   1660             if (struct_init.ast.type_expr != 0) {
   1661                 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1662                 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
   1663                 _ = try structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init);
   1664             } else {
   1665                 _ = try structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon);
   1666             }
   1667             return Zir.Inst.Ref.void_value;
   1668         },
   1669         .ref => {
   1670             if (struct_init.ast.type_expr != 0) {
   1671                 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1672                 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
   1673                 return structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init_ref);
   1674             } else {
   1675                 return structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon_ref);
   1676             }
   1677         },
   1678         .none => {
   1679             if (struct_init.ast.type_expr != 0) {
   1680                 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1681                 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
   1682                 return structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init);
   1683             } else {
   1684                 return structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon);
   1685             }
   1686         },
   1687         .ty, .coerced_ty => |ty_inst| {
   1688             if (struct_init.ast.type_expr == 0) {
   1689                 const result = try structInitExprRlNone(gz, scope, node, struct_init, ty_inst, .struct_init_anon);
   1690                 return rvalue(gz, ri, result, node);
   1691             }
   1692             const inner_ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1693             _ = try gz.addUnNode(.validate_struct_init_ty, inner_ty_inst, node);
   1694             const result = try structInitExprRlTy(gz, scope, node, struct_init, inner_ty_inst, .struct_init);
   1695             return rvalue(gz, ri, result, node);
   1696         },
   1697         .ptr => |ptr_res| return structInitExprRlPtr(gz, scope, ri, node, struct_init, ptr_res.inst),
   1698         .inferred_ptr => |ptr_inst| {
   1699             if (struct_init.ast.type_expr == 0) {
   1700                 // We treat this case differently so that we don't get a crash when
   1701                 // analyzing field_base_ptr against an alloc_inferred_mut.
   1702                 // See corresponding logic in arrayInitExpr.
   1703                 const result = try structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon);
   1704                 return rvalue(gz, ri, result, node);
   1705             } else {
   1706                 return structInitExprRlPtr(gz, scope, ri, node, struct_init, ptr_inst);
   1707             }
   1708         },
   1709         .block_ptr => |block_gz| {
   1710             // This condition is here for the same reason as the above condition in `inferred_ptr`.
   1711             // See corresponding logic in arrayInitExpr.
   1712             if (struct_init.ast.type_expr == 0 and astgen.isInferred(block_gz.rl_ptr)) {
   1713                 const result = try structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon);
   1714                 return rvalue(gz, ri, result, node);
   1715             }
   1716 
   1717             return structInitExprRlPtr(gz, scope, ri, node, struct_init, block_gz.rl_ptr);
   1718         },
   1719     }
   1720 }
   1721 
   1722 fn structInitExprRlNone(
   1723     gz: *GenZir,
   1724     scope: *Scope,
   1725     node: Ast.Node.Index,
   1726     struct_init: Ast.full.StructInit,
   1727     ty_inst: Zir.Inst.Ref,
   1728     tag: Zir.Inst.Tag,
   1729 ) InnerError!Zir.Inst.Ref {
   1730     const astgen = gz.astgen;
   1731     const tree = astgen.tree;
   1732 
   1733     const payload_index = try addExtra(astgen, Zir.Inst.StructInitAnon{
   1734         .fields_len = @intCast(u32, struct_init.ast.fields.len),
   1735     });
   1736     const field_size = @typeInfo(Zir.Inst.StructInitAnon.Item).Struct.fields.len;
   1737     var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size);
   1738 
   1739     for (struct_init.ast.fields) |field_init| {
   1740         const name_token = tree.firstToken(field_init) - 2;
   1741         const str_index = try astgen.identAsString(name_token);
   1742         const sub_ri: ResultInfo = if (ty_inst != .none)
   1743             ResultInfo{ .rl = .{ .ty = try gz.addPlNode(.field_type, field_init, Zir.Inst.FieldType{
   1744                 .container_type = ty_inst,
   1745                 .name_start = str_index,
   1746             }) } }
   1747         else .{ .rl = .none };
   1748         setExtra(astgen, extra_index, Zir.Inst.StructInitAnon.Item{
   1749             .field_name = str_index,
   1750             .init = try expr(gz, scope, sub_ri, field_init),
   1751         });
   1752         extra_index += field_size;
   1753     }
   1754 
   1755     return try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1756 }
   1757 
   1758 fn structInitExprRlPtr(
   1759     gz: *GenZir,
   1760     scope: *Scope,
   1761     ri: ResultInfo,
   1762     node: Ast.Node.Index,
   1763     struct_init: Ast.full.StructInit,
   1764     result_ptr: Zir.Inst.Ref,
   1765 ) InnerError!Zir.Inst.Ref {
   1766     if (struct_init.ast.type_expr == 0) {
   1767         const base_ptr = try gz.addUnNode(.field_base_ptr, result_ptr, node);
   1768         return structInitExprRlPtrInner(gz, scope, node, struct_init, base_ptr);
   1769     }
   1770     const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1771     _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
   1772 
   1773     var as_scope = try gz.makeCoercionScope(scope, ty_inst, result_ptr, node);
   1774     defer as_scope.unstack();
   1775 
   1776     const result = try structInitExprRlPtrInner(&as_scope, scope, node, struct_init, as_scope.rl_ptr);
   1777     return as_scope.finishCoercion(gz, ri, node, result, ty_inst);
   1778 }
   1779 
   1780 fn structInitExprRlPtrInner(
   1781     gz: *GenZir,
   1782     scope: *Scope,
   1783     node: Ast.Node.Index,
   1784     struct_init: Ast.full.StructInit,
   1785     result_ptr: Zir.Inst.Ref,
   1786 ) InnerError!Zir.Inst.Ref {
   1787     const astgen = gz.astgen;
   1788     const tree = astgen.tree;
   1789 
   1790     const payload_index = try addExtra(astgen, Zir.Inst.Block{
   1791         .body_len = @intCast(u32, struct_init.ast.fields.len),
   1792     });
   1793     var extra_index = try reserveExtra(astgen, struct_init.ast.fields.len);
   1794 
   1795     for (struct_init.ast.fields) |field_init| {
   1796         const name_token = tree.firstToken(field_init) - 2;
   1797         const str_index = try astgen.identAsString(name_token);
   1798         const field_ptr = try gz.addPlNode(.field_ptr_init, field_init, Zir.Inst.Field{
   1799             .lhs = result_ptr,
   1800             .field_name_start = str_index,
   1801         });
   1802         astgen.extra.items[extra_index] = refToIndex(field_ptr).?;
   1803         extra_index += 1;
   1804         _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = field_ptr } } }, field_init);
   1805     }
   1806 
   1807     const tag: Zir.Inst.Tag = if (gz.force_comptime)
   1808         .validate_struct_init_comptime
   1809     else
   1810         .validate_struct_init;
   1811 
   1812     _ = try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1813     return Zir.Inst.Ref.void_value;
   1814 }
   1815 
   1816 fn structInitExprRlTy(
   1817     gz: *GenZir,
   1818     scope: *Scope,
   1819     node: Ast.Node.Index,
   1820     struct_init: Ast.full.StructInit,
   1821     ty_inst: Zir.Inst.Ref,
   1822     tag: Zir.Inst.Tag,
   1823 ) InnerError!Zir.Inst.Ref {
   1824     const astgen = gz.astgen;
   1825     const tree = astgen.tree;
   1826 
   1827     const payload_index = try addExtra(astgen, Zir.Inst.StructInit{
   1828         .fields_len = @intCast(u32, struct_init.ast.fields.len),
   1829     });
   1830     const field_size = @typeInfo(Zir.Inst.StructInit.Item).Struct.fields.len;
   1831     var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size);
   1832 
   1833     for (struct_init.ast.fields) |field_init| {
   1834         const name_token = tree.firstToken(field_init) - 2;
   1835         const str_index = try astgen.identAsString(name_token);
   1836         const field_ty_inst = try gz.addPlNode(.field_type, field_init, Zir.Inst.FieldType{
   1837             .container_type = ty_inst,
   1838             .name_start = str_index,
   1839         });
   1840         setExtra(astgen, extra_index, Zir.Inst.StructInit.Item{
   1841             .field_type = refToIndex(field_ty_inst).?,
   1842             .init = try expr(gz, scope, .{ .rl = .{ .ty = field_ty_inst } }, field_init),
   1843         });
   1844         extra_index += field_size;
   1845     }
   1846 
   1847     return try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1848 }
   1849 
   1850 /// This calls expr in a comptime scope, and is intended to be called as a helper function.
   1851 /// The one that corresponds to `comptime` expression syntax is `comptimeExprAst`.
   1852 fn comptimeExpr(
   1853     gz: *GenZir,
   1854     scope: *Scope,
   1855     ri: ResultInfo,
   1856     node: Ast.Node.Index,
   1857 ) InnerError!Zir.Inst.Ref {
   1858     const prev_force_comptime = gz.force_comptime;
   1859     gz.force_comptime = true;
   1860     defer gz.force_comptime = prev_force_comptime;
   1861 
   1862     return expr(gz, scope, ri, node);
   1863 }
   1864 
   1865 /// This one is for an actual `comptime` syntax, and will emit a compile error if
   1866 /// the scope already has `force_comptime=true`.
   1867 /// See `comptimeExpr` for the helper function for calling expr in a comptime scope.
   1868 fn comptimeExprAst(
   1869     gz: *GenZir,
   1870     scope: *Scope,
   1871     ri: ResultInfo,
   1872     node: Ast.Node.Index,
   1873 ) InnerError!Zir.Inst.Ref {
   1874     const astgen = gz.astgen;
   1875     if (gz.force_comptime) {
   1876         return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
   1877     }
   1878     const tree = astgen.tree;
   1879     const node_datas = tree.nodes.items(.data);
   1880     const body_node = node_datas[node].lhs;
   1881     gz.force_comptime = true;
   1882     const result = try expr(gz, scope, ri, body_node);
   1883     gz.force_comptime = false;
   1884     return result;
   1885 }
   1886 
   1887 /// Restore the error return trace index. Performs the restore only if the result is a non-error or
   1888 /// if the result location is a non-error-handling expression.
   1889 fn restoreErrRetIndex(
   1890     gz: *GenZir,
   1891     bt: GenZir.BranchTarget,
   1892     ri: ResultInfo,
   1893     node: Ast.Node.Index,
   1894     result: Zir.Inst.Ref,
   1895 ) !void {
   1896     const op = switch (nodeMayEvalToError(gz.astgen.tree, node)) {
   1897         .always => return, // never restore/pop
   1898         .never => .none, // always restore/pop
   1899         .maybe => switch (ri.ctx) {
   1900             .error_handling_expr, .@"return", .fn_arg, .const_init => switch (ri.rl) {
   1901                 .ptr => |ptr_res| try gz.addUnNode(.load, ptr_res.inst, node),
   1902                 .inferred_ptr => |ptr| try gz.addUnNode(.load, ptr, node),
   1903                 .block_ptr => |block_scope| if (block_scope.rvalue_rl_count != block_scope.break_count) b: {
   1904                     // The result location may have been used by this expression, in which case
   1905                     // the operand is not the result and we need to load the rl ptr.
   1906                     switch (gz.astgen.instructions.items(.tag)[Zir.refToIndex(block_scope.rl_ptr).?]) {
   1907                         .alloc_inferred, .alloc_inferred_mut => {
   1908                             // This is a terrible workaround for Sema's inability to load from a .alloc_inferred ptr
   1909                             // before its type has been resolved. The operand we use here instead is not guaranteed
   1910                             // to be valid, and when it's not, we will pop error traces prematurely.
   1911                             //
   1912                             // TODO: Update this to do a proper load from the rl_ptr, once Sema can support it.
   1913                             break :b result;
   1914                         },
   1915                         else => break :b try gz.addUnNode(.load, block_scope.rl_ptr, node),
   1916                     }
   1917                 } else result,
   1918                 else => result,
   1919             },
   1920             else => .none, // always restore/pop
   1921         },
   1922     };
   1923     _ = try gz.addRestoreErrRetIndex(bt, .{ .if_non_error = op });
   1924 }
   1925 
   1926 fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   1927     const astgen = parent_gz.astgen;
   1928     const tree = astgen.tree;
   1929     const node_datas = tree.nodes.items(.data);
   1930     const break_label = node_datas[node].lhs;
   1931     const rhs = node_datas[node].rhs;
   1932 
   1933     // Look for the label in the scope.
   1934     var scope = parent_scope;
   1935     while (true) {
   1936         switch (scope.tag) {
   1937             .gen_zir => {
   1938                 const block_gz = scope.cast(GenZir).?;
   1939 
   1940                 if (block_gz.cur_defer_node != 0) {
   1941                     // We are breaking out of a `defer` block.
   1942                     return astgen.failNodeNotes(node, "cannot break out of defer expression", .{}, &.{
   1943                         try astgen.errNoteNode(
   1944                             block_gz.cur_defer_node,
   1945                             "defer expression here",
   1946                             .{},
   1947                         ),
   1948                     });
   1949                 }
   1950 
   1951                 const block_inst = blk: {
   1952                     if (break_label != 0) {
   1953                         if (block_gz.label) |*label| {
   1954                             if (try astgen.tokenIdentEql(label.token, break_label)) {
   1955                                 label.used = true;
   1956                                 break :blk label.block_inst;
   1957                             }
   1958                         }
   1959                     } else if (block_gz.break_block != 0) {
   1960                         break :blk block_gz.break_block;
   1961                     }
   1962                     // If not the target, start over with the parent
   1963                     scope = block_gz.parent;
   1964                     continue;
   1965                 };
   1966                 // If we made it here, this block is the target of the break expr
   1967 
   1968                 const break_tag: Zir.Inst.Tag = if (block_gz.is_inline or block_gz.force_comptime)
   1969                     .break_inline
   1970                 else
   1971                     .@"break";
   1972 
   1973                 if (rhs == 0) {
   1974                     try genDefers(parent_gz, scope, parent_scope, .normal_only);
   1975 
   1976                     // As our last action before the break, "pop" the error trace if needed
   1977                     if (!block_gz.force_comptime)
   1978                         _ = try parent_gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always);
   1979 
   1980                     _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
   1981                     return Zir.Inst.Ref.unreachable_value;
   1982                 }
   1983                 block_gz.break_count += 1;
   1984 
   1985                 const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_info, rhs, node);
   1986                 const search_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
   1987 
   1988                 try genDefers(parent_gz, scope, parent_scope, .normal_only);
   1989 
   1990                 // As our last action before the break, "pop" the error trace if needed
   1991                 if (!block_gz.force_comptime)
   1992                     try restoreErrRetIndex(parent_gz, .{ .block = block_inst }, block_gz.break_result_info, rhs, operand);
   1993 
   1994                 switch (block_gz.break_result_info.rl) {
   1995                     .block_ptr => {
   1996                         const br = try parent_gz.addBreak(break_tag, block_inst, operand);
   1997                         try block_gz.labeled_breaks.append(astgen.gpa, .{ .br = br, .search = search_index });
   1998                     },
   1999                     .ptr => {
   2000                         // In this case we don't have any mechanism to intercept it;
   2001                         // we assume the result location is written, and we break with void.
   2002                         _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
   2003                     },
   2004                     .discard => {
   2005                         _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
   2006                     },
   2007                     else => {
   2008                         _ = try parent_gz.addBreak(break_tag, block_inst, operand);
   2009                     },
   2010                 }
   2011                 return Zir.Inst.Ref.unreachable_value;
   2012             },
   2013             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   2014             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   2015             .namespace => break,
   2016             .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   2017             .top => unreachable,
   2018         }
   2019     }
   2020     if (break_label != 0) {
   2021         const label_name = try astgen.identifierTokenString(break_label);
   2022         return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
   2023     } else {
   2024         return astgen.failNode(node, "break expression outside loop", .{});
   2025     }
   2026 }
   2027 
   2028 fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   2029     const astgen = parent_gz.astgen;
   2030     const tree = astgen.tree;
   2031     const node_datas = tree.nodes.items(.data);
   2032     const break_label = node_datas[node].lhs;
   2033 
   2034     // Look for the label in the scope.
   2035     var scope = parent_scope;
   2036     while (true) {
   2037         switch (scope.tag) {
   2038             .gen_zir => {
   2039                 const gen_zir = scope.cast(GenZir).?;
   2040 
   2041                 if (gen_zir.cur_defer_node != 0) {
   2042                     return astgen.failNodeNotes(node, "cannot continue out of defer expression", .{}, &.{
   2043                         try astgen.errNoteNode(
   2044                             gen_zir.cur_defer_node,
   2045                             "defer expression here",
   2046                             .{},
   2047                         ),
   2048                     });
   2049                 }
   2050                 const continue_block = gen_zir.continue_block;
   2051                 if (continue_block == 0) {
   2052                     scope = gen_zir.parent;
   2053                     continue;
   2054                 }
   2055                 if (break_label != 0) blk: {
   2056                     if (gen_zir.label) |*label| {
   2057                         if (try astgen.tokenIdentEql(label.token, break_label)) {
   2058                             label.used = true;
   2059                             break :blk;
   2060                         }
   2061                     }
   2062                     // found continue but either it has a different label, or no label
   2063                     scope = gen_zir.parent;
   2064                     continue;
   2065                 }
   2066 
   2067                 const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline or gen_zir.force_comptime)
   2068                     .break_inline
   2069                 else
   2070                     .@"break";
   2071                 if (break_tag == .break_inline) {
   2072                     _ = try parent_gz.addUnNode(.check_comptime_control_flow, Zir.indexToRef(continue_block), node);
   2073                 }
   2074 
   2075                 // As our last action before the continue, "pop" the error trace if needed
   2076                 if (!gen_zir.force_comptime)
   2077                     _ = try parent_gz.addRestoreErrRetIndex(.{ .block = continue_block }, .always);
   2078 
   2079                 _ = try parent_gz.addBreak(break_tag, continue_block, .void_value);
   2080                 return Zir.Inst.Ref.unreachable_value;
   2081             },
   2082             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   2083             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   2084             .defer_normal => {
   2085                 const defer_scope = scope.cast(Scope.Defer).?;
   2086                 scope = defer_scope.parent;
   2087                 try parent_gz.addDefer(defer_scope.index, defer_scope.len);
   2088             },
   2089             .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   2090             .namespace => break,
   2091             .top => unreachable,
   2092         }
   2093     }
   2094     if (break_label != 0) {
   2095         const label_name = try astgen.identifierTokenString(break_label);
   2096         return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
   2097     } else {
   2098         return astgen.failNode(node, "continue expression outside loop", .{});
   2099     }
   2100 }
   2101 
   2102 fn blockExpr(
   2103     gz: *GenZir,
   2104     scope: *Scope,
   2105     ri: ResultInfo,
   2106     block_node: Ast.Node.Index,
   2107     statements: []const Ast.Node.Index,
   2108 ) InnerError!Zir.Inst.Ref {
   2109     const tracy = trace(@src());
   2110     defer tracy.end();
   2111 
   2112     const astgen = gz.astgen;
   2113     const tree = astgen.tree;
   2114     const main_tokens = tree.nodes.items(.main_token);
   2115     const token_tags = tree.tokens.items(.tag);
   2116 
   2117     const lbrace = main_tokens[block_node];
   2118     if (token_tags[lbrace - 1] == .colon and
   2119         token_tags[lbrace - 2] == .identifier)
   2120     {
   2121         return labeledBlockExpr(gz, scope, ri, block_node, statements);
   2122     }
   2123 
   2124     if (!gz.force_comptime) {
   2125         // Since this block is unlabeled, its control flow is effectively linear and we
   2126         // can *almost* get away with inlining the block here. However, we actually need
   2127         // to preserve the .block for Sema, to properly pop the error return trace.
   2128 
   2129         const block_tag: Zir.Inst.Tag = .block;
   2130         const block_inst = try gz.makeBlockInst(block_tag, block_node);
   2131         try gz.instructions.append(astgen.gpa, block_inst);
   2132 
   2133         var block_scope = gz.makeSubBlock(scope);
   2134         defer block_scope.unstack();
   2135 
   2136         try blockExprStmts(&block_scope, &block_scope.base, statements);
   2137 
   2138         if (!block_scope.endsWithNoReturn()) {
   2139             // As our last action before the break, "pop" the error trace if needed
   2140             _ = try gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always);
   2141 
   2142             const break_tag: Zir.Inst.Tag = if (block_scope.force_comptime) .break_inline else .@"break";
   2143             _ = try block_scope.addBreak(break_tag, block_inst, .void_value);
   2144         }
   2145 
   2146         try block_scope.setBlockBody(block_inst);
   2147     } else {
   2148         var sub_gz = gz.makeSubBlock(scope);
   2149         try blockExprStmts(&sub_gz, &sub_gz.base, statements);
   2150     }
   2151 
   2152     return rvalue(gz, ri, .void_value, block_node);
   2153 }
   2154 
   2155 fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.TokenIndex) !void {
   2156     // Look for the label in the scope.
   2157     var scope = parent_scope;
   2158     while (true) {
   2159         switch (scope.tag) {
   2160             .gen_zir => {
   2161                 const gen_zir = scope.cast(GenZir).?;
   2162                 if (gen_zir.label) |prev_label| {
   2163                     if (try astgen.tokenIdentEql(label, prev_label.token)) {
   2164                         const label_name = try astgen.identifierTokenString(label);
   2165                         return astgen.failTokNotes(label, "redefinition of label '{s}'", .{
   2166                             label_name,
   2167                         }, &[_]u32{
   2168                             try astgen.errNoteTok(
   2169                                 prev_label.token,
   2170                                 "previous definition here",
   2171                                 .{},
   2172                             ),
   2173                         });
   2174                     }
   2175                 }
   2176                 scope = gen_zir.parent;
   2177             },
   2178             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   2179             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   2180             .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   2181             .namespace => break,
   2182             .top => unreachable,
   2183         }
   2184     }
   2185 }
   2186 
   2187 fn labeledBlockExpr(
   2188     gz: *GenZir,
   2189     parent_scope: *Scope,
   2190     ri: ResultInfo,
   2191     block_node: Ast.Node.Index,
   2192     statements: []const Ast.Node.Index,
   2193 ) InnerError!Zir.Inst.Ref {
   2194     const tracy = trace(@src());
   2195     defer tracy.end();
   2196 
   2197     const astgen = gz.astgen;
   2198     const tree = astgen.tree;
   2199     const main_tokens = tree.nodes.items(.main_token);
   2200     const token_tags = tree.tokens.items(.tag);
   2201 
   2202     const lbrace = main_tokens[block_node];
   2203     const label_token = lbrace - 2;
   2204     assert(token_tags[label_token] == .identifier);
   2205 
   2206     try astgen.checkLabelRedefinition(parent_scope, label_token);
   2207 
   2208     // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct
   2209     // so that break statements can reference it.
   2210     const block_tag: Zir.Inst.Tag = if (gz.force_comptime) .block_inline else .block;
   2211     const block_inst = try gz.makeBlockInst(block_tag, block_node);
   2212     try gz.instructions.append(astgen.gpa, block_inst);
   2213 
   2214     var block_scope = gz.makeSubBlock(parent_scope);
   2215     block_scope.label = GenZir.Label{
   2216         .token = label_token,
   2217         .block_inst = block_inst,
   2218     };
   2219     block_scope.setBreakResultInfo(ri);
   2220     defer block_scope.unstack();
   2221     defer block_scope.labeled_breaks.deinit(astgen.gpa);
   2222 
   2223     try blockExprStmts(&block_scope, &block_scope.base, statements);
   2224     if (!block_scope.endsWithNoReturn()) {
   2225         // As our last action before the return, "pop" the error trace if needed
   2226         _ = try gz.addRestoreErrRetIndex(.{ .block = block_inst }, .always);
   2227 
   2228         const break_tag: Zir.Inst.Tag = if (block_scope.force_comptime) .break_inline else .@"break";
   2229         _ = try block_scope.addBreak(break_tag, block_inst, .void_value);
   2230     }
   2231 
   2232     if (!block_scope.label.?.used) {
   2233         try astgen.appendErrorTok(label_token, "unused block label", .{});
   2234     }
   2235 
   2236     const zir_datas = gz.astgen.instructions.items(.data);
   2237     const zir_tags = gz.astgen.instructions.items(.tag);
   2238     const strat = ri.rl.strategy(&block_scope);
   2239     switch (strat.tag) {
   2240         .break_void => {
   2241             // The code took advantage of the result location as a pointer.
   2242             // Turn the break instruction operands into void.
   2243             for (block_scope.labeled_breaks.items) |br| {
   2244                 zir_datas[br.br].@"break".operand = .void_value;
   2245             }
   2246             try block_scope.setBlockBody(block_inst);
   2247 
   2248             return indexToRef(block_inst);
   2249         },
   2250         .break_operand => {
   2251             // All break operands are values that did not use the result location pointer
   2252             // (except for a single .store_to_block_ptr inst which we re-write here).
   2253             // The break instructions need to have their operands coerced if the
   2254             // block's result location is a `ty`. In this case we overwrite the
   2255             // `store_to_block_ptr` instruction with an `as` instruction and repurpose
   2256             // it as the break operand.
   2257             // This corresponds to similar code in `setCondBrPayloadElideBlockStorePtr`.
   2258             if (block_scope.rl_ty_inst != .none) {
   2259                 for (block_scope.labeled_breaks.items) |br| {
   2260                     // We expect the `store_to_block_ptr` to be created between 1-3 instructions
   2261                     // prior to the break.
   2262                     var search_index = br.search -| 3;
   2263                     while (search_index < br.search) : (search_index += 1) {
   2264                         if (zir_tags[search_index] == .store_to_block_ptr and
   2265                             zir_datas[search_index].bin.lhs == block_scope.rl_ptr)
   2266                         {
   2267                             zir_tags[search_index] = .as;
   2268                             zir_datas[search_index].bin = .{
   2269                                 .lhs = block_scope.rl_ty_inst,
   2270                                 .rhs = zir_datas[br.br].@"break".operand,
   2271                             };
   2272                             zir_datas[br.br].@"break".operand = indexToRef(search_index);
   2273                             break;
   2274                         }
   2275                     } else unreachable;
   2276                 }
   2277             }
   2278             try block_scope.setBlockBody(block_inst);
   2279             const block_ref = indexToRef(block_inst);
   2280             switch (ri.rl) {
   2281                 .ref => return block_ref,
   2282                 else => return rvalue(gz, ri, block_ref, block_node),
   2283             }
   2284         },
   2285     }
   2286 }
   2287 
   2288 fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Node.Index) !void {
   2289     const astgen = gz.astgen;
   2290     const tree = astgen.tree;
   2291     const node_tags = tree.nodes.items(.tag);
   2292     const node_data = tree.nodes.items(.data);
   2293 
   2294     if (statements.len == 0) return;
   2295 
   2296     try gz.addDbgBlockBegin();
   2297 
   2298     var block_arena = std.heap.ArenaAllocator.init(gz.astgen.gpa);
   2299     defer block_arena.deinit();
   2300     const block_arena_allocator = block_arena.allocator();
   2301 
   2302     var noreturn_src_node: Ast.Node.Index = 0;
   2303     var scope = parent_scope;
   2304     for (statements) |statement| {
   2305         if (noreturn_src_node != 0) {
   2306             try astgen.appendErrorNodeNotes(
   2307                 statement,
   2308                 "unreachable code",
   2309                 .{},
   2310                 &[_]u32{
   2311                     try astgen.errNoteNode(
   2312                         noreturn_src_node,
   2313                         "control flow is diverted here",
   2314                         .{},
   2315                     ),
   2316                 },
   2317             );
   2318         }
   2319         var inner_node = statement;
   2320         while (true) {
   2321             switch (node_tags[inner_node]) {
   2322                 // zig fmt: off
   2323             .global_var_decl  => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.globalVarDecl(statement)),
   2324             .local_var_decl   => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.localVarDecl(statement)),
   2325             .simple_var_decl  => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.simpleVarDecl(statement)),
   2326             .aligned_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.alignedVarDecl(statement)),
   2327 
   2328             .@"defer"    => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_normal),
   2329             .@"errdefer" => scope = try deferStmt(gz, scope, statement, block_arena_allocator, .defer_error),
   2330 
   2331             .assign => try assign(gz, scope, statement),
   2332 
   2333             .assign_shl => try assignShift(gz, scope, statement, .shl),
   2334             .assign_shr => try assignShift(gz, scope, statement, .shr),
   2335 
   2336             .assign_bit_and  => try assignOp(gz, scope, statement, .bit_and),
   2337             .assign_bit_or   => try assignOp(gz, scope, statement, .bit_or),
   2338             .assign_bit_xor  => try assignOp(gz, scope, statement, .xor),
   2339             .assign_div      => try assignOp(gz, scope, statement, .div),
   2340             .assign_sub      => try assignOp(gz, scope, statement, .sub),
   2341             .assign_sub_wrap => try assignOp(gz, scope, statement, .subwrap),
   2342             .assign_mod      => try assignOp(gz, scope, statement, .mod_rem),
   2343             .assign_add      => try assignOp(gz, scope, statement, .add),
   2344             .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap),
   2345             .assign_mul      => try assignOp(gz, scope, statement, .mul),
   2346             .assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap),
   2347 
   2348             .grouped_expression => {
   2349                 inner_node = node_data[statement].lhs;
   2350                 continue;
   2351             },
   2352 
   2353             .while_simple => _ = try whileExpr(gz, scope, .{ .rl = .discard }, inner_node, tree.whileSimple(inner_node), true),
   2354             .while_cont => _ = try whileExpr(gz, scope, .{ .rl = .discard }, inner_node, tree.whileCont(inner_node), true),
   2355             .@"while" => _ = try whileExpr(gz, scope, .{ .rl = .discard }, inner_node, tree.whileFull(inner_node), true),
   2356 
   2357             .for_simple => _ = try forExpr(gz, scope, .{ .rl = .discard }, inner_node, tree.forSimple(inner_node), true),
   2358             .@"for" => _ = try forExpr(gz, scope, .{ .rl = .discard }, inner_node, tree.forFull(inner_node), true),
   2359 
   2360             else => noreturn_src_node = try unusedResultExpr(gz, scope, inner_node),
   2361             // zig fmt: on
   2362             }
   2363             break;
   2364         }
   2365     }
   2366 
   2367     try gz.addDbgBlockEnd();
   2368 
   2369     try genDefers(gz, parent_scope, scope, .normal_only);
   2370     try checkUsed(gz, parent_scope, scope);
   2371 }
   2372 
   2373 /// Returns AST source node of the thing that is noreturn if the statement is
   2374 /// definitely `noreturn`. Otherwise returns 0.
   2375 fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) InnerError!Ast.Node.Index {
   2376     try emitDbgNode(gz, statement);
   2377     // We need to emit an error if the result is not `noreturn` or `void`, but
   2378     // we want to avoid adding the ZIR instruction if possible for performance.
   2379     const maybe_unused_result = try expr(gz, scope, .{ .rl = .none }, statement);
   2380     return addEnsureResult(gz, maybe_unused_result, statement);
   2381 }
   2382 
   2383 fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: Ast.Node.Index) InnerError!Ast.Node.Index {
   2384     var noreturn_src_node: Ast.Node.Index = 0;
   2385     const elide_check = if (refToIndex(maybe_unused_result)) |inst| b: {
   2386         // Note that this array becomes invalid after appending more items to it
   2387         // in the above while loop.
   2388         const zir_tags = gz.astgen.instructions.items(.tag);
   2389         switch (zir_tags[inst]) {
   2390             // For some instructions, modify the zir data
   2391             // so we can avoid a separate ensure_result_used instruction.
   2392             .call => {
   2393                 const extra_index = gz.astgen.instructions.items(.data)[inst].pl_node.payload_index;
   2394                 const slot = &gz.astgen.extra.items[extra_index];
   2395                 var flags = @bitCast(Zir.Inst.Call.Flags, slot.*);
   2396                 flags.ensure_result_used = true;
   2397                 slot.* = @bitCast(u32, flags);
   2398                 break :b true;
   2399             },
   2400             .builtin_call => {
   2401                 const extra_index = gz.astgen.instructions.items(.data)[inst].pl_node.payload_index;
   2402                 const slot = &gz.astgen.extra.items[extra_index];
   2403                 var flags = @bitCast(Zir.Inst.BuiltinCall.Flags, slot.*);
   2404                 flags.ensure_result_used = true;
   2405                 slot.* = @bitCast(u32, flags);
   2406                 break :b true;
   2407             },
   2408 
   2409             // ZIR instructions that might be a type other than `noreturn` or `void`.
   2410             .add,
   2411             .addwrap,
   2412             .add_sat,
   2413             .param,
   2414             .param_comptime,
   2415             .param_anytype,
   2416             .param_anytype_comptime,
   2417             .alloc,
   2418             .alloc_mut,
   2419             .alloc_comptime_mut,
   2420             .alloc_inferred,
   2421             .alloc_inferred_mut,
   2422             .alloc_inferred_comptime,
   2423             .alloc_inferred_comptime_mut,
   2424             .make_ptr_const,
   2425             .array_cat,
   2426             .array_mul,
   2427             .array_type,
   2428             .array_type_sentinel,
   2429             .elem_type_index,
   2430             .vector_type,
   2431             .indexable_ptr_len,
   2432             .anyframe_type,
   2433             .as,
   2434             .as_node,
   2435             .as_shift_operand,
   2436             .bit_and,
   2437             .bitcast,
   2438             .bit_or,
   2439             .block,
   2440             .block_inline,
   2441             .suspend_block,
   2442             .loop,
   2443             .bool_br_and,
   2444             .bool_br_or,
   2445             .bool_not,
   2446             .cmp_lt,
   2447             .cmp_lte,
   2448             .cmp_eq,
   2449             .cmp_gte,
   2450             .cmp_gt,
   2451             .cmp_neq,
   2452             .coerce_result_ptr,
   2453             .decl_ref,
   2454             .decl_val,
   2455             .load,
   2456             .div,
   2457             .elem_ptr,
   2458             .elem_val,
   2459             .elem_ptr_node,
   2460             .elem_ptr_imm,
   2461             .elem_val_node,
   2462             .field_ptr,
   2463             .field_ptr_init,
   2464             .field_val,
   2465             .field_call_bind,
   2466             .field_ptr_named,
   2467             .field_val_named,
   2468             .func,
   2469             .func_inferred,
   2470             .func_fancy,
   2471             .int,
   2472             .int_big,
   2473             .float,
   2474             .float128,
   2475             .int_type,
   2476             .is_non_null,
   2477             .is_non_null_ptr,
   2478             .is_non_err,
   2479             .is_non_err_ptr,
   2480             .mod_rem,
   2481             .mul,
   2482             .mulwrap,
   2483             .mul_sat,
   2484             .ref,
   2485             .shl,
   2486             .shl_sat,
   2487             .shr,
   2488             .str,
   2489             .sub,
   2490             .subwrap,
   2491             .sub_sat,
   2492             .negate,
   2493             .negate_wrap,
   2494             .typeof,
   2495             .typeof_builtin,
   2496             .xor,
   2497             .optional_type,
   2498             .optional_payload_safe,
   2499             .optional_payload_unsafe,
   2500             .optional_payload_safe_ptr,
   2501             .optional_payload_unsafe_ptr,
   2502             .err_union_payload_unsafe,
   2503             .err_union_payload_unsafe_ptr,
   2504             .err_union_code,
   2505             .err_union_code_ptr,
   2506             .ptr_type,
   2507             .overflow_arithmetic_ptr,
   2508             .enum_literal,
   2509             .merge_error_sets,
   2510             .error_union_type,
   2511             .bit_not,
   2512             .error_value,
   2513             .slice_start,
   2514             .slice_end,
   2515             .slice_sentinel,
   2516             .import,
   2517             .switch_block,
   2518             .switch_cond,
   2519             .switch_cond_ref,
   2520             .switch_capture,
   2521             .switch_capture_ref,
   2522             .switch_capture_multi,
   2523             .switch_capture_multi_ref,
   2524             .switch_capture_tag,
   2525             .struct_init_empty,
   2526             .struct_init,
   2527             .struct_init_ref,
   2528             .struct_init_anon,
   2529             .struct_init_anon_ref,
   2530             .array_init,
   2531             .array_init_anon,
   2532             .array_init_ref,
   2533             .array_init_anon_ref,
   2534             .union_init,
   2535             .field_type,
   2536             .field_type_ref,
   2537             .error_set_decl,
   2538             .error_set_decl_anon,
   2539             .error_set_decl_func,
   2540             .int_to_enum,
   2541             .enum_to_int,
   2542             .type_info,
   2543             .size_of,
   2544             .bit_size_of,
   2545             .log2_int_type,
   2546             .typeof_log2_int_type,
   2547             .ptr_to_int,
   2548             .align_of,
   2549             .bool_to_int,
   2550             .embed_file,
   2551             .error_name,
   2552             .sqrt,
   2553             .sin,
   2554             .cos,
   2555             .tan,
   2556             .exp,
   2557             .exp2,
   2558             .log,
   2559             .log2,
   2560             .log10,
   2561             .fabs,
   2562             .floor,
   2563             .ceil,
   2564             .trunc,
   2565             .round,
   2566             .tag_name,
   2567             .type_name,
   2568             .frame_type,
   2569             .frame_size,
   2570             .float_to_int,
   2571             .int_to_float,
   2572             .int_to_ptr,
   2573             .float_cast,
   2574             .int_cast,
   2575             .ptr_cast,
   2576             .truncate,
   2577             .align_cast,
   2578             .has_decl,
   2579             .has_field,
   2580             .clz,
   2581             .ctz,
   2582             .pop_count,
   2583             .byte_swap,
   2584             .bit_reverse,
   2585             .div_exact,
   2586             .div_floor,
   2587             .div_trunc,
   2588             .mod,
   2589             .rem,
   2590             .shl_exact,
   2591             .shr_exact,
   2592             .bit_offset_of,
   2593             .offset_of,
   2594             .splat,
   2595             .reduce,
   2596             .shuffle,
   2597             .atomic_load,
   2598             .atomic_rmw,
   2599             .mul_add,
   2600             .field_parent_ptr,
   2601             .max,
   2602             .min,
   2603             .c_import,
   2604             .@"resume",
   2605             .@"await",
   2606             .ret_err_value_code,
   2607             .closure_get,
   2608             .array_base_ptr,
   2609             .field_base_ptr,
   2610             .ret_ptr,
   2611             .ret_type,
   2612             .@"try",
   2613             .try_ptr,
   2614             //.try_inline,
   2615             //.try_ptr_inline,
   2616             => break :b false,
   2617 
   2618             .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) {
   2619                 .breakpoint,
   2620                 .fence,
   2621                 .set_align_stack,
   2622                 .set_float_mode,
   2623                 => break :b true,
   2624                 else => break :b false,
   2625             },
   2626 
   2627             // ZIR instructions that are always `noreturn`.
   2628             .@"break",
   2629             .break_inline,
   2630             .condbr,
   2631             .condbr_inline,
   2632             .compile_error,
   2633             .ret_node,
   2634             .ret_load,
   2635             .ret_implicit,
   2636             .ret_err_value,
   2637             .@"unreachable",
   2638             .repeat,
   2639             .repeat_inline,
   2640             .panic,
   2641             .panic_comptime,
   2642             .check_comptime_control_flow,
   2643             => {
   2644                 noreturn_src_node = statement;
   2645                 break :b true;
   2646             },
   2647 
   2648             // ZIR instructions that are always `void`.
   2649             .dbg_stmt,
   2650             .dbg_var_ptr,
   2651             .dbg_var_val,
   2652             .dbg_block_begin,
   2653             .dbg_block_end,
   2654             .ensure_result_used,
   2655             .ensure_result_non_error,
   2656             .ensure_err_union_payload_void,
   2657             .@"export",
   2658             .export_value,
   2659             .set_eval_branch_quota,
   2660             .atomic_store,
   2661             .store,
   2662             .store_node,
   2663             .store_to_block_ptr,
   2664             .store_to_inferred_ptr,
   2665             .resolve_inferred_alloc,
   2666             .validate_struct_init,
   2667             .validate_struct_init_comptime,
   2668             .validate_array_init,
   2669             .validate_array_init_comptime,
   2670             .set_cold,
   2671             .set_runtime_safety,
   2672             .closure_capture,
   2673             .memcpy,
   2674             .memset,
   2675             .validate_array_init_ty,
   2676             .validate_struct_init_ty,
   2677             .validate_deref,
   2678             .save_err_ret_index,
   2679             .restore_err_ret_index,
   2680             => break :b true,
   2681 
   2682             .@"defer" => unreachable,
   2683             .defer_err_code => unreachable,
   2684         }
   2685     } else switch (maybe_unused_result) {
   2686         .none => unreachable,
   2687 
   2688         .unreachable_value => b: {
   2689             noreturn_src_node = statement;
   2690             break :b true;
   2691         },
   2692 
   2693         .void_value => true,
   2694 
   2695         else => false,
   2696     };
   2697     if (!elide_check) {
   2698         _ = try gz.addUnNode(.ensure_result_used, maybe_unused_result, statement);
   2699     }
   2700     return noreturn_src_node;
   2701 }
   2702 
   2703 fn countDefers(outer_scope: *Scope, inner_scope: *Scope) struct {
   2704     have_any: bool,
   2705     have_normal: bool,
   2706     have_err: bool,
   2707     need_err_code: bool,
   2708 } {
   2709     var have_normal = false;
   2710     var have_err = false;
   2711     var need_err_code = false;
   2712     var scope = inner_scope;
   2713     while (scope != outer_scope) {
   2714         switch (scope.tag) {
   2715             .gen_zir => scope = scope.cast(GenZir).?.parent,
   2716             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   2717             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   2718             .defer_normal => {
   2719                 const defer_scope = scope.cast(Scope.Defer).?;
   2720                 scope = defer_scope.parent;
   2721 
   2722                 have_normal = true;
   2723             },
   2724             .defer_error => {
   2725                 const defer_scope = scope.cast(Scope.Defer).?;
   2726                 scope = defer_scope.parent;
   2727 
   2728                 have_err = true;
   2729 
   2730                 const have_err_payload = defer_scope.remapped_err_code != 0;
   2731                 need_err_code = need_err_code or have_err_payload;
   2732             },
   2733             .namespace => unreachable,
   2734             .top => unreachable,
   2735         }
   2736     }
   2737     return .{
   2738         .have_any = have_normal or have_err,
   2739         .have_normal = have_normal,
   2740         .have_err = have_err,
   2741         .need_err_code = need_err_code,
   2742     };
   2743 }
   2744 
   2745 const DefersToEmit = union(enum) {
   2746     both: Zir.Inst.Ref, // err code
   2747     both_sans_err,
   2748     normal_only,
   2749 };
   2750 
   2751 fn genDefers(
   2752     gz: *GenZir,
   2753     outer_scope: *Scope,
   2754     inner_scope: *Scope,
   2755     which_ones: DefersToEmit,
   2756 ) InnerError!void {
   2757     const gpa = gz.astgen.gpa;
   2758 
   2759     var scope = inner_scope;
   2760     while (scope != outer_scope) {
   2761         switch (scope.tag) {
   2762             .gen_zir => scope = scope.cast(GenZir).?.parent,
   2763             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   2764             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   2765             .defer_normal => {
   2766                 const defer_scope = scope.cast(Scope.Defer).?;
   2767                 scope = defer_scope.parent;
   2768                 try gz.addDefer(defer_scope.index, defer_scope.len);
   2769             },
   2770             .defer_error => {
   2771                 const defer_scope = scope.cast(Scope.Defer).?;
   2772                 scope = defer_scope.parent;
   2773                 switch (which_ones) {
   2774                     .both_sans_err => {
   2775                         try gz.addDefer(defer_scope.index, defer_scope.len);
   2776                     },
   2777                     .both => |err_code| {
   2778                         if (defer_scope.remapped_err_code == 0) {
   2779                             try gz.addDefer(defer_scope.index, defer_scope.len);
   2780                         } else {
   2781                             try gz.instructions.ensureUnusedCapacity(gpa, 1);
   2782                             try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
   2783 
   2784                             const payload_index = try gz.astgen.addExtra(Zir.Inst.DeferErrCode{
   2785                                 .remapped_err_code = defer_scope.remapped_err_code,
   2786                                 .index = defer_scope.index,
   2787                                 .len = defer_scope.len,
   2788                             });
   2789                             const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
   2790                             gz.astgen.instructions.appendAssumeCapacity(.{
   2791                                 .tag = .defer_err_code,
   2792                                 .data = .{ .defer_err_code = .{
   2793                                     .err_code = err_code,
   2794                                     .payload_index = payload_index,
   2795                                 } },
   2796                             });
   2797                             gz.instructions.appendAssumeCapacity(new_index);
   2798                         }
   2799                     },
   2800                     .normal_only => continue,
   2801                 }
   2802             },
   2803             .namespace => unreachable,
   2804             .top => unreachable,
   2805         }
   2806     }
   2807 }
   2808 
   2809 fn checkUsed(gz: *GenZir, outer_scope: *Scope, inner_scope: *Scope) InnerError!void {
   2810     const astgen = gz.astgen;
   2811 
   2812     var scope = inner_scope;
   2813     while (scope != outer_scope) {
   2814         switch (scope.tag) {
   2815             .gen_zir => scope = scope.cast(GenZir).?.parent,
   2816             .local_val => {
   2817                 const s = scope.cast(Scope.LocalVal).?;
   2818                 if (s.used == 0 and s.discarded == 0) {
   2819                     try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
   2820                 } else if (s.used != 0 and s.discarded != 0) {
   2821                     try astgen.appendErrorTokNotes(s.discarded, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{
   2822                         try gz.astgen.errNoteTok(s.used, "used here", .{}),
   2823                     });
   2824                 }
   2825                 scope = s.parent;
   2826             },
   2827             .local_ptr => {
   2828                 const s = scope.cast(Scope.LocalPtr).?;
   2829                 if (s.used == 0 and s.discarded == 0) {
   2830                     try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
   2831                 } else if (s.used != 0 and s.discarded != 0) {
   2832                     try astgen.appendErrorTokNotes(s.discarded, "pointless discard of {s}", .{@tagName(s.id_cat)}, &[_]u32{
   2833                         try gz.astgen.errNoteTok(s.used, "used here", .{}),
   2834                     });
   2835                 }
   2836                 scope = s.parent;
   2837             },
   2838             .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   2839             .namespace => unreachable,
   2840             .top => unreachable,
   2841         }
   2842     }
   2843 }
   2844 
   2845 fn deferStmt(
   2846     gz: *GenZir,
   2847     scope: *Scope,
   2848     node: Ast.Node.Index,
   2849     block_arena: Allocator,
   2850     scope_tag: Scope.Tag,
   2851 ) InnerError!*Scope {
   2852     var defer_gen = gz.makeSubBlock(scope);
   2853     defer_gen.cur_defer_node = node;
   2854     defer_gen.any_defer_node = node;
   2855     defer defer_gen.unstack();
   2856 
   2857     const tree = gz.astgen.tree;
   2858     const node_datas = tree.nodes.items(.data);
   2859     const expr_node = node_datas[node].rhs;
   2860 
   2861     const payload_token = node_datas[node].lhs;
   2862     var local_val_scope: Scope.LocalVal = undefined;
   2863     var remapped_err_code: Zir.Inst.Index = 0;
   2864     const have_err_code = scope_tag == .defer_error and payload_token != 0;
   2865     const sub_scope = if (!have_err_code) &defer_gen.base else blk: {
   2866         try gz.addDbgBlockBegin();
   2867         const ident_name = try gz.astgen.identAsString(payload_token);
   2868         remapped_err_code = @intCast(u32, try gz.astgen.instructions.addOne(gz.astgen.gpa));
   2869         const remapped_err_code_ref = Zir.indexToRef(remapped_err_code);
   2870         local_val_scope = .{
   2871             .parent = &defer_gen.base,
   2872             .gen_zir = gz,
   2873             .name = ident_name,
   2874             .inst = remapped_err_code_ref,
   2875             .token_src = payload_token,
   2876             .id_cat = .capture,
   2877         };
   2878         try gz.addDbgVar(.dbg_var_val, ident_name, remapped_err_code_ref);
   2879         break :blk &local_val_scope.base;
   2880     };
   2881     _ = try unusedResultExpr(&defer_gen, sub_scope, expr_node);
   2882     try checkUsed(gz, scope, sub_scope);
   2883     if (have_err_code) try gz.addDbgBlockEnd();
   2884     _ = try defer_gen.addBreak(.break_inline, 0, .void_value);
   2885 
   2886     const body = defer_gen.instructionsSlice();
   2887     const body_len = gz.astgen.countBodyLenAfterFixups(body);
   2888 
   2889     const index = @intCast(u32, gz.astgen.extra.items.len);
   2890     try gz.astgen.extra.ensureUnusedCapacity(gz.astgen.gpa, body_len);
   2891     gz.astgen.appendBodyWithFixups(body);
   2892 
   2893     const defer_scope = try block_arena.create(Scope.Defer);
   2894 
   2895     defer_scope.* = .{
   2896         .base = .{ .tag = scope_tag },
   2897         .parent = scope,
   2898         .index = index,
   2899         .len = body_len,
   2900         .remapped_err_code = remapped_err_code,
   2901     };
   2902     return &defer_scope.base;
   2903 }
   2904 
   2905 fn varDecl(
   2906     gz: *GenZir,
   2907     scope: *Scope,
   2908     node: Ast.Node.Index,
   2909     block_arena: Allocator,
   2910     var_decl: Ast.full.VarDecl,
   2911 ) InnerError!*Scope {
   2912     try emitDbgNode(gz, node);
   2913     const astgen = gz.astgen;
   2914     const tree = astgen.tree;
   2915     const token_tags = tree.tokens.items(.tag);
   2916     const main_tokens = tree.nodes.items(.main_token);
   2917 
   2918     const name_token = var_decl.ast.mut_token + 1;
   2919     const ident_name_raw = tree.tokenSlice(name_token);
   2920     if (mem.eql(u8, ident_name_raw, "_")) {
   2921         return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{});
   2922     }
   2923     const ident_name = try astgen.identAsString(name_token);
   2924 
   2925     try astgen.detectLocalShadowing(
   2926         scope,
   2927         ident_name,
   2928         name_token,
   2929         ident_name_raw,
   2930         if (token_tags[var_decl.ast.mut_token] == .keyword_const) .@"local constant" else .@"local variable",
   2931     );
   2932 
   2933     if (var_decl.ast.init_node == 0) {
   2934         return astgen.failNode(node, "variables must be initialized", .{});
   2935     }
   2936 
   2937     if (var_decl.ast.addrspace_node != 0) {
   2938         return astgen.failTok(main_tokens[var_decl.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw});
   2939     }
   2940 
   2941     if (var_decl.ast.section_node != 0) {
   2942         return astgen.failTok(main_tokens[var_decl.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw});
   2943     }
   2944 
   2945     const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node != 0)
   2946         try expr(gz, scope, align_ri, var_decl.ast.align_node)
   2947     else
   2948         .none;
   2949 
   2950     switch (token_tags[var_decl.ast.mut_token]) {
   2951         .keyword_const => {
   2952             if (var_decl.comptime_token) |comptime_token| {
   2953                 try astgen.appendErrorTok(comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
   2954             }
   2955 
   2956             // Depending on the type of AST the initialization expression is, we may need an lvalue
   2957             // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
   2958             // the variable, no memory location needed.
   2959             const type_node = var_decl.ast.type_node;
   2960             if (align_inst == .none and
   2961                 !nodeMayNeedMemoryLocation(tree, var_decl.ast.init_node, type_node != 0))
   2962             {
   2963                 const result_info: ResultInfo = if (type_node != 0) .{
   2964                     .rl = .{ .ty = try typeExpr(gz, scope, type_node) },
   2965                     .ctx = .const_init,
   2966                 } else .{ .rl = .none, .ctx = .const_init };
   2967                 const prev_anon_name_strategy = gz.anon_name_strategy;
   2968                 gz.anon_name_strategy = .dbg_var;
   2969                 const init_inst = try reachableExpr(gz, scope, result_info, var_decl.ast.init_node, node);
   2970                 gz.anon_name_strategy = prev_anon_name_strategy;
   2971 
   2972                 try gz.addDbgVar(.dbg_var_val, ident_name, init_inst);
   2973 
   2974                 // The const init expression may have modified the error return trace, so signal
   2975                 // to Sema that it should save the new index for restoring later.
   2976                 if (nodeMayAppendToErrorTrace(tree, var_decl.ast.init_node))
   2977                     _ = try gz.addSaveErrRetIndex(.{ .if_of_error_type = init_inst });
   2978 
   2979                 const sub_scope = try block_arena.create(Scope.LocalVal);
   2980                 sub_scope.* = .{
   2981                     .parent = scope,
   2982                     .gen_zir = gz,
   2983                     .name = ident_name,
   2984                     .inst = init_inst,
   2985                     .token_src = name_token,
   2986                     .id_cat = .@"local constant",
   2987                 };
   2988                 return &sub_scope.base;
   2989             }
   2990 
   2991             const is_comptime = gz.force_comptime or
   2992                 tree.nodes.items(.tag)[var_decl.ast.init_node] == .@"comptime";
   2993 
   2994             // Detect whether the initialization expression actually uses the
   2995             // result location pointer.
   2996             var init_scope = gz.makeSubBlock(scope);
   2997             // we may add more instructions to gz before stacking init_scope
   2998             init_scope.instructions_top = GenZir.unstacked_top;
   2999             init_scope.anon_name_strategy = .dbg_var;
   3000             defer init_scope.unstack();
   3001 
   3002             var resolve_inferred_alloc: Zir.Inst.Ref = .none;
   3003             var opt_type_inst: Zir.Inst.Ref = .none;
   3004             if (type_node != 0) {
   3005                 const type_inst = try typeExpr(gz, &init_scope.base, type_node);
   3006                 opt_type_inst = type_inst;
   3007                 if (align_inst == .none) {
   3008                     init_scope.instructions_top = gz.instructions.items.len;
   3009                     init_scope.rl_ptr = try init_scope.addUnNode(.alloc, type_inst, node);
   3010                 } else {
   3011                     init_scope.rl_ptr = try gz.addAllocExtended(.{
   3012                         .node = node,
   3013                         .type_inst = type_inst,
   3014                         .align_inst = align_inst,
   3015                         .is_const = true,
   3016                         .is_comptime = is_comptime,
   3017                     });
   3018                     init_scope.instructions_top = gz.instructions.items.len;
   3019                 }
   3020                 init_scope.rl_ty_inst = type_inst;
   3021             } else {
   3022                 const alloc = if (align_inst == .none) alloc: {
   3023                     init_scope.instructions_top = gz.instructions.items.len;
   3024                     const tag: Zir.Inst.Tag = if (is_comptime)
   3025                         .alloc_inferred_comptime
   3026                     else
   3027                         .alloc_inferred;
   3028                     break :alloc try init_scope.addNode(tag, node);
   3029                 } else alloc: {
   3030                     const ref = try gz.addAllocExtended(.{
   3031                         .node = node,
   3032                         .type_inst = .none,
   3033                         .align_inst = align_inst,
   3034                         .is_const = true,
   3035                         .is_comptime = is_comptime,
   3036                     });
   3037                     init_scope.instructions_top = gz.instructions.items.len;
   3038                     break :alloc ref;
   3039                 };
   3040                 resolve_inferred_alloc = alloc;
   3041                 init_scope.rl_ptr = alloc;
   3042                 init_scope.rl_ty_inst = .none;
   3043             }
   3044             const init_result_info: ResultInfo = .{ .rl = .{ .block_ptr = &init_scope }, .ctx = .const_init };
   3045             const init_inst = try reachableExpr(&init_scope, &init_scope.base, init_result_info, var_decl.ast.init_node, node);
   3046 
   3047             // The const init expression may have modified the error return trace, so signal
   3048             // to Sema that it should save the new index for restoring later.
   3049             if (nodeMayAppendToErrorTrace(tree, var_decl.ast.init_node))
   3050                 _ = try init_scope.addSaveErrRetIndex(.{ .if_of_error_type = init_inst });
   3051 
   3052             const zir_tags = astgen.instructions.items(.tag);
   3053             const zir_datas = astgen.instructions.items(.data);
   3054 
   3055             if (align_inst == .none and init_scope.rvalue_rl_count == 1) {
   3056                 // Result location pointer not used. We don't need an alloc for this
   3057                 // const local, and type inference becomes trivial.
   3058                 // Implicitly move the init_scope instructions into the parent scope,
   3059                 // then elide the alloc instruction and the store_to_block_ptr instruction.
   3060                 var src = init_scope.instructions_top;
   3061                 var dst = src;
   3062                 init_scope.instructions_top = GenZir.unstacked_top;
   3063                 while (src < gz.instructions.items.len) : (src += 1) {
   3064                     const src_inst = gz.instructions.items[src];
   3065                     if (indexToRef(src_inst) == init_scope.rl_ptr) continue;
   3066                     if (zir_tags[src_inst] == .store_to_block_ptr) {
   3067                         if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) continue;
   3068                     }
   3069                     gz.instructions.items[dst] = src_inst;
   3070                     dst += 1;
   3071                 }
   3072                 gz.instructions.items.len = dst;
   3073 
   3074                 // In case the result location did not do the coercion
   3075                 // for us so we must do it here.
   3076                 const coerced_init = if (opt_type_inst != .none)
   3077                     try gz.addBin(.as, opt_type_inst, init_inst)
   3078                 else
   3079                     init_inst;
   3080 
   3081                 try gz.addDbgVar(.dbg_var_val, ident_name, coerced_init);
   3082 
   3083                 const sub_scope = try block_arena.create(Scope.LocalVal);
   3084                 sub_scope.* = .{
   3085                     .parent = scope,
   3086                     .gen_zir = gz,
   3087                     .name = ident_name,
   3088                     .inst = coerced_init,
   3089                     .token_src = name_token,
   3090                     .id_cat = .@"local constant",
   3091                 };
   3092                 return &sub_scope.base;
   3093             }
   3094             // The initialization expression took advantage of the result location
   3095             // of the const local. In this case we will create an alloc and a LocalPtr for it.
   3096             // Implicitly move the init_scope instructions into the parent scope, then swap
   3097             // store_to_block_ptr for store_to_inferred_ptr.
   3098 
   3099             var src = init_scope.instructions_top;
   3100             init_scope.instructions_top = GenZir.unstacked_top;
   3101             while (src < gz.instructions.items.len) : (src += 1) {
   3102                 const src_inst = gz.instructions.items[src];
   3103                 if (zir_tags[src_inst] == .store_to_block_ptr) {
   3104                     if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) {
   3105                         if (type_node != 0) {
   3106                             zir_tags[src_inst] = .store;
   3107                         } else {
   3108                             zir_tags[src_inst] = .store_to_inferred_ptr;
   3109                         }
   3110                     }
   3111                 }
   3112             }
   3113             if (resolve_inferred_alloc != .none) {
   3114                 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
   3115             }
   3116             const const_ptr = try gz.addUnNode(.make_ptr_const, init_scope.rl_ptr, node);
   3117 
   3118             try gz.addDbgVar(.dbg_var_ptr, ident_name, const_ptr);
   3119 
   3120             const sub_scope = try block_arena.create(Scope.LocalPtr);
   3121             sub_scope.* = .{
   3122                 .parent = scope,
   3123                 .gen_zir = gz,
   3124                 .name = ident_name,
   3125                 .ptr = const_ptr,
   3126                 .token_src = name_token,
   3127                 .maybe_comptime = true,
   3128                 .id_cat = .@"local constant",
   3129             };
   3130             return &sub_scope.base;
   3131         },
   3132         .keyword_var => {
   3133             const old_rl_ty_inst = gz.rl_ty_inst;
   3134             defer gz.rl_ty_inst = old_rl_ty_inst;
   3135 
   3136             const is_comptime = var_decl.comptime_token != null or gz.force_comptime;
   3137             var resolve_inferred_alloc: Zir.Inst.Ref = .none;
   3138             const var_data: struct {
   3139                 result_info: ResultInfo,
   3140                 alloc: Zir.Inst.Ref,
   3141             } = if (var_decl.ast.type_node != 0) a: {
   3142                 const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node);
   3143                 const alloc = alloc: {
   3144                     if (align_inst == .none) {
   3145                         const tag: Zir.Inst.Tag = if (is_comptime)
   3146                             .alloc_comptime_mut
   3147                         else
   3148                             .alloc_mut;
   3149                         break :alloc try gz.addUnNode(tag, type_inst, node);
   3150                     } else {
   3151                         break :alloc try gz.addAllocExtended(.{
   3152                             .node = node,
   3153                             .type_inst = type_inst,
   3154                             .align_inst = align_inst,
   3155                             .is_const = false,
   3156                             .is_comptime = is_comptime,
   3157                         });
   3158                     }
   3159                 };
   3160                 gz.rl_ty_inst = type_inst;
   3161                 break :a .{ .alloc = alloc, .result_info = .{ .rl = .{ .ptr = .{ .inst = alloc } } } };
   3162             } else a: {
   3163                 const alloc = alloc: {
   3164                     if (align_inst == .none) {
   3165                         const tag: Zir.Inst.Tag = if (is_comptime)
   3166                             .alloc_inferred_comptime_mut
   3167                         else
   3168                             .alloc_inferred_mut;
   3169                         break :alloc try gz.addNode(tag, node);
   3170                     } else {
   3171                         break :alloc try gz.addAllocExtended(.{
   3172                             .node = node,
   3173                             .type_inst = .none,
   3174                             .align_inst = align_inst,
   3175                             .is_const = false,
   3176                             .is_comptime = is_comptime,
   3177                         });
   3178                     }
   3179                 };
   3180                 gz.rl_ty_inst = .none;
   3181                 resolve_inferred_alloc = alloc;
   3182                 break :a .{ .alloc = alloc, .result_info = .{ .rl = .{ .inferred_ptr = alloc } } };
   3183             };
   3184             const prev_anon_name_strategy = gz.anon_name_strategy;
   3185             gz.anon_name_strategy = .dbg_var;
   3186             _ = try reachableExprComptime(gz, scope, var_data.result_info, var_decl.ast.init_node, node, is_comptime);
   3187             gz.anon_name_strategy = prev_anon_name_strategy;
   3188             if (resolve_inferred_alloc != .none) {
   3189                 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
   3190             }
   3191 
   3192             try gz.addDbgVar(.dbg_var_ptr, ident_name, var_data.alloc);
   3193 
   3194             const sub_scope = try block_arena.create(Scope.LocalPtr);
   3195             sub_scope.* = .{
   3196                 .parent = scope,
   3197                 .gen_zir = gz,
   3198                 .name = ident_name,
   3199                 .ptr = var_data.alloc,
   3200                 .token_src = name_token,
   3201                 .maybe_comptime = is_comptime,
   3202                 .id_cat = .@"local variable",
   3203             };
   3204             return &sub_scope.base;
   3205         },
   3206         else => unreachable,
   3207     }
   3208 }
   3209 
   3210 fn emitDbgNode(gz: *GenZir, node: Ast.Node.Index) !void {
   3211     // The instruction emitted here is for debugging runtime code.
   3212     // If the current block will be evaluated only during semantic analysis
   3213     // then no dbg_stmt ZIR instruction is needed.
   3214     if (gz.force_comptime) return;
   3215 
   3216     const astgen = gz.astgen;
   3217     astgen.advanceSourceCursorToNode(node);
   3218     const line = astgen.source_line - gz.decl_line;
   3219     const column = astgen.source_column;
   3220 
   3221     if (gz.instructions.items.len > 0) {
   3222         const last = gz.instructions.items[gz.instructions.items.len - 1];
   3223         const zir_tags = astgen.instructions.items(.tag);
   3224         if (zir_tags[last] == .dbg_stmt) {
   3225             const zir_datas = astgen.instructions.items(.data);
   3226             zir_datas[last].dbg_stmt = .{
   3227                 .line = line,
   3228                 .column = column,
   3229             };
   3230             return;
   3231         }
   3232     }
   3233 
   3234     _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
   3235         .dbg_stmt = .{
   3236             .line = line,
   3237             .column = column,
   3238         },
   3239     } });
   3240 }
   3241 
   3242 fn assign(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void {
   3243     try emitDbgNode(gz, infix_node);
   3244     const astgen = gz.astgen;
   3245     const tree = astgen.tree;
   3246     const node_datas = tree.nodes.items(.data);
   3247     const main_tokens = tree.nodes.items(.main_token);
   3248     const node_tags = tree.nodes.items(.tag);
   3249 
   3250     const lhs = node_datas[infix_node].lhs;
   3251     const rhs = node_datas[infix_node].rhs;
   3252     if (node_tags[lhs] == .identifier) {
   3253         // This intentionally does not support `@"_"` syntax.
   3254         const ident_name = tree.tokenSlice(main_tokens[lhs]);
   3255         if (mem.eql(u8, ident_name, "_")) {
   3256             _ = try expr(gz, scope, .{ .rl = .discard, .ctx = .assignment }, rhs);
   3257             return;
   3258         }
   3259     }
   3260     const lvalue = try lvalExpr(gz, scope, lhs);
   3261     _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{
   3262         .inst = lvalue,
   3263         .src_node = infix_node,
   3264     } } }, rhs);
   3265 }
   3266 
   3267 fn assignOp(
   3268     gz: *GenZir,
   3269     scope: *Scope,
   3270     infix_node: Ast.Node.Index,
   3271     op_inst_tag: Zir.Inst.Tag,
   3272 ) InnerError!void {
   3273     try emitDbgNode(gz, infix_node);
   3274     const astgen = gz.astgen;
   3275     const tree = astgen.tree;
   3276     const node_datas = tree.nodes.items(.data);
   3277 
   3278     const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
   3279 
   3280     var line: u32 = undefined;
   3281     var column: u32 = undefined;
   3282     switch (op_inst_tag) {
   3283         .add, .sub, .mul, .div, .mod_rem => {
   3284             maybeAdvanceSourceCursorToMainToken(gz, infix_node);
   3285             line = gz.astgen.source_line - gz.decl_line;
   3286             column = gz.astgen.source_column;
   3287         },
   3288         else => {},
   3289     }
   3290     const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
   3291     const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node);
   3292     const rhs = try expr(gz, scope, .{ .rl = .{ .coerced_ty = lhs_type } }, node_datas[infix_node].rhs);
   3293 
   3294     switch (op_inst_tag) {
   3295         .add, .sub, .mul, .div, .mod_rem => {
   3296             try emitDbgStmt(gz, line, column);
   3297         },
   3298         else => {},
   3299     }
   3300     const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{
   3301         .lhs = lhs,
   3302         .rhs = rhs,
   3303     });
   3304     _ = try gz.addBin(.store, lhs_ptr, result);
   3305 }
   3306 
   3307 fn assignShift(
   3308     gz: *GenZir,
   3309     scope: *Scope,
   3310     infix_node: Ast.Node.Index,
   3311     op_inst_tag: Zir.Inst.Tag,
   3312 ) InnerError!void {
   3313     try emitDbgNode(gz, infix_node);
   3314     const astgen = gz.astgen;
   3315     const tree = astgen.tree;
   3316     const node_datas = tree.nodes.items(.data);
   3317 
   3318     const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
   3319     const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
   3320     const rhs_type = try gz.addUnNode(.typeof_log2_int_type, lhs, infix_node);
   3321     const rhs = try expr(gz, scope, .{ .rl = .{ .ty = rhs_type } }, node_datas[infix_node].rhs);
   3322 
   3323     const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{
   3324         .lhs = lhs,
   3325         .rhs = rhs,
   3326     });
   3327     _ = try gz.addBin(.store, lhs_ptr, result);
   3328 }
   3329 
   3330 fn assignShiftSat(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void {
   3331     try emitDbgNode(gz, infix_node);
   3332     const astgen = gz.astgen;
   3333     const tree = astgen.tree;
   3334     const node_datas = tree.nodes.items(.data);
   3335 
   3336     const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
   3337     const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
   3338     // Saturating shift-left allows any integer type for both the LHS and RHS.
   3339     const rhs = try expr(gz, scope, .{ .rl = .none }, node_datas[infix_node].rhs);
   3340 
   3341     const result = try gz.addPlNode(.shl_sat, infix_node, Zir.Inst.Bin{
   3342         .lhs = lhs,
   3343         .rhs = rhs,
   3344     });
   3345     _ = try gz.addBin(.store, lhs_ptr, result);
   3346 }
   3347 
   3348 fn ptrType(
   3349     gz: *GenZir,
   3350     scope: *Scope,
   3351     ri: ResultInfo,
   3352     node: Ast.Node.Index,
   3353     ptr_info: Ast.full.PtrType,
   3354 ) InnerError!Zir.Inst.Ref {
   3355     if (ptr_info.size == .C and ptr_info.allowzero_token != null) {
   3356         return gz.astgen.failTok(ptr_info.allowzero_token.?, "C pointers always allow address zero", .{});
   3357     }
   3358 
   3359     const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type);
   3360 
   3361     var sentinel_ref: Zir.Inst.Ref = .none;
   3362     var align_ref: Zir.Inst.Ref = .none;
   3363     var addrspace_ref: Zir.Inst.Ref = .none;
   3364     var bit_start_ref: Zir.Inst.Ref = .none;
   3365     var bit_end_ref: Zir.Inst.Ref = .none;
   3366     var trailing_count: u32 = 0;
   3367 
   3368     if (ptr_info.ast.sentinel != 0) {
   3369         sentinel_ref = try expr(gz, scope, .{ .rl = .{ .ty = elem_type } }, ptr_info.ast.sentinel);
   3370         trailing_count += 1;
   3371     }
   3372     if (ptr_info.ast.align_node != 0) {
   3373         align_ref = try expr(gz, scope, coerced_align_ri, ptr_info.ast.align_node);
   3374         trailing_count += 1;
   3375     }
   3376     if (ptr_info.ast.addrspace_node != 0) {
   3377         addrspace_ref = try expr(gz, scope, .{ .rl = .{ .ty = .address_space_type } }, ptr_info.ast.addrspace_node);
   3378         trailing_count += 1;
   3379     }
   3380     if (ptr_info.ast.bit_range_start != 0) {
   3381         assert(ptr_info.ast.bit_range_end != 0);
   3382         bit_start_ref = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, ptr_info.ast.bit_range_start);
   3383         bit_end_ref = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, ptr_info.ast.bit_range_end);
   3384         trailing_count += 2;
   3385     }
   3386 
   3387     const gpa = gz.astgen.gpa;
   3388     try gz.instructions.ensureUnusedCapacity(gpa, 1);
   3389     try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
   3390     try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.PtrType).Struct.fields.len +
   3391         trailing_count);
   3392 
   3393     const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.PtrType{
   3394         .elem_type = elem_type,
   3395         .src_node = gz.nodeIndexToRelative(node),
   3396     });
   3397     if (sentinel_ref != .none) {
   3398         gz.astgen.extra.appendAssumeCapacity(@enumToInt(sentinel_ref));
   3399     }
   3400     if (align_ref != .none) {
   3401         gz.astgen.extra.appendAssumeCapacity(@enumToInt(align_ref));
   3402     }
   3403     if (addrspace_ref != .none) {
   3404         gz.astgen.extra.appendAssumeCapacity(@enumToInt(addrspace_ref));
   3405     }
   3406     if (bit_start_ref != .none) {
   3407         gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_start_ref));
   3408         gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_end_ref));
   3409     }
   3410 
   3411     const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
   3412     const result = indexToRef(new_index);
   3413     gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{
   3414         .ptr_type = .{
   3415             .flags = .{
   3416                 .is_allowzero = ptr_info.allowzero_token != null,
   3417                 .is_mutable = ptr_info.const_token == null,
   3418                 .is_volatile = ptr_info.volatile_token != null,
   3419                 .has_sentinel = sentinel_ref != .none,
   3420                 .has_align = align_ref != .none,
   3421                 .has_addrspace = addrspace_ref != .none,
   3422                 .has_bit_range = bit_start_ref != .none,
   3423             },
   3424             .size = ptr_info.size,
   3425             .payload_index = payload_index,
   3426         },
   3427     } });
   3428     gz.instructions.appendAssumeCapacity(new_index);
   3429 
   3430     return rvalue(gz, ri, result, node);
   3431 }
   3432 
   3433 fn arrayType(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) !Zir.Inst.Ref {
   3434     const astgen = gz.astgen;
   3435     const tree = astgen.tree;
   3436     const node_datas = tree.nodes.items(.data);
   3437     const node_tags = tree.nodes.items(.tag);
   3438     const main_tokens = tree.nodes.items(.main_token);
   3439 
   3440     const len_node = node_datas[node].lhs;
   3441     if (node_tags[len_node] == .identifier and
   3442         mem.eql(u8, tree.tokenSlice(main_tokens[len_node]), "_"))
   3443     {
   3444         return astgen.failNode(len_node, "unable to infer array size", .{});
   3445     }
   3446     const len = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, len_node);
   3447     const elem_type = try typeExpr(gz, scope, node_datas[node].rhs);
   3448 
   3449     const result = try gz.addPlNode(.array_type, node, Zir.Inst.Bin{
   3450         .lhs = len,
   3451         .rhs = elem_type,
   3452     });
   3453     return rvalue(gz, ri, result, node);
   3454 }
   3455 
   3456 fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) !Zir.Inst.Ref {
   3457     const astgen = gz.astgen;
   3458     const tree = astgen.tree;
   3459     const node_datas = tree.nodes.items(.data);
   3460     const node_tags = tree.nodes.items(.tag);
   3461     const main_tokens = tree.nodes.items(.main_token);
   3462     const extra = tree.extraData(node_datas[node].rhs, Ast.Node.ArrayTypeSentinel);
   3463 
   3464     const len_node = node_datas[node].lhs;
   3465     if (node_tags[len_node] == .identifier and
   3466         mem.eql(u8, tree.tokenSlice(main_tokens[len_node]), "_"))
   3467     {
   3468         return astgen.failNode(len_node, "unable to infer array size", .{});
   3469     }
   3470     const len = try reachableExpr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, len_node, node);
   3471     const elem_type = try typeExpr(gz, scope, extra.elem_type);
   3472     const sentinel = try reachableExpr(gz, scope, .{ .rl = .{ .coerced_ty = elem_type } }, extra.sentinel, node);
   3473 
   3474     const result = try gz.addPlNode(.array_type_sentinel, node, Zir.Inst.ArrayTypeSentinel{
   3475         .len = len,
   3476         .elem_type = elem_type,
   3477         .sentinel = sentinel,
   3478     });
   3479     return rvalue(gz, ri, result, node);
   3480 }
   3481 
   3482 const WipMembers = struct {
   3483     payload: *ArrayListUnmanaged(u32),
   3484     payload_top: usize,
   3485     decls_start: u32,
   3486     decls_end: u32,
   3487     field_bits_start: u32,
   3488     fields_start: u32,
   3489     fields_end: u32,
   3490     decl_index: u32 = 0,
   3491     field_index: u32 = 0,
   3492 
   3493     const Self = @This();
   3494     /// struct, union, enum, and opaque decls all use same 4 bits per decl
   3495     const bits_per_decl = 4;
   3496     const decls_per_u32 = 32 / bits_per_decl;
   3497     /// struct, union, enum, and opaque decls all have maximum size of 11 u32 slots
   3498     /// (4 for src_hash + line + name + value + doc_comment + align + link_section + address_space )
   3499     const max_decl_size = 11;
   3500 
   3501     pub 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 {
   3502         const payload_top = @intCast(u32, payload.items.len);
   3503         const decls_start = payload_top + (decl_count + decls_per_u32 - 1) / decls_per_u32;
   3504         const field_bits_start = decls_start + decl_count * max_decl_size;
   3505         const fields_start = field_bits_start + if (bits_per_field > 0) blk: {
   3506             const fields_per_u32 = 32 / bits_per_field;
   3507             break :blk (field_count + fields_per_u32 - 1) / fields_per_u32;
   3508         } else 0;
   3509         const payload_end = fields_start + field_count * max_field_size;
   3510         try payload.resize(gpa, payload_end);
   3511         return Self{
   3512             .payload = payload,
   3513             .payload_top = payload_top,
   3514             .decls_start = decls_start,
   3515             .field_bits_start = field_bits_start,
   3516             .fields_start = fields_start,
   3517             .decls_end = decls_start,
   3518             .fields_end = fields_start,
   3519         };
   3520     }
   3521 
   3522     pub fn nextDecl(self: *Self, is_pub: bool, is_export: bool, has_align: bool, has_section_or_addrspace: bool) void {
   3523         const index = self.payload_top + self.decl_index / decls_per_u32;
   3524         assert(index < self.decls_start);
   3525         const bit_bag: u32 = if (self.decl_index % decls_per_u32 == 0) 0 else self.payload.items[index];
   3526         self.payload.items[index] = (bit_bag >> bits_per_decl) |
   3527             (@as(u32, @boolToInt(is_pub)) << 28) |
   3528             (@as(u32, @boolToInt(is_export)) << 29) |
   3529             (@as(u32, @boolToInt(has_align)) << 30) |
   3530             (@as(u32, @boolToInt(has_section_or_addrspace)) << 31);
   3531         self.decl_index += 1;
   3532     }
   3533 
   3534     pub fn nextField(self: *Self, comptime bits_per_field: u32, bits: [bits_per_field]bool) void {
   3535         const fields_per_u32 = 32 / bits_per_field;
   3536         const index = self.field_bits_start + self.field_index / fields_per_u32;
   3537         assert(index < self.fields_start);
   3538         var bit_bag: u32 = if (self.field_index % fields_per_u32 == 0) 0 else self.payload.items[index];
   3539         bit_bag >>= bits_per_field;
   3540         comptime var i = 0;
   3541         inline while (i < bits_per_field) : (i += 1) {
   3542             bit_bag |= @as(u32, @boolToInt(bits[i])) << (32 - bits_per_field + i);
   3543         }
   3544         self.payload.items[index] = bit_bag;
   3545         self.field_index += 1;
   3546     }
   3547 
   3548     pub fn appendToDecl(self: *Self, data: u32) void {
   3549         assert(self.decls_end < self.field_bits_start);
   3550         self.payload.items[self.decls_end] = data;
   3551         self.decls_end += 1;
   3552     }
   3553 
   3554     pub fn appendToDeclSlice(self: *Self, data: []const u32) void {
   3555         assert(self.decls_end + data.len <= self.field_bits_start);
   3556         mem.copy(u32, self.payload.items[self.decls_end..], data);
   3557         self.decls_end += @intCast(u32, data.len);
   3558     }
   3559 
   3560     pub fn appendToField(self: *Self, data: u32) void {
   3561         assert(self.fields_end < self.payload.items.len);
   3562         self.payload.items[self.fields_end] = data;
   3563         self.fields_end += 1;
   3564     }
   3565 
   3566     pub fn finishBits(self: *Self, comptime bits_per_field: u32) void {
   3567         const empty_decl_slots = decls_per_u32 - (self.decl_index % decls_per_u32);
   3568         if (self.decl_index > 0 and empty_decl_slots < decls_per_u32) {
   3569             const index = self.payload_top + self.decl_index / decls_per_u32;
   3570             self.payload.items[index] >>= @intCast(u5, empty_decl_slots * bits_per_decl);
   3571         }
   3572         if (bits_per_field > 0) {
   3573             const fields_per_u32 = 32 / bits_per_field;
   3574             const empty_field_slots = fields_per_u32 - (self.field_index % fields_per_u32);
   3575             if (self.field_index > 0 and empty_field_slots < fields_per_u32) {
   3576                 const index = self.field_bits_start + self.field_index / fields_per_u32;
   3577                 self.payload.items[index] >>= @intCast(u5, empty_field_slots * bits_per_field);
   3578             }
   3579         }
   3580     }
   3581 
   3582     pub fn declsSlice(self: *Self) []u32 {
   3583         return self.payload.items[self.payload_top..self.decls_end];
   3584     }
   3585 
   3586     pub fn fieldsSlice(self: *Self) []u32 {
   3587         return self.payload.items[self.field_bits_start..self.fields_end];
   3588     }
   3589 
   3590     pub fn deinit(self: *Self) void {
   3591         self.payload.items.len = self.payload_top;
   3592     }
   3593 };
   3594 
   3595 fn fnDecl(
   3596     astgen: *AstGen,
   3597     gz: *GenZir,
   3598     scope: *Scope,
   3599     wip_members: *WipMembers,
   3600     decl_node: Ast.Node.Index,
   3601     body_node: Ast.Node.Index,
   3602     fn_proto: Ast.full.FnProto,
   3603 ) InnerError!void {
   3604     const tree = astgen.tree;
   3605     const token_tags = tree.tokens.items(.tag);
   3606 
   3607     // missing function name already happened in scanDecls()
   3608     const fn_name_token = fn_proto.name_token orelse return error.AnalysisFail;
   3609     const fn_name_str_index = try astgen.identAsString(fn_name_token);
   3610 
   3611     // We insert this at the beginning so that its instruction index marks the
   3612     // start of the top level declaration.
   3613     const block_inst = try gz.makeBlockInst(.block_inline, fn_proto.ast.proto_node);
   3614     astgen.advanceSourceCursorToNode(decl_node);
   3615 
   3616     var decl_gz: GenZir = .{
   3617         .force_comptime = true,
   3618         .decl_node_index = fn_proto.ast.proto_node,
   3619         .decl_line = astgen.source_line,
   3620         .parent = scope,
   3621         .astgen = astgen,
   3622         .instructions = gz.instructions,
   3623         .instructions_top = gz.instructions.items.len,
   3624     };
   3625     defer decl_gz.unstack();
   3626 
   3627     var fn_gz: GenZir = .{
   3628         .force_comptime = false,
   3629         .decl_node_index = fn_proto.ast.proto_node,
   3630         .decl_line = decl_gz.decl_line,
   3631         .parent = &decl_gz.base,
   3632         .astgen = astgen,
   3633         .instructions = gz.instructions,
   3634         .instructions_top = GenZir.unstacked_top,
   3635     };
   3636     defer fn_gz.unstack();
   3637 
   3638     const is_pub = fn_proto.visib_token != null;
   3639     const is_export = blk: {
   3640         const maybe_export_token = fn_proto.extern_export_inline_token orelse break :blk false;
   3641         break :blk token_tags[maybe_export_token] == .keyword_export;
   3642     };
   3643     const is_extern = blk: {
   3644         const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false;
   3645         break :blk token_tags[maybe_extern_token] == .keyword_extern;
   3646     };
   3647     const has_inline_keyword = blk: {
   3648         const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;
   3649         break :blk token_tags[maybe_inline_token] == .keyword_inline;
   3650     };
   3651     const is_noinline = blk: {
   3652         const maybe_noinline_token = fn_proto.extern_export_inline_token orelse break :blk false;
   3653         break :blk token_tags[maybe_noinline_token] == .keyword_noinline;
   3654     };
   3655 
   3656     const doc_comment_index = try astgen.docCommentAsString(fn_proto.firstToken());
   3657 
   3658     // align, linksection, and addrspace is passed in the func instruction in this case.
   3659     wip_members.nextDecl(is_pub, is_export, false, false);
   3660 
   3661     var noalias_bits: u32 = 0;
   3662     var params_scope = &fn_gz.base;
   3663     const is_var_args = is_var_args: {
   3664         var param_type_i: usize = 0;
   3665         var it = fn_proto.iterate(tree);
   3666         while (it.next()) |param| : (param_type_i += 1) {
   3667             const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) {
   3668                 .keyword_noalias => is_comptime: {
   3669                     noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse
   3670                         return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
   3671                     break :is_comptime false;
   3672                 },
   3673                 .keyword_comptime => true,
   3674                 else => false,
   3675             } else false;
   3676 
   3677             const is_anytype = if (param.anytype_ellipsis3) |token| blk: {
   3678                 switch (token_tags[token]) {
   3679                     .keyword_anytype => break :blk true,
   3680                     .ellipsis3 => break :is_var_args true,
   3681                     else => unreachable,
   3682                 }
   3683             } else false;
   3684 
   3685             const param_name: u32 = if (param.name_token) |name_token| blk: {
   3686                 const name_bytes = tree.tokenSlice(name_token);
   3687                 if (mem.eql(u8, "_", name_bytes))
   3688                     break :blk 0;
   3689 
   3690                 const param_name = try astgen.identAsString(name_token);
   3691                 if (!is_extern) {
   3692                     try astgen.detectLocalShadowing(params_scope, param_name, name_token, name_bytes, .@"function parameter");
   3693                 }
   3694                 break :blk param_name;
   3695             } else if (!is_extern) {
   3696                 if (param.anytype_ellipsis3) |tok| {
   3697                     return astgen.failTok(tok, "missing parameter name", .{});
   3698                 } else {
   3699                     ambiguous: {
   3700                         if (tree.nodes.items(.tag)[param.type_expr] != .identifier) break :ambiguous;
   3701                         const main_token = tree.nodes.items(.main_token)[param.type_expr];
   3702                         const identifier_str = tree.tokenSlice(main_token);
   3703                         if (isPrimitive(identifier_str)) break :ambiguous;
   3704                         return astgen.failNodeNotes(
   3705                             param.type_expr,
   3706                             "missing parameter name or type",
   3707                             .{},
   3708                             &[_]u32{
   3709                                 try astgen.errNoteNode(
   3710                                     param.type_expr,
   3711                                     "if this is a name, annotate its type '{s}: T'",
   3712                                     .{identifier_str},
   3713                                 ),
   3714                                 try astgen.errNoteNode(
   3715                                     param.type_expr,
   3716                                     "if this is a type, give it a name '<name>: {s}'",
   3717                                     .{identifier_str},
   3718                                 ),
   3719                             },
   3720                         );
   3721                     }
   3722                     return astgen.failNode(param.type_expr, "missing parameter name", .{});
   3723                 }
   3724             } else 0;
   3725 
   3726             const param_inst = if (is_anytype) param: {
   3727                 const name_token = param.name_token orelse param.anytype_ellipsis3.?;
   3728                 const tag: Zir.Inst.Tag = if (is_comptime)
   3729                     .param_anytype_comptime
   3730                 else
   3731                     .param_anytype;
   3732                 break :param try decl_gz.addStrTok(tag, param_name, name_token);
   3733             } else param: {
   3734                 const param_type_node = param.type_expr;
   3735                 assert(param_type_node != 0);
   3736                 var param_gz = decl_gz.makeSubBlock(scope);
   3737                 defer param_gz.unstack();
   3738                 const param_type = try expr(&param_gz, params_scope, coerced_type_ri, param_type_node);
   3739                 const param_inst_expected = @intCast(u32, astgen.instructions.len + 1);
   3740                 _ = try param_gz.addBreak(.break_inline, param_inst_expected, param_type);
   3741 
   3742                 const main_tokens = tree.nodes.items(.main_token);
   3743                 const name_token = param.name_token orelse main_tokens[param_type_node];
   3744                 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param;
   3745                 const param_inst = try decl_gz.addParam(&param_gz, tag, name_token, param_name, param.first_doc_comment);
   3746                 assert(param_inst_expected == param_inst);
   3747                 break :param indexToRef(param_inst);
   3748             };
   3749 
   3750             if (param_name == 0 or is_extern) continue;
   3751 
   3752             const sub_scope = try astgen.arena.create(Scope.LocalVal);
   3753             sub_scope.* = .{
   3754                 .parent = params_scope,
   3755                 .gen_zir = &decl_gz,
   3756                 .name = param_name,
   3757                 .inst = param_inst,
   3758                 .token_src = param.name_token.?,
   3759                 .id_cat = .@"function parameter",
   3760             };
   3761             params_scope = &sub_scope.base;
   3762         }
   3763         break :is_var_args false;
   3764     };
   3765 
   3766     const lib_name: u32 = if (fn_proto.lib_name) |lib_name_token| blk: {
   3767         const lib_name_str = try astgen.strLitAsString(lib_name_token);
   3768         const lib_name_slice = astgen.string_bytes.items[lib_name_str.index..][0..lib_name_str.len];
   3769         if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) {
   3770             return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{});
   3771         } else if (lib_name_str.len == 0) {
   3772             return astgen.failTok(lib_name_token, "library name cannot be empty", .{});
   3773         }
   3774         break :blk lib_name_str.index;
   3775     } else 0;
   3776 
   3777     const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
   3778     const is_inferred_error = token_tags[maybe_bang] == .bang;
   3779 
   3780     // After creating the function ZIR instruction, it will need to update the break
   3781     // instructions inside the expression blocks for align, addrspace, cc, and ret_ty
   3782     // to use the function instruction as the "block" to break from.
   3783 
   3784     var align_gz = decl_gz.makeSubBlock(params_scope);
   3785     defer align_gz.unstack();
   3786     const align_ref: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
   3787         const inst = try expr(&decl_gz, params_scope, coerced_align_ri, fn_proto.ast.align_expr);
   3788         if (align_gz.instructionsSlice().len == 0) {
   3789             // In this case we will send a len=0 body which can be encoded more efficiently.
   3790             break :inst inst;
   3791         }
   3792         _ = try align_gz.addBreak(.break_inline, 0, inst);
   3793         break :inst inst;
   3794     };
   3795 
   3796     var addrspace_gz = decl_gz.makeSubBlock(params_scope);
   3797     defer addrspace_gz.unstack();
   3798     const addrspace_ref: Zir.Inst.Ref = if (fn_proto.ast.addrspace_expr == 0) .none else inst: {
   3799         const inst = try expr(&decl_gz, params_scope, .{ .rl = .{ .coerced_ty = .address_space_type } }, fn_proto.ast.addrspace_expr);
   3800         if (addrspace_gz.instructionsSlice().len == 0) {
   3801             // In this case we will send a len=0 body which can be encoded more efficiently.
   3802             break :inst inst;
   3803         }
   3804         _ = try addrspace_gz.addBreak(.break_inline, 0, inst);
   3805         break :inst inst;
   3806     };
   3807 
   3808     var section_gz = decl_gz.makeSubBlock(params_scope);
   3809     defer section_gz.unstack();
   3810     const section_ref: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: {
   3811         const inst = try expr(&decl_gz, params_scope, .{ .rl = .{ .coerced_ty = .const_slice_u8_type } }, fn_proto.ast.section_expr);
   3812         if (section_gz.instructionsSlice().len == 0) {
   3813             // In this case we will send a len=0 body which can be encoded more efficiently.
   3814             break :inst inst;
   3815         }
   3816         _ = try section_gz.addBreak(.break_inline, 0, inst);
   3817         break :inst inst;
   3818     };
   3819 
   3820     var cc_gz = decl_gz.makeSubBlock(params_scope);
   3821     defer cc_gz.unstack();
   3822     const cc_ref: Zir.Inst.Ref = blk: {
   3823         if (fn_proto.ast.callconv_expr != 0) {
   3824             if (has_inline_keyword) {
   3825                 return astgen.failNode(
   3826                     fn_proto.ast.callconv_expr,
   3827                     "explicit callconv incompatible with inline keyword",
   3828                     .{},
   3829                 );
   3830             }
   3831             const inst = try expr(
   3832                 &decl_gz,
   3833                 params_scope,
   3834                 .{ .rl = .{ .coerced_ty = .calling_convention_type } },
   3835                 fn_proto.ast.callconv_expr,
   3836             );
   3837             if (cc_gz.instructionsSlice().len == 0) {
   3838                 // In this case we will send a len=0 body which can be encoded more efficiently.
   3839                 break :blk inst;
   3840             }
   3841             _ = try cc_gz.addBreak(.break_inline, 0, inst);
   3842             break :blk inst;
   3843         } else if (is_extern) {
   3844             // note: https://github.com/ziglang/zig/issues/5269
   3845             break :blk .calling_convention_c;
   3846         } else if (has_inline_keyword) {
   3847             break :blk .calling_convention_inline;
   3848         } else {
   3849             break :blk .none;
   3850         }
   3851     };
   3852 
   3853     var ret_gz = decl_gz.makeSubBlock(params_scope);
   3854     defer ret_gz.unstack();
   3855     const ret_ref: Zir.Inst.Ref = inst: {
   3856         const inst = try expr(&ret_gz, params_scope, coerced_type_ri, fn_proto.ast.return_type);
   3857         if (ret_gz.instructionsSlice().len == 0) {
   3858             // In this case we will send a len=0 body which can be encoded more efficiently.
   3859             break :inst inst;
   3860         }
   3861         _ = try ret_gz.addBreak(.break_inline, 0, inst);
   3862         break :inst inst;
   3863     };
   3864 
   3865     const func_inst: Zir.Inst.Ref = if (body_node == 0) func: {
   3866         if (!is_extern) {
   3867             return astgen.failTok(fn_proto.ast.fn_token, "non-extern function has no body", .{});
   3868         }
   3869         if (is_inferred_error) {
   3870             return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{});
   3871         }
   3872         break :func try decl_gz.addFunc(.{
   3873             .src_node = decl_node,
   3874             .cc_ref = cc_ref,
   3875             .cc_gz = &cc_gz,
   3876             .align_ref = align_ref,
   3877             .align_gz = &align_gz,
   3878             .ret_ref = ret_ref,
   3879             .ret_gz = &ret_gz,
   3880             .section_ref = section_ref,
   3881             .section_gz = &section_gz,
   3882             .addrspace_ref = addrspace_ref,
   3883             .addrspace_gz = &addrspace_gz,
   3884             .param_block = block_inst,
   3885             .body_gz = null,
   3886             .lib_name = lib_name,
   3887             .is_var_args = is_var_args,
   3888             .is_inferred_error = false,
   3889             .is_test = false,
   3890             .is_extern = true,
   3891             .is_noinline = is_noinline,
   3892             .noalias_bits = noalias_bits,
   3893         });
   3894     } else func: {
   3895         if (is_var_args) {
   3896             return astgen.failTok(fn_proto.ast.fn_token, "non-extern function is variadic", .{});
   3897         }
   3898 
   3899         // as a scope, fn_gz encloses ret_gz, but for instruction list, fn_gz stacks on ret_gz
   3900         fn_gz.instructions_top = ret_gz.instructions.items.len;
   3901 
   3902         const prev_fn_block = astgen.fn_block;
   3903         astgen.fn_block = &fn_gz;
   3904         defer astgen.fn_block = prev_fn_block;
   3905 
   3906         astgen.advanceSourceCursorToNode(body_node);
   3907         const lbrace_line = astgen.source_line - decl_gz.decl_line;
   3908         const lbrace_column = astgen.source_column;
   3909 
   3910         _ = try expr(&fn_gz, params_scope, .{ .rl = .none }, body_node);
   3911         try checkUsed(gz, &fn_gz.base, params_scope);
   3912 
   3913         if (!fn_gz.endsWithNoReturn()) {
   3914             // As our last action before the return, "pop" the error trace if needed
   3915             _ = try gz.addRestoreErrRetIndex(.ret, .always);
   3916 
   3917             // Add implicit return at end of function.
   3918             _ = try fn_gz.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node));
   3919         }
   3920 
   3921         break :func try decl_gz.addFunc(.{
   3922             .src_node = decl_node,
   3923             .cc_ref = cc_ref,
   3924             .cc_gz = &cc_gz,
   3925             .align_ref = align_ref,
   3926             .align_gz = &align_gz,
   3927             .ret_ref = ret_ref,
   3928             .ret_gz = &ret_gz,
   3929             .section_ref = section_ref,
   3930             .section_gz = &section_gz,
   3931             .addrspace_ref = addrspace_ref,
   3932             .addrspace_gz = &addrspace_gz,
   3933             .lbrace_line = lbrace_line,
   3934             .lbrace_column = lbrace_column,
   3935             .param_block = block_inst,
   3936             .body_gz = &fn_gz,
   3937             .lib_name = lib_name,
   3938             .is_var_args = is_var_args,
   3939             .is_inferred_error = is_inferred_error,
   3940             .is_test = false,
   3941             .is_extern = false,
   3942             .is_noinline = is_noinline,
   3943             .noalias_bits = noalias_bits,
   3944         });
   3945     };
   3946 
   3947     // We add this at the end so that its instruction index marks the end range
   3948     // of the top level declaration. addFunc already unstacked fn_gz and ret_gz.
   3949     _ = try decl_gz.addBreak(.break_inline, block_inst, func_inst);
   3950     try decl_gz.setBlockBody(block_inst);
   3951 
   3952     {
   3953         const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
   3954         const casted = @bitCast([4]u32, contents_hash);
   3955         wip_members.appendToDeclSlice(&casted);
   3956     }
   3957     {
   3958         const line_delta = decl_gz.decl_line - gz.decl_line;
   3959         wip_members.appendToDecl(line_delta);
   3960     }
   3961     wip_members.appendToDecl(fn_name_str_index);
   3962     wip_members.appendToDecl(block_inst);
   3963     wip_members.appendToDecl(doc_comment_index);
   3964 }
   3965 
   3966 fn globalVarDecl(
   3967     astgen: *AstGen,
   3968     gz: *GenZir,
   3969     scope: *Scope,
   3970     wip_members: *WipMembers,
   3971     node: Ast.Node.Index,
   3972     var_decl: Ast.full.VarDecl,
   3973 ) InnerError!void {
   3974     const tree = astgen.tree;
   3975     const token_tags = tree.tokens.items(.tag);
   3976 
   3977     const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var;
   3978     // We do this at the beginning so that the instruction index marks the range start
   3979     // of the top level declaration.
   3980     const block_inst = try gz.makeBlockInst(.block_inline, node);
   3981 
   3982     const name_token = var_decl.ast.mut_token + 1;
   3983     const name_str_index = try astgen.identAsString(name_token);
   3984     astgen.advanceSourceCursorToNode(node);
   3985 
   3986     var block_scope: GenZir = .{
   3987         .parent = scope,
   3988         .decl_node_index = node,
   3989         .decl_line = astgen.source_line,
   3990         .astgen = astgen,
   3991         .force_comptime = true,
   3992         .anon_name_strategy = .parent,
   3993         .instructions = gz.instructions,
   3994         .instructions_top = gz.instructions.items.len,
   3995     };
   3996     defer block_scope.unstack();
   3997 
   3998     const is_pub = var_decl.visib_token != null;
   3999     const is_export = blk: {
   4000         const maybe_export_token = var_decl.extern_export_token orelse break :blk false;
   4001         break :blk token_tags[maybe_export_token] == .keyword_export;
   4002     };
   4003     const is_extern = blk: {
   4004         const maybe_extern_token = var_decl.extern_export_token orelse break :blk false;
   4005         break :blk token_tags[maybe_extern_token] == .keyword_extern;
   4006     };
   4007     const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node == 0) .none else inst: {
   4008         break :inst try expr(&block_scope, &block_scope.base, align_ri, var_decl.ast.align_node);
   4009     };
   4010     const addrspace_inst: Zir.Inst.Ref = if (var_decl.ast.addrspace_node == 0) .none else inst: {
   4011         break :inst try expr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = .address_space_type } }, var_decl.ast.addrspace_node);
   4012     };
   4013     const section_inst: Zir.Inst.Ref = if (var_decl.ast.section_node == 0) .none else inst: {
   4014         break :inst try comptimeExpr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = .const_slice_u8_type } }, var_decl.ast.section_node);
   4015     };
   4016     const has_section_or_addrspace = section_inst != .none or addrspace_inst != .none;
   4017     wip_members.nextDecl(is_pub, is_export, align_inst != .none, has_section_or_addrspace);
   4018 
   4019     const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: {
   4020         if (!is_mutable) {
   4021             return astgen.failTok(tok, "threadlocal variable cannot be constant", .{});
   4022         }
   4023         break :blk true;
   4024     } else false;
   4025 
   4026     const lib_name: u32 = if (var_decl.lib_name) |lib_name_token| blk: {
   4027         const lib_name_str = try astgen.strLitAsString(lib_name_token);
   4028         const lib_name_slice = astgen.string_bytes.items[lib_name_str.index..][0..lib_name_str.len];
   4029         if (mem.indexOfScalar(u8, lib_name_slice, 0) != null) {
   4030             return astgen.failTok(lib_name_token, "library name cannot contain null bytes", .{});
   4031         } else if (lib_name_str.len == 0) {
   4032             return astgen.failTok(lib_name_token, "library name cannot be empty", .{});
   4033         }
   4034         break :blk lib_name_str.index;
   4035     } else 0;
   4036 
   4037     const doc_comment_index = try astgen.docCommentAsString(var_decl.firstToken());
   4038 
   4039     assert(var_decl.comptime_token == null); // handled by parser
   4040 
   4041     const var_inst: Zir.Inst.Ref = if (var_decl.ast.init_node != 0) vi: {
   4042         if (is_extern) {
   4043             return astgen.failNode(
   4044                 var_decl.ast.init_node,
   4045                 "extern variables have no initializers",
   4046                 .{},
   4047             );
   4048         }
   4049 
   4050         const type_inst: Zir.Inst.Ref = if (var_decl.ast.type_node != 0)
   4051             try expr(
   4052                 &block_scope,
   4053                 &block_scope.base,
   4054                 .{ .rl = .{ .ty = .type_type } },
   4055                 var_decl.ast.type_node,
   4056             )
   4057         else
   4058             .none;
   4059 
   4060         const init_inst = try expr(
   4061             &block_scope,
   4062             &block_scope.base,
   4063             if (type_inst != .none) .{ .rl = .{ .ty = type_inst } } else .{ .rl = .none },
   4064             var_decl.ast.init_node,
   4065         );
   4066 
   4067         if (is_mutable) {
   4068             const var_inst = try block_scope.addVar(.{
   4069                 .var_type = type_inst,
   4070                 .lib_name = 0,
   4071                 .align_inst = .none, // passed via the decls data
   4072                 .init = init_inst,
   4073                 .is_extern = false,
   4074                 .is_threadlocal = is_threadlocal,
   4075             });
   4076             break :vi var_inst;
   4077         } else {
   4078             break :vi init_inst;
   4079         }
   4080     } else if (!is_extern) {
   4081         return astgen.failNode(node, "variables must be initialized", .{});
   4082     } else if (var_decl.ast.type_node != 0) vi: {
   4083         // Extern variable which has an explicit type.
   4084         const type_inst = try typeExpr(&block_scope, &block_scope.base, var_decl.ast.type_node);
   4085 
   4086         const var_inst = try block_scope.addVar(.{
   4087             .var_type = type_inst,
   4088             .lib_name = lib_name,
   4089             .align_inst = .none, // passed via the decls data
   4090             .init = .none,
   4091             .is_extern = true,
   4092             .is_threadlocal = is_threadlocal,
   4093         });
   4094         break :vi var_inst;
   4095     } else {
   4096         return astgen.failNode(node, "unable to infer variable type", .{});
   4097     };
   4098     // We do this at the end so that the instruction index marks the end
   4099     // range of a top level declaration.
   4100     _ = try block_scope.addBreak(.break_inline, block_inst, var_inst);
   4101     try block_scope.setBlockBody(block_inst);
   4102 
   4103     {
   4104         const contents_hash = std.zig.hashSrc(tree.getNodeSource(node));
   4105         const casted = @bitCast([4]u32, contents_hash);
   4106         wip_members.appendToDeclSlice(&casted);
   4107     }
   4108     {
   4109         const line_delta = block_scope.decl_line - gz.decl_line;
   4110         wip_members.appendToDecl(line_delta);
   4111     }
   4112     wip_members.appendToDecl(name_str_index);
   4113     wip_members.appendToDecl(block_inst);
   4114     wip_members.appendToDecl(doc_comment_index); // doc_comment wip
   4115     if (align_inst != .none) {
   4116         wip_members.appendToDecl(@enumToInt(align_inst));
   4117     }
   4118     if (has_section_or_addrspace) {
   4119         wip_members.appendToDecl(@enumToInt(section_inst));
   4120         wip_members.appendToDecl(@enumToInt(addrspace_inst));
   4121     }
   4122 }
   4123 
   4124 fn comptimeDecl(
   4125     astgen: *AstGen,
   4126     gz: *GenZir,
   4127     scope: *Scope,
   4128     wip_members: *WipMembers,
   4129     node: Ast.Node.Index,
   4130 ) InnerError!void {
   4131     const tree = astgen.tree;
   4132     const node_datas = tree.nodes.items(.data);
   4133     const body_node = node_datas[node].lhs;
   4134 
   4135     // Up top so the ZIR instruction index marks the start range of this
   4136     // top-level declaration.
   4137     const block_inst = try gz.makeBlockInst(.block_inline, node);
   4138     wip_members.nextDecl(false, false, false, false);
   4139     astgen.advanceSourceCursorToNode(node);
   4140 
   4141     var decl_block: GenZir = .{
   4142         .force_comptime = true,
   4143         .decl_node_index = node,
   4144         .decl_line = astgen.source_line,
   4145         .parent = scope,
   4146         .astgen = astgen,
   4147         .instructions = gz.instructions,
   4148         .instructions_top = gz.instructions.items.len,
   4149     };
   4150     defer decl_block.unstack();
   4151 
   4152     const block_result = try expr(&decl_block, &decl_block.base, .{ .rl = .none }, body_node);
   4153     if (decl_block.isEmpty() or !decl_block.refIsNoReturn(block_result)) {
   4154         _ = try decl_block.addBreak(.break_inline, block_inst, .void_value);
   4155     }
   4156     try decl_block.setBlockBody(block_inst);
   4157 
   4158     {
   4159         const contents_hash = std.zig.hashSrc(tree.getNodeSource(node));
   4160         const casted = @bitCast([4]u32, contents_hash);
   4161         wip_members.appendToDeclSlice(&casted);
   4162     }
   4163     {
   4164         const line_delta = decl_block.decl_line - gz.decl_line;
   4165         wip_members.appendToDecl(line_delta);
   4166     }
   4167     wip_members.appendToDecl(0);
   4168     wip_members.appendToDecl(block_inst);
   4169     wip_members.appendToDecl(0); // no doc comments on comptime decls
   4170 }
   4171 
   4172 fn usingnamespaceDecl(
   4173     astgen: *AstGen,
   4174     gz: *GenZir,
   4175     scope: *Scope,
   4176     wip_members: *WipMembers,
   4177     node: Ast.Node.Index,
   4178 ) InnerError!void {
   4179     const tree = astgen.tree;
   4180     const node_datas = tree.nodes.items(.data);
   4181 
   4182     const type_expr = node_datas[node].lhs;
   4183     const is_pub = blk: {
   4184         const main_tokens = tree.nodes.items(.main_token);
   4185         const token_tags = tree.tokens.items(.tag);
   4186         const main_token = main_tokens[node];
   4187         break :blk (main_token > 0 and token_tags[main_token - 1] == .keyword_pub);
   4188     };
   4189     // Up top so the ZIR instruction index marks the start range of this
   4190     // top-level declaration.
   4191     const block_inst = try gz.makeBlockInst(.block_inline, node);
   4192     wip_members.nextDecl(is_pub, true, false, false);
   4193     astgen.advanceSourceCursorToNode(node);
   4194 
   4195     var decl_block: GenZir = .{
   4196         .force_comptime = true,
   4197         .decl_node_index = node,
   4198         .decl_line = astgen.source_line,
   4199         .parent = scope,
   4200         .astgen = astgen,
   4201         .instructions = gz.instructions,
   4202         .instructions_top = gz.instructions.items.len,
   4203     };
   4204     defer decl_block.unstack();
   4205 
   4206     const namespace_inst = try typeExpr(&decl_block, &decl_block.base, type_expr);
   4207     _ = try decl_block.addBreak(.break_inline, block_inst, namespace_inst);
   4208     try decl_block.setBlockBody(block_inst);
   4209 
   4210     {
   4211         const contents_hash = std.zig.hashSrc(tree.getNodeSource(node));
   4212         const casted = @bitCast([4]u32, contents_hash);
   4213         wip_members.appendToDeclSlice(&casted);
   4214     }
   4215     {
   4216         const line_delta = decl_block.decl_line - gz.decl_line;
   4217         wip_members.appendToDecl(line_delta);
   4218     }
   4219     wip_members.appendToDecl(0);
   4220     wip_members.appendToDecl(block_inst);
   4221     wip_members.appendToDecl(0); // no doc comments on usingnamespace decls
   4222 }
   4223 
   4224 fn testDecl(
   4225     astgen: *AstGen,
   4226     gz: *GenZir,
   4227     scope: *Scope,
   4228     wip_members: *WipMembers,
   4229     node: Ast.Node.Index,
   4230 ) InnerError!void {
   4231     const tree = astgen.tree;
   4232     const node_datas = tree.nodes.items(.data);
   4233     const body_node = node_datas[node].rhs;
   4234 
   4235     // Up top so the ZIR instruction index marks the start range of this
   4236     // top-level declaration.
   4237     const block_inst = try gz.makeBlockInst(.block_inline, node);
   4238 
   4239     wip_members.nextDecl(false, false, false, false);
   4240     astgen.advanceSourceCursorToNode(node);
   4241 
   4242     var decl_block: GenZir = .{
   4243         .force_comptime = true,
   4244         .decl_node_index = node,
   4245         .decl_line = astgen.source_line,
   4246         .parent = scope,
   4247         .astgen = astgen,
   4248         .instructions = gz.instructions,
   4249         .instructions_top = gz.instructions.items.len,
   4250     };
   4251     defer decl_block.unstack();
   4252 
   4253     const main_tokens = tree.nodes.items(.main_token);
   4254     const token_tags = tree.tokens.items(.tag);
   4255     const test_token = main_tokens[node];
   4256     const test_name_token = test_token + 1;
   4257     const test_name_token_tag = token_tags[test_name_token];
   4258     const is_decltest = test_name_token_tag == .identifier;
   4259     const test_name: u32 = blk: {
   4260         if (test_name_token_tag == .string_literal) {
   4261             break :blk try astgen.testNameString(test_name_token);
   4262         } else if (test_name_token_tag == .identifier) {
   4263             const ident_name_raw = tree.tokenSlice(test_name_token);
   4264 
   4265             if (mem.eql(u8, ident_name_raw, "_")) return astgen.failTok(test_name_token, "'_' used as an identifier without @\"_\" syntax", .{});
   4266 
   4267             // if not @"" syntax, just use raw token slice
   4268             if (ident_name_raw[0] != '@') {
   4269                 if (isPrimitive(ident_name_raw)) return astgen.failTok(test_name_token, "cannot test a primitive", .{});
   4270             }
   4271 
   4272             // Local variables, including function parameters.
   4273             const name_str_index = try astgen.identAsString(test_name_token);
   4274             var s = scope;
   4275             var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
   4276             var num_namespaces_out: u32 = 0;
   4277             var capturing_namespace: ?*Scope.Namespace = null;
   4278             while (true) switch (s.tag) {
   4279                 .local_val, .local_ptr => unreachable, // a test cannot be in a local scope
   4280                 .gen_zir => s = s.cast(GenZir).?.parent,
   4281                 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
   4282                 .namespace => {
   4283                     const ns = s.cast(Scope.Namespace).?;
   4284                     if (ns.decls.get(name_str_index)) |i| {
   4285                         if (found_already) |f| {
   4286                             return astgen.failTokNotes(test_name_token, "ambiguous reference", .{}, &.{
   4287                                 try astgen.errNoteNode(f, "declared here", .{}),
   4288                                 try astgen.errNoteNode(i, "also declared here", .{}),
   4289                             });
   4290                         }
   4291                         // We found a match but must continue looking for ambiguous references to decls.
   4292                         found_already = i;
   4293                     }
   4294                     num_namespaces_out += 1;
   4295                     capturing_namespace = ns;
   4296                     s = ns.parent;
   4297                 },
   4298                 .top => break,
   4299             };
   4300             if (found_already == null) {
   4301                 const ident_name = try astgen.identifierTokenString(test_name_token);
   4302                 return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name});
   4303             }
   4304 
   4305             break :blk name_str_index;
   4306         }
   4307         // String table index 1 has a special meaning here of test decl with no name.
   4308         break :blk 1;
   4309     };
   4310 
   4311     var fn_block: GenZir = .{
   4312         .force_comptime = false,
   4313         .decl_node_index = node,
   4314         .decl_line = decl_block.decl_line,
   4315         .parent = &decl_block.base,
   4316         .astgen = astgen,
   4317         .instructions = decl_block.instructions,
   4318         .instructions_top = decl_block.instructions.items.len,
   4319     };
   4320     defer fn_block.unstack();
   4321 
   4322     const prev_fn_block = astgen.fn_block;
   4323     astgen.fn_block = &fn_block;
   4324     defer astgen.fn_block = prev_fn_block;
   4325 
   4326     astgen.advanceSourceCursorToNode(body_node);
   4327     const lbrace_line = astgen.source_line - decl_block.decl_line;
   4328     const lbrace_column = astgen.source_column;
   4329 
   4330     const block_result = try expr(&fn_block, &fn_block.base, .{ .rl = .none }, body_node);
   4331     if (fn_block.isEmpty() or !fn_block.refIsNoReturn(block_result)) {
   4332 
   4333         // As our last action before the return, "pop" the error trace if needed
   4334         _ = try gz.addRestoreErrRetIndex(.ret, .always);
   4335 
   4336         // Add implicit return at end of function.
   4337         _ = try fn_block.addUnTok(.ret_implicit, .void_value, tree.lastToken(body_node));
   4338     }
   4339 
   4340     const func_inst = try decl_block.addFunc(.{
   4341         .src_node = node,
   4342 
   4343         .cc_ref = .none,
   4344         .cc_gz = null,
   4345         .align_ref = .none,
   4346         .align_gz = null,
   4347         .ret_ref = .void_type,
   4348         .ret_gz = null,
   4349         .section_ref = .none,
   4350         .section_gz = null,
   4351         .addrspace_ref = .none,
   4352         .addrspace_gz = null,
   4353 
   4354         .lbrace_line = lbrace_line,
   4355         .lbrace_column = lbrace_column,
   4356         .param_block = block_inst,
   4357         .body_gz = &fn_block,
   4358         .lib_name = 0,
   4359         .is_var_args = false,
   4360         .is_inferred_error = true,
   4361         .is_test = true,
   4362         .is_extern = false,
   4363         .is_noinline = false,
   4364         .noalias_bits = 0,
   4365     });
   4366 
   4367     _ = try decl_block.addBreak(.break_inline, block_inst, func_inst);
   4368     try decl_block.setBlockBody(block_inst);
   4369 
   4370     {
   4371         const contents_hash = std.zig.hashSrc(tree.getNodeSource(node));
   4372         const casted = @bitCast([4]u32, contents_hash);
   4373         wip_members.appendToDeclSlice(&casted);
   4374     }
   4375     {
   4376         const line_delta = decl_block.decl_line - gz.decl_line;
   4377         wip_members.appendToDecl(line_delta);
   4378     }
   4379     if (is_decltest)
   4380         wip_members.appendToDecl(2) // 2 here means that it is a decltest, look at doc comment for name
   4381     else
   4382         wip_members.appendToDecl(test_name);
   4383     wip_members.appendToDecl(block_inst);
   4384     if (is_decltest)
   4385         wip_members.appendToDecl(test_name) // the doc comment on a decltest represents it's name
   4386     else
   4387         wip_members.appendToDecl(0); // no doc comments on test decls
   4388 }
   4389 
   4390 fn structDeclInner(
   4391     gz: *GenZir,
   4392     scope: *Scope,
   4393     node: Ast.Node.Index,
   4394     container_decl: Ast.full.ContainerDecl,
   4395     layout: std.builtin.Type.ContainerLayout,
   4396     backing_int_node: Ast.Node.Index,
   4397 ) InnerError!Zir.Inst.Ref {
   4398     const decl_inst = try gz.reserveInstructionIndex();
   4399 
   4400     if (container_decl.ast.members.len == 0 and backing_int_node == 0) {
   4401         try gz.setStruct(decl_inst, .{
   4402             .src_node = node,
   4403             .layout = layout,
   4404             .fields_len = 0,
   4405             .decls_len = 0,
   4406             .backing_int_ref = .none,
   4407             .backing_int_body_len = 0,
   4408             .known_non_opv = false,
   4409             .known_comptime_only = false,
   4410             .is_tuple = false,
   4411         });
   4412         return indexToRef(decl_inst);
   4413     }
   4414 
   4415     const astgen = gz.astgen;
   4416     const gpa = astgen.gpa;
   4417     const tree = astgen.tree;
   4418 
   4419     var namespace: Scope.Namespace = .{
   4420         .parent = scope,
   4421         .node = node,
   4422         .inst = decl_inst,
   4423         .declaring_gz = gz,
   4424     };
   4425     defer namespace.deinit(gpa);
   4426 
   4427     // The struct_decl instruction introduces a scope in which the decls of the struct
   4428     // are in scope, so that field types, alignments, and default value expressions
   4429     // can refer to decls within the struct itself.
   4430     astgen.advanceSourceCursorToNode(node);
   4431     var block_scope: GenZir = .{
   4432         .parent = &namespace.base,
   4433         .decl_node_index = node,
   4434         .decl_line = gz.decl_line,
   4435         .astgen = astgen,
   4436         .force_comptime = true,
   4437         .instructions = gz.instructions,
   4438         .instructions_top = gz.instructions.items.len,
   4439     };
   4440     defer block_scope.unstack();
   4441 
   4442     const scratch_top = astgen.scratch.items.len;
   4443     defer astgen.scratch.items.len = scratch_top;
   4444 
   4445     var backing_int_body_len: usize = 0;
   4446     const backing_int_ref: Zir.Inst.Ref = blk: {
   4447         if (backing_int_node != 0) {
   4448             if (layout != .Packed) {
   4449                 return astgen.failNode(backing_int_node, "non-packed struct does not support backing integer type", .{});
   4450             } else {
   4451                 const backing_int_ref = try typeExpr(&block_scope, &namespace.base, backing_int_node);
   4452                 if (!block_scope.isEmpty()) {
   4453                     if (!block_scope.endsWithNoReturn()) {
   4454                         _ = try block_scope.addBreak(.break_inline, decl_inst, backing_int_ref);
   4455                     }
   4456 
   4457                     const body = block_scope.instructionsSlice();
   4458                     const old_scratch_len = astgen.scratch.items.len;
   4459                     try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
   4460                     appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
   4461                     backing_int_body_len = astgen.scratch.items.len - old_scratch_len;
   4462                     block_scope.instructions.items.len = block_scope.instructions_top;
   4463                 }
   4464                 break :blk backing_int_ref;
   4465             }
   4466         } else {
   4467             break :blk .none;
   4468         }
   4469     };
   4470 
   4471     const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members);
   4472     const field_count = @intCast(u32, container_decl.ast.members.len - decl_count);
   4473 
   4474     const bits_per_field = 4;
   4475     const max_field_size = 5;
   4476     var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size);
   4477     defer wip_members.deinit();
   4478 
   4479     // We will use the scratch buffer, starting here, for the bodies:
   4480     //    bodies: { // for every fields_len
   4481     //        field_type_body_inst: Inst, // for each field_type_body_len
   4482     //        align_body_inst: Inst, // for each align_body_len
   4483     //        init_body_inst: Inst, // for each init_body_len
   4484     //    }
   4485     // Note that the scratch buffer is simultaneously being used by WipMembers, however
   4486     // it will not access any elements beyond this point in the ArrayList. It also
   4487     // accesses via the ArrayList items field so it can handle the scratch buffer being
   4488     // reallocated.
   4489     // No defer needed here because it is handled by `wip_members.deinit()` above.
   4490     const bodies_start = astgen.scratch.items.len;
   4491 
   4492     var is_tuple = false;
   4493     const node_tags = tree.nodes.items(.tag);
   4494     for (container_decl.ast.members) |member_node| {
   4495         switch (node_tags[member_node]) {
   4496             .container_field_init => is_tuple = tree.containerFieldInit(member_node).ast.tuple_like,
   4497             .container_field_align => is_tuple = tree.containerFieldAlign(member_node).ast.tuple_like,
   4498             .container_field => is_tuple = tree.containerField(member_node).ast.tuple_like,
   4499             else => continue,
   4500         }
   4501         if (is_tuple) break;
   4502     }
   4503     if (is_tuple) for (container_decl.ast.members) |member_node| {
   4504         switch (node_tags[member_node]) {
   4505             .container_field_init,
   4506             .container_field_align,
   4507             .container_field,
   4508             .@"comptime",
   4509             => continue,
   4510             else => {
   4511                 return astgen.failNode(member_node, "tuple declarations cannot contain declarations", .{});
   4512             },
   4513         }
   4514     };
   4515 
   4516     var known_non_opv = false;
   4517     var known_comptime_only = false;
   4518     for (container_decl.ast.members) |member_node| {
   4519         var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
   4520             .decl => continue,
   4521             .field => |field| field,
   4522         };
   4523 
   4524         if (!is_tuple) {
   4525             member.convertToNonTupleLike(astgen.tree.nodes);
   4526             assert(!member.ast.tuple_like);
   4527 
   4528             const field_name = try astgen.identAsString(member.ast.main_token);
   4529             wip_members.appendToField(field_name);
   4530         } else if (!member.ast.tuple_like) {
   4531             return astgen.failTok(member.ast.main_token, "tuple field has a name", .{});
   4532         }
   4533 
   4534         const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
   4535         wip_members.appendToField(doc_comment_index);
   4536 
   4537         if (member.ast.type_expr == 0) {
   4538             return astgen.failTok(member.ast.main_token, "struct field missing type", .{});
   4539         }
   4540 
   4541         const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr);
   4542         const have_type_body = !block_scope.isEmpty();
   4543         const have_align = member.ast.align_expr != 0;
   4544         const have_value = member.ast.value_expr != 0;
   4545         const is_comptime = member.comptime_token != null;
   4546 
   4547         if (is_comptime and layout == .Packed) {
   4548             return astgen.failTok(member.comptime_token.?, "packed struct fields cannot be marked comptime", .{});
   4549         } else if (is_comptime and layout == .Extern) {
   4550             return astgen.failTok(member.comptime_token.?, "extern struct fields cannot be marked comptime", .{});
   4551         }
   4552 
   4553         if (!is_comptime) {
   4554             known_non_opv = known_non_opv or
   4555                 nodeImpliesMoreThanOnePossibleValue(tree, member.ast.type_expr);
   4556             known_comptime_only = known_comptime_only or
   4557                 nodeImpliesComptimeOnly(tree, member.ast.type_expr);
   4558         }
   4559         wip_members.nextField(bits_per_field, .{ have_align, have_value, is_comptime, have_type_body });
   4560 
   4561         if (have_type_body) {
   4562             if (!block_scope.endsWithNoReturn()) {
   4563                 _ = try block_scope.addBreak(.break_inline, decl_inst, field_type);
   4564             }
   4565             const body = block_scope.instructionsSlice();
   4566             const old_scratch_len = astgen.scratch.items.len;
   4567             try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
   4568             appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
   4569             wip_members.appendToField(@intCast(u32, astgen.scratch.items.len - old_scratch_len));
   4570             block_scope.instructions.items.len = block_scope.instructions_top;
   4571         } else {
   4572             wip_members.appendToField(@enumToInt(field_type));
   4573         }
   4574 
   4575         if (have_align) {
   4576             if (layout == .Packed) {
   4577                 try astgen.appendErrorNode(member.ast.align_expr, "unable to override alignment of packed struct fields", .{});
   4578             }
   4579             const align_ref = try expr(&block_scope, &namespace.base, coerced_align_ri, member.ast.align_expr);
   4580             if (!block_scope.endsWithNoReturn()) {
   4581                 _ = try block_scope.addBreak(.break_inline, decl_inst, align_ref);
   4582             }
   4583             const body = block_scope.instructionsSlice();
   4584             const old_scratch_len = astgen.scratch.items.len;
   4585             try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
   4586             appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
   4587             wip_members.appendToField(@intCast(u32, astgen.scratch.items.len - old_scratch_len));
   4588             block_scope.instructions.items.len = block_scope.instructions_top;
   4589         }
   4590 
   4591         if (have_value) {
   4592             const ri: ResultInfo = .{ .rl = if (field_type == .none) .none else .{ .coerced_ty = field_type } };
   4593 
   4594             const default_inst = try expr(&block_scope, &namespace.base, ri, member.ast.value_expr);
   4595             if (!block_scope.endsWithNoReturn()) {
   4596                 _ = try block_scope.addBreak(.break_inline, decl_inst, default_inst);
   4597             }
   4598             const body = block_scope.instructionsSlice();
   4599             const old_scratch_len = astgen.scratch.items.len;
   4600             try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
   4601             appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
   4602             wip_members.appendToField(@intCast(u32, astgen.scratch.items.len - old_scratch_len));
   4603             block_scope.instructions.items.len = block_scope.instructions_top;
   4604         } else if (member.comptime_token) |comptime_token| {
   4605             return astgen.failTok(comptime_token, "comptime field without default initialization value", .{});
   4606         }
   4607     }
   4608 
   4609     try gz.setStruct(decl_inst, .{
   4610         .src_node = node,
   4611         .layout = layout,
   4612         .fields_len = field_count,
   4613         .decls_len = decl_count,
   4614         .backing_int_ref = backing_int_ref,
   4615         .backing_int_body_len = @intCast(u32, backing_int_body_len),
   4616         .known_non_opv = known_non_opv,
   4617         .known_comptime_only = known_comptime_only,
   4618         .is_tuple = is_tuple,
   4619     });
   4620 
   4621     wip_members.finishBits(bits_per_field);
   4622     const decls_slice = wip_members.declsSlice();
   4623     const fields_slice = wip_members.fieldsSlice();
   4624     const bodies_slice = astgen.scratch.items[bodies_start..];
   4625     try astgen.extra.ensureUnusedCapacity(gpa, backing_int_body_len +
   4626         decls_slice.len + fields_slice.len + bodies_slice.len);
   4627     astgen.extra.appendSliceAssumeCapacity(astgen.scratch.items[scratch_top..][0..backing_int_body_len]);
   4628     astgen.extra.appendSliceAssumeCapacity(decls_slice);
   4629     astgen.extra.appendSliceAssumeCapacity(fields_slice);
   4630     astgen.extra.appendSliceAssumeCapacity(bodies_slice);
   4631 
   4632     block_scope.unstack();
   4633     try gz.addNamespaceCaptures(&namespace);
   4634     return indexToRef(decl_inst);
   4635 }
   4636 
   4637 fn unionDeclInner(
   4638     gz: *GenZir,
   4639     scope: *Scope,
   4640     node: Ast.Node.Index,
   4641     members: []const Ast.Node.Index,
   4642     layout: std.builtin.Type.ContainerLayout,
   4643     arg_node: Ast.Node.Index,
   4644     auto_enum_tok: ?Ast.TokenIndex,
   4645 ) InnerError!Zir.Inst.Ref {
   4646     const decl_inst = try gz.reserveInstructionIndex();
   4647 
   4648     const astgen = gz.astgen;
   4649     const gpa = astgen.gpa;
   4650 
   4651     var namespace: Scope.Namespace = .{
   4652         .parent = scope,
   4653         .node = node,
   4654         .inst = decl_inst,
   4655         .declaring_gz = gz,
   4656     };
   4657     defer namespace.deinit(gpa);
   4658 
   4659     // The union_decl instruction introduces a scope in which the decls of the union
   4660     // are in scope, so that field types, alignments, and default value expressions
   4661     // can refer to decls within the union itself.
   4662     astgen.advanceSourceCursorToNode(node);
   4663     var block_scope: GenZir = .{
   4664         .parent = &namespace.base,
   4665         .decl_node_index = node,
   4666         .decl_line = gz.decl_line,
   4667         .astgen = astgen,
   4668         .force_comptime = true,
   4669         .instructions = gz.instructions,
   4670         .instructions_top = gz.instructions.items.len,
   4671     };
   4672     defer block_scope.unstack();
   4673 
   4674     const decl_count = try astgen.scanDecls(&namespace, members);
   4675     const field_count = @intCast(u32, members.len - decl_count);
   4676 
   4677     if (layout != .Auto and (auto_enum_tok != null or arg_node != 0)) {
   4678         const layout_str = if (layout == .Extern) "extern" else "packed";
   4679         if (arg_node != 0) {
   4680             return astgen.failNode(arg_node, "{s} union does not support enum tag type", .{layout_str});
   4681         } else {
   4682             return astgen.failTok(auto_enum_tok.?, "{s} union does not support enum tag type", .{layout_str});
   4683         }
   4684     }
   4685 
   4686     const arg_inst: Zir.Inst.Ref = if (arg_node != 0)
   4687         try typeExpr(&block_scope, &namespace.base, arg_node)
   4688     else
   4689         .none;
   4690 
   4691     const bits_per_field = 4;
   4692     const max_field_size = 5;
   4693     var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size);
   4694     defer wip_members.deinit();
   4695 
   4696     for (members) |member_node| {
   4697         var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
   4698             .decl => continue,
   4699             .field => |field| field,
   4700         };
   4701         member.convertToNonTupleLike(astgen.tree.nodes);
   4702         if (member.ast.tuple_like) {
   4703             return astgen.failTok(member.ast.main_token, "union field missing name", .{});
   4704         }
   4705         if (member.comptime_token) |comptime_token| {
   4706             return astgen.failTok(comptime_token, "union fields cannot be marked comptime", .{});
   4707         }
   4708 
   4709         const field_name = try astgen.identAsString(member.ast.main_token);
   4710         wip_members.appendToField(field_name);
   4711 
   4712         const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
   4713         wip_members.appendToField(doc_comment_index);
   4714 
   4715         const have_type = member.ast.type_expr != 0;
   4716         const have_align = member.ast.align_expr != 0;
   4717         const have_value = member.ast.value_expr != 0;
   4718         const unused = false;
   4719         wip_members.nextField(bits_per_field, .{ have_type, have_align, have_value, unused });
   4720 
   4721         if (have_type) {
   4722             const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr);
   4723             wip_members.appendToField(@enumToInt(field_type));
   4724         } else if (arg_inst == .none and auto_enum_tok == null) {
   4725             return astgen.failNode(member_node, "union field missing type", .{});
   4726         }
   4727         if (have_align) {
   4728             const align_inst = try expr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = .u32_type } }, member.ast.align_expr);
   4729             wip_members.appendToField(@enumToInt(align_inst));
   4730         }
   4731         if (have_value) {
   4732             if (arg_inst == .none) {
   4733                 return astgen.failNodeNotes(
   4734                     node,
   4735                     "explicitly valued tagged union missing integer tag type",
   4736                     .{},
   4737                     &[_]u32{
   4738                         try astgen.errNoteNode(
   4739                             member.ast.value_expr,
   4740                             "tag value specified here",
   4741                             .{},
   4742                         ),
   4743                     },
   4744                 );
   4745             }
   4746             if (auto_enum_tok == null) {
   4747                 return astgen.failNodeNotes(
   4748                     node,
   4749                     "explicitly valued tagged union requires inferred enum tag type",
   4750                     .{},
   4751                     &[_]u32{
   4752                         try astgen.errNoteNode(
   4753                             member.ast.value_expr,
   4754                             "tag value specified here",
   4755                             .{},
   4756                         ),
   4757                     },
   4758                 );
   4759             }
   4760             const tag_value = try expr(&block_scope, &block_scope.base, .{ .rl = .{ .ty = arg_inst } }, member.ast.value_expr);
   4761             wip_members.appendToField(@enumToInt(tag_value));
   4762         }
   4763     }
   4764 
   4765     if (!block_scope.isEmpty()) {
   4766         _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
   4767     }
   4768 
   4769     const body = block_scope.instructionsSlice();
   4770     const body_len = astgen.countBodyLenAfterFixups(body);
   4771 
   4772     try gz.setUnion(decl_inst, .{
   4773         .src_node = node,
   4774         .layout = layout,
   4775         .tag_type = arg_inst,
   4776         .body_len = body_len,
   4777         .fields_len = field_count,
   4778         .decls_len = decl_count,
   4779         .auto_enum_tag = auto_enum_tok != null,
   4780     });
   4781 
   4782     wip_members.finishBits(bits_per_field);
   4783     const decls_slice = wip_members.declsSlice();
   4784     const fields_slice = wip_members.fieldsSlice();
   4785     try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + body_len + fields_slice.len);
   4786     astgen.extra.appendSliceAssumeCapacity(decls_slice);
   4787     astgen.appendBodyWithFixups(body);
   4788     astgen.extra.appendSliceAssumeCapacity(fields_slice);
   4789 
   4790     block_scope.unstack();
   4791     try gz.addNamespaceCaptures(&namespace);
   4792     return indexToRef(decl_inst);
   4793 }
   4794 
   4795 fn containerDecl(
   4796     gz: *GenZir,
   4797     scope: *Scope,
   4798     ri: ResultInfo,
   4799     node: Ast.Node.Index,
   4800     container_decl: Ast.full.ContainerDecl,
   4801 ) InnerError!Zir.Inst.Ref {
   4802     const astgen = gz.astgen;
   4803     const gpa = astgen.gpa;
   4804     const tree = astgen.tree;
   4805     const token_tags = tree.tokens.items(.tag);
   4806     const node_tags = tree.nodes.items(.tag);
   4807 
   4808     const prev_fn_block = astgen.fn_block;
   4809     astgen.fn_block = null;
   4810     defer astgen.fn_block = prev_fn_block;
   4811 
   4812     // We must not create any types until Sema. Here the goal is only to generate
   4813     // ZIR for all the field types, alignments, and default value expressions.
   4814 
   4815     switch (token_tags[container_decl.ast.main_token]) {
   4816         .keyword_struct => {
   4817             const layout = if (container_decl.layout_token) |t| switch (token_tags[t]) {
   4818                 .keyword_packed => std.builtin.Type.ContainerLayout.Packed,
   4819                 .keyword_extern => std.builtin.Type.ContainerLayout.Extern,
   4820                 else => unreachable,
   4821             } else std.builtin.Type.ContainerLayout.Auto;
   4822 
   4823             const result = try structDeclInner(gz, scope, node, container_decl, layout, container_decl.ast.arg);
   4824             return rvalue(gz, ri, result, node);
   4825         },
   4826         .keyword_union => {
   4827             const layout = if (container_decl.layout_token) |t| switch (token_tags[t]) {
   4828                 .keyword_packed => std.builtin.Type.ContainerLayout.Packed,
   4829                 .keyword_extern => std.builtin.Type.ContainerLayout.Extern,
   4830                 else => unreachable,
   4831             } else std.builtin.Type.ContainerLayout.Auto;
   4832 
   4833             const result = try unionDeclInner(gz, scope, node, container_decl.ast.members, layout, container_decl.ast.arg, container_decl.ast.enum_token);
   4834             return rvalue(gz, ri, result, node);
   4835         },
   4836         .keyword_enum => {
   4837             if (container_decl.layout_token) |t| {
   4838                 return astgen.failTok(t, "enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", .{});
   4839             }
   4840             // Count total fields as well as how many have explicitly provided tag values.
   4841             const counts = blk: {
   4842                 var values: usize = 0;
   4843                 var total_fields: usize = 0;
   4844                 var decls: usize = 0;
   4845                 var nonexhaustive_node: Ast.Node.Index = 0;
   4846                 var nonfinal_nonexhaustive = false;
   4847                 for (container_decl.ast.members) |member_node| {
   4848                     var member = switch (node_tags[member_node]) {
   4849                         .container_field_init => tree.containerFieldInit(member_node),
   4850                         .container_field_align => tree.containerFieldAlign(member_node),
   4851                         .container_field => tree.containerField(member_node),
   4852                         else => {
   4853                             decls += 1;
   4854                             continue;
   4855                         },
   4856                     };
   4857                     member.convertToNonTupleLike(astgen.tree.nodes);
   4858                     if (member.ast.tuple_like) {
   4859                         return astgen.failTok(member.ast.main_token, "enum field missing name", .{});
   4860                     }
   4861                     if (member.comptime_token) |comptime_token| {
   4862                         return astgen.failTok(comptime_token, "enum fields cannot be marked comptime", .{});
   4863                     }
   4864                     if (member.ast.type_expr != 0) {
   4865                         return astgen.failNodeNotes(
   4866                             member.ast.type_expr,
   4867                             "enum fields do not have types",
   4868                             .{},
   4869                             &[_]u32{
   4870                                 try astgen.errNoteNode(
   4871                                     node,
   4872                                     "consider 'union(enum)' here to make it a tagged union",
   4873                                     .{},
   4874                                 ),
   4875                             },
   4876                         );
   4877                     }
   4878                     if (member.ast.align_expr != 0) {
   4879                         return astgen.failNode(member.ast.align_expr, "enum fields cannot be aligned", .{});
   4880                     }
   4881 
   4882                     const name_token = member.ast.main_token;
   4883                     if (mem.eql(u8, tree.tokenSlice(name_token), "_")) {
   4884                         if (nonexhaustive_node != 0) {
   4885                             return astgen.failNodeNotes(
   4886                                 member_node,
   4887                                 "redundant non-exhaustive enum mark",
   4888                                 .{},
   4889                                 &[_]u32{
   4890                                     try astgen.errNoteNode(
   4891                                         nonexhaustive_node,
   4892                                         "other mark here",
   4893                                         .{},
   4894                                     ),
   4895                                 },
   4896                             );
   4897                         }
   4898                         nonexhaustive_node = member_node;
   4899                         if (member.ast.value_expr != 0) {
   4900                             return astgen.failNode(member.ast.value_expr, "'_' is used to mark an enum as non-exhaustive and cannot be assigned a value", .{});
   4901                         }
   4902                         continue;
   4903                     } else if (nonexhaustive_node != 0) {
   4904                         nonfinal_nonexhaustive = true;
   4905                     }
   4906                     total_fields += 1;
   4907                     if (member.ast.value_expr != 0) {
   4908                         if (container_decl.ast.arg == 0) {
   4909                             return astgen.failNode(member.ast.value_expr, "value assigned to enum tag with inferred tag type", .{});
   4910                         }
   4911                         values += 1;
   4912                     }
   4913                 }
   4914                 if (nonfinal_nonexhaustive) {
   4915                     return astgen.failNode(nonexhaustive_node, "'_' field of non-exhaustive enum must be last", .{});
   4916                 }
   4917                 break :blk .{
   4918                     .total_fields = total_fields,
   4919                     .values = values,
   4920                     .decls = decls,
   4921                     .nonexhaustive_node = nonexhaustive_node,
   4922                 };
   4923             };
   4924             if (counts.nonexhaustive_node != 0 and container_decl.ast.arg == 0) {
   4925                 try astgen.appendErrorNodeNotes(
   4926                     node,
   4927                     "non-exhaustive enum missing integer tag type",
   4928                     .{},
   4929                     &[_]u32{
   4930                         try astgen.errNoteNode(
   4931                             counts.nonexhaustive_node,
   4932                             "marked non-exhaustive here",
   4933                             .{},
   4934                         ),
   4935                     },
   4936                 );
   4937             }
   4938             // In this case we must generate ZIR code for the tag values, similar to
   4939             // how structs are handled above.
   4940             const nonexhaustive = counts.nonexhaustive_node != 0;
   4941 
   4942             const decl_inst = try gz.reserveInstructionIndex();
   4943 
   4944             var namespace: Scope.Namespace = .{
   4945                 .parent = scope,
   4946                 .node = node,
   4947                 .inst = decl_inst,
   4948                 .declaring_gz = gz,
   4949             };
   4950             defer namespace.deinit(gpa);
   4951 
   4952             // The enum_decl instruction introduces a scope in which the decls of the enum
   4953             // are in scope, so that tag values can refer to decls within the enum itself.
   4954             astgen.advanceSourceCursorToNode(node);
   4955             var block_scope: GenZir = .{
   4956                 .parent = &namespace.base,
   4957                 .decl_node_index = node,
   4958                 .decl_line = gz.decl_line,
   4959                 .astgen = astgen,
   4960                 .force_comptime = true,
   4961                 .instructions = gz.instructions,
   4962                 .instructions_top = gz.instructions.items.len,
   4963             };
   4964             defer block_scope.unstack();
   4965 
   4966             _ = try astgen.scanDecls(&namespace, container_decl.ast.members);
   4967 
   4968             const arg_inst: Zir.Inst.Ref = if (container_decl.ast.arg != 0)
   4969                 try comptimeExpr(&block_scope, &namespace.base, .{ .rl = .{ .ty = .type_type } }, container_decl.ast.arg)
   4970             else
   4971                 .none;
   4972 
   4973             const bits_per_field = 1;
   4974             const max_field_size = 3;
   4975             var wip_members = try WipMembers.init(gpa, &astgen.scratch, @intCast(u32, counts.decls), @intCast(u32, counts.total_fields), bits_per_field, max_field_size);
   4976             defer wip_members.deinit();
   4977 
   4978             for (container_decl.ast.members) |member_node| {
   4979                 if (member_node == counts.nonexhaustive_node)
   4980                     continue;
   4981                 var member = switch (try containerMember(&block_scope, &namespace.base, &wip_members, member_node)) {
   4982                     .decl => continue,
   4983                     .field => |field| field,
   4984                 };
   4985                 member.convertToNonTupleLike(astgen.tree.nodes);
   4986                 assert(member.comptime_token == null);
   4987                 assert(member.ast.type_expr == 0);
   4988                 assert(member.ast.align_expr == 0);
   4989 
   4990                 const field_name = try astgen.identAsString(member.ast.main_token);
   4991                 wip_members.appendToField(field_name);
   4992 
   4993                 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
   4994                 wip_members.appendToField(doc_comment_index);
   4995 
   4996                 const have_value = member.ast.value_expr != 0;
   4997                 wip_members.nextField(bits_per_field, .{have_value});
   4998 
   4999                 if (have_value) {
   5000                     if (arg_inst == .none) {
   5001                         return astgen.failNodeNotes(
   5002                             node,
   5003                             "explicitly valued enum missing integer tag type",
   5004                             .{},
   5005                             &[_]u32{
   5006                                 try astgen.errNoteNode(
   5007                                     member.ast.value_expr,
   5008                                     "tag value specified here",
   5009                                     .{},
   5010                                 ),
   5011                             },
   5012                         );
   5013                     }
   5014                     const tag_value_inst = try expr(&block_scope, &namespace.base, .{ .rl = .{ .ty = arg_inst } }, member.ast.value_expr);
   5015                     wip_members.appendToField(@enumToInt(tag_value_inst));
   5016                 }
   5017             }
   5018 
   5019             if (!block_scope.isEmpty()) {
   5020                 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
   5021             }
   5022 
   5023             const body = block_scope.instructionsSlice();
   5024             const body_len = astgen.countBodyLenAfterFixups(body);
   5025 
   5026             try gz.setEnum(decl_inst, .{
   5027                 .src_node = node,
   5028                 .nonexhaustive = nonexhaustive,
   5029                 .tag_type = arg_inst,
   5030                 .body_len = body_len,
   5031                 .fields_len = @intCast(u32, counts.total_fields),
   5032                 .decls_len = @intCast(u32, counts.decls),
   5033             });
   5034 
   5035             wip_members.finishBits(bits_per_field);
   5036             const decls_slice = wip_members.declsSlice();
   5037             const fields_slice = wip_members.fieldsSlice();
   5038             try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + body_len + fields_slice.len);
   5039             astgen.extra.appendSliceAssumeCapacity(decls_slice);
   5040             astgen.appendBodyWithFixups(body);
   5041             astgen.extra.appendSliceAssumeCapacity(fields_slice);
   5042 
   5043             block_scope.unstack();
   5044             try gz.addNamespaceCaptures(&namespace);
   5045             return rvalue(gz, ri, indexToRef(decl_inst), node);
   5046         },
   5047         .keyword_opaque => {
   5048             assert(container_decl.ast.arg == 0);
   5049 
   5050             const decl_inst = try gz.reserveInstructionIndex();
   5051 
   5052             var namespace: Scope.Namespace = .{
   5053                 .parent = scope,
   5054                 .node = node,
   5055                 .inst = decl_inst,
   5056                 .declaring_gz = gz,
   5057             };
   5058             defer namespace.deinit(gpa);
   5059 
   5060             astgen.advanceSourceCursorToNode(node);
   5061             var block_scope: GenZir = .{
   5062                 .parent = &namespace.base,
   5063                 .decl_node_index = node,
   5064                 .decl_line = gz.decl_line,
   5065                 .astgen = astgen,
   5066                 .force_comptime = true,
   5067                 .instructions = gz.instructions,
   5068                 .instructions_top = gz.instructions.items.len,
   5069             };
   5070             defer block_scope.unstack();
   5071 
   5072             const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members);
   5073 
   5074             var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, 0, 0, 0);
   5075             defer wip_members.deinit();
   5076 
   5077             for (container_decl.ast.members) |member_node| {
   5078                 const res = try containerMember(&block_scope, &namespace.base, &wip_members, member_node);
   5079                 if (res == .field) {
   5080                     return astgen.failNode(member_node, "opaque types cannot have fields", .{});
   5081                 }
   5082             }
   5083 
   5084             try gz.setOpaque(decl_inst, .{
   5085                 .src_node = node,
   5086                 .decls_len = decl_count,
   5087             });
   5088 
   5089             wip_members.finishBits(0);
   5090             const decls_slice = wip_members.declsSlice();
   5091             try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len);
   5092             astgen.extra.appendSliceAssumeCapacity(decls_slice);
   5093 
   5094             block_scope.unstack();
   5095             try gz.addNamespaceCaptures(&namespace);
   5096             return rvalue(gz, ri, indexToRef(decl_inst), node);
   5097         },
   5098         else => unreachable,
   5099     }
   5100 }
   5101 
   5102 const ContainerMemberResult = union(enum) { decl, field: Ast.full.ContainerField };
   5103 
   5104 fn containerMember(
   5105     gz: *GenZir,
   5106     scope: *Scope,
   5107     wip_members: *WipMembers,
   5108     member_node: Ast.Node.Index,
   5109 ) InnerError!ContainerMemberResult {
   5110     const astgen = gz.astgen;
   5111     const tree = astgen.tree;
   5112     const node_tags = tree.nodes.items(.tag);
   5113     const node_datas = tree.nodes.items(.data);
   5114     switch (node_tags[member_node]) {
   5115         .container_field_init => return ContainerMemberResult{ .field = tree.containerFieldInit(member_node) },
   5116         .container_field_align => return ContainerMemberResult{ .field = tree.containerFieldAlign(member_node) },
   5117         .container_field => return ContainerMemberResult{ .field = tree.containerField(member_node) },
   5118 
   5119         .fn_decl => {
   5120             const fn_proto = node_datas[member_node].lhs;
   5121             const body = node_datas[member_node].rhs;
   5122             switch (node_tags[fn_proto]) {
   5123                 .fn_proto_simple => {
   5124                     var params: [1]Ast.Node.Index = undefined;
   5125                     astgen.fnDecl(gz, scope, wip_members, member_node, body, tree.fnProtoSimple(&params, fn_proto)) catch |err| switch (err) {
   5126                         error.OutOfMemory => return error.OutOfMemory,
   5127                         error.AnalysisFail => {},
   5128                     };
   5129                 },
   5130                 .fn_proto_multi => {
   5131                     astgen.fnDecl(gz, scope, wip_members, member_node, body, tree.fnProtoMulti(fn_proto)) catch |err| switch (err) {
   5132                         error.OutOfMemory => return error.OutOfMemory,
   5133                         error.AnalysisFail => {},
   5134                     };
   5135                 },
   5136                 .fn_proto_one => {
   5137                     var params: [1]Ast.Node.Index = undefined;
   5138                     astgen.fnDecl(gz, scope, wip_members, member_node, body, tree.fnProtoOne(&params, fn_proto)) catch |err| switch (err) {
   5139                         error.OutOfMemory => return error.OutOfMemory,
   5140                         error.AnalysisFail => {},
   5141                     };
   5142                 },
   5143                 .fn_proto => {
   5144                     astgen.fnDecl(gz, scope, wip_members, member_node, body, tree.fnProto(fn_proto)) catch |err| switch (err) {
   5145                         error.OutOfMemory => return error.OutOfMemory,
   5146                         error.AnalysisFail => {},
   5147                     };
   5148                 },
   5149                 else => unreachable,
   5150             }
   5151         },
   5152         .fn_proto_simple => {
   5153             var params: [1]Ast.Node.Index = undefined;
   5154             astgen.fnDecl(gz, scope, wip_members, member_node, 0, tree.fnProtoSimple(&params, member_node)) catch |err| switch (err) {
   5155                 error.OutOfMemory => return error.OutOfMemory,
   5156                 error.AnalysisFail => {},
   5157             };
   5158         },
   5159         .fn_proto_multi => {
   5160             astgen.fnDecl(gz, scope, wip_members, member_node, 0, tree.fnProtoMulti(member_node)) catch |err| switch (err) {
   5161                 error.OutOfMemory => return error.OutOfMemory,
   5162                 error.AnalysisFail => {},
   5163             };
   5164         },
   5165         .fn_proto_one => {
   5166             var params: [1]Ast.Node.Index = undefined;
   5167             astgen.fnDecl(gz, scope, wip_members, member_node, 0, tree.fnProtoOne(&params, member_node)) catch |err| switch (err) {
   5168                 error.OutOfMemory => return error.OutOfMemory,
   5169                 error.AnalysisFail => {},
   5170             };
   5171         },
   5172         .fn_proto => {
   5173             astgen.fnDecl(gz, scope, wip_members, member_node, 0, tree.fnProto(member_node)) catch |err| switch (err) {
   5174                 error.OutOfMemory => return error.OutOfMemory,
   5175                 error.AnalysisFail => {},
   5176             };
   5177         },
   5178 
   5179         .global_var_decl => {
   5180             astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.globalVarDecl(member_node)) catch |err| switch (err) {
   5181                 error.OutOfMemory => return error.OutOfMemory,
   5182                 error.AnalysisFail => {},
   5183             };
   5184         },
   5185         .local_var_decl => {
   5186             astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.localVarDecl(member_node)) catch |err| switch (err) {
   5187                 error.OutOfMemory => return error.OutOfMemory,
   5188                 error.AnalysisFail => {},
   5189             };
   5190         },
   5191         .simple_var_decl => {
   5192             astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.simpleVarDecl(member_node)) catch |err| switch (err) {
   5193                 error.OutOfMemory => return error.OutOfMemory,
   5194                 error.AnalysisFail => {},
   5195             };
   5196         },
   5197         .aligned_var_decl => {
   5198             astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.alignedVarDecl(member_node)) catch |err| switch (err) {
   5199                 error.OutOfMemory => return error.OutOfMemory,
   5200                 error.AnalysisFail => {},
   5201             };
   5202         },
   5203 
   5204         .@"comptime" => {
   5205             astgen.comptimeDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
   5206                 error.OutOfMemory => return error.OutOfMemory,
   5207                 error.AnalysisFail => {},
   5208             };
   5209         },
   5210         .@"usingnamespace" => {
   5211             astgen.usingnamespaceDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
   5212                 error.OutOfMemory => return error.OutOfMemory,
   5213                 error.AnalysisFail => {},
   5214             };
   5215         },
   5216         .test_decl => {
   5217             astgen.testDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
   5218                 error.OutOfMemory => return error.OutOfMemory,
   5219                 error.AnalysisFail => {},
   5220             };
   5221         },
   5222         else => unreachable,
   5223     }
   5224     return .decl;
   5225 }
   5226 
   5227 fn errorSetDecl(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   5228     const astgen = gz.astgen;
   5229     const gpa = astgen.gpa;
   5230     const tree = astgen.tree;
   5231     const main_tokens = tree.nodes.items(.main_token);
   5232     const token_tags = tree.tokens.items(.tag);
   5233 
   5234     const payload_index = try reserveExtra(astgen, @typeInfo(Zir.Inst.ErrorSetDecl).Struct.fields.len);
   5235     var fields_len: usize = 0;
   5236     {
   5237         var idents: std.AutoHashMapUnmanaged(u32, Ast.TokenIndex) = .{};
   5238         defer idents.deinit(gpa);
   5239 
   5240         const error_token = main_tokens[node];
   5241         var tok_i = error_token + 2;
   5242         while (true) : (tok_i += 1) {
   5243             switch (token_tags[tok_i]) {
   5244                 .doc_comment, .comma => {},
   5245                 .identifier => {
   5246                     const str_index = try astgen.identAsString(tok_i);
   5247                     const gop = try idents.getOrPut(gpa, str_index);
   5248                     if (gop.found_existing) {
   5249                         const name = try gpa.dupe(u8, mem.span(astgen.nullTerminatedString(str_index)));
   5250                         defer gpa.free(name);
   5251                         return astgen.failTokNotes(
   5252                             tok_i,
   5253                             "duplicate error set field '{s}'",
   5254                             .{name},
   5255                             &[_]u32{
   5256                                 try astgen.errNoteTok(
   5257                                     gop.value_ptr.*,
   5258                                     "previous declaration here",
   5259                                     .{},
   5260                                 ),
   5261                             },
   5262                         );
   5263                     }
   5264                     gop.value_ptr.* = tok_i;
   5265 
   5266                     try astgen.extra.ensureUnusedCapacity(gpa, 2);
   5267                     astgen.extra.appendAssumeCapacity(str_index);
   5268                     const doc_comment_index = try astgen.docCommentAsString(tok_i);
   5269                     astgen.extra.appendAssumeCapacity(doc_comment_index);
   5270                     fields_len += 1;
   5271                 },
   5272                 .r_brace => break,
   5273                 else => unreachable,
   5274             }
   5275         }
   5276     }
   5277 
   5278     setExtra(astgen, payload_index, Zir.Inst.ErrorSetDecl{
   5279         .fields_len = @intCast(u32, fields_len),
   5280     });
   5281     const result = try gz.addPlNodePayloadIndex(.error_set_decl, node, payload_index);
   5282     return rvalue(gz, ri, result, node);
   5283 }
   5284 
   5285 fn tryExpr(
   5286     parent_gz: *GenZir,
   5287     scope: *Scope,
   5288     ri: ResultInfo,
   5289     node: Ast.Node.Index,
   5290     operand_node: Ast.Node.Index,
   5291 ) InnerError!Zir.Inst.Ref {
   5292     const astgen = parent_gz.astgen;
   5293 
   5294     const fn_block = astgen.fn_block orelse {
   5295         return astgen.failNode(node, "'try' outside function scope", .{});
   5296     };
   5297 
   5298     if (parent_gz.any_defer_node != 0) {
   5299         return astgen.failNodeNotes(node, "'try' not allowed inside defer expression", .{}, &.{
   5300             try astgen.errNoteNode(
   5301                 parent_gz.any_defer_node,
   5302                 "defer expression here",
   5303                 .{},
   5304             ),
   5305         });
   5306     }
   5307 
   5308     // Ensure debug line/column information is emitted for this try expression.
   5309     // Then we will save the line/column so that we can emit another one that goes
   5310     // "backwards" because we want to evaluate the operand, but then put the debug
   5311     // info back at the try keyword for error return tracing.
   5312     if (!parent_gz.force_comptime) {
   5313         try emitDbgNode(parent_gz, node);
   5314     }
   5315     const try_line = astgen.source_line - parent_gz.decl_line;
   5316     const try_column = astgen.source_column;
   5317 
   5318     const operand_ri: ResultInfo = switch (ri.rl) {
   5319         .ref => .{ .rl = .ref, .ctx = .error_handling_expr },
   5320         else => .{ .rl = .none, .ctx = .error_handling_expr },
   5321     };
   5322     // This could be a pointer or value depending on the `ri` parameter.
   5323     const operand = try reachableExpr(parent_gz, scope, operand_ri, operand_node, node);
   5324     const is_inline = parent_gz.force_comptime;
   5325     const is_inline_bit = @as(u2, @boolToInt(is_inline));
   5326     const is_ptr_bit = @as(u2, @boolToInt(operand_ri.rl == .ref)) << 1;
   5327     const block_tag: Zir.Inst.Tag = switch (is_inline_bit | is_ptr_bit) {
   5328         0b00 => .@"try",
   5329         0b01 => .@"try",
   5330         //0b01 => .try_inline,
   5331         0b10 => .try_ptr,
   5332         0b11 => .try_ptr,
   5333         //0b11 => .try_ptr_inline,
   5334     };
   5335     const try_inst = try parent_gz.makeBlockInst(block_tag, node);
   5336     try parent_gz.instructions.append(astgen.gpa, try_inst);
   5337 
   5338     var else_scope = parent_gz.makeSubBlock(scope);
   5339     defer else_scope.unstack();
   5340 
   5341     const err_tag = switch (ri.rl) {
   5342         .ref => Zir.Inst.Tag.err_union_code_ptr,
   5343         else => Zir.Inst.Tag.err_union_code,
   5344     };
   5345     const err_code = try else_scope.addUnNode(err_tag, operand, node);
   5346     try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code });
   5347     try emitDbgStmt(&else_scope, try_line, try_column);
   5348     _ = try else_scope.addUnNode(.ret_node, err_code, node);
   5349 
   5350     try else_scope.setTryBody(try_inst, operand);
   5351     const result = indexToRef(try_inst);
   5352     switch (ri.rl) {
   5353         .ref => return result,
   5354         else => return rvalue(parent_gz, ri, result, node),
   5355     }
   5356 }
   5357 
   5358 fn orelseCatchExpr(
   5359     parent_gz: *GenZir,
   5360     scope: *Scope,
   5361     ri: ResultInfo,
   5362     node: Ast.Node.Index,
   5363     lhs: Ast.Node.Index,
   5364     cond_op: Zir.Inst.Tag,
   5365     unwrap_op: Zir.Inst.Tag,
   5366     unwrap_code_op: Zir.Inst.Tag,
   5367     rhs: Ast.Node.Index,
   5368     payload_token: ?Ast.TokenIndex,
   5369 ) InnerError!Zir.Inst.Ref {
   5370     const astgen = parent_gz.astgen;
   5371     const tree = astgen.tree;
   5372 
   5373     const do_err_trace = astgen.fn_block != null and (cond_op == .is_non_err or cond_op == .is_non_err_ptr);
   5374 
   5375     var block_scope = parent_gz.makeSubBlock(scope);
   5376     block_scope.setBreakResultInfo(ri);
   5377     defer block_scope.unstack();
   5378 
   5379     const operand_ri: ResultInfo = switch (block_scope.break_result_info.rl) {
   5380         .ref => .{ .rl = .ref, .ctx = if (do_err_trace) .error_handling_expr else .none },
   5381         else => .{ .rl = .none, .ctx = if (do_err_trace) .error_handling_expr else .none },
   5382     };
   5383     block_scope.break_count += 1;
   5384     // This could be a pointer or value depending on the `operand_ri` parameter.
   5385     // We cannot use `block_scope.break_result_info` because that has the bare
   5386     // type, whereas this expression has the optional type. Later we make
   5387     // up for this fact by calling rvalue on the else branch.
   5388     const operand = try reachableExpr(&block_scope, &block_scope.base, operand_ri, lhs, rhs);
   5389     const cond = try block_scope.addUnNode(cond_op, operand, node);
   5390     const condbr_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .condbr_inline else .condbr;
   5391     const condbr = try block_scope.addCondBr(condbr_tag, node);
   5392 
   5393     const block_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .block_inline else .block;
   5394     const block = try parent_gz.makeBlockInst(block_tag, node);
   5395     try block_scope.setBlockBody(block);
   5396     // block_scope unstacked now, can add new instructions to parent_gz
   5397     try parent_gz.instructions.append(astgen.gpa, block);
   5398 
   5399     var then_scope = block_scope.makeSubBlock(scope);
   5400     defer then_scope.unstack();
   5401 
   5402     // This could be a pointer or value depending on `unwrap_op`.
   5403     const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node);
   5404     const then_result = switch (ri.rl) {
   5405         .ref => unwrapped_payload,
   5406         else => try rvalue(&then_scope, block_scope.break_result_info, unwrapped_payload, node),
   5407     };
   5408 
   5409     var else_scope = block_scope.makeSubBlock(scope);
   5410     defer else_scope.unstack();
   5411 
   5412     // We know that the operand (almost certainly) modified the error return trace,
   5413     // so signal to Sema that it should save the new index for restoring later.
   5414     if (do_err_trace and nodeMayAppendToErrorTrace(tree, lhs))
   5415         _ = try else_scope.addSaveErrRetIndex(.always);
   5416 
   5417     var err_val_scope: Scope.LocalVal = undefined;
   5418     const else_sub_scope = blk: {
   5419         const payload = payload_token orelse break :blk &else_scope.base;
   5420         const err_str = tree.tokenSlice(payload);
   5421         if (mem.eql(u8, err_str, "_")) {
   5422             return astgen.failTok(payload, "discard of error capture; omit it instead", .{});
   5423         }
   5424         const err_name = try astgen.identAsString(payload);
   5425 
   5426         try astgen.detectLocalShadowing(scope, err_name, payload, err_str, .capture);
   5427 
   5428         err_val_scope = .{
   5429             .parent = &else_scope.base,
   5430             .gen_zir = &else_scope,
   5431             .name = err_name,
   5432             .inst = try else_scope.addUnNode(unwrap_code_op, operand, node),
   5433             .token_src = payload,
   5434             .id_cat = .capture,
   5435         };
   5436         break :blk &err_val_scope.base;
   5437     };
   5438 
   5439     const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_info, rhs);
   5440     if (!else_scope.endsWithNoReturn()) {
   5441         block_scope.break_count += 1;
   5442 
   5443         // As our last action before the break, "pop" the error trace if needed
   5444         if (do_err_trace)
   5445             try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, rhs, else_result);
   5446     }
   5447     try checkUsed(parent_gz, &else_scope.base, else_sub_scope);
   5448 
   5449     // We hold off on the break instructions as well as copying the then/else
   5450     // instructions into place until we know whether to keep store_to_block_ptr
   5451     // instructions or not.
   5452 
   5453     const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break";
   5454     const result = try finishThenElseBlock(
   5455         parent_gz,
   5456         ri,
   5457         node,
   5458         &block_scope,
   5459         &then_scope,
   5460         &else_scope,
   5461         condbr,
   5462         cond,
   5463         then_result,
   5464         else_result,
   5465         block,
   5466         block,
   5467         break_tag,
   5468     );
   5469     return result;
   5470 }
   5471 
   5472 /// Supports `else_scope` stacked on `then_scope` stacked on `block_scope`. Unstacks `else_scope` then `then_scope`.
   5473 fn finishThenElseBlock(
   5474     parent_gz: *GenZir,
   5475     ri: ResultInfo,
   5476     node: Ast.Node.Index,
   5477     block_scope: *GenZir,
   5478     then_scope: *GenZir,
   5479     else_scope: *GenZir,
   5480     condbr: Zir.Inst.Index,
   5481     cond: Zir.Inst.Ref,
   5482     then_result: Zir.Inst.Ref,
   5483     else_result: Zir.Inst.Ref,
   5484     main_block: Zir.Inst.Index,
   5485     then_break_block: Zir.Inst.Index,
   5486     break_tag: Zir.Inst.Tag,
   5487 ) InnerError!Zir.Inst.Ref {
   5488     // We now have enough information to decide whether the result instruction should
   5489     // be communicated via result location pointer or break instructions.
   5490     const strat = ri.rl.strategy(block_scope);
   5491     // else_scope may be stacked on then_scope, so check for no-return on then_scope manually
   5492     const tags = parent_gz.astgen.instructions.items(.tag);
   5493     const then_slice = then_scope.instructionsSliceUpto(else_scope);
   5494     const then_no_return = then_slice.len > 0 and tags[then_slice[then_slice.len - 1]].isNoReturn();
   5495     const else_no_return = else_scope.endsWithNoReturn();
   5496 
   5497     switch (strat.tag) {
   5498         .break_void => {
   5499             const then_break = if (!then_no_return) try then_scope.makeBreak(break_tag, then_break_block, .void_value) else 0;
   5500             const else_break = if (!else_no_return) try else_scope.makeBreak(break_tag, main_block, .void_value) else 0;
   5501             assert(!strat.elide_store_to_block_ptr_instructions);
   5502             try setCondBrPayload(condbr, cond, then_scope, then_break, else_scope, else_break);
   5503             return indexToRef(main_block);
   5504         },
   5505         .break_operand => {
   5506             const then_break = if (!then_no_return) try then_scope.makeBreak(break_tag, then_break_block, then_result) else 0;
   5507             const else_break = if (else_result == .none)
   5508                 try else_scope.makeBreak(break_tag, main_block, .void_value)
   5509             else if (!else_no_return)
   5510                 try else_scope.makeBreak(break_tag, main_block, else_result)
   5511             else
   5512                 0;
   5513 
   5514             if (strat.elide_store_to_block_ptr_instructions) {
   5515                 try setCondBrPayloadElideBlockStorePtr(condbr, cond, then_scope, then_break, else_scope, else_break, block_scope.rl_ptr);
   5516             } else {
   5517                 try setCondBrPayload(condbr, cond, then_scope, then_break, else_scope, else_break);
   5518             }
   5519             const block_ref = indexToRef(main_block);
   5520             switch (ri.rl) {
   5521                 .ref => return block_ref,
   5522                 else => return rvalue(parent_gz, ri, block_ref, node),
   5523             }
   5524         },
   5525     }
   5526 }
   5527 
   5528 /// Return whether the identifier names of two tokens are equal. Resolves @""
   5529 /// tokens without allocating.
   5530 /// OK in theory it could do it without allocating. This implementation
   5531 /// allocates when the @"" form is used.
   5532 fn tokenIdentEql(astgen: *AstGen, token1: Ast.TokenIndex, token2: Ast.TokenIndex) !bool {
   5533     const ident_name_1 = try astgen.identifierTokenString(token1);
   5534     const ident_name_2 = try astgen.identifierTokenString(token2);
   5535     return mem.eql(u8, ident_name_1, ident_name_2);
   5536 }
   5537 
   5538 fn fieldAccess(
   5539     gz: *GenZir,
   5540     scope: *Scope,
   5541     ri: ResultInfo,
   5542     node: Ast.Node.Index,
   5543 ) InnerError!Zir.Inst.Ref {
   5544     switch (ri.rl) {
   5545         .ref => return addFieldAccess(.field_ptr, gz, scope, .{ .rl = .ref }, node),
   5546         else => {
   5547             const access = try addFieldAccess(.field_val, gz, scope, .{ .rl = .none }, node);
   5548             return rvalue(gz, ri, access, node);
   5549         },
   5550     }
   5551 }
   5552 
   5553 fn addFieldAccess(
   5554     tag: Zir.Inst.Tag,
   5555     gz: *GenZir,
   5556     scope: *Scope,
   5557     lhs_ri: ResultInfo,
   5558     node: Ast.Node.Index,
   5559 ) InnerError!Zir.Inst.Ref {
   5560     const astgen = gz.astgen;
   5561     const tree = astgen.tree;
   5562     const main_tokens = tree.nodes.items(.main_token);
   5563     const node_datas = tree.nodes.items(.data);
   5564 
   5565     const object_node = node_datas[node].lhs;
   5566     const dot_token = main_tokens[node];
   5567     const field_ident = dot_token + 1;
   5568     const str_index = try astgen.identAsString(field_ident);
   5569     const lhs = try expr(gz, scope, lhs_ri, object_node);
   5570 
   5571     maybeAdvanceSourceCursorToMainToken(gz, node);
   5572     const line = gz.astgen.source_line - gz.decl_line;
   5573     const column = gz.astgen.source_column;
   5574     try emitDbgStmt(gz, line, column);
   5575 
   5576     return gz.addPlNode(tag, node, Zir.Inst.Field{
   5577         .lhs = lhs,
   5578         .field_name_start = str_index,
   5579     });
   5580 }
   5581 
   5582 fn arrayAccess(
   5583     gz: *GenZir,
   5584     scope: *Scope,
   5585     ri: ResultInfo,
   5586     node: Ast.Node.Index,
   5587 ) InnerError!Zir.Inst.Ref {
   5588     const tree = gz.astgen.tree;
   5589     const node_datas = tree.nodes.items(.data);
   5590     switch (ri.rl) {
   5591         .ref => {
   5592             const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
   5593 
   5594             maybeAdvanceSourceCursorToMainToken(gz, node);
   5595             const line = gz.astgen.source_line - gz.decl_line;
   5596             const column = gz.astgen.source_column;
   5597 
   5598             const rhs = try expr(gz, scope, .{ .rl = .{ .ty = .usize_type } }, node_datas[node].rhs);
   5599             try emitDbgStmt(gz, line, column);
   5600 
   5601             return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
   5602         },
   5603         else => {
   5604             const lhs = try expr(gz, scope, .{ .rl = .none }, node_datas[node].lhs);
   5605 
   5606             maybeAdvanceSourceCursorToMainToken(gz, node);
   5607             const line = gz.astgen.source_line - gz.decl_line;
   5608             const column = gz.astgen.source_column;
   5609 
   5610             const rhs = try expr(gz, scope, .{ .rl = .{ .ty = .usize_type } }, node_datas[node].rhs);
   5611             try emitDbgStmt(gz, line, column);
   5612 
   5613             return rvalue(gz, ri, try gz.addPlNode(.elem_val_node, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs }), node);
   5614         },
   5615     }
   5616 }
   5617 
   5618 fn simpleBinOp(
   5619     gz: *GenZir,
   5620     scope: *Scope,
   5621     ri: ResultInfo,
   5622     node: Ast.Node.Index,
   5623     op_inst_tag: Zir.Inst.Tag,
   5624 ) InnerError!Zir.Inst.Ref {
   5625     const astgen = gz.astgen;
   5626     const tree = astgen.tree;
   5627     const node_datas = tree.nodes.items(.data);
   5628 
   5629     if (op_inst_tag == .cmp_neq or op_inst_tag == .cmp_eq) {
   5630         const node_tags = tree.nodes.items(.tag);
   5631         const str = if (op_inst_tag == .cmp_eq) "==" else "!=";
   5632         if (node_tags[node_datas[node].lhs] == .string_literal or
   5633             node_tags[node_datas[node].rhs] == .string_literal)
   5634             return astgen.failNode(node, "cannot compare strings with {s}", .{str});
   5635     }
   5636 
   5637     const lhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].lhs, node);
   5638     var line: u32 = undefined;
   5639     var column: u32 = undefined;
   5640     switch (op_inst_tag) {
   5641         .add, .sub, .mul, .div, .mod_rem => {
   5642             maybeAdvanceSourceCursorToMainToken(gz, node);
   5643             line = gz.astgen.source_line - gz.decl_line;
   5644             column = gz.astgen.source_column;
   5645         },
   5646         else => {},
   5647     }
   5648     const rhs = try reachableExpr(gz, scope, .{ .rl = .none }, node_datas[node].rhs, node);
   5649 
   5650     switch (op_inst_tag) {
   5651         .add, .sub, .mul, .div, .mod_rem => {
   5652             try emitDbgStmt(gz, line, column);
   5653         },
   5654         else => {},
   5655     }
   5656     const result = try gz.addPlNode(op_inst_tag, node, Zir.Inst.Bin{ .lhs = lhs, .rhs = rhs });
   5657     return rvalue(gz, ri, result, node);
   5658 }
   5659 
   5660 fn simpleStrTok(
   5661     gz: *GenZir,
   5662     ri: ResultInfo,
   5663     ident_token: Ast.TokenIndex,
   5664     node: Ast.Node.Index,
   5665     op_inst_tag: Zir.Inst.Tag,
   5666 ) InnerError!Zir.Inst.Ref {
   5667     const astgen = gz.astgen;
   5668     const str_index = try astgen.identAsString(ident_token);
   5669     const result = try gz.addStrTok(op_inst_tag, str_index, ident_token);
   5670     return rvalue(gz, ri, result, node);
   5671 }
   5672 
   5673 fn boolBinOp(
   5674     gz: *GenZir,
   5675     scope: *Scope,
   5676     ri: ResultInfo,
   5677     node: Ast.Node.Index,
   5678     zir_tag: Zir.Inst.Tag,
   5679 ) InnerError!Zir.Inst.Ref {
   5680     const astgen = gz.astgen;
   5681     const tree = astgen.tree;
   5682     const node_datas = tree.nodes.items(.data);
   5683 
   5684     const lhs = try expr(gz, scope, bool_ri, node_datas[node].lhs);
   5685     const bool_br = try gz.addBoolBr(zir_tag, lhs);
   5686 
   5687     var rhs_scope = gz.makeSubBlock(scope);
   5688     defer rhs_scope.unstack();
   5689     const rhs = try expr(&rhs_scope, &rhs_scope.base, bool_ri, node_datas[node].rhs);
   5690     if (!gz.refIsNoReturn(rhs)) {
   5691         _ = try rhs_scope.addBreak(.break_inline, bool_br, rhs);
   5692     }
   5693     try rhs_scope.setBoolBrBody(bool_br);
   5694 
   5695     const block_ref = indexToRef(bool_br);
   5696     return rvalue(gz, ri, block_ref, node);
   5697 }
   5698 
   5699 fn ifExpr(
   5700     parent_gz: *GenZir,
   5701     scope: *Scope,
   5702     ri: ResultInfo,
   5703     node: Ast.Node.Index,
   5704     if_full: Ast.full.If,
   5705 ) InnerError!Zir.Inst.Ref {
   5706     const astgen = parent_gz.astgen;
   5707     const tree = astgen.tree;
   5708     const token_tags = tree.tokens.items(.tag);
   5709 
   5710     const do_err_trace = astgen.fn_block != null and if_full.error_token != null;
   5711 
   5712     var block_scope = parent_gz.makeSubBlock(scope);
   5713     block_scope.setBreakResultInfo(ri);
   5714     defer block_scope.unstack();
   5715 
   5716     const payload_is_ref = if (if_full.payload_token) |payload_token|
   5717         token_tags[payload_token] == .asterisk
   5718     else
   5719         false;
   5720 
   5721     try emitDbgNode(parent_gz, if_full.ast.cond_expr);
   5722     const cond: struct {
   5723         inst: Zir.Inst.Ref,
   5724         bool_bit: Zir.Inst.Ref,
   5725     } = c: {
   5726         if (if_full.error_token) |_| {
   5727             const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none, .ctx = .error_handling_expr };
   5728             const err_union = try expr(&block_scope, &block_scope.base, cond_ri, if_full.ast.cond_expr);
   5729             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
   5730             break :c .{
   5731                 .inst = err_union,
   5732                 .bool_bit = try block_scope.addUnNode(tag, err_union, if_full.ast.cond_expr),
   5733             };
   5734         } else if (if_full.payload_token) |_| {
   5735             const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
   5736             const optional = try expr(&block_scope, &block_scope.base, cond_ri, if_full.ast.cond_expr);
   5737             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
   5738             break :c .{
   5739                 .inst = optional,
   5740                 .bool_bit = try block_scope.addUnNode(tag, optional, if_full.ast.cond_expr),
   5741             };
   5742         } else {
   5743             const cond = try expr(&block_scope, &block_scope.base, bool_ri, if_full.ast.cond_expr);
   5744             break :c .{
   5745                 .inst = cond,
   5746                 .bool_bit = cond,
   5747             };
   5748         }
   5749     };
   5750 
   5751     const condbr_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .condbr_inline else .condbr;
   5752     const condbr = try block_scope.addCondBr(condbr_tag, node);
   5753 
   5754     const block_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .block_inline else .block;
   5755     const block = try parent_gz.makeBlockInst(block_tag, node);
   5756     try block_scope.setBlockBody(block);
   5757     // block_scope unstacked now, can add new instructions to parent_gz
   5758     try parent_gz.instructions.append(astgen.gpa, block);
   5759 
   5760     var then_scope = parent_gz.makeSubBlock(scope);
   5761     defer then_scope.unstack();
   5762 
   5763     var payload_val_scope: Scope.LocalVal = undefined;
   5764 
   5765     try then_scope.addDbgBlockBegin();
   5766     const then_sub_scope = s: {
   5767         if (if_full.error_token != null) {
   5768             if (if_full.payload_token) |payload_token| {
   5769                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   5770                     .err_union_payload_unsafe_ptr
   5771                 else
   5772                     .err_union_payload_unsafe;
   5773                 const payload_inst = try then_scope.addUnNode(tag, cond.inst, if_full.ast.then_expr);
   5774                 const token_name_index = payload_token + @boolToInt(payload_is_ref);
   5775                 const ident_name = try astgen.identAsString(token_name_index);
   5776                 const token_name_str = tree.tokenSlice(token_name_index);
   5777                 if (mem.eql(u8, "_", token_name_str))
   5778                     break :s &then_scope.base;
   5779                 try astgen.detectLocalShadowing(&then_scope.base, ident_name, token_name_index, token_name_str, .capture);
   5780                 payload_val_scope = .{
   5781                     .parent = &then_scope.base,
   5782                     .gen_zir = &then_scope,
   5783                     .name = ident_name,
   5784                     .inst = payload_inst,
   5785                     .token_src = payload_token,
   5786                     .id_cat = .capture,
   5787                 };
   5788                 try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
   5789                 break :s &payload_val_scope.base;
   5790             } else {
   5791                 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node);
   5792                 break :s &then_scope.base;
   5793             }
   5794         } else if (if_full.payload_token) |payload_token| {
   5795             const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
   5796             const tag: Zir.Inst.Tag = if (payload_is_ref)
   5797                 .optional_payload_unsafe_ptr
   5798             else
   5799                 .optional_payload_unsafe;
   5800             const ident_bytes = tree.tokenSlice(ident_token);
   5801             if (mem.eql(u8, "_", ident_bytes))
   5802                 break :s &then_scope.base;
   5803             const payload_inst = try then_scope.addUnNode(tag, cond.inst, if_full.ast.then_expr);
   5804             const ident_name = try astgen.identAsString(ident_token);
   5805             try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture);
   5806             payload_val_scope = .{
   5807                 .parent = &then_scope.base,
   5808                 .gen_zir = &then_scope,
   5809                 .name = ident_name,
   5810                 .inst = payload_inst,
   5811                 .token_src = ident_token,
   5812                 .id_cat = .capture,
   5813             };
   5814             try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
   5815             break :s &payload_val_scope.base;
   5816         } else {
   5817             break :s &then_scope.base;
   5818         }
   5819     };
   5820 
   5821     const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_info, if_full.ast.then_expr);
   5822     if (!then_scope.endsWithNoReturn()) {
   5823         block_scope.break_count += 1;
   5824     }
   5825     try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
   5826     try then_scope.addDbgBlockEnd();
   5827     // We hold off on the break instructions as well as copying the then/else
   5828     // instructions into place until we know whether to keep store_to_block_ptr
   5829     // instructions or not.
   5830 
   5831     var else_scope = parent_gz.makeSubBlock(scope);
   5832     defer else_scope.unstack();
   5833 
   5834     // We know that the operand (almost certainly) modified the error return trace,
   5835     // so signal to Sema that it should save the new index for restoring later.
   5836     if (do_err_trace and nodeMayAppendToErrorTrace(tree, if_full.ast.cond_expr))
   5837         _ = try else_scope.addSaveErrRetIndex(.always);
   5838 
   5839     const else_node = if_full.ast.else_expr;
   5840     const else_info: struct {
   5841         src: Ast.Node.Index,
   5842         result: Zir.Inst.Ref,
   5843     } = if (else_node != 0) blk: {
   5844         try else_scope.addDbgBlockBegin();
   5845         const sub_scope = s: {
   5846             if (if_full.error_token) |error_token| {
   5847                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   5848                     .err_union_code_ptr
   5849                 else
   5850                     .err_union_code;
   5851                 const payload_inst = try else_scope.addUnNode(tag, cond.inst, if_full.ast.cond_expr);
   5852                 const ident_name = try astgen.identAsString(error_token);
   5853                 const error_token_str = tree.tokenSlice(error_token);
   5854                 if (mem.eql(u8, "_", error_token_str))
   5855                     break :s &else_scope.base;
   5856                 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, error_token_str, .capture);
   5857                 payload_val_scope = .{
   5858                     .parent = &else_scope.base,
   5859                     .gen_zir = &else_scope,
   5860                     .name = ident_name,
   5861                     .inst = payload_inst,
   5862                     .token_src = error_token,
   5863                     .id_cat = .capture,
   5864                 };
   5865                 try else_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
   5866                 break :s &payload_val_scope.base;
   5867             } else {
   5868                 break :s &else_scope.base;
   5869             }
   5870         };
   5871         const e = try expr(&else_scope, sub_scope, block_scope.break_result_info, else_node);
   5872         if (!else_scope.endsWithNoReturn()) {
   5873             block_scope.break_count += 1;
   5874 
   5875             // As our last action before the break, "pop" the error trace if needed
   5876             if (do_err_trace)
   5877                 try restoreErrRetIndex(&else_scope, .{ .block = block }, block_scope.break_result_info, else_node, e);
   5878         }
   5879         try checkUsed(parent_gz, &else_scope.base, sub_scope);
   5880         try else_scope.addDbgBlockEnd();
   5881         break :blk .{
   5882             .src = else_node,
   5883             .result = e,
   5884         };
   5885     } else .{
   5886         .src = if_full.ast.then_expr,
   5887         .result = switch (ri.rl) {
   5888             // Explicitly store void to ptr result loc if there is no else branch
   5889             .ptr, .block_ptr => try rvalue(&else_scope, ri, .void_value, node),
   5890             else => .none,
   5891         },
   5892     };
   5893 
   5894     const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break";
   5895     const result = try finishThenElseBlock(
   5896         parent_gz,
   5897         ri,
   5898         node,
   5899         &block_scope,
   5900         &then_scope,
   5901         &else_scope,
   5902         condbr,
   5903         cond.bool_bit,
   5904         then_result,
   5905         else_info.result,
   5906         block,
   5907         block,
   5908         break_tag,
   5909     );
   5910     return result;
   5911 }
   5912 
   5913 /// Supports `else_scope` stacked on `then_scope`. Unstacks `else_scope` then `then_scope`.
   5914 fn setCondBrPayload(
   5915     condbr: Zir.Inst.Index,
   5916     cond: Zir.Inst.Ref,
   5917     then_scope: *GenZir,
   5918     then_break: Zir.Inst.Index,
   5919     else_scope: *GenZir,
   5920     else_break: Zir.Inst.Index,
   5921 ) !void {
   5922     defer then_scope.unstack();
   5923     defer else_scope.unstack();
   5924     const astgen = then_scope.astgen;
   5925     const then_body = then_scope.instructionsSliceUpto(else_scope);
   5926     const else_body = else_scope.instructionsSlice();
   5927     const then_body_len = astgen.countBodyLenAfterFixups(then_body) + @boolToInt(then_break != 0);
   5928     const else_body_len = astgen.countBodyLenAfterFixups(else_body) + @boolToInt(else_break != 0);
   5929     try astgen.extra.ensureUnusedCapacity(
   5930         astgen.gpa,
   5931         @typeInfo(Zir.Inst.CondBr).Struct.fields.len + then_body_len + else_body_len,
   5932     );
   5933 
   5934     const zir_datas = astgen.instructions.items(.data);
   5935     zir_datas[condbr].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.CondBr{
   5936         .condition = cond,
   5937         .then_body_len = then_body_len,
   5938         .else_body_len = else_body_len,
   5939     });
   5940     astgen.appendBodyWithFixups(then_body);
   5941     if (then_break != 0) astgen.extra.appendAssumeCapacity(then_break);
   5942     astgen.appendBodyWithFixups(else_body);
   5943     if (else_break != 0) astgen.extra.appendAssumeCapacity(else_break);
   5944 }
   5945 
   5946 /// Supports `else_scope` stacked on `then_scope`. Unstacks `else_scope` then `then_scope`.
   5947 fn setCondBrPayloadElideBlockStorePtr(
   5948     condbr: Zir.Inst.Index,
   5949     cond: Zir.Inst.Ref,
   5950     then_scope: *GenZir,
   5951     then_break: Zir.Inst.Index,
   5952     else_scope: *GenZir,
   5953     else_break: Zir.Inst.Index,
   5954     block_ptr: Zir.Inst.Ref,
   5955 ) !void {
   5956     defer then_scope.unstack();
   5957     defer else_scope.unstack();
   5958     const astgen = then_scope.astgen;
   5959     const then_body = then_scope.instructionsSliceUpto(else_scope);
   5960     const else_body = else_scope.instructionsSlice();
   5961     const has_then_break = then_break != 0;
   5962     const has_else_break = else_break != 0;
   5963     const then_body_len = astgen.countBodyLenAfterFixups(then_body) + @boolToInt(has_then_break);
   5964     const else_body_len = astgen.countBodyLenAfterFixups(else_body) + @boolToInt(has_else_break);
   5965     try astgen.extra.ensureUnusedCapacity(
   5966         astgen.gpa,
   5967         @typeInfo(Zir.Inst.CondBr).Struct.fields.len + then_body_len + else_body_len,
   5968     );
   5969 
   5970     const zir_tags = astgen.instructions.items(.tag);
   5971     const zir_datas = astgen.instructions.items(.data);
   5972 
   5973     const condbr_pl = astgen.addExtraAssumeCapacity(Zir.Inst.CondBr{
   5974         .condition = cond,
   5975         .then_body_len = then_body_len,
   5976         .else_body_len = else_body_len,
   5977     });
   5978     zir_datas[condbr].pl_node.payload_index = condbr_pl;
   5979     const then_body_len_index = condbr_pl + 1;
   5980     const else_body_len_index = condbr_pl + 2;
   5981 
   5982     // The break instructions need to have their operands coerced if the
   5983     // switch's result location is a `ty`. In this case we overwrite the
   5984     // `store_to_block_ptr` instruction with an `as` instruction and repurpose
   5985     // it as the break operand.
   5986     // This corresponds to similar code in `labeledBlockExpr`.
   5987     for (then_body) |src_inst| {
   5988         if (zir_tags[src_inst] == .store_to_block_ptr and
   5989             zir_datas[src_inst].bin.lhs == block_ptr)
   5990         {
   5991             if (then_scope.rl_ty_inst != .none and has_then_break) {
   5992                 zir_tags[src_inst] = .as;
   5993                 zir_datas[src_inst].bin = .{
   5994                     .lhs = then_scope.rl_ty_inst,
   5995                     .rhs = zir_datas[then_break].@"break".operand,
   5996                 };
   5997                 zir_datas[then_break].@"break".operand = indexToRef(src_inst);
   5998             } else {
   5999                 astgen.extra.items[then_body_len_index] -= 1;
   6000                 continue;
   6001             }
   6002         }
   6003         appendPossiblyRefdBodyInst(astgen, &astgen.extra, src_inst);
   6004     }
   6005     if (has_then_break) astgen.extra.appendAssumeCapacity(then_break);
   6006 
   6007     for (else_body) |src_inst| {
   6008         if (zir_tags[src_inst] == .store_to_block_ptr and
   6009             zir_datas[src_inst].bin.lhs == block_ptr)
   6010         {
   6011             if (else_scope.rl_ty_inst != .none and has_else_break) {
   6012                 zir_tags[src_inst] = .as;
   6013                 zir_datas[src_inst].bin = .{
   6014                     .lhs = else_scope.rl_ty_inst,
   6015                     .rhs = zir_datas[else_break].@"break".operand,
   6016                 };
   6017                 zir_datas[else_break].@"break".operand = indexToRef(src_inst);
   6018             } else {
   6019                 astgen.extra.items[else_body_len_index] -= 1;
   6020                 continue;
   6021             }
   6022         }
   6023         appendPossiblyRefdBodyInst(astgen, &astgen.extra, src_inst);
   6024     }
   6025     if (has_else_break) astgen.extra.appendAssumeCapacity(else_break);
   6026 }
   6027 
   6028 fn whileExpr(
   6029     parent_gz: *GenZir,
   6030     scope: *Scope,
   6031     ri: ResultInfo,
   6032     node: Ast.Node.Index,
   6033     while_full: Ast.full.While,
   6034     is_statement: bool,
   6035 ) InnerError!Zir.Inst.Ref {
   6036     const astgen = parent_gz.astgen;
   6037     const tree = astgen.tree;
   6038     const token_tags = tree.tokens.items(.tag);
   6039 
   6040     if (while_full.label_token) |label_token| {
   6041         try astgen.checkLabelRedefinition(scope, label_token);
   6042     }
   6043 
   6044     const is_inline = parent_gz.force_comptime or while_full.inline_token != null;
   6045     const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop;
   6046     const loop_block = try parent_gz.makeBlockInst(loop_tag, node);
   6047     try parent_gz.instructions.append(astgen.gpa, loop_block);
   6048 
   6049     var loop_scope = parent_gz.makeSubBlock(scope);
   6050     loop_scope.is_inline = is_inline;
   6051     loop_scope.setBreakResultInfo(ri);
   6052     defer loop_scope.unstack();
   6053     defer loop_scope.labeled_breaks.deinit(astgen.gpa);
   6054 
   6055     var cond_scope = parent_gz.makeSubBlock(&loop_scope.base);
   6056     defer cond_scope.unstack();
   6057 
   6058     const payload_is_ref = if (while_full.payload_token) |payload_token|
   6059         token_tags[payload_token] == .asterisk
   6060     else
   6061         false;
   6062 
   6063     try emitDbgNode(parent_gz, while_full.ast.cond_expr);
   6064     const cond: struct {
   6065         inst: Zir.Inst.Ref,
   6066         bool_bit: Zir.Inst.Ref,
   6067     } = c: {
   6068         if (while_full.error_token) |_| {
   6069             const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
   6070             const err_union = try expr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr);
   6071             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
   6072             break :c .{
   6073                 .inst = err_union,
   6074                 .bool_bit = try cond_scope.addUnNode(tag, err_union, while_full.ast.then_expr),
   6075             };
   6076         } else if (while_full.payload_token) |_| {
   6077             const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
   6078             const optional = try expr(&cond_scope, &cond_scope.base, cond_ri, while_full.ast.cond_expr);
   6079             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
   6080             break :c .{
   6081                 .inst = optional,
   6082                 .bool_bit = try cond_scope.addUnNode(tag, optional, while_full.ast.then_expr),
   6083             };
   6084         } else {
   6085             const cond = try expr(&cond_scope, &cond_scope.base, bool_ri, while_full.ast.cond_expr);
   6086             break :c .{
   6087                 .inst = cond,
   6088                 .bool_bit = cond,
   6089             };
   6090         }
   6091     };
   6092 
   6093     const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
   6094     const condbr = try cond_scope.addCondBr(condbr_tag, node);
   6095     const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block;
   6096     const cond_block = try loop_scope.makeBlockInst(block_tag, node);
   6097     try cond_scope.setBlockBody(cond_block);
   6098     // cond_scope unstacked now, can add new instructions to loop_scope
   6099     try loop_scope.instructions.append(astgen.gpa, cond_block);
   6100 
   6101     // make scope now but don't stack on parent_gz until loop_scope
   6102     // gets unstacked after cont_expr is emitted and added below
   6103     var then_scope = parent_gz.makeSubBlock(&cond_scope.base);
   6104     then_scope.instructions_top = GenZir.unstacked_top;
   6105     defer then_scope.unstack();
   6106 
   6107     var dbg_var_name: ?u32 = null;
   6108     var dbg_var_inst: Zir.Inst.Ref = undefined;
   6109     var payload_inst: Zir.Inst.Index = 0;
   6110     var payload_val_scope: Scope.LocalVal = undefined;
   6111     const then_sub_scope = s: {
   6112         if (while_full.error_token != null) {
   6113             if (while_full.payload_token) |payload_token| {
   6114                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   6115                     .err_union_payload_unsafe_ptr
   6116                 else
   6117                     .err_union_payload_unsafe;
   6118                 // will add this instruction to then_scope.instructions below
   6119                 payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr);
   6120                 const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
   6121                 const ident_bytes = tree.tokenSlice(ident_token);
   6122                 if (mem.eql(u8, "_", ident_bytes))
   6123                     break :s &then_scope.base;
   6124                 const payload_name_loc = payload_token + @boolToInt(payload_is_ref);
   6125                 const ident_name = try astgen.identAsString(payload_name_loc);
   6126                 try astgen.detectLocalShadowing(&then_scope.base, ident_name, payload_name_loc, ident_bytes, .capture);
   6127                 payload_val_scope = .{
   6128                     .parent = &then_scope.base,
   6129                     .gen_zir = &then_scope,
   6130                     .name = ident_name,
   6131                     .inst = indexToRef(payload_inst),
   6132                     .token_src = payload_token,
   6133                     .id_cat = .capture,
   6134                 };
   6135                 dbg_var_name = ident_name;
   6136                 dbg_var_inst = indexToRef(payload_inst);
   6137                 break :s &payload_val_scope.base;
   6138             } else {
   6139                 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node);
   6140                 break :s &then_scope.base;
   6141             }
   6142         } else if (while_full.payload_token) |payload_token| {
   6143             const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
   6144             const tag: Zir.Inst.Tag = if (payload_is_ref)
   6145                 .optional_payload_unsafe_ptr
   6146             else
   6147                 .optional_payload_unsafe;
   6148             // will add this instruction to then_scope.instructions below
   6149             payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr);
   6150             const ident_name = try astgen.identAsString(ident_token);
   6151             const ident_bytes = tree.tokenSlice(ident_token);
   6152             if (mem.eql(u8, "_", ident_bytes))
   6153                 break :s &then_scope.base;
   6154             try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes, .capture);
   6155             payload_val_scope = .{
   6156                 .parent = &then_scope.base,
   6157                 .gen_zir = &then_scope,
   6158                 .name = ident_name,
   6159                 .inst = indexToRef(payload_inst),
   6160                 .token_src = ident_token,
   6161                 .id_cat = .capture,
   6162             };
   6163             dbg_var_name = ident_name;
   6164             dbg_var_inst = indexToRef(payload_inst);
   6165             break :s &payload_val_scope.base;
   6166         } else {
   6167             break :s &then_scope.base;
   6168         }
   6169     };
   6170 
   6171     var continue_scope = parent_gz.makeSubBlock(then_sub_scope);
   6172     continue_scope.instructions_top = GenZir.unstacked_top;
   6173     defer continue_scope.unstack();
   6174     const continue_block = try then_scope.makeBlockInst(block_tag, node);
   6175 
   6176     const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
   6177     _ = try loop_scope.addNode(repeat_tag, node);
   6178 
   6179     try loop_scope.setBlockBody(loop_block);
   6180     loop_scope.break_block = loop_block;
   6181     loop_scope.continue_block = continue_block;
   6182     if (while_full.label_token) |label_token| {
   6183         loop_scope.label = @as(?GenZir.Label, GenZir.Label{
   6184             .token = label_token,
   6185             .block_inst = loop_block,
   6186         });
   6187     }
   6188 
   6189     // done adding instructions to loop_scope, can now stack then_scope
   6190     then_scope.instructions_top = then_scope.instructions.items.len;
   6191 
   6192     try then_scope.addDbgBlockBegin();
   6193     if (payload_inst != 0) try then_scope.instructions.append(astgen.gpa, payload_inst);
   6194     if (dbg_var_name) |name| try then_scope.addDbgVar(.dbg_var_val, name, dbg_var_inst);
   6195     try then_scope.instructions.append(astgen.gpa, continue_block);
   6196     // This code could be improved to avoid emitting the continue expr when there
   6197     // are no jumps to it. This happens when the last statement of a while body is noreturn
   6198     // and there are no `continue` statements.
   6199     // Tracking issue: https://github.com/ziglang/zig/issues/9185
   6200     if (while_full.ast.cont_expr != 0) {
   6201         _ = try unusedResultExpr(&then_scope, then_sub_scope, while_full.ast.cont_expr);
   6202     }
   6203     try then_scope.addDbgBlockEnd();
   6204 
   6205     continue_scope.instructions_top = continue_scope.instructions.items.len;
   6206     _ = try unusedResultExpr(&continue_scope, &continue_scope.base, while_full.ast.then_expr);
   6207     try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
   6208     const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
   6209     if (!continue_scope.endsWithNoReturn()) {
   6210         const break_inst = try continue_scope.makeBreak(break_tag, continue_block, .void_value);
   6211         try then_scope.instructions.append(astgen.gpa, break_inst);
   6212     }
   6213     try continue_scope.setBlockBody(continue_block);
   6214 
   6215     var else_scope = parent_gz.makeSubBlock(&cond_scope.base);
   6216     defer else_scope.unstack();
   6217 
   6218     const else_node = while_full.ast.else_expr;
   6219     const else_info: struct {
   6220         src: Ast.Node.Index,
   6221         result: Zir.Inst.Ref,
   6222     } = if (else_node != 0) blk: {
   6223         try else_scope.addDbgBlockBegin();
   6224         const sub_scope = s: {
   6225             if (while_full.error_token) |error_token| {
   6226                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   6227                     .err_union_code_ptr
   6228                 else
   6229                     .err_union_code;
   6230                 const else_payload_inst = try else_scope.addUnNode(tag, cond.inst, while_full.ast.cond_expr);
   6231                 const ident_name = try astgen.identAsString(error_token);
   6232                 const ident_bytes = tree.tokenSlice(error_token);
   6233                 if (mem.eql(u8, ident_bytes, "_"))
   6234                     break :s &else_scope.base;
   6235                 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, ident_bytes, .capture);
   6236                 payload_val_scope = .{
   6237                     .parent = &else_scope.base,
   6238                     .gen_zir = &else_scope,
   6239                     .name = ident_name,
   6240                     .inst = else_payload_inst,
   6241                     .token_src = error_token,
   6242                     .id_cat = .capture,
   6243                 };
   6244                 try else_scope.addDbgVar(.dbg_var_val, ident_name, else_payload_inst);
   6245                 break :s &payload_val_scope.base;
   6246             } else {
   6247                 break :s &else_scope.base;
   6248             }
   6249         };
   6250         // Remove the continue block and break block so that `continue` and `break`
   6251         // control flow apply to outer loops; not this one.
   6252         loop_scope.continue_block = 0;
   6253         loop_scope.break_block = 0;
   6254         const else_result = try expr(&else_scope, sub_scope, loop_scope.break_result_info, else_node);
   6255         if (is_statement) {
   6256             _ = try addEnsureResult(&else_scope, else_result, else_node);
   6257         }
   6258 
   6259         if (!else_scope.endsWithNoReturn()) {
   6260             loop_scope.break_count += 1;
   6261         }
   6262         try checkUsed(parent_gz, &else_scope.base, sub_scope);
   6263         try else_scope.addDbgBlockEnd();
   6264         break :blk .{
   6265             .src = else_node,
   6266             .result = else_result,
   6267         };
   6268     } else .{
   6269         .src = while_full.ast.then_expr,
   6270         .result = .none,
   6271     };
   6272 
   6273     if (loop_scope.label) |some| {
   6274         if (!some.used) {
   6275             try astgen.appendErrorTok(some.token, "unused while loop label", .{});
   6276         }
   6277     }
   6278     const result = try finishThenElseBlock(
   6279         parent_gz,
   6280         ri,
   6281         node,
   6282         &loop_scope,
   6283         &then_scope,
   6284         &else_scope,
   6285         condbr,
   6286         cond.bool_bit,
   6287         .void_value,
   6288         else_info.result,
   6289         loop_block,
   6290         cond_block,
   6291         break_tag,
   6292     );
   6293     if (is_statement) {
   6294         _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
   6295     }
   6296     return result;
   6297 }
   6298 
   6299 fn forExpr(
   6300     parent_gz: *GenZir,
   6301     scope: *Scope,
   6302     ri: ResultInfo,
   6303     node: Ast.Node.Index,
   6304     for_full: Ast.full.While,
   6305     is_statement: bool,
   6306 ) InnerError!Zir.Inst.Ref {
   6307     const astgen = parent_gz.astgen;
   6308 
   6309     if (for_full.label_token) |label_token| {
   6310         try astgen.checkLabelRedefinition(scope, label_token);
   6311     }
   6312 
   6313     // Set up variables and constants.
   6314     const is_inline = parent_gz.force_comptime or for_full.inline_token != null;
   6315     const tree = astgen.tree;
   6316     const token_tags = tree.tokens.items(.tag);
   6317 
   6318     const payload_is_ref = if (for_full.payload_token) |payload_token|
   6319         token_tags[payload_token] == .asterisk
   6320     else
   6321         false;
   6322 
   6323     try emitDbgNode(parent_gz, for_full.ast.cond_expr);
   6324 
   6325     const cond_ri: ResultInfo = .{ .rl = if (payload_is_ref) .ref else .none };
   6326     const array_ptr = try expr(parent_gz, scope, cond_ri, for_full.ast.cond_expr);
   6327     const len = try parent_gz.addUnNode(.indexable_ptr_len, array_ptr, for_full.ast.cond_expr);
   6328 
   6329     const index_ptr = blk: {
   6330         const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime_mut else .alloc;
   6331         const index_ptr = try parent_gz.addUnNode(alloc_tag, .usize_type, node);
   6332         // initialize to zero
   6333         _ = try parent_gz.addBin(.store, index_ptr, .zero_usize);
   6334         break :blk index_ptr;
   6335     };
   6336 
   6337     const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop;
   6338     const loop_block = try parent_gz.makeBlockInst(loop_tag, node);
   6339     try parent_gz.instructions.append(astgen.gpa, loop_block);
   6340 
   6341     var loop_scope = parent_gz.makeSubBlock(scope);
   6342     loop_scope.is_inline = is_inline;
   6343     loop_scope.setBreakResultInfo(ri);
   6344     defer loop_scope.unstack();
   6345     defer loop_scope.labeled_breaks.deinit(astgen.gpa);
   6346 
   6347     var cond_scope = parent_gz.makeSubBlock(&loop_scope.base);
   6348     defer cond_scope.unstack();
   6349 
   6350     // check condition i < array_expr.len
   6351     const index = try cond_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr);
   6352     const cond = try cond_scope.addPlNode(.cmp_lt, for_full.ast.cond_expr, Zir.Inst.Bin{
   6353         .lhs = index,
   6354         .rhs = len,
   6355     });
   6356 
   6357     const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
   6358     const condbr = try cond_scope.addCondBr(condbr_tag, node);
   6359     const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block;
   6360     const cond_block = try loop_scope.makeBlockInst(block_tag, node);
   6361     try cond_scope.setBlockBody(cond_block);
   6362     // cond_block unstacked now, can add new instructions to loop_scope
   6363     try loop_scope.instructions.append(astgen.gpa, cond_block);
   6364 
   6365     // Increment the index variable.
   6366     const index_2 = try loop_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr);
   6367     const index_plus_one = try loop_scope.addPlNode(.add, node, Zir.Inst.Bin{
   6368         .lhs = index_2,
   6369         .rhs = .one_usize,
   6370     });
   6371     _ = try loop_scope.addBin(.store, index_ptr, index_plus_one);
   6372     const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
   6373     _ = try loop_scope.addNode(repeat_tag, node);
   6374 
   6375     try loop_scope.setBlockBody(loop_block);
   6376     loop_scope.break_block = loop_block;
   6377     loop_scope.continue_block = cond_block;
   6378     if (for_full.label_token) |label_token| {
   6379         loop_scope.label = @as(?GenZir.Label, GenZir.Label{
   6380             .token = label_token,
   6381             .block_inst = loop_block,
   6382         });
   6383     }
   6384 
   6385     var then_scope = parent_gz.makeSubBlock(&cond_scope.base);
   6386     defer then_scope.unstack();
   6387 
   6388     try then_scope.addDbgBlockBegin();
   6389     var payload_val_scope: Scope.LocalVal = undefined;
   6390     var index_scope: Scope.LocalPtr = undefined;
   6391     const then_sub_scope = blk: {
   6392         const payload_token = for_full.payload_token.?;
   6393         const ident = if (token_tags[payload_token] == .asterisk)
   6394             payload_token + 1
   6395         else
   6396             payload_token;
   6397         const is_ptr = ident != payload_token;
   6398         const value_name = tree.tokenSlice(ident);
   6399         var payload_sub_scope: *Scope = undefined;
   6400         if (!mem.eql(u8, value_name, "_")) {
   6401             const name_str_index = try astgen.identAsString(ident);
   6402             const tag: Zir.Inst.Tag = if (is_ptr) .elem_ptr else .elem_val;
   6403             const payload_inst = try then_scope.addPlNode(tag, for_full.ast.cond_expr, Zir.Inst.Bin{
   6404                 .lhs = array_ptr,
   6405                 .rhs = index,
   6406             });
   6407             try astgen.detectLocalShadowing(&then_scope.base, name_str_index, ident, value_name, .capture);
   6408             payload_val_scope = .{
   6409                 .parent = &then_scope.base,
   6410                 .gen_zir = &then_scope,
   6411                 .name = name_str_index,
   6412                 .inst = payload_inst,
   6413                 .token_src = ident,
   6414                 .id_cat = .capture,
   6415             };
   6416             try then_scope.addDbgVar(.dbg_var_val, name_str_index, payload_inst);
   6417             payload_sub_scope = &payload_val_scope.base;
   6418         } else if (is_ptr) {
   6419             return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   6420         } else {
   6421             payload_sub_scope = &then_scope.base;
   6422         }
   6423 
   6424         const index_token = if (token_tags[ident + 1] == .comma)
   6425             ident + 2
   6426         else
   6427             break :blk payload_sub_scope;
   6428         const token_bytes = tree.tokenSlice(index_token);
   6429         if (mem.eql(u8, token_bytes, "_")) {
   6430             return astgen.failTok(index_token, "discard of index capture; omit it instead", .{});
   6431         }
   6432         const index_name = try astgen.identAsString(index_token);
   6433         try astgen.detectLocalShadowing(payload_sub_scope, index_name, index_token, token_bytes, .@"loop index capture");
   6434         index_scope = .{
   6435             .parent = payload_sub_scope,
   6436             .gen_zir = &then_scope,
   6437             .name = index_name,
   6438             .ptr = index_ptr,
   6439             .token_src = index_token,
   6440             .maybe_comptime = is_inline,
   6441             .id_cat = .@"loop index capture",
   6442         };
   6443         try then_scope.addDbgVar(.dbg_var_val, index_name, index_ptr);
   6444         break :blk &index_scope.base;
   6445     };
   6446 
   6447     const then_result = try expr(&then_scope, then_sub_scope, .{ .rl = .none }, for_full.ast.then_expr);
   6448     _ = try addEnsureResult(&then_scope, then_result, for_full.ast.then_expr);
   6449 
   6450     try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
   6451     try then_scope.addDbgBlockEnd();
   6452 
   6453     var else_scope = parent_gz.makeSubBlock(&cond_scope.base);
   6454     defer else_scope.unstack();
   6455 
   6456     const else_node = for_full.ast.else_expr;
   6457     const else_info: struct {
   6458         src: Ast.Node.Index,
   6459         result: Zir.Inst.Ref,
   6460     } = if (else_node != 0) blk: {
   6461         const sub_scope = &else_scope.base;
   6462         // Remove the continue block and break block so that `continue` and `break`
   6463         // control flow apply to outer loops; not this one.
   6464         loop_scope.continue_block = 0;
   6465         loop_scope.break_block = 0;
   6466         const else_result = try expr(&else_scope, sub_scope, loop_scope.break_result_info, else_node);
   6467         if (is_statement) {
   6468             _ = try addEnsureResult(&else_scope, else_result, else_node);
   6469         }
   6470 
   6471         if (!else_scope.endsWithNoReturn()) {
   6472             loop_scope.break_count += 1;
   6473         }
   6474         break :blk .{
   6475             .src = else_node,
   6476             .result = else_result,
   6477         };
   6478     } else .{
   6479         .src = for_full.ast.then_expr,
   6480         .result = .none,
   6481     };
   6482 
   6483     if (loop_scope.label) |some| {
   6484         if (!some.used) {
   6485             try astgen.appendErrorTok(some.token, "unused for loop label", .{});
   6486         }
   6487     }
   6488     const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
   6489     const result = try finishThenElseBlock(
   6490         parent_gz,
   6491         ri,
   6492         node,
   6493         &loop_scope,
   6494         &then_scope,
   6495         &else_scope,
   6496         condbr,
   6497         cond,
   6498         then_result,
   6499         else_info.result,
   6500         loop_block,
   6501         cond_block,
   6502         break_tag,
   6503     );
   6504     if (is_statement) {
   6505         _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
   6506     }
   6507     return result;
   6508 }
   6509 
   6510 fn switchExpr(
   6511     parent_gz: *GenZir,
   6512     scope: *Scope,
   6513     ri: ResultInfo,
   6514     switch_node: Ast.Node.Index,
   6515 ) InnerError!Zir.Inst.Ref {
   6516     const astgen = parent_gz.astgen;
   6517     const gpa = astgen.gpa;
   6518     const tree = astgen.tree;
   6519     const node_datas = tree.nodes.items(.data);
   6520     const node_tags = tree.nodes.items(.tag);
   6521     const main_tokens = tree.nodes.items(.main_token);
   6522     const token_tags = tree.tokens.items(.tag);
   6523     const operand_node = node_datas[switch_node].lhs;
   6524     const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
   6525     const case_nodes = tree.extra_data[extra.start..extra.end];
   6526 
   6527     // We perform two passes over the AST. This first pass is to collect information
   6528     // for the following variables, make note of the special prong AST node index,
   6529     // and bail out with a compile error if there are multiple special prongs present.
   6530     var any_payload_is_ref = false;
   6531     var scalar_cases_len: u32 = 0;
   6532     var multi_cases_len: u32 = 0;
   6533     var inline_cases_len: u32 = 0;
   6534     var special_prong: Zir.SpecialProng = .none;
   6535     var special_node: Ast.Node.Index = 0;
   6536     var else_src: ?Ast.TokenIndex = null;
   6537     var underscore_src: ?Ast.TokenIndex = null;
   6538     for (case_nodes) |case_node| {
   6539         const case = switch (node_tags[case_node]) {
   6540             .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
   6541             .switch_case, .switch_case_inline => tree.switchCase(case_node),
   6542             else => unreachable,
   6543         };
   6544         if (case.payload_token) |payload_token| {
   6545             if (token_tags[payload_token] == .asterisk) {
   6546                 any_payload_is_ref = true;
   6547             }
   6548         }
   6549         // Check for else/`_` prong.
   6550         if (case.ast.values.len == 0) {
   6551             const case_src = case.ast.arrow_token - 1;
   6552             if (else_src) |src| {
   6553                 return astgen.failTokNotes(
   6554                     case_src,
   6555                     "multiple else prongs in switch expression",
   6556                     .{},
   6557                     &[_]u32{
   6558                         try astgen.errNoteTok(
   6559                             src,
   6560                             "previous else prong here",
   6561                             .{},
   6562                         ),
   6563                     },
   6564                 );
   6565             } else if (underscore_src) |some_underscore| {
   6566                 return astgen.failNodeNotes(
   6567                     switch_node,
   6568                     "else and '_' prong in switch expression",
   6569                     .{},
   6570                     &[_]u32{
   6571                         try astgen.errNoteTok(
   6572                             case_src,
   6573                             "else prong here",
   6574                             .{},
   6575                         ),
   6576                         try astgen.errNoteTok(
   6577                             some_underscore,
   6578                             "'_' prong here",
   6579                             .{},
   6580                         ),
   6581                     },
   6582                 );
   6583             }
   6584             special_node = case_node;
   6585             special_prong = .@"else";
   6586             else_src = case_src;
   6587             continue;
   6588         } else if (case.ast.values.len == 1 and
   6589             node_tags[case.ast.values[0]] == .identifier and
   6590             mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
   6591         {
   6592             const case_src = case.ast.arrow_token - 1;
   6593             if (underscore_src) |src| {
   6594                 return astgen.failTokNotes(
   6595                     case_src,
   6596                     "multiple '_' prongs in switch expression",
   6597                     .{},
   6598                     &[_]u32{
   6599                         try astgen.errNoteTok(
   6600                             src,
   6601                             "previous '_' prong here",
   6602                             .{},
   6603                         ),
   6604                     },
   6605                 );
   6606             } else if (else_src) |some_else| {
   6607                 return astgen.failNodeNotes(
   6608                     switch_node,
   6609                     "else and '_' prong in switch expression",
   6610                     .{},
   6611                     &[_]u32{
   6612                         try astgen.errNoteTok(
   6613                             some_else,
   6614                             "else prong here",
   6615                             .{},
   6616                         ),
   6617                         try astgen.errNoteTok(
   6618                             case_src,
   6619                             "'_' prong here",
   6620                             .{},
   6621                         ),
   6622                     },
   6623                 );
   6624             }
   6625             if (case.inline_token != null) {
   6626                 return astgen.failTok(case_src, "cannot inline '_' prong", .{});
   6627             }
   6628             special_node = case_node;
   6629             special_prong = .under;
   6630             underscore_src = case_src;
   6631             continue;
   6632         }
   6633 
   6634         for (case.ast.values) |val| {
   6635             if (node_tags[val] == .string_literal)
   6636                 return astgen.failNode(val, "cannot switch on strings", .{});
   6637         }
   6638 
   6639         if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] != .switch_range) {
   6640             scalar_cases_len += 1;
   6641         } else {
   6642             multi_cases_len += 1;
   6643         }
   6644         if (case.inline_token != null) {
   6645             inline_cases_len += 1;
   6646         }
   6647     }
   6648 
   6649     const operand_ri: ResultInfo = .{ .rl = if (any_payload_is_ref) .ref else .none };
   6650     astgen.advanceSourceCursorToNode(operand_node);
   6651     const operand_line = astgen.source_line - parent_gz.decl_line;
   6652     const operand_column = astgen.source_column;
   6653     const raw_operand = try expr(parent_gz, scope, operand_ri, operand_node);
   6654     const cond_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_cond_ref else .switch_cond;
   6655     const cond = try parent_gz.addUnNode(cond_tag, raw_operand, operand_node);
   6656     // Sema expects a dbg_stmt immediately after switch_cond(_ref)
   6657     try emitDbgStmt(parent_gz, operand_line, operand_column);
   6658     // We need the type of the operand to use as the result location for all the prong items.
   6659     const cond_ty_inst = try parent_gz.addUnNode(.typeof, cond, operand_node);
   6660     const item_ri: ResultInfo = .{ .rl = .{ .ty = cond_ty_inst } };
   6661 
   6662     // This contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti,
   6663     // except the first cases_nodes.len slots are a table that indexes payloads later in the array, with
   6664     // the special case index coming first, then scalar_case_len indexes, then multi_cases_len indexes
   6665     const payloads = &astgen.scratch;
   6666     const scratch_top = astgen.scratch.items.len;
   6667     const case_table_start = scratch_top;
   6668     const scalar_case_table = case_table_start + @boolToInt(special_prong != .none);
   6669     const multi_case_table = scalar_case_table + scalar_cases_len;
   6670     const case_table_end = multi_case_table + multi_cases_len;
   6671     try astgen.scratch.resize(gpa, case_table_end);
   6672     defer astgen.scratch.items.len = scratch_top;
   6673 
   6674     var block_scope = parent_gz.makeSubBlock(scope);
   6675     // block_scope not used for collecting instructions
   6676     block_scope.instructions_top = GenZir.unstacked_top;
   6677     block_scope.setBreakResultInfo(ri);
   6678 
   6679     // This gets added to the parent block later, after the item expressions.
   6680     const switch_block = try parent_gz.makeBlockInst(.switch_block, switch_node);
   6681 
   6682     // We re-use this same scope for all cases, including the special prong, if any.
   6683     var case_scope = parent_gz.makeSubBlock(&block_scope.base);
   6684     case_scope.instructions_top = GenZir.unstacked_top;
   6685 
   6686     // In this pass we generate all the item and prong expressions.
   6687     var multi_case_index: u32 = 0;
   6688     var scalar_case_index: u32 = 0;
   6689     for (case_nodes) |case_node| {
   6690         const case = switch (node_tags[case_node]) {
   6691             .switch_case_one, .switch_case_inline_one => tree.switchCaseOne(case_node),
   6692             .switch_case, .switch_case_inline => tree.switchCase(case_node),
   6693             else => unreachable,
   6694         };
   6695 
   6696         const is_multi_case = case.ast.values.len > 1 or
   6697             (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .switch_range);
   6698 
   6699         var dbg_var_name: ?u32 = null;
   6700         var dbg_var_inst: Zir.Inst.Ref = undefined;
   6701         var dbg_var_tag_name: ?u32 = null;
   6702         var dbg_var_tag_inst: Zir.Inst.Ref = undefined;
   6703         var capture_inst: Zir.Inst.Index = 0;
   6704         var tag_inst: Zir.Inst.Index = 0;
   6705         var capture_val_scope: Scope.LocalVal = undefined;
   6706         var tag_scope: Scope.LocalVal = undefined;
   6707         const sub_scope = blk: {
   6708             const payload_token = case.payload_token orelse break :blk &case_scope.base;
   6709             const ident = if (token_tags[payload_token] == .asterisk)
   6710                 payload_token + 1
   6711             else
   6712                 payload_token;
   6713             const is_ptr = ident != payload_token;
   6714             const ident_slice = tree.tokenSlice(ident);
   6715             var payload_sub_scope: *Scope = undefined;
   6716             if (mem.eql(u8, ident_slice, "_")) {
   6717                 if (is_ptr) {
   6718                     return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   6719                 }
   6720                 payload_sub_scope = &case_scope.base;
   6721             } else {
   6722                 if (case_node == special_node) {
   6723                     const capture_tag: Zir.Inst.Tag = if (is_ptr)
   6724                         .switch_capture_ref
   6725                     else
   6726                         .switch_capture;
   6727                     capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
   6728                     try astgen.instructions.append(gpa, .{
   6729                         .tag = capture_tag,
   6730                         .data = .{
   6731                             .switch_capture = .{
   6732                                 .switch_inst = switch_block,
   6733                                 // Max int communicates that this is the else/underscore prong.
   6734                                 .prong_index = std.math.maxInt(u32),
   6735                             },
   6736                         },
   6737                     });
   6738                 } else {
   6739                     const is_multi_case_bits: u2 = @boolToInt(is_multi_case);
   6740                     const is_ptr_bits: u2 = @boolToInt(is_ptr);
   6741                     const capture_tag: Zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) {
   6742                         0b00 => .switch_capture,
   6743                         0b01 => .switch_capture_ref,
   6744                         0b10 => .switch_capture_multi,
   6745                         0b11 => .switch_capture_multi_ref,
   6746                     };
   6747                     const capture_index = if (is_multi_case) multi_case_index else scalar_case_index;
   6748                     capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
   6749                     try astgen.instructions.append(gpa, .{
   6750                         .tag = capture_tag,
   6751                         .data = .{ .switch_capture = .{
   6752                             .switch_inst = switch_block,
   6753                             .prong_index = capture_index,
   6754                         } },
   6755                     });
   6756                 }
   6757                 const capture_name = try astgen.identAsString(ident);
   6758                 try astgen.detectLocalShadowing(&case_scope.base, capture_name, ident, ident_slice, .capture);
   6759                 capture_val_scope = .{
   6760                     .parent = &case_scope.base,
   6761                     .gen_zir = &case_scope,
   6762                     .name = capture_name,
   6763                     .inst = indexToRef(capture_inst),
   6764                     .token_src = payload_token,
   6765                     .id_cat = .capture,
   6766                 };
   6767                 dbg_var_name = capture_name;
   6768                 dbg_var_inst = indexToRef(capture_inst);
   6769                 payload_sub_scope = &capture_val_scope.base;
   6770             }
   6771 
   6772             const tag_token = if (token_tags[ident + 1] == .comma)
   6773                 ident + 2
   6774             else
   6775                 break :blk payload_sub_scope;
   6776             const tag_slice = tree.tokenSlice(tag_token);
   6777             if (mem.eql(u8, tag_slice, "_")) {
   6778                 return astgen.failTok(tag_token, "discard of tag capture; omit it instead", .{});
   6779             } else if (case.inline_token == null) {
   6780                 return astgen.failTok(tag_token, "tag capture on non-inline prong", .{});
   6781             }
   6782             const tag_name = try astgen.identAsString(tag_token);
   6783             try astgen.detectLocalShadowing(payload_sub_scope, tag_name, tag_token, tag_slice, .@"switch tag capture");
   6784             tag_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
   6785             try astgen.instructions.append(gpa, .{
   6786                 .tag = .switch_capture_tag,
   6787                 .data = .{ .un_tok = .{
   6788                     .operand = cond,
   6789                     .src_tok = case_scope.tokenIndexToRelative(tag_token),
   6790                 } },
   6791             });
   6792 
   6793             tag_scope = .{
   6794                 .parent = payload_sub_scope,
   6795                 .gen_zir = &case_scope,
   6796                 .name = tag_name,
   6797                 .inst = indexToRef(tag_inst),
   6798                 .token_src = tag_token,
   6799                 .id_cat = .@"switch tag capture",
   6800             };
   6801             dbg_var_tag_name = tag_name;
   6802             dbg_var_tag_inst = indexToRef(tag_inst);
   6803             break :blk &tag_scope.base;
   6804         };
   6805 
   6806         const header_index = @intCast(u32, payloads.items.len);
   6807         const body_len_index = if (is_multi_case) blk: {
   6808             payloads.items[multi_case_table + multi_case_index] = header_index;
   6809             multi_case_index += 1;
   6810             try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len
   6811 
   6812             // items
   6813             var items_len: u32 = 0;
   6814             for (case.ast.values) |item_node| {
   6815                 if (node_tags[item_node] == .switch_range) continue;
   6816                 items_len += 1;
   6817 
   6818                 const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node);
   6819                 try payloads.append(gpa, @enumToInt(item_inst));
   6820             }
   6821 
   6822             // ranges
   6823             var ranges_len: u32 = 0;
   6824             for (case.ast.values) |range| {
   6825                 if (node_tags[range] != .switch_range) continue;
   6826                 ranges_len += 1;
   6827 
   6828                 const first = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].lhs);
   6829                 const last = try comptimeExpr(parent_gz, scope, item_ri, node_datas[range].rhs);
   6830                 try payloads.appendSlice(gpa, &[_]u32{
   6831                     @enumToInt(first), @enumToInt(last),
   6832                 });
   6833             }
   6834 
   6835             payloads.items[header_index] = items_len;
   6836             payloads.items[header_index + 1] = ranges_len;
   6837             break :blk header_index + 2;
   6838         } else if (case_node == special_node) blk: {
   6839             payloads.items[case_table_start] = header_index;
   6840             try payloads.resize(gpa, header_index + 1); // body_len
   6841             break :blk header_index;
   6842         } else blk: {
   6843             payloads.items[scalar_case_table + scalar_case_index] = header_index;
   6844             scalar_case_index += 1;
   6845             try payloads.resize(gpa, header_index + 2); // item, body_len
   6846             const item_node = case.ast.values[0];
   6847             const item_inst = try comptimeExpr(parent_gz, scope, item_ri, item_node);
   6848             payloads.items[header_index] = @enumToInt(item_inst);
   6849             break :blk header_index + 1;
   6850         };
   6851 
   6852         {
   6853             // temporarily stack case_scope on parent_gz
   6854             case_scope.instructions_top = parent_gz.instructions.items.len;
   6855             defer case_scope.unstack();
   6856 
   6857             if (capture_inst != 0) try case_scope.instructions.append(gpa, capture_inst);
   6858             if (tag_inst != 0) try case_scope.instructions.append(gpa, tag_inst);
   6859             try case_scope.addDbgBlockBegin();
   6860             if (dbg_var_name) |some| {
   6861                 try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
   6862             }
   6863             if (dbg_var_tag_name) |some| {
   6864                 try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_tag_inst);
   6865             }
   6866             const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_info, case.ast.target_expr);
   6867             try checkUsed(parent_gz, &case_scope.base, sub_scope);
   6868             try case_scope.addDbgBlockEnd();
   6869             if (!parent_gz.refIsNoReturn(case_result)) {
   6870                 block_scope.break_count += 1;
   6871                 _ = try case_scope.addBreak(.@"break", switch_block, case_result);
   6872             }
   6873 
   6874             const case_slice = case_scope.instructionsSlice();
   6875             const body_len = astgen.countBodyLenAfterFixups(case_slice);
   6876             try payloads.ensureUnusedCapacity(gpa, body_len);
   6877             const inline_bit = @as(u32, @boolToInt(case.inline_token != null)) << 31;
   6878             payloads.items[body_len_index] = body_len | inline_bit;
   6879             appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
   6880         }
   6881     }
   6882     // Now that the item expressions are generated we can add this.
   6883     try parent_gz.instructions.append(gpa, switch_block);
   6884 
   6885     try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).Struct.fields.len +
   6886         @boolToInt(multi_cases_len != 0) +
   6887         payloads.items.len - case_table_end);
   6888 
   6889     const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
   6890         .operand = cond,
   6891         .bits = Zir.Inst.SwitchBlock.Bits{
   6892             .has_multi_cases = multi_cases_len != 0,
   6893             .has_else = special_prong == .@"else",
   6894             .has_under = special_prong == .under,
   6895             .scalar_cases_len = @intCast(Zir.Inst.SwitchBlock.Bits.ScalarCasesLen, scalar_cases_len),
   6896         },
   6897     });
   6898 
   6899     if (multi_cases_len != 0) {
   6900         astgen.extra.appendAssumeCapacity(multi_cases_len);
   6901     }
   6902 
   6903     const zir_datas = astgen.instructions.items(.data);
   6904     const zir_tags = astgen.instructions.items(.tag);
   6905 
   6906     zir_datas[switch_block].pl_node.payload_index = payload_index;
   6907 
   6908     const strat = ri.rl.strategy(&block_scope);
   6909     for (payloads.items[case_table_start..case_table_end]) |start_index, i| {
   6910         var body_len_index = start_index;
   6911         var end_index = start_index;
   6912         const table_index = case_table_start + i;
   6913         if (table_index < scalar_case_table) {
   6914             end_index += 1;
   6915         } else if (table_index < multi_case_table) {
   6916             body_len_index += 1;
   6917             end_index += 2;
   6918         } else {
   6919             body_len_index += 2;
   6920             const items_len = payloads.items[start_index];
   6921             const ranges_len = payloads.items[start_index + 1];
   6922             end_index += 3 + items_len + 2 * ranges_len;
   6923         }
   6924 
   6925         const body_len = @truncate(u31, payloads.items[body_len_index]);
   6926         end_index += body_len;
   6927 
   6928         switch (strat.tag) {
   6929             .break_operand => blk: {
   6930                 // Switch expressions return `true` for `nodeMayNeedMemoryLocation` thus
   6931                 // `elide_store_to_block_ptr_instructions` will either be true,
   6932                 // or all prongs are noreturn.
   6933                 if (!strat.elide_store_to_block_ptr_instructions)
   6934                     break :blk;
   6935 
   6936                 // There will necessarily be a store_to_block_ptr for
   6937                 // all prongs, except for prongs that ended with a noreturn instruction.
   6938                 // Elide all the `store_to_block_ptr` instructions.
   6939 
   6940                 // The break instructions need to have their operands coerced if the
   6941                 // switch's result location is a `ty`. In this case we overwrite the
   6942                 // `store_to_block_ptr` instruction with an `as` instruction and repurpose
   6943                 // it as the break operand.
   6944                 if (body_len < 2)
   6945                     break :blk;
   6946                 const store_inst = payloads.items[end_index - 2];
   6947                 if (zir_tags[store_inst] != .store_to_block_ptr or
   6948                     zir_datas[store_inst].bin.lhs != block_scope.rl_ptr)
   6949                     break :blk;
   6950                 const break_inst = payloads.items[end_index - 1];
   6951                 if (block_scope.rl_ty_inst != .none) {
   6952                     zir_tags[store_inst] = .as;
   6953                     zir_datas[store_inst].bin = .{
   6954                         .lhs = block_scope.rl_ty_inst,
   6955                         .rhs = zir_datas[break_inst].@"break".operand,
   6956                     };
   6957                     zir_datas[break_inst].@"break".operand = indexToRef(store_inst);
   6958                 } else {
   6959                     payloads.items[body_len_index] -= 1;
   6960                     astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index .. end_index - 2]);
   6961                     astgen.extra.appendAssumeCapacity(break_inst);
   6962                     continue;
   6963                 }
   6964             },
   6965             .break_void => {
   6966                 assert(!strat.elide_store_to_block_ptr_instructions);
   6967                 const last_inst = payloads.items[end_index - 1];
   6968                 if (zir_tags[last_inst] == .@"break" and
   6969                     zir_datas[last_inst].@"break".block_inst == switch_block)
   6970                 {
   6971                     zir_datas[last_inst].@"break".operand = .void_value;
   6972                 }
   6973             },
   6974         }
   6975 
   6976         astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
   6977     }
   6978 
   6979     const block_ref = indexToRef(switch_block);
   6980     if (strat.tag == .break_operand and strat.elide_store_to_block_ptr_instructions and ri.rl != .ref)
   6981         return rvalue(parent_gz, ri, block_ref, switch_node);
   6982     return block_ref;
   6983 }
   6984 
   6985 fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   6986     const astgen = gz.astgen;
   6987     const tree = astgen.tree;
   6988     const node_datas = tree.nodes.items(.data);
   6989     const node_tags = tree.nodes.items(.tag);
   6990 
   6991     if (astgen.fn_block == null) {
   6992         return astgen.failNode(node, "'return' outside function scope", .{});
   6993     }
   6994 
   6995     if (gz.any_defer_node != 0) {
   6996         return astgen.failNodeNotes(node, "cannot return from defer expression", .{}, &.{
   6997             try astgen.errNoteNode(
   6998                 gz.any_defer_node,
   6999                 "defer expression here",
   7000                 .{},
   7001             ),
   7002         });
   7003     }
   7004 
   7005     // Ensure debug line/column information is emitted for this return expression.
   7006     // Then we will save the line/column so that we can emit another one that goes
   7007     // "backwards" because we want to evaluate the operand, but then put the debug
   7008     // info back at the return keyword for error return tracing.
   7009     if (!gz.force_comptime) {
   7010         try emitDbgNode(gz, node);
   7011     }
   7012     const ret_line = astgen.source_line - gz.decl_line;
   7013     const ret_column = astgen.source_column;
   7014 
   7015     const defer_outer = &astgen.fn_block.?.base;
   7016 
   7017     const operand_node = node_datas[node].lhs;
   7018     if (operand_node == 0) {
   7019         // Returning a void value; skip error defers.
   7020         try genDefers(gz, defer_outer, scope, .normal_only);
   7021 
   7022         // As our last action before the return, "pop" the error trace if needed
   7023         _ = try gz.addRestoreErrRetIndex(.ret, .always);
   7024 
   7025         _ = try gz.addUnNode(.ret_node, .void_value, node);
   7026         return Zir.Inst.Ref.unreachable_value;
   7027     }
   7028 
   7029     if (node_tags[operand_node] == .error_value) {
   7030         // Hot path for `return error.Foo`. This bypasses result location logic as well as logic
   7031         // for detecting whether to add something to the function's inferred error set.
   7032         const ident_token = node_datas[operand_node].rhs;
   7033         const err_name_str_index = try astgen.identAsString(ident_token);
   7034         const defer_counts = countDefers(defer_outer, scope);
   7035         if (!defer_counts.need_err_code) {
   7036             try genDefers(gz, defer_outer, scope, .both_sans_err);
   7037             try emitDbgStmt(gz, ret_line, ret_column);
   7038             _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token);
   7039             return Zir.Inst.Ref.unreachable_value;
   7040         }
   7041         const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token);
   7042         try genDefers(gz, defer_outer, scope, .{ .both = err_code });
   7043         try emitDbgStmt(gz, ret_line, ret_column);
   7044         _ = try gz.addUnNode(.ret_node, err_code, node);
   7045         return Zir.Inst.Ref.unreachable_value;
   7046     }
   7047 
   7048     const ri: ResultInfo = if (nodeMayNeedMemoryLocation(tree, operand_node, true)) .{
   7049         .rl = .{ .ptr = .{ .inst = try gz.addNode(.ret_ptr, node) } },
   7050         .ctx = .@"return",
   7051     } else .{
   7052         .rl = .{ .ty = try gz.addNode(.ret_type, node) },
   7053         .ctx = .@"return",
   7054     };
   7055     const prev_anon_name_strategy = gz.anon_name_strategy;
   7056     gz.anon_name_strategy = .func;
   7057     const operand = try reachableExpr(gz, scope, ri, operand_node, node);
   7058     gz.anon_name_strategy = prev_anon_name_strategy;
   7059 
   7060     switch (nodeMayEvalToError(tree, operand_node)) {
   7061         .never => {
   7062             // Returning a value that cannot be an error; skip error defers.
   7063             try genDefers(gz, defer_outer, scope, .normal_only);
   7064 
   7065             // As our last action before the return, "pop" the error trace if needed
   7066             _ = try gz.addRestoreErrRetIndex(.ret, .always);
   7067 
   7068             try emitDbgStmt(gz, ret_line, ret_column);
   7069             try gz.addRet(ri, operand, node);
   7070             return Zir.Inst.Ref.unreachable_value;
   7071         },
   7072         .always => {
   7073             // Value is always an error. Emit both error defers and regular defers.
   7074             const err_code = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
   7075             try genDefers(gz, defer_outer, scope, .{ .both = err_code });
   7076             try emitDbgStmt(gz, ret_line, ret_column);
   7077             try gz.addRet(ri, operand, node);
   7078             return Zir.Inst.Ref.unreachable_value;
   7079         },
   7080         .maybe => {
   7081             const defer_counts = countDefers(defer_outer, scope);
   7082             if (!defer_counts.have_err) {
   7083                 // Only regular defers; no branch needed.
   7084                 try genDefers(gz, defer_outer, scope, .normal_only);
   7085                 try emitDbgStmt(gz, ret_line, ret_column);
   7086 
   7087                 // As our last action before the return, "pop" the error trace if needed
   7088                 const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
   7089                 _ = try gz.addRestoreErrRetIndex(.ret, .{ .if_non_error = result });
   7090 
   7091                 try gz.addRet(ri, operand, node);
   7092                 return Zir.Inst.Ref.unreachable_value;
   7093             }
   7094 
   7095             // Emit conditional branch for generating errdefers.
   7096             const result = if (ri.rl == .ptr) try gz.addUnNode(.load, ri.rl.ptr.inst, node) else operand;
   7097             const is_non_err = try gz.addUnNode(.is_non_err, result, node);
   7098             const condbr = try gz.addCondBr(.condbr, node);
   7099 
   7100             var then_scope = gz.makeSubBlock(scope);
   7101             defer then_scope.unstack();
   7102 
   7103             try genDefers(&then_scope, defer_outer, scope, .normal_only);
   7104 
   7105             // As our last action before the return, "pop" the error trace if needed
   7106             _ = try then_scope.addRestoreErrRetIndex(.ret, .always);
   7107 
   7108             try emitDbgStmt(&then_scope, ret_line, ret_column);
   7109             try then_scope.addRet(ri, operand, node);
   7110 
   7111             var else_scope = gz.makeSubBlock(scope);
   7112             defer else_scope.unstack();
   7113 
   7114             const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{
   7115                 .both = try else_scope.addUnNode(.err_union_code, result, node),
   7116             };
   7117             try genDefers(&else_scope, defer_outer, scope, which_ones);
   7118             try emitDbgStmt(&else_scope, ret_line, ret_column);
   7119             try else_scope.addRet(ri, operand, node);
   7120 
   7121             try setCondBrPayload(condbr, is_non_err, &then_scope, 0, &else_scope, 0);
   7122 
   7123             return Zir.Inst.Ref.unreachable_value;
   7124         },
   7125     }
   7126 }
   7127 
   7128 /// Parses the string `buf` as a base 10 integer of type `u16`.
   7129 ///
   7130 /// Unlike std.fmt.parseInt, does not allow the '_' character in `buf`.
   7131 fn parseBitCount(buf: []const u8) std.fmt.ParseIntError!u16 {
   7132     if (buf.len == 0) return error.InvalidCharacter;
   7133 
   7134     var x: u16 = 0;
   7135 
   7136     for (buf) |c| {
   7137         const digit = switch (c) {
   7138             '0'...'9' => c - '0',
   7139             else => return error.InvalidCharacter,
   7140         };
   7141 
   7142         if (x != 0) x = try std.math.mul(u16, x, 10);
   7143         x = try std.math.add(u16, x, @as(u16, digit));
   7144     }
   7145 
   7146     return x;
   7147 }
   7148 
   7149 fn identifier(
   7150     gz: *GenZir,
   7151     scope: *Scope,
   7152     ri: ResultInfo,
   7153     ident: Ast.Node.Index,
   7154 ) InnerError!Zir.Inst.Ref {
   7155     const tracy = trace(@src());
   7156     defer tracy.end();
   7157 
   7158     const astgen = gz.astgen;
   7159     const tree = astgen.tree;
   7160     const main_tokens = tree.nodes.items(.main_token);
   7161 
   7162     const ident_token = main_tokens[ident];
   7163     const ident_name_raw = tree.tokenSlice(ident_token);
   7164     if (mem.eql(u8, ident_name_raw, "_")) {
   7165         return astgen.failNode(ident, "'_' used as an identifier without @\"_\" syntax", .{});
   7166     }
   7167 
   7168     // if not @"" syntax, just use raw token slice
   7169     if (ident_name_raw[0] != '@') {
   7170         if (primitive_instrs.get(ident_name_raw)) |zir_const_ref| {
   7171             return rvalue(gz, ri, zir_const_ref, ident);
   7172         }
   7173 
   7174         if (ident_name_raw.len >= 2) integer: {
   7175             const first_c = ident_name_raw[0];
   7176             if (first_c == 'i' or first_c == 'u') {
   7177                 const signedness: std.builtin.Signedness = switch (first_c == 'i') {
   7178                     true => .signed,
   7179                     false => .unsigned,
   7180                 };
   7181                 if (ident_name_raw.len >= 3 and ident_name_raw[1] == '0') {
   7182                     return astgen.failNode(
   7183                         ident,
   7184                         "primitive integer type '{s}' has leading zero",
   7185                         .{ident_name_raw},
   7186                     );
   7187                 }
   7188                 const bit_count = parseBitCount(ident_name_raw[1..]) catch |err| switch (err) {
   7189                     error.Overflow => return astgen.failNode(
   7190                         ident,
   7191                         "primitive integer type '{s}' exceeds maximum bit width of 65535",
   7192                         .{ident_name_raw},
   7193                     ),
   7194                     error.InvalidCharacter => break :integer,
   7195                 };
   7196                 const result = try gz.add(.{
   7197                     .tag = .int_type,
   7198                     .data = .{ .int_type = .{
   7199                         .src_node = gz.nodeIndexToRelative(ident),
   7200                         .signedness = signedness,
   7201                         .bit_count = bit_count,
   7202                     } },
   7203                 });
   7204                 return rvalue(gz, ri, result, ident);
   7205             }
   7206         }
   7207     }
   7208 
   7209     // Local variables, including function parameters.
   7210     return localVarRef(gz, scope, ri, ident, ident_token);
   7211 }
   7212 
   7213 fn localVarRef(
   7214     gz: *GenZir,
   7215     scope: *Scope,
   7216     ri: ResultInfo,
   7217     ident: Ast.Node.Index,
   7218     ident_token: Ast.TokenIndex,
   7219 ) InnerError!Zir.Inst.Ref {
   7220     const astgen = gz.astgen;
   7221     const gpa = astgen.gpa;
   7222     const name_str_index = try astgen.identAsString(ident_token);
   7223     var s = scope;
   7224     var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
   7225     var num_namespaces_out: u32 = 0;
   7226     var capturing_namespace: ?*Scope.Namespace = null;
   7227     while (true) switch (s.tag) {
   7228         .local_val => {
   7229             const local_val = s.cast(Scope.LocalVal).?;
   7230 
   7231             if (local_val.name == name_str_index) {
   7232                 // Locals cannot shadow anything, so we do not need to look for ambiguous
   7233                 // references in this case.
   7234                 if (ri.rl == .discard and ri.ctx == .assignment) {
   7235                     local_val.discarded = ident_token;
   7236                 } else {
   7237                     local_val.used = ident_token;
   7238                 }
   7239 
   7240                 const value_inst = try tunnelThroughClosure(
   7241                     gz,
   7242                     ident,
   7243                     num_namespaces_out,
   7244                     capturing_namespace,
   7245                     local_val.inst,
   7246                     local_val.token_src,
   7247                     gpa,
   7248                 );
   7249 
   7250                 return rvalue(gz, ri, value_inst, ident);
   7251             }
   7252             s = local_val.parent;
   7253         },
   7254         .local_ptr => {
   7255             const local_ptr = s.cast(Scope.LocalPtr).?;
   7256             if (local_ptr.name == name_str_index) {
   7257                 if (ri.rl == .discard and ri.ctx == .assignment) {
   7258                     local_ptr.discarded = ident_token;
   7259                 } else {
   7260                     local_ptr.used = ident_token;
   7261                 }
   7262 
   7263                 // Can't close over a runtime variable
   7264                 if (num_namespaces_out != 0 and !local_ptr.maybe_comptime) {
   7265                     const ident_name = try astgen.identifierTokenString(ident_token);
   7266                     return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{
   7267                         try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}),
   7268                         try astgen.errNoteNode(capturing_namespace.?.node, "crosses namespace boundary here", .{}),
   7269                     });
   7270                 }
   7271 
   7272                 const ptr_inst = try tunnelThroughClosure(
   7273                     gz,
   7274                     ident,
   7275                     num_namespaces_out,
   7276                     capturing_namespace,
   7277                     local_ptr.ptr,
   7278                     local_ptr.token_src,
   7279                     gpa,
   7280                 );
   7281 
   7282                 switch (ri.rl) {
   7283                     .ref => return ptr_inst,
   7284                     else => {
   7285                         const loaded = try gz.addUnNode(.load, ptr_inst, ident);
   7286                         return rvalue(gz, ri, loaded, ident);
   7287                     },
   7288                 }
   7289             }
   7290             s = local_ptr.parent;
   7291         },
   7292         .gen_zir => s = s.cast(GenZir).?.parent,
   7293         .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
   7294         .namespace => {
   7295             const ns = s.cast(Scope.Namespace).?;
   7296             if (ns.decls.get(name_str_index)) |i| {
   7297                 if (found_already) |f| {
   7298                     return astgen.failNodeNotes(ident, "ambiguous reference", .{}, &.{
   7299                         try astgen.errNoteNode(f, "declared here", .{}),
   7300                         try astgen.errNoteNode(i, "also declared here", .{}),
   7301                     });
   7302                 }
   7303                 // We found a match but must continue looking for ambiguous references to decls.
   7304                 found_already = i;
   7305             }
   7306             num_namespaces_out += 1;
   7307             capturing_namespace = ns;
   7308             s = ns.parent;
   7309         },
   7310         .top => break,
   7311     };
   7312     if (found_already == null) {
   7313         const ident_name = try astgen.identifierTokenString(ident_token);
   7314         return astgen.failNode(ident, "use of undeclared identifier '{s}'", .{ident_name});
   7315     }
   7316 
   7317     // Decl references happen by name rather than ZIR index so that when unrelated
   7318     // decls are modified, ZIR code containing references to them can be unmodified.
   7319     switch (ri.rl) {
   7320         .ref => return gz.addStrTok(.decl_ref, name_str_index, ident_token),
   7321         else => {
   7322             const result = try gz.addStrTok(.decl_val, name_str_index, ident_token);
   7323             return rvalue(gz, ri, result, ident);
   7324         },
   7325     }
   7326 }
   7327 
   7328 /// Adds a capture to a namespace, if needed.
   7329 /// Returns the index of the closure_capture instruction.
   7330 fn tunnelThroughClosure(
   7331     gz: *GenZir,
   7332     inner_ref_node: Ast.Node.Index,
   7333     num_tunnels: u32,
   7334     ns: ?*Scope.Namespace,
   7335     value: Zir.Inst.Ref,
   7336     token: Ast.TokenIndex,
   7337     gpa: Allocator,
   7338 ) !Zir.Inst.Ref {
   7339     // For trivial values, we don't need a tunnel.
   7340     // Just return the ref.
   7341     if (num_tunnels == 0 or refToIndex(value) == null) {
   7342         return value;
   7343     }
   7344 
   7345     // Otherwise we need a tunnel.  Check if this namespace
   7346     // already has one for this value.
   7347     const gop = try ns.?.captures.getOrPut(gpa, refToIndex(value).?);
   7348     if (!gop.found_existing) {
   7349         // Make a new capture for this value but don't add it to the declaring_gz yet
   7350         try gz.astgen.instructions.append(gz.astgen.gpa, .{
   7351             .tag = .closure_capture,
   7352             .data = .{ .un_tok = .{
   7353                 .operand = value,
   7354                 .src_tok = ns.?.declaring_gz.?.tokenIndexToRelative(token),
   7355             } },
   7356         });
   7357         gop.value_ptr.* = @intCast(Zir.Inst.Index, gz.astgen.instructions.len - 1);
   7358     }
   7359 
   7360     // Add an instruction to get the value from the closure into
   7361     // our current context
   7362     return try gz.addInstNode(.closure_get, gop.value_ptr.*, inner_ref_node);
   7363 }
   7364 
   7365 fn stringLiteral(
   7366     gz: *GenZir,
   7367     ri: ResultInfo,
   7368     node: Ast.Node.Index,
   7369 ) InnerError!Zir.Inst.Ref {
   7370     const astgen = gz.astgen;
   7371     const tree = astgen.tree;
   7372     const main_tokens = tree.nodes.items(.main_token);
   7373     const str_lit_token = main_tokens[node];
   7374     const str = try astgen.strLitAsString(str_lit_token);
   7375     const result = try gz.add(.{
   7376         .tag = .str,
   7377         .data = .{ .str = .{
   7378             .start = str.index,
   7379             .len = str.len,
   7380         } },
   7381     });
   7382     return rvalue(gz, ri, result, node);
   7383 }
   7384 
   7385 fn multilineStringLiteral(
   7386     gz: *GenZir,
   7387     ri: ResultInfo,
   7388     node: Ast.Node.Index,
   7389 ) InnerError!Zir.Inst.Ref {
   7390     const astgen = gz.astgen;
   7391     const str = try astgen.strLitNodeAsString(node);
   7392     const result = try gz.add(.{
   7393         .tag = .str,
   7394         .data = .{ .str = .{
   7395             .start = str.index,
   7396             .len = str.len,
   7397         } },
   7398     });
   7399     return rvalue(gz, ri, result, node);
   7400 }
   7401 
   7402 fn charLiteral(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   7403     const astgen = gz.astgen;
   7404     const tree = astgen.tree;
   7405     const main_tokens = tree.nodes.items(.main_token);
   7406     const main_token = main_tokens[node];
   7407     const slice = tree.tokenSlice(main_token);
   7408 
   7409     switch (std.zig.parseCharLiteral(slice)) {
   7410         .success => |codepoint| {
   7411             const result = try gz.addInt(codepoint);
   7412             return rvalue(gz, ri, result, node);
   7413         },
   7414         .failure => |err| return astgen.failWithStrLitError(err, main_token, slice, 0),
   7415     }
   7416 }
   7417 
   7418 const Sign = enum { negative, positive };
   7419 
   7420 fn numberLiteral(gz: *GenZir, ri: ResultInfo, node: Ast.Node.Index, source_node: Ast.Node.Index, sign: Sign) InnerError!Zir.Inst.Ref {
   7421     const astgen = gz.astgen;
   7422     const tree = astgen.tree;
   7423     const main_tokens = tree.nodes.items(.main_token);
   7424     const num_token = main_tokens[node];
   7425     const bytes = tree.tokenSlice(num_token);
   7426 
   7427     const result: Zir.Inst.Ref = switch (std.zig.parseNumberLiteral(bytes)) {
   7428         .int => |num| switch (num) {
   7429             0 => .zero,
   7430             1 => .one,
   7431             else => try gz.addInt(num),
   7432         },
   7433         .big_int => |base| big: {
   7434             const gpa = astgen.gpa;
   7435             var big_int = try std.math.big.int.Managed.init(gpa);
   7436             defer big_int.deinit();
   7437             const prefix_offset = @as(u8, 2) * @boolToInt(base != .decimal);
   7438             big_int.setString(@enumToInt(base), bytes[prefix_offset..]) catch |err| switch (err) {
   7439                 error.InvalidCharacter => unreachable, // caught in `parseNumberLiteral`
   7440                 error.InvalidBase => unreachable, // we only pass 16, 8, 2, see above
   7441                 error.OutOfMemory => return error.OutOfMemory,
   7442             };
   7443 
   7444             const limbs = big_int.limbs[0..big_int.len()];
   7445             assert(big_int.isPositive());
   7446             break :big try gz.addIntBig(limbs);
   7447         },
   7448         .float => {
   7449             const unsigned_float_number = std.fmt.parseFloat(f128, bytes) catch |err| switch (err) {
   7450                 error.InvalidCharacter => unreachable, // validated by tokenizer
   7451             };
   7452             const float_number = switch (sign) {
   7453                 .negative => -unsigned_float_number,
   7454                 .positive => unsigned_float_number,
   7455             };
   7456             // If the value fits into a f64 without losing any precision, store it that way.
   7457             @setFloatMode(.Strict);
   7458             const smaller_float = @floatCast(f64, float_number);
   7459             const bigger_again: f128 = smaller_float;
   7460             if (bigger_again == float_number) {
   7461                 const result = try gz.addFloat(smaller_float);
   7462                 return rvalue(gz, ri, result, source_node);
   7463             }
   7464             // We need to use 128 bits. Break the float into 4 u32 values so we can
   7465             // put it into the `extra` array.
   7466             const int_bits = @bitCast(u128, float_number);
   7467             const result = try gz.addPlNode(.float128, node, Zir.Inst.Float128{
   7468                 .piece0 = @truncate(u32, int_bits),
   7469                 .piece1 = @truncate(u32, int_bits >> 32),
   7470                 .piece2 = @truncate(u32, int_bits >> 64),
   7471                 .piece3 = @truncate(u32, int_bits >> 96),
   7472             });
   7473             return rvalue(gz, ri, result, source_node);
   7474         },
   7475         .failure => |err| return astgen.failWithNumberError(err, num_token, bytes),
   7476     };
   7477 
   7478     if (sign == .positive) {
   7479         return rvalue(gz, ri, result, source_node);
   7480     } else {
   7481         const negated = try gz.addUnNode(.negate, result, source_node);
   7482         return rvalue(gz, ri, negated, source_node);
   7483     }
   7484 }
   7485 
   7486 fn failWithNumberError(astgen: *AstGen, err: std.zig.number_literal.Error, token: Ast.TokenIndex, bytes: []const u8) InnerError {
   7487     const is_float = std.mem.indexOfScalar(u8, bytes, '.') != null;
   7488     switch (err) {
   7489         .leading_zero => if (is_float) {
   7490             return astgen.failTok(token, "number '{s}' has leading zero", .{bytes});
   7491         } else {
   7492             return astgen.failTokNotes(token, "number '{s}' has leading zero", .{bytes}, &.{
   7493                 try astgen.errNoteTok(token, "use '0o' prefix for octal literals", .{}),
   7494             });
   7495         },
   7496         .digit_after_base => return astgen.failTok(token, "expected a digit after base prefix", .{}),
   7497         .upper_case_base => |i| return astgen.failOff(token, @intCast(u32, i), "base prefix must be lowercase", .{}),
   7498         .invalid_float_base => |i| return astgen.failOff(token, @intCast(u32, i), "invalid base for float literal", .{}),
   7499         .repeated_underscore => |i| return astgen.failOff(token, @intCast(u32, i), "repeated digit separator", .{}),
   7500         .invalid_underscore_after_special => |i| return astgen.failOff(token, @intCast(u32, i), "expected digit before digit separator", .{}),
   7501         .invalid_digit => |info| return astgen.failOff(token, @intCast(u32, info.i), "invalid digit '{c}' for {s} base", .{ bytes[info.i], @tagName(info.base) }),
   7502         .invalid_digit_exponent => |i| return astgen.failOff(token, @intCast(u32, i), "invalid digit '{c}' in exponent", .{bytes[i]}),
   7503         .duplicate_exponent => |i| return astgen.failOff(token, @intCast(u32, i), "duplicate exponent", .{}),
   7504         .invalid_hex_exponent => |i| return astgen.failOff(token, @intCast(u32, i), "hex exponent in decimal float", .{}),
   7505         .exponent_after_underscore => |i| return astgen.failOff(token, @intCast(u32, i), "expected digit before exponent", .{}),
   7506         .special_after_underscore => |i| return astgen.failOff(token, @intCast(u32, i), "expected digit before '{c}'", .{bytes[i]}),
   7507         .trailing_special => |i| return astgen.failOff(token, @intCast(u32, i), "expected digit after '{c}'", .{bytes[i - 1]}),
   7508         .trailing_underscore => |i| return astgen.failOff(token, @intCast(u32, i), "trailing digit separator", .{}),
   7509         .duplicate_period => unreachable, // Validated by tokenizer
   7510         .invalid_character => unreachable, // Validated by tokenizer
   7511         .invalid_exponent_sign => unreachable, // Validated by tokenizer
   7512     }
   7513 }
   7514 
   7515 fn asmExpr(
   7516     gz: *GenZir,
   7517     scope: *Scope,
   7518     ri: ResultInfo,
   7519     node: Ast.Node.Index,
   7520     full: Ast.full.Asm,
   7521 ) InnerError!Zir.Inst.Ref {
   7522     const astgen = gz.astgen;
   7523     const tree = astgen.tree;
   7524     const main_tokens = tree.nodes.items(.main_token);
   7525     const node_datas = tree.nodes.items(.data);
   7526     const node_tags = tree.nodes.items(.tag);
   7527     const token_tags = tree.tokens.items(.tag);
   7528 
   7529     const TagAndTmpl = struct { tag: Zir.Inst.Extended, tmpl: u32 };
   7530     const tag_and_tmpl: TagAndTmpl = switch (node_tags[full.ast.template]) {
   7531         .string_literal => .{
   7532             .tag = .@"asm",
   7533             .tmpl = (try astgen.strLitAsString(main_tokens[full.ast.template])).index,
   7534         },
   7535         .multiline_string_literal => .{
   7536             .tag = .@"asm",
   7537             .tmpl = (try astgen.strLitNodeAsString(full.ast.template)).index,
   7538         },
   7539         else => .{
   7540             .tag = .asm_expr,
   7541             .tmpl = @enumToInt(try comptimeExpr(gz, scope, .{ .rl = .none }, full.ast.template)),
   7542         },
   7543     };
   7544 
   7545     // See https://github.com/ziglang/zig/issues/215 and related issues discussing
   7546     // possible inline assembly improvements. Until then here is status quo AstGen
   7547     // for assembly syntax. It's used by std lib crypto aesni.zig.
   7548     const is_container_asm = astgen.fn_block == null;
   7549     if (is_container_asm) {
   7550         if (full.volatile_token) |t|
   7551             return astgen.failTok(t, "volatile is meaningless on global assembly", .{});
   7552         if (full.outputs.len != 0 or full.inputs.len != 0 or full.first_clobber != null)
   7553             return astgen.failNode(node, "global assembly cannot have inputs, outputs, or clobbers", .{});
   7554     } else {
   7555         if (full.outputs.len == 0 and full.volatile_token == null) {
   7556             return astgen.failNode(node, "assembly expression with no output must be marked volatile", .{});
   7557         }
   7558     }
   7559     if (full.outputs.len > 32) {
   7560         return astgen.failNode(full.outputs[32], "too many asm outputs", .{});
   7561     }
   7562     var outputs_buffer: [32]Zir.Inst.Asm.Output = undefined;
   7563     const outputs = outputs_buffer[0..full.outputs.len];
   7564 
   7565     var output_type_bits: u32 = 0;
   7566 
   7567     for (full.outputs) |output_node, i| {
   7568         const symbolic_name = main_tokens[output_node];
   7569         const name = try astgen.identAsString(symbolic_name);
   7570         const constraint_token = symbolic_name + 2;
   7571         const constraint = (try astgen.strLitAsString(constraint_token)).index;
   7572         const has_arrow = token_tags[symbolic_name + 4] == .arrow;
   7573         if (has_arrow) {
   7574             if (output_type_bits != 0) {
   7575                 return astgen.failNode(output_node, "inline assembly allows up to one output value", .{});
   7576             }
   7577             output_type_bits |= @as(u32, 1) << @intCast(u5, i);
   7578             const out_type_node = node_datas[output_node].lhs;
   7579             const out_type_inst = try typeExpr(gz, scope, out_type_node);
   7580             outputs[i] = .{
   7581                 .name = name,
   7582                 .constraint = constraint,
   7583                 .operand = out_type_inst,
   7584             };
   7585         } else {
   7586             const ident_token = symbolic_name + 4;
   7587             // TODO have a look at #215 and related issues and decide how to
   7588             // handle outputs. Do we want this to be identifiers?
   7589             // Or maybe we want to force this to be expressions with a pointer type.
   7590             outputs[i] = .{
   7591                 .name = name,
   7592                 .constraint = constraint,
   7593                 .operand = try localVarRef(gz, scope, .{ .rl = .ref }, node, ident_token),
   7594             };
   7595         }
   7596     }
   7597 
   7598     if (full.inputs.len > 32) {
   7599         return astgen.failNode(full.inputs[32], "too many asm inputs", .{});
   7600     }
   7601     var inputs_buffer: [32]Zir.Inst.Asm.Input = undefined;
   7602     const inputs = inputs_buffer[0..full.inputs.len];
   7603 
   7604     for (full.inputs) |input_node, i| {
   7605         const symbolic_name = main_tokens[input_node];
   7606         const name = try astgen.identAsString(symbolic_name);
   7607         const constraint_token = symbolic_name + 2;
   7608         const constraint = (try astgen.strLitAsString(constraint_token)).index;
   7609         const operand = try expr(gz, scope, .{ .rl = .none }, node_datas[input_node].lhs);
   7610         inputs[i] = .{
   7611             .name = name,
   7612             .constraint = constraint,
   7613             .operand = operand,
   7614         };
   7615     }
   7616 
   7617     var clobbers_buffer: [32]u32 = undefined;
   7618     var clobber_i: usize = 0;
   7619     if (full.first_clobber) |first_clobber| clobbers: {
   7620         // asm ("foo" ::: "a", "b")
   7621         // asm ("foo" ::: "a", "b",)
   7622         var tok_i = first_clobber;
   7623         while (true) : (tok_i += 1) {
   7624             if (clobber_i >= clobbers_buffer.len) {
   7625                 return astgen.failTok(tok_i, "too many asm clobbers", .{});
   7626             }
   7627             clobbers_buffer[clobber_i] = (try astgen.strLitAsString(tok_i)).index;
   7628             clobber_i += 1;
   7629             tok_i += 1;
   7630             switch (token_tags[tok_i]) {
   7631                 .r_paren => break :clobbers,
   7632                 .comma => {
   7633                     if (token_tags[tok_i + 1] == .r_paren) {
   7634                         break :clobbers;
   7635                     } else {
   7636                         continue;
   7637                     }
   7638                 },
   7639                 else => unreachable,
   7640             }
   7641         }
   7642     }
   7643 
   7644     const result = try gz.addAsm(.{
   7645         .tag = tag_and_tmpl.tag,
   7646         .node = node,
   7647         .asm_source = tag_and_tmpl.tmpl,
   7648         .is_volatile = full.volatile_token != null,
   7649         .output_type_bits = output_type_bits,
   7650         .outputs = outputs,
   7651         .inputs = inputs,
   7652         .clobbers = clobbers_buffer[0..clobber_i],
   7653     });
   7654     return rvalue(gz, ri, result, node);
   7655 }
   7656 
   7657 fn as(
   7658     gz: *GenZir,
   7659     scope: *Scope,
   7660     ri: ResultInfo,
   7661     node: Ast.Node.Index,
   7662     lhs: Ast.Node.Index,
   7663     rhs: Ast.Node.Index,
   7664 ) InnerError!Zir.Inst.Ref {
   7665     const dest_type = try typeExpr(gz, scope, lhs);
   7666     switch (ri.rl) {
   7667         .none, .discard, .ref, .ty, .coerced_ty => {
   7668             const result = try reachableExpr(gz, scope, .{ .rl = .{ .ty = dest_type } }, rhs, node);
   7669             return rvalue(gz, ri, result, node);
   7670         },
   7671         .ptr => |result_ptr| {
   7672             return asRlPtr(gz, scope, ri, node, result_ptr.inst, rhs, dest_type);
   7673         },
   7674         .inferred_ptr => |result_ptr| {
   7675             return asRlPtr(gz, scope, ri, node, result_ptr, rhs, dest_type);
   7676         },
   7677         .block_ptr => |block_scope| {
   7678             return asRlPtr(gz, scope, ri, node, block_scope.rl_ptr, rhs, dest_type);
   7679         },
   7680     }
   7681 }
   7682 
   7683 fn unionInit(
   7684     gz: *GenZir,
   7685     scope: *Scope,
   7686     ri: ResultInfo,
   7687     node: Ast.Node.Index,
   7688     params: []const Ast.Node.Index,
   7689 ) InnerError!Zir.Inst.Ref {
   7690     const union_type = try typeExpr(gz, scope, params[0]);
   7691     const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[1]);
   7692     const field_type = try gz.addPlNode(.field_type_ref, params[1], Zir.Inst.FieldTypeRef{
   7693         .container_type = union_type,
   7694         .field_name = field_name,
   7695     });
   7696     const init = try reachableExpr(gz, scope, .{ .rl = .{ .ty = field_type } }, params[2], node);
   7697     const result = try gz.addPlNode(.union_init, node, Zir.Inst.UnionInit{
   7698         .union_type = union_type,
   7699         .init = init,
   7700         .field_name = field_name,
   7701     });
   7702     return rvalue(gz, ri, result, node);
   7703 }
   7704 
   7705 fn asRlPtr(
   7706     parent_gz: *GenZir,
   7707     scope: *Scope,
   7708     ri: ResultInfo,
   7709     src_node: Ast.Node.Index,
   7710     result_ptr: Zir.Inst.Ref,
   7711     operand_node: Ast.Node.Index,
   7712     dest_type: Zir.Inst.Ref,
   7713 ) InnerError!Zir.Inst.Ref {
   7714     var as_scope = try parent_gz.makeCoercionScope(scope, dest_type, result_ptr, src_node);
   7715     defer as_scope.unstack();
   7716 
   7717     const result = try reachableExpr(&as_scope, &as_scope.base, .{ .rl = .{ .block_ptr = &as_scope } }, operand_node, src_node);
   7718     return as_scope.finishCoercion(parent_gz, ri, operand_node, result, dest_type);
   7719 }
   7720 
   7721 fn bitCast(
   7722     gz: *GenZir,
   7723     scope: *Scope,
   7724     ri: ResultInfo,
   7725     node: Ast.Node.Index,
   7726     lhs: Ast.Node.Index,
   7727     rhs: Ast.Node.Index,
   7728 ) InnerError!Zir.Inst.Ref {
   7729     const dest_type = try reachableTypeExpr(gz, scope, lhs, node);
   7730     const operand = try reachableExpr(gz, scope, .{ .rl = .none }, rhs, node);
   7731     const result = try gz.addPlNode(.bitcast, node, Zir.Inst.Bin{
   7732         .lhs = dest_type,
   7733         .rhs = operand,
   7734     });
   7735     return rvalue(gz, ri, result, node);
   7736 }
   7737 
   7738 fn typeOf(
   7739     gz: *GenZir,
   7740     scope: *Scope,
   7741     ri: ResultInfo,
   7742     node: Ast.Node.Index,
   7743     args: []const Ast.Node.Index,
   7744 ) InnerError!Zir.Inst.Ref {
   7745     const astgen = gz.astgen;
   7746     if (args.len < 1) {
   7747         return astgen.failNode(node, "expected at least 1 argument, found 0", .{});
   7748     }
   7749     const gpa = astgen.gpa;
   7750     if (args.len == 1) {
   7751         const typeof_inst = try gz.makeBlockInst(.typeof_builtin, node);
   7752 
   7753         var typeof_scope = gz.makeSubBlock(scope);
   7754         typeof_scope.force_comptime = false;
   7755         typeof_scope.c_import = false;
   7756         defer typeof_scope.unstack();
   7757 
   7758         const ty_expr = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, args[0], node);
   7759         if (!gz.refIsNoReturn(ty_expr)) {
   7760             _ = try typeof_scope.addBreak(.break_inline, typeof_inst, ty_expr);
   7761         }
   7762         try typeof_scope.setBlockBody(typeof_inst);
   7763 
   7764         // typeof_scope unstacked now, can add new instructions to gz
   7765         try gz.instructions.append(gpa, typeof_inst);
   7766         return rvalue(gz, ri, indexToRef(typeof_inst), node);
   7767     }
   7768     const payload_size: u32 = std.meta.fields(Zir.Inst.TypeOfPeer).len;
   7769     const payload_index = try reserveExtra(astgen, payload_size + args.len);
   7770     var args_index = payload_index + payload_size;
   7771 
   7772     const typeof_inst = try gz.addExtendedMultiOpPayloadIndex(.typeof_peer, payload_index, args.len);
   7773 
   7774     var typeof_scope = gz.makeSubBlock(scope);
   7775     typeof_scope.force_comptime = false;
   7776 
   7777     for (args) |arg, i| {
   7778         const param_ref = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, arg, node);
   7779         astgen.extra.items[args_index + i] = @enumToInt(param_ref);
   7780     }
   7781     _ = try typeof_scope.addBreak(.break_inline, refToIndex(typeof_inst).?, .void_value);
   7782 
   7783     const body = typeof_scope.instructionsSlice();
   7784     const body_len = astgen.countBodyLenAfterFixups(body);
   7785     astgen.setExtra(payload_index, Zir.Inst.TypeOfPeer{
   7786         .body_len = @intCast(u32, body_len),
   7787         .body_index = @intCast(u32, astgen.extra.items.len),
   7788         .src_node = gz.nodeIndexToRelative(node),
   7789     });
   7790     try astgen.extra.ensureUnusedCapacity(gpa, body_len);
   7791     astgen.appendBodyWithFixups(body);
   7792     typeof_scope.unstack();
   7793 
   7794     return rvalue(gz, ri, typeof_inst, node);
   7795 }
   7796 
   7797 fn builtinCall(
   7798     gz: *GenZir,
   7799     scope: *Scope,
   7800     ri: ResultInfo,
   7801     node: Ast.Node.Index,
   7802     params: []const Ast.Node.Index,
   7803 ) InnerError!Zir.Inst.Ref {
   7804     const astgen = gz.astgen;
   7805     const tree = astgen.tree;
   7806     const main_tokens = tree.nodes.items(.main_token);
   7807 
   7808     const builtin_token = main_tokens[node];
   7809     const builtin_name = tree.tokenSlice(builtin_token);
   7810 
   7811     // We handle the different builtins manually because they have different semantics depending
   7812     // on the function. For example, `@as` and others participate in result location semantics,
   7813     // and `@cImport` creates a special scope that collects a .c source code text buffer.
   7814     // Also, some builtins have a variable number of parameters.
   7815 
   7816     const info = BuiltinFn.list.get(builtin_name) orelse {
   7817         return astgen.failNode(node, "invalid builtin function: '{s}'", .{
   7818             builtin_name,
   7819         });
   7820     };
   7821     if (info.param_count) |expected| {
   7822         if (expected != params.len) {
   7823             const s = if (expected == 1) "" else "s";
   7824             return astgen.failNode(node, "expected {d} argument{s}, found {d}", .{
   7825                 expected, s, params.len,
   7826             });
   7827         }
   7828     }
   7829 
   7830     switch (info.tag) {
   7831         .import => {
   7832             const node_tags = tree.nodes.items(.tag);
   7833             const operand_node = params[0];
   7834 
   7835             if (node_tags[operand_node] != .string_literal) {
   7836                 // Spec reference: https://github.com/ziglang/zig/issues/2206
   7837                 return astgen.failNode(operand_node, "@import operand must be a string literal", .{});
   7838             }
   7839             const str_lit_token = main_tokens[operand_node];
   7840             const str = try astgen.strLitAsString(str_lit_token);
   7841             const str_slice = astgen.string_bytes.items[str.index..][0..str.len];
   7842             if (mem.indexOfScalar(u8, str_slice, 0) != null) {
   7843                 return astgen.failTok(str_lit_token, "import path cannot contain null bytes", .{});
   7844             } else if (str.len == 0) {
   7845                 return astgen.failTok(str_lit_token, "import path cannot be empty", .{});
   7846             }
   7847             const result = try gz.addStrTok(.import, str.index, str_lit_token);
   7848             const gop = try astgen.imports.getOrPut(astgen.gpa, str.index);
   7849             if (!gop.found_existing) {
   7850                 gop.value_ptr.* = str_lit_token;
   7851             }
   7852             return rvalue(gz, ri, result, node);
   7853         },
   7854         .compile_log => {
   7855             const payload_index = try addExtra(gz.astgen, Zir.Inst.NodeMultiOp{
   7856                 .src_node = gz.nodeIndexToRelative(node),
   7857             });
   7858             var extra_index = try reserveExtra(gz.astgen, params.len);
   7859             for (params) |param| {
   7860                 const param_ref = try expr(gz, scope, .{ .rl = .none }, param);
   7861                 astgen.extra.items[extra_index] = @enumToInt(param_ref);
   7862                 extra_index += 1;
   7863             }
   7864             const result = try gz.addExtendedMultiOpPayloadIndex(.compile_log, payload_index, params.len);
   7865             return rvalue(gz, ri, result, node);
   7866         },
   7867         .field => {
   7868             if (ri.rl == .ref) {
   7869                 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{
   7870                     .lhs = try expr(gz, scope, .{ .rl = .ref }, params[0]),
   7871                     .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[1]),
   7872                 });
   7873             }
   7874             const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{
   7875                 .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]),
   7876                 .field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[1]),
   7877             });
   7878             return rvalue(gz, ri, result, node);
   7879         },
   7880 
   7881         // zig fmt: off
   7882         .as         => return as(       gz, scope, ri, node, params[0], params[1]),
   7883         .bit_cast   => return bitCast(  gz, scope, ri, node, params[0], params[1]),
   7884         .TypeOf     => return typeOf(   gz, scope, ri, node, params),
   7885         .union_init => return unionInit(gz, scope, ri, node, params),
   7886         .c_import   => return cImport(  gz, scope,     node, params[0]),
   7887         // zig fmt: on
   7888 
   7889         .@"export" => {
   7890             const node_tags = tree.nodes.items(.tag);
   7891             const node_datas = tree.nodes.items(.data);
   7892             // This function causes a Decl to be exported. The first parameter is not an expression,
   7893             // but an identifier of the Decl to be exported.
   7894             var namespace: Zir.Inst.Ref = .none;
   7895             var decl_name: u32 = 0;
   7896             switch (node_tags[params[0]]) {
   7897                 .identifier => {
   7898                     const ident_token = main_tokens[params[0]];
   7899                     decl_name = try astgen.identAsString(ident_token);
   7900 
   7901                     var s = scope;
   7902                     var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
   7903                     while (true) switch (s.tag) {
   7904                         .local_val => {
   7905                             const local_val = s.cast(Scope.LocalVal).?;
   7906                             if (local_val.name == decl_name) {
   7907                                 local_val.used = ident_token;
   7908                                 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
   7909                                     .operand = local_val.inst,
   7910                                     .options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .export_options_type } }, params[1]),
   7911                                 });
   7912                                 return rvalue(gz, ri, .void_value, node);
   7913                             }
   7914                             s = local_val.parent;
   7915                         },
   7916                         .local_ptr => {
   7917                             const local_ptr = s.cast(Scope.LocalPtr).?;
   7918                             if (local_ptr.name == decl_name) {
   7919                                 if (!local_ptr.maybe_comptime)
   7920                                     return astgen.failNode(params[0], "unable to export runtime-known value", .{});
   7921                                 local_ptr.used = ident_token;
   7922                                 const loaded = try gz.addUnNode(.load, local_ptr.ptr, node);
   7923                                 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
   7924                                     .operand = loaded,
   7925                                     .options = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .export_options_type } }, params[1]),
   7926                                 });
   7927                                 return rvalue(gz, ri, .void_value, node);
   7928                             }
   7929                             s = local_ptr.parent;
   7930                         },
   7931                         .gen_zir => s = s.cast(GenZir).?.parent,
   7932                         .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
   7933                         .namespace => {
   7934                             const ns = s.cast(Scope.Namespace).?;
   7935                             if (ns.decls.get(decl_name)) |i| {
   7936                                 if (found_already) |f| {
   7937                                     return astgen.failNodeNotes(node, "ambiguous reference", .{}, &.{
   7938                                         try astgen.errNoteNode(f, "declared here", .{}),
   7939                                         try astgen.errNoteNode(i, "also declared here", .{}),
   7940                                     });
   7941                                 }
   7942                                 // We found a match but must continue looking for ambiguous references to decls.
   7943                                 found_already = i;
   7944                             }
   7945                             s = ns.parent;
   7946                         },
   7947                         .top => break,
   7948                     };
   7949                 },
   7950                 .field_access => {
   7951                     const namespace_node = node_datas[params[0]].lhs;
   7952                     namespace = try typeExpr(gz, scope, namespace_node);
   7953                     const dot_token = main_tokens[params[0]];
   7954                     const field_ident = dot_token + 1;
   7955                     decl_name = try astgen.identAsString(field_ident);
   7956                 },
   7957                 else => return astgen.failNode(params[0], "symbol to export must identify a declaration", .{}),
   7958             }
   7959             const options = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .export_options_type } }, params[1]);
   7960             _ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{
   7961                 .namespace = namespace,
   7962                 .decl_name = decl_name,
   7963                 .options = options,
   7964             });
   7965             return rvalue(gz, ri, .void_value, node);
   7966         },
   7967         .@"extern" => {
   7968             const type_inst = try typeExpr(gz, scope, params[0]);
   7969             const options = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .extern_options_type } }, params[1]);
   7970             const result = try gz.addExtendedPayload(.builtin_extern, Zir.Inst.BinNode{
   7971                 .node = gz.nodeIndexToRelative(node),
   7972                 .lhs = type_inst,
   7973                 .rhs = options,
   7974             });
   7975             return rvalue(gz, ri, result, node);
   7976         },
   7977         .fence => {
   7978             const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[0]);
   7979             const result = try gz.addExtendedPayload(.fence, Zir.Inst.UnNode{
   7980                 .node = gz.nodeIndexToRelative(node),
   7981                 .operand = order,
   7982             });
   7983             return rvalue(gz, ri, result, node);
   7984         },
   7985         .set_float_mode => {
   7986             const order = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .float_mode_type } }, params[0]);
   7987             const result = try gz.addExtendedPayload(.set_float_mode, Zir.Inst.UnNode{
   7988                 .node = gz.nodeIndexToRelative(node),
   7989                 .operand = order,
   7990             });
   7991             return rvalue(gz, ri, result, node);
   7992         },
   7993         .set_align_stack => {
   7994             const order = try expr(gz, scope, align_ri, params[0]);
   7995             const result = try gz.addExtendedPayload(.set_align_stack, Zir.Inst.UnNode{
   7996                 .node = gz.nodeIndexToRelative(node),
   7997                 .operand = order,
   7998             });
   7999             return rvalue(gz, ri, result, node);
   8000         },
   8001 
   8002         .src => {
   8003             const token_starts = tree.tokens.items(.start);
   8004             const node_start = token_starts[tree.firstToken(node)];
   8005             astgen.advanceSourceCursor(node_start);
   8006             const result = try gz.addExtendedPayload(.builtin_src, Zir.Inst.Src{
   8007                 .node = gz.nodeIndexToRelative(node),
   8008                 .line = astgen.source_line,
   8009                 .column = astgen.source_column,
   8010             });
   8011             return rvalue(gz, ri, result, node);
   8012         },
   8013 
   8014         // zig fmt: off
   8015         .This               => return rvalue(gz, ri, try gz.addNodeExtended(.this,               node), node),
   8016         .return_address     => return rvalue(gz, ri, try gz.addNodeExtended(.ret_addr,           node), node),
   8017         .error_return_trace => return rvalue(gz, ri, try gz.addNodeExtended(.error_return_trace, node), node),
   8018         .frame              => return rvalue(gz, ri, try gz.addNodeExtended(.frame,              node), node),
   8019         .frame_address      => return rvalue(gz, ri, try gz.addNodeExtended(.frame_address,      node), node),
   8020         .breakpoint         => return rvalue(gz, ri, try gz.addNodeExtended(.breakpoint, node), node),
   8021 
   8022         .type_info   => return simpleUnOpType(gz, scope, ri, node, params[0], .type_info),
   8023         .size_of     => return simpleUnOpType(gz, scope, ri, node, params[0], .size_of),
   8024         .bit_size_of => return simpleUnOpType(gz, scope, ri, node, params[0], .bit_size_of),
   8025         .align_of    => return simpleUnOpType(gz, scope, ri, node, params[0], .align_of),
   8026 
   8027         .ptr_to_int            => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .ptr_to_int),
   8028         .compile_error         => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0], .compile_error),
   8029         .set_eval_branch_quota => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .coerced_ty = .u32_type } },    params[0], .set_eval_branch_quota),
   8030         .enum_to_int           => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .enum_to_int),
   8031         .bool_to_int           => return simpleUnOp(gz, scope, ri, node, bool_ri,                                    params[0], .bool_to_int),
   8032         .embed_file            => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0], .embed_file),
   8033         .error_name            => return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .anyerror_type } },       params[0], .error_name),
   8034         .set_cold              => return simpleUnOp(gz, scope, ri, node, bool_ri,                                    params[0], .set_cold),
   8035         .set_runtime_safety    => return simpleUnOp(gz, scope, ri, node, bool_ri,                                    params[0], .set_runtime_safety),
   8036         .sqrt                  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .sqrt),
   8037         .sin                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .sin),
   8038         .cos                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .cos),
   8039         .tan                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .tan),
   8040         .exp                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .exp),
   8041         .exp2                  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .exp2),
   8042         .log                   => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .log),
   8043         .log2                  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .log2),
   8044         .log10                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .log10),
   8045         .fabs                  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .fabs),
   8046         .floor                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .floor),
   8047         .ceil                  => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .ceil),
   8048         .trunc                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .trunc),
   8049         .round                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .round),
   8050         .tag_name              => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .tag_name),
   8051         .type_name             => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .type_name),
   8052         .Frame                 => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .frame_type),
   8053         .frame_size            => return simpleUnOp(gz, scope, ri, node, .{ .rl = .none },                           params[0], .frame_size),
   8054 
   8055         .float_to_int => return typeCast(gz, scope, ri, node, params[0], params[1], .float_to_int),
   8056         .int_to_float => return typeCast(gz, scope, ri, node, params[0], params[1], .int_to_float),
   8057         .int_to_ptr   => return typeCast(gz, scope, ri, node, params[0], params[1], .int_to_ptr),
   8058         .int_to_enum  => return typeCast(gz, scope, ri, node, params[0], params[1], .int_to_enum),
   8059         .float_cast   => return typeCast(gz, scope, ri, node, params[0], params[1], .float_cast),
   8060         .int_cast     => return typeCast(gz, scope, ri, node, params[0], params[1], .int_cast),
   8061         .ptr_cast     => return typeCast(gz, scope, ri, node, params[0], params[1], .ptr_cast),
   8062         .truncate     => return typeCast(gz, scope, ri, node, params[0], params[1], .truncate),
   8063         // zig fmt: on
   8064 
   8065         .Type => {
   8066             const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .type_info_type } }, params[0]);
   8067 
   8068             const gpa = gz.astgen.gpa;
   8069 
   8070             try gz.instructions.ensureUnusedCapacity(gpa, 1);
   8071             try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
   8072 
   8073             const payload_index = try gz.astgen.addExtra(Zir.Inst.UnNode{
   8074                 .node = gz.nodeIndexToRelative(node),
   8075                 .operand = operand,
   8076             });
   8077             const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
   8078             gz.astgen.instructions.appendAssumeCapacity(.{
   8079                 .tag = .extended,
   8080                 .data = .{ .extended = .{
   8081                     .opcode = .reify,
   8082                     .small = @enumToInt(gz.anon_name_strategy),
   8083                     .operand = payload_index,
   8084                 } },
   8085             });
   8086             gz.instructions.appendAssumeCapacity(new_index);
   8087             const result = indexToRef(new_index);
   8088             return rvalue(gz, ri, result, node);
   8089         },
   8090         .panic => {
   8091             try emitDbgNode(gz, node);
   8092             return simpleUnOp(gz, scope, ri, node, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0], if (gz.force_comptime) .panic_comptime else .panic);
   8093         },
   8094         .error_to_int => {
   8095             const operand = try expr(gz, scope, .{ .rl = .none }, params[0]);
   8096             const result = try gz.addExtendedPayload(.error_to_int, Zir.Inst.UnNode{
   8097                 .node = gz.nodeIndexToRelative(node),
   8098                 .operand = operand,
   8099             });
   8100             return rvalue(gz, ri, result, node);
   8101         },
   8102         .int_to_error => {
   8103             const operand = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u16_type } }, params[0]);
   8104             const result = try gz.addExtendedPayload(.int_to_error, Zir.Inst.UnNode{
   8105                 .node = gz.nodeIndexToRelative(node),
   8106                 .operand = operand,
   8107             });
   8108             return rvalue(gz, ri, result, node);
   8109         },
   8110         .align_cast => {
   8111             const dest_align = try comptimeExpr(gz, scope, align_ri, params[0]);
   8112             const rhs = try expr(gz, scope, .{ .rl = .none }, params[1]);
   8113             const result = try gz.addPlNode(.align_cast, node, Zir.Inst.Bin{
   8114                 .lhs = dest_align,
   8115                 .rhs = rhs,
   8116             });
   8117             return rvalue(gz, ri, result, node);
   8118         },
   8119         .err_set_cast => {
   8120             try emitDbgNode(gz, node);
   8121 
   8122             const result = try gz.addExtendedPayload(.err_set_cast, Zir.Inst.BinNode{
   8123                 .lhs = try typeExpr(gz, scope, params[0]),
   8124                 .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]),
   8125                 .node = gz.nodeIndexToRelative(node),
   8126             });
   8127             return rvalue(gz, ri, result, node);
   8128         },
   8129         .addrspace_cast => {
   8130             const result = try gz.addExtendedPayload(.addrspace_cast, Zir.Inst.BinNode{
   8131                 .lhs = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .address_space_type } }, params[0]),
   8132                 .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]),
   8133                 .node = gz.nodeIndexToRelative(node),
   8134             });
   8135             return rvalue(gz, ri, result, node);
   8136         },
   8137 
   8138         // zig fmt: off
   8139         .has_decl  => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_decl),
   8140         .has_field => return hasDeclOrField(gz, scope, ri, node, params[0], params[1], .has_field),
   8141 
   8142         .clz         => return bitBuiltin(gz, scope, ri, node, params[0], .clz),
   8143         .ctz         => return bitBuiltin(gz, scope, ri, node, params[0], .ctz),
   8144         .pop_count   => return bitBuiltin(gz, scope, ri, node, params[0], .pop_count),
   8145         .byte_swap   => return bitBuiltin(gz, scope, ri, node, params[0], .byte_swap),
   8146         .bit_reverse => return bitBuiltin(gz, scope, ri, node, params[0], .bit_reverse),
   8147 
   8148         .div_exact => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_exact),
   8149         .div_floor => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_floor),
   8150         .div_trunc => return divBuiltin(gz, scope, ri, node, params[0], params[1], .div_trunc),
   8151         .mod       => return divBuiltin(gz, scope, ri, node, params[0], params[1], .mod),
   8152         .rem       => return divBuiltin(gz, scope, ri, node, params[0], params[1], .rem),
   8153 
   8154         .shl_exact => return shiftOp(gz, scope, ri, node, params[0], params[1], .shl_exact),
   8155         .shr_exact => return shiftOp(gz, scope, ri, node, params[0], params[1], .shr_exact),
   8156 
   8157         .bit_offset_of  => return offsetOf(gz, scope, ri, node, params[0], params[1], .bit_offset_of),
   8158         .offset_of => return offsetOf(gz, scope, ri, node, params[0], params[1], .offset_of),
   8159 
   8160         .c_undef   => return simpleCBuiltin(gz, scope, ri, node, params[0], .c_undef),
   8161         .c_include => return simpleCBuiltin(gz, scope, ri, node, params[0], .c_include),
   8162 
   8163         .cmpxchg_strong => return cmpxchg(gz, scope, ri, node, params, 1),
   8164         .cmpxchg_weak   => return cmpxchg(gz, scope, ri, node, params, 0),
   8165         // zig fmt: on
   8166 
   8167         .wasm_memory_size => {
   8168             const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
   8169             const result = try gz.addExtendedPayload(.wasm_memory_size, Zir.Inst.UnNode{
   8170                 .node = gz.nodeIndexToRelative(node),
   8171                 .operand = operand,
   8172             });
   8173             return rvalue(gz, ri, result, node);
   8174         },
   8175         .wasm_memory_grow => {
   8176             const index_arg = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
   8177             const delta_arg = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[1]);
   8178             const result = try gz.addExtendedPayload(.wasm_memory_grow, Zir.Inst.BinNode{
   8179                 .node = gz.nodeIndexToRelative(node),
   8180                 .lhs = index_arg,
   8181                 .rhs = delta_arg,
   8182             });
   8183             return rvalue(gz, ri, result, node);
   8184         },
   8185         .c_define => {
   8186             if (!gz.c_import) return gz.astgen.failNode(node, "C define valid only inside C import block", .{});
   8187             const name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[0]);
   8188             const value = try comptimeExpr(gz, scope, .{ .rl = .none }, params[1]);
   8189             const result = try gz.addExtendedPayload(.c_define, Zir.Inst.BinNode{
   8190                 .node = gz.nodeIndexToRelative(node),
   8191                 .lhs = name,
   8192                 .rhs = value,
   8193             });
   8194             return rvalue(gz, ri, result, node);
   8195         },
   8196 
   8197         .splat => {
   8198             const len = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]);
   8199             const scalar = try expr(gz, scope, .{ .rl = .none }, params[1]);
   8200             const result = try gz.addPlNode(.splat, node, Zir.Inst.Bin{
   8201                 .lhs = len,
   8202                 .rhs = scalar,
   8203             });
   8204             return rvalue(gz, ri, result, node);
   8205         },
   8206         .reduce => {
   8207             const op = try expr(gz, scope, .{ .rl = .{ .ty = .reduce_op_type } }, params[0]);
   8208             const scalar = try expr(gz, scope, .{ .rl = .none }, params[1]);
   8209             const result = try gz.addPlNode(.reduce, node, Zir.Inst.Bin{
   8210                 .lhs = op,
   8211                 .rhs = scalar,
   8212             });
   8213             return rvalue(gz, ri, result, node);
   8214         },
   8215 
   8216         .max => {
   8217             const a = try expr(gz, scope, .{ .rl = .none }, params[0]);
   8218             const b = try expr(gz, scope, .{ .rl = .none }, params[1]);
   8219             const result = try gz.addPlNode(.max, node, Zir.Inst.Bin{
   8220                 .lhs = a,
   8221                 .rhs = b,
   8222             });
   8223             return rvalue(gz, ri, result, node);
   8224         },
   8225         .min => {
   8226             const a = try expr(gz, scope, .{ .rl = .none }, params[0]);
   8227             const b = try expr(gz, scope, .{ .rl = .none }, params[1]);
   8228             const result = try gz.addPlNode(.min, node, Zir.Inst.Bin{
   8229                 .lhs = a,
   8230                 .rhs = b,
   8231             });
   8232             return rvalue(gz, ri, result, node);
   8233         },
   8234 
   8235         .add_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .add_with_overflow),
   8236         .sub_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .sub_with_overflow),
   8237         .mul_with_overflow => return overflowArithmetic(gz, scope, ri, node, params, .mul_with_overflow),
   8238         .shl_with_overflow => {
   8239             const int_type = try typeExpr(gz, scope, params[0]);
   8240             const log2_int_type = try gz.addUnNode(.log2_int_type, int_type, params[0]);
   8241             const ptr_type = try gz.addUnNode(.overflow_arithmetic_ptr, int_type, params[0]);
   8242             const lhs = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[1]);
   8243             const rhs = try expr(gz, scope, .{ .rl = .{ .ty = log2_int_type } }, params[2]);
   8244             const ptr = try expr(gz, scope, .{ .rl = .{ .ty = ptr_type } }, params[3]);
   8245             const result = try gz.addExtendedPayload(.shl_with_overflow, Zir.Inst.OverflowArithmetic{
   8246                 .node = gz.nodeIndexToRelative(node),
   8247                 .lhs = lhs,
   8248                 .rhs = rhs,
   8249                 .ptr = ptr,
   8250             });
   8251             return rvalue(gz, ri, result, node);
   8252         },
   8253 
   8254         .atomic_load => {
   8255             const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.AtomicLoad{
   8256                 // zig fmt: off
   8257                 .elem_type = try typeExpr(gz, scope,                                                   params[0]),
   8258                 .ptr       = try expr    (gz, scope, .{ .rl = .none },                                 params[1]),
   8259                 .ordering  = try expr    (gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[2]),
   8260                 // zig fmt: on
   8261             });
   8262             return rvalue(gz, ri, result, node);
   8263         },
   8264         .atomic_rmw => {
   8265             const int_type = try typeExpr(gz, scope, params[0]);
   8266             const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{
   8267                 // zig fmt: off
   8268                 .ptr       = try expr(gz, scope, .{ .rl = .none },                                  params[1]),
   8269                 .operation = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_rmw_op_type } }, params[2]),
   8270                 .operand   = try expr(gz, scope, .{ .rl = .{ .ty = int_type } },                    params[3]),
   8271                 .ordering  = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } },  params[4]),
   8272                 // zig fmt: on
   8273             });
   8274             return rvalue(gz, ri, result, node);
   8275         },
   8276         .atomic_store => {
   8277             const int_type = try typeExpr(gz, scope, params[0]);
   8278             const result = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{
   8279                 // zig fmt: off
   8280                 .ptr      = try expr(gz, scope, .{ .rl = .none },                                 params[1]),
   8281                 .operand  = try expr(gz, scope, .{ .rl = .{ .ty = int_type } },                   params[2]),
   8282                 .ordering = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[3]),
   8283                 // zig fmt: on
   8284             });
   8285             return rvalue(gz, ri, result, node);
   8286         },
   8287         .mul_add => {
   8288             const float_type = try typeExpr(gz, scope, params[0]);
   8289             const mulend1 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_type } }, params[1]);
   8290             const mulend2 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = float_type } }, params[2]);
   8291             const addend = try expr(gz, scope, .{ .rl = .{ .ty = float_type } }, params[3]);
   8292             const result = try gz.addPlNode(.mul_add, node, Zir.Inst.MulAdd{
   8293                 .mulend1 = mulend1,
   8294                 .mulend2 = mulend2,
   8295                 .addend = addend,
   8296             });
   8297             return rvalue(gz, ri, result, node);
   8298         },
   8299         .call => {
   8300             const options = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .call_options_type } }, params[0]);
   8301             const callee = try calleeExpr(gz, scope, params[1]);
   8302             const args = try expr(gz, scope, .{ .rl = .none }, params[2]);
   8303             const result = try gz.addPlNode(.builtin_call, node, Zir.Inst.BuiltinCall{
   8304                 .options = options,
   8305                 .callee = callee,
   8306                 .args = args,
   8307                 .flags = .{
   8308                     .is_nosuspend = gz.nosuspend_node != 0,
   8309                     .is_comptime = gz.force_comptime,
   8310                     .ensure_result_used = false,
   8311                 },
   8312             });
   8313             return rvalue(gz, ri, result, node);
   8314         },
   8315         .field_parent_ptr => {
   8316             const parent_type = try typeExpr(gz, scope, params[0]);
   8317             const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[1]);
   8318             const result = try gz.addPlNode(.field_parent_ptr, node, Zir.Inst.FieldParentPtr{
   8319                 .parent_type = parent_type,
   8320                 .field_name = field_name,
   8321                 .field_ptr = try expr(gz, scope, .{ .rl = .none }, params[2]),
   8322             });
   8323             return rvalue(gz, ri, result, node);
   8324         },
   8325         .memcpy => {
   8326             const result = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{
   8327                 .dest = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_u8_type } }, params[0]),
   8328                 .source = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_const_u8_type } }, params[1]),
   8329                 .byte_count = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[2]),
   8330             });
   8331             return rvalue(gz, ri, result, node);
   8332         },
   8333         .memset => {
   8334             const result = try gz.addPlNode(.memset, node, Zir.Inst.Memset{
   8335                 .dest = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .manyptr_u8_type } }, params[0]),
   8336                 .byte = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .u8_type } }, params[1]),
   8337                 .byte_count = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, params[2]),
   8338             });
   8339             return rvalue(gz, ri, result, node);
   8340         },
   8341         .shuffle => {
   8342             const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{
   8343                 .elem_type = try typeExpr(gz, scope, params[0]),
   8344                 .a = try expr(gz, scope, .{ .rl = .none }, params[1]),
   8345                 .b = try expr(gz, scope, .{ .rl = .none }, params[2]),
   8346                 .mask = try comptimeExpr(gz, scope, .{ .rl = .none }, params[3]),
   8347             });
   8348             return rvalue(gz, ri, result, node);
   8349         },
   8350         .select => {
   8351             const result = try gz.addExtendedPayload(.select, Zir.Inst.Select{
   8352                 .node = gz.nodeIndexToRelative(node),
   8353                 .elem_type = try typeExpr(gz, scope, params[0]),
   8354                 .pred = try expr(gz, scope, .{ .rl = .none }, params[1]),
   8355                 .a = try expr(gz, scope, .{ .rl = .none }, params[2]),
   8356                 .b = try expr(gz, scope, .{ .rl = .none }, params[3]),
   8357             });
   8358             return rvalue(gz, ri, result, node);
   8359         },
   8360         .async_call => {
   8361             const result = try gz.addExtendedPayload(.builtin_async_call, Zir.Inst.AsyncCall{
   8362                 .node = gz.nodeIndexToRelative(node),
   8363                 .frame_buffer = try expr(gz, scope, .{ .rl = .none }, params[0]),
   8364                 .result_ptr = try expr(gz, scope, .{ .rl = .none }, params[1]),
   8365                 .fn_ptr = try expr(gz, scope, .{ .rl = .none }, params[2]),
   8366                 .args = try expr(gz, scope, .{ .rl = .none }, params[3]),
   8367             });
   8368             return rvalue(gz, ri, result, node);
   8369         },
   8370         .Vector => {
   8371             const result = try gz.addPlNode(.vector_type, node, Zir.Inst.Bin{
   8372                 .lhs = try comptimeExpr(gz, scope, .{ .rl = .{ .coerced_ty = .u32_type } }, params[0]),
   8373                 .rhs = try typeExpr(gz, scope, params[1]),
   8374             });
   8375             return rvalue(gz, ri, result, node);
   8376         },
   8377         .prefetch => {
   8378             const ptr = try expr(gz, scope, .{ .rl = .none }, params[0]);
   8379             const options = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .prefetch_options_type } }, params[1]);
   8380             const result = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{
   8381                 .node = gz.nodeIndexToRelative(node),
   8382                 .lhs = ptr,
   8383                 .rhs = options,
   8384             });
   8385             return rvalue(gz, ri, result, node);
   8386         },
   8387     }
   8388 }
   8389 
   8390 fn simpleNoOpVoid(
   8391     gz: *GenZir,
   8392     ri: ResultInfo,
   8393     node: Ast.Node.Index,
   8394     tag: Zir.Inst.Tag,
   8395 ) InnerError!Zir.Inst.Ref {
   8396     _ = try gz.addNode(tag, node);
   8397     return rvalue(gz, ri, .void_value, node);
   8398 }
   8399 
   8400 fn hasDeclOrField(
   8401     gz: *GenZir,
   8402     scope: *Scope,
   8403     ri: ResultInfo,
   8404     node: Ast.Node.Index,
   8405     lhs_node: Ast.Node.Index,
   8406     rhs_node: Ast.Node.Index,
   8407     tag: Zir.Inst.Tag,
   8408 ) InnerError!Zir.Inst.Ref {
   8409     const container_type = try typeExpr(gz, scope, lhs_node);
   8410     const name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .const_slice_u8_type } }, rhs_node);
   8411     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   8412         .lhs = container_type,
   8413         .rhs = name,
   8414     });
   8415     return rvalue(gz, ri, result, node);
   8416 }
   8417 
   8418 fn typeCast(
   8419     gz: *GenZir,
   8420     scope: *Scope,
   8421     ri: ResultInfo,
   8422     node: Ast.Node.Index,
   8423     lhs_node: Ast.Node.Index,
   8424     rhs_node: Ast.Node.Index,
   8425     tag: Zir.Inst.Tag,
   8426 ) InnerError!Zir.Inst.Ref {
   8427     try emitDbgNode(gz, node);
   8428 
   8429     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   8430         .lhs = try typeExpr(gz, scope, lhs_node),
   8431         .rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node),
   8432     });
   8433     return rvalue(gz, ri, result, node);
   8434 }
   8435 
   8436 fn simpleUnOpType(
   8437     gz: *GenZir,
   8438     scope: *Scope,
   8439     ri: ResultInfo,
   8440     node: Ast.Node.Index,
   8441     operand_node: Ast.Node.Index,
   8442     tag: Zir.Inst.Tag,
   8443 ) InnerError!Zir.Inst.Ref {
   8444     const operand = try typeExpr(gz, scope, operand_node);
   8445     const result = try gz.addUnNode(tag, operand, node);
   8446     return rvalue(gz, ri, result, node);
   8447 }
   8448 
   8449 fn simpleUnOp(
   8450     gz: *GenZir,
   8451     scope: *Scope,
   8452     ri: ResultInfo,
   8453     node: Ast.Node.Index,
   8454     operand_ri: ResultInfo,
   8455     operand_node: Ast.Node.Index,
   8456     tag: Zir.Inst.Tag,
   8457 ) InnerError!Zir.Inst.Ref {
   8458     switch (tag) {
   8459         .tag_name, .error_name, .ptr_to_int => try emitDbgNode(gz, node),
   8460         else => {},
   8461     }
   8462     const operand = try expr(gz, scope, operand_ri, operand_node);
   8463     const result = try gz.addUnNode(tag, operand, node);
   8464     return rvalue(gz, ri, result, node);
   8465 }
   8466 
   8467 fn negation(
   8468     gz: *GenZir,
   8469     scope: *Scope,
   8470     ri: ResultInfo,
   8471     node: Ast.Node.Index,
   8472 ) InnerError!Zir.Inst.Ref {
   8473     const astgen = gz.astgen;
   8474     const tree = astgen.tree;
   8475     const node_tags = tree.nodes.items(.tag);
   8476     const node_datas = tree.nodes.items(.data);
   8477 
   8478     // Check for float literal as the sub-expression because we want to preserve
   8479     // its negativity rather than having it go through comptime subtraction.
   8480     const operand_node = node_datas[node].lhs;
   8481     if (node_tags[operand_node] == .number_literal) {
   8482         return numberLiteral(gz, ri, operand_node, node, .negative);
   8483     }
   8484 
   8485     const operand = try expr(gz, scope, .{ .rl = .none }, operand_node);
   8486     const result = try gz.addUnNode(.negate, operand, node);
   8487     return rvalue(gz, ri, result, node);
   8488 }
   8489 
   8490 fn cmpxchg(
   8491     gz: *GenZir,
   8492     scope: *Scope,
   8493     ri: ResultInfo,
   8494     node: Ast.Node.Index,
   8495     params: []const Ast.Node.Index,
   8496     small: u16,
   8497 ) InnerError!Zir.Inst.Ref {
   8498     const int_type = try typeExpr(gz, scope, params[0]);
   8499     const result = try gz.addExtendedPayloadSmall(.cmpxchg, small, Zir.Inst.Cmpxchg{
   8500         // zig fmt: off
   8501         .node           = gz.nodeIndexToRelative(node),
   8502         .ptr            = try expr(gz, scope, .{ .rl = .none },                                 params[1]),
   8503         .expected_value = try expr(gz, scope, .{ .rl = .{ .ty = int_type } },                   params[2]),
   8504         .new_value      = try expr(gz, scope, .{ .rl = .{ .coerced_ty = int_type } },           params[3]),
   8505         .success_order  = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[4]),
   8506         .failure_order  = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .atomic_order_type } }, params[5]),
   8507         // zig fmt: on
   8508     });
   8509     return rvalue(gz, ri, result, node);
   8510 }
   8511 
   8512 fn bitBuiltin(
   8513     gz: *GenZir,
   8514     scope: *Scope,
   8515     ri: ResultInfo,
   8516     node: Ast.Node.Index,
   8517     operand_node: Ast.Node.Index,
   8518     tag: Zir.Inst.Tag,
   8519 ) InnerError!Zir.Inst.Ref {
   8520     const operand = try expr(gz, scope, .{ .rl = .none }, operand_node);
   8521     const result = try gz.addUnNode(tag, operand, node);
   8522     return rvalue(gz, ri, result, node);
   8523 }
   8524 
   8525 fn divBuiltin(
   8526     gz: *GenZir,
   8527     scope: *Scope,
   8528     ri: ResultInfo,
   8529     node: Ast.Node.Index,
   8530     lhs_node: Ast.Node.Index,
   8531     rhs_node: Ast.Node.Index,
   8532     tag: Zir.Inst.Tag,
   8533 ) InnerError!Zir.Inst.Ref {
   8534     try emitDbgNode(gz, node);
   8535 
   8536     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   8537         .lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node),
   8538         .rhs = try expr(gz, scope, .{ .rl = .none }, rhs_node),
   8539     });
   8540     return rvalue(gz, ri, result, node);
   8541 }
   8542 
   8543 fn simpleCBuiltin(
   8544     gz: *GenZir,
   8545     scope: *Scope,
   8546     ri: ResultInfo,
   8547     node: Ast.Node.Index,
   8548     operand_node: Ast.Node.Index,
   8549     tag: Zir.Inst.Extended,
   8550 ) InnerError!Zir.Inst.Ref {
   8551     const name: []const u8 = if (tag == .c_undef) "C undef" else "C include";
   8552     if (!gz.c_import) return gz.astgen.failNode(node, "{s} valid only inside C import block", .{name});
   8553     const operand = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .const_slice_u8_type } }, operand_node);
   8554     _ = try gz.addExtendedPayload(tag, Zir.Inst.UnNode{
   8555         .node = gz.nodeIndexToRelative(node),
   8556         .operand = operand,
   8557     });
   8558     return rvalue(gz, ri, .void_value, node);
   8559 }
   8560 
   8561 fn offsetOf(
   8562     gz: *GenZir,
   8563     scope: *Scope,
   8564     ri: ResultInfo,
   8565     node: Ast.Node.Index,
   8566     lhs_node: Ast.Node.Index,
   8567     rhs_node: Ast.Node.Index,
   8568     tag: Zir.Inst.Tag,
   8569 ) InnerError!Zir.Inst.Ref {
   8570     const type_inst = try typeExpr(gz, scope, lhs_node);
   8571     const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .const_slice_u8_type } }, rhs_node);
   8572     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   8573         .lhs = type_inst,
   8574         .rhs = field_name,
   8575     });
   8576     return rvalue(gz, ri, result, node);
   8577 }
   8578 
   8579 fn shiftOp(
   8580     gz: *GenZir,
   8581     scope: *Scope,
   8582     ri: ResultInfo,
   8583     node: Ast.Node.Index,
   8584     lhs_node: Ast.Node.Index,
   8585     rhs_node: Ast.Node.Index,
   8586     tag: Zir.Inst.Tag,
   8587 ) InnerError!Zir.Inst.Ref {
   8588     var line = gz.astgen.source_line - gz.decl_line;
   8589     var column = gz.astgen.source_column;
   8590     const lhs = try expr(gz, scope, .{ .rl = .none }, lhs_node);
   8591 
   8592     switch (gz.astgen.tree.nodes.items(.tag)[node]) {
   8593         .shl, .shr => {
   8594             maybeAdvanceSourceCursorToMainToken(gz, node);
   8595             line = gz.astgen.source_line - gz.decl_line;
   8596             column = gz.astgen.source_column;
   8597         },
   8598         else => {},
   8599     }
   8600 
   8601     const log2_int_type = try gz.addUnNode(.typeof_log2_int_type, lhs, lhs_node);
   8602     const rhs = try expr(gz, scope, .{ .rl = .{ .ty = log2_int_type }, .ctx = .shift_op }, rhs_node);
   8603 
   8604     try emitDbgStmt(gz, line, column);
   8605     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   8606         .lhs = lhs,
   8607         .rhs = rhs,
   8608     });
   8609     return rvalue(gz, ri, result, node);
   8610 }
   8611 
   8612 fn cImport(
   8613     gz: *GenZir,
   8614     scope: *Scope,
   8615     node: Ast.Node.Index,
   8616     body_node: Ast.Node.Index,
   8617 ) InnerError!Zir.Inst.Ref {
   8618     const astgen = gz.astgen;
   8619     const gpa = astgen.gpa;
   8620 
   8621     if (gz.c_import) return gz.astgen.failNode(node, "cannot nest @cImport", .{});
   8622 
   8623     var block_scope = gz.makeSubBlock(scope);
   8624     block_scope.force_comptime = true;
   8625     block_scope.c_import = true;
   8626     defer block_scope.unstack();
   8627 
   8628     const block_inst = try gz.makeBlockInst(.c_import, node);
   8629     const block_result = try expr(&block_scope, &block_scope.base, .{ .rl = .none }, body_node);
   8630     _ = try gz.addUnNode(.ensure_result_used, block_result, node);
   8631     if (!gz.refIsNoReturn(block_result)) {
   8632         _ = try block_scope.addBreak(.break_inline, block_inst, .void_value);
   8633     }
   8634     try block_scope.setBlockBody(block_inst);
   8635     // block_scope unstacked now, can add new instructions to gz
   8636     try gz.instructions.append(gpa, block_inst);
   8637 
   8638     return indexToRef(block_inst);
   8639 }
   8640 
   8641 fn overflowArithmetic(
   8642     gz: *GenZir,
   8643     scope: *Scope,
   8644     ri: ResultInfo,
   8645     node: Ast.Node.Index,
   8646     params: []const Ast.Node.Index,
   8647     tag: Zir.Inst.Extended,
   8648 ) InnerError!Zir.Inst.Ref {
   8649     const int_type = try typeExpr(gz, scope, params[0]);
   8650     const ptr_type = try gz.addUnNode(.overflow_arithmetic_ptr, int_type, params[0]);
   8651     const lhs = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[1]);
   8652     const rhs = try expr(gz, scope, .{ .rl = .{ .ty = int_type } }, params[2]);
   8653     const ptr = try expr(gz, scope, .{ .rl = .{ .ty = ptr_type } }, params[3]);
   8654     const result = try gz.addExtendedPayload(tag, Zir.Inst.OverflowArithmetic{
   8655         .node = gz.nodeIndexToRelative(node),
   8656         .lhs = lhs,
   8657         .rhs = rhs,
   8658         .ptr = ptr,
   8659     });
   8660     return rvalue(gz, ri, result, node);
   8661 }
   8662 
   8663 fn callExpr(
   8664     gz: *GenZir,
   8665     scope: *Scope,
   8666     ri: ResultInfo,
   8667     node: Ast.Node.Index,
   8668     call: Ast.full.Call,
   8669 ) InnerError!Zir.Inst.Ref {
   8670     const astgen = gz.astgen;
   8671 
   8672     const callee = try calleeExpr(gz, scope, call.ast.fn_expr);
   8673     const modifier: std.builtin.CallOptions.Modifier = blk: {
   8674         if (gz.force_comptime) {
   8675             break :blk .compile_time;
   8676         }
   8677         if (call.async_token != null) {
   8678             break :blk .async_kw;
   8679         }
   8680         if (gz.nosuspend_node != 0) {
   8681             break :blk .no_async;
   8682         }
   8683         break :blk .auto;
   8684     };
   8685 
   8686     {
   8687         astgen.advanceSourceCursor(astgen.tree.tokens.items(.start)[call.ast.lparen]);
   8688         const line = astgen.source_line - gz.decl_line;
   8689         const column = astgen.source_column;
   8690 
   8691         _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
   8692             .dbg_stmt = .{
   8693                 .line = line,
   8694                 .column = column,
   8695             },
   8696         } });
   8697     }
   8698 
   8699     assert(callee != .none);
   8700     assert(node != 0);
   8701 
   8702     const call_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
   8703     const call_inst = Zir.indexToRef(call_index);
   8704     try gz.astgen.instructions.append(astgen.gpa, undefined);
   8705     try gz.instructions.append(astgen.gpa, call_index);
   8706 
   8707     const scratch_top = astgen.scratch.items.len;
   8708     defer astgen.scratch.items.len = scratch_top;
   8709 
   8710     var scratch_index = scratch_top;
   8711     try astgen.scratch.resize(astgen.gpa, scratch_top + call.ast.params.len);
   8712 
   8713     for (call.ast.params) |param_node| {
   8714         var arg_block = gz.makeSubBlock(scope);
   8715         defer arg_block.unstack();
   8716 
   8717         // `call_inst` is reused to provide the param type.
   8718         const arg_ref = try expr(&arg_block, &arg_block.base, .{ .rl = .{ .coerced_ty = call_inst }, .ctx = .fn_arg }, param_node);
   8719         _ = try arg_block.addBreak(.break_inline, call_index, arg_ref);
   8720 
   8721         const body = arg_block.instructionsSlice();
   8722         try astgen.scratch.ensureUnusedCapacity(astgen.gpa, countBodyLenAfterFixups(astgen, body));
   8723         appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
   8724 
   8725         astgen.scratch.items[scratch_index] = @intCast(u32, astgen.scratch.items.len - scratch_top);
   8726         scratch_index += 1;
   8727     }
   8728 
   8729     // If our result location is a try/catch/error-union-if/return, a function argument,
   8730     // or an initializer for a `const` variable, the error trace propagates.
   8731     // Otherwise, it should always be popped (handled in Sema).
   8732     const propagate_error_trace = switch (ri.ctx) {
   8733         .error_handling_expr, .@"return", .fn_arg, .const_init => true,
   8734         else => false,
   8735     };
   8736 
   8737     const payload_index = try addExtra(astgen, Zir.Inst.Call{
   8738         .callee = callee,
   8739         .flags = .{
   8740             .pop_error_return_trace = !propagate_error_trace,
   8741             .packed_modifier = @intCast(Zir.Inst.Call.Flags.PackedModifier, @enumToInt(modifier)),
   8742             .args_len = @intCast(Zir.Inst.Call.Flags.PackedArgsLen, call.ast.params.len),
   8743         },
   8744     });
   8745     if (call.ast.params.len != 0) {
   8746         try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]);
   8747     }
   8748     gz.astgen.instructions.set(call_index, .{
   8749         .tag = .call,
   8750         .data = .{ .pl_node = .{
   8751             .src_node = gz.nodeIndexToRelative(node),
   8752             .payload_index = payload_index,
   8753         } },
   8754     });
   8755     return rvalue(gz, ri, call_inst, node); // TODO function call with result location
   8756 }
   8757 
   8758 /// calleeExpr generates the function part of a call expression (f in f(x)), or the
   8759 /// callee argument to the @call() builtin. If the lhs is a field access or the
   8760 /// @field() builtin, we need to generate a special field_call_bind instruction
   8761 /// instead of the normal field_val or field_ptr.  If this is a inst.func() call,
   8762 /// this instruction will capture the value of the first argument before evaluating
   8763 /// the other arguments. We need to use .ref here to guarantee we will be able to
   8764 /// promote an lvalue to an address if the first parameter requires it.  This
   8765 /// unfortunately also means we need to take a reference to any types on the lhs.
   8766 fn calleeExpr(
   8767     gz: *GenZir,
   8768     scope: *Scope,
   8769     node: Ast.Node.Index,
   8770 ) InnerError!Zir.Inst.Ref {
   8771     const astgen = gz.astgen;
   8772     const tree = astgen.tree;
   8773 
   8774     const tag = tree.nodes.items(.tag)[node];
   8775     switch (tag) {
   8776         .field_access => return addFieldAccess(.field_call_bind, gz, scope, .{ .rl = .ref }, node),
   8777 
   8778         .builtin_call_two,
   8779         .builtin_call_two_comma,
   8780         .builtin_call,
   8781         .builtin_call_comma,
   8782         => {
   8783             const node_datas = tree.nodes.items(.data);
   8784             const main_tokens = tree.nodes.items(.main_token);
   8785             const builtin_token = main_tokens[node];
   8786             const builtin_name = tree.tokenSlice(builtin_token);
   8787 
   8788             var inline_params: [2]Ast.Node.Index = undefined;
   8789             var params: []Ast.Node.Index = switch (tag) {
   8790                 .builtin_call,
   8791                 .builtin_call_comma,
   8792                 => tree.extra_data[node_datas[node].lhs..node_datas[node].rhs],
   8793 
   8794                 .builtin_call_two,
   8795                 .builtin_call_two_comma,
   8796                 => blk: {
   8797                     inline_params = .{ node_datas[node].lhs, node_datas[node].rhs };
   8798                     const len: usize = if (inline_params[0] == 0) @as(usize, 0) else if (inline_params[1] == 0) @as(usize, 1) else @as(usize, 2);
   8799                     break :blk inline_params[0..len];
   8800                 },
   8801 
   8802                 else => unreachable,
   8803             };
   8804 
   8805             // If anything is wrong, fall back to builtinCall.
   8806             // It will emit any necessary compile errors and notes.
   8807             if (std.mem.eql(u8, builtin_name, "@field") and params.len == 2) {
   8808                 const lhs = try expr(gz, scope, .{ .rl = .ref }, params[0]);
   8809                 const field_name = try comptimeExpr(gz, scope, .{ .rl = .{ .ty = .const_slice_u8_type } }, params[1]);
   8810                 return gz.addExtendedPayload(.field_call_bind_named, Zir.Inst.FieldNamedNode{
   8811                     .node = gz.nodeIndexToRelative(node),
   8812                     .lhs = lhs,
   8813                     .field_name = field_name,
   8814                 });
   8815             }
   8816 
   8817             return builtinCall(gz, scope, .{ .rl = .none }, node, params);
   8818         },
   8819         else => return expr(gz, scope, .{ .rl = .none }, node),
   8820     }
   8821 }
   8822 
   8823 const primitive_instrs = std.ComptimeStringMap(Zir.Inst.Ref, .{
   8824     .{ "anyerror", .anyerror_type },
   8825     .{ "anyframe", .anyframe_type },
   8826     .{ "anyopaque", .anyopaque_type },
   8827     .{ "bool", .bool_type },
   8828     .{ "c_int", .c_int_type },
   8829     .{ "c_long", .c_long_type },
   8830     .{ "c_longdouble", .c_longdouble_type },
   8831     .{ "c_longlong", .c_longlong_type },
   8832     .{ "c_short", .c_short_type },
   8833     .{ "c_uint", .c_uint_type },
   8834     .{ "c_ulong", .c_ulong_type },
   8835     .{ "c_ulonglong", .c_ulonglong_type },
   8836     .{ "c_ushort", .c_ushort_type },
   8837     .{ "comptime_float", .comptime_float_type },
   8838     .{ "comptime_int", .comptime_int_type },
   8839     .{ "f128", .f128_type },
   8840     .{ "f16", .f16_type },
   8841     .{ "f32", .f32_type },
   8842     .{ "f64", .f64_type },
   8843     .{ "f80", .f80_type },
   8844     .{ "false", .bool_false },
   8845     .{ "i16", .i16_type },
   8846     .{ "i32", .i32_type },
   8847     .{ "i64", .i64_type },
   8848     .{ "i128", .i128_type },
   8849     .{ "i8", .i8_type },
   8850     .{ "isize", .isize_type },
   8851     .{ "noreturn", .noreturn_type },
   8852     .{ "null", .null_value },
   8853     .{ "true", .bool_true },
   8854     .{ "type", .type_type },
   8855     .{ "u16", .u16_type },
   8856     .{ "u29", .u29_type },
   8857     .{ "u32", .u32_type },
   8858     .{ "u64", .u64_type },
   8859     .{ "u128", .u128_type },
   8860     .{ "u1", .u1_type },
   8861     .{ "u8", .u8_type },
   8862     .{ "undefined", .undef },
   8863     .{ "usize", .usize_type },
   8864     .{ "void", .void_type },
   8865 });
   8866 
   8867 comptime {
   8868     // These checks ensure that std.zig.primitives stays in synce with the primitive->Zir map.
   8869     const primitives = std.zig.primitives;
   8870     for (primitive_instrs.kvs) |kv| {
   8871         if (!primitives.isPrimitive(kv.key)) {
   8872             @compileError("std.zig.isPrimitive() is not aware of Zir instr '" ++ @tagName(kv.value) ++ "'");
   8873         }
   8874     }
   8875     for (primitives.names.kvs) |kv| {
   8876         if (primitive_instrs.get(kv.key) == null) {
   8877             @compileError("std.zig.primitives entry '" ++ kv.key ++ "' does not have a corresponding Zir instr");
   8878         }
   8879     }
   8880 }
   8881 
   8882 fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index, have_res_ty: bool) bool {
   8883     const node_tags = tree.nodes.items(.tag);
   8884     const node_datas = tree.nodes.items(.data);
   8885     const main_tokens = tree.nodes.items(.main_token);
   8886     const token_tags = tree.tokens.items(.tag);
   8887 
   8888     var node = start_node;
   8889     while (true) {
   8890         switch (node_tags[node]) {
   8891             .root,
   8892             .@"usingnamespace",
   8893             .test_decl,
   8894             .switch_case,
   8895             .switch_case_inline,
   8896             .switch_case_one,
   8897             .switch_case_inline_one,
   8898             .container_field_init,
   8899             .container_field_align,
   8900             .container_field,
   8901             .asm_output,
   8902             .asm_input,
   8903             => unreachable,
   8904 
   8905             .@"return",
   8906             .@"break",
   8907             .@"continue",
   8908             .bit_not,
   8909             .bool_not,
   8910             .global_var_decl,
   8911             .local_var_decl,
   8912             .simple_var_decl,
   8913             .aligned_var_decl,
   8914             .@"defer",
   8915             .@"errdefer",
   8916             .address_of,
   8917             .optional_type,
   8918             .negation,
   8919             .negation_wrap,
   8920             .@"resume",
   8921             .array_type,
   8922             .array_type_sentinel,
   8923             .ptr_type_aligned,
   8924             .ptr_type_sentinel,
   8925             .ptr_type,
   8926             .ptr_type_bit_range,
   8927             .@"suspend",
   8928             .fn_proto_simple,
   8929             .fn_proto_multi,
   8930             .fn_proto_one,
   8931             .fn_proto,
   8932             .fn_decl,
   8933             .anyframe_type,
   8934             .anyframe_literal,
   8935             .number_literal,
   8936             .enum_literal,
   8937             .string_literal,
   8938             .multiline_string_literal,
   8939             .char_literal,
   8940             .unreachable_literal,
   8941             .identifier,
   8942             .error_set_decl,
   8943             .container_decl,
   8944             .container_decl_trailing,
   8945             .container_decl_two,
   8946             .container_decl_two_trailing,
   8947             .container_decl_arg,
   8948             .container_decl_arg_trailing,
   8949             .tagged_union,
   8950             .tagged_union_trailing,
   8951             .tagged_union_two,
   8952             .tagged_union_two_trailing,
   8953             .tagged_union_enum_tag,
   8954             .tagged_union_enum_tag_trailing,
   8955             .@"asm",
   8956             .asm_simple,
   8957             .add,
   8958             .add_wrap,
   8959             .add_sat,
   8960             .array_cat,
   8961             .array_mult,
   8962             .assign,
   8963             .assign_bit_and,
   8964             .assign_bit_or,
   8965             .assign_shl,
   8966             .assign_shl_sat,
   8967             .assign_shr,
   8968             .assign_bit_xor,
   8969             .assign_div,
   8970             .assign_sub,
   8971             .assign_sub_wrap,
   8972             .assign_sub_sat,
   8973             .assign_mod,
   8974             .assign_add,
   8975             .assign_add_wrap,
   8976             .assign_add_sat,
   8977             .assign_mul,
   8978             .assign_mul_wrap,
   8979             .assign_mul_sat,
   8980             .bang_equal,
   8981             .bit_and,
   8982             .bit_or,
   8983             .shl,
   8984             .shl_sat,
   8985             .shr,
   8986             .bit_xor,
   8987             .bool_and,
   8988             .bool_or,
   8989             .div,
   8990             .equal_equal,
   8991             .error_union,
   8992             .greater_or_equal,
   8993             .greater_than,
   8994             .less_or_equal,
   8995             .less_than,
   8996             .merge_error_sets,
   8997             .mod,
   8998             .mul,
   8999             .mul_wrap,
   9000             .mul_sat,
   9001             .switch_range,
   9002             .field_access,
   9003             .sub,
   9004             .sub_wrap,
   9005             .sub_sat,
   9006             .slice,
   9007             .slice_open,
   9008             .slice_sentinel,
   9009             .deref,
   9010             .array_access,
   9011             .error_value,
   9012             .while_simple, // This variant cannot have an else expression.
   9013             .while_cont, // This variant cannot have an else expression.
   9014             .for_simple, // This variant cannot have an else expression.
   9015             .if_simple, // This variant cannot have an else expression.
   9016             => return false,
   9017 
   9018             // Forward the question to the LHS sub-expression.
   9019             .grouped_expression,
   9020             .@"try",
   9021             .@"await",
   9022             .@"comptime",
   9023             .@"nosuspend",
   9024             .unwrap_optional,
   9025             => node = node_datas[node].lhs,
   9026 
   9027             // Forward the question to the RHS sub-expression.
   9028             .@"catch",
   9029             .@"orelse",
   9030             => node = node_datas[node].rhs,
   9031 
   9032             // Array and struct init exprs write to result locs, but anon literals do not.
   9033             .array_init_one,
   9034             .array_init_one_comma,
   9035             .struct_init_one,
   9036             .struct_init_one_comma,
   9037             .array_init,
   9038             .array_init_comma,
   9039             .struct_init,
   9040             .struct_init_comma,
   9041             => return have_res_ty or node_datas[node].lhs != 0,
   9042 
   9043             // Anon literals do not need result location.
   9044             .array_init_dot_two,
   9045             .array_init_dot_two_comma,
   9046             .array_init_dot,
   9047             .array_init_dot_comma,
   9048             .struct_init_dot_two,
   9049             .struct_init_dot_two_comma,
   9050             .struct_init_dot,
   9051             .struct_init_dot_comma,
   9052             => return have_res_ty,
   9053 
   9054             // True because depending on comptime conditions, sub-expressions
   9055             // may be the kind that need memory locations.
   9056             .@"while", // This variant always has an else expression.
   9057             .@"if", // This variant always has an else expression.
   9058             .@"for", // This variant always has an else expression.
   9059             .@"switch",
   9060             .switch_comma,
   9061             .call_one,
   9062             .call_one_comma,
   9063             .async_call_one,
   9064             .async_call_one_comma,
   9065             .call,
   9066             .call_comma,
   9067             .async_call,
   9068             .async_call_comma,
   9069             => return true,
   9070 
   9071             .block_two,
   9072             .block_two_semicolon,
   9073             .block,
   9074             .block_semicolon,
   9075             => {
   9076                 const lbrace = main_tokens[node];
   9077                 if (token_tags[lbrace - 1] == .colon) {
   9078                     // Labeled blocks may need a memory location to forward
   9079                     // to their break statements.
   9080                     return true;
   9081                 } else {
   9082                     return false;
   9083                 }
   9084             },
   9085 
   9086             .builtin_call_two, .builtin_call_two_comma => {
   9087                 const builtin_token = main_tokens[node];
   9088                 const builtin_name = tree.tokenSlice(builtin_token);
   9089                 // If the builtin is an invalid name, we don't cause an error here; instead
   9090                 // let it pass, and the error will be "invalid builtin function" later.
   9091                 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false;
   9092                 switch (builtin_info.needs_mem_loc) {
   9093                     .never => return false,
   9094                     .always => return true,
   9095                     .forward1 => node = node_datas[node].rhs,
   9096                 }
   9097             },
   9098 
   9099             .builtin_call, .builtin_call_comma => {
   9100                 const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
   9101                 const builtin_token = main_tokens[node];
   9102                 const builtin_name = tree.tokenSlice(builtin_token);
   9103                 // If the builtin is an invalid name, we don't cause an error here; instead
   9104                 // let it pass, and the error will be "invalid builtin function" later.
   9105                 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false;
   9106                 switch (builtin_info.needs_mem_loc) {
   9107                     .never => return false,
   9108                     .always => return true,
   9109                     .forward1 => node = params[1],
   9110                 }
   9111             },
   9112         }
   9113     }
   9114 }
   9115 
   9116 fn nodeMayAppendToErrorTrace(tree: *const Ast, start_node: Ast.Node.Index) bool {
   9117     const node_tags = tree.nodes.items(.tag);
   9118     const node_datas = tree.nodes.items(.data);
   9119 
   9120     var node = start_node;
   9121     while (true) {
   9122         switch (node_tags[node]) {
   9123             // These don't have the opportunity to call any runtime functions.
   9124             .error_value,
   9125             .identifier,
   9126             .@"comptime",
   9127             => return false,
   9128 
   9129             // Forward the question to the LHS sub-expression.
   9130             .grouped_expression,
   9131             .@"try",
   9132             .@"nosuspend",
   9133             .unwrap_optional,
   9134             => node = node_datas[node].lhs,
   9135 
   9136             // Anything that does not eval to an error is guaranteed to pop any
   9137             // additions to the error trace, so it effectively does not append.
   9138             else => return nodeMayEvalToError(tree, start_node) != .never,
   9139         }
   9140     }
   9141 }
   9142 
   9143 fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.EvalToError {
   9144     const node_tags = tree.nodes.items(.tag);
   9145     const node_datas = tree.nodes.items(.data);
   9146     const main_tokens = tree.nodes.items(.main_token);
   9147     const token_tags = tree.tokens.items(.tag);
   9148 
   9149     var node = start_node;
   9150     while (true) {
   9151         switch (node_tags[node]) {
   9152             .root,
   9153             .@"usingnamespace",
   9154             .test_decl,
   9155             .switch_case,
   9156             .switch_case_inline,
   9157             .switch_case_one,
   9158             .switch_case_inline_one,
   9159             .container_field_init,
   9160             .container_field_align,
   9161             .container_field,
   9162             .asm_output,
   9163             .asm_input,
   9164             => unreachable,
   9165 
   9166             .error_value => return .always,
   9167 
   9168             .@"asm",
   9169             .asm_simple,
   9170             .identifier,
   9171             .field_access,
   9172             .deref,
   9173             .array_access,
   9174             .while_simple,
   9175             .while_cont,
   9176             .for_simple,
   9177             .if_simple,
   9178             .@"while",
   9179             .@"if",
   9180             .@"for",
   9181             .@"switch",
   9182             .switch_comma,
   9183             .call_one,
   9184             .call_one_comma,
   9185             .async_call_one,
   9186             .async_call_one_comma,
   9187             .call,
   9188             .call_comma,
   9189             .async_call,
   9190             .async_call_comma,
   9191             => return .maybe,
   9192 
   9193             .@"return",
   9194             .@"break",
   9195             .@"continue",
   9196             .bit_not,
   9197             .bool_not,
   9198             .global_var_decl,
   9199             .local_var_decl,
   9200             .simple_var_decl,
   9201             .aligned_var_decl,
   9202             .@"defer",
   9203             .@"errdefer",
   9204             .address_of,
   9205             .optional_type,
   9206             .negation,
   9207             .negation_wrap,
   9208             .@"resume",
   9209             .array_type,
   9210             .array_type_sentinel,
   9211             .ptr_type_aligned,
   9212             .ptr_type_sentinel,
   9213             .ptr_type,
   9214             .ptr_type_bit_range,
   9215             .@"suspend",
   9216             .fn_proto_simple,
   9217             .fn_proto_multi,
   9218             .fn_proto_one,
   9219             .fn_proto,
   9220             .fn_decl,
   9221             .anyframe_type,
   9222             .anyframe_literal,
   9223             .number_literal,
   9224             .enum_literal,
   9225             .string_literal,
   9226             .multiline_string_literal,
   9227             .char_literal,
   9228             .unreachable_literal,
   9229             .error_set_decl,
   9230             .container_decl,
   9231             .container_decl_trailing,
   9232             .container_decl_two,
   9233             .container_decl_two_trailing,
   9234             .container_decl_arg,
   9235             .container_decl_arg_trailing,
   9236             .tagged_union,
   9237             .tagged_union_trailing,
   9238             .tagged_union_two,
   9239             .tagged_union_two_trailing,
   9240             .tagged_union_enum_tag,
   9241             .tagged_union_enum_tag_trailing,
   9242             .add,
   9243             .add_wrap,
   9244             .add_sat,
   9245             .array_cat,
   9246             .array_mult,
   9247             .assign,
   9248             .assign_bit_and,
   9249             .assign_bit_or,
   9250             .assign_shl,
   9251             .assign_shl_sat,
   9252             .assign_shr,
   9253             .assign_bit_xor,
   9254             .assign_div,
   9255             .assign_sub,
   9256             .assign_sub_wrap,
   9257             .assign_sub_sat,
   9258             .assign_mod,
   9259             .assign_add,
   9260             .assign_add_wrap,
   9261             .assign_add_sat,
   9262             .assign_mul,
   9263             .assign_mul_wrap,
   9264             .assign_mul_sat,
   9265             .bang_equal,
   9266             .bit_and,
   9267             .bit_or,
   9268             .shl,
   9269             .shl_sat,
   9270             .shr,
   9271             .bit_xor,
   9272             .bool_and,
   9273             .bool_or,
   9274             .div,
   9275             .equal_equal,
   9276             .error_union,
   9277             .greater_or_equal,
   9278             .greater_than,
   9279             .less_or_equal,
   9280             .less_than,
   9281             .merge_error_sets,
   9282             .mod,
   9283             .mul,
   9284             .mul_wrap,
   9285             .mul_sat,
   9286             .switch_range,
   9287             .sub,
   9288             .sub_wrap,
   9289             .sub_sat,
   9290             .slice,
   9291             .slice_open,
   9292             .slice_sentinel,
   9293             .array_init_one,
   9294             .array_init_one_comma,
   9295             .array_init_dot_two,
   9296             .array_init_dot_two_comma,
   9297             .array_init_dot,
   9298             .array_init_dot_comma,
   9299             .array_init,
   9300             .array_init_comma,
   9301             .struct_init_one,
   9302             .struct_init_one_comma,
   9303             .struct_init_dot_two,
   9304             .struct_init_dot_two_comma,
   9305             .struct_init_dot,
   9306             .struct_init_dot_comma,
   9307             .struct_init,
   9308             .struct_init_comma,
   9309             => return .never,
   9310 
   9311             // Forward the question to the LHS sub-expression.
   9312             .grouped_expression,
   9313             .@"try",
   9314             .@"await",
   9315             .@"comptime",
   9316             .@"nosuspend",
   9317             .unwrap_optional,
   9318             => node = node_datas[node].lhs,
   9319 
   9320             // LHS sub-expression may still be an error under the outer optional or error union
   9321             .@"catch",
   9322             .@"orelse",
   9323             => return .maybe,
   9324 
   9325             .block_two,
   9326             .block_two_semicolon,
   9327             .block,
   9328             .block_semicolon,
   9329             => {
   9330                 const lbrace = main_tokens[node];
   9331                 if (token_tags[lbrace - 1] == .colon) {
   9332                     // Labeled blocks may need a memory location to forward
   9333                     // to their break statements.
   9334                     return .maybe;
   9335                 } else {
   9336                     return .never;
   9337                 }
   9338             },
   9339 
   9340             .builtin_call,
   9341             .builtin_call_comma,
   9342             .builtin_call_two,
   9343             .builtin_call_two_comma,
   9344             => {
   9345                 const builtin_token = main_tokens[node];
   9346                 const builtin_name = tree.tokenSlice(builtin_token);
   9347                 // If the builtin is an invalid name, we don't cause an error here; instead
   9348                 // let it pass, and the error will be "invalid builtin function" later.
   9349                 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return .maybe;
   9350                 return builtin_info.eval_to_error;
   9351             },
   9352         }
   9353     }
   9354 }
   9355 
   9356 /// Returns `true` if it is known the type expression has more than one possible value;
   9357 /// `false` otherwise.
   9358 fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.Index) bool {
   9359     const node_tags = tree.nodes.items(.tag);
   9360     const node_datas = tree.nodes.items(.data);
   9361 
   9362     var node = start_node;
   9363     while (true) {
   9364         switch (node_tags[node]) {
   9365             .root,
   9366             .@"usingnamespace",
   9367             .test_decl,
   9368             .switch_case,
   9369             .switch_case_inline,
   9370             .switch_case_one,
   9371             .switch_case_inline_one,
   9372             .container_field_init,
   9373             .container_field_align,
   9374             .container_field,
   9375             .asm_output,
   9376             .asm_input,
   9377             .global_var_decl,
   9378             .local_var_decl,
   9379             .simple_var_decl,
   9380             .aligned_var_decl,
   9381             => unreachable,
   9382 
   9383             .@"return",
   9384             .@"break",
   9385             .@"continue",
   9386             .bit_not,
   9387             .bool_not,
   9388             .@"defer",
   9389             .@"errdefer",
   9390             .address_of,
   9391             .negation,
   9392             .negation_wrap,
   9393             .@"resume",
   9394             .array_type,
   9395             .@"suspend",
   9396             .fn_decl,
   9397             .anyframe_literal,
   9398             .number_literal,
   9399             .enum_literal,
   9400             .string_literal,
   9401             .multiline_string_literal,
   9402             .char_literal,
   9403             .unreachable_literal,
   9404             .error_set_decl,
   9405             .container_decl,
   9406             .container_decl_trailing,
   9407             .container_decl_two,
   9408             .container_decl_two_trailing,
   9409             .container_decl_arg,
   9410             .container_decl_arg_trailing,
   9411             .tagged_union,
   9412             .tagged_union_trailing,
   9413             .tagged_union_two,
   9414             .tagged_union_two_trailing,
   9415             .tagged_union_enum_tag,
   9416             .tagged_union_enum_tag_trailing,
   9417             .@"asm",
   9418             .asm_simple,
   9419             .add,
   9420             .add_wrap,
   9421             .add_sat,
   9422             .array_cat,
   9423             .array_mult,
   9424             .assign,
   9425             .assign_bit_and,
   9426             .assign_bit_or,
   9427             .assign_shl,
   9428             .assign_shl_sat,
   9429             .assign_shr,
   9430             .assign_bit_xor,
   9431             .assign_div,
   9432             .assign_sub,
   9433             .assign_sub_wrap,
   9434             .assign_sub_sat,
   9435             .assign_mod,
   9436             .assign_add,
   9437             .assign_add_wrap,
   9438             .assign_add_sat,
   9439             .assign_mul,
   9440             .assign_mul_wrap,
   9441             .assign_mul_sat,
   9442             .bang_equal,
   9443             .bit_and,
   9444             .bit_or,
   9445             .shl,
   9446             .shl_sat,
   9447             .shr,
   9448             .bit_xor,
   9449             .bool_and,
   9450             .bool_or,
   9451             .div,
   9452             .equal_equal,
   9453             .error_union,
   9454             .greater_or_equal,
   9455             .greater_than,
   9456             .less_or_equal,
   9457             .less_than,
   9458             .merge_error_sets,
   9459             .mod,
   9460             .mul,
   9461             .mul_wrap,
   9462             .mul_sat,
   9463             .switch_range,
   9464             .field_access,
   9465             .sub,
   9466             .sub_wrap,
   9467             .sub_sat,
   9468             .slice,
   9469             .slice_open,
   9470             .slice_sentinel,
   9471             .deref,
   9472             .array_access,
   9473             .error_value,
   9474             .while_simple,
   9475             .while_cont,
   9476             .for_simple,
   9477             .if_simple,
   9478             .@"catch",
   9479             .@"orelse",
   9480             .array_init_one,
   9481             .array_init_one_comma,
   9482             .array_init_dot_two,
   9483             .array_init_dot_two_comma,
   9484             .array_init_dot,
   9485             .array_init_dot_comma,
   9486             .array_init,
   9487             .array_init_comma,
   9488             .struct_init_one,
   9489             .struct_init_one_comma,
   9490             .struct_init_dot_two,
   9491             .struct_init_dot_two_comma,
   9492             .struct_init_dot,
   9493             .struct_init_dot_comma,
   9494             .struct_init,
   9495             .struct_init_comma,
   9496             .@"while",
   9497             .@"if",
   9498             .@"for",
   9499             .@"switch",
   9500             .switch_comma,
   9501             .call_one,
   9502             .call_one_comma,
   9503             .async_call_one,
   9504             .async_call_one_comma,
   9505             .call,
   9506             .call_comma,
   9507             .async_call,
   9508             .async_call_comma,
   9509             .block_two,
   9510             .block_two_semicolon,
   9511             .block,
   9512             .block_semicolon,
   9513             .builtin_call,
   9514             .builtin_call_comma,
   9515             .builtin_call_two,
   9516             .builtin_call_two_comma,
   9517             // these are function bodies, not pointers
   9518             .fn_proto_simple,
   9519             .fn_proto_multi,
   9520             .fn_proto_one,
   9521             .fn_proto,
   9522             => return false,
   9523 
   9524             // Forward the question to the LHS sub-expression.
   9525             .grouped_expression,
   9526             .@"try",
   9527             .@"await",
   9528             .@"comptime",
   9529             .@"nosuspend",
   9530             .unwrap_optional,
   9531             => node = node_datas[node].lhs,
   9532 
   9533             .ptr_type_aligned,
   9534             .ptr_type_sentinel,
   9535             .ptr_type,
   9536             .ptr_type_bit_range,
   9537             .optional_type,
   9538             .anyframe_type,
   9539             .array_type_sentinel,
   9540             => return true,
   9541 
   9542             .identifier => {
   9543                 const main_tokens = tree.nodes.items(.main_token);
   9544                 const ident_bytes = tree.tokenSlice(main_tokens[node]);
   9545                 if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) {
   9546                     .anyerror_type,
   9547                     .anyframe_type,
   9548                     .anyopaque_type,
   9549                     .bool_type,
   9550                     .c_int_type,
   9551                     .c_long_type,
   9552                     .c_longdouble_type,
   9553                     .c_longlong_type,
   9554                     .c_short_type,
   9555                     .c_uint_type,
   9556                     .c_ulong_type,
   9557                     .c_ulonglong_type,
   9558                     .c_ushort_type,
   9559                     .comptime_float_type,
   9560                     .comptime_int_type,
   9561                     .f16_type,
   9562                     .f32_type,
   9563                     .f64_type,
   9564                     .f80_type,
   9565                     .f128_type,
   9566                     .i16_type,
   9567                     .i32_type,
   9568                     .i64_type,
   9569                     .i128_type,
   9570                     .i8_type,
   9571                     .isize_type,
   9572                     .type_type,
   9573                     .u16_type,
   9574                     .u29_type,
   9575                     .u32_type,
   9576                     .u64_type,
   9577                     .u128_type,
   9578                     .u1_type,
   9579                     .u8_type,
   9580                     .usize_type,
   9581                     => return true,
   9582 
   9583                     .void_type,
   9584                     .bool_false,
   9585                     .bool_true,
   9586                     .null_value,
   9587                     .undef,
   9588                     .noreturn_type,
   9589                     => return false,
   9590 
   9591                     else => unreachable, // that's all the values from `primitives`.
   9592                 } else {
   9593                     return false;
   9594                 }
   9595             },
   9596         }
   9597     }
   9598 }
   9599 
   9600 /// Returns `true` if it is known the expression is a type that cannot be used at runtime;
   9601 /// `false` otherwise.
   9602 fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
   9603     const node_tags = tree.nodes.items(.tag);
   9604     const node_datas = tree.nodes.items(.data);
   9605 
   9606     var node = start_node;
   9607     while (true) {
   9608         switch (node_tags[node]) {
   9609             .root,
   9610             .@"usingnamespace",
   9611             .test_decl,
   9612             .switch_case,
   9613             .switch_case_inline,
   9614             .switch_case_one,
   9615             .switch_case_inline_one,
   9616             .container_field_init,
   9617             .container_field_align,
   9618             .container_field,
   9619             .asm_output,
   9620             .asm_input,
   9621             .global_var_decl,
   9622             .local_var_decl,
   9623             .simple_var_decl,
   9624             .aligned_var_decl,
   9625             => unreachable,
   9626 
   9627             .@"return",
   9628             .@"break",
   9629             .@"continue",
   9630             .bit_not,
   9631             .bool_not,
   9632             .@"defer",
   9633             .@"errdefer",
   9634             .address_of,
   9635             .negation,
   9636             .negation_wrap,
   9637             .@"resume",
   9638             .array_type,
   9639             .@"suspend",
   9640             .fn_decl,
   9641             .anyframe_literal,
   9642             .number_literal,
   9643             .enum_literal,
   9644             .string_literal,
   9645             .multiline_string_literal,
   9646             .char_literal,
   9647             .unreachable_literal,
   9648             .error_set_decl,
   9649             .container_decl,
   9650             .container_decl_trailing,
   9651             .container_decl_two,
   9652             .container_decl_two_trailing,
   9653             .container_decl_arg,
   9654             .container_decl_arg_trailing,
   9655             .tagged_union,
   9656             .tagged_union_trailing,
   9657             .tagged_union_two,
   9658             .tagged_union_two_trailing,
   9659             .tagged_union_enum_tag,
   9660             .tagged_union_enum_tag_trailing,
   9661             .@"asm",
   9662             .asm_simple,
   9663             .add,
   9664             .add_wrap,
   9665             .add_sat,
   9666             .array_cat,
   9667             .array_mult,
   9668             .assign,
   9669             .assign_bit_and,
   9670             .assign_bit_or,
   9671             .assign_shl,
   9672             .assign_shl_sat,
   9673             .assign_shr,
   9674             .assign_bit_xor,
   9675             .assign_div,
   9676             .assign_sub,
   9677             .assign_sub_wrap,
   9678             .assign_sub_sat,
   9679             .assign_mod,
   9680             .assign_add,
   9681             .assign_add_wrap,
   9682             .assign_add_sat,
   9683             .assign_mul,
   9684             .assign_mul_wrap,
   9685             .assign_mul_sat,
   9686             .bang_equal,
   9687             .bit_and,
   9688             .bit_or,
   9689             .shl,
   9690             .shl_sat,
   9691             .shr,
   9692             .bit_xor,
   9693             .bool_and,
   9694             .bool_or,
   9695             .div,
   9696             .equal_equal,
   9697             .error_union,
   9698             .greater_or_equal,
   9699             .greater_than,
   9700             .less_or_equal,
   9701             .less_than,
   9702             .merge_error_sets,
   9703             .mod,
   9704             .mul,
   9705             .mul_wrap,
   9706             .mul_sat,
   9707             .switch_range,
   9708             .field_access,
   9709             .sub,
   9710             .sub_wrap,
   9711             .sub_sat,
   9712             .slice,
   9713             .slice_open,
   9714             .slice_sentinel,
   9715             .deref,
   9716             .array_access,
   9717             .error_value,
   9718             .while_simple,
   9719             .while_cont,
   9720             .for_simple,
   9721             .if_simple,
   9722             .@"catch",
   9723             .@"orelse",
   9724             .array_init_one,
   9725             .array_init_one_comma,
   9726             .array_init_dot_two,
   9727             .array_init_dot_two_comma,
   9728             .array_init_dot,
   9729             .array_init_dot_comma,
   9730             .array_init,
   9731             .array_init_comma,
   9732             .struct_init_one,
   9733             .struct_init_one_comma,
   9734             .struct_init_dot_two,
   9735             .struct_init_dot_two_comma,
   9736             .struct_init_dot,
   9737             .struct_init_dot_comma,
   9738             .struct_init,
   9739             .struct_init_comma,
   9740             .@"while",
   9741             .@"if",
   9742             .@"for",
   9743             .@"switch",
   9744             .switch_comma,
   9745             .call_one,
   9746             .call_one_comma,
   9747             .async_call_one,
   9748             .async_call_one_comma,
   9749             .call,
   9750             .call_comma,
   9751             .async_call,
   9752             .async_call_comma,
   9753             .block_two,
   9754             .block_two_semicolon,
   9755             .block,
   9756             .block_semicolon,
   9757             .builtin_call,
   9758             .builtin_call_comma,
   9759             .builtin_call_two,
   9760             .builtin_call_two_comma,
   9761             .ptr_type_aligned,
   9762             .ptr_type_sentinel,
   9763             .ptr_type,
   9764             .ptr_type_bit_range,
   9765             .optional_type,
   9766             .anyframe_type,
   9767             .array_type_sentinel,
   9768             => return false,
   9769 
   9770             // these are function bodies, not pointers
   9771             .fn_proto_simple,
   9772             .fn_proto_multi,
   9773             .fn_proto_one,
   9774             .fn_proto,
   9775             => return true,
   9776 
   9777             // Forward the question to the LHS sub-expression.
   9778             .grouped_expression,
   9779             .@"try",
   9780             .@"await",
   9781             .@"comptime",
   9782             .@"nosuspend",
   9783             .unwrap_optional,
   9784             => node = node_datas[node].lhs,
   9785 
   9786             .identifier => {
   9787                 const main_tokens = tree.nodes.items(.main_token);
   9788                 const ident_bytes = tree.tokenSlice(main_tokens[node]);
   9789                 if (primitive_instrs.get(ident_bytes)) |primitive| switch (primitive) {
   9790                     .anyerror_type,
   9791                     .anyframe_type,
   9792                     .anyopaque_type,
   9793                     .bool_type,
   9794                     .c_int_type,
   9795                     .c_long_type,
   9796                     .c_longdouble_type,
   9797                     .c_longlong_type,
   9798                     .c_short_type,
   9799                     .c_uint_type,
   9800                     .c_ulong_type,
   9801                     .c_ulonglong_type,
   9802                     .c_ushort_type,
   9803                     .f16_type,
   9804                     .f32_type,
   9805                     .f64_type,
   9806                     .f80_type,
   9807                     .f128_type,
   9808                     .i16_type,
   9809                     .i32_type,
   9810                     .i64_type,
   9811                     .i128_type,
   9812                     .i8_type,
   9813                     .isize_type,
   9814                     .u16_type,
   9815                     .u29_type,
   9816                     .u32_type,
   9817                     .u64_type,
   9818                     .u128_type,
   9819                     .u1_type,
   9820                     .u8_type,
   9821                     .usize_type,
   9822                     .void_type,
   9823                     .bool_false,
   9824                     .bool_true,
   9825                     .null_value,
   9826                     .undef,
   9827                     .noreturn_type,
   9828                     => return false,
   9829 
   9830                     .comptime_float_type,
   9831                     .comptime_int_type,
   9832                     .type_type,
   9833                     => return true,
   9834 
   9835                     else => unreachable, // that's all the values from `primitives`.
   9836                 } else {
   9837                     return false;
   9838                 }
   9839             },
   9840         }
   9841     }
   9842 }
   9843 
   9844 /// Returns `true` if the node uses `gz.anon_name_strategy`.
   9845 fn nodeUsesAnonNameStrategy(tree: *const Ast, node: Ast.Node.Index) bool {
   9846     const node_tags = tree.nodes.items(.tag);
   9847     switch (node_tags[node]) {
   9848         .container_decl,
   9849         .container_decl_trailing,
   9850         .container_decl_two,
   9851         .container_decl_two_trailing,
   9852         .container_decl_arg,
   9853         .container_decl_arg_trailing,
   9854         .tagged_union,
   9855         .tagged_union_trailing,
   9856         .tagged_union_two,
   9857         .tagged_union_two_trailing,
   9858         .tagged_union_enum_tag,
   9859         .tagged_union_enum_tag_trailing,
   9860         => return true,
   9861         .builtin_call_two, .builtin_call_two_comma, .builtin_call, .builtin_call_comma => {
   9862             const builtin_token = tree.nodes.items(.main_token)[node];
   9863             const builtin_name = tree.tokenSlice(builtin_token);
   9864             return std.mem.eql(u8, builtin_name, "@Type");
   9865         },
   9866         else => return false,
   9867     }
   9868 }
   9869 
   9870 /// Applies `rl` semantics to `result`. Expressions which do not do their own handling of
   9871 /// result locations must call this function on their result.
   9872 /// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer.
   9873 /// If the `ResultLoc` is `ty`, it will coerce the result to the type.
   9874 /// Assumes nothing stacked on `gz`.
   9875 fn rvalue(
   9876     gz: *GenZir,
   9877     ri: ResultInfo,
   9878     raw_result: Zir.Inst.Ref,
   9879     src_node: Ast.Node.Index,
   9880 ) InnerError!Zir.Inst.Ref {
   9881     const result = r: {
   9882         if (refToIndex(raw_result)) |result_index| {
   9883             const zir_tags = gz.astgen.instructions.items(.tag);
   9884             const data = gz.astgen.instructions.items(.data)[result_index];
   9885             if (zir_tags[result_index].isAlwaysVoid(data)) {
   9886                 break :r Zir.Inst.Ref.void_value;
   9887             }
   9888         }
   9889         break :r raw_result;
   9890     };
   9891     if (gz.endsWithNoReturn()) return result;
   9892     switch (ri.rl) {
   9893         .none, .coerced_ty => return result,
   9894         .discard => {
   9895             // Emit a compile error for discarding error values.
   9896             _ = try gz.addUnNode(.ensure_result_non_error, result, src_node);
   9897             return result;
   9898         },
   9899         .ref => {
   9900             // We need a pointer but we have a value.
   9901             // Unfortunately it's not quite as simple as directly emitting a ref
   9902             // instruction here because we need subsequent address-of operator on
   9903             // const locals to return the same address.
   9904             const astgen = gz.astgen;
   9905             const tree = astgen.tree;
   9906             const src_token = tree.firstToken(src_node);
   9907             const result_index = refToIndex(result) orelse
   9908                 return gz.addUnTok(.ref, result, src_token);
   9909             const zir_tags = gz.astgen.instructions.items(.tag);
   9910             if (zir_tags[result_index].isParam() or astgen.isInferred(result))
   9911                 return gz.addUnTok(.ref, result, src_token);
   9912             const gop = try astgen.ref_table.getOrPut(astgen.gpa, result_index);
   9913             if (!gop.found_existing) {
   9914                 gop.value_ptr.* = try gz.makeUnTok(.ref, result, src_token);
   9915             }
   9916             return indexToRef(gop.value_ptr.*);
   9917         },
   9918         .ty => |ty_inst| {
   9919             // Quickly eliminate some common, unnecessary type coercion.
   9920             const as_ty = @as(u64, @enumToInt(Zir.Inst.Ref.type_type)) << 32;
   9921             const as_comptime_int = @as(u64, @enumToInt(Zir.Inst.Ref.comptime_int_type)) << 32;
   9922             const as_bool = @as(u64, @enumToInt(Zir.Inst.Ref.bool_type)) << 32;
   9923             const as_usize = @as(u64, @enumToInt(Zir.Inst.Ref.usize_type)) << 32;
   9924             const as_void = @as(u64, @enumToInt(Zir.Inst.Ref.void_type)) << 32;
   9925             switch ((@as(u64, @enumToInt(ty_inst)) << 32) | @as(u64, @enumToInt(result))) {
   9926                 as_ty | @enumToInt(Zir.Inst.Ref.u1_type),
   9927                 as_ty | @enumToInt(Zir.Inst.Ref.u8_type),
   9928                 as_ty | @enumToInt(Zir.Inst.Ref.i8_type),
   9929                 as_ty | @enumToInt(Zir.Inst.Ref.u16_type),
   9930                 as_ty | @enumToInt(Zir.Inst.Ref.u29_type),
   9931                 as_ty | @enumToInt(Zir.Inst.Ref.i16_type),
   9932                 as_ty | @enumToInt(Zir.Inst.Ref.u32_type),
   9933                 as_ty | @enumToInt(Zir.Inst.Ref.i32_type),
   9934                 as_ty | @enumToInt(Zir.Inst.Ref.u64_type),
   9935                 as_ty | @enumToInt(Zir.Inst.Ref.i64_type),
   9936                 as_ty | @enumToInt(Zir.Inst.Ref.usize_type),
   9937                 as_ty | @enumToInt(Zir.Inst.Ref.isize_type),
   9938                 as_ty | @enumToInt(Zir.Inst.Ref.c_short_type),
   9939                 as_ty | @enumToInt(Zir.Inst.Ref.c_ushort_type),
   9940                 as_ty | @enumToInt(Zir.Inst.Ref.c_int_type),
   9941                 as_ty | @enumToInt(Zir.Inst.Ref.c_uint_type),
   9942                 as_ty | @enumToInt(Zir.Inst.Ref.c_long_type),
   9943                 as_ty | @enumToInt(Zir.Inst.Ref.c_ulong_type),
   9944                 as_ty | @enumToInt(Zir.Inst.Ref.c_longlong_type),
   9945                 as_ty | @enumToInt(Zir.Inst.Ref.c_ulonglong_type),
   9946                 as_ty | @enumToInt(Zir.Inst.Ref.c_longdouble_type),
   9947                 as_ty | @enumToInt(Zir.Inst.Ref.f16_type),
   9948                 as_ty | @enumToInt(Zir.Inst.Ref.f32_type),
   9949                 as_ty | @enumToInt(Zir.Inst.Ref.f64_type),
   9950                 as_ty | @enumToInt(Zir.Inst.Ref.f80_type),
   9951                 as_ty | @enumToInt(Zir.Inst.Ref.f128_type),
   9952                 as_ty | @enumToInt(Zir.Inst.Ref.anyopaque_type),
   9953                 as_ty | @enumToInt(Zir.Inst.Ref.bool_type),
   9954                 as_ty | @enumToInt(Zir.Inst.Ref.void_type),
   9955                 as_ty | @enumToInt(Zir.Inst.Ref.type_type),
   9956                 as_ty | @enumToInt(Zir.Inst.Ref.anyerror_type),
   9957                 as_ty | @enumToInt(Zir.Inst.Ref.comptime_int_type),
   9958                 as_ty | @enumToInt(Zir.Inst.Ref.comptime_float_type),
   9959                 as_ty | @enumToInt(Zir.Inst.Ref.noreturn_type),
   9960                 as_ty | @enumToInt(Zir.Inst.Ref.null_type),
   9961                 as_ty | @enumToInt(Zir.Inst.Ref.undefined_type),
   9962                 as_ty | @enumToInt(Zir.Inst.Ref.fn_noreturn_no_args_type),
   9963                 as_ty | @enumToInt(Zir.Inst.Ref.fn_void_no_args_type),
   9964                 as_ty | @enumToInt(Zir.Inst.Ref.fn_naked_noreturn_no_args_type),
   9965                 as_ty | @enumToInt(Zir.Inst.Ref.fn_ccc_void_no_args_type),
   9966                 as_ty | @enumToInt(Zir.Inst.Ref.single_const_pointer_to_comptime_int_type),
   9967                 as_ty | @enumToInt(Zir.Inst.Ref.const_slice_u8_type),
   9968                 as_ty | @enumToInt(Zir.Inst.Ref.enum_literal_type),
   9969                 as_comptime_int | @enumToInt(Zir.Inst.Ref.zero),
   9970                 as_comptime_int | @enumToInt(Zir.Inst.Ref.one),
   9971                 as_bool | @enumToInt(Zir.Inst.Ref.bool_true),
   9972                 as_bool | @enumToInt(Zir.Inst.Ref.bool_false),
   9973                 as_usize | @enumToInt(Zir.Inst.Ref.zero_usize),
   9974                 as_usize | @enumToInt(Zir.Inst.Ref.one_usize),
   9975                 as_void | @enumToInt(Zir.Inst.Ref.void_value),
   9976                 => return result, // type of result is already correct
   9977 
   9978                 // Need an explicit type coercion instruction.
   9979                 else => return gz.addPlNode(ri.zirTag(), src_node, Zir.Inst.As{
   9980                     .dest_type = ty_inst,
   9981                     .operand = result,
   9982                 }),
   9983             }
   9984         },
   9985         .ptr => |ptr_res| {
   9986             _ = try gz.addPlNode(.store_node, ptr_res.src_node orelse src_node, Zir.Inst.Bin{
   9987                 .lhs = ptr_res.inst,
   9988                 .rhs = result,
   9989             });
   9990             return result;
   9991         },
   9992         .inferred_ptr => |alloc| {
   9993             _ = try gz.addBin(.store_to_inferred_ptr, alloc, result);
   9994             return result;
   9995         },
   9996         .block_ptr => |block_scope| {
   9997             block_scope.rvalue_rl_count += 1;
   9998             _ = try gz.addBin(.store_to_block_ptr, block_scope.rl_ptr, result);
   9999             return result;
  10000         },
  10001     }
  10002 }
  10003 
  10004 /// Given an identifier token, obtain the string for it.
  10005 /// If the token uses @"" syntax, parses as a string, reports errors if applicable,
  10006 /// and allocates the result within `astgen.arena`.
  10007 /// Otherwise, returns a reference to the source code bytes directly.
  10008 /// See also `appendIdentStr` and `parseStrLit`.
  10009 fn identifierTokenString(astgen: *AstGen, token: Ast.TokenIndex) InnerError![]const u8 {
  10010     const tree = astgen.tree;
  10011     const token_tags = tree.tokens.items(.tag);
  10012     assert(token_tags[token] == .identifier);
  10013     const ident_name = tree.tokenSlice(token);
  10014     if (!mem.startsWith(u8, ident_name, "@")) {
  10015         return ident_name;
  10016     }
  10017     var buf: ArrayListUnmanaged(u8) = .{};
  10018     defer buf.deinit(astgen.gpa);
  10019     try astgen.parseStrLit(token, &buf, ident_name, 1);
  10020     if (mem.indexOfScalar(u8, buf.items, 0) != null) {
  10021         return astgen.failTok(token, "identifier cannot contain null bytes", .{});
  10022     } else if (buf.items.len == 0) {
  10023         return astgen.failTok(token, "identifier cannot be empty", .{});
  10024     }
  10025     const duped = try astgen.arena.dupe(u8, buf.items);
  10026     return duped;
  10027 }
  10028 
  10029 /// Given an identifier token, obtain the string for it (possibly parsing as a string
  10030 /// literal if it is @"" syntax), and append the string to `buf`.
  10031 /// See also `identifierTokenString` and `parseStrLit`.
  10032 fn appendIdentStr(
  10033     astgen: *AstGen,
  10034     token: Ast.TokenIndex,
  10035     buf: *ArrayListUnmanaged(u8),
  10036 ) InnerError!void {
  10037     const tree = astgen.tree;
  10038     const token_tags = tree.tokens.items(.tag);
  10039     assert(token_tags[token] == .identifier);
  10040     const ident_name = tree.tokenSlice(token);
  10041     if (!mem.startsWith(u8, ident_name, "@")) {
  10042         return buf.appendSlice(astgen.gpa, ident_name);
  10043     } else {
  10044         const start = buf.items.len;
  10045         try astgen.parseStrLit(token, buf, ident_name, 1);
  10046         const slice = buf.items[start..];
  10047         if (mem.indexOfScalar(u8, slice, 0) != null) {
  10048             return astgen.failTok(token, "identifier cannot contain null bytes", .{});
  10049         } else if (slice.len == 0) {
  10050             return astgen.failTok(token, "identifier cannot be empty", .{});
  10051         }
  10052     }
  10053 }
  10054 
  10055 /// Appends the result to `buf`.
  10056 fn parseStrLit(
  10057     astgen: *AstGen,
  10058     token: Ast.TokenIndex,
  10059     buf: *ArrayListUnmanaged(u8),
  10060     bytes: []const u8,
  10061     offset: u32,
  10062 ) InnerError!void {
  10063     const raw_string = bytes[offset..];
  10064     var buf_managed = buf.toManaged(astgen.gpa);
  10065     const result = std.zig.string_literal.parseWrite(buf_managed.writer(), raw_string);
  10066     buf.* = buf_managed.moveToUnmanaged();
  10067     switch (try result) {
  10068         .success => return,
  10069         .failure => |err| return astgen.failWithStrLitError(err, token, bytes, offset),
  10070     }
  10071 }
  10072 
  10073 fn failWithStrLitError(astgen: *AstGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, bytes: []const u8, offset: u32) InnerError {
  10074     const raw_string = bytes[offset..];
  10075     switch (err) {
  10076         .invalid_escape_character => |bad_index| {
  10077             return astgen.failOff(
  10078                 token,
  10079                 offset + @intCast(u32, bad_index),
  10080                 "invalid escape character: '{c}'",
  10081                 .{raw_string[bad_index]},
  10082             );
  10083         },
  10084         .expected_hex_digit => |bad_index| {
  10085             return astgen.failOff(
  10086                 token,
  10087                 offset + @intCast(u32, bad_index),
  10088                 "expected hex digit, found '{c}'",
  10089                 .{raw_string[bad_index]},
  10090             );
  10091         },
  10092         .empty_unicode_escape_sequence => |bad_index| {
  10093             return astgen.failOff(
  10094                 token,
  10095                 offset + @intCast(u32, bad_index),
  10096                 "empty unicode escape sequence",
  10097                 .{},
  10098             );
  10099         },
  10100         .expected_hex_digit_or_rbrace => |bad_index| {
  10101             return astgen.failOff(
  10102                 token,
  10103                 offset + @intCast(u32, bad_index),
  10104                 "expected hex digit or '}}', found '{c}'",
  10105                 .{raw_string[bad_index]},
  10106             );
  10107         },
  10108         .invalid_unicode_codepoint => |bad_index| {
  10109             return astgen.failOff(
  10110                 token,
  10111                 offset + @intCast(u32, bad_index),
  10112                 "unicode escape does not correspond to a valid codepoint",
  10113                 .{},
  10114             );
  10115         },
  10116         .expected_lbrace => |bad_index| {
  10117             return astgen.failOff(
  10118                 token,
  10119                 offset + @intCast(u32, bad_index),
  10120                 "expected '{{', found '{c}",
  10121                 .{raw_string[bad_index]},
  10122             );
  10123         },
  10124         .expected_rbrace => |bad_index| {
  10125             return astgen.failOff(
  10126                 token,
  10127                 offset + @intCast(u32, bad_index),
  10128                 "expected '}}', found '{c}",
  10129                 .{raw_string[bad_index]},
  10130             );
  10131         },
  10132         .expected_single_quote => |bad_index| {
  10133             return astgen.failOff(
  10134                 token,
  10135                 offset + @intCast(u32, bad_index),
  10136                 "expected single quote ('), found '{c}",
  10137                 .{raw_string[bad_index]},
  10138             );
  10139         },
  10140         .invalid_character => |bad_index| {
  10141             return astgen.failOff(
  10142                 token,
  10143                 offset + @intCast(u32, bad_index),
  10144                 "invalid byte in string or character literal: '{c}'",
  10145                 .{raw_string[bad_index]},
  10146             );
  10147         },
  10148     }
  10149 }
  10150 
  10151 fn failNode(
  10152     astgen: *AstGen,
  10153     node: Ast.Node.Index,
  10154     comptime format: []const u8,
  10155     args: anytype,
  10156 ) InnerError {
  10157     return astgen.failNodeNotes(node, format, args, &[0]u32{});
  10158 }
  10159 
  10160 fn appendErrorNode(
  10161     astgen: *AstGen,
  10162     node: Ast.Node.Index,
  10163     comptime format: []const u8,
  10164     args: anytype,
  10165 ) Allocator.Error!void {
  10166     try astgen.appendErrorNodeNotes(node, format, args, &[0]u32{});
  10167 }
  10168 
  10169 fn appendErrorNodeNotes(
  10170     astgen: *AstGen,
  10171     node: Ast.Node.Index,
  10172     comptime format: []const u8,
  10173     args: anytype,
  10174     notes: []const u32,
  10175 ) Allocator.Error!void {
  10176     @setCold(true);
  10177     const string_bytes = &astgen.string_bytes;
  10178     const msg = @intCast(u32, string_bytes.items.len);
  10179     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
  10180     const notes_index: u32 = if (notes.len != 0) blk: {
  10181         const notes_start = astgen.extra.items.len;
  10182         try astgen.extra.ensureTotalCapacity(astgen.gpa, notes_start + 1 + notes.len);
  10183         astgen.extra.appendAssumeCapacity(@intCast(u32, notes.len));
  10184         astgen.extra.appendSliceAssumeCapacity(notes);
  10185         break :blk @intCast(u32, notes_start);
  10186     } else 0;
  10187     try astgen.compile_errors.append(astgen.gpa, .{
  10188         .msg = msg,
  10189         .node = node,
  10190         .token = 0,
  10191         .byte_offset = 0,
  10192         .notes = notes_index,
  10193     });
  10194 }
  10195 
  10196 fn failNodeNotes(
  10197     astgen: *AstGen,
  10198     node: Ast.Node.Index,
  10199     comptime format: []const u8,
  10200     args: anytype,
  10201     notes: []const u32,
  10202 ) InnerError {
  10203     try appendErrorNodeNotes(astgen, node, format, args, notes);
  10204     return error.AnalysisFail;
  10205 }
  10206 
  10207 fn failTok(
  10208     astgen: *AstGen,
  10209     token: Ast.TokenIndex,
  10210     comptime format: []const u8,
  10211     args: anytype,
  10212 ) InnerError {
  10213     return astgen.failTokNotes(token, format, args, &[0]u32{});
  10214 }
  10215 
  10216 fn appendErrorTok(
  10217     astgen: *AstGen,
  10218     token: Ast.TokenIndex,
  10219     comptime format: []const u8,
  10220     args: anytype,
  10221 ) !void {
  10222     try astgen.appendErrorTokNotes(token, format, args, &[0]u32{});
  10223 }
  10224 
  10225 fn failTokNotes(
  10226     astgen: *AstGen,
  10227     token: Ast.TokenIndex,
  10228     comptime format: []const u8,
  10229     args: anytype,
  10230     notes: []const u32,
  10231 ) InnerError {
  10232     try appendErrorTokNotes(astgen, token, format, args, notes);
  10233     return error.AnalysisFail;
  10234 }
  10235 
  10236 fn appendErrorTokNotes(
  10237     astgen: *AstGen,
  10238     token: Ast.TokenIndex,
  10239     comptime format: []const u8,
  10240     args: anytype,
  10241     notes: []const u32,
  10242 ) !void {
  10243     @setCold(true);
  10244     const string_bytes = &astgen.string_bytes;
  10245     const msg = @intCast(u32, string_bytes.items.len);
  10246     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
  10247     const notes_index: u32 = if (notes.len != 0) blk: {
  10248         const notes_start = astgen.extra.items.len;
  10249         try astgen.extra.ensureTotalCapacity(astgen.gpa, notes_start + 1 + notes.len);
  10250         astgen.extra.appendAssumeCapacity(@intCast(u32, notes.len));
  10251         astgen.extra.appendSliceAssumeCapacity(notes);
  10252         break :blk @intCast(u32, notes_start);
  10253     } else 0;
  10254     try astgen.compile_errors.append(astgen.gpa, .{
  10255         .msg = msg,
  10256         .node = 0,
  10257         .token = token,
  10258         .byte_offset = 0,
  10259         .notes = notes_index,
  10260     });
  10261 }
  10262 
  10263 /// Same as `fail`, except given an absolute byte offset.
  10264 fn failOff(
  10265     astgen: *AstGen,
  10266     token: Ast.TokenIndex,
  10267     byte_offset: u32,
  10268     comptime format: []const u8,
  10269     args: anytype,
  10270 ) InnerError {
  10271     try appendErrorOff(astgen, token, byte_offset, format, args);
  10272     return error.AnalysisFail;
  10273 }
  10274 
  10275 fn appendErrorOff(
  10276     astgen: *AstGen,
  10277     token: Ast.TokenIndex,
  10278     byte_offset: u32,
  10279     comptime format: []const u8,
  10280     args: anytype,
  10281 ) Allocator.Error!void {
  10282     @setCold(true);
  10283     const string_bytes = &astgen.string_bytes;
  10284     const msg = @intCast(u32, string_bytes.items.len);
  10285     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
  10286     try astgen.compile_errors.append(astgen.gpa, .{
  10287         .msg = msg,
  10288         .node = 0,
  10289         .token = token,
  10290         .byte_offset = byte_offset,
  10291         .notes = 0,
  10292     });
  10293 }
  10294 
  10295 fn errNoteTok(
  10296     astgen: *AstGen,
  10297     token: Ast.TokenIndex,
  10298     comptime format: []const u8,
  10299     args: anytype,
  10300 ) Allocator.Error!u32 {
  10301     @setCold(true);
  10302     const string_bytes = &astgen.string_bytes;
  10303     const msg = @intCast(u32, string_bytes.items.len);
  10304     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
  10305     return astgen.addExtra(Zir.Inst.CompileErrors.Item{
  10306         .msg = msg,
  10307         .node = 0,
  10308         .token = token,
  10309         .byte_offset = 0,
  10310         .notes = 0,
  10311     });
  10312 }
  10313 
  10314 fn errNoteNode(
  10315     astgen: *AstGen,
  10316     node: Ast.Node.Index,
  10317     comptime format: []const u8,
  10318     args: anytype,
  10319 ) Allocator.Error!u32 {
  10320     @setCold(true);
  10321     const string_bytes = &astgen.string_bytes;
  10322     const msg = @intCast(u32, string_bytes.items.len);
  10323     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
  10324     return astgen.addExtra(Zir.Inst.CompileErrors.Item{
  10325         .msg = msg,
  10326         .node = node,
  10327         .token = 0,
  10328         .byte_offset = 0,
  10329         .notes = 0,
  10330     });
  10331 }
  10332 
  10333 fn identAsString(astgen: *AstGen, ident_token: Ast.TokenIndex) !u32 {
  10334     const gpa = astgen.gpa;
  10335     const string_bytes = &astgen.string_bytes;
  10336     const str_index = @intCast(u32, string_bytes.items.len);
  10337     try astgen.appendIdentStr(ident_token, string_bytes);
  10338     const key = string_bytes.items[str_index..];
  10339     const gop = try astgen.string_table.getOrPutContextAdapted(gpa, @as([]const u8, key), StringIndexAdapter{
  10340         .bytes = string_bytes,
  10341     }, StringIndexContext{
  10342         .bytes = string_bytes,
  10343     });
  10344     if (gop.found_existing) {
  10345         string_bytes.shrinkRetainingCapacity(str_index);
  10346         return gop.key_ptr.*;
  10347     } else {
  10348         gop.key_ptr.* = str_index;
  10349         try string_bytes.append(gpa, 0);
  10350         return str_index;
  10351     }
  10352 }
  10353 
  10354 /// Adds a doc comment block to `string_bytes` by walking backwards from `end_token`.
  10355 /// `end_token` must point at the first token after the last doc coment line.
  10356 /// Returns 0 if no doc comment is present.
  10357 fn docCommentAsString(astgen: *AstGen, end_token: Ast.TokenIndex) !u32 {
  10358     if (end_token == 0) return @as(u32, 0);
  10359 
  10360     const token_tags = astgen.tree.tokens.items(.tag);
  10361 
  10362     var tok = end_token - 1;
  10363     while (token_tags[tok] == .doc_comment) {
  10364         if (tok == 0) break;
  10365         tok -= 1;
  10366     } else {
  10367         tok += 1;
  10368     }
  10369     return docCommentAsStringFromFirst(astgen, end_token, tok);
  10370 }
  10371 
  10372 /// end_token must be > the index of the last doc comment.
  10373 fn docCommentAsStringFromFirst(
  10374     astgen: *AstGen,
  10375     end_token: Ast.TokenIndex,
  10376     start_token: Ast.TokenIndex,
  10377 ) !u32 {
  10378     if (start_token == end_token) return 0;
  10379 
  10380     const gpa = astgen.gpa;
  10381     const string_bytes = &astgen.string_bytes;
  10382     const str_index = @intCast(u32, string_bytes.items.len);
  10383     const token_starts = astgen.tree.tokens.items(.start);
  10384     const token_tags = astgen.tree.tokens.items(.tag);
  10385 
  10386     const total_bytes = token_starts[end_token] - token_starts[start_token];
  10387     try string_bytes.ensureUnusedCapacity(gpa, total_bytes);
  10388 
  10389     var current_token = start_token;
  10390     while (current_token < end_token) : (current_token += 1) {
  10391         switch (token_tags[current_token]) {
  10392             .doc_comment => {
  10393                 const tok_bytes = astgen.tree.tokenSlice(current_token)[3..];
  10394                 string_bytes.appendSliceAssumeCapacity(tok_bytes);
  10395                 if (current_token != end_token - 1) {
  10396                     string_bytes.appendAssumeCapacity('\n');
  10397                 }
  10398             },
  10399             else => break,
  10400         }
  10401     }
  10402 
  10403     const key = string_bytes.items[str_index..];
  10404     const gop = try astgen.string_table.getOrPutContextAdapted(gpa, @as([]const u8, key), StringIndexAdapter{
  10405         .bytes = string_bytes,
  10406     }, StringIndexContext{
  10407         .bytes = string_bytes,
  10408     });
  10409 
  10410     if (gop.found_existing) {
  10411         string_bytes.shrinkRetainingCapacity(str_index);
  10412         return gop.key_ptr.*;
  10413     } else {
  10414         gop.key_ptr.* = str_index;
  10415         try string_bytes.append(gpa, 0);
  10416         return str_index;
  10417     }
  10418 }
  10419 
  10420 const IndexSlice = struct { index: u32, len: u32 };
  10421 
  10422 fn strLitAsString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !IndexSlice {
  10423     const gpa = astgen.gpa;
  10424     const string_bytes = &astgen.string_bytes;
  10425     const str_index = @intCast(u32, string_bytes.items.len);
  10426     const token_bytes = astgen.tree.tokenSlice(str_lit_token);
  10427     try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0);
  10428     const key = string_bytes.items[str_index..];
  10429     const gop = try astgen.string_table.getOrPutContextAdapted(gpa, @as([]const u8, key), StringIndexAdapter{
  10430         .bytes = string_bytes,
  10431     }, StringIndexContext{
  10432         .bytes = string_bytes,
  10433     });
  10434     if (gop.found_existing) {
  10435         string_bytes.shrinkRetainingCapacity(str_index);
  10436         return IndexSlice{
  10437             .index = gop.key_ptr.*,
  10438             .len = @intCast(u32, key.len),
  10439         };
  10440     } else {
  10441         gop.key_ptr.* = str_index;
  10442         // Still need a null byte because we are using the same table
  10443         // to lookup null terminated strings, so if we get a match, it has to
  10444         // be null terminated for that to work.
  10445         try string_bytes.append(gpa, 0);
  10446         return IndexSlice{
  10447             .index = str_index,
  10448             .len = @intCast(u32, key.len),
  10449         };
  10450     }
  10451 }
  10452 
  10453 fn strLitNodeAsString(astgen: *AstGen, node: Ast.Node.Index) !IndexSlice {
  10454     const tree = astgen.tree;
  10455     const node_datas = tree.nodes.items(.data);
  10456 
  10457     const start = node_datas[node].lhs;
  10458     const end = node_datas[node].rhs;
  10459 
  10460     const gpa = astgen.gpa;
  10461     const string_bytes = &astgen.string_bytes;
  10462     const str_index = string_bytes.items.len;
  10463 
  10464     // First line: do not append a newline.
  10465     var tok_i = start;
  10466     {
  10467         const slice = tree.tokenSlice(tok_i);
  10468         const line_bytes = slice[2 .. slice.len - 1];
  10469         try string_bytes.appendSlice(gpa, line_bytes);
  10470         tok_i += 1;
  10471     }
  10472     // Following lines: each line prepends a newline.
  10473     while (tok_i <= end) : (tok_i += 1) {
  10474         const slice = tree.tokenSlice(tok_i);
  10475         const line_bytes = slice[2 .. slice.len - 1];
  10476         try string_bytes.ensureUnusedCapacity(gpa, line_bytes.len + 1);
  10477         string_bytes.appendAssumeCapacity('\n');
  10478         string_bytes.appendSliceAssumeCapacity(line_bytes);
  10479     }
  10480     const len = string_bytes.items.len - str_index;
  10481     try string_bytes.append(gpa, 0);
  10482     return IndexSlice{
  10483         .index = @intCast(u32, str_index),
  10484         .len = @intCast(u32, len),
  10485     };
  10486 }
  10487 
  10488 fn testNameString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !u32 {
  10489     const gpa = astgen.gpa;
  10490     const string_bytes = &astgen.string_bytes;
  10491     const str_index = @intCast(u32, string_bytes.items.len);
  10492     const token_bytes = astgen.tree.tokenSlice(str_lit_token);
  10493     try string_bytes.append(gpa, 0); // Indicates this is a test.
  10494     try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0);
  10495     const slice = string_bytes.items[str_index + 1 ..];
  10496     if (mem.indexOfScalar(u8, slice, 0) != null) {
  10497         return astgen.failTok(str_lit_token, "test name cannot contain null bytes", .{});
  10498     } else if (slice.len == 0) {
  10499         return astgen.failTok(str_lit_token, "empty test name must be omitted", .{});
  10500     }
  10501     try string_bytes.append(gpa, 0);
  10502     return str_index;
  10503 }
  10504 
  10505 const Scope = struct {
  10506     tag: Tag,
  10507 
  10508     fn cast(base: *Scope, comptime T: type) ?*T {
  10509         if (T == Defer) {
  10510             switch (base.tag) {
  10511                 .defer_normal, .defer_error => return @fieldParentPtr(T, "base", base),
  10512                 else => return null,
  10513             }
  10514         }
  10515         if (base.tag != T.base_tag)
  10516             return null;
  10517 
  10518         return @fieldParentPtr(T, "base", base);
  10519     }
  10520 
  10521     fn parent(base: *Scope) ?*Scope {
  10522         return switch (base.tag) {
  10523             .gen_zir => base.cast(GenZir).?.parent,
  10524             .local_val => base.cast(LocalVal).?.parent,
  10525             .local_ptr => base.cast(LocalPtr).?.parent,
  10526             .defer_normal, .defer_error => base.cast(Defer).?.parent,
  10527             .namespace => base.cast(Namespace).?.parent,
  10528             .top => null,
  10529         };
  10530     }
  10531 
  10532     const Tag = enum {
  10533         gen_zir,
  10534         local_val,
  10535         local_ptr,
  10536         defer_normal,
  10537         defer_error,
  10538         namespace,
  10539         top,
  10540     };
  10541 
  10542     /// The category of identifier. These tag names are user-visible in compile errors.
  10543     const IdCat = enum {
  10544         @"function parameter",
  10545         @"local constant",
  10546         @"local variable",
  10547         @"loop index capture",
  10548         @"switch tag capture",
  10549         capture,
  10550     };
  10551 
  10552     /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.
  10553     /// This structure lives as long as the AST generation of the Block
  10554     /// node that contains the variable.
  10555     const LocalVal = struct {
  10556         const base_tag: Tag = .local_val;
  10557         base: Scope = Scope{ .tag = base_tag },
  10558         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
  10559         parent: *Scope,
  10560         gen_zir: *GenZir,
  10561         inst: Zir.Inst.Ref,
  10562         /// Source location of the corresponding variable declaration.
  10563         token_src: Ast.TokenIndex,
  10564         /// Track the first identifer where it is referenced.
  10565         /// 0 means never referenced.
  10566         used: Ast.TokenIndex = 0,
  10567         /// Track the identifier where it is discarded, like this `_ = foo;`.
  10568         /// 0 means never discarded.
  10569         discarded: Ast.TokenIndex = 0,
  10570         /// String table index.
  10571         name: u32,
  10572         id_cat: IdCat,
  10573     };
  10574 
  10575     /// This could be a `const` or `var` local. It has a pointer instead of a value.
  10576     /// This structure lives as long as the AST generation of the Block
  10577     /// node that contains the variable.
  10578     const LocalPtr = struct {
  10579         const base_tag: Tag = .local_ptr;
  10580         base: Scope = Scope{ .tag = base_tag },
  10581         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
  10582         parent: *Scope,
  10583         gen_zir: *GenZir,
  10584         ptr: Zir.Inst.Ref,
  10585         /// Source location of the corresponding variable declaration.
  10586         token_src: Ast.TokenIndex,
  10587         /// Track the first identifer where it is referenced.
  10588         /// 0 means never referenced.
  10589         used: Ast.TokenIndex = 0,
  10590         /// Track the identifier where it is discarded, like this `_ = foo;`.
  10591         /// 0 means never discarded.
  10592         discarded: Ast.TokenIndex = 0,
  10593         /// String table index.
  10594         name: u32,
  10595         id_cat: IdCat,
  10596         /// true means we find out during Sema whether the value is comptime.
  10597         /// false means it is already known at AstGen the value is runtime-known.
  10598         maybe_comptime: bool,
  10599     };
  10600 
  10601     const Defer = struct {
  10602         base: Scope,
  10603         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
  10604         parent: *Scope,
  10605         index: u32,
  10606         len: u32,
  10607         remapped_err_code: Zir.Inst.Index = 0,
  10608     };
  10609 
  10610     /// Represents a global scope that has any number of declarations in it.
  10611     /// Each declaration has this as the parent scope.
  10612     const Namespace = struct {
  10613         const base_tag: Tag = .namespace;
  10614         base: Scope = Scope{ .tag = base_tag },
  10615 
  10616         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
  10617         parent: *Scope,
  10618         /// Maps string table index to the source location of declaration,
  10619         /// for the purposes of reporting name shadowing compile errors.
  10620         decls: std.AutoHashMapUnmanaged(u32, Ast.Node.Index) = .{},
  10621         node: Ast.Node.Index,
  10622         inst: Zir.Inst.Index,
  10623 
  10624         /// The astgen scope containing this namespace.
  10625         /// Only valid during astgen.
  10626         declaring_gz: ?*GenZir,
  10627 
  10628         /// Map from the raw captured value to the instruction
  10629         /// ref of the capture for decls in this namespace
  10630         captures: std.AutoArrayHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},
  10631 
  10632         pub fn deinit(self: *Namespace, gpa: Allocator) void {
  10633             self.decls.deinit(gpa);
  10634             self.captures.deinit(gpa);
  10635             self.* = undefined;
  10636         }
  10637     };
  10638 
  10639     const Top = struct {
  10640         const base_tag: Scope.Tag = .top;
  10641         base: Scope = Scope{ .tag = base_tag },
  10642     };
  10643 };
  10644 
  10645 /// This is a temporary structure; references to it are valid only
  10646 /// while constructing a `Zir`.
  10647 const GenZir = struct {
  10648     const base_tag: Scope.Tag = .gen_zir;
  10649     base: Scope = Scope{ .tag = base_tag },
  10650     force_comptime: bool,
  10651     /// This is set to true for inline loops; false otherwise.
  10652     is_inline: bool = false,
  10653     c_import: bool = false,
  10654     /// How decls created in this scope should be named.
  10655     anon_name_strategy: Zir.Inst.NameStrategy = .anon,
  10656     /// The containing decl AST node.
  10657     decl_node_index: Ast.Node.Index,
  10658     /// The containing decl line index, absolute.
  10659     decl_line: u32,
  10660     /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
  10661     parent: *Scope,
  10662     /// All `GenZir` scopes for the same ZIR share this.
  10663     astgen: *AstGen,
  10664     /// Keeps track of the list of instructions in this scope. Possibly shared.
  10665     /// Indexes to instructions in `astgen`.
  10666     instructions: *ArrayListUnmanaged(Zir.Inst.Index),
  10667     /// A sub-block may share its instructions ArrayList with containing GenZir,
  10668     /// if use is strictly nested. This saves prior size of list for unstacking.
  10669     instructions_top: usize,
  10670     label: ?Label = null,
  10671     break_block: Zir.Inst.Index = 0,
  10672     continue_block: Zir.Inst.Index = 0,
  10673     /// Only valid when setBreakResultInfo is called.
  10674     break_result_info: AstGen.ResultInfo = undefined,
  10675     /// When a block has a pointer result location, here it is.
  10676     rl_ptr: Zir.Inst.Ref = .none,
  10677     /// When a block has a type result location, here it is.
  10678     rl_ty_inst: Zir.Inst.Ref = .none,
  10679     /// Keeps track of how many branches of a block did not actually
  10680     /// consume the result location. astgen uses this to figure out
  10681     /// whether to rely on break instructions or writing to the result
  10682     /// pointer for the result instruction.
  10683     rvalue_rl_count: usize = 0,
  10684     /// Keeps track of how many break instructions there are. When astgen is finished
  10685     /// with a block, it can check this against rvalue_rl_count to find out whether
  10686     /// the break instructions should be downgraded to break_void.
  10687     break_count: usize = 0,
  10688     /// Tracks `break :foo bar` instructions so they can possibly be elided later if
  10689     /// the labeled block ends up not needing a result location pointer.
  10690     labeled_breaks: ArrayListUnmanaged(struct { br: Zir.Inst.Index, search: Zir.Inst.Index }) = .{},
  10691 
  10692     suspend_node: Ast.Node.Index = 0,
  10693     nosuspend_node: Ast.Node.Index = 0,
  10694     /// Set if this GenZir is a defer.
  10695     cur_defer_node: Ast.Node.Index = 0,
  10696     // Set if this GenZir is a defer or it is inside a defer.
  10697     any_defer_node: Ast.Node.Index = 0,
  10698 
  10699     /// Namespace members are lazy.  When executing a decl within a namespace,
  10700     /// any references to external instructions need to be treated specially.
  10701     /// This list tracks those references.  See also .closure_capture and .closure_get.
  10702     /// Keys are the raw instruction index, values are the closure_capture instruction.
  10703     captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},
  10704 
  10705     const unstacked_top = std.math.maxInt(usize);
  10706     /// Call unstack before adding any new instructions to containing GenZir.
  10707     fn unstack(self: *GenZir) void {
  10708         if (self.instructions_top != unstacked_top) {
  10709             self.instructions.items.len = self.instructions_top;
  10710             self.instructions_top = unstacked_top;
  10711         }
  10712     }
  10713 
  10714     fn isEmpty(self: *const GenZir) bool {
  10715         return (self.instructions_top == unstacked_top) or
  10716             (self.instructions.items.len == self.instructions_top);
  10717     }
  10718 
  10719     fn instructionsSlice(self: *const GenZir) []Zir.Inst.Index {
  10720         return if (self.instructions_top == unstacked_top)
  10721             &[0]Zir.Inst.Index{}
  10722         else
  10723             self.instructions.items[self.instructions_top..];
  10724     }
  10725 
  10726     fn instructionsSliceUpto(self: *const GenZir, stacked_gz: *GenZir) []Zir.Inst.Index {
  10727         return if (self.instructions_top == unstacked_top)
  10728             &[0]Zir.Inst.Index{}
  10729         else if (self.instructions == stacked_gz.instructions and stacked_gz.instructions_top != unstacked_top)
  10730             self.instructions.items[self.instructions_top..stacked_gz.instructions_top]
  10731         else
  10732             self.instructions.items[self.instructions_top..];
  10733     }
  10734 
  10735     fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {
  10736         return .{
  10737             .force_comptime = gz.force_comptime,
  10738             .c_import = gz.c_import,
  10739             .decl_node_index = gz.decl_node_index,
  10740             .decl_line = gz.decl_line,
  10741             .parent = scope,
  10742             .rl_ty_inst = gz.rl_ty_inst,
  10743             .astgen = gz.astgen,
  10744             .suspend_node = gz.suspend_node,
  10745             .nosuspend_node = gz.nosuspend_node,
  10746             .any_defer_node = gz.any_defer_node,
  10747             .instructions = gz.instructions,
  10748             .instructions_top = gz.instructions.items.len,
  10749         };
  10750     }
  10751 
  10752     fn makeCoercionScope(
  10753         parent_gz: *GenZir,
  10754         scope: *Scope,
  10755         dest_type: Zir.Inst.Ref,
  10756         result_ptr: Zir.Inst.Ref,
  10757         src_node: Ast.Node.Index,
  10758     ) !GenZir {
  10759         // Detect whether this expr() call goes into rvalue() to store the result into the
  10760         // result location. If it does, elide the coerce_result_ptr instruction
  10761         // as well as the store instruction, instead passing the result as an rvalue.
  10762         var as_scope = parent_gz.makeSubBlock(scope);
  10763         errdefer as_scope.unstack();
  10764         as_scope.rl_ptr = try as_scope.addPlNode(.coerce_result_ptr, src_node, Zir.Inst.Bin{ .lhs = dest_type, .rhs = result_ptr });
  10765 
  10766         // `rl_ty_inst` needs to be set in case the stores to `rl_ptr` are eliminated.
  10767         as_scope.rl_ty_inst = dest_type;
  10768 
  10769         return as_scope;
  10770     }
  10771 
  10772     /// Assumes `as_scope` is stacked immediately on top of `parent_gz`. Unstacks `as_scope`.
  10773     fn finishCoercion(
  10774         as_scope: *GenZir,
  10775         parent_gz: *GenZir,
  10776         ri: ResultInfo,
  10777         src_node: Ast.Node.Index,
  10778         result: Zir.Inst.Ref,
  10779         dest_type: Zir.Inst.Ref,
  10780     ) InnerError!Zir.Inst.Ref {
  10781         assert(as_scope.instructions == parent_gz.instructions);
  10782         const astgen = as_scope.astgen;
  10783         if (as_scope.rvalue_rl_count == 1) {
  10784             // Busted! This expression didn't actually need a pointer.
  10785             const zir_tags = astgen.instructions.items(.tag);
  10786             const zir_datas = astgen.instructions.items(.data);
  10787             var src: usize = as_scope.instructions_top;
  10788             var dst: usize = src;
  10789             while (src < as_scope.instructions.items.len) : (src += 1) {
  10790                 const src_inst = as_scope.instructions.items[src];
  10791                 if (indexToRef(src_inst) == as_scope.rl_ptr) continue;
  10792                 if (zir_tags[src_inst] == .store_to_block_ptr) {
  10793                     if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue;
  10794                 }
  10795                 as_scope.instructions.items[dst] = src_inst;
  10796                 dst += 1;
  10797             }
  10798             parent_gz.instructions.items.len -= src - dst;
  10799             as_scope.instructions_top = GenZir.unstacked_top;
  10800             // as_scope now unstacked, can add new instructions to parent_gz
  10801             const casted_result = try parent_gz.addBin(.as, dest_type, result);
  10802             return rvalue(parent_gz, ri, casted_result, src_node);
  10803         } else {
  10804             // implicitly move all as_scope instructions to parent_gz
  10805             as_scope.instructions_top = GenZir.unstacked_top;
  10806             return result;
  10807         }
  10808     }
  10809 
  10810     const Label = struct {
  10811         token: Ast.TokenIndex,
  10812         block_inst: Zir.Inst.Index,
  10813         used: bool = false,
  10814     };
  10815 
  10816     /// Assumes nothing stacked on `gz`.
  10817     fn endsWithNoReturn(gz: GenZir) bool {
  10818         if (gz.isEmpty()) return false;
  10819         const tags = gz.astgen.instructions.items(.tag);
  10820         const last_inst = gz.instructions.items[gz.instructions.items.len - 1];
  10821         return tags[last_inst].isNoReturn();
  10822     }
  10823 
  10824     /// TODO all uses of this should be replaced with uses of `endsWithNoReturn`.
  10825     fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool {
  10826         if (inst_ref == .unreachable_value) return true;
  10827         if (refToIndex(inst_ref)) |inst_index| {
  10828             return gz.astgen.instructions.items(.tag)[inst_index].isNoReturn();
  10829         }
  10830         return false;
  10831     }
  10832 
  10833     fn nodeIndexToRelative(gz: GenZir, node_index: Ast.Node.Index) i32 {
  10834         return @bitCast(i32, node_index) - @bitCast(i32, gz.decl_node_index);
  10835     }
  10836 
  10837     fn tokenIndexToRelative(gz: GenZir, token: Ast.TokenIndex) u32 {
  10838         return token - gz.srcToken();
  10839     }
  10840 
  10841     fn srcToken(gz: GenZir) Ast.TokenIndex {
  10842         return gz.astgen.tree.firstToken(gz.decl_node_index);
  10843     }
  10844 
  10845     fn setBreakResultInfo(gz: *GenZir, parent_ri: AstGen.ResultInfo) void {
  10846         // Depending on whether the result location is a pointer or value, different
  10847         // ZIR needs to be generated. In the former case we rely on storing to the
  10848         // pointer to communicate the result, and use breakvoid; in the latter case
  10849         // the block break instructions will have the result values.
  10850         // One more complication: when the result location is a pointer, we detect
  10851         // the scenario where the result location is not consumed. In this case
  10852         // we emit ZIR for the block break instructions to have the result values,
  10853         // and then rvalue() on that to pass the value to the result location.
  10854         switch (parent_ri.rl) {
  10855             .ty, .coerced_ty => |ty_inst| {
  10856                 gz.rl_ty_inst = ty_inst;
  10857                 gz.break_result_info = parent_ri;
  10858             },
  10859 
  10860             .none, .ref => {
  10861                 gz.rl_ty_inst = .none;
  10862                 gz.break_result_info = parent_ri;
  10863             },
  10864 
  10865             .discard => {
  10866                 gz.rl_ty_inst = .none;
  10867                 gz.break_result_info = .{ .rl = .discard };
  10868             },
  10869 
  10870             .ptr => |ptr_res| {
  10871                 gz.rl_ty_inst = .none;
  10872                 gz.break_result_info = .{ .rl = .{ .ptr = .{ .inst = ptr_res.inst } }, .ctx = parent_ri.ctx };
  10873             },
  10874 
  10875             .inferred_ptr => |ptr| {
  10876                 gz.rl_ty_inst = .none;
  10877                 gz.rl_ptr = ptr;
  10878                 gz.break_result_info = .{ .rl = .{ .block_ptr = gz }, .ctx = parent_ri.ctx };
  10879             },
  10880 
  10881             .block_ptr => |parent_block_scope| {
  10882                 gz.rl_ty_inst = parent_block_scope.rl_ty_inst;
  10883                 gz.rl_ptr = parent_block_scope.rl_ptr;
  10884                 gz.break_result_info = .{ .rl = .{ .block_ptr = gz }, .ctx = parent_ri.ctx };
  10885             },
  10886         }
  10887     }
  10888 
  10889     /// Assumes nothing stacked on `gz`. Unstacks `gz`.
  10890     fn setBoolBrBody(gz: *GenZir, inst: Zir.Inst.Index) !void {
  10891         const astgen = gz.astgen;
  10892         const gpa = astgen.gpa;
  10893         const body = gz.instructionsSlice();
  10894         const body_len = astgen.countBodyLenAfterFixups(body);
  10895         try astgen.extra.ensureUnusedCapacity(
  10896             gpa,
  10897             @typeInfo(Zir.Inst.Block).Struct.fields.len + body_len,
  10898         );
  10899         const zir_datas = astgen.instructions.items(.data);
  10900         zir_datas[inst].bool_br.payload_index = astgen.addExtraAssumeCapacity(
  10901             Zir.Inst.Block{ .body_len = body_len },
  10902         );
  10903         astgen.appendBodyWithFixups(body);
  10904         gz.unstack();
  10905     }
  10906 
  10907     /// Assumes nothing stacked on `gz`. Unstacks `gz`.
  10908     fn setBlockBody(gz: *GenZir, inst: Zir.Inst.Index) !void {
  10909         const astgen = gz.astgen;
  10910         const gpa = astgen.gpa;
  10911         const body = gz.instructionsSlice();
  10912         const body_len = astgen.countBodyLenAfterFixups(body);
  10913         try astgen.extra.ensureUnusedCapacity(
  10914             gpa,
  10915             @typeInfo(Zir.Inst.Block).Struct.fields.len + body_len,
  10916         );
  10917         const zir_datas = astgen.instructions.items(.data);
  10918         zir_datas[inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(
  10919             Zir.Inst.Block{ .body_len = body_len },
  10920         );
  10921         astgen.appendBodyWithFixups(body);
  10922         gz.unstack();
  10923     }
  10924 
  10925     /// Assumes nothing stacked on `gz`. Unstacks `gz`.
  10926     fn setTryBody(gz: *GenZir, inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !void {
  10927         const astgen = gz.astgen;
  10928         const gpa = astgen.gpa;
  10929         const body = gz.instructionsSlice();
  10930         const body_len = astgen.countBodyLenAfterFixups(body);
  10931         try astgen.extra.ensureUnusedCapacity(
  10932             gpa,
  10933             @typeInfo(Zir.Inst.Try).Struct.fields.len + body_len,
  10934         );
  10935         const zir_datas = astgen.instructions.items(.data);
  10936         zir_datas[inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(
  10937             Zir.Inst.Try{
  10938                 .operand = operand,
  10939                 .body_len = body_len,
  10940             },
  10941         );
  10942         astgen.appendBodyWithFixups(body);
  10943         gz.unstack();
  10944     }
  10945 
  10946     /// Must be called with the following stack set up:
  10947     ///  * gz (bottom)
  10948     ///  * align_gz
  10949     ///  * addrspace_gz
  10950     ///  * section_gz
  10951     ///  * cc_gz
  10952     ///  * ret_gz
  10953     ///  * body_gz (top)
  10954     /// Unstacks all of those except for `gz`.
  10955     fn addFunc(gz: *GenZir, args: struct {
  10956         src_node: Ast.Node.Index,
  10957         lbrace_line: u32 = 0,
  10958         lbrace_column: u32 = 0,
  10959         param_block: Zir.Inst.Index,
  10960 
  10961         align_gz: ?*GenZir,
  10962         addrspace_gz: ?*GenZir,
  10963         section_gz: ?*GenZir,
  10964         cc_gz: ?*GenZir,
  10965         ret_gz: ?*GenZir,
  10966         body_gz: ?*GenZir,
  10967 
  10968         align_ref: Zir.Inst.Ref,
  10969         addrspace_ref: Zir.Inst.Ref,
  10970         section_ref: Zir.Inst.Ref,
  10971         cc_ref: Zir.Inst.Ref,
  10972         ret_ref: Zir.Inst.Ref,
  10973 
  10974         lib_name: u32,
  10975         noalias_bits: u32,
  10976         is_var_args: bool,
  10977         is_inferred_error: bool,
  10978         is_test: bool,
  10979         is_extern: bool,
  10980         is_noinline: bool,
  10981     }) !Zir.Inst.Ref {
  10982         assert(args.src_node != 0);
  10983         const astgen = gz.astgen;
  10984         const gpa = astgen.gpa;
  10985         const ret_ref = if (args.ret_ref == .void_type) .none else args.ret_ref;
  10986         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  10987 
  10988         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10989 
  10990         var body: []Zir.Inst.Index = &[0]Zir.Inst.Index{};
  10991         var ret_body: []Zir.Inst.Index = &[0]Zir.Inst.Index{};
  10992         var src_locs_buffer: [3]u32 = undefined;
  10993         var src_locs: []u32 = src_locs_buffer[0..0];
  10994         if (args.body_gz) |body_gz| {
  10995             const tree = astgen.tree;
  10996             const node_tags = tree.nodes.items(.tag);
  10997             const node_datas = tree.nodes.items(.data);
  10998             const token_starts = tree.tokens.items(.start);
  10999             const fn_decl = args.src_node;
  11000             assert(node_tags[fn_decl] == .fn_decl or node_tags[fn_decl] == .test_decl);
  11001             const block = node_datas[fn_decl].rhs;
  11002             const rbrace_start = token_starts[tree.lastToken(block)];
  11003             astgen.advanceSourceCursor(rbrace_start);
  11004             const rbrace_line = @intCast(u32, astgen.source_line - gz.decl_line);
  11005             const rbrace_column = @intCast(u32, astgen.source_column);
  11006 
  11007             const columns = args.lbrace_column | (rbrace_column << 16);
  11008             src_locs_buffer[0] = args.lbrace_line;
  11009             src_locs_buffer[1] = rbrace_line;
  11010             src_locs_buffer[2] = columns;
  11011             src_locs = &src_locs_buffer;
  11012 
  11013             body = body_gz.instructionsSlice();
  11014             if (args.ret_gz) |ret_gz|
  11015                 ret_body = ret_gz.instructionsSliceUpto(body_gz);
  11016         } else {
  11017             if (args.ret_gz) |ret_gz|
  11018                 ret_body = ret_gz.instructionsSlice();
  11019         }
  11020         const body_len = astgen.countBodyLenAfterFixups(body);
  11021 
  11022         if (args.cc_ref != .none or args.lib_name != 0 or args.is_var_args or args.is_test or
  11023             args.is_extern or args.align_ref != .none or args.section_ref != .none or
  11024             args.addrspace_ref != .none or args.noalias_bits != 0 or args.is_noinline)
  11025         {
  11026             var align_body: []Zir.Inst.Index = &.{};
  11027             var addrspace_body: []Zir.Inst.Index = &.{};
  11028             var section_body: []Zir.Inst.Index = &.{};
  11029             var cc_body: []Zir.Inst.Index = &.{};
  11030             if (args.ret_gz != null) {
  11031                 align_body = args.align_gz.?.instructionsSliceUpto(args.addrspace_gz.?);
  11032                 addrspace_body = args.addrspace_gz.?.instructionsSliceUpto(args.section_gz.?);
  11033                 section_body = args.section_gz.?.instructionsSliceUpto(args.cc_gz.?);
  11034                 cc_body = args.cc_gz.?.instructionsSliceUpto(args.ret_gz.?);
  11035             }
  11036 
  11037             try astgen.extra.ensureUnusedCapacity(
  11038                 gpa,
  11039                 @typeInfo(Zir.Inst.FuncFancy).Struct.fields.len +
  11040                     fancyFnExprExtraLen(astgen, align_body, args.align_ref) +
  11041                     fancyFnExprExtraLen(astgen, addrspace_body, args.addrspace_ref) +
  11042                     fancyFnExprExtraLen(astgen, section_body, args.section_ref) +
  11043                     fancyFnExprExtraLen(astgen, cc_body, args.cc_ref) +
  11044                     fancyFnExprExtraLen(astgen, ret_body, ret_ref) +
  11045                     body_len + src_locs.len +
  11046                     @boolToInt(args.lib_name != 0) +
  11047                     @boolToInt(args.noalias_bits != 0),
  11048             );
  11049             const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.FuncFancy{
  11050                 .param_block = args.param_block,
  11051                 .body_len = body_len,
  11052                 .bits = .{
  11053                     .is_var_args = args.is_var_args,
  11054                     .is_inferred_error = args.is_inferred_error,
  11055                     .is_test = args.is_test,
  11056                     .is_extern = args.is_extern,
  11057                     .is_noinline = args.is_noinline,
  11058                     .has_lib_name = args.lib_name != 0,
  11059                     .has_any_noalias = args.noalias_bits != 0,
  11060 
  11061                     .has_align_ref = args.align_ref != .none,
  11062                     .has_addrspace_ref = args.addrspace_ref != .none,
  11063                     .has_section_ref = args.section_ref != .none,
  11064                     .has_cc_ref = args.cc_ref != .none,
  11065                     .has_ret_ty_ref = ret_ref != .none,
  11066 
  11067                     .has_align_body = align_body.len != 0,
  11068                     .has_addrspace_body = addrspace_body.len != 0,
  11069                     .has_section_body = section_body.len != 0,
  11070                     .has_cc_body = cc_body.len != 0,
  11071                     .has_ret_ty_body = ret_body.len != 0,
  11072                 },
  11073             });
  11074             if (args.lib_name != 0) {
  11075                 astgen.extra.appendAssumeCapacity(args.lib_name);
  11076             }
  11077 
  11078             const zir_datas = astgen.instructions.items(.data);
  11079             if (align_body.len != 0) {
  11080                 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, align_body));
  11081                 astgen.appendBodyWithFixups(align_body);
  11082                 zir_datas[align_body[align_body.len - 1]].@"break".block_inst = new_index;
  11083             } else if (args.align_ref != .none) {
  11084                 astgen.extra.appendAssumeCapacity(@enumToInt(args.align_ref));
  11085             }
  11086             if (addrspace_body.len != 0) {
  11087                 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, addrspace_body));
  11088                 astgen.appendBodyWithFixups(addrspace_body);
  11089                 zir_datas[addrspace_body[addrspace_body.len - 1]].@"break".block_inst = new_index;
  11090             } else if (args.addrspace_ref != .none) {
  11091                 astgen.extra.appendAssumeCapacity(@enumToInt(args.addrspace_ref));
  11092             }
  11093             if (section_body.len != 0) {
  11094                 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, section_body));
  11095                 astgen.appendBodyWithFixups(section_body);
  11096                 zir_datas[section_body[section_body.len - 1]].@"break".block_inst = new_index;
  11097             } else if (args.section_ref != .none) {
  11098                 astgen.extra.appendAssumeCapacity(@enumToInt(args.section_ref));
  11099             }
  11100             if (cc_body.len != 0) {
  11101                 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, cc_body));
  11102                 astgen.appendBodyWithFixups(cc_body);
  11103                 zir_datas[cc_body[cc_body.len - 1]].@"break".block_inst = new_index;
  11104             } else if (args.cc_ref != .none) {
  11105                 astgen.extra.appendAssumeCapacity(@enumToInt(args.cc_ref));
  11106             }
  11107             if (ret_body.len != 0) {
  11108                 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, ret_body));
  11109                 astgen.appendBodyWithFixups(ret_body);
  11110                 zir_datas[ret_body[ret_body.len - 1]].@"break".block_inst = new_index;
  11111             } else if (ret_ref != .none) {
  11112                 astgen.extra.appendAssumeCapacity(@enumToInt(ret_ref));
  11113             }
  11114 
  11115             if (args.noalias_bits != 0) {
  11116                 astgen.extra.appendAssumeCapacity(args.noalias_bits);
  11117             }
  11118 
  11119             astgen.appendBodyWithFixups(body);
  11120             astgen.extra.appendSliceAssumeCapacity(src_locs);
  11121 
  11122             // Order is important when unstacking.
  11123             if (args.body_gz) |body_gz| body_gz.unstack();
  11124             if (args.ret_gz != null) {
  11125                 args.ret_gz.?.unstack();
  11126                 args.cc_gz.?.unstack();
  11127                 args.section_gz.?.unstack();
  11128                 args.addrspace_gz.?.unstack();
  11129                 args.align_gz.?.unstack();
  11130             }
  11131 
  11132             try gz.instructions.ensureUnusedCapacity(gpa, 1);
  11133 
  11134             astgen.instructions.appendAssumeCapacity(.{
  11135                 .tag = .func_fancy,
  11136                 .data = .{ .pl_node = .{
  11137                     .src_node = gz.nodeIndexToRelative(args.src_node),
  11138                     .payload_index = payload_index,
  11139                 } },
  11140             });
  11141             gz.instructions.appendAssumeCapacity(new_index);
  11142             return indexToRef(new_index);
  11143         } else {
  11144             try astgen.extra.ensureUnusedCapacity(
  11145                 gpa,
  11146                 @typeInfo(Zir.Inst.Func).Struct.fields.len + 1 +
  11147                     fancyFnExprExtraLen(astgen, ret_body, ret_ref) +
  11148                     body_len + src_locs.len,
  11149             );
  11150 
  11151             const ret_body_len = if (ret_body.len != 0)
  11152                 countBodyLenAfterFixups(astgen, ret_body)
  11153             else
  11154                 @boolToInt(ret_ref != .none);
  11155 
  11156             const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.Func{
  11157                 .param_block = args.param_block,
  11158                 .ret_body_len = ret_body_len,
  11159                 .body_len = body_len,
  11160             });
  11161             const zir_datas = astgen.instructions.items(.data);
  11162             if (ret_body.len != 0) {
  11163                 astgen.appendBodyWithFixups(ret_body);
  11164                 zir_datas[ret_body[ret_body.len - 1]].@"break".block_inst = new_index;
  11165             } else if (ret_ref != .none) {
  11166                 astgen.extra.appendAssumeCapacity(@enumToInt(ret_ref));
  11167             }
  11168             astgen.appendBodyWithFixups(body);
  11169             astgen.extra.appendSliceAssumeCapacity(src_locs);
  11170 
  11171             // Order is important when unstacking.
  11172             if (args.body_gz) |body_gz| body_gz.unstack();
  11173             if (args.ret_gz) |ret_gz| ret_gz.unstack();
  11174             if (args.cc_gz) |cc_gz| cc_gz.unstack();
  11175             if (args.section_gz) |section_gz| section_gz.unstack();
  11176             if (args.addrspace_gz) |addrspace_gz| addrspace_gz.unstack();
  11177             if (args.align_gz) |align_gz| align_gz.unstack();
  11178 
  11179             try gz.instructions.ensureUnusedCapacity(gpa, 1);
  11180 
  11181             const tag: Zir.Inst.Tag = if (args.is_inferred_error) .func_inferred else .func;
  11182             astgen.instructions.appendAssumeCapacity(.{
  11183                 .tag = tag,
  11184                 .data = .{ .pl_node = .{
  11185                     .src_node = gz.nodeIndexToRelative(args.src_node),
  11186                     .payload_index = payload_index,
  11187                 } },
  11188             });
  11189             gz.instructions.appendAssumeCapacity(new_index);
  11190             return indexToRef(new_index);
  11191         }
  11192     }
  11193 
  11194     fn fancyFnExprExtraLen(astgen: *AstGen, body: []Zir.Inst.Index, ref: Zir.Inst.Ref) u32 {
  11195         // In the case of non-empty body, there is one for the body length,
  11196         // and then one for each instruction.
  11197         return countBodyLenAfterFixups(astgen, body) + @boolToInt(ref != .none);
  11198     }
  11199 
  11200     fn addVar(gz: *GenZir, args: struct {
  11201         align_inst: Zir.Inst.Ref,
  11202         lib_name: u32,
  11203         var_type: Zir.Inst.Ref,
  11204         init: Zir.Inst.Ref,
  11205         is_extern: bool,
  11206         is_threadlocal: bool,
  11207     }) !Zir.Inst.Ref {
  11208         const astgen = gz.astgen;
  11209         const gpa = astgen.gpa;
  11210 
  11211         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  11212         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  11213 
  11214         try astgen.extra.ensureUnusedCapacity(
  11215             gpa,
  11216             @typeInfo(Zir.Inst.ExtendedVar).Struct.fields.len +
  11217                 @boolToInt(args.lib_name != 0) +
  11218                 @boolToInt(args.align_inst != .none) +
  11219                 @boolToInt(args.init != .none),
  11220         );
  11221         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.ExtendedVar{
  11222             .var_type = args.var_type,
  11223         });
  11224         if (args.lib_name != 0) {
  11225             astgen.extra.appendAssumeCapacity(args.lib_name);
  11226         }
  11227         if (args.align_inst != .none) {
  11228             astgen.extra.appendAssumeCapacity(@enumToInt(args.align_inst));
  11229         }
  11230         if (args.init != .none) {
  11231             astgen.extra.appendAssumeCapacity(@enumToInt(args.init));
  11232         }
  11233 
  11234         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  11235         astgen.instructions.appendAssumeCapacity(.{
  11236             .tag = .extended,
  11237             .data = .{ .extended = .{
  11238                 .opcode = .variable,
  11239                 .small = @bitCast(u16, Zir.Inst.ExtendedVar.Small{
  11240                     .has_lib_name = args.lib_name != 0,
  11241                     .has_align = args.align_inst != .none,
  11242                     .has_init = args.init != .none,
  11243                     .is_extern = args.is_extern,
  11244                     .is_threadlocal = args.is_threadlocal,
  11245                 }),
  11246                 .operand = payload_index,
  11247             } },
  11248         });
  11249         gz.instructions.appendAssumeCapacity(new_index);
  11250         return indexToRef(new_index);
  11251     }
  11252 
  11253     /// Note that this returns a `Zir.Inst.Index` not a ref.
  11254     /// Leaves the `payload_index` field undefined.
  11255     fn addBoolBr(
  11256         gz: *GenZir,
  11257         tag: Zir.Inst.Tag,
  11258         lhs: Zir.Inst.Ref,
  11259     ) !Zir.Inst.Index {
  11260         assert(lhs != .none);
  11261         const gpa = gz.astgen.gpa;
  11262         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  11263         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  11264 
  11265         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  11266         gz.astgen.instructions.appendAssumeCapacity(.{
  11267             .tag = tag,
  11268             .data = .{ .bool_br = .{
  11269                 .lhs = lhs,
  11270                 .payload_index = undefined,
  11271             } },
  11272         });
  11273         gz.instructions.appendAssumeCapacity(new_index);
  11274         return new_index;
  11275     }
  11276 
  11277     fn addInt(gz: *GenZir, integer: u64) !Zir.Inst.Ref {
  11278         return gz.add(.{
  11279             .tag = .int,
  11280             .data = .{ .int = integer },
  11281         });
  11282     }
  11283 
  11284     fn addIntBig(gz: *GenZir, limbs: []const std.math.big.Limb) !Zir.Inst.Ref {
  11285         const astgen = gz.astgen;
  11286         const gpa = astgen.gpa;
  11287         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  11288         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  11289         try astgen.string_bytes.ensureUnusedCapacity(gpa, @sizeOf(std.math.big.Limb) * limbs.len);
  11290 
  11291         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  11292         astgen.instructions.appendAssumeCapacity(.{
  11293             .tag = .int_big,
  11294             .data = .{ .str = .{
  11295                 .start = @intCast(u32, astgen.string_bytes.items.len),
  11296                 .len = @intCast(u32, limbs.len),
  11297             } },
  11298         });
  11299         gz.instructions.appendAssumeCapacity(new_index);
  11300         astgen.string_bytes.appendSliceAssumeCapacity(mem.sliceAsBytes(limbs));
  11301         return indexToRef(new_index);
  11302     }
  11303 
  11304     fn addFloat(gz: *GenZir, number: f64) !Zir.Inst.Ref {
  11305         return gz.add(.{
  11306             .tag = .float,
  11307             .data = .{ .float = number },
  11308         });
  11309     }
  11310 
  11311     fn addUnNode(
  11312         gz: *GenZir,
  11313         tag: Zir.Inst.Tag,
  11314         operand: Zir.Inst.Ref,
  11315         /// Absolute node index. This function does the conversion to offset from Decl.
  11316         src_node: Ast.Node.Index,
  11317     ) !Zir.Inst.Ref {
  11318         assert(operand != .none);
  11319         return gz.add(.{
  11320             .tag = tag,
  11321             .data = .{ .un_node = .{
  11322                 .operand = operand,
  11323                 .src_node = gz.nodeIndexToRelative(src_node),
  11324             } },
  11325         });
  11326     }
  11327 
  11328     fn makeUnNode(
  11329         gz: *GenZir,
  11330         tag: Zir.Inst.Tag,
  11331         operand: Zir.Inst.Ref,
  11332         /// Absolute node index. This function does the conversion to offset from Decl.
  11333         src_node: Ast.Node.Index,
  11334     ) !Zir.Inst.Index {
  11335         assert(operand != .none);
  11336         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  11337         try gz.astgen.instructions.append(gz.astgen.gpa, .{
  11338             .tag = tag,
  11339             .data = .{ .un_node = .{
  11340                 .operand = operand,
  11341                 .src_node = gz.nodeIndexToRelative(src_node),
  11342             } },
  11343         });
  11344         return new_index;
  11345     }
  11346 
  11347     fn addPlNode(
  11348         gz: *GenZir,
  11349         tag: Zir.Inst.Tag,
  11350         /// Absolute node index. This function does the conversion to offset from Decl.
  11351         src_node: Ast.Node.Index,
  11352         extra: anytype,
  11353     ) !Zir.Inst.Ref {
  11354         const gpa = gz.astgen.gpa;
  11355         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  11356         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  11357 
  11358         const payload_index = try gz.astgen.addExtra(extra);
  11359         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  11360         gz.astgen.instructions.appendAssumeCapacity(.{
  11361             .tag = tag,
  11362             .data = .{ .pl_node = .{
  11363                 .src_node = gz.nodeIndexToRelative(src_node),
  11364                 .payload_index = payload_index,
  11365             } },
  11366         });
  11367         gz.instructions.appendAssumeCapacity(new_index);
  11368         return indexToRef(new_index);
  11369     }
  11370 
  11371     fn addPlNodePayloadIndex(
  11372         gz: *GenZir,
  11373         tag: Zir.Inst.Tag,
  11374         /// Absolute node index. This function does the conversion to offset from Decl.
  11375         src_node: Ast.Node.Index,
  11376         payload_index: u32,
  11377     ) !Zir.Inst.Ref {
  11378         return try gz.add(.{
  11379             .tag = tag,
  11380             .data = .{ .pl_node = .{
  11381                 .src_node = gz.nodeIndexToRelative(src_node),
  11382                 .payload_index = payload_index,
  11383             } },
  11384         });
  11385     }
  11386 
  11387     /// Supports `param_gz` stacked on `gz`. Assumes nothing stacked on `param_gz`. Unstacks `param_gz`.
  11388     fn addParam(
  11389         gz: *GenZir,
  11390         param_gz: *GenZir,
  11391         tag: Zir.Inst.Tag,
  11392         /// Absolute token index. This function does the conversion to Decl offset.
  11393         abs_tok_index: Ast.TokenIndex,
  11394         name: u32,
  11395         first_doc_comment: ?Ast.TokenIndex,
  11396     ) !Zir.Inst.Index {
  11397         const gpa = gz.astgen.gpa;
  11398         const param_body = param_gz.instructionsSlice();
  11399         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  11400         try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Param).Struct.fields.len +
  11401             param_body.len);
  11402 
  11403         const doc_comment_index = if (first_doc_comment) |first|
  11404             try gz.astgen.docCommentAsStringFromFirst(abs_tok_index, first)
  11405         else
  11406             0;
  11407 
  11408         const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Param{
  11409             .name = name,
  11410             .doc_comment = doc_comment_index,
  11411             .body_len = @intCast(u32, param_body.len),
  11412         });
  11413         gz.astgen.extra.appendSliceAssumeCapacity(param_body);
  11414         param_gz.unstack();
  11415 
  11416         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  11417         gz.astgen.instructions.appendAssumeCapacity(.{
  11418             .tag = tag,
  11419             .data = .{ .pl_tok = .{
  11420                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  11421                 .payload_index = payload_index,
  11422             } },
  11423         });
  11424         gz.instructions.appendAssumeCapacity(new_index);
  11425         return new_index;
  11426     }
  11427 
  11428     fn addExtendedPayload(gz: *GenZir, opcode: Zir.Inst.Extended, extra: anytype) !Zir.Inst.Ref {
  11429         return addExtendedPayloadSmall(gz, opcode, undefined, extra);
  11430     }
  11431 
  11432     fn addExtendedPayloadSmall(
  11433         gz: *GenZir,
  11434         opcode: Zir.Inst.Extended,
  11435         small: u16,
  11436         extra: anytype,
  11437     ) !Zir.Inst.Ref {
  11438         const gpa = gz.astgen.gpa;
  11439 
  11440         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  11441         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  11442 
  11443         const payload_index = try gz.astgen.addExtra(extra);
  11444         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  11445         gz.astgen.instructions.appendAssumeCapacity(.{
  11446             .tag = .extended,
  11447             .data = .{ .extended = .{
  11448                 .opcode = opcode,
  11449                 .small = small,
  11450                 .operand = payload_index,
  11451             } },
  11452         });
  11453         gz.instructions.appendAssumeCapacity(new_index);
  11454         return indexToRef(new_index);
  11455     }
  11456 
  11457     fn addExtendedMultiOp(
  11458         gz: *GenZir,
  11459         opcode: Zir.Inst.Extended,
  11460         node: Ast.Node.Index,
  11461         operands: []const Zir.Inst.Ref,
  11462     ) !Zir.Inst.Ref {
  11463         const astgen = gz.astgen;
  11464         const gpa = astgen.gpa;
  11465 
  11466         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  11467         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  11468         try astgen.extra.ensureUnusedCapacity(
  11469             gpa,
  11470             @typeInfo(Zir.Inst.NodeMultiOp).Struct.fields.len + operands.len,
  11471         );
  11472 
  11473         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.NodeMultiOp{
  11474             .src_node = gz.nodeIndexToRelative(node),
  11475         });
  11476         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  11477         astgen.instructions.appendAssumeCapacity(.{
  11478             .tag = .extended,
  11479             .data = .{ .extended = .{
  11480                 .opcode = opcode,
  11481                 .small = @intCast(u16, operands.len),
  11482                 .operand = payload_index,
  11483             } },
  11484         });
  11485         gz.instructions.appendAssumeCapacity(new_index);
  11486         astgen.appendRefsAssumeCapacity(operands);
  11487         return indexToRef(new_index);
  11488     }
  11489 
  11490     fn addExtendedMultiOpPayloadIndex(
  11491         gz: *GenZir,
  11492         opcode: Zir.Inst.Extended,
  11493         payload_index: u32,
  11494         trailing_len: usize,
  11495     ) !Zir.Inst.Ref {
  11496         const astgen = gz.astgen;
  11497         const gpa = astgen.gpa;
  11498 
  11499         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  11500         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  11501         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  11502         astgen.instructions.appendAssumeCapacity(.{
  11503             .tag = .extended,
  11504             .data = .{ .extended = .{
  11505                 .opcode = opcode,
  11506                 .small = @intCast(u16, trailing_len),
  11507                 .operand = payload_index,
  11508             } },
  11509         });
  11510         gz.instructions.appendAssumeCapacity(new_index);
  11511         return indexToRef(new_index);
  11512     }
  11513 
  11514     fn addUnTok(
  11515         gz: *GenZir,
  11516         tag: Zir.Inst.Tag,
  11517         operand: Zir.Inst.Ref,
  11518         /// Absolute token index. This function does the conversion to Decl offset.
  11519         abs_tok_index: Ast.TokenIndex,
  11520     ) !Zir.Inst.Ref {
  11521         assert(operand != .none);
  11522         return gz.add(.{
  11523             .tag = tag,
  11524             .data = .{ .un_tok = .{
  11525                 .operand = operand,
  11526                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  11527             } },
  11528         });
  11529     }
  11530 
  11531     fn makeUnTok(
  11532         gz: *GenZir,
  11533         tag: Zir.Inst.Tag,
  11534         operand: Zir.Inst.Ref,
  11535         /// Absolute token index. This function does the conversion to Decl offset.
  11536         abs_tok_index: Ast.TokenIndex,
  11537     ) !Zir.Inst.Index {
  11538         const astgen = gz.astgen;
  11539         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  11540         assert(operand != .none);
  11541         try astgen.instructions.append(astgen.gpa, .{
  11542             .tag = tag,
  11543             .data = .{ .un_tok = .{
  11544                 .operand = operand,
  11545                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  11546             } },
  11547         });
  11548         return new_index;
  11549     }
  11550 
  11551     fn addStrTok(
  11552         gz: *GenZir,
  11553         tag: Zir.Inst.Tag,
  11554         str_index: u32,
  11555         /// Absolute token index. This function does the conversion to Decl offset.
  11556         abs_tok_index: Ast.TokenIndex,
  11557     ) !Zir.Inst.Ref {
  11558         return gz.add(.{
  11559             .tag = tag,
  11560             .data = .{ .str_tok = .{
  11561                 .start = str_index,
  11562                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  11563             } },
  11564         });
  11565     }
  11566 
  11567     fn addSaveErrRetIndex(
  11568         gz: *GenZir,
  11569         cond: union(enum) {
  11570             always: void,
  11571             if_of_error_type: Zir.Inst.Ref,
  11572         },
  11573     ) !Zir.Inst.Index {
  11574         return gz.addAsIndex(.{
  11575             .tag = .save_err_ret_index,
  11576             .data = .{ .save_err_ret_index = .{
  11577                 .operand = if (cond == .if_of_error_type) cond.if_of_error_type else .none,
  11578             } },
  11579         });
  11580     }
  11581 
  11582     const BranchTarget = union(enum) {
  11583         ret,
  11584         block: Zir.Inst.Index,
  11585     };
  11586 
  11587     fn addRestoreErrRetIndex(
  11588         gz: *GenZir,
  11589         bt: BranchTarget,
  11590         cond: union(enum) {
  11591             always: void,
  11592             if_non_error: Zir.Inst.Ref,
  11593         },
  11594     ) !Zir.Inst.Index {
  11595         return gz.addAsIndex(.{
  11596             .tag = .restore_err_ret_index,
  11597             .data = .{ .restore_err_ret_index = .{
  11598                 .block = switch (bt) {
  11599                     .ret => .none,
  11600                     .block => |b| Zir.indexToRef(b),
  11601                 },
  11602                 .operand = if (cond == .if_non_error) cond.if_non_error else .none,
  11603             } },
  11604         });
  11605     }
  11606 
  11607     fn addBreak(
  11608         gz: *GenZir,
  11609         tag: Zir.Inst.Tag,
  11610         break_block: Zir.Inst.Index,
  11611         operand: Zir.Inst.Ref,
  11612     ) !Zir.Inst.Index {
  11613         return gz.addAsIndex(.{
  11614             .tag = tag,
  11615             .data = .{ .@"break" = .{
  11616                 .block_inst = break_block,
  11617                 .operand = operand,
  11618             } },
  11619         });
  11620     }
  11621 
  11622     fn makeBreak(
  11623         gz: *GenZir,
  11624         tag: Zir.Inst.Tag,
  11625         break_block: Zir.Inst.Index,
  11626         operand: Zir.Inst.Ref,
  11627     ) !Zir.Inst.Index {
  11628         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  11629         try gz.astgen.instructions.append(gz.astgen.gpa, .{
  11630             .tag = tag,
  11631             .data = .{ .@"break" = .{
  11632                 .block_inst = break_block,
  11633                 .operand = operand,
  11634             } },
  11635         });
  11636         return new_index;
  11637     }
  11638 
  11639     fn addBin(
  11640         gz: *GenZir,
  11641         tag: Zir.Inst.Tag,
  11642         lhs: Zir.Inst.Ref,
  11643         rhs: Zir.Inst.Ref,
  11644     ) !Zir.Inst.Ref {
  11645         assert(lhs != .none);
  11646         assert(rhs != .none);
  11647         return gz.add(.{
  11648             .tag = tag,
  11649             .data = .{ .bin = .{
  11650                 .lhs = lhs,
  11651                 .rhs = rhs,
  11652             } },
  11653         });
  11654     }
  11655 
  11656     fn addDefer(gz: *GenZir, index: u32, len: u32) !void {
  11657         _ = try gz.add(.{
  11658             .tag = .@"defer",
  11659             .data = .{ .@"defer" = .{
  11660                 .index = index,
  11661                 .len = len,
  11662             } },
  11663         });
  11664     }
  11665 
  11666     fn addDecl(
  11667         gz: *GenZir,
  11668         tag: Zir.Inst.Tag,
  11669         decl_index: u32,
  11670         src_node: Ast.Node.Index,
  11671     ) !Zir.Inst.Ref {
  11672         return gz.add(.{
  11673             .tag = tag,
  11674             .data = .{ .pl_node = .{
  11675                 .src_node = gz.nodeIndexToRelative(src_node),
  11676                 .payload_index = decl_index,
  11677             } },
  11678         });
  11679     }
  11680 
  11681     fn addNode(
  11682         gz: *GenZir,
  11683         tag: Zir.Inst.Tag,
  11684         /// Absolute node index. This function does the conversion to offset from Decl.
  11685         src_node: Ast.Node.Index,
  11686     ) !Zir.Inst.Ref {
  11687         return gz.add(.{
  11688             .tag = tag,
  11689             .data = .{ .node = gz.nodeIndexToRelative(src_node) },
  11690         });
  11691     }
  11692 
  11693     fn addInstNode(
  11694         gz: *GenZir,
  11695         tag: Zir.Inst.Tag,
  11696         inst: Zir.Inst.Index,
  11697         /// Absolute node index. This function does the conversion to offset from Decl.
  11698         src_node: Ast.Node.Index,
  11699     ) !Zir.Inst.Ref {
  11700         return gz.add(.{
  11701             .tag = tag,
  11702             .data = .{ .inst_node = .{
  11703                 .inst = inst,
  11704                 .src_node = gz.nodeIndexToRelative(src_node),
  11705             } },
  11706         });
  11707     }
  11708 
  11709     fn addNodeExtended(
  11710         gz: *GenZir,
  11711         opcode: Zir.Inst.Extended,
  11712         /// Absolute node index. This function does the conversion to offset from Decl.
  11713         src_node: Ast.Node.Index,
  11714     ) !Zir.Inst.Ref {
  11715         return gz.add(.{
  11716             .tag = .extended,
  11717             .data = .{ .extended = .{
  11718                 .opcode = opcode,
  11719                 .small = undefined,
  11720                 .operand = @bitCast(u32, gz.nodeIndexToRelative(src_node)),
  11721             } },
  11722         });
  11723     }
  11724 
  11725     fn addAllocExtended(
  11726         gz: *GenZir,
  11727         args: struct {
  11728             /// Absolute node index. This function does the conversion to offset from Decl.
  11729             node: Ast.Node.Index,
  11730             type_inst: Zir.Inst.Ref,
  11731             align_inst: Zir.Inst.Ref,
  11732             is_const: bool,
  11733             is_comptime: bool,
  11734         },
  11735     ) !Zir.Inst.Ref {
  11736         const astgen = gz.astgen;
  11737         const gpa = astgen.gpa;
  11738 
  11739         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  11740         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  11741         try astgen.extra.ensureUnusedCapacity(
  11742             gpa,
  11743             @typeInfo(Zir.Inst.AllocExtended).Struct.fields.len +
  11744                 @as(usize, @boolToInt(args.type_inst != .none)) +
  11745                 @as(usize, @boolToInt(args.align_inst != .none)),
  11746         );
  11747         const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.AllocExtended{
  11748             .src_node = gz.nodeIndexToRelative(args.node),
  11749         });
  11750         if (args.type_inst != .none) {
  11751             astgen.extra.appendAssumeCapacity(@enumToInt(args.type_inst));
  11752         }
  11753         if (args.align_inst != .none) {
  11754             astgen.extra.appendAssumeCapacity(@enumToInt(args.align_inst));
  11755         }
  11756 
  11757         const has_type: u4 = @boolToInt(args.type_inst != .none);
  11758         const has_align: u4 = @boolToInt(args.align_inst != .none);
  11759         const is_const: u4 = @boolToInt(args.is_const);
  11760         const is_comptime: u4 = @boolToInt(args.is_comptime);
  11761         const small: u16 = has_type | (has_align << 1) | (is_const << 2) | (is_comptime << 3);
  11762 
  11763         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  11764         astgen.instructions.appendAssumeCapacity(.{
  11765             .tag = .extended,
  11766             .data = .{ .extended = .{
  11767                 .opcode = .alloc,
  11768                 .small = small,
  11769                 .operand = payload_index,
  11770             } },
  11771         });
  11772         gz.instructions.appendAssumeCapacity(new_index);
  11773         return indexToRef(new_index);
  11774     }
  11775 
  11776     fn addAsm(
  11777         gz: *GenZir,
  11778         args: struct {
  11779             tag: Zir.Inst.Extended,
  11780             /// Absolute node index. This function does the conversion to offset from Decl.
  11781             node: Ast.Node.Index,
  11782             asm_source: u32,
  11783             output_type_bits: u32,
  11784             is_volatile: bool,
  11785             outputs: []const Zir.Inst.Asm.Output,
  11786             inputs: []const Zir.Inst.Asm.Input,
  11787             clobbers: []const u32,
  11788         },
  11789     ) !Zir.Inst.Ref {
  11790         const astgen = gz.astgen;
  11791         const gpa = astgen.gpa;
  11792 
  11793         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  11794         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  11795         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Asm).Struct.fields.len +
  11796             args.outputs.len * @typeInfo(Zir.Inst.Asm.Output).Struct.fields.len +
  11797             args.inputs.len * @typeInfo(Zir.Inst.Asm.Input).Struct.fields.len +
  11798             args.clobbers.len);
  11799 
  11800         const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Asm{
  11801             .src_node = gz.nodeIndexToRelative(args.node),
  11802             .asm_source = args.asm_source,
  11803             .output_type_bits = args.output_type_bits,
  11804         });
  11805         for (args.outputs) |output| {
  11806             _ = gz.astgen.addExtraAssumeCapacity(output);
  11807         }
  11808         for (args.inputs) |input| {
  11809             _ = gz.astgen.addExtraAssumeCapacity(input);
  11810         }
  11811         gz.astgen.extra.appendSliceAssumeCapacity(args.clobbers);
  11812 
  11813         //  * 0b00000000_000XXXXX - `outputs_len`.
  11814         //  * 0b000000XX_XXX00000 - `inputs_len`.
  11815         //  * 0b0XXXXX00_00000000 - `clobbers_len`.
  11816         //  * 0bX0000000_00000000 - is volatile
  11817         const small: u16 = @intCast(u16, args.outputs.len) |
  11818             @intCast(u16, args.inputs.len << 5) |
  11819             @intCast(u16, args.clobbers.len << 10) |
  11820             (@as(u16, @boolToInt(args.is_volatile)) << 15);
  11821 
  11822         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  11823         astgen.instructions.appendAssumeCapacity(.{
  11824             .tag = .extended,
  11825             .data = .{ .extended = .{
  11826                 .opcode = args.tag,
  11827                 .small = small,
  11828                 .operand = payload_index,
  11829             } },
  11830         });
  11831         gz.instructions.appendAssumeCapacity(new_index);
  11832         return indexToRef(new_index);
  11833     }
  11834 
  11835     /// Note that this returns a `Zir.Inst.Index` not a ref.
  11836     /// Does *not* append the block instruction to the scope.
  11837     /// Leaves the `payload_index` field undefined.
  11838     fn makeBlockInst(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
  11839         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  11840         const gpa = gz.astgen.gpa;
  11841         try gz.astgen.instructions.append(gpa, .{
  11842             .tag = tag,
  11843             .data = .{ .pl_node = .{
  11844                 .src_node = gz.nodeIndexToRelative(node),
  11845                 .payload_index = undefined,
  11846             } },
  11847         });
  11848         return new_index;
  11849     }
  11850 
  11851     /// Note that this returns a `Zir.Inst.Index` not a ref.
  11852     /// Leaves the `payload_index` field undefined.
  11853     fn addCondBr(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
  11854         const gpa = gz.astgen.gpa;
  11855         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  11856         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  11857         try gz.astgen.instructions.append(gpa, .{
  11858             .tag = tag,
  11859             .data = .{ .pl_node = .{
  11860                 .src_node = gz.nodeIndexToRelative(node),
  11861                 .payload_index = undefined,
  11862             } },
  11863         });
  11864         gz.instructions.appendAssumeCapacity(new_index);
  11865         return new_index;
  11866     }
  11867 
  11868     fn setStruct(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  11869         src_node: Ast.Node.Index,
  11870         fields_len: u32,
  11871         decls_len: u32,
  11872         backing_int_ref: Zir.Inst.Ref,
  11873         backing_int_body_len: u32,
  11874         layout: std.builtin.Type.ContainerLayout,
  11875         known_non_opv: bool,
  11876         known_comptime_only: bool,
  11877         is_tuple: bool,
  11878     }) !void {
  11879         const astgen = gz.astgen;
  11880         const gpa = astgen.gpa;
  11881 
  11882         try astgen.extra.ensureUnusedCapacity(gpa, 6);
  11883         const payload_index = @intCast(u32, astgen.extra.items.len);
  11884 
  11885         if (args.src_node != 0) {
  11886             const node_offset = gz.nodeIndexToRelative(args.src_node);
  11887             astgen.extra.appendAssumeCapacity(@bitCast(u32, node_offset));
  11888         }
  11889         if (args.fields_len != 0) {
  11890             astgen.extra.appendAssumeCapacity(args.fields_len);
  11891         }
  11892         if (args.decls_len != 0) {
  11893             astgen.extra.appendAssumeCapacity(args.decls_len);
  11894         }
  11895         if (args.backing_int_ref != .none) {
  11896             astgen.extra.appendAssumeCapacity(args.backing_int_body_len);
  11897             if (args.backing_int_body_len == 0) {
  11898                 astgen.extra.appendAssumeCapacity(@enumToInt(args.backing_int_ref));
  11899             }
  11900         }
  11901         astgen.instructions.set(inst, .{
  11902             .tag = .extended,
  11903             .data = .{ .extended = .{
  11904                 .opcode = .struct_decl,
  11905                 .small = @bitCast(u16, Zir.Inst.StructDecl.Small{
  11906                     .has_src_node = args.src_node != 0,
  11907                     .has_fields_len = args.fields_len != 0,
  11908                     .has_decls_len = args.decls_len != 0,
  11909                     .has_backing_int = args.backing_int_ref != .none,
  11910                     .known_non_opv = args.known_non_opv,
  11911                     .known_comptime_only = args.known_comptime_only,
  11912                     .is_tuple = args.is_tuple,
  11913                     .name_strategy = gz.anon_name_strategy,
  11914                     .layout = args.layout,
  11915                 }),
  11916                 .operand = payload_index,
  11917             } },
  11918         });
  11919     }
  11920 
  11921     fn setUnion(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  11922         src_node: Ast.Node.Index,
  11923         tag_type: Zir.Inst.Ref,
  11924         body_len: u32,
  11925         fields_len: u32,
  11926         decls_len: u32,
  11927         layout: std.builtin.Type.ContainerLayout,
  11928         auto_enum_tag: bool,
  11929     }) !void {
  11930         const astgen = gz.astgen;
  11931         const gpa = astgen.gpa;
  11932 
  11933         try astgen.extra.ensureUnusedCapacity(gpa, 5);
  11934         const payload_index = @intCast(u32, astgen.extra.items.len);
  11935 
  11936         if (args.src_node != 0) {
  11937             const node_offset = gz.nodeIndexToRelative(args.src_node);
  11938             astgen.extra.appendAssumeCapacity(@bitCast(u32, node_offset));
  11939         }
  11940         if (args.tag_type != .none) {
  11941             astgen.extra.appendAssumeCapacity(@enumToInt(args.tag_type));
  11942         }
  11943         if (args.body_len != 0) {
  11944             astgen.extra.appendAssumeCapacity(args.body_len);
  11945         }
  11946         if (args.fields_len != 0) {
  11947             astgen.extra.appendAssumeCapacity(args.fields_len);
  11948         }
  11949         if (args.decls_len != 0) {
  11950             astgen.extra.appendAssumeCapacity(args.decls_len);
  11951         }
  11952         astgen.instructions.set(inst, .{
  11953             .tag = .extended,
  11954             .data = .{ .extended = .{
  11955                 .opcode = .union_decl,
  11956                 .small = @bitCast(u16, Zir.Inst.UnionDecl.Small{
  11957                     .has_src_node = args.src_node != 0,
  11958                     .has_tag_type = args.tag_type != .none,
  11959                     .has_body_len = args.body_len != 0,
  11960                     .has_fields_len = args.fields_len != 0,
  11961                     .has_decls_len = args.decls_len != 0,
  11962                     .name_strategy = gz.anon_name_strategy,
  11963                     .layout = args.layout,
  11964                     .auto_enum_tag = args.auto_enum_tag,
  11965                 }),
  11966                 .operand = payload_index,
  11967             } },
  11968         });
  11969     }
  11970 
  11971     fn setEnum(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  11972         src_node: Ast.Node.Index,
  11973         tag_type: Zir.Inst.Ref,
  11974         body_len: u32,
  11975         fields_len: u32,
  11976         decls_len: u32,
  11977         nonexhaustive: bool,
  11978     }) !void {
  11979         const astgen = gz.astgen;
  11980         const gpa = astgen.gpa;
  11981 
  11982         try astgen.extra.ensureUnusedCapacity(gpa, 5);
  11983         const payload_index = @intCast(u32, astgen.extra.items.len);
  11984 
  11985         if (args.src_node != 0) {
  11986             const node_offset = gz.nodeIndexToRelative(args.src_node);
  11987             astgen.extra.appendAssumeCapacity(@bitCast(u32, node_offset));
  11988         }
  11989         if (args.tag_type != .none) {
  11990             astgen.extra.appendAssumeCapacity(@enumToInt(args.tag_type));
  11991         }
  11992         if (args.body_len != 0) {
  11993             astgen.extra.appendAssumeCapacity(args.body_len);
  11994         }
  11995         if (args.fields_len != 0) {
  11996             astgen.extra.appendAssumeCapacity(args.fields_len);
  11997         }
  11998         if (args.decls_len != 0) {
  11999             astgen.extra.appendAssumeCapacity(args.decls_len);
  12000         }
  12001         astgen.instructions.set(inst, .{
  12002             .tag = .extended,
  12003             .data = .{ .extended = .{
  12004                 .opcode = .enum_decl,
  12005                 .small = @bitCast(u16, Zir.Inst.EnumDecl.Small{
  12006                     .has_src_node = args.src_node != 0,
  12007                     .has_tag_type = args.tag_type != .none,
  12008                     .has_body_len = args.body_len != 0,
  12009                     .has_fields_len = args.fields_len != 0,
  12010                     .has_decls_len = args.decls_len != 0,
  12011                     .name_strategy = gz.anon_name_strategy,
  12012                     .nonexhaustive = args.nonexhaustive,
  12013                 }),
  12014                 .operand = payload_index,
  12015             } },
  12016         });
  12017     }
  12018 
  12019     fn setOpaque(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  12020         src_node: Ast.Node.Index,
  12021         decls_len: u32,
  12022     }) !void {
  12023         const astgen = gz.astgen;
  12024         const gpa = astgen.gpa;
  12025 
  12026         try astgen.extra.ensureUnusedCapacity(gpa, 2);
  12027         const payload_index = @intCast(u32, astgen.extra.items.len);
  12028 
  12029         if (args.src_node != 0) {
  12030             const node_offset = gz.nodeIndexToRelative(args.src_node);
  12031             astgen.extra.appendAssumeCapacity(@bitCast(u32, node_offset));
  12032         }
  12033         if (args.decls_len != 0) {
  12034             astgen.extra.appendAssumeCapacity(args.decls_len);
  12035         }
  12036         astgen.instructions.set(inst, .{
  12037             .tag = .extended,
  12038             .data = .{ .extended = .{
  12039                 .opcode = .opaque_decl,
  12040                 .small = @bitCast(u16, Zir.Inst.OpaqueDecl.Small{
  12041                     .has_src_node = args.src_node != 0,
  12042                     .has_decls_len = args.decls_len != 0,
  12043                     .name_strategy = gz.anon_name_strategy,
  12044                 }),
  12045                 .operand = payload_index,
  12046             } },
  12047         });
  12048     }
  12049 
  12050     fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref {
  12051         return indexToRef(try gz.addAsIndex(inst));
  12052     }
  12053 
  12054     fn addAsIndex(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Index {
  12055         const gpa = gz.astgen.gpa;
  12056         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12057         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12058 
  12059         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  12060         gz.astgen.instructions.appendAssumeCapacity(inst);
  12061         gz.instructions.appendAssumeCapacity(new_index);
  12062         return new_index;
  12063     }
  12064 
  12065     fn reserveInstructionIndex(gz: *GenZir) !Zir.Inst.Index {
  12066         const gpa = gz.astgen.gpa;
  12067         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  12068         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  12069 
  12070         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  12071         gz.astgen.instructions.len += 1;
  12072         gz.instructions.appendAssumeCapacity(new_index);
  12073         return new_index;
  12074     }
  12075 
  12076     fn addRet(gz: *GenZir, ri: ResultInfo, operand: Zir.Inst.Ref, node: Ast.Node.Index) !void {
  12077         switch (ri.rl) {
  12078             .ptr => |ptr_res| _ = try gz.addUnNode(.ret_load, ptr_res.inst, node),
  12079             .ty => _ = try gz.addUnNode(.ret_node, operand, node),
  12080             else => unreachable,
  12081         }
  12082     }
  12083 
  12084     fn addNamespaceCaptures(gz: *GenZir, namespace: *Scope.Namespace) !void {
  12085         if (namespace.captures.count() > 0) {
  12086             try gz.instructions.ensureUnusedCapacity(gz.astgen.gpa, namespace.captures.count());
  12087             for (namespace.captures.values()) |capture| {
  12088                 gz.instructions.appendAssumeCapacity(capture);
  12089             }
  12090         }
  12091     }
  12092 
  12093     fn addDbgVar(gz: *GenZir, tag: Zir.Inst.Tag, name: u32, inst: Zir.Inst.Ref) !void {
  12094         if (gz.force_comptime) return;
  12095 
  12096         _ = try gz.add(.{ .tag = tag, .data = .{
  12097             .str_op = .{
  12098                 .str = name,
  12099                 .operand = inst,
  12100             },
  12101         } });
  12102     }
  12103 
  12104     fn addDbgBlockBegin(gz: *GenZir) !void {
  12105         if (gz.force_comptime) return;
  12106 
  12107         _ = try gz.add(.{ .tag = .dbg_block_begin, .data = undefined });
  12108     }
  12109 
  12110     fn addDbgBlockEnd(gz: *GenZir) !void {
  12111         if (gz.force_comptime) return;
  12112         const gpa = gz.astgen.gpa;
  12113 
  12114         const tags = gz.astgen.instructions.items(.tag);
  12115         const last_inst = gz.instructions.items[gz.instructions.items.len - 1];
  12116         // remove dbg_block_begin immediately followed by dbg_block_end
  12117         if (tags[last_inst] == .dbg_block_begin) {
  12118             _ = gz.instructions.pop();
  12119             return;
  12120         }
  12121 
  12122         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  12123         try gz.astgen.instructions.append(gpa, .{ .tag = .dbg_block_end, .data = undefined });
  12124         try gz.instructions.insert(gpa, gz.instructions.items.len - 1, new_index);
  12125     }
  12126 };
  12127 
  12128 /// This can only be for short-lived references; the memory becomes invalidated
  12129 /// when another string is added.
  12130 fn nullTerminatedString(astgen: AstGen, index: usize) [*:0]const u8 {
  12131     return @ptrCast([*:0]const u8, astgen.string_bytes.items.ptr) + index;
  12132 }
  12133 
  12134 /// Local variables shadowing detection, including function parameters.
  12135 fn detectLocalShadowing(
  12136     astgen: *AstGen,
  12137     scope: *Scope,
  12138     ident_name: u32,
  12139     name_token: Ast.TokenIndex,
  12140     token_bytes: []const u8,
  12141     id_cat: Scope.IdCat,
  12142 ) !void {
  12143     const gpa = astgen.gpa;
  12144     if (token_bytes[0] != '@' and isPrimitive(token_bytes)) {
  12145         return astgen.failTokNotes(name_token, "name shadows primitive '{s}'", .{
  12146             token_bytes,
  12147         }, &[_]u32{
  12148             try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{
  12149                 token_bytes,
  12150             }),
  12151         });
  12152     }
  12153 
  12154     var s = scope;
  12155     var outer_scope = false;
  12156     while (true) switch (s.tag) {
  12157         .local_val => {
  12158             const local_val = s.cast(Scope.LocalVal).?;
  12159             if (local_val.name == ident_name) {
  12160                 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
  12161                 const name = try gpa.dupe(u8, name_slice);
  12162                 defer gpa.free(name);
  12163                 if (outer_scope) {
  12164                     return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{
  12165                         @tagName(id_cat), name, @tagName(local_val.id_cat),
  12166                     }, &[_]u32{
  12167                         try astgen.errNoteTok(
  12168                             local_val.token_src,
  12169                             "previous declaration here",
  12170                             .{},
  12171                         ),
  12172                     });
  12173                 }
  12174                 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{
  12175                     @tagName(local_val.id_cat), name,
  12176                 }, &[_]u32{
  12177                     try astgen.errNoteTok(
  12178                         local_val.token_src,
  12179                         "previous declaration here",
  12180                         .{},
  12181                     ),
  12182                 });
  12183             }
  12184             s = local_val.parent;
  12185         },
  12186         .local_ptr => {
  12187             const local_ptr = s.cast(Scope.LocalPtr).?;
  12188             if (local_ptr.name == ident_name) {
  12189                 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
  12190                 const name = try gpa.dupe(u8, name_slice);
  12191                 defer gpa.free(name);
  12192                 if (outer_scope) {
  12193                     return astgen.failTokNotes(name_token, "{s} '{s}' shadows {s} from outer scope", .{
  12194                         @tagName(id_cat), name, @tagName(local_ptr.id_cat),
  12195                     }, &[_]u32{
  12196                         try astgen.errNoteTok(
  12197                             local_ptr.token_src,
  12198                             "previous declaration here",
  12199                             .{},
  12200                         ),
  12201                     });
  12202                 }
  12203                 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{
  12204                     @tagName(local_ptr.id_cat), name,
  12205                 }, &[_]u32{
  12206                     try astgen.errNoteTok(
  12207                         local_ptr.token_src,
  12208                         "previous declaration here",
  12209                         .{},
  12210                     ),
  12211                 });
  12212             }
  12213             s = local_ptr.parent;
  12214         },
  12215         .namespace => {
  12216             outer_scope = true;
  12217             const ns = s.cast(Scope.Namespace).?;
  12218             const decl_node = ns.decls.get(ident_name) orelse {
  12219                 s = ns.parent;
  12220                 continue;
  12221             };
  12222             const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
  12223             const name = try gpa.dupe(u8, name_slice);
  12224             defer gpa.free(name);
  12225             return astgen.failTokNotes(name_token, "{s} shadows declaration of '{s}'", .{
  12226                 @tagName(id_cat), name,
  12227             }, &[_]u32{
  12228                 try astgen.errNoteNode(decl_node, "declared here", .{}),
  12229             });
  12230         },
  12231         .gen_zir => {
  12232             s = s.cast(GenZir).?.parent;
  12233             outer_scope = true;
  12234         },
  12235         .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
  12236         .top => break,
  12237     };
  12238 }
  12239 
  12240 /// Advances the source cursor to the main token of `node` if not in comptime scope.
  12241 /// Usually paired with `emitDbgStmt`.
  12242 fn maybeAdvanceSourceCursorToMainToken(gz: *GenZir, node: Ast.Node.Index) void {
  12243     if (gz.force_comptime) return;
  12244 
  12245     const tree = gz.astgen.tree;
  12246     const token_starts = tree.tokens.items(.start);
  12247     const main_tokens = tree.nodes.items(.main_token);
  12248     const node_start = token_starts[main_tokens[node]];
  12249     gz.astgen.advanceSourceCursor(node_start);
  12250 }
  12251 
  12252 /// Advances the source cursor to the beginning of `node`.
  12253 fn advanceSourceCursorToNode(astgen: *AstGen, node: Ast.Node.Index) void {
  12254     const tree = astgen.tree;
  12255     const token_starts = tree.tokens.items(.start);
  12256     const node_start = token_starts[tree.firstToken(node)];
  12257     astgen.advanceSourceCursor(node_start);
  12258 }
  12259 
  12260 /// Advances the source cursor to an absolute byte offset `end` in the file.
  12261 fn advanceSourceCursor(astgen: *AstGen, end: usize) void {
  12262     const source = astgen.tree.source;
  12263     var i = astgen.source_offset;
  12264     var line = astgen.source_line;
  12265     var column = astgen.source_column;
  12266     assert(i <= end);
  12267     while (i < end) : (i += 1) {
  12268         if (source[i] == '\n') {
  12269             line += 1;
  12270             column = 0;
  12271         } else {
  12272             column += 1;
  12273         }
  12274     }
  12275     astgen.source_offset = i;
  12276     astgen.source_line = line;
  12277     astgen.source_column = column;
  12278 }
  12279 
  12280 fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.Node.Index) !u32 {
  12281     const gpa = astgen.gpa;
  12282     const tree = astgen.tree;
  12283     const node_tags = tree.nodes.items(.tag);
  12284     const main_tokens = tree.nodes.items(.main_token);
  12285     const token_tags = tree.tokens.items(.tag);
  12286     var decl_count: u32 = 0;
  12287     for (members) |member_node| {
  12288         const name_token = switch (node_tags[member_node]) {
  12289             .fn_proto_simple,
  12290             .fn_proto_multi,
  12291             .fn_proto_one,
  12292             .fn_proto,
  12293             .global_var_decl,
  12294             .local_var_decl,
  12295             .simple_var_decl,
  12296             .aligned_var_decl,
  12297             => blk: {
  12298                 decl_count += 1;
  12299                 break :blk main_tokens[member_node] + 1;
  12300             },
  12301 
  12302             .fn_decl => blk: {
  12303                 decl_count += 1;
  12304                 const ident = main_tokens[member_node] + 1;
  12305                 if (token_tags[ident] != .identifier) {
  12306                     switch (astgen.failNode(member_node, "missing function name", .{})) {
  12307                         error.AnalysisFail => continue,
  12308                         error.OutOfMemory => return error.OutOfMemory,
  12309                     }
  12310                 }
  12311                 break :blk ident;
  12312             },
  12313 
  12314             .@"comptime", .@"usingnamespace", .test_decl => {
  12315                 decl_count += 1;
  12316                 continue;
  12317             },
  12318 
  12319             else => continue,
  12320         };
  12321 
  12322         const token_bytes = astgen.tree.tokenSlice(name_token);
  12323         if (token_bytes[0] != '@' and isPrimitive(token_bytes)) {
  12324             switch (astgen.failTokNotes(name_token, "name shadows primitive '{s}'", .{
  12325                 token_bytes,
  12326             }, &[_]u32{
  12327                 try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{
  12328                     token_bytes,
  12329                 }),
  12330             })) {
  12331                 error.AnalysisFail => continue,
  12332                 error.OutOfMemory => return error.OutOfMemory,
  12333             }
  12334         }
  12335 
  12336         const name_str_index = try astgen.identAsString(name_token);
  12337         const gop = try namespace.decls.getOrPut(gpa, name_str_index);
  12338         if (gop.found_existing) {
  12339             const name = try gpa.dupe(u8, mem.span(astgen.nullTerminatedString(name_str_index)));
  12340             defer gpa.free(name);
  12341             switch (astgen.failNodeNotes(member_node, "redeclaration of '{s}'", .{
  12342                 name,
  12343             }, &[_]u32{
  12344                 try astgen.errNoteNode(gop.value_ptr.*, "other declaration here", .{}),
  12345             })) {
  12346                 error.AnalysisFail => continue,
  12347                 error.OutOfMemory => return error.OutOfMemory,
  12348             }
  12349         }
  12350 
  12351         var s = namespace.parent;
  12352         while (true) switch (s.tag) {
  12353             .local_val => {
  12354                 const local_val = s.cast(Scope.LocalVal).?;
  12355                 if (local_val.name == name_str_index) {
  12356                     return astgen.failTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{
  12357                         token_bytes, @tagName(local_val.id_cat),
  12358                     }, &[_]u32{
  12359                         try astgen.errNoteTok(
  12360                             local_val.token_src,
  12361                             "previous declaration here",
  12362                             .{},
  12363                         ),
  12364                     });
  12365                 }
  12366                 s = local_val.parent;
  12367             },
  12368             .local_ptr => {
  12369                 const local_ptr = s.cast(Scope.LocalPtr).?;
  12370                 if (local_ptr.name == name_str_index) {
  12371                     return astgen.failTokNotes(name_token, "declaration '{s}' shadows {s} from outer scope", .{
  12372                         token_bytes, @tagName(local_ptr.id_cat),
  12373                     }, &[_]u32{
  12374                         try astgen.errNoteTok(
  12375                             local_ptr.token_src,
  12376                             "previous declaration here",
  12377                             .{},
  12378                         ),
  12379                     });
  12380                 }
  12381                 s = local_ptr.parent;
  12382             },
  12383             .namespace => s = s.cast(Scope.Namespace).?.parent,
  12384             .gen_zir => s = s.cast(GenZir).?.parent,
  12385             .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
  12386             .top => break,
  12387         };
  12388         gop.value_ptr.* = member_node;
  12389     }
  12390     return decl_count;
  12391 }
  12392 
  12393 fn isInferred(astgen: *AstGen, ref: Zir.Inst.Ref) bool {
  12394     const inst = refToIndex(ref) orelse return false;
  12395     const zir_tags = astgen.instructions.items(.tag);
  12396     return switch (zir_tags[inst]) {
  12397         .alloc_inferred,
  12398         .alloc_inferred_mut,
  12399         .alloc_inferred_comptime,
  12400         .alloc_inferred_comptime_mut,
  12401         => true,
  12402 
  12403         .extended => {
  12404             const zir_data = astgen.instructions.items(.data);
  12405             if (zir_data[inst].extended.opcode != .alloc) return false;
  12406             const small = @bitCast(Zir.Inst.AllocExtended.Small, zir_data[inst].extended.small);
  12407             return !small.has_type;
  12408         },
  12409 
  12410         else => false,
  12411     };
  12412 }
  12413 
  12414 /// Assumes capacity for body has already been added. Needed capacity taking into
  12415 /// account fixups can be found with `countBodyLenAfterFixups`.
  12416 fn appendBodyWithFixups(astgen: *AstGen, body: []const Zir.Inst.Index) void {
  12417     return appendBodyWithFixupsArrayList(astgen, &astgen.extra, body);
  12418 }
  12419 
  12420 fn appendBodyWithFixupsArrayList(
  12421     astgen: *AstGen,
  12422     list: *std.ArrayListUnmanaged(u32),
  12423     body: []const Zir.Inst.Index,
  12424 ) void {
  12425     for (body) |body_inst| {
  12426         appendPossiblyRefdBodyInst(astgen, list, body_inst);
  12427     }
  12428 }
  12429 
  12430 fn appendPossiblyRefdBodyInst(
  12431     astgen: *AstGen,
  12432     list: *std.ArrayListUnmanaged(u32),
  12433     body_inst: Zir.Inst.Index,
  12434 ) void {
  12435     list.appendAssumeCapacity(body_inst);
  12436     const kv = astgen.ref_table.fetchRemove(body_inst) orelse return;
  12437     const ref_inst = kv.value;
  12438     return appendPossiblyRefdBodyInst(astgen, list, ref_inst);
  12439 }
  12440 
  12441 fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 {
  12442     var count = body.len;
  12443     for (body) |body_inst| {
  12444         var check_inst = body_inst;
  12445         while (astgen.ref_table.get(check_inst)) |ref_inst| {
  12446             count += 1;
  12447             check_inst = ref_inst;
  12448         }
  12449     }
  12450     return @intCast(u32, count);
  12451 }
  12452 
  12453 fn emitDbgStmt(gz: *GenZir, line: u32, column: u32) !void {
  12454     if (gz.force_comptime) return;
  12455 
  12456     _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
  12457         .dbg_stmt = .{
  12458             .line = line,
  12459             .column = column,
  12460         },
  12461     } });
  12462 }