zig

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

blob a0ff7a0e (446120B) - 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 Zir = @import("Zir.zig");
     14 const refToIndex = Zir.refToIndex;
     15 const indexToRef = Zir.indexToRef;
     16 const trace = @import("tracy.zig").trace;
     17 const BuiltinFn = @import("BuiltinFn.zig");
     18 
     19 gpa: Allocator,
     20 tree: *const Ast,
     21 instructions: std.MultiArrayList(Zir.Inst) = .{},
     22 extra: ArrayListUnmanaged(u32) = .{},
     23 string_bytes: ArrayListUnmanaged(u8) = .{},
     24 /// Tracks the current byte offset within the source file.
     25 /// Used to populate line deltas in the ZIR. AstGen maintains
     26 /// this "cursor" throughout the entire AST lowering process in order
     27 /// to avoid starting over the line/column scan for every declaration, which
     28 /// would be O(N^2).
     29 source_offset: u32 = 0,
     30 /// Tracks the corresponding line of `source_offset`.
     31 /// This value is absolute.
     32 source_line: u32 = 0,
     33 /// Tracks the corresponding column of `source_offset`.
     34 /// This value is absolute.
     35 source_column: u32 = 0,
     36 /// Used for temporary allocations; freed after AstGen is complete.
     37 /// The resulting ZIR code has no references to anything in this arena.
     38 arena: Allocator,
     39 string_table: std.HashMapUnmanaged(u32, void, StringIndexContext, std.hash_map.default_max_load_percentage) = .{},
     40 compile_errors: ArrayListUnmanaged(Zir.Inst.CompileErrors.Item) = .{},
     41 /// The topmost block of the current function.
     42 fn_block: ?*GenZir = null,
     43 /// Maps string table indexes to the first `@import` ZIR instruction
     44 /// that uses this string as the operand.
     45 imports: std.AutoArrayHashMapUnmanaged(u32, Ast.TokenIndex) = .{},
     46 /// Used for temporary storage when building payloads.
     47 scratch: std.ArrayListUnmanaged(u32) = .{},
     48 /// Whenever a `ref` instruction is needed, it is created and saved in this
     49 /// table instead of being immediately appended to the current block body.
     50 /// Then, when the instruction is being added to the parent block (typically from
     51 /// setBlockBody), if it has a ref_table entry, then the ref instruction is added
     52 /// there. This makes sure two properties are upheld:
     53 /// 1. All pointers to the same locals return the same address. This is required
     54 ///    to be compliant with the language specification.
     55 /// 2. `ref` instructions will dominate their uses. This is a required property
     56 ///    of ZIR.
     57 /// The key is the ref operand; the value is the ref instruction.
     58 ref_table: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},
     59 
     60 const InnerError = error{ OutOfMemory, AnalysisFail };
     61 
     62 fn addExtra(astgen: *AstGen, extra: anytype) Allocator.Error!u32 {
     63     const fields = std.meta.fields(@TypeOf(extra));
     64     try astgen.extra.ensureUnusedCapacity(astgen.gpa, fields.len);
     65     return addExtraAssumeCapacity(astgen, extra);
     66 }
     67 
     68 fn addExtraAssumeCapacity(astgen: *AstGen, extra: anytype) u32 {
     69     const fields = std.meta.fields(@TypeOf(extra));
     70     const result = @intCast(u32, astgen.extra.items.len);
     71     astgen.extra.items.len += fields.len;
     72     setExtra(astgen, result, extra);
     73     return result;
     74 }
     75 
     76 fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void {
     77     const fields = std.meta.fields(@TypeOf(extra));
     78     var i = index;
     79     inline for (fields) |field| {
     80         astgen.extra.items[i] = switch (field.field_type) {
     81             u32 => @field(extra, field.name),
     82             Zir.Inst.Ref => @enumToInt(@field(extra, field.name)),
     83             i32 => @bitCast(u32, @field(extra, field.name)),
     84             Zir.Inst.Call.Flags => @bitCast(u32, @field(extra, field.name)),
     85             Zir.Inst.BuiltinCall.Flags => @bitCast(u32, @field(extra, field.name)),
     86             Zir.Inst.SwitchBlock.Bits => @bitCast(u32, @field(extra, field.name)),
     87             Zir.Inst.FuncFancy.Bits => @bitCast(u32, @field(extra, field.name)),
     88             else => @compileError("bad field type"),
     89         };
     90         i += 1;
     91     }
     92 }
     93 
     94 fn reserveExtra(astgen: *AstGen, size: usize) Allocator.Error!u32 {
     95     const result = @intCast(u32, astgen.extra.items.len);
     96     try astgen.extra.resize(astgen.gpa, result + size);
     97     return result;
     98 }
     99 
    100 fn appendRefs(astgen: *AstGen, refs: []const Zir.Inst.Ref) !void {
    101     const coerced = @ptrCast([]const u32, refs);
    102     return astgen.extra.appendSlice(astgen.gpa, coerced);
    103 }
    104 
    105 fn appendRefsAssumeCapacity(astgen: *AstGen, refs: []const Zir.Inst.Ref) void {
    106     const coerced = @ptrCast([]const u32, refs);
    107     astgen.extra.appendSliceAssumeCapacity(coerced);
    108 }
    109 
    110 pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
    111     var arena = std.heap.ArenaAllocator.init(gpa);
    112     defer arena.deinit();
    113 
    114     var astgen: AstGen = .{
    115         .gpa = gpa,
    116         .arena = arena.allocator(),
    117         .tree = &tree,
    118     };
    119     defer astgen.deinit(gpa);
    120 
    121     // String table indexes 0, 1, 2 are reserved for special meaning.
    122     try astgen.string_bytes.appendSlice(gpa, &[_]u8{ 0, 0, 0 });
    123 
    124     // We expect at least as many ZIR instructions and extra data items
    125     // as AST nodes.
    126     try astgen.instructions.ensureTotalCapacity(gpa, tree.nodes.len);
    127 
    128     // First few indexes of extra are reserved and set at the end.
    129     const reserved_count = @typeInfo(Zir.ExtraIndex).Enum.fields.len;
    130     try astgen.extra.ensureTotalCapacity(gpa, tree.nodes.len + reserved_count);
    131     astgen.extra.items.len += reserved_count;
    132 
    133     var top_scope: Scope.Top = .{};
    134 
    135     var gz_instructions: std.ArrayListUnmanaged(Zir.Inst.Index) = .{};
    136     var gen_scope: GenZir = .{
    137         .force_comptime = true,
    138         .in_defer = false,
    139         .parent = &top_scope.base,
    140         .anon_name_strategy = .parent,
    141         .decl_node_index = 0,
    142         .decl_line = 0,
    143         .astgen = &astgen,
    144         .instructions = &gz_instructions,
    145         .instructions_top = 0,
    146     };
    147     defer gz_instructions.deinit(gpa);
    148 
    149     if (AstGen.structDeclInner(
    150         &gen_scope,
    151         &gen_scope.base,
    152         0,
    153         tree.containerDeclRoot(),
    154         .Auto,
    155     )) |struct_decl_ref| {
    156         assert(refToIndex(struct_decl_ref).? == 0);
    157     } else |err| switch (err) {
    158         error.OutOfMemory => return error.OutOfMemory,
    159         error.AnalysisFail => {}, // Handled via compile_errors below.
    160     }
    161 
    162     const err_index = @enumToInt(Zir.ExtraIndex.compile_errors);
    163     if (astgen.compile_errors.items.len == 0) {
    164         astgen.extra.items[err_index] = 0;
    165     } else {
    166         try astgen.extra.ensureUnusedCapacity(gpa, 1 + astgen.compile_errors.items.len *
    167             @typeInfo(Zir.Inst.CompileErrors.Item).Struct.fields.len);
    168 
    169         astgen.extra.items[err_index] = astgen.addExtraAssumeCapacity(Zir.Inst.CompileErrors{
    170             .items_len = @intCast(u32, astgen.compile_errors.items.len),
    171         });
    172 
    173         for (astgen.compile_errors.items) |item| {
    174             _ = astgen.addExtraAssumeCapacity(item);
    175         }
    176     }
    177 
    178     const imports_index = @enumToInt(Zir.ExtraIndex.imports);
    179     if (astgen.imports.count() == 0) {
    180         astgen.extra.items[imports_index] = 0;
    181     } else {
    182         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Imports).Struct.fields.len +
    183             astgen.imports.count() * @typeInfo(Zir.Inst.Imports.Item).Struct.fields.len);
    184 
    185         astgen.extra.items[imports_index] = astgen.addExtraAssumeCapacity(Zir.Inst.Imports{
    186             .imports_len = @intCast(u32, astgen.imports.count()),
    187         });
    188 
    189         var it = astgen.imports.iterator();
    190         while (it.next()) |entry| {
    191             _ = astgen.addExtraAssumeCapacity(Zir.Inst.Imports.Item{
    192                 .name = entry.key_ptr.*,
    193                 .token = entry.value_ptr.*,
    194             });
    195         }
    196     }
    197 
    198     return Zir{
    199         .instructions = astgen.instructions.toOwnedSlice(),
    200         .string_bytes = astgen.string_bytes.toOwnedSlice(gpa),
    201         .extra = astgen.extra.toOwnedSlice(gpa),
    202     };
    203 }
    204 
    205 pub fn deinit(astgen: *AstGen, gpa: Allocator) void {
    206     astgen.instructions.deinit(gpa);
    207     astgen.extra.deinit(gpa);
    208     astgen.string_table.deinit(gpa);
    209     astgen.string_bytes.deinit(gpa);
    210     astgen.compile_errors.deinit(gpa);
    211     astgen.imports.deinit(gpa);
    212     astgen.scratch.deinit(gpa);
    213     astgen.ref_table.deinit(gpa);
    214 }
    215 
    216 pub const ResultLoc = union(enum) {
    217     /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
    218     /// expression should be generated. The result instruction from the expression must
    219     /// be ignored.
    220     discard,
    221     /// The expression has an inferred type, and it will be evaluated as an rvalue.
    222     none,
    223     /// The expression must generate a pointer rather than a value. For example, the left hand side
    224     /// of an assignment uses this kind of result location.
    225     ref,
    226     /// The expression will be coerced into this type, but it will be evaluated as an rvalue.
    227     ty: Zir.Inst.Ref,
    228     /// Same as `ty` but it is guaranteed that Sema will additionally perform the coercion,
    229     /// so no `as` instruction needs to be emitted.
    230     coerced_ty: Zir.Inst.Ref,
    231     /// The expression must store its result into this typed pointer. The result instruction
    232     /// from the expression must be ignored.
    233     ptr: Zir.Inst.Ref,
    234     /// The expression must store its result into this allocation, which has an inferred type.
    235     /// The result instruction from the expression must be ignored.
    236     /// Always an instruction with tag `alloc_inferred`.
    237     inferred_ptr: Zir.Inst.Ref,
    238     /// There is a pointer for the expression to store its result into, however, its type
    239     /// is inferred based on peer type resolution for a `Zir.Inst.Block`.
    240     /// The result instruction from the expression must be ignored.
    241     block_ptr: *GenZir,
    242 
    243     pub const Strategy = struct {
    244         elide_store_to_block_ptr_instructions: bool,
    245         tag: Tag,
    246 
    247         pub const Tag = enum {
    248             /// Both branches will use break_void; result location is used to communicate the
    249             /// result instruction.
    250             break_void,
    251             /// Use break statements to pass the block result value, and call rvalue() at
    252             /// the end depending on rl. Also elide the store_to_block_ptr instructions
    253             /// depending on rl.
    254             break_operand,
    255         };
    256     };
    257 
    258     fn strategy(rl: ResultLoc, block_scope: *GenZir) Strategy {
    259         switch (rl) {
    260             // In this branch there will not be any store_to_block_ptr instructions.
    261             .none, .ty, .coerced_ty, .ref => return .{
    262                 .tag = .break_operand,
    263                 .elide_store_to_block_ptr_instructions = false,
    264             },
    265             .discard => return .{
    266                 .tag = .break_void,
    267                 .elide_store_to_block_ptr_instructions = false,
    268             },
    269             // The pointer got passed through to the sub-expressions, so we will use
    270             // break_void here.
    271             // In this branch there will not be any store_to_block_ptr instructions.
    272             .ptr => return .{
    273                 .tag = .break_void,
    274                 .elide_store_to_block_ptr_instructions = false,
    275             },
    276             .inferred_ptr, .block_ptr => {
    277                 if (block_scope.rvalue_rl_count == block_scope.break_count) {
    278                     // Neither prong of the if consumed the result location, so we can
    279                     // use break instructions to create an rvalue.
    280                     return .{
    281                         .tag = .break_operand,
    282                         .elide_store_to_block_ptr_instructions = true,
    283                     };
    284                 } else {
    285                     // Allow the store_to_block_ptr instructions to remain so that
    286                     // semantic analysis can turn them into bitcasts.
    287                     return .{
    288                         .tag = .break_void,
    289                         .elide_store_to_block_ptr_instructions = false,
    290                     };
    291                 }
    292             },
    293         }
    294     }
    295 
    296     /// Turns a `coerced_ty` back into a `ty`. Should be called at branch points
    297     /// such as if and switch expressions.
    298     fn br(rl: ResultLoc) ResultLoc {
    299         return switch (rl) {
    300             .coerced_ty => |ty| .{ .ty = ty },
    301             else => rl,
    302         };
    303     }
    304 };
    305 
    306 pub const align_rl: ResultLoc = .{ .ty = .u29_type };
    307 pub const coerced_align_rl: ResultLoc = .{ .coerced_ty = .u29_type };
    308 pub const bool_rl: ResultLoc = .{ .ty = .bool_type };
    309 pub const type_rl: ResultLoc = .{ .ty = .type_type };
    310 pub const coerced_type_rl: ResultLoc = .{ .coerced_ty = .type_type };
    311 
    312 fn typeExpr(gz: *GenZir, scope: *Scope, type_node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
    313     const prev_force_comptime = gz.force_comptime;
    314     gz.force_comptime = true;
    315     defer gz.force_comptime = prev_force_comptime;
    316 
    317     return expr(gz, scope, coerced_type_rl, type_node);
    318 }
    319 
    320 fn reachableTypeExpr(
    321     gz: *GenZir,
    322     scope: *Scope,
    323     type_node: Ast.Node.Index,
    324     reachable_node: Ast.Node.Index,
    325 ) InnerError!Zir.Inst.Ref {
    326     const prev_force_comptime = gz.force_comptime;
    327     gz.force_comptime = true;
    328     defer gz.force_comptime = prev_force_comptime;
    329 
    330     return reachableExpr(gz, scope, coerced_type_rl, type_node, reachable_node);
    331 }
    332 
    333 /// Same as `expr` but fails with a compile error if the result type is `noreturn`.
    334 fn reachableExpr(
    335     gz: *GenZir,
    336     scope: *Scope,
    337     rl: ResultLoc,
    338     node: Ast.Node.Index,
    339     reachable_node: Ast.Node.Index,
    340 ) InnerError!Zir.Inst.Ref {
    341     return reachableExprComptime(gz, scope, rl, node, reachable_node, false);
    342 }
    343 
    344 fn reachableExprComptime(
    345     gz: *GenZir,
    346     scope: *Scope,
    347     rl: ResultLoc,
    348     node: Ast.Node.Index,
    349     reachable_node: Ast.Node.Index,
    350     force_comptime: bool,
    351 ) InnerError!Zir.Inst.Ref {
    352     const prev_force_comptime = gz.force_comptime;
    353     gz.force_comptime = prev_force_comptime or force_comptime;
    354     defer gz.force_comptime = prev_force_comptime;
    355 
    356     const result_inst = try expr(gz, scope, rl, node);
    357     if (gz.refIsNoReturn(result_inst)) {
    358         try gz.astgen.appendErrorNodeNotes(reachable_node, "unreachable code", .{}, &[_]u32{
    359             try gz.astgen.errNoteNode(node, "control flow is diverted here", .{}),
    360         });
    361     }
    362     return result_inst;
    363 }
    364 
    365 fn lvalExpr(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
    366     const astgen = gz.astgen;
    367     const tree = astgen.tree;
    368     const node_tags = tree.nodes.items(.tag);
    369     const main_tokens = tree.nodes.items(.main_token);
    370     switch (node_tags[node]) {
    371         .root => unreachable,
    372         .@"usingnamespace" => unreachable,
    373         .test_decl => unreachable,
    374         .global_var_decl => unreachable,
    375         .local_var_decl => unreachable,
    376         .simple_var_decl => unreachable,
    377         .aligned_var_decl => unreachable,
    378         .switch_case => unreachable,
    379         .switch_case_one => unreachable,
    380         .container_field_init => unreachable,
    381         .container_field_align => unreachable,
    382         .container_field => unreachable,
    383         .asm_output => unreachable,
    384         .asm_input => unreachable,
    385 
    386         .assign,
    387         .assign_bit_and,
    388         .assign_bit_or,
    389         .assign_shl,
    390         .assign_shl_sat,
    391         .assign_shr,
    392         .assign_bit_xor,
    393         .assign_div,
    394         .assign_sub,
    395         .assign_sub_wrap,
    396         .assign_sub_sat,
    397         .assign_mod,
    398         .assign_add,
    399         .assign_add_wrap,
    400         .assign_add_sat,
    401         .assign_mul,
    402         .assign_mul_wrap,
    403         .assign_mul_sat,
    404         .add,
    405         .add_wrap,
    406         .add_sat,
    407         .sub,
    408         .sub_wrap,
    409         .sub_sat,
    410         .mul,
    411         .mul_wrap,
    412         .mul_sat,
    413         .div,
    414         .mod,
    415         .bit_and,
    416         .bit_or,
    417         .shl,
    418         .shl_sat,
    419         .shr,
    420         .bit_xor,
    421         .bang_equal,
    422         .equal_equal,
    423         .greater_than,
    424         .greater_or_equal,
    425         .less_than,
    426         .less_or_equal,
    427         .array_cat,
    428         .array_mult,
    429         .bool_and,
    430         .bool_or,
    431         .@"asm",
    432         .asm_simple,
    433         .string_literal,
    434         .integer_literal,
    435         .call,
    436         .call_comma,
    437         .async_call,
    438         .async_call_comma,
    439         .call_one,
    440         .call_one_comma,
    441         .async_call_one,
    442         .async_call_one_comma,
    443         .unreachable_literal,
    444         .@"return",
    445         .@"if",
    446         .if_simple,
    447         .@"while",
    448         .while_simple,
    449         .while_cont,
    450         .bool_not,
    451         .address_of,
    452         .float_literal,
    453         .optional_type,
    454         .block,
    455         .block_semicolon,
    456         .block_two,
    457         .block_two_semicolon,
    458         .@"break",
    459         .ptr_type_aligned,
    460         .ptr_type_sentinel,
    461         .ptr_type,
    462         .ptr_type_bit_range,
    463         .array_type,
    464         .array_type_sentinel,
    465         .enum_literal,
    466         .multiline_string_literal,
    467         .char_literal,
    468         .@"defer",
    469         .@"errdefer",
    470         .@"catch",
    471         .error_union,
    472         .merge_error_sets,
    473         .switch_range,
    474         .@"await",
    475         .bit_not,
    476         .negation,
    477         .negation_wrap,
    478         .@"resume",
    479         .@"try",
    480         .slice,
    481         .slice_open,
    482         .slice_sentinel,
    483         .array_init_one,
    484         .array_init_one_comma,
    485         .array_init_dot_two,
    486         .array_init_dot_two_comma,
    487         .array_init_dot,
    488         .array_init_dot_comma,
    489         .array_init,
    490         .array_init_comma,
    491         .struct_init_one,
    492         .struct_init_one_comma,
    493         .struct_init_dot_two,
    494         .struct_init_dot_two_comma,
    495         .struct_init_dot,
    496         .struct_init_dot_comma,
    497         .struct_init,
    498         .struct_init_comma,
    499         .@"switch",
    500         .switch_comma,
    501         .@"for",
    502         .for_simple,
    503         .@"suspend",
    504         .@"continue",
    505         .fn_proto_simple,
    506         .fn_proto_multi,
    507         .fn_proto_one,
    508         .fn_proto,
    509         .fn_decl,
    510         .anyframe_type,
    511         .anyframe_literal,
    512         .error_set_decl,
    513         .container_decl,
    514         .container_decl_trailing,
    515         .container_decl_two,
    516         .container_decl_two_trailing,
    517         .container_decl_arg,
    518         .container_decl_arg_trailing,
    519         .tagged_union,
    520         .tagged_union_trailing,
    521         .tagged_union_two,
    522         .tagged_union_two_trailing,
    523         .tagged_union_enum_tag,
    524         .tagged_union_enum_tag_trailing,
    525         .@"comptime",
    526         .@"nosuspend",
    527         .error_value,
    528         => return astgen.failNode(node, "invalid left-hand side to assignment", .{}),
    529 
    530         .builtin_call,
    531         .builtin_call_comma,
    532         .builtin_call_two,
    533         .builtin_call_two_comma,
    534         => {
    535             const builtin_token = main_tokens[node];
    536             const builtin_name = tree.tokenSlice(builtin_token);
    537             // If the builtin is an invalid name, we don't cause an error here; instead
    538             // let it pass, and the error will be "invalid builtin function" later.
    539             if (BuiltinFn.list.get(builtin_name)) |info| {
    540                 if (!info.allows_lvalue) {
    541                     return astgen.failNode(node, "invalid left-hand side to assignment", .{});
    542                 }
    543             }
    544         },
    545 
    546         // These can be assigned to.
    547         .unwrap_optional,
    548         .deref,
    549         .field_access,
    550         .array_access,
    551         .identifier,
    552         .grouped_expression,
    553         .@"orelse",
    554         => {},
    555     }
    556     return expr(gz, scope, .ref, node);
    557 }
    558 
    559 /// Turn Zig AST into untyped ZIR instructions.
    560 /// When `rl` is discard, ptr, inferred_ptr, or inferred_ptr, the
    561 /// result instruction can be used to inspect whether it is isNoReturn() but that is it,
    562 /// it must otherwise not be used.
    563 fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
    564     const astgen = gz.astgen;
    565     const tree = astgen.tree;
    566     const main_tokens = tree.nodes.items(.main_token);
    567     const token_tags = tree.tokens.items(.tag);
    568     const node_datas = tree.nodes.items(.data);
    569     const node_tags = tree.nodes.items(.tag);
    570 
    571     switch (node_tags[node]) {
    572         .root => unreachable, // Top-level declaration.
    573         .@"usingnamespace" => unreachable, // Top-level declaration.
    574         .test_decl => unreachable, // Top-level declaration.
    575         .container_field_init => unreachable, // Top-level declaration.
    576         .container_field_align => unreachable, // Top-level declaration.
    577         .container_field => unreachable, // Top-level declaration.
    578         .fn_decl => unreachable, // Top-level declaration.
    579 
    580         .global_var_decl => unreachable, // Handled in `blockExpr`.
    581         .local_var_decl => unreachable, // Handled in `blockExpr`.
    582         .simple_var_decl => unreachable, // Handled in `blockExpr`.
    583         .aligned_var_decl => unreachable, // Handled in `blockExpr`.
    584         .@"defer" => unreachable, // Handled in `blockExpr`.
    585         .@"errdefer" => unreachable, // Handled in `blockExpr`.
    586 
    587         .switch_case => unreachable, // Handled in `switchExpr`.
    588         .switch_case_one => unreachable, // Handled in `switchExpr`.
    589         .switch_range => unreachable, // Handled in `switchExpr`.
    590 
    591         .asm_output => unreachable, // Handled in `asmExpr`.
    592         .asm_input => unreachable, // Handled in `asmExpr`.
    593 
    594         .assign => {
    595             try assign(gz, scope, node);
    596             return rvalue(gz, rl, .void_value, node);
    597         },
    598 
    599         .assign_shl => {
    600             try assignShift(gz, scope, node, .shl);
    601             return rvalue(gz, rl, .void_value, node);
    602         },
    603         .assign_shl_sat => {
    604             try assignShiftSat(gz, scope, node);
    605             return rvalue(gz, rl, .void_value, node);
    606         },
    607         .assign_shr => {
    608             try assignShift(gz, scope, node, .shr);
    609             return rvalue(gz, rl, .void_value, node);
    610         },
    611 
    612         .assign_bit_and => {
    613             try assignOp(gz, scope, node, .bit_and);
    614             return rvalue(gz, rl, .void_value, node);
    615         },
    616         .assign_bit_or => {
    617             try assignOp(gz, scope, node, .bit_or);
    618             return rvalue(gz, rl, .void_value, node);
    619         },
    620         .assign_bit_xor => {
    621             try assignOp(gz, scope, node, .xor);
    622             return rvalue(gz, rl, .void_value, node);
    623         },
    624         .assign_div => {
    625             try assignOp(gz, scope, node, .div);
    626             return rvalue(gz, rl, .void_value, node);
    627         },
    628         .assign_sub => {
    629             try assignOp(gz, scope, node, .sub);
    630             return rvalue(gz, rl, .void_value, node);
    631         },
    632         .assign_sub_wrap => {
    633             try assignOp(gz, scope, node, .subwrap);
    634             return rvalue(gz, rl, .void_value, node);
    635         },
    636         .assign_sub_sat => {
    637             try assignOp(gz, scope, node, .sub_sat);
    638             return rvalue(gz, rl, .void_value, node);
    639         },
    640         .assign_mod => {
    641             try assignOp(gz, scope, node, .mod_rem);
    642             return rvalue(gz, rl, .void_value, node);
    643         },
    644         .assign_add => {
    645             try assignOp(gz, scope, node, .add);
    646             return rvalue(gz, rl, .void_value, node);
    647         },
    648         .assign_add_wrap => {
    649             try assignOp(gz, scope, node, .addwrap);
    650             return rvalue(gz, rl, .void_value, node);
    651         },
    652         .assign_add_sat => {
    653             try assignOp(gz, scope, node, .add_sat);
    654             return rvalue(gz, rl, .void_value, node);
    655         },
    656         .assign_mul => {
    657             try assignOp(gz, scope, node, .mul);
    658             return rvalue(gz, rl, .void_value, node);
    659         },
    660         .assign_mul_wrap => {
    661             try assignOp(gz, scope, node, .mulwrap);
    662             return rvalue(gz, rl, .void_value, node);
    663         },
    664         .assign_mul_sat => {
    665             try assignOp(gz, scope, node, .mul_sat);
    666             return rvalue(gz, rl, .void_value, node);
    667         },
    668 
    669         // zig fmt: off
    670         .shl => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shl),
    671         .shr => return shiftOp(gz, scope, rl, node, node_datas[node].lhs, node_datas[node].rhs, .shr),
    672 
    673         .add      => return simpleBinOp(gz, scope, rl, node, .add),
    674         .add_wrap => return simpleBinOp(gz, scope, rl, node, .addwrap),
    675         .add_sat  => return simpleBinOp(gz, scope, rl, node, .add_sat),
    676         .sub      => return simpleBinOp(gz, scope, rl, node, .sub),
    677         .sub_wrap => return simpleBinOp(gz, scope, rl, node, .subwrap),
    678         .sub_sat  => return simpleBinOp(gz, scope, rl, node, .sub_sat),
    679         .mul      => return simpleBinOp(gz, scope, rl, node, .mul),
    680         .mul_wrap => return simpleBinOp(gz, scope, rl, node, .mulwrap),
    681         .mul_sat  => return simpleBinOp(gz, scope, rl, node, .mul_sat),
    682         .div      => return simpleBinOp(gz, scope, rl, node, .div),
    683         .mod      => return simpleBinOp(gz, scope, rl, node, .mod_rem),
    684         .shl_sat  => return simpleBinOp(gz, scope, rl, node, .shl_sat),
    685 
    686         .bit_and          => return simpleBinOp(gz, scope, rl, node, .bit_and),
    687         .bit_or           => return simpleBinOp(gz, scope, rl, node, .bit_or),
    688         .bit_xor          => return simpleBinOp(gz, scope, rl, node, .xor),
    689         .bang_equal       => return simpleBinOp(gz, scope, rl, node, .cmp_neq),
    690         .equal_equal      => return simpleBinOp(gz, scope, rl, node, .cmp_eq),
    691         .greater_than     => return simpleBinOp(gz, scope, rl, node, .cmp_gt),
    692         .greater_or_equal => return simpleBinOp(gz, scope, rl, node, .cmp_gte),
    693         .less_than        => return simpleBinOp(gz, scope, rl, node, .cmp_lt),
    694         .less_or_equal    => return simpleBinOp(gz, scope, rl, node, .cmp_lte),
    695         .array_cat        => return simpleBinOp(gz, scope, rl, node, .array_cat),
    696 
    697         .array_mult => {
    698             const result = try gz.addPlNode(.array_mul, node, Zir.Inst.Bin{
    699                 .lhs = try expr(gz, scope, .none, node_datas[node].lhs),
    700                 .rhs = try comptimeExpr(gz, scope, .{ .coerced_ty = .usize_type }, node_datas[node].rhs),
    701             });
    702             return rvalue(gz, rl, result, node);
    703         },
    704 
    705         .error_union      => return simpleBinOp(gz, scope, rl, node, .error_union_type),
    706         .merge_error_sets => return simpleBinOp(gz, scope, rl, node, .merge_error_sets),
    707 
    708         .bool_and => return boolBinOp(gz, scope, rl, node, .bool_br_and),
    709         .bool_or  => return boolBinOp(gz, scope, rl, node, .bool_br_or),
    710 
    711         .bool_not => return simpleUnOp(gz, scope, rl, node, bool_rl, node_datas[node].lhs, .bool_not),
    712         .bit_not  => return simpleUnOp(gz, scope, rl, node, .none, node_datas[node].lhs, .bit_not),
    713 
    714         .negation      => return   negation(gz, scope, rl, node),
    715         .negation_wrap => return simpleUnOp(gz, scope, rl, node, .none, node_datas[node].lhs, .negate_wrap),
    716 
    717         .identifier => return identifier(gz, scope, rl, node),
    718 
    719         .asm_simple => return asmExpr(gz, scope, rl, node, tree.asmSimple(node)),
    720         .@"asm"     => return asmExpr(gz, scope, rl, node, tree.asmFull(node)),
    721 
    722         .string_literal           => return stringLiteral(gz, rl, node),
    723         .multiline_string_literal => return multilineStringLiteral(gz, rl, node),
    724 
    725         .integer_literal => return integerLiteral(gz, rl, node),
    726         // zig fmt: on
    727 
    728         .builtin_call_two, .builtin_call_two_comma => {
    729             if (node_datas[node].lhs == 0) {
    730                 const params = [_]Ast.Node.Index{};
    731                 return builtinCall(gz, scope, rl, node, &params);
    732             } else if (node_datas[node].rhs == 0) {
    733                 const params = [_]Ast.Node.Index{node_datas[node].lhs};
    734                 return builtinCall(gz, scope, rl, node, &params);
    735             } else {
    736                 const params = [_]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
    737                 return builtinCall(gz, scope, rl, node, &params);
    738             }
    739         },
    740         .builtin_call, .builtin_call_comma => {
    741             const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
    742             return builtinCall(gz, scope, rl, node, params);
    743         },
    744 
    745         .call_one, .call_one_comma, .async_call_one, .async_call_one_comma => {
    746             var params: [1]Ast.Node.Index = undefined;
    747             return callExpr(gz, scope, rl, node, tree.callOne(&params, node));
    748         },
    749         .call, .call_comma, .async_call, .async_call_comma => {
    750             return callExpr(gz, scope, rl, node, tree.callFull(node));
    751         },
    752 
    753         .unreachable_literal => {
    754             _ = try gz.addAsIndex(.{
    755                 .tag = .@"unreachable",
    756                 .data = .{ .@"unreachable" = .{
    757                     .force_comptime = gz.force_comptime,
    758                     .src_node = gz.nodeIndexToRelative(node),
    759                 } },
    760             });
    761             return Zir.Inst.Ref.unreachable_value;
    762         },
    763         .@"return" => return ret(gz, scope, node),
    764         .field_access => return fieldAccess(gz, scope, rl, node),
    765         .float_literal => return floatLiteral(gz, rl, node, .positive),
    766 
    767         .if_simple => return ifExpr(gz, scope, rl.br(), node, tree.ifSimple(node)),
    768         .@"if" => return ifExpr(gz, scope, rl.br(), node, tree.ifFull(node)),
    769 
    770         .while_simple => return whileExpr(gz, scope, rl.br(), node, tree.whileSimple(node)),
    771         .while_cont => return whileExpr(gz, scope, rl.br(), node, tree.whileCont(node)),
    772         .@"while" => return whileExpr(gz, scope, rl.br(), node, tree.whileFull(node)),
    773 
    774         .for_simple => return forExpr(gz, scope, rl.br(), node, tree.forSimple(node)),
    775         .@"for" => return forExpr(gz, scope, rl.br(), node, tree.forFull(node)),
    776 
    777         .slice_open => {
    778             const lhs = try expr(gz, scope, .ref, node_datas[node].lhs);
    779             const start = try expr(gz, scope, .{ .coerced_ty = .usize_type }, node_datas[node].rhs);
    780             const result = try gz.addPlNode(.slice_start, node, Zir.Inst.SliceStart{
    781                 .lhs = lhs,
    782                 .start = start,
    783             });
    784             return rvalue(gz, rl, result, node);
    785         },
    786         .slice => {
    787             const lhs = try expr(gz, scope, .ref, node_datas[node].lhs);
    788             const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);
    789             const start = try expr(gz, scope, .{ .coerced_ty = .usize_type }, extra.start);
    790             const end = try expr(gz, scope, .{ .coerced_ty = .usize_type }, extra.end);
    791             const result = try gz.addPlNode(.slice_end, node, Zir.Inst.SliceEnd{
    792                 .lhs = lhs,
    793                 .start = start,
    794                 .end = end,
    795             });
    796             return rvalue(gz, rl, result, node);
    797         },
    798         .slice_sentinel => {
    799             const lhs = try expr(gz, scope, .ref, node_datas[node].lhs);
    800             const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
    801             const start = try expr(gz, scope, .{ .coerced_ty = .usize_type }, extra.start);
    802             const end = if (extra.end != 0) try expr(gz, scope, .{ .coerced_ty = .usize_type }, extra.end) else .none;
    803             const sentinel = try expr(gz, scope, .none, extra.sentinel);
    804             const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{
    805                 .lhs = lhs,
    806                 .start = start,
    807                 .end = end,
    808                 .sentinel = sentinel,
    809             });
    810             return rvalue(gz, rl, result, node);
    811         },
    812 
    813         .deref => {
    814             const lhs = try expr(gz, scope, .none, node_datas[node].lhs);
    815             _ = try gz.addUnTok(.validate_deref, lhs, main_tokens[node]);
    816             switch (rl) {
    817                 .ref => return lhs,
    818                 else => {
    819                     const result = try gz.addUnNode(.load, lhs, node);
    820                     return rvalue(gz, rl, result, node);
    821                 },
    822             }
    823         },
    824         .address_of => {
    825             const result = try expr(gz, scope, .ref, node_datas[node].lhs);
    826             return rvalue(gz, rl, result, node);
    827         },
    828         .optional_type => {
    829             const operand = try typeExpr(gz, scope, node_datas[node].lhs);
    830             const result = try gz.addUnNode(.optional_type, operand, node);
    831             return rvalue(gz, rl, result, node);
    832         },
    833         .unwrap_optional => switch (rl) {
    834             .ref => return gz.addUnNode(
    835                 .optional_payload_safe_ptr,
    836                 try expr(gz, scope, .ref, node_datas[node].lhs),
    837                 node,
    838             ),
    839             else => return rvalue(gz, rl, try gz.addUnNode(
    840                 .optional_payload_safe,
    841                 try expr(gz, scope, .none, node_datas[node].lhs),
    842                 node,
    843             ), node),
    844         },
    845         .block_two, .block_two_semicolon => {
    846             const statements = [2]Ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
    847             if (node_datas[node].lhs == 0) {
    848                 return blockExpr(gz, scope, rl, node, statements[0..0]);
    849             } else if (node_datas[node].rhs == 0) {
    850                 return blockExpr(gz, scope, rl, node, statements[0..1]);
    851             } else {
    852                 return blockExpr(gz, scope, rl, node, statements[0..2]);
    853             }
    854         },
    855         .block, .block_semicolon => {
    856             const statements = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
    857             return blockExpr(gz, scope, rl, node, statements);
    858         },
    859         .enum_literal => return simpleStrTok(gz, rl, main_tokens[node], node, .enum_literal),
    860         .error_value => return simpleStrTok(gz, rl, node_datas[node].rhs, node, .error_value),
    861         .anyframe_literal => return rvalue(gz, rl, .anyframe_type, node),
    862         .anyframe_type => {
    863             const return_type = try typeExpr(gz, scope, node_datas[node].rhs);
    864             const result = try gz.addUnNode(.anyframe_type, return_type, node);
    865             return rvalue(gz, rl, result, node);
    866         },
    867         .@"catch" => {
    868             const catch_token = main_tokens[node];
    869             const payload_token: ?Ast.TokenIndex = if (token_tags[catch_token + 1] == .pipe)
    870                 catch_token + 2
    871             else
    872                 null;
    873 
    874             var rhs = node_datas[node].rhs;
    875             while (true) switch (node_tags[rhs]) {
    876                 .grouped_expression => rhs = node_datas[rhs].lhs,
    877                 .unreachable_literal => {
    878                     if (payload_token != null and mem.eql(u8, tree.tokenSlice(payload_token.?), "_")) {
    879                         return astgen.failTok(payload_token.?, "discard of error capture; omit it instead", .{});
    880                     } else if (payload_token != null) {
    881                         return astgen.failTok(payload_token.?, "unused capture", .{});
    882                     }
    883                     const lhs = node_datas[node].lhs;
    884 
    885                     const operand = try reachableExpr(gz, scope, switch (rl) {
    886                         .ref => .ref,
    887                         else => .none,
    888                     }, lhs, lhs);
    889                     const result = try gz.addUnNode(switch (rl) {
    890                         .ref => .err_union_payload_safe_ptr,
    891                         else => .err_union_payload_safe,
    892                     }, operand, node);
    893                     switch (rl) {
    894                         .none, .coerced_ty, .discard, .ref => return result,
    895                         else => return rvalue(gz, rl, result, lhs),
    896                     }
    897                 },
    898                 else => break,
    899             };
    900             switch (rl) {
    901                 .ref => return orelseCatchExpr(
    902                     gz,
    903                     scope,
    904                     rl,
    905                     node,
    906                     node_datas[node].lhs,
    907                     .is_non_err_ptr,
    908                     .err_union_payload_unsafe_ptr,
    909                     .err_union_code_ptr,
    910                     node_datas[node].rhs,
    911                     payload_token,
    912                 ),
    913                 else => return orelseCatchExpr(
    914                     gz,
    915                     scope,
    916                     rl,
    917                     node,
    918                     node_datas[node].lhs,
    919                     .is_non_err,
    920                     .err_union_payload_unsafe,
    921                     .err_union_code,
    922                     node_datas[node].rhs,
    923                     payload_token,
    924                 ),
    925             }
    926         },
    927         .@"orelse" => switch (rl) {
    928             .ref => return orelseCatchExpr(
    929                 gz,
    930                 scope,
    931                 rl,
    932                 node,
    933                 node_datas[node].lhs,
    934                 .is_non_null_ptr,
    935                 .optional_payload_unsafe_ptr,
    936                 undefined,
    937                 node_datas[node].rhs,
    938                 null,
    939             ),
    940             else => return orelseCatchExpr(
    941                 gz,
    942                 scope,
    943                 rl,
    944                 node,
    945                 node_datas[node].lhs,
    946                 .is_non_null,
    947                 .optional_payload_unsafe,
    948                 undefined,
    949                 node_datas[node].rhs,
    950                 null,
    951             ),
    952         },
    953 
    954         .ptr_type_aligned => return ptrType(gz, scope, rl, node, tree.ptrTypeAligned(node)),
    955         .ptr_type_sentinel => return ptrType(gz, scope, rl, node, tree.ptrTypeSentinel(node)),
    956         .ptr_type => return ptrType(gz, scope, rl, node, tree.ptrType(node)),
    957         .ptr_type_bit_range => return ptrType(gz, scope, rl, node, tree.ptrTypeBitRange(node)),
    958 
    959         .container_decl,
    960         .container_decl_trailing,
    961         => return containerDecl(gz, scope, rl, node, tree.containerDecl(node)),
    962         .container_decl_two, .container_decl_two_trailing => {
    963             var buffer: [2]Ast.Node.Index = undefined;
    964             return containerDecl(gz, scope, rl, node, tree.containerDeclTwo(&buffer, node));
    965         },
    966         .container_decl_arg,
    967         .container_decl_arg_trailing,
    968         => return containerDecl(gz, scope, rl, node, tree.containerDeclArg(node)),
    969 
    970         .tagged_union,
    971         .tagged_union_trailing,
    972         => return containerDecl(gz, scope, rl, node, tree.taggedUnion(node)),
    973         .tagged_union_two, .tagged_union_two_trailing => {
    974             var buffer: [2]Ast.Node.Index = undefined;
    975             return containerDecl(gz, scope, rl, node, tree.taggedUnionTwo(&buffer, node));
    976         },
    977         .tagged_union_enum_tag,
    978         .tagged_union_enum_tag_trailing,
    979         => return containerDecl(gz, scope, rl, node, tree.taggedUnionEnumTag(node)),
    980 
    981         .@"break" => return breakExpr(gz, scope, node),
    982         .@"continue" => return continueExpr(gz, scope, node),
    983         .grouped_expression => return expr(gz, scope, rl, node_datas[node].lhs),
    984         .array_type => return arrayType(gz, scope, rl, node),
    985         .array_type_sentinel => return arrayTypeSentinel(gz, scope, rl, node),
    986         .char_literal => return charLiteral(gz, rl, node),
    987         .error_set_decl => return errorSetDecl(gz, rl, node),
    988         .array_access => return arrayAccess(gz, scope, rl, node),
    989         .@"comptime" => return comptimeExprAst(gz, scope, rl, node),
    990         .@"switch", .switch_comma => return switchExpr(gz, scope, rl.br(), node),
    991 
    992         .@"nosuspend" => return nosuspendExpr(gz, scope, rl, node),
    993         .@"suspend" => return suspendExpr(gz, scope, node),
    994         .@"await" => return awaitExpr(gz, scope, rl, node),
    995         .@"resume" => return resumeExpr(gz, scope, rl, node),
    996 
    997         .@"try" => return tryExpr(gz, scope, rl, node, node_datas[node].lhs),
    998 
    999         .array_init_one, .array_init_one_comma => {
   1000             var elements: [1]Ast.Node.Index = undefined;
   1001             return arrayInitExpr(gz, scope, rl, node, tree.arrayInitOne(&elements, node));
   1002         },
   1003         .array_init_dot_two, .array_init_dot_two_comma => {
   1004             var elements: [2]Ast.Node.Index = undefined;
   1005             return arrayInitExpr(gz, scope, rl, node, tree.arrayInitDotTwo(&elements, node));
   1006         },
   1007         .array_init_dot,
   1008         .array_init_dot_comma,
   1009         => return arrayInitExpr(gz, scope, rl, node, tree.arrayInitDot(node)),
   1010         .array_init,
   1011         .array_init_comma,
   1012         => return arrayInitExpr(gz, scope, rl, node, tree.arrayInit(node)),
   1013 
   1014         .struct_init_one, .struct_init_one_comma => {
   1015             var fields: [1]Ast.Node.Index = undefined;
   1016             return structInitExpr(gz, scope, rl, node, tree.structInitOne(&fields, node));
   1017         },
   1018         .struct_init_dot_two, .struct_init_dot_two_comma => {
   1019             var fields: [2]Ast.Node.Index = undefined;
   1020             return structInitExpr(gz, scope, rl, node, tree.structInitDotTwo(&fields, node));
   1021         },
   1022         .struct_init_dot,
   1023         .struct_init_dot_comma,
   1024         => return structInitExpr(gz, scope, rl, node, tree.structInitDot(node)),
   1025         .struct_init,
   1026         .struct_init_comma,
   1027         => return structInitExpr(gz, scope, rl, node, tree.structInit(node)),
   1028 
   1029         .fn_proto_simple => {
   1030             var params: [1]Ast.Node.Index = undefined;
   1031             return fnProtoExpr(gz, scope, rl, node, tree.fnProtoSimple(&params, node));
   1032         },
   1033         .fn_proto_multi => {
   1034             return fnProtoExpr(gz, scope, rl, node, tree.fnProtoMulti(node));
   1035         },
   1036         .fn_proto_one => {
   1037             var params: [1]Ast.Node.Index = undefined;
   1038             return fnProtoExpr(gz, scope, rl, node, tree.fnProtoOne(&params, node));
   1039         },
   1040         .fn_proto => {
   1041             return fnProtoExpr(gz, scope, rl, node, tree.fnProto(node));
   1042         },
   1043     }
   1044 }
   1045 
   1046 fn nosuspendExpr(
   1047     gz: *GenZir,
   1048     scope: *Scope,
   1049     rl: ResultLoc,
   1050     node: Ast.Node.Index,
   1051 ) InnerError!Zir.Inst.Ref {
   1052     const astgen = gz.astgen;
   1053     const tree = astgen.tree;
   1054     const node_datas = tree.nodes.items(.data);
   1055     const body_node = node_datas[node].lhs;
   1056     assert(body_node != 0);
   1057     if (gz.nosuspend_node != 0) {
   1058         try astgen.appendErrorNodeNotes(node, "redundant nosuspend block", .{}, &[_]u32{
   1059             try astgen.errNoteNode(gz.nosuspend_node, "other nosuspend block here", .{}),
   1060         });
   1061     }
   1062     gz.nosuspend_node = node;
   1063     defer gz.nosuspend_node = 0;
   1064     return expr(gz, scope, rl, body_node);
   1065 }
   1066 
   1067 fn suspendExpr(
   1068     gz: *GenZir,
   1069     scope: *Scope,
   1070     node: Ast.Node.Index,
   1071 ) InnerError!Zir.Inst.Ref {
   1072     const astgen = gz.astgen;
   1073     const gpa = astgen.gpa;
   1074     const tree = astgen.tree;
   1075     const node_datas = tree.nodes.items(.data);
   1076     const body_node = node_datas[node].lhs;
   1077 
   1078     if (gz.nosuspend_node != 0) {
   1079         return astgen.failNodeNotes(node, "suspend inside nosuspend block", .{}, &[_]u32{
   1080             try astgen.errNoteNode(gz.nosuspend_node, "nosuspend block here", .{}),
   1081         });
   1082     }
   1083     if (gz.suspend_node != 0) {
   1084         return astgen.failNodeNotes(node, "cannot suspend inside suspend block", .{}, &[_]u32{
   1085             try astgen.errNoteNode(gz.suspend_node, "other suspend block here", .{}),
   1086         });
   1087     }
   1088     assert(body_node != 0);
   1089 
   1090     const suspend_inst = try gz.makeBlockInst(.suspend_block, node);
   1091     try gz.instructions.append(gpa, suspend_inst);
   1092 
   1093     var suspend_scope = gz.makeSubBlock(scope);
   1094     suspend_scope.suspend_node = node;
   1095     defer suspend_scope.unstack();
   1096 
   1097     const body_result = try expr(&suspend_scope, &suspend_scope.base, .none, body_node);
   1098     if (!gz.refIsNoReturn(body_result)) {
   1099         _ = try suspend_scope.addBreak(.break_inline, suspend_inst, .void_value);
   1100     }
   1101     try suspend_scope.setBlockBody(suspend_inst);
   1102 
   1103     return indexToRef(suspend_inst);
   1104 }
   1105 
   1106 fn awaitExpr(
   1107     gz: *GenZir,
   1108     scope: *Scope,
   1109     rl: ResultLoc,
   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 rhs_node = node_datas[node].lhs;
   1116 
   1117     if (gz.suspend_node != 0) {
   1118         return astgen.failNodeNotes(node, "cannot await inside suspend block", .{}, &[_]u32{
   1119             try astgen.errNoteNode(gz.suspend_node, "suspend block here", .{}),
   1120         });
   1121     }
   1122     const operand = try expr(gz, scope, .none, rhs_node);
   1123     const result = if (gz.nosuspend_node != 0)
   1124         try gz.addExtendedPayload(.await_nosuspend, Zir.Inst.UnNode{
   1125             .node = gz.nodeIndexToRelative(node),
   1126             .operand = operand,
   1127         })
   1128     else
   1129         try gz.addUnNode(.@"await", operand, node);
   1130 
   1131     return rvalue(gz, rl, result, node);
   1132 }
   1133 
   1134 fn resumeExpr(
   1135     gz: *GenZir,
   1136     scope: *Scope,
   1137     rl: ResultLoc,
   1138     node: Ast.Node.Index,
   1139 ) InnerError!Zir.Inst.Ref {
   1140     const astgen = gz.astgen;
   1141     const tree = astgen.tree;
   1142     const node_datas = tree.nodes.items(.data);
   1143     const rhs_node = node_datas[node].lhs;
   1144     const operand = try expr(gz, scope, .none, rhs_node);
   1145     const result = try gz.addUnNode(.@"resume", operand, node);
   1146     return rvalue(gz, rl, result, node);
   1147 }
   1148 
   1149 fn fnProtoExpr(
   1150     gz: *GenZir,
   1151     scope: *Scope,
   1152     rl: ResultLoc,
   1153     node: Ast.Node.Index,
   1154     fn_proto: Ast.full.FnProto,
   1155 ) InnerError!Zir.Inst.Ref {
   1156     const astgen = gz.astgen;
   1157     const tree = astgen.tree;
   1158     const token_tags = tree.tokens.items(.tag);
   1159 
   1160     const is_extern = blk: {
   1161         const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false;
   1162         break :blk token_tags[maybe_extern_token] == .keyword_extern;
   1163     };
   1164     assert(!is_extern);
   1165 
   1166     var block_scope = gz.makeSubBlock(scope);
   1167     defer block_scope.unstack();
   1168 
   1169     const block_inst = try gz.makeBlockInst(.block_inline, node);
   1170 
   1171     var noalias_bits: u32 = 0;
   1172     const is_var_args = is_var_args: {
   1173         var param_type_i: usize = 0;
   1174         var it = fn_proto.iterate(tree);
   1175         while (it.next()) |param| : (param_type_i += 1) {
   1176             const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) {
   1177                 .keyword_noalias => is_comptime: {
   1178                     noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse
   1179                         return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
   1180                     break :is_comptime false;
   1181                 },
   1182                 .keyword_comptime => true,
   1183                 else => false,
   1184             } else false;
   1185 
   1186             const is_anytype = if (param.anytype_ellipsis3) |token| blk: {
   1187                 switch (token_tags[token]) {
   1188                     .keyword_anytype => break :blk true,
   1189                     .ellipsis3 => break :is_var_args true,
   1190                     else => unreachable,
   1191                 }
   1192             } else false;
   1193 
   1194             const param_name: u32 = if (param.name_token) |name_token| blk: {
   1195                 if (mem.eql(u8, "_", tree.tokenSlice(name_token)))
   1196                     break :blk 0;
   1197 
   1198                 break :blk try astgen.identAsString(name_token);
   1199             } else 0;
   1200 
   1201             if (is_anytype) {
   1202                 const name_token = param.name_token orelse param.anytype_ellipsis3.?;
   1203 
   1204                 const tag: Zir.Inst.Tag = if (is_comptime)
   1205                     .param_anytype_comptime
   1206                 else
   1207                     .param_anytype;
   1208                 _ = try block_scope.addStrTok(tag, param_name, name_token);
   1209             } else {
   1210                 const param_type_node = param.type_expr;
   1211                 assert(param_type_node != 0);
   1212                 var param_gz = block_scope.makeSubBlock(scope);
   1213                 defer param_gz.unstack();
   1214                 const param_type = try expr(&param_gz, scope, coerced_type_rl, param_type_node);
   1215                 const param_inst_expected = @intCast(u32, astgen.instructions.len + 1);
   1216                 _ = try param_gz.addBreak(.break_inline, param_inst_expected, param_type);
   1217                 const main_tokens = tree.nodes.items(.main_token);
   1218                 const name_token = param.name_token orelse main_tokens[param_type_node];
   1219                 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param;
   1220                 const param_inst = try block_scope.addParam(&param_gz, tag, name_token, param_name, param.first_doc_comment);
   1221                 assert(param_inst_expected == param_inst);
   1222             }
   1223         }
   1224         break :is_var_args false;
   1225     };
   1226 
   1227     const align_ref: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
   1228         break :inst try expr(&block_scope, scope, align_rl, fn_proto.ast.align_expr);
   1229     };
   1230 
   1231     if (fn_proto.ast.addrspace_expr != 0) {
   1232         return astgen.failNode(fn_proto.ast.addrspace_expr, "addrspace not allowed on function prototypes", .{});
   1233     }
   1234 
   1235     if (fn_proto.ast.section_expr != 0) {
   1236         return astgen.failNode(fn_proto.ast.section_expr, "linksection not allowed on function prototypes", .{});
   1237     }
   1238 
   1239     const cc: Zir.Inst.Ref = if (fn_proto.ast.callconv_expr != 0)
   1240         try expr(
   1241             &block_scope,
   1242             scope,
   1243             .{ .ty = .calling_convention_type },
   1244             fn_proto.ast.callconv_expr,
   1245         )
   1246     else
   1247         Zir.Inst.Ref.none;
   1248 
   1249     const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
   1250     const is_inferred_error = token_tags[maybe_bang] == .bang;
   1251     if (is_inferred_error) {
   1252         return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{});
   1253     }
   1254     const ret_ty = try expr(&block_scope, scope, coerced_type_rl, fn_proto.ast.return_type);
   1255 
   1256     const result = try block_scope.addFunc(.{
   1257         .src_node = fn_proto.ast.proto_node,
   1258 
   1259         .cc_ref = cc,
   1260         .cc_gz = null,
   1261         .align_ref = align_ref,
   1262         .align_gz = null,
   1263         .ret_ref = ret_ty,
   1264         .ret_gz = null,
   1265         .section_ref = .none,
   1266         .section_gz = null,
   1267         .addrspace_ref = .none,
   1268         .addrspace_gz = null,
   1269 
   1270         .param_block = block_inst,
   1271         .body_gz = null,
   1272         .lib_name = 0,
   1273         .is_var_args = is_var_args,
   1274         .is_inferred_error = false,
   1275         .is_test = false,
   1276         .is_extern = false,
   1277         .noalias_bits = noalias_bits,
   1278     });
   1279 
   1280     _ = try block_scope.addBreak(.break_inline, block_inst, result);
   1281     try block_scope.setBlockBody(block_inst);
   1282     try gz.instructions.append(astgen.gpa, block_inst);
   1283 
   1284     return rvalue(gz, rl, indexToRef(block_inst), fn_proto.ast.proto_node);
   1285 }
   1286 
   1287 fn arrayInitExpr(
   1288     gz: *GenZir,
   1289     scope: *Scope,
   1290     rl: ResultLoc,
   1291     node: Ast.Node.Index,
   1292     array_init: Ast.full.ArrayInit,
   1293 ) InnerError!Zir.Inst.Ref {
   1294     const astgen = gz.astgen;
   1295     const tree = astgen.tree;
   1296     const node_tags = tree.nodes.items(.tag);
   1297     const main_tokens = tree.nodes.items(.main_token);
   1298 
   1299     assert(array_init.ast.elements.len != 0); // Otherwise it would be struct init.
   1300 
   1301     const types: struct {
   1302         array: Zir.Inst.Ref,
   1303         elem: Zir.Inst.Ref,
   1304     } = inst: {
   1305         if (array_init.ast.type_expr == 0) break :inst .{
   1306             .array = .none,
   1307             .elem = .none,
   1308         };
   1309 
   1310         infer: {
   1311             const array_type: Ast.full.ArrayType = switch (node_tags[array_init.ast.type_expr]) {
   1312                 .array_type => tree.arrayType(array_init.ast.type_expr),
   1313                 .array_type_sentinel => tree.arrayTypeSentinel(array_init.ast.type_expr),
   1314                 else => break :infer,
   1315             };
   1316             // This intentionally does not support `@"_"` syntax.
   1317             if (node_tags[array_type.ast.elem_count] == .identifier and
   1318                 mem.eql(u8, tree.tokenSlice(main_tokens[array_type.ast.elem_count]), "_"))
   1319             {
   1320                 const len_inst = try gz.addInt(array_init.ast.elements.len);
   1321                 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type);
   1322                 if (array_type.ast.sentinel == 0) {
   1323                     const array_type_inst = try gz.addBin(.array_type, len_inst, elem_type);
   1324                     break :inst .{
   1325                         .array = array_type_inst,
   1326                         .elem = elem_type,
   1327                     };
   1328                 } else {
   1329                     const sentinel = try comptimeExpr(gz, scope, .{ .ty = elem_type }, array_type.ast.sentinel);
   1330                     const array_type_inst = try gz.addPlNode(
   1331                         .array_type_sentinel,
   1332                         array_init.ast.type_expr,
   1333                         Zir.Inst.ArrayTypeSentinel{
   1334                             .len = len_inst,
   1335                             .elem_type = elem_type,
   1336                             .sentinel = sentinel,
   1337                         },
   1338                     );
   1339                     break :inst .{
   1340                         .array = array_type_inst,
   1341                         .elem = elem_type,
   1342                     };
   1343                 }
   1344             }
   1345         }
   1346         const array_type_inst = try typeExpr(gz, scope, array_init.ast.type_expr);
   1347         _ = try gz.addUnNode(.validate_array_init_ty, array_type_inst, node);
   1348         break :inst .{
   1349             .array = array_type_inst,
   1350             .elem = .none,
   1351         };
   1352     };
   1353 
   1354     switch (rl) {
   1355         .discard => {
   1356             // TODO elements should still be coerced if type is provided
   1357             for (array_init.ast.elements) |elem_init| {
   1358                 _ = try expr(gz, scope, .discard, elem_init);
   1359             }
   1360             return Zir.Inst.Ref.void_value;
   1361         },
   1362         .ref => {
   1363             const tag: Zir.Inst.Tag = if (types.array != .none) .array_init_ref else .array_init_anon_ref;
   1364             return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
   1365         },
   1366         .none => {
   1367             const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;
   1368             return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
   1369         },
   1370         .ty, .coerced_ty => {
   1371             const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;
   1372             const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
   1373             return rvalue(gz, rl, result, node);
   1374         },
   1375         .ptr => |ptr_inst| {
   1376             return arrayInitExprRlPtr(gz, scope, rl, node, ptr_inst, array_init.ast.elements, types.array);
   1377         },
   1378         .inferred_ptr => |ptr_inst| {
   1379             if (types.array == .none) {
   1380                 // We treat this case differently so that we don't get a crash when
   1381                 // analyzing array_base_ptr against an alloc_inferred_mut.
   1382                 // See corresponding logic in structInitExpr.
   1383                 const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon);
   1384                 return rvalue(gz, rl, result, node);
   1385             } else {
   1386                 return arrayInitExprRlPtr(gz, scope, rl, node, ptr_inst, array_init.ast.elements, types.array);
   1387             }
   1388         },
   1389         .block_ptr => |block_gz| {
   1390             // This condition is here for the same reason as the above condition in `inferred_ptr`.
   1391             // See corresponding logic in structInitExpr.
   1392             if (types.array == .none and astgen.isInferred(block_gz.rl_ptr)) {
   1393                 const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon);
   1394                 return rvalue(gz, rl, result, node);
   1395             }
   1396             return arrayInitExprRlPtr(gz, scope, rl, node, block_gz.rl_ptr, array_init.ast.elements, types.array);
   1397         },
   1398     }
   1399 }
   1400 
   1401 fn arrayInitExprRlNone(
   1402     gz: *GenZir,
   1403     scope: *Scope,
   1404     node: Ast.Node.Index,
   1405     elements: []const Ast.Node.Index,
   1406     tag: Zir.Inst.Tag,
   1407 ) InnerError!Zir.Inst.Ref {
   1408     const astgen = gz.astgen;
   1409 
   1410     const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{
   1411         .operands_len = @intCast(u32, elements.len),
   1412     });
   1413     var extra_index = try reserveExtra(astgen, elements.len);
   1414 
   1415     for (elements) |elem_init| {
   1416         const elem_ref = try expr(gz, scope, .none, elem_init);
   1417         astgen.extra.items[extra_index] = @enumToInt(elem_ref);
   1418         extra_index += 1;
   1419     }
   1420     return try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1421 }
   1422 
   1423 fn arrayInitExprInner(
   1424     gz: *GenZir,
   1425     scope: *Scope,
   1426     node: Ast.Node.Index,
   1427     elements: []const Ast.Node.Index,
   1428     array_ty_inst: Zir.Inst.Ref,
   1429     elem_ty: Zir.Inst.Ref,
   1430     tag: Zir.Inst.Tag,
   1431 ) InnerError!Zir.Inst.Ref {
   1432     const astgen = gz.astgen;
   1433 
   1434     const len = elements.len + @boolToInt(array_ty_inst != .none);
   1435     const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{
   1436         .operands_len = @intCast(u32, len),
   1437     });
   1438     var extra_index = try reserveExtra(astgen, len);
   1439     if (array_ty_inst != .none) {
   1440         astgen.extra.items[extra_index] = @enumToInt(array_ty_inst);
   1441         extra_index += 1;
   1442     }
   1443 
   1444     for (elements) |elem_init, i| {
   1445         const rl = if (elem_ty != .none)
   1446             ResultLoc{ .coerced_ty = elem_ty }
   1447         else if (array_ty_inst != .none and nodeMayNeedMemoryLocation(astgen.tree, elem_init, true)) rl: {
   1448             const ty_expr = try gz.add(.{
   1449                 .tag = .elem_type_index,
   1450                 .data = .{ .bin = .{
   1451                     .lhs = array_ty_inst,
   1452                     .rhs = @intToEnum(Zir.Inst.Ref, i),
   1453                 } },
   1454             });
   1455             break :rl ResultLoc{ .coerced_ty = ty_expr };
   1456         } else ResultLoc{ .none = {} };
   1457 
   1458         const elem_ref = try expr(gz, scope, rl, elem_init);
   1459         astgen.extra.items[extra_index] = @enumToInt(elem_ref);
   1460         extra_index += 1;
   1461     }
   1462 
   1463     return try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1464 }
   1465 
   1466 fn arrayInitExprRlPtr(
   1467     gz: *GenZir,
   1468     scope: *Scope,
   1469     rl: ResultLoc,
   1470     node: Ast.Node.Index,
   1471     result_ptr: Zir.Inst.Ref,
   1472     elements: []const Ast.Node.Index,
   1473     array_ty: Zir.Inst.Ref,
   1474 ) InnerError!Zir.Inst.Ref {
   1475     if (array_ty == .none) {
   1476         const base_ptr = try gz.addUnNode(.array_base_ptr, result_ptr, node);
   1477         return arrayInitExprRlPtrInner(gz, scope, node, base_ptr, elements);
   1478     }
   1479 
   1480     var as_scope = try gz.makeCoercionScope(scope, array_ty, result_ptr, node);
   1481     defer as_scope.unstack();
   1482 
   1483     const result = try arrayInitExprRlPtrInner(&as_scope, scope, node, as_scope.rl_ptr, elements);
   1484     return as_scope.finishCoercion(gz, rl, node, result, array_ty);
   1485 }
   1486 
   1487 fn arrayInitExprRlPtrInner(
   1488     gz: *GenZir,
   1489     scope: *Scope,
   1490     node: Ast.Node.Index,
   1491     result_ptr: Zir.Inst.Ref,
   1492     elements: []const Ast.Node.Index,
   1493 ) InnerError!Zir.Inst.Ref {
   1494     const astgen = gz.astgen;
   1495 
   1496     const payload_index = try addExtra(astgen, Zir.Inst.Block{
   1497         .body_len = @intCast(u32, elements.len),
   1498     });
   1499     var extra_index = try reserveExtra(astgen, elements.len);
   1500 
   1501     for (elements) |elem_init, i| {
   1502         const elem_ptr = try gz.addPlNode(.elem_ptr_imm, elem_init, Zir.Inst.ElemPtrImm{
   1503             .ptr = result_ptr,
   1504             .index = @intCast(u32, i),
   1505         });
   1506         astgen.extra.items[extra_index] = refToIndex(elem_ptr).?;
   1507         extra_index += 1;
   1508         _ = try expr(gz, scope, .{ .ptr = elem_ptr }, elem_init);
   1509     }
   1510 
   1511     const tag: Zir.Inst.Tag = if (gz.force_comptime)
   1512         .validate_array_init_comptime
   1513     else
   1514         .validate_array_init;
   1515 
   1516     _ = try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1517     return .void_value;
   1518 }
   1519 
   1520 fn structInitExpr(
   1521     gz: *GenZir,
   1522     scope: *Scope,
   1523     rl: ResultLoc,
   1524     node: Ast.Node.Index,
   1525     struct_init: Ast.full.StructInit,
   1526 ) InnerError!Zir.Inst.Ref {
   1527     const astgen = gz.astgen;
   1528     const tree = astgen.tree;
   1529 
   1530     if (struct_init.ast.type_expr == 0) {
   1531         if (struct_init.ast.fields.len == 0) {
   1532             return rvalue(gz, rl, .empty_struct, node);
   1533         }
   1534     } else array: {
   1535         const node_tags = tree.nodes.items(.tag);
   1536         const main_tokens = tree.nodes.items(.main_token);
   1537         const array_type: Ast.full.ArrayType = switch (node_tags[struct_init.ast.type_expr]) {
   1538             .array_type => tree.arrayType(struct_init.ast.type_expr),
   1539             .array_type_sentinel => tree.arrayTypeSentinel(struct_init.ast.type_expr),
   1540             else => {
   1541                 if (struct_init.ast.fields.len == 0) {
   1542                     const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1543                     const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
   1544                     return rvalue(gz, rl, result, node);
   1545                 }
   1546                 break :array;
   1547             },
   1548         };
   1549         const is_inferred_array_len = node_tags[array_type.ast.elem_count] == .identifier and
   1550             // This intentionally does not support `@"_"` syntax.
   1551             mem.eql(u8, tree.tokenSlice(main_tokens[array_type.ast.elem_count]), "_");
   1552         if (struct_init.ast.fields.len == 0) {
   1553             if (is_inferred_array_len) {
   1554                 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type);
   1555                 const array_type_inst = if (array_type.ast.sentinel == 0) blk: {
   1556                     break :blk try gz.addBin(.array_type, .zero_usize, elem_type);
   1557                 } else blk: {
   1558                     const sentinel = try comptimeExpr(gz, scope, .{ .ty = elem_type }, array_type.ast.sentinel);
   1559                     break :blk try gz.addPlNode(
   1560                         .array_type_sentinel,
   1561                         struct_init.ast.type_expr,
   1562                         Zir.Inst.ArrayTypeSentinel{
   1563                             .len = .zero_usize,
   1564                             .elem_type = elem_type,
   1565                             .sentinel = sentinel,
   1566                         },
   1567                     );
   1568                 };
   1569                 const result = try gz.addUnNode(.struct_init_empty, array_type_inst, node);
   1570                 return rvalue(gz, rl, result, node);
   1571             }
   1572             const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1573             const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
   1574             return rvalue(gz, rl, result, node);
   1575         } else {
   1576             return astgen.failNode(
   1577                 struct_init.ast.type_expr,
   1578                 "initializing array with struct syntax",
   1579                 .{},
   1580             );
   1581         }
   1582     }
   1583 
   1584     switch (rl) {
   1585         .discard => {
   1586             // TODO if a type expr is given the fields should be validated for that type
   1587             if (struct_init.ast.type_expr != 0) {
   1588                 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1589                 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
   1590             }
   1591             for (struct_init.ast.fields) |field_init| {
   1592                 _ = try expr(gz, scope, .discard, field_init);
   1593             }
   1594             return Zir.Inst.Ref.void_value;
   1595         },
   1596         .ref => {
   1597             if (struct_init.ast.type_expr != 0) {
   1598                 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1599                 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
   1600                 return structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init_ref);
   1601             } else {
   1602                 return structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon_ref);
   1603             }
   1604         },
   1605         .none => {
   1606             if (struct_init.ast.type_expr != 0) {
   1607                 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1608                 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
   1609                 return structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init);
   1610             } else {
   1611                 return structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon);
   1612             }
   1613         },
   1614         .ty, .coerced_ty => |ty_inst| {
   1615             if (struct_init.ast.type_expr == 0) {
   1616                 const result = try structInitExprRlNone(gz, scope, node, struct_init, ty_inst, .struct_init_anon);
   1617                 return rvalue(gz, rl, result, node);
   1618             }
   1619             const inner_ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1620             _ = try gz.addUnNode(.validate_struct_init_ty, inner_ty_inst, node);
   1621             const result = try structInitExprRlTy(gz, scope, node, struct_init, inner_ty_inst, .struct_init);
   1622             return rvalue(gz, rl, result, node);
   1623         },
   1624         .ptr => |ptr_inst| return structInitExprRlPtr(gz, scope, rl, node, struct_init, ptr_inst),
   1625         .inferred_ptr => |ptr_inst| {
   1626             if (struct_init.ast.type_expr == 0) {
   1627                 // We treat this case differently so that we don't get a crash when
   1628                 // analyzing field_base_ptr against an alloc_inferred_mut.
   1629                 // See corresponding logic in arrayInitExpr.
   1630                 const result = try structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon);
   1631                 return rvalue(gz, rl, result, node);
   1632             } else {
   1633                 return structInitExprRlPtr(gz, scope, rl, node, struct_init, ptr_inst);
   1634             }
   1635         },
   1636         .block_ptr => |block_gz| {
   1637             // This condition is here for the same reason as the above condition in `inferred_ptr`.
   1638             // See corresponding logic in arrayInitExpr.
   1639             if (struct_init.ast.type_expr == 0 and astgen.isInferred(block_gz.rl_ptr)) {
   1640                 const result = try structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon);
   1641                 return rvalue(gz, rl, result, node);
   1642             }
   1643 
   1644             return structInitExprRlPtr(gz, scope, rl, node, struct_init, block_gz.rl_ptr);
   1645         },
   1646     }
   1647 }
   1648 
   1649 fn structInitExprRlNone(
   1650     gz: *GenZir,
   1651     scope: *Scope,
   1652     node: Ast.Node.Index,
   1653     struct_init: Ast.full.StructInit,
   1654     ty_inst: Zir.Inst.Ref,
   1655     tag: Zir.Inst.Tag,
   1656 ) InnerError!Zir.Inst.Ref {
   1657     const astgen = gz.astgen;
   1658     const tree = astgen.tree;
   1659 
   1660     const payload_index = try addExtra(astgen, Zir.Inst.StructInitAnon{
   1661         .fields_len = @intCast(u32, struct_init.ast.fields.len),
   1662     });
   1663     const field_size = @typeInfo(Zir.Inst.StructInitAnon.Item).Struct.fields.len;
   1664     var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size);
   1665 
   1666     for (struct_init.ast.fields) |field_init| {
   1667         const name_token = tree.firstToken(field_init) - 2;
   1668         const str_index = try astgen.identAsString(name_token);
   1669         const sub_rl: ResultLoc = if (ty_inst != .none)
   1670             ResultLoc{ .ty = try gz.addPlNode(.field_type, field_init, Zir.Inst.FieldType{
   1671                 .container_type = ty_inst,
   1672                 .name_start = str_index,
   1673             }) }
   1674         else
   1675             .none;
   1676         setExtra(astgen, extra_index, Zir.Inst.StructInitAnon.Item{
   1677             .field_name = str_index,
   1678             .init = try expr(gz, scope, sub_rl, field_init),
   1679         });
   1680         extra_index += field_size;
   1681     }
   1682 
   1683     return try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1684 }
   1685 
   1686 fn structInitExprRlPtr(
   1687     gz: *GenZir,
   1688     scope: *Scope,
   1689     rl: ResultLoc,
   1690     node: Ast.Node.Index,
   1691     struct_init: Ast.full.StructInit,
   1692     result_ptr: Zir.Inst.Ref,
   1693 ) InnerError!Zir.Inst.Ref {
   1694     if (struct_init.ast.type_expr == 0) {
   1695         const base_ptr = try gz.addUnNode(.field_base_ptr, result_ptr, node);
   1696         return structInitExprRlPtrInner(gz, scope, node, struct_init, base_ptr);
   1697     }
   1698     const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1699     _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
   1700 
   1701     var as_scope = try gz.makeCoercionScope(scope, ty_inst, result_ptr, node);
   1702     defer as_scope.unstack();
   1703 
   1704     const result = try structInitExprRlPtrInner(&as_scope, scope, node, struct_init, as_scope.rl_ptr);
   1705     return as_scope.finishCoercion(gz, rl, node, result, ty_inst);
   1706 }
   1707 
   1708 fn structInitExprRlPtrInner(
   1709     gz: *GenZir,
   1710     scope: *Scope,
   1711     node: Ast.Node.Index,
   1712     struct_init: Ast.full.StructInit,
   1713     result_ptr: Zir.Inst.Ref,
   1714 ) InnerError!Zir.Inst.Ref {
   1715     const astgen = gz.astgen;
   1716     const tree = astgen.tree;
   1717 
   1718     const payload_index = try addExtra(astgen, Zir.Inst.Block{
   1719         .body_len = @intCast(u32, struct_init.ast.fields.len),
   1720     });
   1721     var extra_index = try reserveExtra(astgen, struct_init.ast.fields.len);
   1722 
   1723     for (struct_init.ast.fields) |field_init| {
   1724         const name_token = tree.firstToken(field_init) - 2;
   1725         const str_index = try astgen.identAsString(name_token);
   1726         const field_ptr = try gz.addPlNode(.field_ptr, field_init, Zir.Inst.Field{
   1727             .lhs = result_ptr,
   1728             .field_name_start = str_index,
   1729         });
   1730         astgen.extra.items[extra_index] = refToIndex(field_ptr).?;
   1731         extra_index += 1;
   1732         _ = try expr(gz, scope, .{ .ptr = field_ptr }, field_init);
   1733     }
   1734 
   1735     const tag: Zir.Inst.Tag = if (gz.force_comptime)
   1736         .validate_struct_init_comptime
   1737     else
   1738         .validate_struct_init;
   1739 
   1740     _ = try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1741     return Zir.Inst.Ref.void_value;
   1742 }
   1743 
   1744 fn structInitExprRlTy(
   1745     gz: *GenZir,
   1746     scope: *Scope,
   1747     node: Ast.Node.Index,
   1748     struct_init: Ast.full.StructInit,
   1749     ty_inst: Zir.Inst.Ref,
   1750     tag: Zir.Inst.Tag,
   1751 ) InnerError!Zir.Inst.Ref {
   1752     const astgen = gz.astgen;
   1753     const tree = astgen.tree;
   1754 
   1755     const payload_index = try addExtra(astgen, Zir.Inst.StructInit{
   1756         .fields_len = @intCast(u32, struct_init.ast.fields.len),
   1757     });
   1758     const field_size = @typeInfo(Zir.Inst.StructInit.Item).Struct.fields.len;
   1759     var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size);
   1760 
   1761     for (struct_init.ast.fields) |field_init| {
   1762         const name_token = tree.firstToken(field_init) - 2;
   1763         const str_index = try astgen.identAsString(name_token);
   1764         const field_ty_inst = try gz.addPlNode(.field_type, field_init, Zir.Inst.FieldType{
   1765             .container_type = ty_inst,
   1766             .name_start = str_index,
   1767         });
   1768         setExtra(astgen, extra_index, Zir.Inst.StructInit.Item{
   1769             .field_type = refToIndex(field_ty_inst).?,
   1770             .init = try expr(gz, scope, .{ .ty = field_ty_inst }, field_init),
   1771         });
   1772         extra_index += field_size;
   1773     }
   1774 
   1775     return try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1776 }
   1777 
   1778 /// This calls expr in a comptime scope, and is intended to be called as a helper function.
   1779 /// The one that corresponds to `comptime` expression syntax is `comptimeExprAst`.
   1780 fn comptimeExpr(
   1781     gz: *GenZir,
   1782     scope: *Scope,
   1783     rl: ResultLoc,
   1784     node: Ast.Node.Index,
   1785 ) InnerError!Zir.Inst.Ref {
   1786     const prev_force_comptime = gz.force_comptime;
   1787     gz.force_comptime = true;
   1788     defer gz.force_comptime = prev_force_comptime;
   1789 
   1790     return expr(gz, scope, rl, node);
   1791 }
   1792 
   1793 /// This one is for an actual `comptime` syntax, and will emit a compile error if
   1794 /// the scope already has `force_comptime=true`.
   1795 /// See `comptimeExpr` for the helper function for calling expr in a comptime scope.
   1796 fn comptimeExprAst(
   1797     gz: *GenZir,
   1798     scope: *Scope,
   1799     rl: ResultLoc,
   1800     node: Ast.Node.Index,
   1801 ) InnerError!Zir.Inst.Ref {
   1802     const astgen = gz.astgen;
   1803     if (gz.force_comptime) {
   1804         return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
   1805     }
   1806     const tree = astgen.tree;
   1807     const node_datas = tree.nodes.items(.data);
   1808     const body_node = node_datas[node].lhs;
   1809     gz.force_comptime = true;
   1810     const result = try expr(gz, scope, rl, body_node);
   1811     gz.force_comptime = false;
   1812     return result;
   1813 }
   1814 
   1815 fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   1816     const astgen = parent_gz.astgen;
   1817     const tree = astgen.tree;
   1818     const node_datas = tree.nodes.items(.data);
   1819     const break_label = node_datas[node].lhs;
   1820     const rhs = node_datas[node].rhs;
   1821 
   1822     // Look for the label in the scope.
   1823     var scope = parent_scope;
   1824     while (true) {
   1825         switch (scope.tag) {
   1826             .gen_zir => {
   1827                 const block_gz = scope.cast(GenZir).?;
   1828 
   1829                 const block_inst = blk: {
   1830                     if (break_label != 0) {
   1831                         if (block_gz.label) |*label| {
   1832                             if (try astgen.tokenIdentEql(label.token, break_label)) {
   1833                                 label.used = true;
   1834                                 break :blk label.block_inst;
   1835                             }
   1836                         }
   1837                     } else if (block_gz.break_block != 0) {
   1838                         break :blk block_gz.break_block;
   1839                     }
   1840                     scope = block_gz.parent;
   1841                     continue;
   1842                 };
   1843 
   1844                 const break_tag: Zir.Inst.Tag = if (block_gz.is_inline or block_gz.force_comptime)
   1845                     .break_inline
   1846                 else
   1847                     .@"break";
   1848 
   1849                 if (rhs == 0) {
   1850                     try genDefers(parent_gz, scope, parent_scope, .normal_only);
   1851 
   1852                     _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
   1853                     return Zir.Inst.Ref.unreachable_value;
   1854                 }
   1855                 block_gz.break_count += 1;
   1856 
   1857                 // The loop scope has a mechanism to prevent rvalue() from emitting a
   1858                 // store to the result location for the loop body (since it is continues
   1859                 // rather than returning a result from the loop) but here is a `break`
   1860                 // which needs to override this behavior.
   1861                 const prev_rvalue_noresult = parent_gz.rvalue_noresult;
   1862                 parent_gz.rvalue_noresult = .none;
   1863                 const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_loc, rhs, node);
   1864                 const search_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
   1865                 parent_gz.rvalue_noresult = prev_rvalue_noresult;
   1866 
   1867                 try genDefers(parent_gz, scope, parent_scope, .normal_only);
   1868 
   1869                 switch (block_gz.break_result_loc) {
   1870                     .block_ptr => {
   1871                         const br = try parent_gz.addBreak(break_tag, block_inst, operand);
   1872                         try block_gz.labeled_breaks.append(astgen.gpa, .{ .br = br, .search = search_index });
   1873                     },
   1874                     .ptr => {
   1875                         // In this case we don't have any mechanism to intercept it;
   1876                         // we assume the result location is written, and we break with void.
   1877                         _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
   1878                     },
   1879                     .discard => {
   1880                         _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
   1881                     },
   1882                     else => {
   1883                         _ = try parent_gz.addBreak(break_tag, block_inst, operand);
   1884                     },
   1885                 }
   1886                 return Zir.Inst.Ref.unreachable_value;
   1887             },
   1888             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   1889             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   1890             .namespace => break,
   1891             .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   1892             .top => unreachable,
   1893         }
   1894     }
   1895     if (break_label != 0) {
   1896         const label_name = try astgen.identifierTokenString(break_label);
   1897         return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
   1898     } else {
   1899         return astgen.failNode(node, "break expression outside loop", .{});
   1900     }
   1901 }
   1902 
   1903 fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   1904     const astgen = parent_gz.astgen;
   1905     const tree = astgen.tree;
   1906     const node_datas = tree.nodes.items(.data);
   1907     const break_label = node_datas[node].lhs;
   1908 
   1909     // Look for the label in the scope.
   1910     var scope = parent_scope;
   1911     while (true) {
   1912         switch (scope.tag) {
   1913             .gen_zir => {
   1914                 const gen_zir = scope.cast(GenZir).?;
   1915                 const continue_block = gen_zir.continue_block;
   1916                 if (continue_block == 0) {
   1917                     scope = gen_zir.parent;
   1918                     continue;
   1919                 }
   1920                 if (break_label != 0) blk: {
   1921                     if (gen_zir.label) |*label| {
   1922                         if (try astgen.tokenIdentEql(label.token, break_label)) {
   1923                             label.used = true;
   1924                             break :blk;
   1925                         }
   1926                     }
   1927                     // found continue but either it has a different label, or no label
   1928                     scope = gen_zir.parent;
   1929                     continue;
   1930                 }
   1931 
   1932                 const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline or gen_zir.force_comptime)
   1933                     .break_inline
   1934                 else
   1935                     .@"break";
   1936                 _ = try parent_gz.addBreak(break_tag, continue_block, .void_value);
   1937                 return Zir.Inst.Ref.unreachable_value;
   1938             },
   1939             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   1940             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   1941             .defer_normal => {
   1942                 const defer_scope = scope.cast(Scope.Defer).?;
   1943                 scope = defer_scope.parent;
   1944                 const expr_node = node_datas[defer_scope.defer_node].rhs;
   1945                 try unusedResultDeferExpr(parent_gz, defer_scope, defer_scope.parent, expr_node);
   1946             },
   1947             .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   1948             .namespace => break,
   1949             .top => unreachable,
   1950         }
   1951     }
   1952     if (break_label != 0) {
   1953         const label_name = try astgen.identifierTokenString(break_label);
   1954         return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
   1955     } else {
   1956         return astgen.failNode(node, "continue expression outside loop", .{});
   1957     }
   1958 }
   1959 
   1960 fn blockExpr(
   1961     gz: *GenZir,
   1962     scope: *Scope,
   1963     rl: ResultLoc,
   1964     block_node: Ast.Node.Index,
   1965     statements: []const Ast.Node.Index,
   1966 ) InnerError!Zir.Inst.Ref {
   1967     const tracy = trace(@src());
   1968     defer tracy.end();
   1969 
   1970     const astgen = gz.astgen;
   1971     const tree = astgen.tree;
   1972     const main_tokens = tree.nodes.items(.main_token);
   1973     const token_tags = tree.tokens.items(.tag);
   1974 
   1975     const lbrace = main_tokens[block_node];
   1976     if (token_tags[lbrace - 1] == .colon and
   1977         token_tags[lbrace - 2] == .identifier)
   1978     {
   1979         return labeledBlockExpr(gz, scope, rl, block_node, statements);
   1980     }
   1981 
   1982     try blockExprStmts(gz, scope, statements);
   1983     return rvalue(gz, rl, .void_value, block_node);
   1984 }
   1985 
   1986 fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.TokenIndex) !void {
   1987     // Look for the label in the scope.
   1988     var scope = parent_scope;
   1989     while (true) {
   1990         switch (scope.tag) {
   1991             .gen_zir => {
   1992                 const gen_zir = scope.cast(GenZir).?;
   1993                 if (gen_zir.label) |prev_label| {
   1994                     if (try astgen.tokenIdentEql(label, prev_label.token)) {
   1995                         const label_name = try astgen.identifierTokenString(label);
   1996                         return astgen.failTokNotes(label, "redefinition of label '{s}'", .{
   1997                             label_name,
   1998                         }, &[_]u32{
   1999                             try astgen.errNoteTok(
   2000                                 prev_label.token,
   2001                                 "previous definition here",
   2002                                 .{},
   2003                             ),
   2004                         });
   2005                     }
   2006                 }
   2007                 scope = gen_zir.parent;
   2008             },
   2009             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   2010             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   2011             .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   2012             .namespace => break,
   2013             .top => unreachable,
   2014         }
   2015     }
   2016 }
   2017 
   2018 fn labeledBlockExpr(
   2019     gz: *GenZir,
   2020     parent_scope: *Scope,
   2021     rl: ResultLoc,
   2022     block_node: Ast.Node.Index,
   2023     statements: []const Ast.Node.Index,
   2024 ) InnerError!Zir.Inst.Ref {
   2025     const tracy = trace(@src());
   2026     defer tracy.end();
   2027 
   2028     const astgen = gz.astgen;
   2029     const tree = astgen.tree;
   2030     const main_tokens = tree.nodes.items(.main_token);
   2031     const token_tags = tree.tokens.items(.tag);
   2032 
   2033     const lbrace = main_tokens[block_node];
   2034     const label_token = lbrace - 2;
   2035     assert(token_tags[label_token] == .identifier);
   2036 
   2037     try astgen.checkLabelRedefinition(parent_scope, label_token);
   2038 
   2039     // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct
   2040     // so that break statements can reference it.
   2041     const block_tag: Zir.Inst.Tag = if (gz.force_comptime) .block_inline else .block;
   2042     const block_inst = try gz.makeBlockInst(block_tag, block_node);
   2043     try gz.instructions.append(astgen.gpa, block_inst);
   2044 
   2045     var block_scope = gz.makeSubBlock(parent_scope);
   2046     block_scope.label = GenZir.Label{
   2047         .token = label_token,
   2048         .block_inst = block_inst,
   2049     };
   2050     block_scope.setBreakResultLoc(rl);
   2051     defer block_scope.unstack();
   2052     defer block_scope.labeled_breaks.deinit(astgen.gpa);
   2053 
   2054     try blockExprStmts(&block_scope, &block_scope.base, statements);
   2055     if (!block_scope.endsWithNoReturn()) {
   2056         const break_tag: Zir.Inst.Tag = if (block_scope.force_comptime) .break_inline else .@"break";
   2057         _ = try block_scope.addBreak(break_tag, block_inst, .void_value);
   2058     }
   2059 
   2060     if (!block_scope.label.?.used) {
   2061         try astgen.appendErrorTok(label_token, "unused block label", .{});
   2062     }
   2063 
   2064     const zir_datas = gz.astgen.instructions.items(.data);
   2065     const zir_tags = gz.astgen.instructions.items(.tag);
   2066     const strat = rl.strategy(&block_scope);
   2067     switch (strat.tag) {
   2068         .break_void => {
   2069             // The code took advantage of the result location as a pointer.
   2070             // Turn the break instruction operands into void.
   2071             for (block_scope.labeled_breaks.items) |br| {
   2072                 zir_datas[br.br].@"break".operand = .void_value;
   2073             }
   2074             try block_scope.setBlockBody(block_inst);
   2075 
   2076             return indexToRef(block_inst);
   2077         },
   2078         .break_operand => {
   2079             // All break operands are values that did not use the result location pointer.
   2080             // The break instructions need to have their operands coerced if the
   2081             // block's result location is a `ty`. In this case we overwrite the
   2082             // `store_to_block_ptr` instruction with an `as` instruction and repurpose
   2083             // it as the break operand.
   2084             // This corresponds to similar code in `setCondBrPayloadElideBlockStorePtr`.
   2085             if (block_scope.rl_ty_inst != .none) {
   2086                 for (block_scope.labeled_breaks.items) |br| {
   2087                     // We expect the `store_to_block_ptr` to be created between 1-3 instructions
   2088                     // prior to the break.
   2089                     var search_index = br.search -| 3;
   2090                     while (search_index < br.search) : (search_index += 1) {
   2091                         if (zir_tags[search_index] == .store_to_block_ptr and
   2092                             zir_datas[search_index].bin.lhs == block_scope.rl_ptr)
   2093                         {
   2094                             zir_tags[search_index] = .as;
   2095                             zir_datas[search_index].bin = .{
   2096                                 .lhs = block_scope.rl_ty_inst,
   2097                                 .rhs = zir_datas[br.br].@"break".operand,
   2098                             };
   2099                             zir_datas[br.br].@"break".operand = indexToRef(search_index);
   2100                             break;
   2101                         }
   2102                     } else unreachable;
   2103                 }
   2104             }
   2105             try block_scope.setBlockBody(block_inst);
   2106             const block_ref = indexToRef(block_inst);
   2107             switch (rl) {
   2108                 .ref => return block_ref,
   2109                 else => return rvalue(gz, rl, block_ref, block_node),
   2110             }
   2111         },
   2112     }
   2113 }
   2114 
   2115 fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Node.Index) !void {
   2116     const astgen = gz.astgen;
   2117     const tree = astgen.tree;
   2118     const node_tags = tree.nodes.items(.tag);
   2119 
   2120     if (statements.len == 0) return;
   2121 
   2122     try gz.addDbgBlockBegin();
   2123 
   2124     var block_arena = std.heap.ArenaAllocator.init(gz.astgen.gpa);
   2125     defer block_arena.deinit();
   2126     const block_arena_allocator = block_arena.allocator();
   2127 
   2128     var noreturn_src_node: Ast.Node.Index = 0;
   2129     var scope = parent_scope;
   2130     for (statements) |statement| {
   2131         if (noreturn_src_node != 0) {
   2132             try astgen.appendErrorNodeNotes(
   2133                 statement,
   2134                 "unreachable code",
   2135                 .{},
   2136                 &[_]u32{
   2137                     try astgen.errNoteNode(
   2138                         noreturn_src_node,
   2139                         "control flow is diverted here",
   2140                         .{},
   2141                     ),
   2142                 },
   2143             );
   2144         }
   2145         switch (node_tags[statement]) {
   2146             // zig fmt: off
   2147             .global_var_decl  => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.globalVarDecl(statement)),
   2148             .local_var_decl   => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.localVarDecl(statement)),
   2149             .simple_var_decl  => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.simpleVarDecl(statement)),
   2150             .aligned_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.alignedVarDecl(statement)),
   2151 
   2152             .@"defer"    => scope = try makeDeferScope(gz.astgen, scope, statement, block_arena_allocator, .defer_normal),
   2153             .@"errdefer" => scope = try makeDeferScope(gz.astgen, scope, statement, block_arena_allocator, .defer_error),
   2154 
   2155             .assign => try assign(gz, scope, statement),
   2156 
   2157             .assign_shl => try assignShift(gz, scope, statement, .shl),
   2158             .assign_shr => try assignShift(gz, scope, statement, .shr),
   2159 
   2160             .assign_bit_and  => try assignOp(gz, scope, statement, .bit_and),
   2161             .assign_bit_or   => try assignOp(gz, scope, statement, .bit_or),
   2162             .assign_bit_xor  => try assignOp(gz, scope, statement, .xor),
   2163             .assign_div      => try assignOp(gz, scope, statement, .div),
   2164             .assign_sub      => try assignOp(gz, scope, statement, .sub),
   2165             .assign_sub_wrap => try assignOp(gz, scope, statement, .subwrap),
   2166             .assign_mod      => try assignOp(gz, scope, statement, .mod_rem),
   2167             .assign_add      => try assignOp(gz, scope, statement, .add),
   2168             .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap),
   2169             .assign_mul      => try assignOp(gz, scope, statement, .mul),
   2170             .assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap),
   2171 
   2172             else => noreturn_src_node = try unusedResultExpr(gz, scope, statement),
   2173             // zig fmt: on
   2174         }
   2175     }
   2176 
   2177     try gz.addDbgBlockEnd();
   2178 
   2179     try genDefers(gz, parent_scope, scope, .normal_only);
   2180     try checkUsed(gz, parent_scope, scope);
   2181 }
   2182 
   2183 fn unusedResultDeferExpr(gz: *GenZir, defer_scope: *Scope.Defer, expr_scope: *Scope, expr_node: Ast.Node.Index) InnerError!void {
   2184     const astgen = gz.astgen;
   2185     const prev_offset = astgen.source_offset;
   2186     const prev_line = astgen.source_line;
   2187     const prev_column = astgen.source_column;
   2188     defer {
   2189         astgen.source_offset = prev_offset;
   2190         astgen.source_line = prev_line;
   2191         astgen.source_column = prev_column;
   2192     }
   2193     astgen.source_offset = defer_scope.source_offset;
   2194     astgen.source_line = defer_scope.source_line;
   2195     astgen.source_column = defer_scope.source_column;
   2196     _ = try unusedResultExpr(gz, expr_scope, expr_node);
   2197 }
   2198 
   2199 /// Returns AST source node of the thing that is noreturn if the statement is
   2200 /// definitely `noreturn`. Otherwise returns 0.
   2201 fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) InnerError!Ast.Node.Index {
   2202     try emitDbgNode(gz, statement);
   2203     // We need to emit an error if the result is not `noreturn` or `void`, but
   2204     // we want to avoid adding the ZIR instruction if possible for performance.
   2205     const maybe_unused_result = try expr(gz, scope, .none, statement);
   2206     var noreturn_src_node: Ast.Node.Index = 0;
   2207     const elide_check = if (refToIndex(maybe_unused_result)) |inst| b: {
   2208         // Note that this array becomes invalid after appending more items to it
   2209         // in the above while loop.
   2210         const zir_tags = gz.astgen.instructions.items(.tag);
   2211         switch (zir_tags[inst]) {
   2212             // For some instructions, modify the zir data
   2213             // so we can avoid a separate ensure_result_used instruction.
   2214             .call => {
   2215                 const extra_index = gz.astgen.instructions.items(.data)[inst].pl_node.payload_index;
   2216                 const slot = &gz.astgen.extra.items[extra_index];
   2217                 var flags = @bitCast(Zir.Inst.Call.Flags, slot.*);
   2218                 flags.ensure_result_used = true;
   2219                 slot.* = @bitCast(u32, flags);
   2220                 break :b true;
   2221             },
   2222             .builtin_call => {
   2223                 const extra_index = gz.astgen.instructions.items(.data)[inst].pl_node.payload_index;
   2224                 const slot = &gz.astgen.extra.items[extra_index];
   2225                 var flags = @bitCast(Zir.Inst.BuiltinCall.Flags, slot.*);
   2226                 flags.ensure_result_used = true;
   2227                 slot.* = @bitCast(u32, flags);
   2228                 break :b true;
   2229             },
   2230 
   2231             // ZIR instructions that might be a type other than `noreturn` or `void`.
   2232             .add,
   2233             .addwrap,
   2234             .add_sat,
   2235             .param,
   2236             .param_comptime,
   2237             .param_anytype,
   2238             .param_anytype_comptime,
   2239             .alloc,
   2240             .alloc_mut,
   2241             .alloc_comptime_mut,
   2242             .alloc_inferred,
   2243             .alloc_inferred_mut,
   2244             .alloc_inferred_comptime,
   2245             .alloc_inferred_comptime_mut,
   2246             .make_ptr_const,
   2247             .array_cat,
   2248             .array_mul,
   2249             .array_type,
   2250             .array_type_sentinel,
   2251             .elem_type_index,
   2252             .vector_type,
   2253             .indexable_ptr_len,
   2254             .anyframe_type,
   2255             .as,
   2256             .as_node,
   2257             .bit_and,
   2258             .bitcast,
   2259             .bit_or,
   2260             .block,
   2261             .block_inline,
   2262             .suspend_block,
   2263             .loop,
   2264             .bool_br_and,
   2265             .bool_br_or,
   2266             .bool_not,
   2267             .cmp_lt,
   2268             .cmp_lte,
   2269             .cmp_eq,
   2270             .cmp_gte,
   2271             .cmp_gt,
   2272             .cmp_neq,
   2273             .coerce_result_ptr,
   2274             .decl_ref,
   2275             .decl_val,
   2276             .load,
   2277             .div,
   2278             .elem_ptr,
   2279             .elem_val,
   2280             .elem_ptr_node,
   2281             .elem_ptr_imm,
   2282             .elem_val_node,
   2283             .field_ptr,
   2284             .field_val,
   2285             .field_call_bind,
   2286             .field_ptr_named,
   2287             .field_val_named,
   2288             .func,
   2289             .func_inferred,
   2290             .func_fancy,
   2291             .int,
   2292             .int_big,
   2293             .float,
   2294             .float128,
   2295             .int_type,
   2296             .is_non_null,
   2297             .is_non_null_ptr,
   2298             .is_non_err,
   2299             .is_non_err_ptr,
   2300             .mod_rem,
   2301             .mul,
   2302             .mulwrap,
   2303             .mul_sat,
   2304             .ref,
   2305             .shl,
   2306             .shl_sat,
   2307             .shr,
   2308             .str,
   2309             .sub,
   2310             .subwrap,
   2311             .sub_sat,
   2312             .negate,
   2313             .negate_wrap,
   2314             .typeof,
   2315             .typeof_builtin,
   2316             .xor,
   2317             .optional_type,
   2318             .optional_payload_safe,
   2319             .optional_payload_unsafe,
   2320             .optional_payload_safe_ptr,
   2321             .optional_payload_unsafe_ptr,
   2322             .err_union_payload_safe,
   2323             .err_union_payload_unsafe,
   2324             .err_union_payload_safe_ptr,
   2325             .err_union_payload_unsafe_ptr,
   2326             .err_union_code,
   2327             .err_union_code_ptr,
   2328             .ptr_type,
   2329             .ptr_type_simple,
   2330             .enum_literal,
   2331             .merge_error_sets,
   2332             .error_union_type,
   2333             .bit_not,
   2334             .error_value,
   2335             .error_to_int,
   2336             .int_to_error,
   2337             .slice_start,
   2338             .slice_end,
   2339             .slice_sentinel,
   2340             .import,
   2341             .switch_block,
   2342             .switch_cond,
   2343             .switch_cond_ref,
   2344             .switch_capture,
   2345             .switch_capture_ref,
   2346             .switch_capture_multi,
   2347             .switch_capture_multi_ref,
   2348             .struct_init_empty,
   2349             .struct_init,
   2350             .struct_init_ref,
   2351             .struct_init_anon,
   2352             .struct_init_anon_ref,
   2353             .array_init,
   2354             .array_init_anon,
   2355             .array_init_ref,
   2356             .array_init_anon_ref,
   2357             .union_init,
   2358             .field_type,
   2359             .field_type_ref,
   2360             .error_set_decl,
   2361             .error_set_decl_anon,
   2362             .error_set_decl_func,
   2363             .int_to_enum,
   2364             .enum_to_int,
   2365             .type_info,
   2366             .size_of,
   2367             .bit_size_of,
   2368             .log2_int_type,
   2369             .typeof_log2_int_type,
   2370             .ptr_to_int,
   2371             .align_of,
   2372             .bool_to_int,
   2373             .embed_file,
   2374             .error_name,
   2375             .sqrt,
   2376             .sin,
   2377             .cos,
   2378             .tan,
   2379             .exp,
   2380             .exp2,
   2381             .log,
   2382             .log2,
   2383             .log10,
   2384             .fabs,
   2385             .floor,
   2386             .ceil,
   2387             .trunc,
   2388             .round,
   2389             .tag_name,
   2390             .reify,
   2391             .type_name,
   2392             .frame_type,
   2393             .frame_size,
   2394             .float_to_int,
   2395             .int_to_float,
   2396             .int_to_ptr,
   2397             .float_cast,
   2398             .int_cast,
   2399             .ptr_cast,
   2400             .truncate,
   2401             .align_cast,
   2402             .has_decl,
   2403             .has_field,
   2404             .clz,
   2405             .ctz,
   2406             .pop_count,
   2407             .byte_swap,
   2408             .bit_reverse,
   2409             .div_exact,
   2410             .div_floor,
   2411             .div_trunc,
   2412             .mod,
   2413             .rem,
   2414             .shl_exact,
   2415             .shr_exact,
   2416             .bit_offset_of,
   2417             .offset_of,
   2418             .cmpxchg_strong,
   2419             .cmpxchg_weak,
   2420             .splat,
   2421             .reduce,
   2422             .shuffle,
   2423             .select,
   2424             .atomic_load,
   2425             .atomic_rmw,
   2426             .mul_add,
   2427             .field_parent_ptr,
   2428             .maximum,
   2429             .minimum,
   2430             .builtin_async_call,
   2431             .c_import,
   2432             .@"resume",
   2433             .@"await",
   2434             .ret_err_value_code,
   2435             .closure_get,
   2436             .array_base_ptr,
   2437             .field_base_ptr,
   2438             .param_type,
   2439             .ret_ptr,
   2440             .ret_type,
   2441             .@"try",
   2442             .try_ptr,
   2443             //.try_inline,
   2444             //.try_ptr_inline,
   2445             => break :b false,
   2446 
   2447             .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) {
   2448                 .breakpoint,
   2449                 .fence,
   2450                 .set_align_stack,
   2451                 .set_float_mode,
   2452                 => break :b true,
   2453                 else => break :b false,
   2454             },
   2455 
   2456             // ZIR instructions that are always `noreturn`.
   2457             .@"break",
   2458             .break_inline,
   2459             .condbr,
   2460             .condbr_inline,
   2461             .compile_error,
   2462             .ret_node,
   2463             .ret_load,
   2464             .ret_tok,
   2465             .ret_err_value,
   2466             .@"unreachable",
   2467             .repeat,
   2468             .repeat_inline,
   2469             .panic,
   2470             => {
   2471                 noreturn_src_node = statement;
   2472                 break :b true;
   2473             },
   2474 
   2475             // ZIR instructions that are always `void`.
   2476             .dbg_stmt,
   2477             .dbg_var_ptr,
   2478             .dbg_var_val,
   2479             .dbg_block_begin,
   2480             .dbg_block_end,
   2481             .ensure_result_used,
   2482             .ensure_result_non_error,
   2483             .@"export",
   2484             .export_value,
   2485             .set_eval_branch_quota,
   2486             .ensure_err_payload_void,
   2487             .atomic_store,
   2488             .store,
   2489             .store_node,
   2490             .store_to_block_ptr,
   2491             .store_to_inferred_ptr,
   2492             .resolve_inferred_alloc,
   2493             .validate_struct_init,
   2494             .validate_struct_init_comptime,
   2495             .validate_array_init,
   2496             .validate_array_init_comptime,
   2497             .set_cold,
   2498             .set_runtime_safety,
   2499             .closure_capture,
   2500             .memcpy,
   2501             .memset,
   2502             .validate_array_init_ty,
   2503             .validate_struct_init_ty,
   2504             .validate_deref,
   2505             => break :b true,
   2506         }
   2507     } else switch (maybe_unused_result) {
   2508         .none => unreachable,
   2509 
   2510         .unreachable_value => b: {
   2511             noreturn_src_node = statement;
   2512             break :b true;
   2513         },
   2514 
   2515         .void_value => true,
   2516 
   2517         else => false,
   2518     };
   2519     if (!elide_check) {
   2520         _ = try gz.addUnNode(.ensure_result_used, maybe_unused_result, statement);
   2521     }
   2522     return noreturn_src_node;
   2523 }
   2524 
   2525 fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct {
   2526     have_any: bool,
   2527     have_normal: bool,
   2528     have_err: bool,
   2529     need_err_code: bool,
   2530 } {
   2531     const tree = astgen.tree;
   2532     const node_datas = tree.nodes.items(.data);
   2533 
   2534     var have_normal = false;
   2535     var have_err = false;
   2536     var need_err_code = false;
   2537     var scope = inner_scope;
   2538     while (scope != outer_scope) {
   2539         switch (scope.tag) {
   2540             .gen_zir => scope = scope.cast(GenZir).?.parent,
   2541             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   2542             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   2543             .defer_normal => {
   2544                 const defer_scope = scope.cast(Scope.Defer).?;
   2545                 scope = defer_scope.parent;
   2546 
   2547                 have_normal = true;
   2548             },
   2549             .defer_error => {
   2550                 const defer_scope = scope.cast(Scope.Defer).?;
   2551                 scope = defer_scope.parent;
   2552 
   2553                 have_err = true;
   2554 
   2555                 const have_err_payload = node_datas[defer_scope.defer_node].lhs != 0;
   2556                 need_err_code = need_err_code or have_err_payload;
   2557             },
   2558             .namespace => unreachable,
   2559             .top => unreachable,
   2560         }
   2561     }
   2562     return .{
   2563         .have_any = have_normal or have_err,
   2564         .have_normal = have_normal,
   2565         .have_err = have_err,
   2566         .need_err_code = need_err_code,
   2567     };
   2568 }
   2569 
   2570 const DefersToEmit = union(enum) {
   2571     both: Zir.Inst.Ref, // err code
   2572     both_sans_err,
   2573     normal_only,
   2574 };
   2575 
   2576 fn genDefers(
   2577     gz: *GenZir,
   2578     outer_scope: *Scope,
   2579     inner_scope: *Scope,
   2580     which_ones: DefersToEmit,
   2581 ) InnerError!void {
   2582     const astgen = gz.astgen;
   2583     const tree = astgen.tree;
   2584     const node_datas = tree.nodes.items(.data);
   2585 
   2586     var scope = inner_scope;
   2587     while (scope != outer_scope) {
   2588         switch (scope.tag) {
   2589             .gen_zir => scope = scope.cast(GenZir).?.parent,
   2590             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   2591             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   2592             .defer_normal => {
   2593                 const defer_scope = scope.cast(Scope.Defer).?;
   2594                 scope = defer_scope.parent;
   2595                 const expr_node = node_datas[defer_scope.defer_node].rhs;
   2596                 const prev_in_defer = gz.in_defer;
   2597                 gz.in_defer = true;
   2598                 defer gz.in_defer = prev_in_defer;
   2599                 try unusedResultDeferExpr(gz, defer_scope, defer_scope.parent, expr_node);
   2600             },
   2601             .defer_error => {
   2602                 const defer_scope = scope.cast(Scope.Defer).?;
   2603                 scope = defer_scope.parent;
   2604                 switch (which_ones) {
   2605                     .both_sans_err => {
   2606                         const expr_node = node_datas[defer_scope.defer_node].rhs;
   2607                         const prev_in_defer = gz.in_defer;
   2608                         gz.in_defer = true;
   2609                         defer gz.in_defer = prev_in_defer;
   2610                         try unusedResultDeferExpr(gz, defer_scope, defer_scope.parent, expr_node);
   2611                     },
   2612                     .both => |err_code| {
   2613                         const expr_node = node_datas[defer_scope.defer_node].rhs;
   2614                         const payload_token = node_datas[defer_scope.defer_node].lhs;
   2615                         const prev_in_defer = gz.in_defer;
   2616                         gz.in_defer = true;
   2617                         defer gz.in_defer = prev_in_defer;
   2618                         var local_val_scope: Scope.LocalVal = undefined;
   2619                         try gz.addDbgBlockBegin();
   2620                         const sub_scope = if (payload_token == 0) defer_scope.parent else blk: {
   2621                             const ident_name = try astgen.identAsString(payload_token);
   2622                             local_val_scope = .{
   2623                                 .parent = defer_scope.parent,
   2624                                 .gen_zir = gz,
   2625                                 .name = ident_name,
   2626                                 .inst = err_code,
   2627                                 .token_src = payload_token,
   2628                                 .id_cat = .@"capture",
   2629                             };
   2630                             try gz.addDbgVar(.dbg_var_val, ident_name, err_code);
   2631                             break :blk &local_val_scope.base;
   2632                         };
   2633                         try unusedResultDeferExpr(gz, defer_scope, sub_scope, expr_node);
   2634                         try gz.addDbgBlockEnd();
   2635                     },
   2636                     .normal_only => continue,
   2637                 }
   2638             },
   2639             .namespace => unreachable,
   2640             .top => unreachable,
   2641         }
   2642     }
   2643 }
   2644 
   2645 fn checkUsed(
   2646     gz: *GenZir,
   2647     outer_scope: *Scope,
   2648     inner_scope: *Scope,
   2649 ) InnerError!void {
   2650     const astgen = gz.astgen;
   2651 
   2652     var scope = inner_scope;
   2653     while (scope != outer_scope) {
   2654         switch (scope.tag) {
   2655             .gen_zir => scope = scope.cast(GenZir).?.parent,
   2656             .local_val => {
   2657                 const s = scope.cast(Scope.LocalVal).?;
   2658                 if (!s.used) {
   2659                     try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
   2660                 }
   2661                 scope = s.parent;
   2662             },
   2663             .local_ptr => {
   2664                 const s = scope.cast(Scope.LocalPtr).?;
   2665                 if (!s.used) {
   2666                     try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
   2667                 }
   2668                 scope = s.parent;
   2669             },
   2670             .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   2671             .namespace => unreachable,
   2672             .top => unreachable,
   2673         }
   2674     }
   2675 }
   2676 
   2677 fn makeDeferScope(
   2678     astgen: *AstGen,
   2679     scope: *Scope,
   2680     node: Ast.Node.Index,
   2681     block_arena: Allocator,
   2682     scope_tag: Scope.Tag,
   2683 ) InnerError!*Scope {
   2684     const tree = astgen.tree;
   2685     const node_datas = tree.nodes.items(.data);
   2686     const expr_node = node_datas[node].rhs;
   2687     const token_starts = tree.tokens.items(.start);
   2688     const node_start = token_starts[tree.firstToken(expr_node)];
   2689     const defer_scope = try block_arena.create(Scope.Defer);
   2690     astgen.advanceSourceCursor(node_start);
   2691 
   2692     defer_scope.* = .{
   2693         .base = .{ .tag = scope_tag },
   2694         .parent = scope,
   2695         .defer_node = node,
   2696         .source_offset = astgen.source_offset,
   2697         .source_line = astgen.source_line,
   2698         .source_column = astgen.source_column,
   2699     };
   2700     return &defer_scope.base;
   2701 }
   2702 
   2703 fn varDecl(
   2704     gz: *GenZir,
   2705     scope: *Scope,
   2706     node: Ast.Node.Index,
   2707     block_arena: Allocator,
   2708     var_decl: Ast.full.VarDecl,
   2709 ) InnerError!*Scope {
   2710     try emitDbgNode(gz, node);
   2711     const astgen = gz.astgen;
   2712     const tree = astgen.tree;
   2713     const token_tags = tree.tokens.items(.tag);
   2714     const main_tokens = tree.nodes.items(.main_token);
   2715 
   2716     const name_token = var_decl.ast.mut_token + 1;
   2717     const ident_name_raw = tree.tokenSlice(name_token);
   2718     if (mem.eql(u8, ident_name_raw, "_")) {
   2719         return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{});
   2720     }
   2721     const ident_name = try astgen.identAsString(name_token);
   2722 
   2723     try astgen.detectLocalShadowing(scope, ident_name, name_token, ident_name_raw);
   2724 
   2725     if (var_decl.ast.init_node == 0) {
   2726         return astgen.failNode(node, "variables must be initialized", .{});
   2727     }
   2728 
   2729     if (var_decl.ast.addrspace_node != 0) {
   2730         return astgen.failTok(main_tokens[var_decl.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw});
   2731     }
   2732 
   2733     if (var_decl.ast.section_node != 0) {
   2734         return astgen.failTok(main_tokens[var_decl.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw});
   2735     }
   2736 
   2737     const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node != 0)
   2738         try expr(gz, scope, align_rl, var_decl.ast.align_node)
   2739     else
   2740         .none;
   2741 
   2742     switch (token_tags[var_decl.ast.mut_token]) {
   2743         .keyword_const => {
   2744             if (var_decl.comptime_token) |comptime_token| {
   2745                 try astgen.appendErrorTok(comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
   2746             }
   2747 
   2748             // Depending on the type of AST the initialization expression is, we may need an lvalue
   2749             // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
   2750             // the variable, no memory location needed.
   2751             const type_node = var_decl.ast.type_node;
   2752             if (align_inst == .none and
   2753                 !nodeMayNeedMemoryLocation(tree, var_decl.ast.init_node, type_node != 0))
   2754             {
   2755                 const result_loc: ResultLoc = if (type_node != 0) .{
   2756                     .ty = try typeExpr(gz, scope, type_node),
   2757                 } else .none;
   2758                 const prev_anon_name_strategy = gz.anon_name_strategy;
   2759                 gz.anon_name_strategy = .dbg_var;
   2760                 const init_inst = try reachableExpr(gz, scope, result_loc, var_decl.ast.init_node, node);
   2761                 gz.anon_name_strategy = prev_anon_name_strategy;
   2762 
   2763                 try gz.addDbgVar(.dbg_var_val, ident_name, init_inst);
   2764 
   2765                 const sub_scope = try block_arena.create(Scope.LocalVal);
   2766                 sub_scope.* = .{
   2767                     .parent = scope,
   2768                     .gen_zir = gz,
   2769                     .name = ident_name,
   2770                     .inst = init_inst,
   2771                     .token_src = name_token,
   2772                     .id_cat = .@"local constant",
   2773                 };
   2774                 return &sub_scope.base;
   2775             }
   2776 
   2777             const is_comptime = gz.force_comptime or
   2778                 tree.nodes.items(.tag)[var_decl.ast.init_node] == .@"comptime";
   2779 
   2780             // Detect whether the initialization expression actually uses the
   2781             // result location pointer.
   2782             var init_scope = gz.makeSubBlock(scope);
   2783             // we may add more instructions to gz before stacking init_scope
   2784             init_scope.instructions_top = GenZir.unstacked_top;
   2785             init_scope.anon_name_strategy = .dbg_var;
   2786             defer init_scope.unstack();
   2787 
   2788             var resolve_inferred_alloc: Zir.Inst.Ref = .none;
   2789             var opt_type_inst: Zir.Inst.Ref = .none;
   2790             if (type_node != 0) {
   2791                 const type_inst = try typeExpr(gz, &init_scope.base, type_node);
   2792                 opt_type_inst = type_inst;
   2793                 if (align_inst == .none) {
   2794                     init_scope.instructions_top = gz.instructions.items.len;
   2795                     init_scope.rl_ptr = try init_scope.addUnNode(.alloc, type_inst, node);
   2796                 } else {
   2797                     init_scope.rl_ptr = try gz.addAllocExtended(.{
   2798                         .node = node,
   2799                         .type_inst = type_inst,
   2800                         .align_inst = align_inst,
   2801                         .is_const = true,
   2802                         .is_comptime = is_comptime,
   2803                     });
   2804                     init_scope.instructions_top = gz.instructions.items.len;
   2805                 }
   2806                 init_scope.rl_ty_inst = type_inst;
   2807             } else {
   2808                 const alloc = if (align_inst == .none) alloc: {
   2809                     init_scope.instructions_top = gz.instructions.items.len;
   2810                     const tag: Zir.Inst.Tag = if (is_comptime)
   2811                         .alloc_inferred_comptime
   2812                     else
   2813                         .alloc_inferred;
   2814                     break :alloc try init_scope.addNode(tag, node);
   2815                 } else alloc: {
   2816                     const ref = try gz.addAllocExtended(.{
   2817                         .node = node,
   2818                         .type_inst = .none,
   2819                         .align_inst = align_inst,
   2820                         .is_const = true,
   2821                         .is_comptime = is_comptime,
   2822                     });
   2823                     init_scope.instructions_top = gz.instructions.items.len;
   2824                     break :alloc ref;
   2825                 };
   2826                 resolve_inferred_alloc = alloc;
   2827                 init_scope.rl_ptr = alloc;
   2828                 init_scope.rl_ty_inst = .none;
   2829             }
   2830             const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope };
   2831             const init_inst = try reachableExpr(&init_scope, &init_scope.base, init_result_loc, var_decl.ast.init_node, node);
   2832 
   2833             const zir_tags = astgen.instructions.items(.tag);
   2834             const zir_datas = astgen.instructions.items(.data);
   2835 
   2836             if (align_inst == .none and init_scope.rvalue_rl_count == 1) {
   2837                 // Result location pointer not used. We don't need an alloc for this
   2838                 // const local, and type inference becomes trivial.
   2839                 // Implicitly move the init_scope instructions into the parent scope,
   2840                 // then elide the alloc instruction and the store_to_block_ptr instruction.
   2841                 var src = init_scope.instructions_top;
   2842                 var dst = src;
   2843                 init_scope.instructions_top = GenZir.unstacked_top;
   2844                 while (src < gz.instructions.items.len) : (src += 1) {
   2845                     const src_inst = gz.instructions.items[src];
   2846                     if (indexToRef(src_inst) == init_scope.rl_ptr) continue;
   2847                     if (zir_tags[src_inst] == .store_to_block_ptr) {
   2848                         if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) continue;
   2849                     }
   2850                     gz.instructions.items[dst] = src_inst;
   2851                     dst += 1;
   2852                 }
   2853                 gz.instructions.items.len = dst;
   2854 
   2855                 // In case the result location did not do the coercion
   2856                 // for us so we must do it here.
   2857                 const coerced_init = if (opt_type_inst != .none)
   2858                     try gz.addBin(.as, opt_type_inst, init_inst)
   2859                 else
   2860                     init_inst;
   2861 
   2862                 try gz.addDbgVar(.dbg_var_val, ident_name, coerced_init);
   2863 
   2864                 const sub_scope = try block_arena.create(Scope.LocalVal);
   2865                 sub_scope.* = .{
   2866                     .parent = scope,
   2867                     .gen_zir = gz,
   2868                     .name = ident_name,
   2869                     .inst = coerced_init,
   2870                     .token_src = name_token,
   2871                     .id_cat = .@"local constant",
   2872                 };
   2873                 return &sub_scope.base;
   2874             }
   2875             // The initialization expression took advantage of the result location
   2876             // of the const local. In this case we will create an alloc and a LocalPtr for it.
   2877             // Implicitly move the init_scope instructions into the parent scope, then swap
   2878             // store_to_block_ptr for store_to_inferred_ptr.
   2879 
   2880             var src = init_scope.instructions_top;
   2881             init_scope.instructions_top = GenZir.unstacked_top;
   2882             while (src < gz.instructions.items.len) : (src += 1) {
   2883                 const src_inst = gz.instructions.items[src];
   2884                 if (zir_tags[src_inst] == .store_to_block_ptr) {
   2885                     if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) {
   2886                         if (type_node != 0) {
   2887                             zir_tags[src_inst] = .store;
   2888                         } else {
   2889                             zir_tags[src_inst] = .store_to_inferred_ptr;
   2890                         }
   2891                     }
   2892                 }
   2893             }
   2894             if (resolve_inferred_alloc != .none) {
   2895                 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
   2896             }
   2897             const const_ptr = try gz.addUnNode(.make_ptr_const, init_scope.rl_ptr, node);
   2898 
   2899             try gz.addDbgVar(.dbg_var_ptr, ident_name, const_ptr);
   2900 
   2901             const sub_scope = try block_arena.create(Scope.LocalPtr);
   2902             sub_scope.* = .{
   2903                 .parent = scope,
   2904                 .gen_zir = gz,
   2905                 .name = ident_name,
   2906                 .ptr = const_ptr,
   2907                 .token_src = name_token,
   2908                 .maybe_comptime = true,
   2909                 .id_cat = .@"local constant",
   2910             };
   2911             return &sub_scope.base;
   2912         },
   2913         .keyword_var => {
   2914             const old_rl_ty_inst = gz.rl_ty_inst;
   2915             defer gz.rl_ty_inst = old_rl_ty_inst;
   2916 
   2917             const is_comptime = var_decl.comptime_token != null or gz.force_comptime;
   2918             var resolve_inferred_alloc: Zir.Inst.Ref = .none;
   2919             const var_data: struct {
   2920                 result_loc: ResultLoc,
   2921                 alloc: Zir.Inst.Ref,
   2922             } = if (var_decl.ast.type_node != 0) a: {
   2923                 const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node);
   2924                 const alloc = alloc: {
   2925                     if (align_inst == .none) {
   2926                         const tag: Zir.Inst.Tag = if (is_comptime)
   2927                             .alloc_comptime_mut
   2928                         else
   2929                             .alloc_mut;
   2930                         break :alloc try gz.addUnNode(tag, type_inst, node);
   2931                     } else {
   2932                         break :alloc try gz.addAllocExtended(.{
   2933                             .node = node,
   2934                             .type_inst = type_inst,
   2935                             .align_inst = align_inst,
   2936                             .is_const = false,
   2937                             .is_comptime = is_comptime,
   2938                         });
   2939                     }
   2940                 };
   2941                 gz.rl_ty_inst = type_inst;
   2942                 break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
   2943             } else a: {
   2944                 const alloc = alloc: {
   2945                     if (align_inst == .none) {
   2946                         const tag: Zir.Inst.Tag = if (is_comptime)
   2947                             .alloc_inferred_comptime_mut
   2948                         else
   2949                             .alloc_inferred_mut;
   2950                         break :alloc try gz.addNode(tag, node);
   2951                     } else {
   2952                         break :alloc try gz.addAllocExtended(.{
   2953                             .node = node,
   2954                             .type_inst = .none,
   2955                             .align_inst = align_inst,
   2956                             .is_const = false,
   2957                             .is_comptime = is_comptime,
   2958                         });
   2959                     }
   2960                 };
   2961                 gz.rl_ty_inst = .none;
   2962                 resolve_inferred_alloc = alloc;
   2963                 break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc } };
   2964             };
   2965             const prev_anon_name_strategy = gz.anon_name_strategy;
   2966             gz.anon_name_strategy = .dbg_var;
   2967             _ = try reachableExprComptime(gz, scope, var_data.result_loc, var_decl.ast.init_node, node, is_comptime);
   2968             gz.anon_name_strategy = prev_anon_name_strategy;
   2969             if (resolve_inferred_alloc != .none) {
   2970                 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
   2971             }
   2972 
   2973             try gz.addDbgVar(.dbg_var_ptr, ident_name, var_data.alloc);
   2974 
   2975             const sub_scope = try block_arena.create(Scope.LocalPtr);
   2976             sub_scope.* = .{
   2977                 .parent = scope,
   2978                 .gen_zir = gz,
   2979                 .name = ident_name,
   2980                 .ptr = var_data.alloc,
   2981                 .token_src = name_token,
   2982                 .maybe_comptime = is_comptime,
   2983                 .id_cat = .@"local variable",
   2984             };
   2985             return &sub_scope.base;
   2986         },
   2987         else => unreachable,
   2988     }
   2989 }
   2990 
   2991 fn emitDbgNode(gz: *GenZir, node: Ast.Node.Index) !void {
   2992     // The instruction emitted here is for debugging runtime code.
   2993     // If the current block will be evaluated only during semantic analysis
   2994     // then no dbg_stmt ZIR instruction is needed.
   2995     if (gz.force_comptime) return;
   2996 
   2997     const astgen = gz.astgen;
   2998     astgen.advanceSourceCursorToNode(node);
   2999     const line = astgen.source_line - gz.decl_line;
   3000     const column = astgen.source_column;
   3001 
   3002     _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
   3003         .dbg_stmt = .{
   3004             .line = line,
   3005             .column = column,
   3006         },
   3007     } });
   3008 }
   3009 
   3010 fn assign(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void {
   3011     try emitDbgNode(gz, infix_node);
   3012     const astgen = gz.astgen;
   3013     const tree = astgen.tree;
   3014     const node_datas = tree.nodes.items(.data);
   3015     const main_tokens = tree.nodes.items(.main_token);
   3016     const node_tags = tree.nodes.items(.tag);
   3017 
   3018     const lhs = node_datas[infix_node].lhs;
   3019     const rhs = node_datas[infix_node].rhs;
   3020     if (node_tags[lhs] == .identifier) {
   3021         // This intentionally does not support `@"_"` syntax.
   3022         const ident_name = tree.tokenSlice(main_tokens[lhs]);
   3023         if (mem.eql(u8, ident_name, "_")) {
   3024             _ = try expr(gz, scope, .discard, rhs);
   3025             return;
   3026         }
   3027     }
   3028     const lvalue = try lvalExpr(gz, scope, lhs);
   3029     _ = try expr(gz, scope, .{ .ptr = lvalue }, rhs);
   3030 }
   3031 
   3032 fn assignOp(
   3033     gz: *GenZir,
   3034     scope: *Scope,
   3035     infix_node: Ast.Node.Index,
   3036     op_inst_tag: Zir.Inst.Tag,
   3037 ) InnerError!void {
   3038     try emitDbgNode(gz, infix_node);
   3039     const astgen = gz.astgen;
   3040     const tree = astgen.tree;
   3041     const node_datas = tree.nodes.items(.data);
   3042 
   3043     const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
   3044     const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
   3045     const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node);
   3046     const rhs = try expr(gz, scope, .{ .coerced_ty = lhs_type }, node_datas[infix_node].rhs);
   3047 
   3048     const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{
   3049         .lhs = lhs,
   3050         .rhs = rhs,
   3051     });
   3052     _ = try gz.addBin(.store, lhs_ptr, result);
   3053 }
   3054 
   3055 fn assignShift(
   3056     gz: *GenZir,
   3057     scope: *Scope,
   3058     infix_node: Ast.Node.Index,
   3059     op_inst_tag: Zir.Inst.Tag,
   3060 ) InnerError!void {
   3061     try emitDbgNode(gz, infix_node);
   3062     const astgen = gz.astgen;
   3063     const tree = astgen.tree;
   3064     const node_datas = tree.nodes.items(.data);
   3065 
   3066     const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
   3067     const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
   3068     const rhs_type = try gz.addUnNode(.typeof_log2_int_type, lhs, infix_node);
   3069     const rhs = try expr(gz, scope, .{ .ty = rhs_type }, node_datas[infix_node].rhs);
   3070 
   3071     const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{
   3072         .lhs = lhs,
   3073         .rhs = rhs,
   3074     });
   3075     _ = try gz.addBin(.store, lhs_ptr, result);
   3076 }
   3077 
   3078 fn assignShiftSat(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void {
   3079     try emitDbgNode(gz, infix_node);
   3080     const astgen = gz.astgen;
   3081     const tree = astgen.tree;
   3082     const node_datas = tree.nodes.items(.data);
   3083 
   3084     const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
   3085     const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
   3086     // Saturating shift-left allows any integer type for both the LHS and RHS.
   3087     const rhs = try expr(gz, scope, .none, node_datas[infix_node].rhs);
   3088 
   3089     const result = try gz.addPlNode(.shl_sat, infix_node, Zir.Inst.Bin{
   3090         .lhs = lhs,
   3091         .rhs = rhs,
   3092     });
   3093     _ = try gz.addBin(.store, lhs_ptr, result);
   3094 }
   3095 
   3096 fn ptrType(
   3097     gz: *GenZir,
   3098     scope: *Scope,
   3099     rl: ResultLoc,
   3100     node: Ast.Node.Index,
   3101     ptr_info: Ast.full.PtrType,
   3102 ) InnerError!Zir.Inst.Ref {
   3103     const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type);
   3104 
   3105     const simple = ptr_info.ast.align_node == 0 and
   3106         ptr_info.ast.addrspace_node == 0 and
   3107         ptr_info.ast.sentinel == 0 and
   3108         ptr_info.ast.bit_range_start == 0;
   3109 
   3110     if (simple) {
   3111         const result = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
   3112             .ptr_type_simple = .{
   3113                 .is_allowzero = ptr_info.allowzero_token != null,
   3114                 .is_mutable = ptr_info.const_token == null,
   3115                 .is_volatile = ptr_info.volatile_token != null,
   3116                 .size = ptr_info.size,
   3117                 .elem_type = elem_type,
   3118             },
   3119         } });
   3120         return rvalue(gz, rl, result, node);
   3121     }
   3122 
   3123     var sentinel_ref: Zir.Inst.Ref = .none;
   3124     var align_ref: Zir.Inst.Ref = .none;
   3125     var addrspace_ref: Zir.Inst.Ref = .none;
   3126     var bit_start_ref: Zir.Inst.Ref = .none;
   3127     var bit_end_ref: Zir.Inst.Ref = .none;
   3128     var trailing_count: u32 = 0;
   3129 
   3130     if (ptr_info.ast.sentinel != 0) {
   3131         sentinel_ref = try expr(gz, scope, .{ .ty = elem_type }, ptr_info.ast.sentinel);
   3132         trailing_count += 1;
   3133     }
   3134     if (ptr_info.ast.align_node != 0) {
   3135         align_ref = try expr(gz, scope, coerced_align_rl, ptr_info.ast.align_node);
   3136         trailing_count += 1;
   3137     }
   3138     if (ptr_info.ast.addrspace_node != 0) {
   3139         addrspace_ref = try expr(gz, scope, .{ .ty = .address_space_type }, ptr_info.ast.addrspace_node);
   3140         trailing_count += 1;
   3141     }
   3142     if (ptr_info.ast.bit_range_start != 0) {
   3143         assert(ptr_info.ast.bit_range_end != 0);
   3144         bit_start_ref = try expr(gz, scope, .{ .coerced_ty = .u16_type }, ptr_info.ast.bit_range_start);
   3145         bit_end_ref = try expr(gz, scope, .{ .coerced_ty = .u16_type }, ptr_info.ast.bit_range_end);
   3146         trailing_count += 2;
   3147     }
   3148 
   3149     const gpa = gz.astgen.gpa;
   3150     try gz.instructions.ensureUnusedCapacity(gpa, 1);
   3151     try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
   3152     try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.PtrType).Struct.fields.len +
   3153         trailing_count);
   3154 
   3155     const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.PtrType{ .elem_type = elem_type });
   3156     if (sentinel_ref != .none) {
   3157         gz.astgen.extra.appendAssumeCapacity(@enumToInt(sentinel_ref));
   3158     }
   3159     if (align_ref != .none) {
   3160         gz.astgen.extra.appendAssumeCapacity(@enumToInt(align_ref));
   3161     }
   3162     if (addrspace_ref != .none) {
   3163         gz.astgen.extra.appendAssumeCapacity(@enumToInt(addrspace_ref));
   3164     }
   3165     if (bit_start_ref != .none) {
   3166         gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_start_ref));
   3167         gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_end_ref));
   3168     }
   3169 
   3170     const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
   3171     const result = indexToRef(new_index);
   3172     gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{
   3173         .ptr_type = .{
   3174             .flags = .{
   3175                 .is_allowzero = ptr_info.allowzero_token != null,
   3176                 .is_mutable = ptr_info.const_token == null,
   3177                 .is_volatile = ptr_info.volatile_token != null,
   3178                 .has_sentinel = sentinel_ref != .none,
   3179                 .has_align = align_ref != .none,
   3180                 .has_addrspace = addrspace_ref != .none,
   3181                 .has_bit_range = bit_start_ref != .none,
   3182             },
   3183             .size = ptr_info.size,
   3184             .payload_index = payload_index,
   3185         },
   3186     } });
   3187     gz.instructions.appendAssumeCapacity(new_index);
   3188 
   3189     return rvalue(gz, rl, result, node);
   3190 }
   3191 
   3192 fn arrayType(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) !Zir.Inst.Ref {
   3193     const astgen = gz.astgen;
   3194     const tree = astgen.tree;
   3195     const node_datas = tree.nodes.items(.data);
   3196     const node_tags = tree.nodes.items(.tag);
   3197     const main_tokens = tree.nodes.items(.main_token);
   3198 
   3199     const len_node = node_datas[node].lhs;
   3200     if (node_tags[len_node] == .identifier and
   3201         mem.eql(u8, tree.tokenSlice(main_tokens[len_node]), "_"))
   3202     {
   3203         return astgen.failNode(len_node, "unable to infer array size", .{});
   3204     }
   3205     const len = try expr(gz, scope, .{ .coerced_ty = .usize_type }, len_node);
   3206     const elem_type = try typeExpr(gz, scope, node_datas[node].rhs);
   3207 
   3208     const result = try gz.addBin(.array_type, len, elem_type);
   3209     return rvalue(gz, rl, result, node);
   3210 }
   3211 
   3212 fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) !Zir.Inst.Ref {
   3213     const astgen = gz.astgen;
   3214     const tree = astgen.tree;
   3215     const node_datas = tree.nodes.items(.data);
   3216     const node_tags = tree.nodes.items(.tag);
   3217     const main_tokens = tree.nodes.items(.main_token);
   3218     const extra = tree.extraData(node_datas[node].rhs, Ast.Node.ArrayTypeSentinel);
   3219 
   3220     const len_node = node_datas[node].lhs;
   3221     if (node_tags[len_node] == .identifier and
   3222         mem.eql(u8, tree.tokenSlice(main_tokens[len_node]), "_"))
   3223     {
   3224         return astgen.failNode(len_node, "unable to infer array size", .{});
   3225     }
   3226     const len = try reachableExpr(gz, scope, .{ .coerced_ty = .usize_type }, len_node, node);
   3227     const elem_type = try typeExpr(gz, scope, extra.elem_type);
   3228     const sentinel = try reachableExpr(gz, scope, .{ .coerced_ty = elem_type }, extra.sentinel, node);
   3229 
   3230     const result = try gz.addPlNode(.array_type_sentinel, node, Zir.Inst.ArrayTypeSentinel{
   3231         .len = len,
   3232         .elem_type = elem_type,
   3233         .sentinel = sentinel,
   3234     });
   3235     return rvalue(gz, rl, result, node);
   3236 }
   3237 
   3238 const WipMembers = struct {
   3239     payload: *ArrayListUnmanaged(u32),
   3240     payload_top: usize,
   3241     decls_start: u32,
   3242     decls_end: u32,
   3243     field_bits_start: u32,
   3244     fields_start: u32,
   3245     fields_end: u32,
   3246     decl_index: u32 = 0,
   3247     field_index: u32 = 0,
   3248 
   3249     const Self = @This();
   3250     /// struct, union, enum, and opaque decls all use same 4 bits per decl
   3251     const bits_per_decl = 4;
   3252     const decls_per_u32 = 32 / bits_per_decl;
   3253     /// struct, union, enum, and opaque decls all have maximum size of 11 u32 slots
   3254     /// (4 for src_hash + line + name + value + doc_comment + align + link_section + address_space )
   3255     const max_decl_size = 11;
   3256 
   3257     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 {
   3258         const payload_top = @intCast(u32, payload.items.len);
   3259         const decls_start = payload_top + (decl_count + decls_per_u32 - 1) / decls_per_u32;
   3260         const field_bits_start = decls_start + decl_count * max_decl_size;
   3261         const fields_start = field_bits_start + if (bits_per_field > 0) blk: {
   3262             const fields_per_u32 = 32 / bits_per_field;
   3263             break :blk (field_count + fields_per_u32 - 1) / fields_per_u32;
   3264         } else 0;
   3265         const payload_end = fields_start + field_count * max_field_size;
   3266         try payload.resize(gpa, payload_end);
   3267         return Self{
   3268             .payload = payload,
   3269             .payload_top = payload_top,
   3270             .decls_start = decls_start,
   3271             .field_bits_start = field_bits_start,
   3272             .fields_start = fields_start,
   3273             .decls_end = decls_start,
   3274             .fields_end = fields_start,
   3275         };
   3276     }
   3277 
   3278     pub fn nextDecl(self: *Self, is_pub: bool, is_export: bool, has_align: bool, has_section_or_addrspace: bool) void {
   3279         const index = self.payload_top + self.decl_index / decls_per_u32;
   3280         assert(index < self.decls_start);
   3281         const bit_bag: u32 = if (self.decl_index % decls_per_u32 == 0) 0 else self.payload.items[index];
   3282         self.payload.items[index] = (bit_bag >> bits_per_decl) |
   3283             (@as(u32, @boolToInt(is_pub)) << 28) |
   3284             (@as(u32, @boolToInt(is_export)) << 29) |
   3285             (@as(u32, @boolToInt(has_align)) << 30) |
   3286             (@as(u32, @boolToInt(has_section_or_addrspace)) << 31);
   3287         self.decl_index += 1;
   3288     }
   3289 
   3290     pub fn nextField(self: *Self, comptime bits_per_field: u32, bits: [bits_per_field]bool) void {
   3291         const fields_per_u32 = 32 / bits_per_field;
   3292         const index = self.field_bits_start + self.field_index / fields_per_u32;
   3293         assert(index < self.fields_start);
   3294         var bit_bag: u32 = if (self.field_index % fields_per_u32 == 0) 0 else self.payload.items[index];
   3295         bit_bag >>= bits_per_field;
   3296         comptime var i = 0;
   3297         inline while (i < bits_per_field) : (i += 1) {
   3298             bit_bag |= @as(u32, @boolToInt(bits[i])) << (32 - bits_per_field + i);
   3299         }
   3300         self.payload.items[index] = bit_bag;
   3301         self.field_index += 1;
   3302     }
   3303 
   3304     pub fn appendToDecl(self: *Self, data: u32) void {
   3305         assert(self.decls_end < self.field_bits_start);
   3306         self.payload.items[self.decls_end] = data;
   3307         self.decls_end += 1;
   3308     }
   3309 
   3310     pub fn appendToDeclSlice(self: *Self, data: []const u32) void {
   3311         assert(self.decls_end + data.len <= self.field_bits_start);
   3312         mem.copy(u32, self.payload.items[self.decls_end..], data);
   3313         self.decls_end += @intCast(u32, data.len);
   3314     }
   3315 
   3316     pub fn appendToField(self: *Self, data: u32) void {
   3317         assert(self.fields_end < self.payload.items.len);
   3318         self.payload.items[self.fields_end] = data;
   3319         self.fields_end += 1;
   3320     }
   3321 
   3322     pub fn finishBits(self: *Self, comptime bits_per_field: u32) void {
   3323         const empty_decl_slots = decls_per_u32 - (self.decl_index % decls_per_u32);
   3324         if (self.decl_index > 0 and empty_decl_slots < decls_per_u32) {
   3325             const index = self.payload_top + self.decl_index / decls_per_u32;
   3326             self.payload.items[index] >>= @intCast(u5, empty_decl_slots * bits_per_decl);
   3327         }
   3328         if (bits_per_field > 0) {
   3329             const fields_per_u32 = 32 / bits_per_field;
   3330             const empty_field_slots = fields_per_u32 - (self.field_index % fields_per_u32);
   3331             if (self.field_index > 0 and empty_field_slots < fields_per_u32) {
   3332                 const index = self.field_bits_start + self.field_index / fields_per_u32;
   3333                 self.payload.items[index] >>= @intCast(u5, empty_field_slots * bits_per_field);
   3334             }
   3335         }
   3336     }
   3337 
   3338     pub fn declsSlice(self: *Self) []u32 {
   3339         return self.payload.items[self.payload_top..self.decls_end];
   3340     }
   3341 
   3342     pub fn fieldsSlice(self: *Self) []u32 {
   3343         return self.payload.items[self.field_bits_start..self.fields_end];
   3344     }
   3345 
   3346     pub fn deinit(self: *Self) void {
   3347         self.payload.items.len = self.payload_top;
   3348     }
   3349 };
   3350 
   3351 fn fnDecl(
   3352     astgen: *AstGen,
   3353     gz: *GenZir,
   3354     scope: *Scope,
   3355     wip_members: *WipMembers,
   3356     decl_node: Ast.Node.Index,
   3357     body_node: Ast.Node.Index,
   3358     fn_proto: Ast.full.FnProto,
   3359 ) InnerError!void {
   3360     const tree = astgen.tree;
   3361     const token_tags = tree.tokens.items(.tag);
   3362 
   3363     // missing function name already happened in scanDecls()
   3364     const fn_name_token = fn_proto.name_token orelse return error.AnalysisFail;
   3365     const fn_name_str_index = try astgen.identAsString(fn_name_token);
   3366 
   3367     // We insert this at the beginning so that its instruction index marks the
   3368     // start of the top level declaration.
   3369     const block_inst = try gz.makeBlockInst(.block_inline, fn_proto.ast.proto_node);
   3370     astgen.advanceSourceCursorToNode(decl_node);
   3371 
   3372     var decl_gz: GenZir = .{
   3373         .force_comptime = true,
   3374         .in_defer = false,
   3375         .decl_node_index = fn_proto.ast.proto_node,
   3376         .decl_line = astgen.source_line,
   3377         .parent = scope,
   3378         .astgen = astgen,
   3379         .instructions = gz.instructions,
   3380         .instructions_top = gz.instructions.items.len,
   3381     };
   3382     defer decl_gz.unstack();
   3383 
   3384     var fn_gz: GenZir = .{
   3385         .force_comptime = false,
   3386         .in_defer = false,
   3387         .decl_node_index = fn_proto.ast.proto_node,
   3388         .decl_line = decl_gz.decl_line,
   3389         .parent = &decl_gz.base,
   3390         .astgen = astgen,
   3391         .instructions = gz.instructions,
   3392         .instructions_top = GenZir.unstacked_top,
   3393     };
   3394     defer fn_gz.unstack();
   3395 
   3396     // TODO: support noinline
   3397     const is_pub = fn_proto.visib_token != null;
   3398     const is_export = blk: {
   3399         const maybe_export_token = fn_proto.extern_export_inline_token orelse break :blk false;
   3400         break :blk token_tags[maybe_export_token] == .keyword_export;
   3401     };
   3402     const is_extern = blk: {
   3403         const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false;
   3404         break :blk token_tags[maybe_extern_token] == .keyword_extern;
   3405     };
   3406     const has_inline_keyword = blk: {
   3407         const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;
   3408         break :blk token_tags[maybe_inline_token] == .keyword_inline;
   3409     };
   3410 
   3411     const doc_comment_index = try astgen.docCommentAsString(fn_proto.firstToken());
   3412 
   3413     // align, linksection, and addrspace is passed in the func instruction in this case.
   3414     wip_members.nextDecl(is_pub, is_export, false, false);
   3415 
   3416     var noalias_bits: u32 = 0;
   3417     var params_scope = &fn_gz.base;
   3418     const is_var_args = is_var_args: {
   3419         var param_type_i: usize = 0;
   3420         var it = fn_proto.iterate(tree);
   3421         while (it.next()) |param| : (param_type_i += 1) {
   3422             const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) {
   3423                 .keyword_noalias => is_comptime: {
   3424                     noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse
   3425                         return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
   3426                     break :is_comptime false;
   3427                 },
   3428                 .keyword_comptime => true,
   3429                 else => false,
   3430             } else false;
   3431 
   3432             const is_anytype = if (param.anytype_ellipsis3) |token| blk: {
   3433                 switch (token_tags[token]) {
   3434                     .keyword_anytype => break :blk true,
   3435                     .ellipsis3 => break :is_var_args true,
   3436                     else => unreachable,
   3437                 }
   3438             } else false;
   3439 
   3440             const param_name: u32 = if (param.name_token) |name_token| blk: {
   3441                 const name_bytes = tree.tokenSlice(name_token);
   3442                 if (mem.eql(u8, "_", name_bytes))
   3443                     break :blk 0;
   3444 
   3445                 const param_name = try astgen.identAsString(name_token);
   3446                 if (!is_extern) {
   3447                     try astgen.detectLocalShadowing(params_scope, param_name, name_token, name_bytes);
   3448                 }
   3449                 break :blk param_name;
   3450             } else if (!is_extern) {
   3451                 if (param.anytype_ellipsis3) |tok| {
   3452                     return astgen.failTok(tok, "missing parameter name", .{});
   3453                 } else {
   3454                     return astgen.failNode(param.type_expr, "missing parameter name", .{});
   3455                 }
   3456             } else 0;
   3457 
   3458             const param_inst = if (is_anytype) param: {
   3459                 const name_token = param.name_token orelse param.anytype_ellipsis3.?;
   3460                 const tag: Zir.Inst.Tag = if (is_comptime)
   3461                     .param_anytype_comptime
   3462                 else
   3463                     .param_anytype;
   3464                 break :param try decl_gz.addStrTok(tag, param_name, name_token);
   3465             } else param: {
   3466                 const param_type_node = param.type_expr;
   3467                 assert(param_type_node != 0);
   3468                 var param_gz = decl_gz.makeSubBlock(scope);
   3469                 defer param_gz.unstack();
   3470                 const param_type = try expr(&param_gz, params_scope, coerced_type_rl, param_type_node);
   3471                 const param_inst_expected = @intCast(u32, astgen.instructions.len + 1);
   3472                 _ = try param_gz.addBreak(.break_inline, param_inst_expected, param_type);
   3473 
   3474                 const main_tokens = tree.nodes.items(.main_token);
   3475                 const name_token = param.name_token orelse main_tokens[param_type_node];
   3476                 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param;
   3477                 const param_inst = try decl_gz.addParam(&param_gz, tag, name_token, param_name, param.first_doc_comment);
   3478                 assert(param_inst_expected == param_inst);
   3479                 break :param indexToRef(param_inst);
   3480             };
   3481 
   3482             if (param_name == 0 or is_extern) continue;
   3483 
   3484             const sub_scope = try astgen.arena.create(Scope.LocalVal);
   3485             sub_scope.* = .{
   3486                 .parent = params_scope,
   3487                 .gen_zir = &decl_gz,
   3488                 .name = param_name,
   3489                 .inst = param_inst,
   3490                 .token_src = param.name_token.?,
   3491                 .id_cat = .@"function parameter",
   3492             };
   3493             params_scope = &sub_scope.base;
   3494         }
   3495         break :is_var_args false;
   3496     };
   3497 
   3498     const lib_name: u32 = if (fn_proto.lib_name) |lib_name_token| blk: {
   3499         const lib_name_str = try astgen.strLitAsString(lib_name_token);
   3500         break :blk lib_name_str.index;
   3501     } else 0;
   3502 
   3503     const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
   3504     const is_inferred_error = token_tags[maybe_bang] == .bang;
   3505 
   3506     // After creating the function ZIR instruction, it will need to update the break
   3507     // instructions inside the expression blocks for align, addrspace, cc, and ret_ty
   3508     // to use the function instruction as the "block" to break from.
   3509 
   3510     var align_gz = decl_gz.makeSubBlock(params_scope);
   3511     defer align_gz.unstack();
   3512     const align_ref: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
   3513         const inst = try expr(&decl_gz, params_scope, coerced_align_rl, fn_proto.ast.align_expr);
   3514         if (align_gz.instructionsSlice().len == 0) {
   3515             // In this case we will send a len=0 body which can be encoded more efficiently.
   3516             break :inst inst;
   3517         }
   3518         _ = try align_gz.addBreak(.break_inline, 0, inst);
   3519         break :inst inst;
   3520     };
   3521 
   3522     var addrspace_gz = decl_gz.makeSubBlock(params_scope);
   3523     defer addrspace_gz.unstack();
   3524     const addrspace_ref: Zir.Inst.Ref = if (fn_proto.ast.addrspace_expr == 0) .none else inst: {
   3525         const inst = try expr(&decl_gz, params_scope, .{ .coerced_ty = .address_space_type }, fn_proto.ast.addrspace_expr);
   3526         if (addrspace_gz.instructionsSlice().len == 0) {
   3527             // In this case we will send a len=0 body which can be encoded more efficiently.
   3528             break :inst inst;
   3529         }
   3530         _ = try addrspace_gz.addBreak(.break_inline, 0, inst);
   3531         break :inst inst;
   3532     };
   3533 
   3534     var section_gz = decl_gz.makeSubBlock(params_scope);
   3535     defer section_gz.unstack();
   3536     const section_ref: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: {
   3537         const inst = try expr(&decl_gz, params_scope, .{ .coerced_ty = .const_slice_u8_type }, fn_proto.ast.section_expr);
   3538         if (section_gz.instructionsSlice().len == 0) {
   3539             // In this case we will send a len=0 body which can be encoded more efficiently.
   3540             break :inst inst;
   3541         }
   3542         _ = try section_gz.addBreak(.break_inline, 0, inst);
   3543         break :inst inst;
   3544     };
   3545 
   3546     var cc_gz = decl_gz.makeSubBlock(params_scope);
   3547     defer cc_gz.unstack();
   3548     const cc_ref: Zir.Inst.Ref = blk: {
   3549         if (fn_proto.ast.callconv_expr != 0) {
   3550             if (has_inline_keyword) {
   3551                 return astgen.failNode(
   3552                     fn_proto.ast.callconv_expr,
   3553                     "explicit callconv incompatible with inline keyword",
   3554                     .{},
   3555                 );
   3556             }
   3557             const inst = try expr(
   3558                 &decl_gz,
   3559                 params_scope,
   3560                 .{ .coerced_ty = .calling_convention_type },
   3561                 fn_proto.ast.callconv_expr,
   3562             );
   3563             if (cc_gz.instructionsSlice().len == 0) {
   3564                 // In this case we will send a len=0 body which can be encoded more efficiently.
   3565                 break :blk inst;
   3566             }
   3567             _ = try cc_gz.addBreak(.break_inline, 0, inst);
   3568             break :blk inst;
   3569         } else if (is_extern) {
   3570             // note: https://github.com/ziglang/zig/issues/5269
   3571             break :blk .calling_convention_c;
   3572         } else if (has_inline_keyword) {
   3573             break :blk .calling_convention_inline;
   3574         } else {
   3575             break :blk .none;
   3576         }
   3577     };
   3578 
   3579     var ret_gz = decl_gz.makeSubBlock(params_scope);
   3580     defer ret_gz.unstack();
   3581     const ret_ref: Zir.Inst.Ref = inst: {
   3582         const inst = try expr(&ret_gz, params_scope, coerced_type_rl, fn_proto.ast.return_type);
   3583         if (ret_gz.instructionsSlice().len == 0) {
   3584             // In this case we will send a len=0 body which can be encoded more efficiently.
   3585             break :inst inst;
   3586         }
   3587         _ = try ret_gz.addBreak(.break_inline, 0, inst);
   3588         break :inst inst;
   3589     };
   3590 
   3591     const func_inst: Zir.Inst.Ref = if (body_node == 0) func: {
   3592         if (!is_extern) {
   3593             return astgen.failTok(fn_proto.ast.fn_token, "non-extern function has no body", .{});
   3594         }
   3595         if (is_inferred_error) {
   3596             return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{});
   3597         }
   3598         break :func try decl_gz.addFunc(.{
   3599             .src_node = decl_node,
   3600             .cc_ref = cc_ref,
   3601             .cc_gz = &cc_gz,
   3602             .align_ref = align_ref,
   3603             .align_gz = &align_gz,
   3604             .ret_ref = ret_ref,
   3605             .ret_gz = &ret_gz,
   3606             .section_ref = section_ref,
   3607             .section_gz = &section_gz,
   3608             .addrspace_ref = addrspace_ref,
   3609             .addrspace_gz = &addrspace_gz,
   3610             .param_block = block_inst,
   3611             .body_gz = null,
   3612             .lib_name = lib_name,
   3613             .is_var_args = is_var_args,
   3614             .is_inferred_error = false,
   3615             .is_test = false,
   3616             .is_extern = true,
   3617             .noalias_bits = noalias_bits,
   3618         });
   3619     } else func: {
   3620         if (is_var_args) {
   3621             return astgen.failTok(fn_proto.ast.fn_token, "non-extern function is variadic", .{});
   3622         }
   3623 
   3624         // as a scope, fn_gz encloses ret_gz, but for instruction list, fn_gz stacks on ret_gz
   3625         fn_gz.instructions_top = ret_gz.instructions.items.len;
   3626 
   3627         const prev_fn_block = astgen.fn_block;
   3628         astgen.fn_block = &fn_gz;
   3629         defer astgen.fn_block = prev_fn_block;
   3630 
   3631         astgen.advanceSourceCursorToNode(body_node);
   3632         const lbrace_line = astgen.source_line - decl_gz.decl_line;
   3633         const lbrace_column = astgen.source_column;
   3634 
   3635         _ = try expr(&fn_gz, params_scope, .none, body_node);
   3636         try checkUsed(gz, &fn_gz.base, params_scope);
   3637 
   3638         if (!fn_gz.endsWithNoReturn()) {
   3639             // Since we are adding the return instruction here, we must handle the coercion.
   3640             // We do this by using the `ret_tok` instruction.
   3641             _ = try fn_gz.addUnTok(.ret_tok, .void_value, tree.lastToken(body_node));
   3642         }
   3643 
   3644         break :func try decl_gz.addFunc(.{
   3645             .src_node = decl_node,
   3646             .cc_ref = cc_ref,
   3647             .cc_gz = &cc_gz,
   3648             .align_ref = align_ref,
   3649             .align_gz = &align_gz,
   3650             .ret_ref = ret_ref,
   3651             .ret_gz = &ret_gz,
   3652             .section_ref = section_ref,
   3653             .section_gz = &section_gz,
   3654             .addrspace_ref = addrspace_ref,
   3655             .addrspace_gz = &addrspace_gz,
   3656             .lbrace_line = lbrace_line,
   3657             .lbrace_column = lbrace_column,
   3658             .param_block = block_inst,
   3659             .body_gz = &fn_gz,
   3660             .lib_name = lib_name,
   3661             .is_var_args = is_var_args,
   3662             .is_inferred_error = is_inferred_error,
   3663             .is_test = false,
   3664             .is_extern = false,
   3665             .noalias_bits = noalias_bits,
   3666         });
   3667     };
   3668 
   3669     // We add this at the end so that its instruction index marks the end range
   3670     // of the top level declaration. addFunc already unstacked fn_gz and ret_gz.
   3671     _ = try decl_gz.addBreak(.break_inline, block_inst, func_inst);
   3672     try decl_gz.setBlockBody(block_inst);
   3673 
   3674     {
   3675         const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
   3676         const casted = @bitCast([4]u32, contents_hash);
   3677         wip_members.appendToDeclSlice(&casted);
   3678     }
   3679     {
   3680         const line_delta = decl_gz.decl_line - gz.decl_line;
   3681         wip_members.appendToDecl(line_delta);
   3682     }
   3683     wip_members.appendToDecl(fn_name_str_index);
   3684     wip_members.appendToDecl(block_inst);
   3685     wip_members.appendToDecl(doc_comment_index);
   3686 }
   3687 
   3688 fn globalVarDecl(
   3689     astgen: *AstGen,
   3690     gz: *GenZir,
   3691     scope: *Scope,
   3692     wip_members: *WipMembers,
   3693     node: Ast.Node.Index,
   3694     var_decl: Ast.full.VarDecl,
   3695 ) InnerError!void {
   3696     const tree = astgen.tree;
   3697     const token_tags = tree.tokens.items(.tag);
   3698 
   3699     const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var;
   3700     // We do this at the beginning so that the instruction index marks the range start
   3701     // of the top level declaration.
   3702     const block_inst = try gz.makeBlockInst(.block_inline, node);
   3703 
   3704     const name_token = var_decl.ast.mut_token + 1;
   3705     const name_str_index = try astgen.identAsString(name_token);
   3706     astgen.advanceSourceCursorToNode(node);
   3707 
   3708     var block_scope: GenZir = .{
   3709         .parent = scope,
   3710         .decl_node_index = node,
   3711         .decl_line = astgen.source_line,
   3712         .astgen = astgen,
   3713         .force_comptime = true,
   3714         .in_defer = false,
   3715         .anon_name_strategy = .parent,
   3716         .instructions = gz.instructions,
   3717         .instructions_top = gz.instructions.items.len,
   3718     };
   3719     defer block_scope.unstack();
   3720 
   3721     const is_pub = var_decl.visib_token != null;
   3722     const is_export = blk: {
   3723         const maybe_export_token = var_decl.extern_export_token orelse break :blk false;
   3724         break :blk token_tags[maybe_export_token] == .keyword_export;
   3725     };
   3726     const is_extern = blk: {
   3727         const maybe_extern_token = var_decl.extern_export_token orelse break :blk false;
   3728         break :blk token_tags[maybe_extern_token] == .keyword_extern;
   3729     };
   3730     const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node == 0) .none else inst: {
   3731         break :inst try expr(&block_scope, &block_scope.base, align_rl, var_decl.ast.align_node);
   3732     };
   3733     const addrspace_inst: Zir.Inst.Ref = if (var_decl.ast.addrspace_node == 0) .none else inst: {
   3734         break :inst try expr(&block_scope, &block_scope.base, .{ .ty = .address_space_type }, var_decl.ast.addrspace_node);
   3735     };
   3736     const section_inst: Zir.Inst.Ref = if (var_decl.ast.section_node == 0) .none else inst: {
   3737         break :inst try comptimeExpr(&block_scope, &block_scope.base, .{ .ty = .const_slice_u8_type }, var_decl.ast.section_node);
   3738     };
   3739     const has_section_or_addrspace = section_inst != .none or addrspace_inst != .none;
   3740     wip_members.nextDecl(is_pub, is_export, align_inst != .none, has_section_or_addrspace);
   3741 
   3742     const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: {
   3743         if (!is_mutable) {
   3744             return astgen.failTok(tok, "threadlocal variable cannot be constant", .{});
   3745         }
   3746         break :blk true;
   3747     } else false;
   3748 
   3749     const lib_name: u32 = if (var_decl.lib_name) |lib_name_token| blk: {
   3750         const lib_name_str = try astgen.strLitAsString(lib_name_token);
   3751         break :blk lib_name_str.index;
   3752     } else 0;
   3753 
   3754     const doc_comment_index = try astgen.docCommentAsString(var_decl.firstToken());
   3755 
   3756     assert(var_decl.comptime_token == null); // handled by parser
   3757 
   3758     const var_inst: Zir.Inst.Ref = if (var_decl.ast.init_node != 0) vi: {
   3759         if (is_extern) {
   3760             return astgen.failNode(
   3761                 var_decl.ast.init_node,
   3762                 "extern variables have no initializers",
   3763                 .{},
   3764             );
   3765         }
   3766 
   3767         const type_inst: Zir.Inst.Ref = if (var_decl.ast.type_node != 0)
   3768             try expr(
   3769                 &block_scope,
   3770                 &block_scope.base,
   3771                 .{ .ty = .type_type },
   3772                 var_decl.ast.type_node,
   3773             )
   3774         else
   3775             .none;
   3776 
   3777         const init_inst = try expr(
   3778             &block_scope,
   3779             &block_scope.base,
   3780             if (type_inst != .none) .{ .ty = type_inst } else .none,
   3781             var_decl.ast.init_node,
   3782         );
   3783 
   3784         if (is_mutable) {
   3785             const var_inst = try block_scope.addVar(.{
   3786                 .var_type = type_inst,
   3787                 .lib_name = 0,
   3788                 .align_inst = .none, // passed via the decls data
   3789                 .init = init_inst,
   3790                 .is_extern = false,
   3791                 .is_threadlocal = is_threadlocal,
   3792             });
   3793             break :vi var_inst;
   3794         } else {
   3795             break :vi init_inst;
   3796         }
   3797     } else if (!is_extern) {
   3798         return astgen.failNode(node, "variables must be initialized", .{});
   3799     } else if (var_decl.ast.type_node != 0) vi: {
   3800         // Extern variable which has an explicit type.
   3801         const type_inst = try typeExpr(&block_scope, &block_scope.base, var_decl.ast.type_node);
   3802 
   3803         const var_inst = try block_scope.addVar(.{
   3804             .var_type = type_inst,
   3805             .lib_name = lib_name,
   3806             .align_inst = .none, // passed via the decls data
   3807             .init = .none,
   3808             .is_extern = true,
   3809             .is_threadlocal = is_threadlocal,
   3810         });
   3811         break :vi var_inst;
   3812     } else {
   3813         return astgen.failNode(node, "unable to infer variable type", .{});
   3814     };
   3815     // We do this at the end so that the instruction index marks the end
   3816     // range of a top level declaration.
   3817     _ = try block_scope.addBreak(.break_inline, block_inst, var_inst);
   3818     try block_scope.setBlockBody(block_inst);
   3819 
   3820     {
   3821         const contents_hash = std.zig.hashSrc(tree.getNodeSource(node));
   3822         const casted = @bitCast([4]u32, contents_hash);
   3823         wip_members.appendToDeclSlice(&casted);
   3824     }
   3825     {
   3826         const line_delta = block_scope.decl_line - gz.decl_line;
   3827         wip_members.appendToDecl(line_delta);
   3828     }
   3829     wip_members.appendToDecl(name_str_index);
   3830     wip_members.appendToDecl(block_inst);
   3831     wip_members.appendToDecl(doc_comment_index); // doc_comment wip
   3832     if (align_inst != .none) {
   3833         wip_members.appendToDecl(@enumToInt(align_inst));
   3834     }
   3835     if (has_section_or_addrspace) {
   3836         wip_members.appendToDecl(@enumToInt(section_inst));
   3837         wip_members.appendToDecl(@enumToInt(addrspace_inst));
   3838     }
   3839 }
   3840 
   3841 fn comptimeDecl(
   3842     astgen: *AstGen,
   3843     gz: *GenZir,
   3844     scope: *Scope,
   3845     wip_members: *WipMembers,
   3846     node: Ast.Node.Index,
   3847 ) InnerError!void {
   3848     const tree = astgen.tree;
   3849     const node_datas = tree.nodes.items(.data);
   3850     const body_node = node_datas[node].lhs;
   3851 
   3852     // Up top so the ZIR instruction index marks the start range of this
   3853     // top-level declaration.
   3854     const block_inst = try gz.makeBlockInst(.block_inline, node);
   3855     wip_members.nextDecl(false, false, false, false);
   3856     astgen.advanceSourceCursorToNode(node);
   3857 
   3858     var decl_block: GenZir = .{
   3859         .force_comptime = true,
   3860         .in_defer = false,
   3861         .decl_node_index = node,
   3862         .decl_line = astgen.source_line,
   3863         .parent = scope,
   3864         .astgen = astgen,
   3865         .instructions = gz.instructions,
   3866         .instructions_top = gz.instructions.items.len,
   3867     };
   3868     defer decl_block.unstack();
   3869 
   3870     const block_result = try expr(&decl_block, &decl_block.base, .none, body_node);
   3871     if (decl_block.isEmpty() or !decl_block.refIsNoReturn(block_result)) {
   3872         _ = try decl_block.addBreak(.break_inline, block_inst, .void_value);
   3873     }
   3874     try decl_block.setBlockBody(block_inst);
   3875 
   3876     {
   3877         const contents_hash = std.zig.hashSrc(tree.getNodeSource(node));
   3878         const casted = @bitCast([4]u32, contents_hash);
   3879         wip_members.appendToDeclSlice(&casted);
   3880     }
   3881     {
   3882         const line_delta = decl_block.decl_line - gz.decl_line;
   3883         wip_members.appendToDecl(line_delta);
   3884     }
   3885     wip_members.appendToDecl(0);
   3886     wip_members.appendToDecl(block_inst);
   3887     wip_members.appendToDecl(0); // no doc comments on comptime decls
   3888 }
   3889 
   3890 fn usingnamespaceDecl(
   3891     astgen: *AstGen,
   3892     gz: *GenZir,
   3893     scope: *Scope,
   3894     wip_members: *WipMembers,
   3895     node: Ast.Node.Index,
   3896 ) InnerError!void {
   3897     const tree = astgen.tree;
   3898     const node_datas = tree.nodes.items(.data);
   3899 
   3900     const type_expr = node_datas[node].lhs;
   3901     const is_pub = blk: {
   3902         const main_tokens = tree.nodes.items(.main_token);
   3903         const token_tags = tree.tokens.items(.tag);
   3904         const main_token = main_tokens[node];
   3905         break :blk (main_token > 0 and token_tags[main_token - 1] == .keyword_pub);
   3906     };
   3907     // Up top so the ZIR instruction index marks the start range of this
   3908     // top-level declaration.
   3909     const block_inst = try gz.makeBlockInst(.block_inline, node);
   3910     wip_members.nextDecl(is_pub, true, false, false);
   3911     astgen.advanceSourceCursorToNode(node);
   3912 
   3913     var decl_block: GenZir = .{
   3914         .force_comptime = true,
   3915         .in_defer = false,
   3916         .decl_node_index = node,
   3917         .decl_line = astgen.source_line,
   3918         .parent = scope,
   3919         .astgen = astgen,
   3920         .instructions = gz.instructions,
   3921         .instructions_top = gz.instructions.items.len,
   3922     };
   3923     defer decl_block.unstack();
   3924 
   3925     const namespace_inst = try typeExpr(&decl_block, &decl_block.base, type_expr);
   3926     _ = try decl_block.addBreak(.break_inline, block_inst, namespace_inst);
   3927     try decl_block.setBlockBody(block_inst);
   3928 
   3929     {
   3930         const contents_hash = std.zig.hashSrc(tree.getNodeSource(node));
   3931         const casted = @bitCast([4]u32, contents_hash);
   3932         wip_members.appendToDeclSlice(&casted);
   3933     }
   3934     {
   3935         const line_delta = decl_block.decl_line - gz.decl_line;
   3936         wip_members.appendToDecl(line_delta);
   3937     }
   3938     wip_members.appendToDecl(0);
   3939     wip_members.appendToDecl(block_inst);
   3940     wip_members.appendToDecl(0); // no doc comments on usingnamespace decls
   3941 }
   3942 
   3943 fn testDecl(
   3944     astgen: *AstGen,
   3945     gz: *GenZir,
   3946     scope: *Scope,
   3947     wip_members: *WipMembers,
   3948     node: Ast.Node.Index,
   3949 ) InnerError!void {
   3950     const tree = astgen.tree;
   3951     const node_datas = tree.nodes.items(.data);
   3952     const body_node = node_datas[node].rhs;
   3953 
   3954     // Up top so the ZIR instruction index marks the start range of this
   3955     // top-level declaration.
   3956     const block_inst = try gz.makeBlockInst(.block_inline, node);
   3957 
   3958     wip_members.nextDecl(false, false, false, false);
   3959     astgen.advanceSourceCursorToNode(node);
   3960 
   3961     var decl_block: GenZir = .{
   3962         .force_comptime = true,
   3963         .in_defer = false,
   3964         .decl_node_index = node,
   3965         .decl_line = astgen.source_line,
   3966         .parent = scope,
   3967         .astgen = astgen,
   3968         .instructions = gz.instructions,
   3969         .instructions_top = gz.instructions.items.len,
   3970     };
   3971     defer decl_block.unstack();
   3972 
   3973     const main_tokens = tree.nodes.items(.main_token);
   3974     const token_tags = tree.tokens.items(.tag);
   3975     const test_token = main_tokens[node];
   3976     const test_name_token = test_token + 1;
   3977     const test_name_token_tag = token_tags[test_name_token];
   3978     const is_decltest = test_name_token_tag == .identifier;
   3979     const test_name: u32 = blk: {
   3980         if (test_name_token_tag == .string_literal) {
   3981             break :blk try astgen.testNameString(test_name_token);
   3982         } else if (test_name_token_tag == .identifier) {
   3983             const ident_name_raw = tree.tokenSlice(test_name_token);
   3984 
   3985             if (mem.eql(u8, ident_name_raw, "_")) return astgen.failTok(test_name_token, "'_' used as an identifier without @\"_\" syntax", .{});
   3986 
   3987             // if not @"" syntax, just use raw token slice
   3988             if (ident_name_raw[0] != '@') {
   3989                 if (primitives.get(ident_name_raw)) |_| return astgen.failTok(test_name_token, "cannot test a primitive", .{});
   3990 
   3991                 if (ident_name_raw.len >= 2) integer: {
   3992                     const first_c = ident_name_raw[0];
   3993                     if (first_c == 'i' or first_c == 'u') {
   3994                         _ = switch (first_c == 'i') {
   3995                             true => .signed,
   3996                             false => .unsigned,
   3997                         };
   3998                         _ = parseBitCount(ident_name_raw[1..]) catch |err| switch (err) {
   3999                             error.Overflow => return astgen.failTok(
   4000                                 test_name_token,
   4001                                 "primitive integer type '{s}' exceeds maximum bit width of 65535",
   4002                                 .{ident_name_raw},
   4003                             ),
   4004                             error.InvalidCharacter => break :integer,
   4005                         };
   4006                         return astgen.failTok(test_name_token, "cannot test a primitive", .{});
   4007                     }
   4008                 }
   4009             }
   4010 
   4011             // Local variables, including function parameters.
   4012             const name_str_index = try astgen.identAsString(test_name_token);
   4013             var s = scope;
   4014             var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
   4015             var num_namespaces_out: u32 = 0;
   4016             var capturing_namespace: ?*Scope.Namespace = null;
   4017             while (true) switch (s.tag) {
   4018                 .local_val, .local_ptr => unreachable, // a test cannot be in a local scope
   4019                 .gen_zir => s = s.cast(GenZir).?.parent,
   4020                 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
   4021                 .namespace => {
   4022                     const ns = s.cast(Scope.Namespace).?;
   4023                     if (ns.decls.get(name_str_index)) |i| {
   4024                         if (found_already) |f| {
   4025                             return astgen.failTokNotes(test_name_token, "ambiguous reference", .{}, &.{
   4026                                 try astgen.errNoteNode(f, "declared here", .{}),
   4027                                 try astgen.errNoteNode(i, "also declared here", .{}),
   4028                             });
   4029                         }
   4030                         // We found a match but must continue looking for ambiguous references to decls.
   4031                         found_already = i;
   4032                     }
   4033                     num_namespaces_out += 1;
   4034                     capturing_namespace = ns;
   4035                     s = ns.parent;
   4036                 },
   4037                 .top => break,
   4038             };
   4039             if (found_already == null) {
   4040                 const ident_name = try astgen.identifierTokenString(test_name_token);
   4041                 return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name});
   4042             }
   4043 
   4044             break :blk name_str_index;
   4045         }
   4046         // String table index 1 has a special meaning here of test decl with no name.
   4047         break :blk 1;
   4048     };
   4049 
   4050     var fn_block: GenZir = .{
   4051         .force_comptime = false,
   4052         .in_defer = false,
   4053         .decl_node_index = node,
   4054         .decl_line = decl_block.decl_line,
   4055         .parent = &decl_block.base,
   4056         .astgen = astgen,
   4057         .instructions = decl_block.instructions,
   4058         .instructions_top = decl_block.instructions.items.len,
   4059     };
   4060     defer fn_block.unstack();
   4061 
   4062     const prev_fn_block = astgen.fn_block;
   4063     astgen.fn_block = &fn_block;
   4064     defer astgen.fn_block = prev_fn_block;
   4065 
   4066     astgen.advanceSourceCursorToNode(body_node);
   4067     const lbrace_line = astgen.source_line - decl_block.decl_line;
   4068     const lbrace_column = astgen.source_column;
   4069 
   4070     const block_result = try expr(&fn_block, &fn_block.base, .none, body_node);
   4071     if (fn_block.isEmpty() or !fn_block.refIsNoReturn(block_result)) {
   4072         // Since we are adding the return instruction here, we must handle the coercion.
   4073         // We do this by using the `ret_tok` instruction.
   4074         _ = try fn_block.addUnTok(.ret_tok, .void_value, tree.lastToken(body_node));
   4075     }
   4076 
   4077     const func_inst = try decl_block.addFunc(.{
   4078         .src_node = node,
   4079 
   4080         .cc_ref = .none,
   4081         .cc_gz = null,
   4082         .align_ref = .none,
   4083         .align_gz = null,
   4084         .ret_ref = .void_type,
   4085         .ret_gz = null,
   4086         .section_ref = .none,
   4087         .section_gz = null,
   4088         .addrspace_ref = .none,
   4089         .addrspace_gz = null,
   4090 
   4091         .lbrace_line = lbrace_line,
   4092         .lbrace_column = lbrace_column,
   4093         .param_block = block_inst,
   4094         .body_gz = &fn_block,
   4095         .lib_name = 0,
   4096         .is_var_args = false,
   4097         .is_inferred_error = true,
   4098         .is_test = true,
   4099         .is_extern = false,
   4100         .noalias_bits = 0,
   4101     });
   4102 
   4103     _ = try decl_block.addBreak(.break_inline, block_inst, func_inst);
   4104     try decl_block.setBlockBody(block_inst);
   4105 
   4106     {
   4107         const contents_hash = std.zig.hashSrc(tree.getNodeSource(node));
   4108         const casted = @bitCast([4]u32, contents_hash);
   4109         wip_members.appendToDeclSlice(&casted);
   4110     }
   4111     {
   4112         const line_delta = decl_block.decl_line - gz.decl_line;
   4113         wip_members.appendToDecl(line_delta);
   4114     }
   4115     if (is_decltest)
   4116         wip_members.appendToDecl(2) // 2 here means that it is a decltest, look at doc comment for name
   4117     else
   4118         wip_members.appendToDecl(test_name);
   4119     wip_members.appendToDecl(block_inst);
   4120     if (is_decltest)
   4121         wip_members.appendToDecl(test_name) // the doc comment on a decltest represents it's name
   4122     else
   4123         wip_members.appendToDecl(0); // no doc comments on test decls
   4124 }
   4125 
   4126 fn structDeclInner(
   4127     gz: *GenZir,
   4128     scope: *Scope,
   4129     node: Ast.Node.Index,
   4130     container_decl: Ast.full.ContainerDecl,
   4131     layout: std.builtin.Type.ContainerLayout,
   4132 ) InnerError!Zir.Inst.Ref {
   4133     const decl_inst = try gz.reserveInstructionIndex();
   4134 
   4135     if (container_decl.ast.members.len == 0) {
   4136         try gz.setStruct(decl_inst, .{
   4137             .src_node = node,
   4138             .layout = layout,
   4139             .fields_len = 0,
   4140             .body_len = 0,
   4141             .decls_len = 0,
   4142             .known_non_opv = false,
   4143             .known_comptime_only = false,
   4144         });
   4145         return indexToRef(decl_inst);
   4146     }
   4147 
   4148     const astgen = gz.astgen;
   4149     const gpa = astgen.gpa;
   4150     const tree = astgen.tree;
   4151 
   4152     var namespace: Scope.Namespace = .{
   4153         .parent = scope,
   4154         .node = node,
   4155         .inst = decl_inst,
   4156         .declaring_gz = gz,
   4157     };
   4158     defer namespace.deinit(gpa);
   4159 
   4160     // The struct_decl instruction introduces a scope in which the decls of the struct
   4161     // are in scope, so that field types, alignments, and default value expressions
   4162     // can refer to decls within the struct itself.
   4163     astgen.advanceSourceCursorToNode(node);
   4164     var block_scope: GenZir = .{
   4165         .parent = &namespace.base,
   4166         .decl_node_index = node,
   4167         .decl_line = astgen.source_line,
   4168         .astgen = astgen,
   4169         .force_comptime = true,
   4170         .in_defer = false,
   4171         .instructions = gz.instructions,
   4172         .instructions_top = gz.instructions.items.len,
   4173     };
   4174     defer block_scope.unstack();
   4175 
   4176     const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members);
   4177     const field_count = @intCast(u32, container_decl.ast.members.len - decl_count);
   4178 
   4179     const bits_per_field = 4;
   4180     const max_field_size = 5;
   4181     var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size);
   4182     defer wip_members.deinit();
   4183 
   4184     var known_non_opv = false;
   4185     var known_comptime_only = false;
   4186     for (container_decl.ast.members) |member_node| {
   4187         const member = switch (try containerMember(gz, &namespace.base, &wip_members, member_node)) {
   4188             .decl => continue,
   4189             .field => |field| field,
   4190         };
   4191 
   4192         const field_name = try astgen.identAsString(member.ast.name_token);
   4193         wip_members.appendToField(field_name);
   4194 
   4195         if (member.ast.type_expr == 0) {
   4196             return astgen.failTok(member.ast.name_token, "struct field missing type", .{});
   4197         }
   4198 
   4199         const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr);
   4200         wip_members.appendToField(@enumToInt(field_type));
   4201 
   4202         const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
   4203         wip_members.appendToField(doc_comment_index);
   4204 
   4205         const have_align = member.ast.align_expr != 0;
   4206         const have_value = member.ast.value_expr != 0;
   4207         const is_comptime = member.comptime_token != null;
   4208         const unused = false;
   4209 
   4210         if (!is_comptime) {
   4211             known_non_opv = known_non_opv or
   4212                 nodeImpliesMoreThanOnePossibleValue(tree, member.ast.type_expr);
   4213             known_comptime_only = known_comptime_only or
   4214                 nodeImpliesComptimeOnly(tree, member.ast.type_expr);
   4215         }
   4216         wip_members.nextField(bits_per_field, .{ have_align, have_value, is_comptime, unused });
   4217 
   4218         if (have_align) {
   4219             if (layout == .Packed) {
   4220                 try astgen.appendErrorNode(member.ast.align_expr, "unable to override alignment of packed struct fields", .{});
   4221             }
   4222             const align_inst = try expr(&block_scope, &namespace.base, align_rl, member.ast.align_expr);
   4223             wip_members.appendToField(@enumToInt(align_inst));
   4224         }
   4225         if (have_value) {
   4226             const rl: ResultLoc = if (field_type == .none) .none else .{ .ty = field_type };
   4227 
   4228             const default_inst = try expr(&block_scope, &namespace.base, rl, member.ast.value_expr);
   4229             wip_members.appendToField(@enumToInt(default_inst));
   4230         } else if (member.comptime_token) |comptime_token| {
   4231             return astgen.failTok(comptime_token, "comptime field without default initialization value", .{});
   4232         }
   4233     }
   4234 
   4235     if (!block_scope.isEmpty()) {
   4236         _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
   4237     }
   4238 
   4239     const body = block_scope.instructionsSlice();
   4240     const body_len = astgen.countBodyLenAfterFixups(body);
   4241 
   4242     try gz.setStruct(decl_inst, .{
   4243         .src_node = node,
   4244         .layout = layout,
   4245         .body_len = body_len,
   4246         .fields_len = field_count,
   4247         .decls_len = decl_count,
   4248         .known_non_opv = known_non_opv,
   4249         .known_comptime_only = known_comptime_only,
   4250     });
   4251 
   4252     wip_members.finishBits(bits_per_field);
   4253     const decls_slice = wip_members.declsSlice();
   4254     const fields_slice = wip_members.fieldsSlice();
   4255     try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + body_len + fields_slice.len);
   4256     astgen.extra.appendSliceAssumeCapacity(decls_slice);
   4257     astgen.appendBodyWithFixups(body);
   4258     astgen.extra.appendSliceAssumeCapacity(fields_slice);
   4259 
   4260     block_scope.unstack();
   4261     try gz.addNamespaceCaptures(&namespace);
   4262     return indexToRef(decl_inst);
   4263 }
   4264 
   4265 fn unionDeclInner(
   4266     gz: *GenZir,
   4267     scope: *Scope,
   4268     node: Ast.Node.Index,
   4269     members: []const Ast.Node.Index,
   4270     layout: std.builtin.Type.ContainerLayout,
   4271     arg_node: Ast.Node.Index,
   4272     have_auto_enum: bool,
   4273 ) InnerError!Zir.Inst.Ref {
   4274     const decl_inst = try gz.reserveInstructionIndex();
   4275 
   4276     const astgen = gz.astgen;
   4277     const gpa = astgen.gpa;
   4278 
   4279     var namespace: Scope.Namespace = .{
   4280         .parent = scope,
   4281         .node = node,
   4282         .inst = decl_inst,
   4283         .declaring_gz = gz,
   4284     };
   4285     defer namespace.deinit(gpa);
   4286 
   4287     // The union_decl instruction introduces a scope in which the decls of the union
   4288     // are in scope, so that field types, alignments, and default value expressions
   4289     // can refer to decls within the union itself.
   4290     astgen.advanceSourceCursorToNode(node);
   4291     var block_scope: GenZir = .{
   4292         .parent = &namespace.base,
   4293         .decl_node_index = node,
   4294         .decl_line = astgen.source_line,
   4295         .astgen = astgen,
   4296         .force_comptime = true,
   4297         .in_defer = false,
   4298         .instructions = gz.instructions,
   4299         .instructions_top = gz.instructions.items.len,
   4300     };
   4301     defer block_scope.unstack();
   4302 
   4303     const decl_count = try astgen.scanDecls(&namespace, members);
   4304     const field_count = @intCast(u32, members.len - decl_count);
   4305 
   4306     const arg_inst: Zir.Inst.Ref = if (arg_node != 0)
   4307         try typeExpr(&block_scope, &namespace.base, arg_node)
   4308     else
   4309         .none;
   4310 
   4311     const bits_per_field = 4;
   4312     const max_field_size = 5;
   4313     var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size);
   4314     defer wip_members.deinit();
   4315 
   4316     for (members) |member_node| {
   4317         const member = switch (try containerMember(gz, &namespace.base, &wip_members, member_node)) {
   4318             .decl => continue,
   4319             .field => |field| field,
   4320         };
   4321         if (member.comptime_token) |comptime_token| {
   4322             return astgen.failTok(comptime_token, "union fields cannot be marked comptime", .{});
   4323         }
   4324 
   4325         const field_name = try astgen.identAsString(member.ast.name_token);
   4326         wip_members.appendToField(field_name);
   4327 
   4328         const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
   4329         wip_members.appendToField(doc_comment_index);
   4330 
   4331         const have_type = member.ast.type_expr != 0;
   4332         const have_align = member.ast.align_expr != 0;
   4333         const have_value = member.ast.value_expr != 0;
   4334         const unused = false;
   4335         wip_members.nextField(bits_per_field, .{ have_type, have_align, have_value, unused });
   4336 
   4337         if (have_type) {
   4338             const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr);
   4339             wip_members.appendToField(@enumToInt(field_type));
   4340         } else if (arg_inst == .none and !have_auto_enum) {
   4341             return astgen.failNode(member_node, "union field missing type", .{});
   4342         }
   4343         if (have_align) {
   4344             const align_inst = try expr(&block_scope, &block_scope.base, .{ .ty = .u32_type }, member.ast.align_expr);
   4345             wip_members.appendToField(@enumToInt(align_inst));
   4346         }
   4347         if (have_value) {
   4348             if (arg_inst == .none) {
   4349                 return astgen.failNodeNotes(
   4350                     node,
   4351                     "explicitly valued tagged union missing integer tag type",
   4352                     .{},
   4353                     &[_]u32{
   4354                         try astgen.errNoteNode(
   4355                             member.ast.value_expr,
   4356                             "tag value specified here",
   4357                             .{},
   4358                         ),
   4359                     },
   4360                 );
   4361             }
   4362             if (!have_auto_enum) {
   4363                 return astgen.failNodeNotes(
   4364                     node,
   4365                     "explicitly valued tagged union requires inferred enum tag type",
   4366                     .{},
   4367                     &[_]u32{
   4368                         try astgen.errNoteNode(
   4369                             member.ast.value_expr,
   4370                             "tag value specified here",
   4371                             .{},
   4372                         ),
   4373                     },
   4374                 );
   4375             }
   4376             const tag_value = try expr(&block_scope, &block_scope.base, .{ .ty = arg_inst }, member.ast.value_expr);
   4377             wip_members.appendToField(@enumToInt(tag_value));
   4378         }
   4379     }
   4380     if (field_count == 0) {
   4381         return astgen.failNode(node, "union declarations must have at least one tag", .{});
   4382     }
   4383 
   4384     if (!block_scope.isEmpty()) {
   4385         _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
   4386     }
   4387 
   4388     const body = block_scope.instructionsSlice();
   4389     const body_len = astgen.countBodyLenAfterFixups(body);
   4390 
   4391     try gz.setUnion(decl_inst, .{
   4392         .src_node = node,
   4393         .layout = layout,
   4394         .tag_type = arg_inst,
   4395         .body_len = body_len,
   4396         .fields_len = field_count,
   4397         .decls_len = decl_count,
   4398         .auto_enum_tag = have_auto_enum,
   4399     });
   4400 
   4401     wip_members.finishBits(bits_per_field);
   4402     const decls_slice = wip_members.declsSlice();
   4403     const fields_slice = wip_members.fieldsSlice();
   4404     try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + body_len + fields_slice.len);
   4405     astgen.extra.appendSliceAssumeCapacity(decls_slice);
   4406     astgen.appendBodyWithFixups(body);
   4407     astgen.extra.appendSliceAssumeCapacity(fields_slice);
   4408 
   4409     block_scope.unstack();
   4410     try gz.addNamespaceCaptures(&namespace);
   4411     return indexToRef(decl_inst);
   4412 }
   4413 
   4414 fn containerDecl(
   4415     gz: *GenZir,
   4416     scope: *Scope,
   4417     rl: ResultLoc,
   4418     node: Ast.Node.Index,
   4419     container_decl: Ast.full.ContainerDecl,
   4420 ) InnerError!Zir.Inst.Ref {
   4421     const astgen = gz.astgen;
   4422     const gpa = astgen.gpa;
   4423     const tree = astgen.tree;
   4424     const token_tags = tree.tokens.items(.tag);
   4425     const node_tags = tree.nodes.items(.tag);
   4426 
   4427     const prev_fn_block = astgen.fn_block;
   4428     astgen.fn_block = null;
   4429     defer astgen.fn_block = prev_fn_block;
   4430 
   4431     // We must not create any types until Sema. Here the goal is only to generate
   4432     // ZIR for all the field types, alignments, and default value expressions.
   4433 
   4434     switch (token_tags[container_decl.ast.main_token]) {
   4435         .keyword_struct => {
   4436             const layout = if (container_decl.layout_token) |t| switch (token_tags[t]) {
   4437                 .keyword_packed => std.builtin.Type.ContainerLayout.Packed,
   4438                 .keyword_extern => std.builtin.Type.ContainerLayout.Extern,
   4439                 else => unreachable,
   4440             } else std.builtin.Type.ContainerLayout.Auto;
   4441 
   4442             assert(container_decl.ast.arg == 0);
   4443 
   4444             const result = try structDeclInner(gz, scope, node, container_decl, layout);
   4445             return rvalue(gz, rl, result, node);
   4446         },
   4447         .keyword_union => {
   4448             const layout = if (container_decl.layout_token) |t| switch (token_tags[t]) {
   4449                 .keyword_packed => std.builtin.Type.ContainerLayout.Packed,
   4450                 .keyword_extern => std.builtin.Type.ContainerLayout.Extern,
   4451                 else => unreachable,
   4452             } else std.builtin.Type.ContainerLayout.Auto;
   4453 
   4454             const have_auto_enum = container_decl.ast.enum_token != null;
   4455 
   4456             const result = try unionDeclInner(gz, scope, node, container_decl.ast.members, layout, container_decl.ast.arg, have_auto_enum);
   4457             return rvalue(gz, rl, result, node);
   4458         },
   4459         .keyword_enum => {
   4460             if (container_decl.layout_token) |t| {
   4461                 return astgen.failTok(t, "enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", .{});
   4462             }
   4463             // Count total fields as well as how many have explicitly provided tag values.
   4464             const counts = blk: {
   4465                 var values: usize = 0;
   4466                 var total_fields: usize = 0;
   4467                 var decls: usize = 0;
   4468                 var nonexhaustive_node: Ast.Node.Index = 0;
   4469                 var nonfinal_nonexhaustive = false;
   4470                 for (container_decl.ast.members) |member_node| {
   4471                     const member = switch (node_tags[member_node]) {
   4472                         .container_field_init => tree.containerFieldInit(member_node),
   4473                         .container_field_align => tree.containerFieldAlign(member_node),
   4474                         .container_field => tree.containerField(member_node),
   4475                         else => {
   4476                             decls += 1;
   4477                             continue;
   4478                         },
   4479                     };
   4480                     if (member.comptime_token) |comptime_token| {
   4481                         return astgen.failTok(comptime_token, "enum fields cannot be marked comptime", .{});
   4482                     }
   4483                     if (member.ast.type_expr != 0) {
   4484                         return astgen.failNodeNotes(
   4485                             member.ast.type_expr,
   4486                             "enum fields do not have types",
   4487                             .{},
   4488                             &[_]u32{
   4489                                 try astgen.errNoteNode(
   4490                                     node,
   4491                                     "consider 'union(enum)' here to make it a tagged union",
   4492                                     .{},
   4493                                 ),
   4494                             },
   4495                         );
   4496                     }
   4497                     // Alignment expressions in enums are caught by the parser.
   4498                     assert(member.ast.align_expr == 0);
   4499 
   4500                     const name_token = member.ast.name_token;
   4501                     if (mem.eql(u8, tree.tokenSlice(name_token), "_")) {
   4502                         if (nonexhaustive_node != 0) {
   4503                             return astgen.failNodeNotes(
   4504                                 member_node,
   4505                                 "redundant non-exhaustive enum mark",
   4506                                 .{},
   4507                                 &[_]u32{
   4508                                     try astgen.errNoteNode(
   4509                                         nonexhaustive_node,
   4510                                         "other mark here",
   4511                                         .{},
   4512                                     ),
   4513                                 },
   4514                             );
   4515                         }
   4516                         nonexhaustive_node = member_node;
   4517                         if (member.ast.value_expr != 0) {
   4518                             return astgen.failNode(member.ast.value_expr, "'_' is used to mark an enum as non-exhaustive and cannot be assigned a value", .{});
   4519                         }
   4520                         continue;
   4521                     } else if (nonexhaustive_node != 0) {
   4522                         nonfinal_nonexhaustive = true;
   4523                     }
   4524                     total_fields += 1;
   4525                     if (member.ast.value_expr != 0) {
   4526                         if (container_decl.ast.arg == 0) {
   4527                             return astgen.failNode(member.ast.value_expr, "value assigned to enum tag with inferred tag type", .{});
   4528                         }
   4529                         values += 1;
   4530                     }
   4531                 }
   4532                 if (nonfinal_nonexhaustive) {
   4533                     return astgen.failNode(nonexhaustive_node, "'_' field of non-exhaustive enum must be last", .{});
   4534                 }
   4535                 break :blk .{
   4536                     .total_fields = total_fields,
   4537                     .values = values,
   4538                     .decls = decls,
   4539                     .nonexhaustive_node = nonexhaustive_node,
   4540                 };
   4541             };
   4542             if (counts.total_fields == 0 and counts.nonexhaustive_node == 0) {
   4543                 // One can construct an enum with no tags, and it functions the same as `noreturn`. But
   4544                 // this is only useful for generic code; when explicitly using `enum {}` syntax, there
   4545                 // must be at least one tag.
   4546                 try astgen.appendErrorNode(node, "enum declarations must have at least one tag", .{});
   4547             }
   4548             if (counts.nonexhaustive_node != 0 and container_decl.ast.arg == 0) {
   4549                 try astgen.appendErrorNodeNotes(
   4550                     node,
   4551                     "non-exhaustive enum missing integer tag type",
   4552                     .{},
   4553                     &[_]u32{
   4554                         try astgen.errNoteNode(
   4555                             counts.nonexhaustive_node,
   4556                             "marked non-exhaustive here",
   4557                             .{},
   4558                         ),
   4559                     },
   4560                 );
   4561             }
   4562             // In this case we must generate ZIR code for the tag values, similar to
   4563             // how structs are handled above.
   4564             const nonexhaustive = counts.nonexhaustive_node != 0;
   4565 
   4566             const decl_inst = try gz.reserveInstructionIndex();
   4567 
   4568             var namespace: Scope.Namespace = .{
   4569                 .parent = scope,
   4570                 .node = node,
   4571                 .inst = decl_inst,
   4572                 .declaring_gz = gz,
   4573             };
   4574             defer namespace.deinit(gpa);
   4575 
   4576             // The enum_decl instruction introduces a scope in which the decls of the enum
   4577             // are in scope, so that tag values can refer to decls within the enum itself.
   4578             astgen.advanceSourceCursorToNode(node);
   4579             var block_scope: GenZir = .{
   4580                 .parent = &namespace.base,
   4581                 .decl_node_index = node,
   4582                 .decl_line = astgen.source_line,
   4583                 .astgen = astgen,
   4584                 .force_comptime = true,
   4585                 .in_defer = false,
   4586                 .instructions = gz.instructions,
   4587                 .instructions_top = gz.instructions.items.len,
   4588             };
   4589             defer block_scope.unstack();
   4590 
   4591             _ = try astgen.scanDecls(&namespace, container_decl.ast.members);
   4592 
   4593             const arg_inst: Zir.Inst.Ref = if (container_decl.ast.arg != 0)
   4594                 try comptimeExpr(&block_scope, &namespace.base, .{ .ty = .type_type }, container_decl.ast.arg)
   4595             else
   4596                 .none;
   4597 
   4598             const bits_per_field = 1;
   4599             const max_field_size = 3;
   4600             var wip_members = try WipMembers.init(gpa, &astgen.scratch, @intCast(u32, counts.decls), @intCast(u32, counts.total_fields), bits_per_field, max_field_size);
   4601             defer wip_members.deinit();
   4602 
   4603             for (container_decl.ast.members) |member_node| {
   4604                 if (member_node == counts.nonexhaustive_node)
   4605                     continue;
   4606                 const member = switch (try containerMember(gz, &namespace.base, &wip_members, member_node)) {
   4607                     .decl => continue,
   4608                     .field => |field| field,
   4609                 };
   4610                 assert(member.comptime_token == null);
   4611                 assert(member.ast.type_expr == 0);
   4612                 assert(member.ast.align_expr == 0);
   4613 
   4614                 const field_name = try astgen.identAsString(member.ast.name_token);
   4615                 wip_members.appendToField(field_name);
   4616 
   4617                 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
   4618                 wip_members.appendToField(doc_comment_index);
   4619 
   4620                 const have_value = member.ast.value_expr != 0;
   4621                 wip_members.nextField(bits_per_field, .{have_value});
   4622 
   4623                 if (have_value) {
   4624                     if (arg_inst == .none) {
   4625                         return astgen.failNodeNotes(
   4626                             node,
   4627                             "explicitly valued enum missing integer tag type",
   4628                             .{},
   4629                             &[_]u32{
   4630                                 try astgen.errNoteNode(
   4631                                     member.ast.value_expr,
   4632                                     "tag value specified here",
   4633                                     .{},
   4634                                 ),
   4635                             },
   4636                         );
   4637                     }
   4638                     const tag_value_inst = try expr(&block_scope, &namespace.base, .{ .ty = arg_inst }, member.ast.value_expr);
   4639                     wip_members.appendToField(@enumToInt(tag_value_inst));
   4640                 }
   4641             }
   4642 
   4643             if (!block_scope.isEmpty()) {
   4644                 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
   4645             }
   4646 
   4647             const body = block_scope.instructionsSlice();
   4648             const body_len = astgen.countBodyLenAfterFixups(body);
   4649 
   4650             try gz.setEnum(decl_inst, .{
   4651                 .src_node = node,
   4652                 .nonexhaustive = nonexhaustive,
   4653                 .tag_type = arg_inst,
   4654                 .body_len = body_len,
   4655                 .fields_len = @intCast(u32, counts.total_fields),
   4656                 .decls_len = @intCast(u32, counts.decls),
   4657             });
   4658 
   4659             wip_members.finishBits(bits_per_field);
   4660             const decls_slice = wip_members.declsSlice();
   4661             const fields_slice = wip_members.fieldsSlice();
   4662             try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + body_len + fields_slice.len);
   4663             astgen.extra.appendSliceAssumeCapacity(decls_slice);
   4664             astgen.appendBodyWithFixups(body);
   4665             astgen.extra.appendSliceAssumeCapacity(fields_slice);
   4666 
   4667             block_scope.unstack();
   4668             try gz.addNamespaceCaptures(&namespace);
   4669             return rvalue(gz, rl, indexToRef(decl_inst), node);
   4670         },
   4671         .keyword_opaque => {
   4672             assert(container_decl.ast.arg == 0);
   4673 
   4674             const decl_inst = try gz.reserveInstructionIndex();
   4675 
   4676             var namespace: Scope.Namespace = .{
   4677                 .parent = scope,
   4678                 .node = node,
   4679                 .inst = decl_inst,
   4680                 .declaring_gz = gz,
   4681             };
   4682             defer namespace.deinit(gpa);
   4683 
   4684             const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members);
   4685 
   4686             var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, 0, 0, 0);
   4687             defer wip_members.deinit();
   4688 
   4689             for (container_decl.ast.members) |member_node| {
   4690                 _ = try containerMember(gz, &namespace.base, &wip_members, member_node);
   4691             }
   4692 
   4693             try gz.setOpaque(decl_inst, .{
   4694                 .src_node = node,
   4695                 .decls_len = decl_count,
   4696             });
   4697 
   4698             wip_members.finishBits(0);
   4699             const decls_slice = wip_members.declsSlice();
   4700             try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len);
   4701             astgen.extra.appendSliceAssumeCapacity(decls_slice);
   4702 
   4703             try gz.addNamespaceCaptures(&namespace);
   4704             return rvalue(gz, rl, indexToRef(decl_inst), node);
   4705         },
   4706         else => unreachable,
   4707     }
   4708 }
   4709 
   4710 const ContainerMemberResult = union(enum) { decl, field: Ast.full.ContainerField };
   4711 
   4712 fn containerMember(
   4713     gz: *GenZir,
   4714     scope: *Scope,
   4715     wip_members: *WipMembers,
   4716     member_node: Ast.Node.Index,
   4717 ) InnerError!ContainerMemberResult {
   4718     const astgen = gz.astgen;
   4719     const tree = astgen.tree;
   4720     const node_tags = tree.nodes.items(.tag);
   4721     const node_datas = tree.nodes.items(.data);
   4722     switch (node_tags[member_node]) {
   4723         .container_field_init => return ContainerMemberResult{ .field = tree.containerFieldInit(member_node) },
   4724         .container_field_align => return ContainerMemberResult{ .field = tree.containerFieldAlign(member_node) },
   4725         .container_field => return ContainerMemberResult{ .field = tree.containerField(member_node) },
   4726 
   4727         .fn_decl => {
   4728             const fn_proto = node_datas[member_node].lhs;
   4729             const body = node_datas[member_node].rhs;
   4730             switch (node_tags[fn_proto]) {
   4731                 .fn_proto_simple => {
   4732                     var params: [1]Ast.Node.Index = undefined;
   4733                     astgen.fnDecl(gz, scope, wip_members, member_node, body, tree.fnProtoSimple(&params, fn_proto)) catch |err| switch (err) {
   4734                         error.OutOfMemory => return error.OutOfMemory,
   4735                         error.AnalysisFail => {},
   4736                     };
   4737                 },
   4738                 .fn_proto_multi => {
   4739                     astgen.fnDecl(gz, scope, wip_members, member_node, body, tree.fnProtoMulti(fn_proto)) catch |err| switch (err) {
   4740                         error.OutOfMemory => return error.OutOfMemory,
   4741                         error.AnalysisFail => {},
   4742                     };
   4743                 },
   4744                 .fn_proto_one => {
   4745                     var params: [1]Ast.Node.Index = undefined;
   4746                     astgen.fnDecl(gz, scope, wip_members, member_node, body, tree.fnProtoOne(&params, fn_proto)) catch |err| switch (err) {
   4747                         error.OutOfMemory => return error.OutOfMemory,
   4748                         error.AnalysisFail => {},
   4749                     };
   4750                 },
   4751                 .fn_proto => {
   4752                     astgen.fnDecl(gz, scope, wip_members, member_node, body, tree.fnProto(fn_proto)) catch |err| switch (err) {
   4753                         error.OutOfMemory => return error.OutOfMemory,
   4754                         error.AnalysisFail => {},
   4755                     };
   4756                 },
   4757                 else => unreachable,
   4758             }
   4759         },
   4760         .fn_proto_simple => {
   4761             var params: [1]Ast.Node.Index = undefined;
   4762             astgen.fnDecl(gz, scope, wip_members, member_node, 0, tree.fnProtoSimple(&params, member_node)) catch |err| switch (err) {
   4763                 error.OutOfMemory => return error.OutOfMemory,
   4764                 error.AnalysisFail => {},
   4765             };
   4766         },
   4767         .fn_proto_multi => {
   4768             astgen.fnDecl(gz, scope, wip_members, member_node, 0, tree.fnProtoMulti(member_node)) catch |err| switch (err) {
   4769                 error.OutOfMemory => return error.OutOfMemory,
   4770                 error.AnalysisFail => {},
   4771             };
   4772         },
   4773         .fn_proto_one => {
   4774             var params: [1]Ast.Node.Index = undefined;
   4775             astgen.fnDecl(gz, scope, wip_members, member_node, 0, tree.fnProtoOne(&params, member_node)) catch |err| switch (err) {
   4776                 error.OutOfMemory => return error.OutOfMemory,
   4777                 error.AnalysisFail => {},
   4778             };
   4779         },
   4780         .fn_proto => {
   4781             astgen.fnDecl(gz, scope, wip_members, member_node, 0, tree.fnProto(member_node)) catch |err| switch (err) {
   4782                 error.OutOfMemory => return error.OutOfMemory,
   4783                 error.AnalysisFail => {},
   4784             };
   4785         },
   4786 
   4787         .global_var_decl => {
   4788             astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.globalVarDecl(member_node)) catch |err| switch (err) {
   4789                 error.OutOfMemory => return error.OutOfMemory,
   4790                 error.AnalysisFail => {},
   4791             };
   4792         },
   4793         .local_var_decl => {
   4794             astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.localVarDecl(member_node)) catch |err| switch (err) {
   4795                 error.OutOfMemory => return error.OutOfMemory,
   4796                 error.AnalysisFail => {},
   4797             };
   4798         },
   4799         .simple_var_decl => {
   4800             astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.simpleVarDecl(member_node)) catch |err| switch (err) {
   4801                 error.OutOfMemory => return error.OutOfMemory,
   4802                 error.AnalysisFail => {},
   4803             };
   4804         },
   4805         .aligned_var_decl => {
   4806             astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.alignedVarDecl(member_node)) catch |err| switch (err) {
   4807                 error.OutOfMemory => return error.OutOfMemory,
   4808                 error.AnalysisFail => {},
   4809             };
   4810         },
   4811 
   4812         .@"comptime" => {
   4813             astgen.comptimeDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
   4814                 error.OutOfMemory => return error.OutOfMemory,
   4815                 error.AnalysisFail => {},
   4816             };
   4817         },
   4818         .@"usingnamespace" => {
   4819             astgen.usingnamespaceDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
   4820                 error.OutOfMemory => return error.OutOfMemory,
   4821                 error.AnalysisFail => {},
   4822             };
   4823         },
   4824         .test_decl => {
   4825             astgen.testDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
   4826                 error.OutOfMemory => return error.OutOfMemory,
   4827                 error.AnalysisFail => {},
   4828             };
   4829         },
   4830         else => unreachable,
   4831     }
   4832     return .decl;
   4833 }
   4834 
   4835 fn errorSetDecl(gz: *GenZir, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   4836     const astgen = gz.astgen;
   4837     const gpa = astgen.gpa;
   4838     const tree = astgen.tree;
   4839     const main_tokens = tree.nodes.items(.main_token);
   4840     const token_tags = tree.tokens.items(.tag);
   4841 
   4842     const payload_index = try reserveExtra(astgen, @typeInfo(Zir.Inst.ErrorSetDecl).Struct.fields.len);
   4843     var fields_len: usize = 0;
   4844     {
   4845         var idents: std.AutoHashMapUnmanaged(u32, Ast.TokenIndex) = .{};
   4846         defer idents.deinit(gpa);
   4847 
   4848         const error_token = main_tokens[node];
   4849         var tok_i = error_token + 2;
   4850         while (true) : (tok_i += 1) {
   4851             switch (token_tags[tok_i]) {
   4852                 .doc_comment, .comma => {},
   4853                 .identifier => {
   4854                     const str_index = try astgen.identAsString(tok_i);
   4855                     const gop = try idents.getOrPut(gpa, str_index);
   4856                     if (gop.found_existing) {
   4857                         const name = try gpa.dupe(u8, mem.span(astgen.nullTerminatedString(str_index)));
   4858                         defer gpa.free(name);
   4859                         return astgen.failTokNotes(
   4860                             tok_i,
   4861                             "duplicate error set field '{s}'",
   4862                             .{name},
   4863                             &[_]u32{
   4864                                 try astgen.errNoteTok(
   4865                                     gop.value_ptr.*,
   4866                                     "previous declaration here",
   4867                                     .{},
   4868                                 ),
   4869                             },
   4870                         );
   4871                     }
   4872                     gop.value_ptr.* = tok_i;
   4873 
   4874                     try astgen.extra.ensureUnusedCapacity(gpa, 2);
   4875                     astgen.extra.appendAssumeCapacity(str_index);
   4876                     const doc_comment_index = try astgen.docCommentAsString(tok_i);
   4877                     astgen.extra.appendAssumeCapacity(doc_comment_index);
   4878                     fields_len += 1;
   4879                 },
   4880                 .r_brace => break,
   4881                 else => unreachable,
   4882             }
   4883         }
   4884     }
   4885 
   4886     setExtra(astgen, payload_index, Zir.Inst.ErrorSetDecl{
   4887         .fields_len = @intCast(u32, fields_len),
   4888     });
   4889     const result = try gz.addPlNodePayloadIndex(.error_set_decl, node, payload_index);
   4890     return rvalue(gz, rl, result, node);
   4891 }
   4892 
   4893 fn tryExpr(
   4894     parent_gz: *GenZir,
   4895     scope: *Scope,
   4896     rl: ResultLoc,
   4897     node: Ast.Node.Index,
   4898     operand_node: Ast.Node.Index,
   4899 ) InnerError!Zir.Inst.Ref {
   4900     const astgen = parent_gz.astgen;
   4901 
   4902     const fn_block = astgen.fn_block orelse {
   4903         return astgen.failNode(node, "'try' outside function scope", .{});
   4904     };
   4905 
   4906     if (parent_gz.in_defer) return astgen.failNode(node, "'try' not allowed inside defer expression", .{});
   4907 
   4908     const operand_rl: ResultLoc = switch (rl) {
   4909         .ref => .ref,
   4910         else => .none,
   4911     };
   4912     // This could be a pointer or value depending on the `rl` parameter.
   4913     const operand = try expr(parent_gz, scope, operand_rl, operand_node);
   4914     const is_inline = parent_gz.force_comptime;
   4915     const is_inline_bit = @as(u2, @boolToInt(is_inline));
   4916     const is_ptr_bit = @as(u2, @boolToInt(operand_rl == .ref)) << 1;
   4917     const block_tag: Zir.Inst.Tag = switch (is_inline_bit | is_ptr_bit) {
   4918         0b00 => .@"try",
   4919         0b01 => .@"try",
   4920         //0b01 => .try_inline,
   4921         0b10 => .try_ptr,
   4922         0b11 => .try_ptr,
   4923         //0b11 => .try_ptr_inline,
   4924     };
   4925     const try_inst = try parent_gz.makeBlockInst(block_tag, node);
   4926     try parent_gz.instructions.append(astgen.gpa, try_inst);
   4927 
   4928     var else_scope = parent_gz.makeSubBlock(scope);
   4929     defer else_scope.unstack();
   4930 
   4931     const err_tag = switch (rl) {
   4932         .ref => Zir.Inst.Tag.err_union_code_ptr,
   4933         else => Zir.Inst.Tag.err_union_code,
   4934     };
   4935     const err_code = try else_scope.addUnNode(err_tag, operand, node);
   4936     try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code });
   4937     _ = try else_scope.addUnNode(.ret_node, err_code, node);
   4938 
   4939     try else_scope.setTryBody(try_inst, operand);
   4940     const result = indexToRef(try_inst);
   4941     switch (rl) {
   4942         .ref => return result,
   4943         else => return rvalue(parent_gz, rl, result, node),
   4944     }
   4945 }
   4946 
   4947 fn orelseCatchExpr(
   4948     parent_gz: *GenZir,
   4949     scope: *Scope,
   4950     rl: ResultLoc,
   4951     node: Ast.Node.Index,
   4952     lhs: Ast.Node.Index,
   4953     cond_op: Zir.Inst.Tag,
   4954     unwrap_op: Zir.Inst.Tag,
   4955     unwrap_code_op: Zir.Inst.Tag,
   4956     rhs: Ast.Node.Index,
   4957     payload_token: ?Ast.TokenIndex,
   4958 ) InnerError!Zir.Inst.Ref {
   4959     const astgen = parent_gz.astgen;
   4960     const tree = astgen.tree;
   4961 
   4962     var block_scope = parent_gz.makeSubBlock(scope);
   4963     block_scope.setBreakResultLoc(rl);
   4964     defer block_scope.unstack();
   4965 
   4966     const operand_rl: ResultLoc = switch (block_scope.break_result_loc) {
   4967         .ref => .ref,
   4968         else => .none,
   4969     };
   4970     block_scope.break_count += 1;
   4971     // This could be a pointer or value depending on the `operand_rl` parameter.
   4972     // We cannot use `block_scope.break_result_loc` because that has the bare
   4973     // type, whereas this expression has the optional type. Later we make
   4974     // up for this fact by calling rvalue on the else branch.
   4975     const operand = try reachableExpr(&block_scope, &block_scope.base, operand_rl, lhs, rhs);
   4976     const cond = try block_scope.addUnNode(cond_op, operand, node);
   4977     const condbr = try block_scope.addCondBr(.condbr, node);
   4978 
   4979     const block = try parent_gz.makeBlockInst(.block, node);
   4980     try block_scope.setBlockBody(block);
   4981     // block_scope unstacked now, can add new instructions to parent_gz
   4982     try parent_gz.instructions.append(astgen.gpa, block);
   4983 
   4984     var then_scope = block_scope.makeSubBlock(scope);
   4985     defer then_scope.unstack();
   4986 
   4987     // This could be a pointer or value depending on `unwrap_op`.
   4988     const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node);
   4989     const then_result = switch (rl) {
   4990         .ref => unwrapped_payload,
   4991         else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node),
   4992     };
   4993 
   4994     var else_scope = block_scope.makeSubBlock(scope);
   4995     defer else_scope.unstack();
   4996 
   4997     var err_val_scope: Scope.LocalVal = undefined;
   4998     const else_sub_scope = blk: {
   4999         const payload = payload_token orelse break :blk &else_scope.base;
   5000         if (mem.eql(u8, tree.tokenSlice(payload), "_")) {
   5001             return astgen.failTok(payload, "discard of error capture; omit it instead", .{});
   5002         }
   5003         const err_name = try astgen.identAsString(payload);
   5004         err_val_scope = .{
   5005             .parent = &else_scope.base,
   5006             .gen_zir = &else_scope,
   5007             .name = err_name,
   5008             .inst = try else_scope.addUnNode(unwrap_code_op, operand, node),
   5009             .token_src = payload,
   5010             .id_cat = .@"capture",
   5011         };
   5012         break :blk &err_val_scope.base;
   5013     };
   5014 
   5015     const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_loc, rhs);
   5016     if (!else_scope.endsWithNoReturn()) {
   5017         block_scope.break_count += 1;
   5018     }
   5019     try checkUsed(parent_gz, &else_scope.base, else_sub_scope);
   5020 
   5021     // We hold off on the break instructions as well as copying the then/else
   5022     // instructions into place until we know whether to keep store_to_block_ptr
   5023     // instructions or not.
   5024 
   5025     const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break";
   5026     return finishThenElseBlock(
   5027         parent_gz,
   5028         rl,
   5029         node,
   5030         &block_scope,
   5031         &then_scope,
   5032         &else_scope,
   5033         condbr,
   5034         cond,
   5035         then_result,
   5036         else_result,
   5037         block,
   5038         block,
   5039         break_tag,
   5040     );
   5041 }
   5042 
   5043 /// Supports `else_scope` stacked on `then_scope` stacked on `block_scope`. Unstacks `else_scope` then `then_scope`.
   5044 fn finishThenElseBlock(
   5045     parent_gz: *GenZir,
   5046     rl: ResultLoc,
   5047     node: Ast.Node.Index,
   5048     block_scope: *GenZir,
   5049     then_scope: *GenZir,
   5050     else_scope: *GenZir,
   5051     condbr: Zir.Inst.Index,
   5052     cond: Zir.Inst.Ref,
   5053     then_result: Zir.Inst.Ref,
   5054     else_result: Zir.Inst.Ref,
   5055     main_block: Zir.Inst.Index,
   5056     then_break_block: Zir.Inst.Index,
   5057     break_tag: Zir.Inst.Tag,
   5058 ) InnerError!Zir.Inst.Ref {
   5059     // We now have enough information to decide whether the result instruction should
   5060     // be communicated via result location pointer or break instructions.
   5061     const strat = rl.strategy(block_scope);
   5062     // else_scope may be stacked on then_scope, so check for no-return on then_scope manually
   5063     const tags = parent_gz.astgen.instructions.items(.tag);
   5064     const then_slice = then_scope.instructionsSliceUpto(else_scope);
   5065     const then_no_return = then_slice.len > 0 and tags[then_slice[then_slice.len - 1]].isNoReturn();
   5066     const else_no_return = else_scope.endsWithNoReturn();
   5067 
   5068     switch (strat.tag) {
   5069         .break_void => {
   5070             const then_break = if (!then_no_return) try then_scope.makeBreak(break_tag, then_break_block, .void_value) else 0;
   5071             const else_break = if (!else_no_return) try else_scope.makeBreak(break_tag, main_block, .void_value) else 0;
   5072             assert(!strat.elide_store_to_block_ptr_instructions);
   5073             try setCondBrPayload(condbr, cond, then_scope, then_break, else_scope, else_break);
   5074             return indexToRef(main_block);
   5075         },
   5076         .break_operand => {
   5077             const then_break = if (!then_no_return) try then_scope.makeBreak(break_tag, then_break_block, then_result) else 0;
   5078             const else_break = if (else_result == .none)
   5079                 try else_scope.makeBreak(break_tag, main_block, .void_value)
   5080             else if (!else_no_return)
   5081                 try else_scope.makeBreak(break_tag, main_block, else_result)
   5082             else
   5083                 0;
   5084 
   5085             if (strat.elide_store_to_block_ptr_instructions) {
   5086                 try setCondBrPayloadElideBlockStorePtr(condbr, cond, then_scope, then_break, else_scope, else_break, block_scope.rl_ptr);
   5087             } else {
   5088                 try setCondBrPayload(condbr, cond, then_scope, then_break, else_scope, else_break);
   5089             }
   5090             const block_ref = indexToRef(main_block);
   5091             switch (rl) {
   5092                 .ref => return block_ref,
   5093                 else => return rvalue(parent_gz, rl, block_ref, node),
   5094             }
   5095         },
   5096     }
   5097 }
   5098 
   5099 /// Return whether the identifier names of two tokens are equal. Resolves @""
   5100 /// tokens without allocating.
   5101 /// OK in theory it could do it without allocating. This implementation
   5102 /// allocates when the @"" form is used.
   5103 fn tokenIdentEql(astgen: *AstGen, token1: Ast.TokenIndex, token2: Ast.TokenIndex) !bool {
   5104     const ident_name_1 = try astgen.identifierTokenString(token1);
   5105     const ident_name_2 = try astgen.identifierTokenString(token2);
   5106     return mem.eql(u8, ident_name_1, ident_name_2);
   5107 }
   5108 
   5109 fn fieldAccess(
   5110     gz: *GenZir,
   5111     scope: *Scope,
   5112     rl: ResultLoc,
   5113     node: Ast.Node.Index,
   5114 ) InnerError!Zir.Inst.Ref {
   5115     switch (rl) {
   5116         .ref => return addFieldAccess(.field_ptr, gz, scope, .ref, node),
   5117         else => {
   5118             const access = try addFieldAccess(.field_val, gz, scope, .none, node);
   5119             return rvalue(gz, rl, access, node);
   5120         },
   5121     }
   5122 }
   5123 
   5124 fn addFieldAccess(
   5125     tag: Zir.Inst.Tag,
   5126     gz: *GenZir,
   5127     scope: *Scope,
   5128     lhs_rl: ResultLoc,
   5129     node: Ast.Node.Index,
   5130 ) InnerError!Zir.Inst.Ref {
   5131     const astgen = gz.astgen;
   5132     const tree = astgen.tree;
   5133     const main_tokens = tree.nodes.items(.main_token);
   5134     const node_datas = tree.nodes.items(.data);
   5135 
   5136     const object_node = node_datas[node].lhs;
   5137     const dot_token = main_tokens[node];
   5138     const field_ident = dot_token + 1;
   5139     const str_index = try astgen.identAsString(field_ident);
   5140 
   5141     return gz.addPlNode(tag, node, Zir.Inst.Field{
   5142         .lhs = try expr(gz, scope, lhs_rl, object_node),
   5143         .field_name_start = str_index,
   5144     });
   5145 }
   5146 
   5147 fn arrayAccess(
   5148     gz: *GenZir,
   5149     scope: *Scope,
   5150     rl: ResultLoc,
   5151     node: Ast.Node.Index,
   5152 ) InnerError!Zir.Inst.Ref {
   5153     const astgen = gz.astgen;
   5154     const tree = astgen.tree;
   5155     const node_datas = tree.nodes.items(.data);
   5156     switch (rl) {
   5157         .ref => return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{
   5158             .lhs = try expr(gz, scope, .ref, node_datas[node].lhs),
   5159             .rhs = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs),
   5160         }),
   5161         else => return rvalue(gz, rl, try gz.addPlNode(.elem_val_node, node, Zir.Inst.Bin{
   5162             .lhs = try expr(gz, scope, .none, node_datas[node].lhs),
   5163             .rhs = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs),
   5164         }), node),
   5165     }
   5166 }
   5167 
   5168 fn simpleBinOp(
   5169     gz: *GenZir,
   5170     scope: *Scope,
   5171     rl: ResultLoc,
   5172     node: Ast.Node.Index,
   5173     op_inst_tag: Zir.Inst.Tag,
   5174 ) InnerError!Zir.Inst.Ref {
   5175     const astgen = gz.astgen;
   5176     const tree = astgen.tree;
   5177     const node_datas = tree.nodes.items(.data);
   5178 
   5179     const result = try gz.addPlNode(op_inst_tag, node, Zir.Inst.Bin{
   5180         .lhs = try reachableExpr(gz, scope, .none, node_datas[node].lhs, node),
   5181         .rhs = try reachableExpr(gz, scope, .none, node_datas[node].rhs, node),
   5182     });
   5183     return rvalue(gz, rl, result, node);
   5184 }
   5185 
   5186 fn simpleStrTok(
   5187     gz: *GenZir,
   5188     rl: ResultLoc,
   5189     ident_token: Ast.TokenIndex,
   5190     node: Ast.Node.Index,
   5191     op_inst_tag: Zir.Inst.Tag,
   5192 ) InnerError!Zir.Inst.Ref {
   5193     const astgen = gz.astgen;
   5194     const str_index = try astgen.identAsString(ident_token);
   5195     const result = try gz.addStrTok(op_inst_tag, str_index, ident_token);
   5196     return rvalue(gz, rl, result, node);
   5197 }
   5198 
   5199 fn boolBinOp(
   5200     gz: *GenZir,
   5201     scope: *Scope,
   5202     rl: ResultLoc,
   5203     node: Ast.Node.Index,
   5204     zir_tag: Zir.Inst.Tag,
   5205 ) InnerError!Zir.Inst.Ref {
   5206     const astgen = gz.astgen;
   5207     const tree = astgen.tree;
   5208     const node_datas = tree.nodes.items(.data);
   5209 
   5210     const lhs = try expr(gz, scope, bool_rl, node_datas[node].lhs);
   5211     const bool_br = try gz.addBoolBr(zir_tag, lhs);
   5212 
   5213     var rhs_scope = gz.makeSubBlock(scope);
   5214     defer rhs_scope.unstack();
   5215     const rhs = try expr(&rhs_scope, &rhs_scope.base, bool_rl, node_datas[node].rhs);
   5216     if (!gz.refIsNoReturn(rhs)) {
   5217         _ = try rhs_scope.addBreak(.break_inline, bool_br, rhs);
   5218     }
   5219     try rhs_scope.setBoolBrBody(bool_br);
   5220 
   5221     const block_ref = indexToRef(bool_br);
   5222     return rvalue(gz, rl, block_ref, node);
   5223 }
   5224 
   5225 fn ifExpr(
   5226     parent_gz: *GenZir,
   5227     scope: *Scope,
   5228     rl: ResultLoc,
   5229     node: Ast.Node.Index,
   5230     if_full: Ast.full.If,
   5231 ) InnerError!Zir.Inst.Ref {
   5232     const astgen = parent_gz.astgen;
   5233     const tree = astgen.tree;
   5234     const token_tags = tree.tokens.items(.tag);
   5235 
   5236     var block_scope = parent_gz.makeSubBlock(scope);
   5237     block_scope.setBreakResultLoc(rl);
   5238     defer block_scope.unstack();
   5239 
   5240     const payload_is_ref = if (if_full.payload_token) |payload_token|
   5241         token_tags[payload_token] == .asterisk
   5242     else
   5243         false;
   5244 
   5245     try emitDbgNode(parent_gz, if_full.ast.cond_expr);
   5246     const cond: struct {
   5247         inst: Zir.Inst.Ref,
   5248         bool_bit: Zir.Inst.Ref,
   5249     } = c: {
   5250         if (if_full.error_token) |_| {
   5251             const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
   5252             const err_union = try expr(&block_scope, &block_scope.base, cond_rl, if_full.ast.cond_expr);
   5253             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
   5254             break :c .{
   5255                 .inst = err_union,
   5256                 .bool_bit = try block_scope.addUnNode(tag, err_union, node),
   5257             };
   5258         } else if (if_full.payload_token) |_| {
   5259             const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
   5260             const optional = try expr(&block_scope, &block_scope.base, cond_rl, if_full.ast.cond_expr);
   5261             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
   5262             break :c .{
   5263                 .inst = optional,
   5264                 .bool_bit = try block_scope.addUnNode(tag, optional, node),
   5265             };
   5266         } else {
   5267             const cond = try expr(&block_scope, &block_scope.base, bool_rl, if_full.ast.cond_expr);
   5268             break :c .{
   5269                 .inst = cond,
   5270                 .bool_bit = cond,
   5271             };
   5272         }
   5273     };
   5274 
   5275     const condbr = try block_scope.addCondBr(.condbr, node);
   5276 
   5277     const block = try parent_gz.makeBlockInst(.block, node);
   5278     try block_scope.setBlockBody(block);
   5279     // block_scope unstacked now, can add new instructions to parent_gz
   5280     try parent_gz.instructions.append(astgen.gpa, block);
   5281 
   5282     var then_scope = parent_gz.makeSubBlock(scope);
   5283     defer then_scope.unstack();
   5284 
   5285     var payload_val_scope: Scope.LocalVal = undefined;
   5286 
   5287     try then_scope.addDbgBlockBegin();
   5288     const then_sub_scope = s: {
   5289         if (if_full.error_token != null) {
   5290             if (if_full.payload_token) |payload_token| {
   5291                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   5292                     .err_union_payload_unsafe_ptr
   5293                 else
   5294                     .err_union_payload_unsafe;
   5295                 const payload_inst = try then_scope.addUnNode(tag, cond.inst, node);
   5296                 const token_name_index = payload_token + @boolToInt(payload_is_ref);
   5297                 const ident_name = try astgen.identAsString(token_name_index);
   5298                 const token_name_str = tree.tokenSlice(token_name_index);
   5299                 if (mem.eql(u8, "_", token_name_str))
   5300                     break :s &then_scope.base;
   5301                 try astgen.detectLocalShadowing(&then_scope.base, ident_name, token_name_index, token_name_str);
   5302                 payload_val_scope = .{
   5303                     .parent = &then_scope.base,
   5304                     .gen_zir = &then_scope,
   5305                     .name = ident_name,
   5306                     .inst = payload_inst,
   5307                     .token_src = payload_token,
   5308                     .id_cat = .@"capture",
   5309                 };
   5310                 try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
   5311                 break :s &payload_val_scope.base;
   5312             } else {
   5313                 break :s &then_scope.base;
   5314             }
   5315         } else if (if_full.payload_token) |payload_token| {
   5316             const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
   5317             const tag: Zir.Inst.Tag = if (payload_is_ref)
   5318                 .optional_payload_unsafe_ptr
   5319             else
   5320                 .optional_payload_unsafe;
   5321             const ident_bytes = tree.tokenSlice(ident_token);
   5322             if (mem.eql(u8, "_", ident_bytes))
   5323                 break :s &then_scope.base;
   5324             const payload_inst = try then_scope.addUnNode(tag, cond.inst, node);
   5325             const ident_name = try astgen.identAsString(ident_token);
   5326             try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes);
   5327             payload_val_scope = .{
   5328                 .parent = &then_scope.base,
   5329                 .gen_zir = &then_scope,
   5330                 .name = ident_name,
   5331                 .inst = payload_inst,
   5332                 .token_src = ident_token,
   5333                 .id_cat = .@"capture",
   5334             };
   5335             try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
   5336             break :s &payload_val_scope.base;
   5337         } else {
   5338             break :s &then_scope.base;
   5339         }
   5340     };
   5341 
   5342     const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_loc, if_full.ast.then_expr);
   5343     if (!then_scope.endsWithNoReturn()) {
   5344         block_scope.break_count += 1;
   5345     }
   5346     try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
   5347     try then_scope.addDbgBlockEnd();
   5348     // We hold off on the break instructions as well as copying the then/else
   5349     // instructions into place until we know whether to keep store_to_block_ptr
   5350     // instructions or not.
   5351 
   5352     var else_scope = parent_gz.makeSubBlock(scope);
   5353     defer else_scope.unstack();
   5354 
   5355     const else_node = if_full.ast.else_expr;
   5356     const else_info: struct {
   5357         src: Ast.Node.Index,
   5358         result: Zir.Inst.Ref,
   5359     } = if (else_node != 0) blk: {
   5360         try else_scope.addDbgBlockBegin();
   5361         const sub_scope = s: {
   5362             if (if_full.error_token) |error_token| {
   5363                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   5364                     .err_union_code_ptr
   5365                 else
   5366                     .err_union_code;
   5367                 const payload_inst = try else_scope.addUnNode(tag, cond.inst, node);
   5368                 const ident_name = try astgen.identAsString(error_token);
   5369                 const error_token_str = tree.tokenSlice(error_token);
   5370                 if (mem.eql(u8, "_", error_token_str))
   5371                     break :s &else_scope.base;
   5372                 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, error_token_str);
   5373                 payload_val_scope = .{
   5374                     .parent = &else_scope.base,
   5375                     .gen_zir = &else_scope,
   5376                     .name = ident_name,
   5377                     .inst = payload_inst,
   5378                     .token_src = error_token,
   5379                     .id_cat = .@"capture",
   5380                 };
   5381                 try else_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
   5382                 break :s &payload_val_scope.base;
   5383             } else {
   5384                 break :s &else_scope.base;
   5385             }
   5386         };
   5387         const e = try expr(&else_scope, sub_scope, block_scope.break_result_loc, else_node);
   5388         if (!else_scope.endsWithNoReturn()) {
   5389             block_scope.break_count += 1;
   5390         }
   5391         try checkUsed(parent_gz, &else_scope.base, sub_scope);
   5392         try else_scope.addDbgBlockEnd();
   5393         break :blk .{
   5394             .src = else_node,
   5395             .result = e,
   5396         };
   5397     } else .{
   5398         .src = if_full.ast.then_expr,
   5399         .result = .none,
   5400     };
   5401 
   5402     const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break";
   5403     return finishThenElseBlock(
   5404         parent_gz,
   5405         rl,
   5406         node,
   5407         &block_scope,
   5408         &then_scope,
   5409         &else_scope,
   5410         condbr,
   5411         cond.bool_bit,
   5412         then_result,
   5413         else_info.result,
   5414         block,
   5415         block,
   5416         break_tag,
   5417     );
   5418 }
   5419 
   5420 /// Supports `else_scope` stacked on `then_scope`. Unstacks `else_scope` then `then_scope`.
   5421 fn setCondBrPayload(
   5422     condbr: Zir.Inst.Index,
   5423     cond: Zir.Inst.Ref,
   5424     then_scope: *GenZir,
   5425     then_break: Zir.Inst.Index,
   5426     else_scope: *GenZir,
   5427     else_break: Zir.Inst.Index,
   5428 ) !void {
   5429     defer then_scope.unstack();
   5430     defer else_scope.unstack();
   5431     const astgen = then_scope.astgen;
   5432     const then_body = then_scope.instructionsSliceUpto(else_scope);
   5433     const else_body = else_scope.instructionsSlice();
   5434     const then_body_len = astgen.countBodyLenAfterFixups(then_body) + @boolToInt(then_break != 0);
   5435     const else_body_len = astgen.countBodyLenAfterFixups(else_body) + @boolToInt(else_break != 0);
   5436     try astgen.extra.ensureUnusedCapacity(
   5437         astgen.gpa,
   5438         @typeInfo(Zir.Inst.CondBr).Struct.fields.len + then_body_len + else_body_len,
   5439     );
   5440 
   5441     const zir_datas = astgen.instructions.items(.data);
   5442     zir_datas[condbr].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.CondBr{
   5443         .condition = cond,
   5444         .then_body_len = then_body_len,
   5445         .else_body_len = else_body_len,
   5446     });
   5447     astgen.appendBodyWithFixups(then_body);
   5448     if (then_break != 0) astgen.extra.appendAssumeCapacity(then_break);
   5449     astgen.appendBodyWithFixups(else_body);
   5450     if (else_break != 0) astgen.extra.appendAssumeCapacity(else_break);
   5451 }
   5452 
   5453 /// Supports `else_scope` stacked on `then_scope`. Unstacks `else_scope` then `then_scope`.
   5454 fn setCondBrPayloadElideBlockStorePtr(
   5455     condbr: Zir.Inst.Index,
   5456     cond: Zir.Inst.Ref,
   5457     then_scope: *GenZir,
   5458     then_break: Zir.Inst.Index,
   5459     else_scope: *GenZir,
   5460     else_break: Zir.Inst.Index,
   5461     block_ptr: Zir.Inst.Ref,
   5462 ) !void {
   5463     defer then_scope.unstack();
   5464     defer else_scope.unstack();
   5465     const astgen = then_scope.astgen;
   5466     const then_body = then_scope.instructionsSliceUpto(else_scope);
   5467     const else_body = else_scope.instructionsSlice();
   5468     const has_then_break = then_break != 0;
   5469     const has_else_break = else_break != 0;
   5470     const then_body_len = astgen.countBodyLenAfterFixups(then_body) + @boolToInt(has_then_break);
   5471     const else_body_len = astgen.countBodyLenAfterFixups(else_body) + @boolToInt(has_else_break);
   5472     try astgen.extra.ensureUnusedCapacity(
   5473         astgen.gpa,
   5474         @typeInfo(Zir.Inst.CondBr).Struct.fields.len + then_body_len + else_body_len,
   5475     );
   5476 
   5477     const zir_tags = astgen.instructions.items(.tag);
   5478     const zir_datas = astgen.instructions.items(.data);
   5479 
   5480     const condbr_pl = astgen.addExtraAssumeCapacity(Zir.Inst.CondBr{
   5481         .condition = cond,
   5482         .then_body_len = then_body_len,
   5483         .else_body_len = else_body_len,
   5484     });
   5485     zir_datas[condbr].pl_node.payload_index = condbr_pl;
   5486     const then_body_len_index = condbr_pl + 1;
   5487     const else_body_len_index = condbr_pl + 2;
   5488 
   5489     // The break instructions need to have their operands coerced if the
   5490     // switch's result location is a `ty`. In this case we overwrite the
   5491     // `store_to_block_ptr` instruction with an `as` instruction and repurpose
   5492     // it as the break operand.
   5493     // This corresponds to similar code in `labeledBlockExpr`.
   5494     for (then_body) |src_inst| {
   5495         if (zir_tags[src_inst] == .store_to_block_ptr and
   5496             zir_datas[src_inst].bin.lhs == block_ptr)
   5497         {
   5498             if (then_scope.rl_ty_inst != .none and has_then_break) {
   5499                 zir_tags[src_inst] = .as;
   5500                 zir_datas[src_inst].bin = .{
   5501                     .lhs = then_scope.rl_ty_inst,
   5502                     .rhs = zir_datas[then_break].@"break".operand,
   5503                 };
   5504                 zir_datas[then_break].@"break".operand = indexToRef(src_inst);
   5505             } else {
   5506                 astgen.extra.items[then_body_len_index] -= 1;
   5507                 continue;
   5508             }
   5509         }
   5510         appendPossiblyRefdBodyInst(astgen, &astgen.extra, src_inst);
   5511     }
   5512     if (has_then_break) astgen.extra.appendAssumeCapacity(then_break);
   5513 
   5514     for (else_body) |src_inst| {
   5515         if (zir_tags[src_inst] == .store_to_block_ptr and
   5516             zir_datas[src_inst].bin.lhs == block_ptr)
   5517         {
   5518             if (else_scope.rl_ty_inst != .none and has_else_break) {
   5519                 zir_tags[src_inst] = .as;
   5520                 zir_datas[src_inst].bin = .{
   5521                     .lhs = else_scope.rl_ty_inst,
   5522                     .rhs = zir_datas[else_break].@"break".operand,
   5523                 };
   5524                 zir_datas[else_break].@"break".operand = indexToRef(src_inst);
   5525             } else {
   5526                 astgen.extra.items[else_body_len_index] -= 1;
   5527                 continue;
   5528             }
   5529         }
   5530         appendPossiblyRefdBodyInst(astgen, &astgen.extra, src_inst);
   5531     }
   5532     if (has_else_break) astgen.extra.appendAssumeCapacity(else_break);
   5533 }
   5534 
   5535 fn whileExpr(
   5536     parent_gz: *GenZir,
   5537     scope: *Scope,
   5538     rl: ResultLoc,
   5539     node: Ast.Node.Index,
   5540     while_full: Ast.full.While,
   5541 ) InnerError!Zir.Inst.Ref {
   5542     const astgen = parent_gz.astgen;
   5543     const tree = astgen.tree;
   5544     const token_tags = tree.tokens.items(.tag);
   5545 
   5546     if (while_full.label_token) |label_token| {
   5547         try astgen.checkLabelRedefinition(scope, label_token);
   5548     }
   5549 
   5550     const is_inline = parent_gz.force_comptime or while_full.inline_token != null;
   5551     const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop;
   5552     const loop_block = try parent_gz.makeBlockInst(loop_tag, node);
   5553     try parent_gz.instructions.append(astgen.gpa, loop_block);
   5554 
   5555     var loop_scope = parent_gz.makeSubBlock(scope);
   5556     loop_scope.is_inline = is_inline;
   5557     loop_scope.setBreakResultLoc(rl);
   5558     defer loop_scope.unstack();
   5559     defer loop_scope.labeled_breaks.deinit(astgen.gpa);
   5560 
   5561     var continue_scope = parent_gz.makeSubBlock(&loop_scope.base);
   5562     defer continue_scope.unstack();
   5563 
   5564     const payload_is_ref = if (while_full.payload_token) |payload_token|
   5565         token_tags[payload_token] == .asterisk
   5566     else
   5567         false;
   5568 
   5569     try emitDbgNode(parent_gz, while_full.ast.cond_expr);
   5570     const cond: struct {
   5571         inst: Zir.Inst.Ref,
   5572         bool_bit: Zir.Inst.Ref,
   5573     } = c: {
   5574         if (while_full.error_token) |_| {
   5575             const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
   5576             const err_union = try expr(&continue_scope, &continue_scope.base, cond_rl, while_full.ast.cond_expr);
   5577             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
   5578             break :c .{
   5579                 .inst = err_union,
   5580                 .bool_bit = try continue_scope.addUnNode(tag, err_union, node),
   5581             };
   5582         } else if (while_full.payload_token) |_| {
   5583             const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
   5584             const optional = try expr(&continue_scope, &continue_scope.base, cond_rl, while_full.ast.cond_expr);
   5585             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
   5586             break :c .{
   5587                 .inst = optional,
   5588                 .bool_bit = try continue_scope.addUnNode(tag, optional, node),
   5589             };
   5590         } else {
   5591             const cond = try expr(&continue_scope, &continue_scope.base, bool_rl, while_full.ast.cond_expr);
   5592             break :c .{
   5593                 .inst = cond,
   5594                 .bool_bit = cond,
   5595             };
   5596         }
   5597     };
   5598 
   5599     const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
   5600     const condbr = try continue_scope.addCondBr(condbr_tag, node);
   5601     const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block;
   5602     const cond_block = try loop_scope.makeBlockInst(block_tag, node);
   5603     try continue_scope.setBlockBody(cond_block);
   5604     // continue_scope unstacked now, can add new instructions to loop_scope
   5605     try loop_scope.instructions.append(astgen.gpa, cond_block);
   5606 
   5607     // make scope now but don't stack on parent_gz until loop_scope
   5608     // gets unstacked after cont_expr is emitted and added below
   5609     var then_scope = parent_gz.makeSubBlock(&continue_scope.base);
   5610     then_scope.markAsLoopBody(loop_scope);
   5611     then_scope.instructions_top = GenZir.unstacked_top;
   5612     defer then_scope.unstack();
   5613 
   5614     var dbg_var_name: ?u32 = null;
   5615     var dbg_var_inst: Zir.Inst.Ref = undefined;
   5616     var payload_inst: Zir.Inst.Index = 0;
   5617     var payload_val_scope: Scope.LocalVal = undefined;
   5618     const then_sub_scope = s: {
   5619         if (while_full.error_token != null) {
   5620             if (while_full.payload_token) |payload_token| {
   5621                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   5622                     .err_union_payload_unsafe_ptr
   5623                 else
   5624                     .err_union_payload_unsafe;
   5625                 // will add this instruction to then_scope.instructions below
   5626                 payload_inst = try then_scope.makeUnNode(tag, cond.inst, node);
   5627                 const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
   5628                 const ident_bytes = tree.tokenSlice(ident_token);
   5629                 if (mem.eql(u8, "_", ident_bytes))
   5630                     break :s &then_scope.base;
   5631                 const payload_name_loc = payload_token + @boolToInt(payload_is_ref);
   5632                 const ident_name = try astgen.identAsString(payload_name_loc);
   5633                 try astgen.detectLocalShadowing(&then_scope.base, ident_name, payload_name_loc, ident_bytes);
   5634                 payload_val_scope = .{
   5635                     .parent = &then_scope.base,
   5636                     .gen_zir = &then_scope,
   5637                     .name = ident_name,
   5638                     .inst = indexToRef(payload_inst),
   5639                     .token_src = payload_token,
   5640                     .id_cat = .@"capture",
   5641                 };
   5642                 dbg_var_name = ident_name;
   5643                 dbg_var_inst = indexToRef(payload_inst);
   5644                 break :s &payload_val_scope.base;
   5645             } else {
   5646                 break :s &then_scope.base;
   5647             }
   5648         } else if (while_full.payload_token) |payload_token| {
   5649             const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
   5650             const tag: Zir.Inst.Tag = if (payload_is_ref)
   5651                 .optional_payload_unsafe_ptr
   5652             else
   5653                 .optional_payload_unsafe;
   5654             // will add this instruction to then_scope.instructions below
   5655             payload_inst = try then_scope.makeUnNode(tag, cond.inst, node);
   5656             const ident_name = try astgen.identAsString(ident_token);
   5657             const ident_bytes = tree.tokenSlice(ident_token);
   5658             if (mem.eql(u8, "_", ident_bytes))
   5659                 break :s &then_scope.base;
   5660             try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes);
   5661             payload_val_scope = .{
   5662                 .parent = &then_scope.base,
   5663                 .gen_zir = &then_scope,
   5664                 .name = ident_name,
   5665                 .inst = indexToRef(payload_inst),
   5666                 .token_src = ident_token,
   5667                 .id_cat = .@"capture",
   5668             };
   5669             dbg_var_name = ident_name;
   5670             dbg_var_inst = indexToRef(payload_inst);
   5671             break :s &payload_val_scope.base;
   5672         } else {
   5673             break :s &then_scope.base;
   5674         }
   5675     };
   5676 
   5677     // This code could be improved to avoid emitting the continue expr when there
   5678     // are no jumps to it. This happens when the last statement of a while body is noreturn
   5679     // and there are no `continue` statements.
   5680     // Tracking issue: https://github.com/ziglang/zig/issues/9185
   5681     try then_scope.addDbgBlockBegin();
   5682     if (dbg_var_name) |some| {
   5683         try then_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
   5684     }
   5685     if (while_full.ast.cont_expr != 0) {
   5686         _ = try unusedResultExpr(&loop_scope, then_sub_scope, while_full.ast.cont_expr);
   5687     }
   5688     try then_scope.addDbgBlockEnd();
   5689     const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
   5690     _ = try loop_scope.addNode(repeat_tag, node);
   5691 
   5692     try loop_scope.setBlockBody(loop_block);
   5693     loop_scope.break_block = loop_block;
   5694     loop_scope.continue_block = cond_block;
   5695     if (while_full.label_token) |label_token| {
   5696         loop_scope.label = @as(?GenZir.Label, GenZir.Label{
   5697             .token = label_token,
   5698             .block_inst = loop_block,
   5699         });
   5700     }
   5701 
   5702     // done adding instructions to loop_scope, can now stack then_scope
   5703     then_scope.instructions_top = then_scope.instructions.items.len;
   5704 
   5705     if (payload_inst != 0) try then_scope.instructions.append(astgen.gpa, payload_inst);
   5706     try then_scope.addDbgBlockBegin();
   5707     if (dbg_var_name) |some| {
   5708         try then_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
   5709     }
   5710     const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr);
   5711     try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
   5712     try then_scope.addDbgBlockEnd();
   5713 
   5714     var else_scope = parent_gz.makeSubBlock(&continue_scope.base);
   5715     defer else_scope.unstack();
   5716 
   5717     const else_node = while_full.ast.else_expr;
   5718     const else_info: struct {
   5719         src: Ast.Node.Index,
   5720         result: Zir.Inst.Ref,
   5721     } = if (else_node != 0) blk: {
   5722         try else_scope.addDbgBlockBegin();
   5723         const sub_scope = s: {
   5724             if (while_full.error_token) |error_token| {
   5725                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   5726                     .err_union_code_ptr
   5727                 else
   5728                     .err_union_code;
   5729                 const else_payload_inst = try else_scope.addUnNode(tag, cond.inst, node);
   5730                 const ident_name = try astgen.identAsString(error_token);
   5731                 const ident_bytes = tree.tokenSlice(error_token);
   5732                 if (mem.eql(u8, ident_bytes, "_"))
   5733                     break :s &else_scope.base;
   5734                 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, ident_bytes);
   5735                 payload_val_scope = .{
   5736                     .parent = &else_scope.base,
   5737                     .gen_zir = &else_scope,
   5738                     .name = ident_name,
   5739                     .inst = else_payload_inst,
   5740                     .token_src = error_token,
   5741                     .id_cat = .@"capture",
   5742                 };
   5743                 try else_scope.addDbgVar(.dbg_var_val, ident_name, else_payload_inst);
   5744                 break :s &payload_val_scope.base;
   5745             } else {
   5746                 break :s &else_scope.base;
   5747             }
   5748         };
   5749         const e = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node);
   5750         if (!else_scope.endsWithNoReturn()) {
   5751             loop_scope.break_count += 1;
   5752         }
   5753         try checkUsed(parent_gz, &else_scope.base, sub_scope);
   5754         try else_scope.addDbgBlockEnd();
   5755         break :blk .{
   5756             .src = else_node,
   5757             .result = e,
   5758         };
   5759     } else .{
   5760         .src = while_full.ast.then_expr,
   5761         .result = .none,
   5762     };
   5763 
   5764     if (loop_scope.label) |some| {
   5765         if (!some.used) {
   5766             try astgen.appendErrorTok(some.token, "unused while loop label", .{});
   5767         }
   5768     }
   5769     const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
   5770     return finishThenElseBlock(
   5771         parent_gz,
   5772         rl,
   5773         node,
   5774         &loop_scope,
   5775         &then_scope,
   5776         &else_scope,
   5777         condbr,
   5778         cond.bool_bit,
   5779         then_result,
   5780         else_info.result,
   5781         loop_block,
   5782         cond_block,
   5783         break_tag,
   5784     );
   5785 }
   5786 
   5787 fn forExpr(
   5788     parent_gz: *GenZir,
   5789     scope: *Scope,
   5790     rl: ResultLoc,
   5791     node: Ast.Node.Index,
   5792     for_full: Ast.full.While,
   5793 ) InnerError!Zir.Inst.Ref {
   5794     const astgen = parent_gz.astgen;
   5795 
   5796     if (for_full.label_token) |label_token| {
   5797         try astgen.checkLabelRedefinition(scope, label_token);
   5798     }
   5799 
   5800     // Set up variables and constants.
   5801     const is_inline = parent_gz.force_comptime or for_full.inline_token != null;
   5802     const tree = astgen.tree;
   5803     const token_tags = tree.tokens.items(.tag);
   5804 
   5805     const payload_is_ref = if (for_full.payload_token) |payload_token|
   5806         token_tags[payload_token] == .asterisk
   5807     else
   5808         false;
   5809 
   5810     try emitDbgNode(parent_gz, for_full.ast.cond_expr);
   5811 
   5812     const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
   5813     const array_ptr = try expr(parent_gz, scope, cond_rl, for_full.ast.cond_expr);
   5814     const len = try parent_gz.addUnNode(.indexable_ptr_len, array_ptr, for_full.ast.cond_expr);
   5815 
   5816     const index_ptr = blk: {
   5817         const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime_mut else .alloc;
   5818         const index_ptr = try parent_gz.addUnNode(alloc_tag, .usize_type, node);
   5819         // initialize to zero
   5820         _ = try parent_gz.addBin(.store, index_ptr, .zero_usize);
   5821         break :blk index_ptr;
   5822     };
   5823 
   5824     const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop;
   5825     const loop_block = try parent_gz.makeBlockInst(loop_tag, node);
   5826     try parent_gz.instructions.append(astgen.gpa, loop_block);
   5827 
   5828     var loop_scope = parent_gz.makeSubBlock(scope);
   5829     loop_scope.is_inline = is_inline;
   5830     loop_scope.setBreakResultLoc(rl);
   5831     defer loop_scope.unstack();
   5832     defer loop_scope.labeled_breaks.deinit(astgen.gpa);
   5833 
   5834     var cond_scope = parent_gz.makeSubBlock(&loop_scope.base);
   5835     defer cond_scope.unstack();
   5836 
   5837     // check condition i < array_expr.len
   5838     const index = try cond_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr);
   5839     const cond = try cond_scope.addPlNode(.cmp_lt, for_full.ast.cond_expr, Zir.Inst.Bin{
   5840         .lhs = index,
   5841         .rhs = len,
   5842     });
   5843 
   5844     const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
   5845     const condbr = try cond_scope.addCondBr(condbr_tag, node);
   5846     const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block;
   5847     const cond_block = try loop_scope.makeBlockInst(block_tag, node);
   5848     try cond_scope.setBlockBody(cond_block);
   5849     // cond_block unstacked now, can add new instructions to loop_scope
   5850     try loop_scope.instructions.append(astgen.gpa, cond_block);
   5851 
   5852     // Increment the index variable.
   5853     const index_2 = try loop_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr);
   5854     const index_plus_one = try loop_scope.addPlNode(.add, node, Zir.Inst.Bin{
   5855         .lhs = index_2,
   5856         .rhs = .one_usize,
   5857     });
   5858     _ = try loop_scope.addBin(.store, index_ptr, index_plus_one);
   5859     const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
   5860     _ = try loop_scope.addNode(repeat_tag, node);
   5861 
   5862     try loop_scope.setBlockBody(loop_block);
   5863     loop_scope.break_block = loop_block;
   5864     loop_scope.continue_block = cond_block;
   5865     if (for_full.label_token) |label_token| {
   5866         loop_scope.label = @as(?GenZir.Label, GenZir.Label{
   5867             .token = label_token,
   5868             .block_inst = loop_block,
   5869         });
   5870     }
   5871 
   5872     var then_scope = parent_gz.makeSubBlock(&cond_scope.base);
   5873     then_scope.markAsLoopBody(loop_scope);
   5874     defer then_scope.unstack();
   5875 
   5876     try then_scope.addDbgBlockBegin();
   5877     var payload_val_scope: Scope.LocalVal = undefined;
   5878     var index_scope: Scope.LocalPtr = undefined;
   5879     const then_sub_scope = blk: {
   5880         const payload_token = for_full.payload_token.?;
   5881         const ident = if (token_tags[payload_token] == .asterisk)
   5882             payload_token + 1
   5883         else
   5884             payload_token;
   5885         const is_ptr = ident != payload_token;
   5886         const value_name = tree.tokenSlice(ident);
   5887         var payload_sub_scope: *Scope = undefined;
   5888         if (!mem.eql(u8, value_name, "_")) {
   5889             const name_str_index = try astgen.identAsString(ident);
   5890             const tag: Zir.Inst.Tag = if (is_ptr) .elem_ptr else .elem_val;
   5891             const payload_inst = try then_scope.addPlNode(tag, for_full.ast.cond_expr, Zir.Inst.Bin{
   5892                 .lhs = array_ptr,
   5893                 .rhs = index,
   5894             });
   5895             try astgen.detectLocalShadowing(&then_scope.base, name_str_index, ident, value_name);
   5896             payload_val_scope = .{
   5897                 .parent = &then_scope.base,
   5898                 .gen_zir = &then_scope,
   5899                 .name = name_str_index,
   5900                 .inst = payload_inst,
   5901                 .token_src = ident,
   5902                 .id_cat = .@"capture",
   5903             };
   5904             try then_scope.addDbgVar(.dbg_var_val, name_str_index, payload_inst);
   5905             payload_sub_scope = &payload_val_scope.base;
   5906         } else if (is_ptr) {
   5907             return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   5908         } else {
   5909             payload_sub_scope = &then_scope.base;
   5910         }
   5911 
   5912         const index_token = if (token_tags[ident + 1] == .comma)
   5913             ident + 2
   5914         else
   5915             break :blk payload_sub_scope;
   5916         const token_bytes = tree.tokenSlice(index_token);
   5917         if (mem.eql(u8, token_bytes, "_")) {
   5918             return astgen.failTok(index_token, "discard of index capture; omit it instead", .{});
   5919         }
   5920         const index_name = try astgen.identAsString(index_token);
   5921         try astgen.detectLocalShadowing(payload_sub_scope, index_name, index_token, token_bytes);
   5922         index_scope = .{
   5923             .parent = payload_sub_scope,
   5924             .gen_zir = &then_scope,
   5925             .name = index_name,
   5926             .ptr = index_ptr,
   5927             .token_src = index_token,
   5928             .maybe_comptime = is_inline,
   5929             .id_cat = .@"loop index capture",
   5930         };
   5931         try then_scope.addDbgVar(.dbg_var_val, index_name, index_ptr);
   5932         break :blk &index_scope.base;
   5933     };
   5934 
   5935     const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr);
   5936     try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
   5937     try then_scope.addDbgBlockEnd();
   5938 
   5939     var else_scope = parent_gz.makeSubBlock(&cond_scope.base);
   5940     defer else_scope.unstack();
   5941 
   5942     const else_node = for_full.ast.else_expr;
   5943     const else_info: struct {
   5944         src: Ast.Node.Index,
   5945         result: Zir.Inst.Ref,
   5946     } = if (else_node != 0) blk: {
   5947         const sub_scope = &else_scope.base;
   5948         const else_result = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node);
   5949         if (!else_scope.endsWithNoReturn()) {
   5950             loop_scope.break_count += 1;
   5951         }
   5952         break :blk .{
   5953             .src = else_node,
   5954             .result = else_result,
   5955         };
   5956     } else .{
   5957         .src = for_full.ast.then_expr,
   5958         .result = .none,
   5959     };
   5960 
   5961     if (loop_scope.label) |some| {
   5962         if (!some.used) {
   5963             try astgen.appendErrorTok(some.token, "unused for loop label", .{});
   5964         }
   5965     }
   5966     const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
   5967     return finishThenElseBlock(
   5968         parent_gz,
   5969         rl,
   5970         node,
   5971         &loop_scope,
   5972         &then_scope,
   5973         &else_scope,
   5974         condbr,
   5975         cond,
   5976         then_result,
   5977         else_info.result,
   5978         loop_block,
   5979         cond_block,
   5980         break_tag,
   5981     );
   5982 }
   5983 
   5984 fn switchExpr(
   5985     parent_gz: *GenZir,
   5986     scope: *Scope,
   5987     rl: ResultLoc,
   5988     switch_node: Ast.Node.Index,
   5989 ) InnerError!Zir.Inst.Ref {
   5990     const astgen = parent_gz.astgen;
   5991     const gpa = astgen.gpa;
   5992     const tree = astgen.tree;
   5993     const node_datas = tree.nodes.items(.data);
   5994     const node_tags = tree.nodes.items(.tag);
   5995     const main_tokens = tree.nodes.items(.main_token);
   5996     const token_tags = tree.tokens.items(.tag);
   5997     const operand_node = node_datas[switch_node].lhs;
   5998     const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
   5999     const case_nodes = tree.extra_data[extra.start..extra.end];
   6000 
   6001     // We perform two passes over the AST. This first pass is to collect information
   6002     // for the following variables, make note of the special prong AST node index,
   6003     // and bail out with a compile error if there are multiple special prongs present.
   6004     var any_payload_is_ref = false;
   6005     var scalar_cases_len: u32 = 0;
   6006     var multi_cases_len: u32 = 0;
   6007     var special_prong: Zir.SpecialProng = .none;
   6008     var special_node: Ast.Node.Index = 0;
   6009     var else_src: ?Ast.TokenIndex = null;
   6010     var underscore_src: ?Ast.TokenIndex = null;
   6011     for (case_nodes) |case_node| {
   6012         const case = switch (node_tags[case_node]) {
   6013             .switch_case_one => tree.switchCaseOne(case_node),
   6014             .switch_case => tree.switchCase(case_node),
   6015             else => unreachable,
   6016         };
   6017         if (case.payload_token) |payload_token| {
   6018             if (token_tags[payload_token] == .asterisk) {
   6019                 any_payload_is_ref = true;
   6020             }
   6021         }
   6022         // Check for else/`_` prong.
   6023         if (case.ast.values.len == 0) {
   6024             const case_src = case.ast.arrow_token - 1;
   6025             if (else_src) |src| {
   6026                 return astgen.failTokNotes(
   6027                     case_src,
   6028                     "multiple else prongs in switch expression",
   6029                     .{},
   6030                     &[_]u32{
   6031                         try astgen.errNoteTok(
   6032                             src,
   6033                             "previous else prong here",
   6034                             .{},
   6035                         ),
   6036                     },
   6037                 );
   6038             } else if (underscore_src) |some_underscore| {
   6039                 return astgen.failNodeNotes(
   6040                     switch_node,
   6041                     "else and '_' prong in switch expression",
   6042                     .{},
   6043                     &[_]u32{
   6044                         try astgen.errNoteTok(
   6045                             case_src,
   6046                             "else prong here",
   6047                             .{},
   6048                         ),
   6049                         try astgen.errNoteTok(
   6050                             some_underscore,
   6051                             "'_' prong here",
   6052                             .{},
   6053                         ),
   6054                     },
   6055                 );
   6056             }
   6057             special_node = case_node;
   6058             special_prong = .@"else";
   6059             else_src = case_src;
   6060             continue;
   6061         } else if (case.ast.values.len == 1 and
   6062             node_tags[case.ast.values[0]] == .identifier and
   6063             mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
   6064         {
   6065             const case_src = case.ast.arrow_token - 1;
   6066             if (underscore_src) |src| {
   6067                 return astgen.failTokNotes(
   6068                     case_src,
   6069                     "multiple '_' prongs in switch expression",
   6070                     .{},
   6071                     &[_]u32{
   6072                         try astgen.errNoteTok(
   6073                             src,
   6074                             "previous '_' prong here",
   6075                             .{},
   6076                         ),
   6077                     },
   6078                 );
   6079             } else if (else_src) |some_else| {
   6080                 return astgen.failNodeNotes(
   6081                     switch_node,
   6082                     "else and '_' prong in switch expression",
   6083                     .{},
   6084                     &[_]u32{
   6085                         try astgen.errNoteTok(
   6086                             some_else,
   6087                             "else prong here",
   6088                             .{},
   6089                         ),
   6090                         try astgen.errNoteTok(
   6091                             case_src,
   6092                             "'_' prong here",
   6093                             .{},
   6094                         ),
   6095                     },
   6096                 );
   6097             }
   6098             special_node = case_node;
   6099             special_prong = .under;
   6100             underscore_src = case_src;
   6101             continue;
   6102         }
   6103 
   6104         if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] != .switch_range) {
   6105             scalar_cases_len += 1;
   6106         } else {
   6107             multi_cases_len += 1;
   6108         }
   6109     }
   6110 
   6111     const operand_rl: ResultLoc = if (any_payload_is_ref) .ref else .none;
   6112     const raw_operand = try expr(parent_gz, scope, operand_rl, operand_node);
   6113     const cond_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_cond_ref else .switch_cond;
   6114     const cond = try parent_gz.addUnNode(cond_tag, raw_operand, operand_node);
   6115     // We need the type of the operand to use as the result location for all the prong items.
   6116     const cond_ty_inst = try parent_gz.addUnNode(.typeof, cond, operand_node);
   6117     const item_rl: ResultLoc = .{ .ty = cond_ty_inst };
   6118 
   6119     // This contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti,
   6120     // except the first cases_nodes.len slots are a table that indexes payloads later in the array, with
   6121     // the special case index coming first, then scalar_case_len indexes, then multi_cases_len indexes
   6122     const payloads = &astgen.scratch;
   6123     const scratch_top = astgen.scratch.items.len;
   6124     const case_table_start = scratch_top;
   6125     const scalar_case_table = case_table_start + @boolToInt(special_prong != .none);
   6126     const multi_case_table = scalar_case_table + scalar_cases_len;
   6127     const case_table_end = multi_case_table + multi_cases_len;
   6128     try astgen.scratch.resize(gpa, case_table_end);
   6129     defer astgen.scratch.items.len = scratch_top;
   6130 
   6131     var block_scope = parent_gz.makeSubBlock(scope);
   6132     // block_scope not used for collecting instructions
   6133     block_scope.instructions_top = GenZir.unstacked_top;
   6134     block_scope.setBreakResultLoc(rl);
   6135 
   6136     // This gets added to the parent block later, after the item expressions.
   6137     const switch_block = try parent_gz.makeBlockInst(.switch_block, switch_node);
   6138 
   6139     // We re-use this same scope for all cases, including the special prong, if any.
   6140     var case_scope = parent_gz.makeSubBlock(&block_scope.base);
   6141     case_scope.instructions_top = GenZir.unstacked_top;
   6142 
   6143     // In this pass we generate all the item and prong expressions.
   6144     var multi_case_index: u32 = 0;
   6145     var scalar_case_index: u32 = 0;
   6146     for (case_nodes) |case_node| {
   6147         const case = switch (node_tags[case_node]) {
   6148             .switch_case_one => tree.switchCaseOne(case_node),
   6149             .switch_case => tree.switchCase(case_node),
   6150             else => unreachable,
   6151         };
   6152 
   6153         const is_multi_case = case.ast.values.len > 1 or
   6154             (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .switch_range);
   6155 
   6156         var dbg_var_name: ?u32 = null;
   6157         var dbg_var_inst: Zir.Inst.Ref = undefined;
   6158         var capture_inst: Zir.Inst.Index = 0;
   6159         var capture_val_scope: Scope.LocalVal = undefined;
   6160         const sub_scope = blk: {
   6161             const payload_token = case.payload_token orelse break :blk &case_scope.base;
   6162             const ident = if (token_tags[payload_token] == .asterisk)
   6163                 payload_token + 1
   6164             else
   6165                 payload_token;
   6166             const is_ptr = ident != payload_token;
   6167             if (mem.eql(u8, tree.tokenSlice(ident), "_")) {
   6168                 if (is_ptr) {
   6169                     return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   6170                 }
   6171                 break :blk &case_scope.base;
   6172             }
   6173             if (case_node == special_node) {
   6174                 const capture_tag: Zir.Inst.Tag = if (is_ptr)
   6175                     .switch_capture_ref
   6176                 else
   6177                     .switch_capture;
   6178                 capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
   6179                 try astgen.instructions.append(gpa, .{
   6180                     .tag = capture_tag,
   6181                     .data = .{
   6182                         .switch_capture = .{
   6183                             .switch_inst = switch_block,
   6184                             // Max int communicates that this is the else/underscore prong.
   6185                             .prong_index = std.math.maxInt(u32),
   6186                         },
   6187                     },
   6188                 });
   6189             } else {
   6190                 const is_multi_case_bits: u2 = @boolToInt(is_multi_case);
   6191                 const is_ptr_bits: u2 = @boolToInt(is_ptr);
   6192                 const capture_tag: Zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) {
   6193                     0b00 => .switch_capture,
   6194                     0b01 => .switch_capture_ref,
   6195                     0b10 => .switch_capture_multi,
   6196                     0b11 => .switch_capture_multi_ref,
   6197                 };
   6198                 const capture_index = if (is_multi_case) multi_case_index else scalar_case_index;
   6199                 capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
   6200                 try astgen.instructions.append(gpa, .{
   6201                     .tag = capture_tag,
   6202                     .data = .{ .switch_capture = .{
   6203                         .switch_inst = switch_block,
   6204                         .prong_index = capture_index,
   6205                     } },
   6206                 });
   6207             }
   6208             const capture_name = try astgen.identAsString(ident);
   6209             capture_val_scope = .{
   6210                 .parent = &case_scope.base,
   6211                 .gen_zir = &case_scope,
   6212                 .name = capture_name,
   6213                 .inst = indexToRef(capture_inst),
   6214                 .token_src = payload_token,
   6215                 .id_cat = .@"capture",
   6216             };
   6217             dbg_var_name = capture_name;
   6218             dbg_var_inst = indexToRef(capture_inst);
   6219             break :blk &capture_val_scope.base;
   6220         };
   6221 
   6222         const header_index = @intCast(u32, payloads.items.len);
   6223         const body_len_index = if (is_multi_case) blk: {
   6224             payloads.items[multi_case_table + multi_case_index] = header_index;
   6225             multi_case_index += 1;
   6226             try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len
   6227 
   6228             // items
   6229             var items_len: u32 = 0;
   6230             for (case.ast.values) |item_node| {
   6231                 if (node_tags[item_node] == .switch_range) continue;
   6232                 items_len += 1;
   6233 
   6234                 const item_inst = try comptimeExpr(parent_gz, scope, item_rl, item_node);
   6235                 try payloads.append(gpa, @enumToInt(item_inst));
   6236             }
   6237 
   6238             // ranges
   6239             var ranges_len: u32 = 0;
   6240             for (case.ast.values) |range| {
   6241                 if (node_tags[range] != .switch_range) continue;
   6242                 ranges_len += 1;
   6243 
   6244                 const first = try comptimeExpr(parent_gz, scope, item_rl, node_datas[range].lhs);
   6245                 const last = try comptimeExpr(parent_gz, scope, item_rl, node_datas[range].rhs);
   6246                 try payloads.appendSlice(gpa, &[_]u32{
   6247                     @enumToInt(first), @enumToInt(last),
   6248                 });
   6249             }
   6250 
   6251             payloads.items[header_index] = items_len;
   6252             payloads.items[header_index + 1] = ranges_len;
   6253             break :blk header_index + 2;
   6254         } else if (case_node == special_node) blk: {
   6255             payloads.items[case_table_start] = header_index;
   6256             try payloads.resize(gpa, header_index + 1); // body_len
   6257             break :blk header_index;
   6258         } else blk: {
   6259             payloads.items[scalar_case_table + scalar_case_index] = header_index;
   6260             scalar_case_index += 1;
   6261             try payloads.resize(gpa, header_index + 2); // item, body_len
   6262             const item_node = case.ast.values[0];
   6263             const item_inst = try comptimeExpr(parent_gz, scope, item_rl, item_node);
   6264             payloads.items[header_index] = @enumToInt(item_inst);
   6265             break :blk header_index + 1;
   6266         };
   6267 
   6268         {
   6269             // temporarily stack case_scope on parent_gz
   6270             case_scope.instructions_top = parent_gz.instructions.items.len;
   6271             defer case_scope.unstack();
   6272 
   6273             if (capture_inst != 0) try case_scope.instructions.append(gpa, capture_inst);
   6274             try case_scope.addDbgBlockBegin();
   6275             if (dbg_var_name) |some| {
   6276                 try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
   6277             }
   6278             const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
   6279             try checkUsed(parent_gz, &case_scope.base, sub_scope);
   6280             try case_scope.addDbgBlockEnd();
   6281             if (!parent_gz.refIsNoReturn(case_result)) {
   6282                 block_scope.break_count += 1;
   6283                 _ = try case_scope.addBreak(.@"break", switch_block, case_result);
   6284             }
   6285 
   6286             const case_slice = case_scope.instructionsSlice();
   6287             const body_len = astgen.countBodyLenAfterFixups(case_slice);
   6288             try payloads.ensureUnusedCapacity(gpa, body_len);
   6289             payloads.items[body_len_index] = body_len;
   6290             appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
   6291         }
   6292     }
   6293     // Now that the item expressions are generated we can add this.
   6294     try parent_gz.instructions.append(gpa, switch_block);
   6295 
   6296     try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).Struct.fields.len +
   6297         @boolToInt(multi_cases_len != 0) +
   6298         payloads.items.len - case_table_end);
   6299 
   6300     const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
   6301         .operand = cond,
   6302         .bits = Zir.Inst.SwitchBlock.Bits{
   6303             .is_ref = any_payload_is_ref,
   6304             .has_multi_cases = multi_cases_len != 0,
   6305             .has_else = special_prong == .@"else",
   6306             .has_under = special_prong == .under,
   6307             .scalar_cases_len = @intCast(Zir.Inst.SwitchBlock.Bits.ScalarCasesLen, scalar_cases_len),
   6308         },
   6309     });
   6310 
   6311     if (multi_cases_len != 0) {
   6312         astgen.extra.appendAssumeCapacity(multi_cases_len);
   6313     }
   6314 
   6315     const zir_datas = astgen.instructions.items(.data);
   6316     const zir_tags = astgen.instructions.items(.tag);
   6317 
   6318     zir_datas[switch_block].pl_node.payload_index = payload_index;
   6319 
   6320     const strat = rl.strategy(&block_scope);
   6321     for (payloads.items[case_table_start..case_table_end]) |start_index, i| {
   6322         var body_len_index = start_index;
   6323         var end_index = start_index;
   6324         const table_index = case_table_start + i;
   6325         if (table_index < scalar_case_table) {
   6326             end_index += 1;
   6327         } else if (table_index < multi_case_table) {
   6328             body_len_index += 1;
   6329             end_index += 2;
   6330         } else {
   6331             body_len_index += 2;
   6332             const items_len = payloads.items[start_index];
   6333             const ranges_len = payloads.items[start_index + 1];
   6334             end_index += 3 + items_len + 2 * ranges_len;
   6335         }
   6336 
   6337         const body_len = payloads.items[body_len_index];
   6338         end_index += body_len;
   6339 
   6340         switch (strat.tag) {
   6341             .break_operand => blk: {
   6342                 // Switch expressions return `true` for `nodeMayNeedMemoryLocation` thus
   6343                 // `elide_store_to_block_ptr_instructions` will either be true,
   6344                 // or all prongs are noreturn.
   6345                 if (!strat.elide_store_to_block_ptr_instructions)
   6346                     break :blk;
   6347 
   6348                 // There will necessarily be a store_to_block_ptr for
   6349                 // all prongs, except for prongs that ended with a noreturn instruction.
   6350                 // Elide all the `store_to_block_ptr` instructions.
   6351 
   6352                 // The break instructions need to have their operands coerced if the
   6353                 // switch's result location is a `ty`. In this case we overwrite the
   6354                 // `store_to_block_ptr` instruction with an `as` instruction and repurpose
   6355                 // it as the break operand.
   6356                 if (body_len < 2)
   6357                     break :blk;
   6358                 const store_inst = payloads.items[end_index - 2];
   6359                 if (zir_tags[store_inst] != .store_to_block_ptr or
   6360                     zir_datas[store_inst].bin.lhs != block_scope.rl_ptr)
   6361                     break :blk;
   6362                 const break_inst = payloads.items[end_index - 1];
   6363                 if (block_scope.rl_ty_inst != .none) {
   6364                     zir_tags[store_inst] = .as;
   6365                     zir_datas[store_inst].bin = .{
   6366                         .lhs = block_scope.rl_ty_inst,
   6367                         .rhs = zir_datas[break_inst].@"break".operand,
   6368                     };
   6369                     zir_datas[break_inst].@"break".operand = indexToRef(store_inst);
   6370                 } else {
   6371                     payloads.items[body_len_index] -= 1;
   6372                     astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index .. end_index - 2]);
   6373                     astgen.extra.appendAssumeCapacity(break_inst);
   6374                     continue;
   6375                 }
   6376             },
   6377             .break_void => {
   6378                 assert(!strat.elide_store_to_block_ptr_instructions);
   6379                 const last_inst = payloads.items[end_index - 1];
   6380                 if (zir_tags[last_inst] == .@"break" and
   6381                     zir_datas[last_inst].@"break".block_inst == switch_block)
   6382                 {
   6383                     zir_datas[last_inst].@"break".operand = .void_value;
   6384                 }
   6385             },
   6386         }
   6387 
   6388         astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
   6389     }
   6390 
   6391     const block_ref = indexToRef(switch_block);
   6392     if (strat.tag == .break_operand and strat.elide_store_to_block_ptr_instructions and rl != .ref)
   6393         return rvalue(parent_gz, rl, block_ref, switch_node);
   6394     return block_ref;
   6395 }
   6396 
   6397 fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   6398     const astgen = gz.astgen;
   6399     const tree = astgen.tree;
   6400     const node_datas = tree.nodes.items(.data);
   6401     const node_tags = tree.nodes.items(.tag);
   6402 
   6403     if (astgen.fn_block == null) {
   6404         return astgen.failNode(node, "'return' outside function scope", .{});
   6405     }
   6406 
   6407     if (gz.in_defer) return astgen.failNode(node, "cannot return from defer expression", .{});
   6408 
   6409     const defer_outer = &astgen.fn_block.?.base;
   6410 
   6411     const operand_node = node_datas[node].lhs;
   6412     if (operand_node == 0) {
   6413         // Returning a void value; skip error defers.
   6414         try genDefers(gz, defer_outer, scope, .normal_only);
   6415         _ = try gz.addUnNode(.ret_node, .void_value, node);
   6416         return Zir.Inst.Ref.unreachable_value;
   6417     }
   6418 
   6419     if (node_tags[operand_node] == .error_value) {
   6420         // Hot path for `return error.Foo`. This bypasses result location logic as well as logic
   6421         // for detecting whether to add something to the function's inferred error set.
   6422         const ident_token = node_datas[operand_node].rhs;
   6423         const err_name_str_index = try astgen.identAsString(ident_token);
   6424         const defer_counts = countDefers(astgen, defer_outer, scope);
   6425         if (!defer_counts.need_err_code) {
   6426             try genDefers(gz, defer_outer, scope, .both_sans_err);
   6427             _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token);
   6428             return Zir.Inst.Ref.unreachable_value;
   6429         }
   6430         const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token);
   6431         try genDefers(gz, defer_outer, scope, .{ .both = err_code });
   6432         _ = try gz.addUnNode(.ret_node, err_code, node);
   6433         return Zir.Inst.Ref.unreachable_value;
   6434     }
   6435 
   6436     const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node, true)) .{
   6437         .ptr = try gz.addNode(.ret_ptr, node),
   6438     } else .{
   6439         .ty = try gz.addNode(.ret_type, node),
   6440     };
   6441     const prev_anon_name_strategy = gz.anon_name_strategy;
   6442     gz.anon_name_strategy = .func;
   6443     const operand = try reachableExpr(gz, scope, rl, operand_node, node);
   6444     gz.anon_name_strategy = prev_anon_name_strategy;
   6445 
   6446     switch (nodeMayEvalToError(tree, operand_node)) {
   6447         .never => {
   6448             // Returning a value that cannot be an error; skip error defers.
   6449             try genDefers(gz, defer_outer, scope, .normal_only);
   6450             try gz.addRet(rl, operand, node);
   6451             return Zir.Inst.Ref.unreachable_value;
   6452         },
   6453         .always => {
   6454             // Value is always an error. Emit both error defers and regular defers.
   6455             const result = if (rl == .ptr) try gz.addUnNode(.load, rl.ptr, node) else operand;
   6456             const err_code = try gz.addUnNode(.err_union_code, result, node);
   6457             try genDefers(gz, defer_outer, scope, .{ .both = err_code });
   6458             try gz.addRet(rl, operand, node);
   6459             return Zir.Inst.Ref.unreachable_value;
   6460         },
   6461         .maybe => {
   6462             const defer_counts = countDefers(astgen, defer_outer, scope);
   6463             if (!defer_counts.have_err) {
   6464                 // Only regular defers; no branch needed.
   6465                 try genDefers(gz, defer_outer, scope, .normal_only);
   6466                 try gz.addRet(rl, operand, node);
   6467                 return Zir.Inst.Ref.unreachable_value;
   6468             }
   6469 
   6470             // Emit conditional branch for generating errdefers.
   6471             const result = if (rl == .ptr) try gz.addUnNode(.load, rl.ptr, node) else operand;
   6472             const is_non_err = try gz.addUnNode(.is_non_err, result, node);
   6473             const condbr = try gz.addCondBr(.condbr, node);
   6474 
   6475             var then_scope = gz.makeSubBlock(scope);
   6476             defer then_scope.unstack();
   6477 
   6478             try genDefers(&then_scope, defer_outer, scope, .normal_only);
   6479             try then_scope.addRet(rl, operand, node);
   6480 
   6481             var else_scope = gz.makeSubBlock(scope);
   6482             defer else_scope.unstack();
   6483 
   6484             const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{
   6485                 .both = try else_scope.addUnNode(.err_union_code, result, node),
   6486             };
   6487             try genDefers(&else_scope, defer_outer, scope, which_ones);
   6488             try else_scope.addRet(rl, operand, node);
   6489 
   6490             try setCondBrPayload(condbr, is_non_err, &then_scope, 0, &else_scope, 0);
   6491 
   6492             return Zir.Inst.Ref.unreachable_value;
   6493         },
   6494     }
   6495 }
   6496 
   6497 /// Parses the string `buf` as a base 10 integer of type `u16`.
   6498 ///
   6499 /// Unlike std.fmt.parseInt, does not allow the '_' character in `buf`.
   6500 fn parseBitCount(buf: []const u8) std.fmt.ParseIntError!u16 {
   6501     if (buf.len == 0) return error.InvalidCharacter;
   6502 
   6503     var x: u16 = 0;
   6504 
   6505     for (buf) |c| {
   6506         const digit = switch (c) {
   6507             '0'...'9' => c - '0',
   6508             else => return error.InvalidCharacter,
   6509         };
   6510 
   6511         if (x != 0) x = try std.math.mul(u16, x, 10);
   6512         x = try std.math.add(u16, x, @as(u16, digit));
   6513     }
   6514 
   6515     return x;
   6516 }
   6517 
   6518 fn identifier(
   6519     gz: *GenZir,
   6520     scope: *Scope,
   6521     rl: ResultLoc,
   6522     ident: Ast.Node.Index,
   6523 ) InnerError!Zir.Inst.Ref {
   6524     const tracy = trace(@src());
   6525     defer tracy.end();
   6526 
   6527     const astgen = gz.astgen;
   6528     const tree = astgen.tree;
   6529     const main_tokens = tree.nodes.items(.main_token);
   6530 
   6531     const ident_token = main_tokens[ident];
   6532     const ident_name_raw = tree.tokenSlice(ident_token);
   6533     if (mem.eql(u8, ident_name_raw, "_")) {
   6534         return astgen.failNode(ident, "'_' used as an identifier without @\"_\" syntax", .{});
   6535     }
   6536 
   6537     // if not @"" syntax, just use raw token slice
   6538     if (ident_name_raw[0] != '@') {
   6539         if (primitives.get(ident_name_raw)) |zir_const_ref| {
   6540             return rvalue(gz, rl, zir_const_ref, ident);
   6541         }
   6542 
   6543         if (ident_name_raw.len >= 2) integer: {
   6544             const first_c = ident_name_raw[0];
   6545             if (first_c == 'i' or first_c == 'u') {
   6546                 const signedness: std.builtin.Signedness = switch (first_c == 'i') {
   6547                     true => .signed,
   6548                     false => .unsigned,
   6549                 };
   6550                 const bit_count = parseBitCount(ident_name_raw[1..]) catch |err| switch (err) {
   6551                     error.Overflow => return astgen.failNode(
   6552                         ident,
   6553                         "primitive integer type '{s}' exceeds maximum bit width of 65535",
   6554                         .{ident_name_raw},
   6555                     ),
   6556                     error.InvalidCharacter => break :integer,
   6557                 };
   6558                 const result = try gz.add(.{
   6559                     .tag = .int_type,
   6560                     .data = .{ .int_type = .{
   6561                         .src_node = gz.nodeIndexToRelative(ident),
   6562                         .signedness = signedness,
   6563                         .bit_count = bit_count,
   6564                     } },
   6565                 });
   6566                 return rvalue(gz, rl, result, ident);
   6567             }
   6568         }
   6569     }
   6570 
   6571     // Local variables, including function parameters.
   6572     return localVarRef(gz, scope, rl, ident, ident_token);
   6573 }
   6574 
   6575 fn localVarRef(
   6576     gz: *GenZir,
   6577     scope: *Scope,
   6578     rl: ResultLoc,
   6579     ident: Ast.Node.Index,
   6580     ident_token: Ast.Node.Index,
   6581 ) InnerError!Zir.Inst.Ref {
   6582     const astgen = gz.astgen;
   6583     const gpa = astgen.gpa;
   6584 
   6585     const name_str_index = try astgen.identAsString(ident_token);
   6586     var s = scope;
   6587     var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
   6588     var num_namespaces_out: u32 = 0;
   6589     var capturing_namespace: ?*Scope.Namespace = null;
   6590     while (true) switch (s.tag) {
   6591         .local_val => {
   6592             const local_val = s.cast(Scope.LocalVal).?;
   6593 
   6594             if (local_val.name == name_str_index) {
   6595                 // Locals cannot shadow anything, so we do not need to look for ambiguous
   6596                 // references in this case.
   6597                 local_val.used = true;
   6598 
   6599                 const value_inst = try tunnelThroughClosure(
   6600                     gz,
   6601                     ident,
   6602                     num_namespaces_out,
   6603                     capturing_namespace,
   6604                     local_val.inst,
   6605                     local_val.token_src,
   6606                     gpa,
   6607                 );
   6608 
   6609                 return rvalue(gz, rl, value_inst, ident);
   6610             }
   6611             s = local_val.parent;
   6612         },
   6613         .local_ptr => {
   6614             const local_ptr = s.cast(Scope.LocalPtr).?;
   6615             if (local_ptr.name == name_str_index) {
   6616                 local_ptr.used = true;
   6617 
   6618                 // Can't close over a runtime variable
   6619                 if (num_namespaces_out != 0 and !local_ptr.maybe_comptime) {
   6620                     const ident_name = try astgen.identifierTokenString(ident_token);
   6621                     return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{
   6622                         try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}),
   6623                         try astgen.errNoteNode(capturing_namespace.?.node, "crosses namespace boundary here", .{}),
   6624                     });
   6625                 }
   6626 
   6627                 const ptr_inst = try tunnelThroughClosure(
   6628                     gz,
   6629                     ident,
   6630                     num_namespaces_out,
   6631                     capturing_namespace,
   6632                     local_ptr.ptr,
   6633                     local_ptr.token_src,
   6634                     gpa,
   6635                 );
   6636 
   6637                 switch (rl) {
   6638                     .ref => return ptr_inst,
   6639                     else => {
   6640                         const loaded = try gz.addUnNode(.load, ptr_inst, ident);
   6641                         return rvalue(gz, rl, loaded, ident);
   6642                     },
   6643                 }
   6644             }
   6645             s = local_ptr.parent;
   6646         },
   6647         .gen_zir => s = s.cast(GenZir).?.parent,
   6648         .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
   6649         .namespace => {
   6650             const ns = s.cast(Scope.Namespace).?;
   6651             if (ns.decls.get(name_str_index)) |i| {
   6652                 if (found_already) |f| {
   6653                     return astgen.failNodeNotes(ident, "ambiguous reference", .{}, &.{
   6654                         try astgen.errNoteNode(f, "declared here", .{}),
   6655                         try astgen.errNoteNode(i, "also declared here", .{}),
   6656                     });
   6657                 }
   6658                 // We found a match but must continue looking for ambiguous references to decls.
   6659                 found_already = i;
   6660             }
   6661             num_namespaces_out += 1;
   6662             capturing_namespace = ns;
   6663             s = ns.parent;
   6664         },
   6665         .top => break,
   6666     };
   6667     if (found_already == null) {
   6668         const ident_name = try astgen.identifierTokenString(ident_token);
   6669         return astgen.failNode(ident, "use of undeclared identifier '{s}'", .{ident_name});
   6670     }
   6671 
   6672     // Decl references happen by name rather than ZIR index so that when unrelated
   6673     // decls are modified, ZIR code containing references to them can be unmodified.
   6674     switch (rl) {
   6675         .ref => return gz.addStrTok(.decl_ref, name_str_index, ident_token),
   6676         else => {
   6677             const result = try gz.addStrTok(.decl_val, name_str_index, ident_token);
   6678             return rvalue(gz, rl, result, ident);
   6679         },
   6680     }
   6681 }
   6682 
   6683 /// Adds a capture to a namespace, if needed.
   6684 /// Returns the index of the closure_capture instruction.
   6685 fn tunnelThroughClosure(
   6686     gz: *GenZir,
   6687     inner_ref_node: Ast.Node.Index,
   6688     num_tunnels: u32,
   6689     ns: ?*Scope.Namespace,
   6690     value: Zir.Inst.Ref,
   6691     token: Ast.TokenIndex,
   6692     gpa: Allocator,
   6693 ) !Zir.Inst.Ref {
   6694     // For trivial values, we don't need a tunnel.
   6695     // Just return the ref.
   6696     if (num_tunnels == 0 or refToIndex(value) == null) {
   6697         return value;
   6698     }
   6699 
   6700     // Otherwise we need a tunnel.  Check if this namespace
   6701     // already has one for this value.
   6702     const gop = try ns.?.captures.getOrPut(gpa, refToIndex(value).?);
   6703     if (!gop.found_existing) {
   6704         // Make a new capture for this value but don't add it to the declaring_gz yet
   6705         try gz.astgen.instructions.append(gz.astgen.gpa, .{
   6706             .tag = .closure_capture,
   6707             .data = .{ .un_tok = .{
   6708                 .operand = value,
   6709                 .src_tok = ns.?.declaring_gz.?.tokenIndexToRelative(token),
   6710             } },
   6711         });
   6712         gop.value_ptr.* = @intCast(Zir.Inst.Index, gz.astgen.instructions.len - 1);
   6713     }
   6714 
   6715     // Add an instruction to get the value from the closure into
   6716     // our current context
   6717     return try gz.addInstNode(.closure_get, gop.value_ptr.*, inner_ref_node);
   6718 }
   6719 
   6720 fn stringLiteral(
   6721     gz: *GenZir,
   6722     rl: ResultLoc,
   6723     node: Ast.Node.Index,
   6724 ) InnerError!Zir.Inst.Ref {
   6725     const astgen = gz.astgen;
   6726     const tree = astgen.tree;
   6727     const main_tokens = tree.nodes.items(.main_token);
   6728     const str_lit_token = main_tokens[node];
   6729     const str = try astgen.strLitAsString(str_lit_token);
   6730     const result = try gz.add(.{
   6731         .tag = .str,
   6732         .data = .{ .str = .{
   6733             .start = str.index,
   6734             .len = str.len,
   6735         } },
   6736     });
   6737     return rvalue(gz, rl, result, node);
   6738 }
   6739 
   6740 fn multilineStringLiteral(
   6741     gz: *GenZir,
   6742     rl: ResultLoc,
   6743     node: Ast.Node.Index,
   6744 ) InnerError!Zir.Inst.Ref {
   6745     const astgen = gz.astgen;
   6746     const str = try astgen.strLitNodeAsString(node);
   6747     const result = try gz.add(.{
   6748         .tag = .str,
   6749         .data = .{ .str = .{
   6750             .start = str.index,
   6751             .len = str.len,
   6752         } },
   6753     });
   6754     return rvalue(gz, rl, result, node);
   6755 }
   6756 
   6757 fn charLiteral(gz: *GenZir, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   6758     const astgen = gz.astgen;
   6759     const tree = astgen.tree;
   6760     const main_tokens = tree.nodes.items(.main_token);
   6761     const main_token = main_tokens[node];
   6762     const slice = tree.tokenSlice(main_token);
   6763 
   6764     switch (std.zig.parseCharLiteral(slice)) {
   6765         .success => |codepoint| {
   6766             const result = try gz.addInt(codepoint);
   6767             return rvalue(gz, rl, result, node);
   6768         },
   6769         .failure => |err| return astgen.failWithStrLitError(err, main_token, slice, 0),
   6770     }
   6771 }
   6772 
   6773 fn integerLiteral(gz: *GenZir, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   6774     const astgen = gz.astgen;
   6775     const tree = astgen.tree;
   6776     const main_tokens = tree.nodes.items(.main_token);
   6777     const int_token = main_tokens[node];
   6778     const prefixed_bytes = tree.tokenSlice(int_token);
   6779     if (std.fmt.parseInt(u64, prefixed_bytes, 0)) |small_int| {
   6780         const result: Zir.Inst.Ref = switch (small_int) {
   6781             0 => .zero,
   6782             1 => .one,
   6783             else => try gz.addInt(small_int),
   6784         };
   6785         return rvalue(gz, rl, result, node);
   6786     } else |err| switch (err) {
   6787         error.InvalidCharacter => unreachable, // Caught by the parser.
   6788         error.Overflow => {},
   6789     }
   6790 
   6791     var base: u8 = 10;
   6792     var non_prefixed: []const u8 = prefixed_bytes;
   6793     if (mem.startsWith(u8, prefixed_bytes, "0x")) {
   6794         base = 16;
   6795         non_prefixed = prefixed_bytes[2..];
   6796     } else if (mem.startsWith(u8, prefixed_bytes, "0o")) {
   6797         base = 8;
   6798         non_prefixed = prefixed_bytes[2..];
   6799     } else if (mem.startsWith(u8, prefixed_bytes, "0b")) {
   6800         base = 2;
   6801         non_prefixed = prefixed_bytes[2..];
   6802     }
   6803 
   6804     const gpa = astgen.gpa;
   6805     var big_int = try std.math.big.int.Managed.init(gpa);
   6806     defer big_int.deinit();
   6807     big_int.setString(base, non_prefixed) catch |err| switch (err) {
   6808         error.InvalidCharacter => unreachable, // caught by parser
   6809         error.InvalidBase => unreachable, // we only pass 16, 8, 2, see above
   6810         error.OutOfMemory => return error.OutOfMemory,
   6811     };
   6812 
   6813     const limbs = big_int.limbs[0..big_int.len()];
   6814     assert(big_int.isPositive());
   6815     const result = try gz.addIntBig(limbs);
   6816     return rvalue(gz, rl, result, node);
   6817 }
   6818 
   6819 const Sign = enum { negative, positive };
   6820 
   6821 fn floatLiteral(gz: *GenZir, rl: ResultLoc, node: Ast.Node.Index, sign: Sign) InnerError!Zir.Inst.Ref {
   6822     const astgen = gz.astgen;
   6823     const tree = astgen.tree;
   6824     const main_tokens = tree.nodes.items(.main_token);
   6825 
   6826     const main_token = main_tokens[node];
   6827     const bytes = tree.tokenSlice(main_token);
   6828     const unsigned_float_number = std.fmt.parseFloat(f128, bytes) catch |err| switch (err) {
   6829         error.InvalidCharacter => unreachable, // validated by tokenizer
   6830     };
   6831     const float_number = switch (sign) {
   6832         .negative => -unsigned_float_number,
   6833         .positive => unsigned_float_number,
   6834     };
   6835     // If the value fits into a f64 without losing any precision, store it that way.
   6836     @setFloatMode(.Strict);
   6837     const smaller_float = @floatCast(f64, float_number);
   6838     const bigger_again: f128 = smaller_float;
   6839     if (bigger_again == float_number) {
   6840         const result = try gz.addFloat(smaller_float);
   6841         return rvalue(gz, rl, result, node);
   6842     }
   6843     // We need to use 128 bits. Break the float into 4 u32 values so we can
   6844     // put it into the `extra` array.
   6845     const int_bits = @bitCast(u128, float_number);
   6846     const result = try gz.addPlNode(.float128, node, Zir.Inst.Float128{
   6847         .piece0 = @truncate(u32, int_bits),
   6848         .piece1 = @truncate(u32, int_bits >> 32),
   6849         .piece2 = @truncate(u32, int_bits >> 64),
   6850         .piece3 = @truncate(u32, int_bits >> 96),
   6851     });
   6852     return rvalue(gz, rl, result, node);
   6853 }
   6854 
   6855 fn asmExpr(
   6856     gz: *GenZir,
   6857     scope: *Scope,
   6858     rl: ResultLoc,
   6859     node: Ast.Node.Index,
   6860     full: Ast.full.Asm,
   6861 ) InnerError!Zir.Inst.Ref {
   6862     const astgen = gz.astgen;
   6863     const tree = astgen.tree;
   6864     const main_tokens = tree.nodes.items(.main_token);
   6865     const node_datas = tree.nodes.items(.data);
   6866     const node_tags = tree.nodes.items(.tag);
   6867     const token_tags = tree.tokens.items(.tag);
   6868 
   6869     const asm_source = switch (node_tags[full.ast.template]) {
   6870         .string_literal => try astgen.strLitAsString(main_tokens[full.ast.template]),
   6871         .multiline_string_literal => try astgen.strLitNodeAsString(full.ast.template),
   6872         else => blk: {
   6873             // stage1 allows this, and until we do another design iteration on inline assembly
   6874             // in stage2 to improve support for the various needed use cases, we allow inline
   6875             // assembly templates to be an expression. Once stage2 addresses the real world needs
   6876             // of people using inline assembly (primarily OS developers) then we can re-institute
   6877             // the rule into AstGen that assembly code must use string literal syntax.
   6878             //return astgen.failNode(full.ast.template, "assembly code must use string literal syntax", .{}),
   6879             // We still need to trigger all the expr() calls here to avoid errors for unused things.
   6880             // So we pass 0 as the asm source and stage2 Sema will notice this and
   6881             // report the error.
   6882             _ = try comptimeExpr(gz, scope, .none, full.ast.template);
   6883             break :blk IndexSlice{ .index = 0, .len = 0 };
   6884         },
   6885     };
   6886 
   6887     // See https://github.com/ziglang/zig/issues/215 and related issues discussing
   6888     // possible inline assembly improvements. Until then here is status quo AstGen
   6889     // for assembly syntax. It's used by std lib crypto aesni.zig.
   6890     const is_container_asm = astgen.fn_block == null;
   6891     if (is_container_asm) {
   6892         if (full.volatile_token) |t|
   6893             return astgen.failTok(t, "volatile is meaningless on global assembly", .{});
   6894         if (full.outputs.len != 0 or full.inputs.len != 0 or full.first_clobber != null)
   6895             return astgen.failNode(node, "global assembly cannot have inputs, outputs, or clobbers", .{});
   6896     } else {
   6897         if (full.outputs.len == 0 and full.volatile_token == null) {
   6898             return astgen.failNode(node, "assembly expression with no output must be marked volatile", .{});
   6899         }
   6900     }
   6901     if (full.outputs.len > 32) {
   6902         return astgen.failNode(full.outputs[32], "too many asm outputs", .{});
   6903     }
   6904     var outputs_buffer: [32]Zir.Inst.Asm.Output = undefined;
   6905     const outputs = outputs_buffer[0..full.outputs.len];
   6906 
   6907     var output_type_bits: u32 = 0;
   6908 
   6909     for (full.outputs) |output_node, i| {
   6910         const symbolic_name = main_tokens[output_node];
   6911         const name = try astgen.identAsString(symbolic_name);
   6912         const constraint_token = symbolic_name + 2;
   6913         const constraint = (try astgen.strLitAsString(constraint_token)).index;
   6914         const has_arrow = token_tags[symbolic_name + 4] == .arrow;
   6915         if (has_arrow) {
   6916             if (output_type_bits != 0) {
   6917                 return astgen.failNode(output_node, "inline assembly allows up to one output value", .{});
   6918             }
   6919             output_type_bits |= @as(u32, 1) << @intCast(u5, i);
   6920             const out_type_node = node_datas[output_node].lhs;
   6921             const out_type_inst = try typeExpr(gz, scope, out_type_node);
   6922             outputs[i] = .{
   6923                 .name = name,
   6924                 .constraint = constraint,
   6925                 .operand = out_type_inst,
   6926             };
   6927         } else {
   6928             const ident_token = symbolic_name + 4;
   6929             // TODO have a look at #215 and related issues and decide how to
   6930             // handle outputs. Do we want this to be identifiers?
   6931             // Or maybe we want to force this to be expressions with a pointer type.
   6932             outputs[i] = .{
   6933                 .name = name,
   6934                 .constraint = constraint,
   6935                 .operand = try localVarRef(gz, scope, .ref, node, ident_token),
   6936             };
   6937         }
   6938     }
   6939 
   6940     if (full.inputs.len > 32) {
   6941         return astgen.failNode(full.inputs[32], "too many asm inputs", .{});
   6942     }
   6943     var inputs_buffer: [32]Zir.Inst.Asm.Input = undefined;
   6944     const inputs = inputs_buffer[0..full.inputs.len];
   6945 
   6946     for (full.inputs) |input_node, i| {
   6947         const symbolic_name = main_tokens[input_node];
   6948         const name = try astgen.identAsString(symbolic_name);
   6949         const constraint_token = symbolic_name + 2;
   6950         const constraint = (try astgen.strLitAsString(constraint_token)).index;
   6951         const operand = try expr(gz, scope, .none, node_datas[input_node].lhs);
   6952         inputs[i] = .{
   6953             .name = name,
   6954             .constraint = constraint,
   6955             .operand = operand,
   6956         };
   6957     }
   6958 
   6959     var clobbers_buffer: [32]u32 = undefined;
   6960     var clobber_i: usize = 0;
   6961     if (full.first_clobber) |first_clobber| clobbers: {
   6962         // asm ("foo" ::: "a", "b")
   6963         // asm ("foo" ::: "a", "b",)
   6964         var tok_i = first_clobber;
   6965         while (true) : (tok_i += 1) {
   6966             if (clobber_i >= clobbers_buffer.len) {
   6967                 return astgen.failTok(tok_i, "too many asm clobbers", .{});
   6968             }
   6969             clobbers_buffer[clobber_i] = (try astgen.strLitAsString(tok_i)).index;
   6970             clobber_i += 1;
   6971             tok_i += 1;
   6972             switch (token_tags[tok_i]) {
   6973                 .r_paren => break :clobbers,
   6974                 .comma => {
   6975                     if (token_tags[tok_i + 1] == .r_paren) {
   6976                         break :clobbers;
   6977                     } else {
   6978                         continue;
   6979                     }
   6980                 },
   6981                 else => unreachable,
   6982             }
   6983         }
   6984     }
   6985 
   6986     const result = try gz.addAsm(.{
   6987         .node = node,
   6988         .asm_source = asm_source.index,
   6989         .is_volatile = full.volatile_token != null,
   6990         .output_type_bits = output_type_bits,
   6991         .outputs = outputs,
   6992         .inputs = inputs,
   6993         .clobbers = clobbers_buffer[0..clobber_i],
   6994     });
   6995     return rvalue(gz, rl, result, node);
   6996 }
   6997 
   6998 fn as(
   6999     gz: *GenZir,
   7000     scope: *Scope,
   7001     rl: ResultLoc,
   7002     node: Ast.Node.Index,
   7003     lhs: Ast.Node.Index,
   7004     rhs: Ast.Node.Index,
   7005 ) InnerError!Zir.Inst.Ref {
   7006     const dest_type = try typeExpr(gz, scope, lhs);
   7007     switch (rl) {
   7008         .none, .discard, .ref, .ty, .coerced_ty => {
   7009             const result = try reachableExpr(gz, scope, .{ .ty = dest_type }, rhs, node);
   7010             return rvalue(gz, rl, result, node);
   7011         },
   7012         .ptr, .inferred_ptr => |result_ptr| {
   7013             return asRlPtr(gz, scope, rl, node, result_ptr, rhs, dest_type);
   7014         },
   7015         .block_ptr => |block_scope| {
   7016             return asRlPtr(gz, scope, rl, node, block_scope.rl_ptr, rhs, dest_type);
   7017         },
   7018     }
   7019 }
   7020 
   7021 fn unionInit(
   7022     gz: *GenZir,
   7023     scope: *Scope,
   7024     rl: ResultLoc,
   7025     node: Ast.Node.Index,
   7026     params: []const Ast.Node.Index,
   7027 ) InnerError!Zir.Inst.Ref {
   7028     const union_type = try typeExpr(gz, scope, params[0]);
   7029     const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);
   7030     const field_type = try gz.addPlNode(.field_type_ref, params[1], Zir.Inst.FieldTypeRef{
   7031         .container_type = union_type,
   7032         .field_name = field_name,
   7033     });
   7034     const init = try reachableExpr(gz, scope, .{ .ty = field_type }, params[2], node);
   7035     const result = try gz.addPlNode(.union_init, node, Zir.Inst.UnionInit{
   7036         .union_type = union_type,
   7037         .init = init,
   7038         .field_name = field_name,
   7039     });
   7040     return rvalue(gz, rl, result, node);
   7041 }
   7042 
   7043 fn asRlPtr(
   7044     parent_gz: *GenZir,
   7045     scope: *Scope,
   7046     rl: ResultLoc,
   7047     src_node: Ast.Node.Index,
   7048     result_ptr: Zir.Inst.Ref,
   7049     operand_node: Ast.Node.Index,
   7050     dest_type: Zir.Inst.Ref,
   7051 ) InnerError!Zir.Inst.Ref {
   7052     var as_scope = try parent_gz.makeCoercionScope(scope, dest_type, result_ptr, src_node);
   7053     defer as_scope.unstack();
   7054 
   7055     const result = try reachableExpr(&as_scope, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node, src_node);
   7056     return as_scope.finishCoercion(parent_gz, rl, operand_node, result, dest_type);
   7057 }
   7058 
   7059 fn bitCast(
   7060     gz: *GenZir,
   7061     scope: *Scope,
   7062     rl: ResultLoc,
   7063     node: Ast.Node.Index,
   7064     lhs: Ast.Node.Index,
   7065     rhs: Ast.Node.Index,
   7066 ) InnerError!Zir.Inst.Ref {
   7067     const dest_type = try reachableTypeExpr(gz, scope, lhs, node);
   7068     const operand = try reachableExpr(gz, scope, .none, rhs, node);
   7069     const result = try gz.addPlNode(.bitcast, node, Zir.Inst.Bin{
   7070         .lhs = dest_type,
   7071         .rhs = operand,
   7072     });
   7073     return rvalue(gz, rl, result, node);
   7074 }
   7075 
   7076 fn typeOf(
   7077     gz: *GenZir,
   7078     scope: *Scope,
   7079     rl: ResultLoc,
   7080     node: Ast.Node.Index,
   7081     args: []const Ast.Node.Index,
   7082 ) InnerError!Zir.Inst.Ref {
   7083     const astgen = gz.astgen;
   7084     if (args.len < 1) {
   7085         return astgen.failNode(node, "expected at least 1 argument, found 0", .{});
   7086     }
   7087     const gpa = astgen.gpa;
   7088     if (args.len == 1) {
   7089         const typeof_inst = try gz.makeBlockInst(.typeof_builtin, node);
   7090 
   7091         var typeof_scope = gz.makeSubBlock(scope);
   7092         typeof_scope.force_comptime = false;
   7093         defer typeof_scope.unstack();
   7094 
   7095         const ty_expr = try reachableExpr(&typeof_scope, &typeof_scope.base, .none, args[0], node);
   7096         if (!gz.refIsNoReturn(ty_expr)) {
   7097             _ = try typeof_scope.addBreak(.break_inline, typeof_inst, ty_expr);
   7098         }
   7099         try typeof_scope.setBlockBody(typeof_inst);
   7100 
   7101         // typeof_scope unstacked now, can add new instructions to gz
   7102         try gz.instructions.append(gpa, typeof_inst);
   7103         return rvalue(gz, rl, indexToRef(typeof_inst), node);
   7104     }
   7105     const payload_size: u32 = std.meta.fields(Zir.Inst.TypeOfPeer).len;
   7106     const payload_index = try reserveExtra(astgen, payload_size + args.len);
   7107     var args_index = payload_index + payload_size;
   7108 
   7109     const typeof_inst = try gz.addExtendedMultiOpPayloadIndex(.typeof_peer, payload_index, args.len);
   7110 
   7111     var typeof_scope = gz.makeSubBlock(scope);
   7112     typeof_scope.force_comptime = false;
   7113 
   7114     for (args) |arg, i| {
   7115         const param_ref = try reachableExpr(&typeof_scope, &typeof_scope.base, .none, arg, node);
   7116         astgen.extra.items[args_index + i] = @enumToInt(param_ref);
   7117     }
   7118     _ = try typeof_scope.addBreak(.break_inline, refToIndex(typeof_inst).?, .void_value);
   7119 
   7120     const body = typeof_scope.instructionsSlice();
   7121     const body_len = astgen.countBodyLenAfterFixups(body);
   7122     astgen.setExtra(payload_index, Zir.Inst.TypeOfPeer{
   7123         .body_len = @intCast(u32, body_len),
   7124         .body_index = @intCast(u32, astgen.extra.items.len),
   7125         .src_node = gz.nodeIndexToRelative(node),
   7126     });
   7127     try astgen.extra.ensureUnusedCapacity(gpa, body_len);
   7128     astgen.appendBodyWithFixups(body);
   7129     typeof_scope.unstack();
   7130 
   7131     return rvalue(gz, rl, typeof_inst, node);
   7132 }
   7133 
   7134 fn builtinCall(
   7135     gz: *GenZir,
   7136     scope: *Scope,
   7137     rl: ResultLoc,
   7138     node: Ast.Node.Index,
   7139     params: []const Ast.Node.Index,
   7140 ) InnerError!Zir.Inst.Ref {
   7141     const astgen = gz.astgen;
   7142     const tree = astgen.tree;
   7143     const main_tokens = tree.nodes.items(.main_token);
   7144 
   7145     const builtin_token = main_tokens[node];
   7146     const builtin_name = tree.tokenSlice(builtin_token);
   7147 
   7148     // We handle the different builtins manually because they have different semantics depending
   7149     // on the function. For example, `@as` and others participate in result location semantics,
   7150     // and `@cImport` creates a special scope that collects a .c source code text buffer.
   7151     // Also, some builtins have a variable number of parameters.
   7152 
   7153     const info = BuiltinFn.list.get(builtin_name) orelse {
   7154         return astgen.failNode(node, "invalid builtin function: '{s}'", .{
   7155             builtin_name,
   7156         });
   7157     };
   7158     if (info.param_count) |expected| {
   7159         if (expected != params.len) {
   7160             const s = if (expected == 1) "" else "s";
   7161             return astgen.failNode(node, "expected {d} argument{s}, found {d}", .{
   7162                 expected, s, params.len,
   7163             });
   7164         }
   7165     }
   7166 
   7167     switch (info.tag) {
   7168         .import => {
   7169             const node_tags = tree.nodes.items(.tag);
   7170             const operand_node = params[0];
   7171 
   7172             if (node_tags[operand_node] != .string_literal) {
   7173                 // Spec reference: https://github.com/ziglang/zig/issues/2206
   7174                 return astgen.failNode(operand_node, "@import operand must be a string literal", .{});
   7175             }
   7176             const str_lit_token = main_tokens[operand_node];
   7177             const str = try astgen.strLitAsString(str_lit_token);
   7178             const result = try gz.addStrTok(.import, str.index, str_lit_token);
   7179             const gop = try astgen.imports.getOrPut(astgen.gpa, str.index);
   7180             if (!gop.found_existing) {
   7181                 gop.value_ptr.* = str_lit_token;
   7182             }
   7183             return rvalue(gz, rl, result, node);
   7184         },
   7185         .compile_log => {
   7186             const payload_index = try addExtra(gz.astgen, Zir.Inst.NodeMultiOp{
   7187                 .src_node = gz.nodeIndexToRelative(node),
   7188             });
   7189             var extra_index = try reserveExtra(gz.astgen, params.len);
   7190             for (params) |param| {
   7191                 const param_ref = try expr(gz, scope, .none, param);
   7192                 astgen.extra.items[extra_index] = @enumToInt(param_ref);
   7193                 extra_index += 1;
   7194             }
   7195             const result = try gz.addExtendedMultiOpPayloadIndex(.compile_log, payload_index, params.len);
   7196             return rvalue(gz, rl, result, node);
   7197         },
   7198         .field => {
   7199             if (rl == .ref) {
   7200                 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{
   7201                     .lhs = try expr(gz, scope, .ref, params[0]),
   7202                     .field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]),
   7203                 });
   7204             }
   7205             const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{
   7206                 .lhs = try expr(gz, scope, .none, params[0]),
   7207                 .field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]),
   7208             });
   7209             return rvalue(gz, rl, result, node);
   7210         },
   7211 
   7212         // zig fmt: off
   7213         .as         => return as(       gz, scope, rl, node, params[0], params[1]),
   7214         .bit_cast   => return bitCast(  gz, scope, rl, node, params[0], params[1]),
   7215         .TypeOf     => return typeOf(   gz, scope, rl, node, params),
   7216         .union_init => return unionInit(gz, scope, rl, node, params),
   7217         .c_import   => return cImport(  gz, scope,     node, params[0]),
   7218         // zig fmt: on
   7219 
   7220         .@"export" => {
   7221             const node_tags = tree.nodes.items(.tag);
   7222             const node_datas = tree.nodes.items(.data);
   7223             // This function causes a Decl to be exported. The first parameter is not an expression,
   7224             // but an identifier of the Decl to be exported.
   7225             var namespace: Zir.Inst.Ref = .none;
   7226             var decl_name: u32 = 0;
   7227             switch (node_tags[params[0]]) {
   7228                 .identifier => {
   7229                     const ident_token = main_tokens[params[0]];
   7230                     decl_name = try astgen.identAsString(ident_token);
   7231 
   7232                     var s = scope;
   7233                     var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
   7234                     while (true) switch (s.tag) {
   7235                         .local_val => {
   7236                             const local_val = s.cast(Scope.LocalVal).?;
   7237                             if (local_val.name == decl_name) {
   7238                                 local_val.used = true;
   7239                                 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
   7240                                     .operand = local_val.inst,
   7241                                     .options = try comptimeExpr(gz, scope, .{ .coerced_ty = .export_options_type }, params[1]),
   7242                                 });
   7243                                 return rvalue(gz, rl, .void_value, node);
   7244                             }
   7245                             s = local_val.parent;
   7246                         },
   7247                         .local_ptr => {
   7248                             const local_ptr = s.cast(Scope.LocalPtr).?;
   7249                             if (local_ptr.name == decl_name) {
   7250                                 if (!local_ptr.maybe_comptime)
   7251                                     return astgen.failNode(params[0], "unable to export runtime-known value", .{});
   7252                                 local_ptr.used = true;
   7253                                 const loaded = try gz.addUnNode(.load, local_ptr.ptr, node);
   7254                                 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
   7255                                     .operand = loaded,
   7256                                     .options = try comptimeExpr(gz, scope, .{ .coerced_ty = .export_options_type }, params[1]),
   7257                                 });
   7258                                 return rvalue(gz, rl, .void_value, node);
   7259                             }
   7260                             s = local_ptr.parent;
   7261                         },
   7262                         .gen_zir => s = s.cast(GenZir).?.parent,
   7263                         .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
   7264                         .namespace => {
   7265                             const ns = s.cast(Scope.Namespace).?;
   7266                             if (ns.decls.get(decl_name)) |i| {
   7267                                 if (found_already) |f| {
   7268                                     return astgen.failNodeNotes(node, "ambiguous reference", .{}, &.{
   7269                                         try astgen.errNoteNode(f, "declared here", .{}),
   7270                                         try astgen.errNoteNode(i, "also declared here", .{}),
   7271                                     });
   7272                                 }
   7273                                 // We found a match but must continue looking for ambiguous references to decls.
   7274                                 found_already = i;
   7275                             }
   7276                             s = ns.parent;
   7277                         },
   7278                         .top => break,
   7279                     };
   7280                 },
   7281                 .field_access => {
   7282                     const namespace_node = node_datas[params[0]].lhs;
   7283                     namespace = try typeExpr(gz, scope, namespace_node);
   7284                     const dot_token = main_tokens[params[0]];
   7285                     const field_ident = dot_token + 1;
   7286                     decl_name = try astgen.identAsString(field_ident);
   7287                 },
   7288                 else => return astgen.failNode(params[0], "symbol to export must identify a declaration", .{}),
   7289             }
   7290             const options = try comptimeExpr(gz, scope, .{ .ty = .export_options_type }, params[1]);
   7291             _ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{
   7292                 .namespace = namespace,
   7293                 .decl_name = decl_name,
   7294                 .options = options,
   7295             });
   7296             return rvalue(gz, rl, .void_value, node);
   7297         },
   7298         .@"extern" => {
   7299             const type_inst = try typeExpr(gz, scope, params[0]);
   7300             const options = try comptimeExpr(gz, scope, .{ .ty = .extern_options_type }, params[1]);
   7301             const result = try gz.addExtendedPayload(.builtin_extern, Zir.Inst.BinNode{
   7302                 .node = gz.nodeIndexToRelative(node),
   7303                 .lhs = type_inst,
   7304                 .rhs = options,
   7305             });
   7306             return rvalue(gz, rl, result, node);
   7307         },
   7308         .fence => {
   7309             const order = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[0]);
   7310             const result = try gz.addExtendedPayload(.fence, Zir.Inst.UnNode{
   7311                 .node = gz.nodeIndexToRelative(node),
   7312                 .operand = order,
   7313             });
   7314             return rvalue(gz, rl, result, node);
   7315         },
   7316         .set_float_mode => {
   7317             const order = try expr(gz, scope, .{ .coerced_ty = .float_mode_type }, params[0]);
   7318             const result = try gz.addExtendedPayload(.set_float_mode, Zir.Inst.UnNode{
   7319                 .node = gz.nodeIndexToRelative(node),
   7320                 .operand = order,
   7321             });
   7322             return rvalue(gz, rl, result, node);
   7323         },
   7324         .set_align_stack => {
   7325             const order = try expr(gz, scope, align_rl, params[0]);
   7326             const result = try gz.addExtendedPayload(.set_align_stack, Zir.Inst.UnNode{
   7327                 .node = gz.nodeIndexToRelative(node),
   7328                 .operand = order,
   7329             });
   7330             return rvalue(gz, rl, result, node);
   7331         },
   7332 
   7333         .src => {
   7334             const token_starts = tree.tokens.items(.start);
   7335             const node_start = token_starts[tree.firstToken(node)];
   7336             astgen.advanceSourceCursor(node_start);
   7337             const result = try gz.addExtendedPayload(.builtin_src, Zir.Inst.LineColumn{
   7338                 .line = astgen.source_line,
   7339                 .column = astgen.source_column,
   7340             });
   7341             return rvalue(gz, rl, result, node);
   7342         },
   7343 
   7344         // zig fmt: off
   7345         .This               => return rvalue(gz, rl, try gz.addNodeExtended(.this,               node), node),
   7346         .return_address     => return rvalue(gz, rl, try gz.addNodeExtended(.ret_addr,           node), node),
   7347         .error_return_trace => return rvalue(gz, rl, try gz.addNodeExtended(.error_return_trace, node), node),
   7348         .frame              => return rvalue(gz, rl, try gz.addNodeExtended(.frame,              node), node),
   7349         .frame_address      => return rvalue(gz, rl, try gz.addNodeExtended(.frame_address,      node), node),
   7350         .breakpoint         => return rvalue(gz, rl, try gz.addNodeExtended(.breakpoint, node), node),
   7351 
   7352         .type_info   => return simpleUnOpType(gz, scope, rl, node, params[0], .type_info),
   7353         .size_of     => return simpleUnOpType(gz, scope, rl, node, params[0], .size_of),
   7354         .bit_size_of => return simpleUnOpType(gz, scope, rl, node, params[0], .bit_size_of),
   7355         .align_of    => return simpleUnOpType(gz, scope, rl, node, params[0], .align_of),
   7356 
   7357         .ptr_to_int            => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .ptr_to_int),
   7358         .error_to_int          => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .error_to_int),
   7359         .int_to_error          => return simpleUnOp(gz, scope, rl, node, .{ .coerced_ty = .u16_type },        params[0], .int_to_error),
   7360         .compile_error         => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type },     params[0], .compile_error),
   7361         .set_eval_branch_quota => return simpleUnOp(gz, scope, rl, node, .{ .coerced_ty = .u32_type },        params[0], .set_eval_branch_quota),
   7362         .enum_to_int           => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .enum_to_int),
   7363         .bool_to_int           => return simpleUnOp(gz, scope, rl, node, bool_rl,                             params[0], .bool_to_int),
   7364         .embed_file            => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type },     params[0], .embed_file),
   7365         .error_name            => return simpleUnOp(gz, scope, rl, node, .{ .ty = .anyerror_type },           params[0], .error_name),
   7366         .panic                 => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type },     params[0], .panic),
   7367         .set_cold              => return simpleUnOp(gz, scope, rl, node, bool_rl,                             params[0], .set_cold),
   7368         .set_runtime_safety    => return simpleUnOp(gz, scope, rl, node, bool_rl,                             params[0], .set_runtime_safety),
   7369         .sqrt                  => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .sqrt),
   7370         .sin                   => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .sin),
   7371         .cos                   => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .cos),
   7372         .tan                   => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .tan),
   7373         .exp                   => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .exp),
   7374         .exp2                  => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .exp2),
   7375         .log                   => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .log),
   7376         .log2                  => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .log2),
   7377         .log10                 => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .log10),
   7378         .fabs                  => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .fabs),
   7379         .floor                 => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .floor),
   7380         .ceil                  => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .ceil),
   7381         .trunc                 => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .trunc),
   7382         .round                 => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .round),
   7383         .tag_name              => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .tag_name),
   7384         .Type                  => return simpleUnOp(gz, scope, rl, node, .{ .coerced_ty = .type_info_type },  params[0], .reify),
   7385         .type_name             => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .type_name),
   7386         .Frame                 => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .frame_type),
   7387         .frame_size            => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .frame_size),
   7388 
   7389         .float_to_int => return typeCast(gz, scope, rl, node, params[0], params[1], .float_to_int),
   7390         .int_to_float => return typeCast(gz, scope, rl, node, params[0], params[1], .int_to_float),
   7391         .int_to_ptr   => return typeCast(gz, scope, rl, node, params[0], params[1], .int_to_ptr),
   7392         .int_to_enum  => return typeCast(gz, scope, rl, node, params[0], params[1], .int_to_enum),
   7393         .float_cast   => return typeCast(gz, scope, rl, node, params[0], params[1], .float_cast),
   7394         .int_cast     => return typeCast(gz, scope, rl, node, params[0], params[1], .int_cast),
   7395         .ptr_cast     => return typeCast(gz, scope, rl, node, params[0], params[1], .ptr_cast),
   7396         .truncate     => return typeCast(gz, scope, rl, node, params[0], params[1], .truncate),
   7397         // zig fmt: on
   7398 
   7399         .align_cast => {
   7400             const dest_align = try comptimeExpr(gz, scope, align_rl, params[0]);
   7401             const rhs = try expr(gz, scope, .none, params[1]);
   7402             const result = try gz.addPlNode(.align_cast, node, Zir.Inst.Bin{
   7403                 .lhs = dest_align,
   7404                 .rhs = rhs,
   7405             });
   7406             return rvalue(gz, rl, result, node);
   7407         },
   7408         .err_set_cast => {
   7409             const result = try gz.addExtendedPayload(.err_set_cast, Zir.Inst.BinNode{
   7410                 .lhs = try typeExpr(gz, scope, params[0]),
   7411                 .rhs = try expr(gz, scope, .none, params[1]),
   7412                 .node = gz.nodeIndexToRelative(node),
   7413             });
   7414             return rvalue(gz, rl, result, node);
   7415         },
   7416 
   7417         // zig fmt: off
   7418         .has_decl  => return hasDeclOrField(gz, scope, rl, node, params[0], params[1], .has_decl),
   7419         .has_field => return hasDeclOrField(gz, scope, rl, node, params[0], params[1], .has_field),
   7420 
   7421         .clz         => return bitBuiltin(gz, scope, rl, node, params[0], params[1], .clz),
   7422         .ctz         => return bitBuiltin(gz, scope, rl, node, params[0], params[1], .ctz),
   7423         .pop_count   => return bitBuiltin(gz, scope, rl, node, params[0], params[1], .pop_count),
   7424         .byte_swap   => return bitBuiltin(gz, scope, rl, node, params[0], params[1], .byte_swap),
   7425         .bit_reverse => return bitBuiltin(gz, scope, rl, node, params[0], params[1], .bit_reverse),
   7426 
   7427         .div_exact => return divBuiltin(gz, scope, rl, node, params[0], params[1], .div_exact),
   7428         .div_floor => return divBuiltin(gz, scope, rl, node, params[0], params[1], .div_floor),
   7429         .div_trunc => return divBuiltin(gz, scope, rl, node, params[0], params[1], .div_trunc),
   7430         .mod       => return divBuiltin(gz, scope, rl, node, params[0], params[1], .mod),
   7431         .rem       => return divBuiltin(gz, scope, rl, node, params[0], params[1], .rem),
   7432 
   7433         .shl_exact => return shiftOp(gz, scope, rl, node, params[0], params[1], .shl_exact),
   7434         .shr_exact => return shiftOp(gz, scope, rl, node, params[0], params[1], .shr_exact),
   7435 
   7436         .bit_offset_of  => return offsetOf(gz, scope, rl, node, params[0], params[1], .bit_offset_of),
   7437         .offset_of => return offsetOf(gz, scope, rl, node, params[0], params[1], .offset_of),
   7438 
   7439         .c_undef   => return simpleCBuiltin(gz, scope, rl, node, params[0], .c_undef),
   7440         .c_include => return simpleCBuiltin(gz, scope, rl, node, params[0], .c_include),
   7441 
   7442         .cmpxchg_strong => return cmpxchg(gz, scope, rl, node, params, .cmpxchg_strong),
   7443         .cmpxchg_weak   => return cmpxchg(gz, scope, rl, node, params, .cmpxchg_weak),
   7444         // zig fmt: on
   7445 
   7446         .wasm_memory_size => {
   7447             const operand = try comptimeExpr(gz, scope, .{ .coerced_ty = .u32_type }, params[0]);
   7448             const result = try gz.addExtendedPayload(.wasm_memory_size, Zir.Inst.UnNode{
   7449                 .node = gz.nodeIndexToRelative(node),
   7450                 .operand = operand,
   7451             });
   7452             return rvalue(gz, rl, result, node);
   7453         },
   7454         .wasm_memory_grow => {
   7455             const index_arg = try comptimeExpr(gz, scope, .{ .coerced_ty = .u32_type }, params[0]);
   7456             const delta_arg = try expr(gz, scope, .{ .coerced_ty = .u32_type }, params[1]);
   7457             const result = try gz.addExtendedPayload(.wasm_memory_grow, Zir.Inst.BinNode{
   7458                 .node = gz.nodeIndexToRelative(node),
   7459                 .lhs = index_arg,
   7460                 .rhs = delta_arg,
   7461             });
   7462             return rvalue(gz, rl, result, node);
   7463         },
   7464         .c_define => {
   7465             if (!gz.c_import) return gz.astgen.failNode(node, "C define valid only inside C import block", .{});
   7466             const name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[0]);
   7467             const value = try comptimeExpr(gz, scope, .none, params[1]);
   7468             const result = try gz.addExtendedPayload(.c_define, Zir.Inst.BinNode{
   7469                 .node = gz.nodeIndexToRelative(node),
   7470                 .lhs = name,
   7471                 .rhs = value,
   7472             });
   7473             return rvalue(gz, rl, result, node);
   7474         },
   7475 
   7476         .splat => {
   7477             const len = try expr(gz, scope, .{ .coerced_ty = .u32_type }, params[0]);
   7478             const scalar = try expr(gz, scope, .none, params[1]);
   7479             const result = try gz.addPlNode(.splat, node, Zir.Inst.Bin{
   7480                 .lhs = len,
   7481                 .rhs = scalar,
   7482             });
   7483             return rvalue(gz, rl, result, node);
   7484         },
   7485         .reduce => {
   7486             const op = try expr(gz, scope, .{ .ty = .reduce_op_type }, params[0]);
   7487             const scalar = try expr(gz, scope, .none, params[1]);
   7488             const result = try gz.addPlNode(.reduce, node, Zir.Inst.Bin{
   7489                 .lhs = op,
   7490                 .rhs = scalar,
   7491             });
   7492             return rvalue(gz, rl, result, node);
   7493         },
   7494 
   7495         .maximum => {
   7496             const a = try expr(gz, scope, .none, params[0]);
   7497             const b = try expr(gz, scope, .none, params[1]);
   7498             const result = try gz.addPlNode(.maximum, node, Zir.Inst.Bin{
   7499                 .lhs = a,
   7500                 .rhs = b,
   7501             });
   7502             return rvalue(gz, rl, result, node);
   7503         },
   7504         .minimum => {
   7505             const a = try expr(gz, scope, .none, params[0]);
   7506             const b = try expr(gz, scope, .none, params[1]);
   7507             const result = try gz.addPlNode(.minimum, node, Zir.Inst.Bin{
   7508                 .lhs = a,
   7509                 .rhs = b,
   7510             });
   7511             return rvalue(gz, rl, result, node);
   7512         },
   7513 
   7514         .add_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .add_with_overflow),
   7515         .sub_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .sub_with_overflow),
   7516         .mul_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .mul_with_overflow),
   7517         .shl_with_overflow => {
   7518             const int_type = try typeExpr(gz, scope, params[0]);
   7519             const log2_int_type = try gz.addUnNode(.log2_int_type, int_type, params[0]);
   7520             const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
   7521                 .ptr_type_simple = .{
   7522                     .is_allowzero = false,
   7523                     .is_mutable = true,
   7524                     .is_volatile = false,
   7525                     .size = .One,
   7526                     .elem_type = int_type,
   7527                 },
   7528             } });
   7529             const lhs = try expr(gz, scope, .{ .ty = int_type }, params[1]);
   7530             const rhs = try expr(gz, scope, .{ .ty = log2_int_type }, params[2]);
   7531             const ptr = try expr(gz, scope, .{ .ty = ptr_type }, params[3]);
   7532             const result = try gz.addExtendedPayload(.shl_with_overflow, Zir.Inst.OverflowArithmetic{
   7533                 .node = gz.nodeIndexToRelative(node),
   7534                 .lhs = lhs,
   7535                 .rhs = rhs,
   7536                 .ptr = ptr,
   7537             });
   7538             return rvalue(gz, rl, result, node);
   7539         },
   7540 
   7541         .atomic_load => {
   7542             const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.AtomicLoad{
   7543                 // zig fmt: off
   7544                 .elem_type = try typeExpr(gz, scope,                                        params[0]),
   7545                 .ptr       = try expr    (gz, scope, .none,                                 params[1]),
   7546                 .ordering  = try expr    (gz, scope, .{ .coerced_ty = .atomic_order_type }, params[2]),
   7547                 // zig fmt: on
   7548             });
   7549             return rvalue(gz, rl, result, node);
   7550         },
   7551         .atomic_rmw => {
   7552             const int_type = try typeExpr(gz, scope, params[0]);
   7553             const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{
   7554                 // zig fmt: off
   7555                 .ptr       = try expr(gz, scope, .none,                                  params[1]),
   7556                 .operation = try expr(gz, scope, .{ .coerced_ty = .atomic_rmw_op_type }, params[2]),
   7557                 .operand   = try expr(gz, scope, .{ .ty = int_type },                    params[3]),
   7558                 .ordering  = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type },  params[4]),
   7559                 // zig fmt: on
   7560             });
   7561             return rvalue(gz, rl, result, node);
   7562         },
   7563         .atomic_store => {
   7564             const int_type = try typeExpr(gz, scope, params[0]);
   7565             const result = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{
   7566                 // zig fmt: off
   7567                 .ptr      = try expr(gz, scope, .none,                                 params[1]),
   7568                 .operand  = try expr(gz, scope, .{ .ty = int_type },                   params[2]),
   7569                 .ordering = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[3]),
   7570                 // zig fmt: on
   7571             });
   7572             return rvalue(gz, rl, result, node);
   7573         },
   7574         .mul_add => {
   7575             const float_type = try typeExpr(gz, scope, params[0]);
   7576             const mulend1 = try expr(gz, scope, .{ .coerced_ty = float_type }, params[1]);
   7577             const mulend2 = try expr(gz, scope, .{ .coerced_ty = float_type }, params[2]);
   7578             const addend = try expr(gz, scope, .{ .ty = float_type }, params[3]);
   7579             const result = try gz.addPlNode(.mul_add, node, Zir.Inst.MulAdd{
   7580                 .mulend1 = mulend1,
   7581                 .mulend2 = mulend2,
   7582                 .addend = addend,
   7583             });
   7584             return rvalue(gz, rl, result, node);
   7585         },
   7586         .call => {
   7587             const options = try comptimeExpr(gz, scope, .{ .ty = .call_options_type }, params[0]);
   7588             const callee = try calleeExpr(gz, scope, params[1]);
   7589             const args = try expr(gz, scope, .none, params[2]);
   7590             const result = try gz.addPlNode(.builtin_call, node, Zir.Inst.BuiltinCall{
   7591                 .options = options,
   7592                 .callee = callee,
   7593                 .args = args,
   7594                 .flags = .{
   7595                     .is_nosuspend = gz.nosuspend_node != 0,
   7596                     .is_comptime = gz.force_comptime,
   7597                     .ensure_result_used = false,
   7598                 },
   7599             });
   7600             return rvalue(gz, rl, result, node);
   7601         },
   7602         .field_parent_ptr => {
   7603             const parent_type = try typeExpr(gz, scope, params[0]);
   7604             const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);
   7605             const result = try gz.addPlNode(.field_parent_ptr, node, Zir.Inst.FieldParentPtr{
   7606                 .parent_type = parent_type,
   7607                 .field_name = field_name,
   7608                 .field_ptr = try expr(gz, scope, .none, params[2]),
   7609             });
   7610             return rvalue(gz, rl, result, node);
   7611         },
   7612         .memcpy => {
   7613             const result = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{
   7614                 .dest = try expr(gz, scope, .{ .coerced_ty = .manyptr_u8_type }, params[0]),
   7615                 .source = try expr(gz, scope, .{ .coerced_ty = .manyptr_const_u8_type }, params[1]),
   7616                 .byte_count = try expr(gz, scope, .{ .coerced_ty = .usize_type }, params[2]),
   7617             });
   7618             return rvalue(gz, rl, result, node);
   7619         },
   7620         .memset => {
   7621             const result = try gz.addPlNode(.memset, node, Zir.Inst.Memset{
   7622                 .dest = try expr(gz, scope, .{ .coerced_ty = .manyptr_u8_type }, params[0]),
   7623                 .byte = try expr(gz, scope, .{ .coerced_ty = .u8_type }, params[1]),
   7624                 .byte_count = try expr(gz, scope, .{ .coerced_ty = .usize_type }, params[2]),
   7625             });
   7626             return rvalue(gz, rl, result, node);
   7627         },
   7628         .shuffle => {
   7629             const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{
   7630                 .elem_type = try typeExpr(gz, scope, params[0]),
   7631                 .a = try expr(gz, scope, .none, params[1]),
   7632                 .b = try expr(gz, scope, .none, params[2]),
   7633                 .mask = try comptimeExpr(gz, scope, .none, params[3]),
   7634             });
   7635             return rvalue(gz, rl, result, node);
   7636         },
   7637         .select => {
   7638             const result = try gz.addPlNode(.select, node, Zir.Inst.Select{
   7639                 .elem_type = try typeExpr(gz, scope, params[0]),
   7640                 .pred = try expr(gz, scope, .none, params[1]),
   7641                 .a = try expr(gz, scope, .none, params[2]),
   7642                 .b = try expr(gz, scope, .none, params[3]),
   7643             });
   7644             return rvalue(gz, rl, result, node);
   7645         },
   7646         .async_call => {
   7647             const result = try gz.addPlNode(.builtin_async_call, node, Zir.Inst.AsyncCall{
   7648                 .frame_buffer = try expr(gz, scope, .none, params[0]),
   7649                 .result_ptr = try expr(gz, scope, .none, params[1]),
   7650                 .fn_ptr = try expr(gz, scope, .none, params[2]),
   7651                 .args = try expr(gz, scope, .none, params[3]),
   7652             });
   7653             return rvalue(gz, rl, result, node);
   7654         },
   7655         .Vector => {
   7656             const result = try gz.addPlNode(.vector_type, node, Zir.Inst.Bin{
   7657                 .lhs = try comptimeExpr(gz, scope, .{ .coerced_ty = .u32_type }, params[0]),
   7658                 .rhs = try typeExpr(gz, scope, params[1]),
   7659             });
   7660             return rvalue(gz, rl, result, node);
   7661         },
   7662         .prefetch => {
   7663             const ptr = try expr(gz, scope, .none, params[0]);
   7664             const options = try comptimeExpr(gz, scope, .{ .ty = .prefetch_options_type }, params[1]);
   7665             const result = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{
   7666                 .node = gz.nodeIndexToRelative(node),
   7667                 .lhs = ptr,
   7668                 .rhs = options,
   7669             });
   7670             return rvalue(gz, rl, result, node);
   7671         },
   7672     }
   7673 }
   7674 
   7675 fn simpleNoOpVoid(
   7676     gz: *GenZir,
   7677     rl: ResultLoc,
   7678     node: Ast.Node.Index,
   7679     tag: Zir.Inst.Tag,
   7680 ) InnerError!Zir.Inst.Ref {
   7681     _ = try gz.addNode(tag, node);
   7682     return rvalue(gz, rl, .void_value, node);
   7683 }
   7684 
   7685 fn hasDeclOrField(
   7686     gz: *GenZir,
   7687     scope: *Scope,
   7688     rl: ResultLoc,
   7689     node: Ast.Node.Index,
   7690     lhs_node: Ast.Node.Index,
   7691     rhs_node: Ast.Node.Index,
   7692     tag: Zir.Inst.Tag,
   7693 ) InnerError!Zir.Inst.Ref {
   7694     const container_type = try typeExpr(gz, scope, lhs_node);
   7695     const name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, rhs_node);
   7696     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   7697         .lhs = container_type,
   7698         .rhs = name,
   7699     });
   7700     return rvalue(gz, rl, result, node);
   7701 }
   7702 
   7703 fn typeCast(
   7704     gz: *GenZir,
   7705     scope: *Scope,
   7706     rl: ResultLoc,
   7707     node: Ast.Node.Index,
   7708     lhs_node: Ast.Node.Index,
   7709     rhs_node: Ast.Node.Index,
   7710     tag: Zir.Inst.Tag,
   7711 ) InnerError!Zir.Inst.Ref {
   7712     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   7713         .lhs = try typeExpr(gz, scope, lhs_node),
   7714         .rhs = try expr(gz, scope, .none, rhs_node),
   7715     });
   7716     return rvalue(gz, rl, result, node);
   7717 }
   7718 
   7719 fn simpleUnOpType(
   7720     gz: *GenZir,
   7721     scope: *Scope,
   7722     rl: ResultLoc,
   7723     node: Ast.Node.Index,
   7724     operand_node: Ast.Node.Index,
   7725     tag: Zir.Inst.Tag,
   7726 ) InnerError!Zir.Inst.Ref {
   7727     const operand = try typeExpr(gz, scope, operand_node);
   7728     const result = try gz.addUnNode(tag, operand, node);
   7729     return rvalue(gz, rl, result, node);
   7730 }
   7731 
   7732 fn simpleUnOp(
   7733     gz: *GenZir,
   7734     scope: *Scope,
   7735     rl: ResultLoc,
   7736     node: Ast.Node.Index,
   7737     operand_rl: ResultLoc,
   7738     operand_node: Ast.Node.Index,
   7739     tag: Zir.Inst.Tag,
   7740 ) InnerError!Zir.Inst.Ref {
   7741     const operand = try expr(gz, scope, operand_rl, operand_node);
   7742     const result = try gz.addUnNode(tag, operand, node);
   7743     return rvalue(gz, rl, result, node);
   7744 }
   7745 
   7746 fn negation(
   7747     gz: *GenZir,
   7748     scope: *Scope,
   7749     rl: ResultLoc,
   7750     node: Ast.Node.Index,
   7751 ) InnerError!Zir.Inst.Ref {
   7752     const astgen = gz.astgen;
   7753     const tree = astgen.tree;
   7754     const node_tags = tree.nodes.items(.tag);
   7755     const node_datas = tree.nodes.items(.data);
   7756 
   7757     // Check for float literal as the sub-expression because we want to preserve
   7758     // its negativity rather than having it go through comptime subtraction.
   7759     const operand_node = node_datas[node].lhs;
   7760     if (node_tags[operand_node] == .float_literal) {
   7761         return floatLiteral(gz, rl, operand_node, .negative);
   7762     }
   7763 
   7764     const operand = try expr(gz, scope, .none, operand_node);
   7765     const result = try gz.addUnNode(.negate, operand, node);
   7766     return rvalue(gz, rl, result, node);
   7767 }
   7768 
   7769 fn cmpxchg(
   7770     gz: *GenZir,
   7771     scope: *Scope,
   7772     rl: ResultLoc,
   7773     node: Ast.Node.Index,
   7774     params: []const Ast.Node.Index,
   7775     tag: Zir.Inst.Tag,
   7776 ) InnerError!Zir.Inst.Ref {
   7777     const int_type = try typeExpr(gz, scope, params[0]);
   7778     const result = try gz.addPlNode(tag, node, Zir.Inst.Cmpxchg{
   7779         // zig fmt: off
   7780         .ptr            = try expr(gz, scope, .none,                                 params[1]),
   7781         .expected_value = try expr(gz, scope, .{ .ty = int_type },                   params[2]),
   7782         .new_value      = try expr(gz, scope, .{ .coerced_ty = int_type },           params[3]),
   7783         .success_order  = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[4]),
   7784         .failure_order  = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[5]),
   7785         // zig fmt: on
   7786     });
   7787     return rvalue(gz, rl, result, node);
   7788 }
   7789 
   7790 fn bitBuiltin(
   7791     gz: *GenZir,
   7792     scope: *Scope,
   7793     rl: ResultLoc,
   7794     node: Ast.Node.Index,
   7795     int_type_node: Ast.Node.Index,
   7796     operand_node: Ast.Node.Index,
   7797     tag: Zir.Inst.Tag,
   7798 ) InnerError!Zir.Inst.Ref {
   7799     // The accepted proposal https://github.com/ziglang/zig/issues/6835
   7800     // tells us to remove the type parameter from these builtins. To stay
   7801     // source-compatible with stage1, we still observe the parameter here,
   7802     // but we do not encode it into the ZIR. To implement this proposal in
   7803     // stage2, only AstGen code will need to be changed.
   7804     _ = try typeExpr(gz, scope, int_type_node);
   7805 
   7806     const operand = try expr(gz, scope, .none, operand_node);
   7807     const result = try gz.addUnNode(tag, operand, node);
   7808     return rvalue(gz, rl, result, node);
   7809 }
   7810 
   7811 fn divBuiltin(
   7812     gz: *GenZir,
   7813     scope: *Scope,
   7814     rl: ResultLoc,
   7815     node: Ast.Node.Index,
   7816     lhs_node: Ast.Node.Index,
   7817     rhs_node: Ast.Node.Index,
   7818     tag: Zir.Inst.Tag,
   7819 ) InnerError!Zir.Inst.Ref {
   7820     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   7821         .lhs = try expr(gz, scope, .none, lhs_node),
   7822         .rhs = try expr(gz, scope, .none, rhs_node),
   7823     });
   7824     return rvalue(gz, rl, result, node);
   7825 }
   7826 
   7827 fn simpleCBuiltin(
   7828     gz: *GenZir,
   7829     scope: *Scope,
   7830     rl: ResultLoc,
   7831     node: Ast.Node.Index,
   7832     operand_node: Ast.Node.Index,
   7833     tag: Zir.Inst.Extended,
   7834 ) InnerError!Zir.Inst.Ref {
   7835     const name: []const u8 = if (tag == .c_undef) "C undef" else "C include";
   7836     if (!gz.c_import) return gz.astgen.failNode(node, "{s} valid only inside C import block", .{name});
   7837     const operand = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, operand_node);
   7838     _ = try gz.addExtendedPayload(tag, Zir.Inst.UnNode{
   7839         .node = gz.nodeIndexToRelative(node),
   7840         .operand = operand,
   7841     });
   7842     return rvalue(gz, rl, .void_value, node);
   7843 }
   7844 
   7845 fn offsetOf(
   7846     gz: *GenZir,
   7847     scope: *Scope,
   7848     rl: ResultLoc,
   7849     node: Ast.Node.Index,
   7850     lhs_node: Ast.Node.Index,
   7851     rhs_node: Ast.Node.Index,
   7852     tag: Zir.Inst.Tag,
   7853 ) InnerError!Zir.Inst.Ref {
   7854     const type_inst = try typeExpr(gz, scope, lhs_node);
   7855     const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, rhs_node);
   7856     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   7857         .lhs = type_inst,
   7858         .rhs = field_name,
   7859     });
   7860     return rvalue(gz, rl, result, node);
   7861 }
   7862 
   7863 fn shiftOp(
   7864     gz: *GenZir,
   7865     scope: *Scope,
   7866     rl: ResultLoc,
   7867     node: Ast.Node.Index,
   7868     lhs_node: Ast.Node.Index,
   7869     rhs_node: Ast.Node.Index,
   7870     tag: Zir.Inst.Tag,
   7871 ) InnerError!Zir.Inst.Ref {
   7872     const lhs = try expr(gz, scope, .none, lhs_node);
   7873     const log2_int_type = try gz.addUnNode(.typeof_log2_int_type, lhs, lhs_node);
   7874     const rhs = try expr(gz, scope, .{ .ty = log2_int_type }, rhs_node);
   7875     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   7876         .lhs = lhs,
   7877         .rhs = rhs,
   7878     });
   7879     return rvalue(gz, rl, result, node);
   7880 }
   7881 
   7882 fn cImport(
   7883     gz: *GenZir,
   7884     scope: *Scope,
   7885     node: Ast.Node.Index,
   7886     body_node: Ast.Node.Index,
   7887 ) InnerError!Zir.Inst.Ref {
   7888     const astgen = gz.astgen;
   7889     const gpa = astgen.gpa;
   7890 
   7891     var block_scope = gz.makeSubBlock(scope);
   7892     block_scope.force_comptime = true;
   7893     block_scope.c_import = true;
   7894     defer block_scope.unstack();
   7895 
   7896     const block_inst = try gz.makeBlockInst(.c_import, node);
   7897     const block_result = try expr(&block_scope, &block_scope.base, .none, body_node);
   7898     _ = try gz.addUnNode(.ensure_result_used, block_result, node);
   7899     if (!gz.refIsNoReturn(block_result)) {
   7900         _ = try block_scope.addBreak(.break_inline, block_inst, .void_value);
   7901     }
   7902     try block_scope.setBlockBody(block_inst);
   7903     // block_scope unstacked now, can add new instructions to gz
   7904     try gz.instructions.append(gpa, block_inst);
   7905 
   7906     return indexToRef(block_inst);
   7907 }
   7908 
   7909 fn overflowArithmetic(
   7910     gz: *GenZir,
   7911     scope: *Scope,
   7912     rl: ResultLoc,
   7913     node: Ast.Node.Index,
   7914     params: []const Ast.Node.Index,
   7915     tag: Zir.Inst.Extended,
   7916 ) InnerError!Zir.Inst.Ref {
   7917     const int_type = try typeExpr(gz, scope, params[0]);
   7918     const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
   7919         .ptr_type_simple = .{
   7920             .is_allowzero = false,
   7921             .is_mutable = true,
   7922             .is_volatile = false,
   7923             .size = .One,
   7924             .elem_type = int_type,
   7925         },
   7926     } });
   7927     const lhs = try expr(gz, scope, .{ .ty = int_type }, params[1]);
   7928     const rhs = try expr(gz, scope, .{ .ty = int_type }, params[2]);
   7929     const ptr = try expr(gz, scope, .{ .ty = ptr_type }, params[3]);
   7930     const result = try gz.addExtendedPayload(tag, Zir.Inst.OverflowArithmetic{
   7931         .node = gz.nodeIndexToRelative(node),
   7932         .lhs = lhs,
   7933         .rhs = rhs,
   7934         .ptr = ptr,
   7935     });
   7936     return rvalue(gz, rl, result, node);
   7937 }
   7938 
   7939 fn callExpr(
   7940     gz: *GenZir,
   7941     scope: *Scope,
   7942     rl: ResultLoc,
   7943     node: Ast.Node.Index,
   7944     call: Ast.full.Call,
   7945 ) InnerError!Zir.Inst.Ref {
   7946     const astgen = gz.astgen;
   7947 
   7948     const callee = try calleeExpr(gz, scope, call.ast.fn_expr);
   7949     const modifier: std.builtin.CallOptions.Modifier = blk: {
   7950         if (gz.force_comptime) {
   7951             break :blk .compile_time;
   7952         }
   7953         if (call.async_token != null) {
   7954             break :blk .async_kw;
   7955         }
   7956         if (gz.nosuspend_node != 0) {
   7957             break :blk .no_async;
   7958         }
   7959         break :blk .auto;
   7960     };
   7961 
   7962     {
   7963         astgen.advanceSourceCursor(astgen.tree.tokens.items(.start)[call.ast.lparen]);
   7964         const line = astgen.source_line - gz.decl_line;
   7965         const column = astgen.source_column;
   7966 
   7967         _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
   7968             .dbg_stmt = .{
   7969                 .line = line,
   7970                 .column = column,
   7971             },
   7972         } });
   7973     }
   7974 
   7975     assert(callee != .none);
   7976     assert(node != 0);
   7977 
   7978     const payload_index = try addExtra(astgen, Zir.Inst.Call{
   7979         .callee = callee,
   7980         .flags = .{
   7981             .packed_modifier = @intCast(Zir.Inst.Call.Flags.PackedModifier, @enumToInt(modifier)),
   7982             .args_len = @intCast(Zir.Inst.Call.Flags.PackedArgsLen, call.ast.params.len),
   7983         },
   7984     });
   7985     var extra_index = try reserveExtra(astgen, call.ast.params.len);
   7986 
   7987     for (call.ast.params) |param_node, i| {
   7988         const param_type = try gz.add(.{
   7989             .tag = .param_type,
   7990             .data = .{ .param_type = .{
   7991                 .callee = callee,
   7992                 .param_index = @intCast(u32, i),
   7993             } },
   7994         });
   7995         const arg_ref = try expr(gz, scope, .{ .coerced_ty = param_type }, param_node);
   7996         astgen.extra.items[extra_index] = @enumToInt(arg_ref);
   7997         extra_index += 1;
   7998     }
   7999 
   8000     const call_inst = try gz.addPlNodePayloadIndex(.call, node, payload_index);
   8001     return rvalue(gz, rl, call_inst, node); // TODO function call with result location
   8002 }
   8003 
   8004 /// calleeExpr generates the function part of a call expression (f in f(x)), or the
   8005 /// callee argument to the @call() builtin. If the lhs is a field access or the
   8006 /// @field() builtin, we need to generate a special field_call_bind instruction
   8007 /// instead of the normal field_val or field_ptr.  If this is a inst.func() call,
   8008 /// this instruction will capture the value of the first argument before evaluating
   8009 /// the other arguments. We need to use .ref here to guarantee we will be able to
   8010 /// promote an lvalue to an address if the first parameter requires it.  This
   8011 /// unfortunately also means we need to take a reference to any types on the lhs.
   8012 fn calleeExpr(
   8013     gz: *GenZir,
   8014     scope: *Scope,
   8015     node: Ast.Node.Index,
   8016 ) InnerError!Zir.Inst.Ref {
   8017     const astgen = gz.astgen;
   8018     const tree = astgen.tree;
   8019 
   8020     const tag = tree.nodes.items(.tag)[node];
   8021     switch (tag) {
   8022         .field_access => return addFieldAccess(.field_call_bind, gz, scope, .ref, node),
   8023 
   8024         .builtin_call_two,
   8025         .builtin_call_two_comma,
   8026         .builtin_call,
   8027         .builtin_call_comma,
   8028         => {
   8029             const node_datas = tree.nodes.items(.data);
   8030             const main_tokens = tree.nodes.items(.main_token);
   8031             const builtin_token = main_tokens[node];
   8032             const builtin_name = tree.tokenSlice(builtin_token);
   8033 
   8034             var inline_params: [2]Ast.Node.Index = undefined;
   8035             var params: []Ast.Node.Index = switch (tag) {
   8036                 .builtin_call,
   8037                 .builtin_call_comma,
   8038                 => tree.extra_data[node_datas[node].lhs..node_datas[node].rhs],
   8039 
   8040                 .builtin_call_two,
   8041                 .builtin_call_two_comma,
   8042                 => blk: {
   8043                     inline_params = .{ node_datas[node].lhs, node_datas[node].rhs };
   8044                     const len: usize = if (inline_params[0] == 0) @as(usize, 0) else if (inline_params[1] == 0) @as(usize, 1) else @as(usize, 2);
   8045                     break :blk inline_params[0..len];
   8046                 },
   8047 
   8048                 else => unreachable,
   8049             };
   8050 
   8051             // If anything is wrong, fall back to builtinCall.
   8052             // It will emit any necessary compile errors and notes.
   8053             if (std.mem.eql(u8, builtin_name, "@field") and params.len == 2) {
   8054                 const lhs = try expr(gz, scope, .ref, params[0]);
   8055                 const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);
   8056                 return gz.addExtendedPayload(.field_call_bind_named, Zir.Inst.FieldNamedNode{
   8057                     .node = gz.nodeIndexToRelative(node),
   8058                     .lhs = lhs,
   8059                     .field_name = field_name,
   8060                 });
   8061             }
   8062 
   8063             return builtinCall(gz, scope, .none, node, params);
   8064         },
   8065         else => return expr(gz, scope, .none, node),
   8066     }
   8067 }
   8068 
   8069 const primitives = std.ComptimeStringMap(Zir.Inst.Ref, .{
   8070     .{ "anyerror", .anyerror_type },
   8071     .{ "anyframe", .anyframe_type },
   8072     .{ "anyopaque", .anyopaque_type },
   8073     .{ "bool", .bool_type },
   8074     .{ "c_int", .c_int_type },
   8075     .{ "c_long", .c_long_type },
   8076     .{ "c_longdouble", .c_longdouble_type },
   8077     .{ "c_longlong", .c_longlong_type },
   8078     .{ "c_short", .c_short_type },
   8079     .{ "c_uint", .c_uint_type },
   8080     .{ "c_ulong", .c_ulong_type },
   8081     .{ "c_ulonglong", .c_ulonglong_type },
   8082     .{ "c_ushort", .c_ushort_type },
   8083     .{ "comptime_float", .comptime_float_type },
   8084     .{ "comptime_int", .comptime_int_type },
   8085     .{ "f128", .f128_type },
   8086     .{ "f16", .f16_type },
   8087     .{ "f32", .f32_type },
   8088     .{ "f64", .f64_type },
   8089     .{ "f80", .f80_type },
   8090     .{ "false", .bool_false },
   8091     .{ "i16", .i16_type },
   8092     .{ "i32", .i32_type },
   8093     .{ "i64", .i64_type },
   8094     .{ "i128", .i128_type },
   8095     .{ "i8", .i8_type },
   8096     .{ "isize", .isize_type },
   8097     .{ "noreturn", .noreturn_type },
   8098     .{ "null", .null_value },
   8099     .{ "true", .bool_true },
   8100     .{ "type", .type_type },
   8101     .{ "u16", .u16_type },
   8102     .{ "u29", .u29_type },
   8103     .{ "u32", .u32_type },
   8104     .{ "u64", .u64_type },
   8105     .{ "u128", .u128_type },
   8106     .{ "u1", .u1_type },
   8107     .{ "u8", .u8_type },
   8108     .{ "undefined", .undef },
   8109     .{ "usize", .usize_type },
   8110     .{ "void", .void_type },
   8111 });
   8112 
   8113 fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index, have_res_ty: bool) bool {
   8114     const node_tags = tree.nodes.items(.tag);
   8115     const node_datas = tree.nodes.items(.data);
   8116     const main_tokens = tree.nodes.items(.main_token);
   8117     const token_tags = tree.tokens.items(.tag);
   8118 
   8119     var node = start_node;
   8120     while (true) {
   8121         switch (node_tags[node]) {
   8122             .root,
   8123             .@"usingnamespace",
   8124             .test_decl,
   8125             .switch_case,
   8126             .switch_case_one,
   8127             .container_field_init,
   8128             .container_field_align,
   8129             .container_field,
   8130             .asm_output,
   8131             .asm_input,
   8132             => unreachable,
   8133 
   8134             .@"return",
   8135             .@"break",
   8136             .@"continue",
   8137             .bit_not,
   8138             .bool_not,
   8139             .global_var_decl,
   8140             .local_var_decl,
   8141             .simple_var_decl,
   8142             .aligned_var_decl,
   8143             .@"defer",
   8144             .@"errdefer",
   8145             .address_of,
   8146             .optional_type,
   8147             .negation,
   8148             .negation_wrap,
   8149             .@"resume",
   8150             .array_type,
   8151             .array_type_sentinel,
   8152             .ptr_type_aligned,
   8153             .ptr_type_sentinel,
   8154             .ptr_type,
   8155             .ptr_type_bit_range,
   8156             .@"suspend",
   8157             .fn_proto_simple,
   8158             .fn_proto_multi,
   8159             .fn_proto_one,
   8160             .fn_proto,
   8161             .fn_decl,
   8162             .anyframe_type,
   8163             .anyframe_literal,
   8164             .integer_literal,
   8165             .float_literal,
   8166             .enum_literal,
   8167             .string_literal,
   8168             .multiline_string_literal,
   8169             .char_literal,
   8170             .unreachable_literal,
   8171             .identifier,
   8172             .error_set_decl,
   8173             .container_decl,
   8174             .container_decl_trailing,
   8175             .container_decl_two,
   8176             .container_decl_two_trailing,
   8177             .container_decl_arg,
   8178             .container_decl_arg_trailing,
   8179             .tagged_union,
   8180             .tagged_union_trailing,
   8181             .tagged_union_two,
   8182             .tagged_union_two_trailing,
   8183             .tagged_union_enum_tag,
   8184             .tagged_union_enum_tag_trailing,
   8185             .@"asm",
   8186             .asm_simple,
   8187             .add,
   8188             .add_wrap,
   8189             .add_sat,
   8190             .array_cat,
   8191             .array_mult,
   8192             .assign,
   8193             .assign_bit_and,
   8194             .assign_bit_or,
   8195             .assign_shl,
   8196             .assign_shl_sat,
   8197             .assign_shr,
   8198             .assign_bit_xor,
   8199             .assign_div,
   8200             .assign_sub,
   8201             .assign_sub_wrap,
   8202             .assign_sub_sat,
   8203             .assign_mod,
   8204             .assign_add,
   8205             .assign_add_wrap,
   8206             .assign_add_sat,
   8207             .assign_mul,
   8208             .assign_mul_wrap,
   8209             .assign_mul_sat,
   8210             .bang_equal,
   8211             .bit_and,
   8212             .bit_or,
   8213             .shl,
   8214             .shl_sat,
   8215             .shr,
   8216             .bit_xor,
   8217             .bool_and,
   8218             .bool_or,
   8219             .div,
   8220             .equal_equal,
   8221             .error_union,
   8222             .greater_or_equal,
   8223             .greater_than,
   8224             .less_or_equal,
   8225             .less_than,
   8226             .merge_error_sets,
   8227             .mod,
   8228             .mul,
   8229             .mul_wrap,
   8230             .mul_sat,
   8231             .switch_range,
   8232             .field_access,
   8233             .sub,
   8234             .sub_wrap,
   8235             .sub_sat,
   8236             .slice,
   8237             .slice_open,
   8238             .slice_sentinel,
   8239             .deref,
   8240             .array_access,
   8241             .error_value,
   8242             .while_simple, // This variant cannot have an else expression.
   8243             .while_cont, // This variant cannot have an else expression.
   8244             .for_simple, // This variant cannot have an else expression.
   8245             .if_simple, // This variant cannot have an else expression.
   8246             => return false,
   8247 
   8248             // Forward the question to the LHS sub-expression.
   8249             .grouped_expression,
   8250             .@"try",
   8251             .@"await",
   8252             .@"comptime",
   8253             .@"nosuspend",
   8254             .unwrap_optional,
   8255             => node = node_datas[node].lhs,
   8256 
   8257             // Forward the question to the RHS sub-expression.
   8258             .@"catch",
   8259             .@"orelse",
   8260             => node = node_datas[node].rhs,
   8261 
   8262             // Array and struct init exprs write to result locs, but anon literals do not.
   8263             .array_init_one,
   8264             .array_init_one_comma,
   8265             .struct_init_one,
   8266             .struct_init_one_comma,
   8267             .array_init,
   8268             .array_init_comma,
   8269             .struct_init,
   8270             .struct_init_comma,
   8271             => return have_res_ty or node_datas[node].lhs != 0,
   8272 
   8273             // Anon literals do not need result location.
   8274             .array_init_dot_two,
   8275             .array_init_dot_two_comma,
   8276             .array_init_dot,
   8277             .array_init_dot_comma,
   8278             .struct_init_dot_two,
   8279             .struct_init_dot_two_comma,
   8280             .struct_init_dot,
   8281             .struct_init_dot_comma,
   8282             => return have_res_ty,
   8283 
   8284             // True because depending on comptime conditions, sub-expressions
   8285             // may be the kind that need memory locations.
   8286             .@"while", // This variant always has an else expression.
   8287             .@"if", // This variant always has an else expression.
   8288             .@"for", // This variant always has an else expression.
   8289             .@"switch",
   8290             .switch_comma,
   8291             .call_one,
   8292             .call_one_comma,
   8293             .async_call_one,
   8294             .async_call_one_comma,
   8295             .call,
   8296             .call_comma,
   8297             .async_call,
   8298             .async_call_comma,
   8299             => return true,
   8300 
   8301             .block_two,
   8302             .block_two_semicolon,
   8303             .block,
   8304             .block_semicolon,
   8305             => {
   8306                 const lbrace = main_tokens[node];
   8307                 if (token_tags[lbrace - 1] == .colon) {
   8308                     // Labeled blocks may need a memory location to forward
   8309                     // to their break statements.
   8310                     return true;
   8311                 } else {
   8312                     return false;
   8313                 }
   8314             },
   8315 
   8316             .builtin_call_two, .builtin_call_two_comma => {
   8317                 const builtin_token = main_tokens[node];
   8318                 const builtin_name = tree.tokenSlice(builtin_token);
   8319                 // If the builtin is an invalid name, we don't cause an error here; instead
   8320                 // let it pass, and the error will be "invalid builtin function" later.
   8321                 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false;
   8322                 switch (builtin_info.needs_mem_loc) {
   8323                     .never => return false,
   8324                     .always => return true,
   8325                     .forward1 => node = node_datas[node].rhs,
   8326                 }
   8327             },
   8328 
   8329             .builtin_call, .builtin_call_comma => {
   8330                 const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
   8331                 const builtin_token = main_tokens[node];
   8332                 const builtin_name = tree.tokenSlice(builtin_token);
   8333                 // If the builtin is an invalid name, we don't cause an error here; instead
   8334                 // let it pass, and the error will be "invalid builtin function" later.
   8335                 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false;
   8336                 switch (builtin_info.needs_mem_loc) {
   8337                     .never => return false,
   8338                     .always => return true,
   8339                     .forward1 => node = params[1],
   8340                 }
   8341             },
   8342         }
   8343     }
   8344 }
   8345 
   8346 fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.EvalToError {
   8347     const node_tags = tree.nodes.items(.tag);
   8348     const node_datas = tree.nodes.items(.data);
   8349     const main_tokens = tree.nodes.items(.main_token);
   8350     const token_tags = tree.tokens.items(.tag);
   8351 
   8352     var node = start_node;
   8353     while (true) {
   8354         switch (node_tags[node]) {
   8355             .root,
   8356             .@"usingnamespace",
   8357             .test_decl,
   8358             .switch_case,
   8359             .switch_case_one,
   8360             .container_field_init,
   8361             .container_field_align,
   8362             .container_field,
   8363             .asm_output,
   8364             .asm_input,
   8365             => unreachable,
   8366 
   8367             .error_value => return .always,
   8368 
   8369             .@"asm",
   8370             .asm_simple,
   8371             .identifier,
   8372             .field_access,
   8373             .deref,
   8374             .array_access,
   8375             .while_simple,
   8376             .while_cont,
   8377             .for_simple,
   8378             .if_simple,
   8379             .@"while",
   8380             .@"if",
   8381             .@"for",
   8382             .@"switch",
   8383             .switch_comma,
   8384             .call_one,
   8385             .call_one_comma,
   8386             .async_call_one,
   8387             .async_call_one_comma,
   8388             .call,
   8389             .call_comma,
   8390             .async_call,
   8391             .async_call_comma,
   8392             => return .maybe,
   8393 
   8394             .@"return",
   8395             .@"break",
   8396             .@"continue",
   8397             .bit_not,
   8398             .bool_not,
   8399             .global_var_decl,
   8400             .local_var_decl,
   8401             .simple_var_decl,
   8402             .aligned_var_decl,
   8403             .@"defer",
   8404             .@"errdefer",
   8405             .address_of,
   8406             .optional_type,
   8407             .negation,
   8408             .negation_wrap,
   8409             .@"resume",
   8410             .array_type,
   8411             .array_type_sentinel,
   8412             .ptr_type_aligned,
   8413             .ptr_type_sentinel,
   8414             .ptr_type,
   8415             .ptr_type_bit_range,
   8416             .@"suspend",
   8417             .fn_proto_simple,
   8418             .fn_proto_multi,
   8419             .fn_proto_one,
   8420             .fn_proto,
   8421             .fn_decl,
   8422             .anyframe_type,
   8423             .anyframe_literal,
   8424             .integer_literal,
   8425             .float_literal,
   8426             .enum_literal,
   8427             .string_literal,
   8428             .multiline_string_literal,
   8429             .char_literal,
   8430             .unreachable_literal,
   8431             .error_set_decl,
   8432             .container_decl,
   8433             .container_decl_trailing,
   8434             .container_decl_two,
   8435             .container_decl_two_trailing,
   8436             .container_decl_arg,
   8437             .container_decl_arg_trailing,
   8438             .tagged_union,
   8439             .tagged_union_trailing,
   8440             .tagged_union_two,
   8441             .tagged_union_two_trailing,
   8442             .tagged_union_enum_tag,
   8443             .tagged_union_enum_tag_trailing,
   8444             .add,
   8445             .add_wrap,
   8446             .add_sat,
   8447             .array_cat,
   8448             .array_mult,
   8449             .assign,
   8450             .assign_bit_and,
   8451             .assign_bit_or,
   8452             .assign_shl,
   8453             .assign_shl_sat,
   8454             .assign_shr,
   8455             .assign_bit_xor,
   8456             .assign_div,
   8457             .assign_sub,
   8458             .assign_sub_wrap,
   8459             .assign_sub_sat,
   8460             .assign_mod,
   8461             .assign_add,
   8462             .assign_add_wrap,
   8463             .assign_add_sat,
   8464             .assign_mul,
   8465             .assign_mul_wrap,
   8466             .assign_mul_sat,
   8467             .bang_equal,
   8468             .bit_and,
   8469             .bit_or,
   8470             .shl,
   8471             .shl_sat,
   8472             .shr,
   8473             .bit_xor,
   8474             .bool_and,
   8475             .bool_or,
   8476             .div,
   8477             .equal_equal,
   8478             .error_union,
   8479             .greater_or_equal,
   8480             .greater_than,
   8481             .less_or_equal,
   8482             .less_than,
   8483             .merge_error_sets,
   8484             .mod,
   8485             .mul,
   8486             .mul_wrap,
   8487             .mul_sat,
   8488             .switch_range,
   8489             .sub,
   8490             .sub_wrap,
   8491             .sub_sat,
   8492             .slice,
   8493             .slice_open,
   8494             .slice_sentinel,
   8495             .array_init_one,
   8496             .array_init_one_comma,
   8497             .array_init_dot_two,
   8498             .array_init_dot_two_comma,
   8499             .array_init_dot,
   8500             .array_init_dot_comma,
   8501             .array_init,
   8502             .array_init_comma,
   8503             .struct_init_one,
   8504             .struct_init_one_comma,
   8505             .struct_init_dot_two,
   8506             .struct_init_dot_two_comma,
   8507             .struct_init_dot,
   8508             .struct_init_dot_comma,
   8509             .struct_init,
   8510             .struct_init_comma,
   8511             => return .never,
   8512 
   8513             // Forward the question to the LHS sub-expression.
   8514             .grouped_expression,
   8515             .@"try",
   8516             .@"await",
   8517             .@"comptime",
   8518             .@"nosuspend",
   8519             .unwrap_optional,
   8520             => node = node_datas[node].lhs,
   8521 
   8522             // LHS sub-expression may still be an error under the outer optional or error union
   8523             .@"catch",
   8524             .@"orelse",
   8525             => return .maybe,
   8526 
   8527             .block_two,
   8528             .block_two_semicolon,
   8529             .block,
   8530             .block_semicolon,
   8531             => {
   8532                 const lbrace = main_tokens[node];
   8533                 if (token_tags[lbrace - 1] == .colon) {
   8534                     // Labeled blocks may need a memory location to forward
   8535                     // to their break statements.
   8536                     return .maybe;
   8537                 } else {
   8538                     return .never;
   8539                 }
   8540             },
   8541 
   8542             .builtin_call,
   8543             .builtin_call_comma,
   8544             .builtin_call_two,
   8545             .builtin_call_two_comma,
   8546             => {
   8547                 const builtin_token = main_tokens[node];
   8548                 const builtin_name = tree.tokenSlice(builtin_token);
   8549                 // If the builtin is an invalid name, we don't cause an error here; instead
   8550                 // let it pass, and the error will be "invalid builtin function" later.
   8551                 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return .maybe;
   8552                 return builtin_info.eval_to_error;
   8553             },
   8554         }
   8555     }
   8556 }
   8557 
   8558 /// Returns `true` if it is known the type expression has more than one possible value;
   8559 /// `false` otherwise.
   8560 fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.Index) bool {
   8561     const node_tags = tree.nodes.items(.tag);
   8562     const node_datas = tree.nodes.items(.data);
   8563 
   8564     var node = start_node;
   8565     while (true) {
   8566         switch (node_tags[node]) {
   8567             .root,
   8568             .@"usingnamespace",
   8569             .test_decl,
   8570             .switch_case,
   8571             .switch_case_one,
   8572             .container_field_init,
   8573             .container_field_align,
   8574             .container_field,
   8575             .asm_output,
   8576             .asm_input,
   8577             .global_var_decl,
   8578             .local_var_decl,
   8579             .simple_var_decl,
   8580             .aligned_var_decl,
   8581             => unreachable,
   8582 
   8583             .@"return",
   8584             .@"break",
   8585             .@"continue",
   8586             .bit_not,
   8587             .bool_not,
   8588             .@"defer",
   8589             .@"errdefer",
   8590             .address_of,
   8591             .negation,
   8592             .negation_wrap,
   8593             .@"resume",
   8594             .array_type,
   8595             .@"suspend",
   8596             .fn_decl,
   8597             .anyframe_literal,
   8598             .integer_literal,
   8599             .float_literal,
   8600             .enum_literal,
   8601             .string_literal,
   8602             .multiline_string_literal,
   8603             .char_literal,
   8604             .unreachable_literal,
   8605             .error_set_decl,
   8606             .container_decl,
   8607             .container_decl_trailing,
   8608             .container_decl_two,
   8609             .container_decl_two_trailing,
   8610             .container_decl_arg,
   8611             .container_decl_arg_trailing,
   8612             .tagged_union,
   8613             .tagged_union_trailing,
   8614             .tagged_union_two,
   8615             .tagged_union_two_trailing,
   8616             .tagged_union_enum_tag,
   8617             .tagged_union_enum_tag_trailing,
   8618             .@"asm",
   8619             .asm_simple,
   8620             .add,
   8621             .add_wrap,
   8622             .add_sat,
   8623             .array_cat,
   8624             .array_mult,
   8625             .assign,
   8626             .assign_bit_and,
   8627             .assign_bit_or,
   8628             .assign_shl,
   8629             .assign_shl_sat,
   8630             .assign_shr,
   8631             .assign_bit_xor,
   8632             .assign_div,
   8633             .assign_sub,
   8634             .assign_sub_wrap,
   8635             .assign_sub_sat,
   8636             .assign_mod,
   8637             .assign_add,
   8638             .assign_add_wrap,
   8639             .assign_add_sat,
   8640             .assign_mul,
   8641             .assign_mul_wrap,
   8642             .assign_mul_sat,
   8643             .bang_equal,
   8644             .bit_and,
   8645             .bit_or,
   8646             .shl,
   8647             .shl_sat,
   8648             .shr,
   8649             .bit_xor,
   8650             .bool_and,
   8651             .bool_or,
   8652             .div,
   8653             .equal_equal,
   8654             .error_union,
   8655             .greater_or_equal,
   8656             .greater_than,
   8657             .less_or_equal,
   8658             .less_than,
   8659             .merge_error_sets,
   8660             .mod,
   8661             .mul,
   8662             .mul_wrap,
   8663             .mul_sat,
   8664             .switch_range,
   8665             .field_access,
   8666             .sub,
   8667             .sub_wrap,
   8668             .sub_sat,
   8669             .slice,
   8670             .slice_open,
   8671             .slice_sentinel,
   8672             .deref,
   8673             .array_access,
   8674             .error_value,
   8675             .while_simple,
   8676             .while_cont,
   8677             .for_simple,
   8678             .if_simple,
   8679             .@"catch",
   8680             .@"orelse",
   8681             .array_init_one,
   8682             .array_init_one_comma,
   8683             .array_init_dot_two,
   8684             .array_init_dot_two_comma,
   8685             .array_init_dot,
   8686             .array_init_dot_comma,
   8687             .array_init,
   8688             .array_init_comma,
   8689             .struct_init_one,
   8690             .struct_init_one_comma,
   8691             .struct_init_dot_two,
   8692             .struct_init_dot_two_comma,
   8693             .struct_init_dot,
   8694             .struct_init_dot_comma,
   8695             .struct_init,
   8696             .struct_init_comma,
   8697             .@"while",
   8698             .@"if",
   8699             .@"for",
   8700             .@"switch",
   8701             .switch_comma,
   8702             .call_one,
   8703             .call_one_comma,
   8704             .async_call_one,
   8705             .async_call_one_comma,
   8706             .call,
   8707             .call_comma,
   8708             .async_call,
   8709             .async_call_comma,
   8710             .block_two,
   8711             .block_two_semicolon,
   8712             .block,
   8713             .block_semicolon,
   8714             .builtin_call,
   8715             .builtin_call_comma,
   8716             .builtin_call_two,
   8717             .builtin_call_two_comma,
   8718             // these are function bodies, not pointers
   8719             .fn_proto_simple,
   8720             .fn_proto_multi,
   8721             .fn_proto_one,
   8722             .fn_proto,
   8723             => return false,
   8724 
   8725             // Forward the question to the LHS sub-expression.
   8726             .grouped_expression,
   8727             .@"try",
   8728             .@"await",
   8729             .@"comptime",
   8730             .@"nosuspend",
   8731             .unwrap_optional,
   8732             => node = node_datas[node].lhs,
   8733 
   8734             .ptr_type_aligned,
   8735             .ptr_type_sentinel,
   8736             .ptr_type,
   8737             .ptr_type_bit_range,
   8738             .optional_type,
   8739             .anyframe_type,
   8740             .array_type_sentinel,
   8741             => return true,
   8742 
   8743             .identifier => {
   8744                 const main_tokens = tree.nodes.items(.main_token);
   8745                 const ident_bytes = tree.tokenSlice(main_tokens[node]);
   8746                 if (primitives.get(ident_bytes)) |primitive| switch (primitive) {
   8747                     .anyerror_type,
   8748                     .anyframe_type,
   8749                     .anyopaque_type,
   8750                     .bool_type,
   8751                     .c_int_type,
   8752                     .c_long_type,
   8753                     .c_longdouble_type,
   8754                     .c_longlong_type,
   8755                     .c_short_type,
   8756                     .c_uint_type,
   8757                     .c_ulong_type,
   8758                     .c_ulonglong_type,
   8759                     .c_ushort_type,
   8760                     .comptime_float_type,
   8761                     .comptime_int_type,
   8762                     .f16_type,
   8763                     .f32_type,
   8764                     .f64_type,
   8765                     .f80_type,
   8766                     .f128_type,
   8767                     .i16_type,
   8768                     .i32_type,
   8769                     .i64_type,
   8770                     .i128_type,
   8771                     .i8_type,
   8772                     .isize_type,
   8773                     .type_type,
   8774                     .u16_type,
   8775                     .u29_type,
   8776                     .u32_type,
   8777                     .u64_type,
   8778                     .u128_type,
   8779                     .u1_type,
   8780                     .u8_type,
   8781                     .usize_type,
   8782                     => return true,
   8783 
   8784                     .void_type,
   8785                     .bool_false,
   8786                     .bool_true,
   8787                     .null_value,
   8788                     .undef,
   8789                     .noreturn_type,
   8790                     => return false,
   8791 
   8792                     else => unreachable, // that's all the values from `primitives`.
   8793                 } else {
   8794                     return false;
   8795                 }
   8796             },
   8797         }
   8798     }
   8799 }
   8800 
   8801 /// Returns `true` if it is known the expression is a type that cannot be used at runtime;
   8802 /// `false` otherwise.
   8803 fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
   8804     const node_tags = tree.nodes.items(.tag);
   8805     const node_datas = tree.nodes.items(.data);
   8806 
   8807     var node = start_node;
   8808     while (true) {
   8809         switch (node_tags[node]) {
   8810             .root,
   8811             .@"usingnamespace",
   8812             .test_decl,
   8813             .switch_case,
   8814             .switch_case_one,
   8815             .container_field_init,
   8816             .container_field_align,
   8817             .container_field,
   8818             .asm_output,
   8819             .asm_input,
   8820             .global_var_decl,
   8821             .local_var_decl,
   8822             .simple_var_decl,
   8823             .aligned_var_decl,
   8824             => unreachable,
   8825 
   8826             .@"return",
   8827             .@"break",
   8828             .@"continue",
   8829             .bit_not,
   8830             .bool_not,
   8831             .@"defer",
   8832             .@"errdefer",
   8833             .address_of,
   8834             .negation,
   8835             .negation_wrap,
   8836             .@"resume",
   8837             .array_type,
   8838             .@"suspend",
   8839             .fn_decl,
   8840             .anyframe_literal,
   8841             .integer_literal,
   8842             .float_literal,
   8843             .enum_literal,
   8844             .string_literal,
   8845             .multiline_string_literal,
   8846             .char_literal,
   8847             .unreachable_literal,
   8848             .error_set_decl,
   8849             .container_decl,
   8850             .container_decl_trailing,
   8851             .container_decl_two,
   8852             .container_decl_two_trailing,
   8853             .container_decl_arg,
   8854             .container_decl_arg_trailing,
   8855             .tagged_union,
   8856             .tagged_union_trailing,
   8857             .tagged_union_two,
   8858             .tagged_union_two_trailing,
   8859             .tagged_union_enum_tag,
   8860             .tagged_union_enum_tag_trailing,
   8861             .@"asm",
   8862             .asm_simple,
   8863             .add,
   8864             .add_wrap,
   8865             .add_sat,
   8866             .array_cat,
   8867             .array_mult,
   8868             .assign,
   8869             .assign_bit_and,
   8870             .assign_bit_or,
   8871             .assign_shl,
   8872             .assign_shl_sat,
   8873             .assign_shr,
   8874             .assign_bit_xor,
   8875             .assign_div,
   8876             .assign_sub,
   8877             .assign_sub_wrap,
   8878             .assign_sub_sat,
   8879             .assign_mod,
   8880             .assign_add,
   8881             .assign_add_wrap,
   8882             .assign_add_sat,
   8883             .assign_mul,
   8884             .assign_mul_wrap,
   8885             .assign_mul_sat,
   8886             .bang_equal,
   8887             .bit_and,
   8888             .bit_or,
   8889             .shl,
   8890             .shl_sat,
   8891             .shr,
   8892             .bit_xor,
   8893             .bool_and,
   8894             .bool_or,
   8895             .div,
   8896             .equal_equal,
   8897             .error_union,
   8898             .greater_or_equal,
   8899             .greater_than,
   8900             .less_or_equal,
   8901             .less_than,
   8902             .merge_error_sets,
   8903             .mod,
   8904             .mul,
   8905             .mul_wrap,
   8906             .mul_sat,
   8907             .switch_range,
   8908             .field_access,
   8909             .sub,
   8910             .sub_wrap,
   8911             .sub_sat,
   8912             .slice,
   8913             .slice_open,
   8914             .slice_sentinel,
   8915             .deref,
   8916             .array_access,
   8917             .error_value,
   8918             .while_simple,
   8919             .while_cont,
   8920             .for_simple,
   8921             .if_simple,
   8922             .@"catch",
   8923             .@"orelse",
   8924             .array_init_one,
   8925             .array_init_one_comma,
   8926             .array_init_dot_two,
   8927             .array_init_dot_two_comma,
   8928             .array_init_dot,
   8929             .array_init_dot_comma,
   8930             .array_init,
   8931             .array_init_comma,
   8932             .struct_init_one,
   8933             .struct_init_one_comma,
   8934             .struct_init_dot_two,
   8935             .struct_init_dot_two_comma,
   8936             .struct_init_dot,
   8937             .struct_init_dot_comma,
   8938             .struct_init,
   8939             .struct_init_comma,
   8940             .@"while",
   8941             .@"if",
   8942             .@"for",
   8943             .@"switch",
   8944             .switch_comma,
   8945             .call_one,
   8946             .call_one_comma,
   8947             .async_call_one,
   8948             .async_call_one_comma,
   8949             .call,
   8950             .call_comma,
   8951             .async_call,
   8952             .async_call_comma,
   8953             .block_two,
   8954             .block_two_semicolon,
   8955             .block,
   8956             .block_semicolon,
   8957             .builtin_call,
   8958             .builtin_call_comma,
   8959             .builtin_call_two,
   8960             .builtin_call_two_comma,
   8961             .ptr_type_aligned,
   8962             .ptr_type_sentinel,
   8963             .ptr_type,
   8964             .ptr_type_bit_range,
   8965             .optional_type,
   8966             .anyframe_type,
   8967             .array_type_sentinel,
   8968             => return false,
   8969 
   8970             // these are function bodies, not pointers
   8971             .fn_proto_simple,
   8972             .fn_proto_multi,
   8973             .fn_proto_one,
   8974             .fn_proto,
   8975             => return true,
   8976 
   8977             // Forward the question to the LHS sub-expression.
   8978             .grouped_expression,
   8979             .@"try",
   8980             .@"await",
   8981             .@"comptime",
   8982             .@"nosuspend",
   8983             .unwrap_optional,
   8984             => node = node_datas[node].lhs,
   8985 
   8986             .identifier => {
   8987                 const main_tokens = tree.nodes.items(.main_token);
   8988                 const ident_bytes = tree.tokenSlice(main_tokens[node]);
   8989                 if (primitives.get(ident_bytes)) |primitive| switch (primitive) {
   8990                     .anyerror_type,
   8991                     .anyframe_type,
   8992                     .anyopaque_type,
   8993                     .bool_type,
   8994                     .c_int_type,
   8995                     .c_long_type,
   8996                     .c_longdouble_type,
   8997                     .c_longlong_type,
   8998                     .c_short_type,
   8999                     .c_uint_type,
   9000                     .c_ulong_type,
   9001                     .c_ulonglong_type,
   9002                     .c_ushort_type,
   9003                     .f16_type,
   9004                     .f32_type,
   9005                     .f64_type,
   9006                     .f80_type,
   9007                     .f128_type,
   9008                     .i16_type,
   9009                     .i32_type,
   9010                     .i64_type,
   9011                     .i128_type,
   9012                     .i8_type,
   9013                     .isize_type,
   9014                     .u16_type,
   9015                     .u29_type,
   9016                     .u32_type,
   9017                     .u64_type,
   9018                     .u128_type,
   9019                     .u1_type,
   9020                     .u8_type,
   9021                     .usize_type,
   9022                     .void_type,
   9023                     .bool_false,
   9024                     .bool_true,
   9025                     .null_value,
   9026                     .undef,
   9027                     .noreturn_type,
   9028                     => return false,
   9029 
   9030                     .comptime_float_type,
   9031                     .comptime_int_type,
   9032                     .type_type,
   9033                     => return true,
   9034 
   9035                     else => unreachable, // that's all the values from `primitives`.
   9036                 } else {
   9037                     return false;
   9038                 }
   9039             },
   9040         }
   9041     }
   9042 }
   9043 
   9044 /// Applies `rl` semantics to `result`. Expressions which do not do their own handling of
   9045 /// result locations must call this function on their result.
   9046 /// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer.
   9047 /// If the `ResultLoc` is `ty`, it will coerce the result to the type.
   9048 /// Assumes nothing stacked on `gz`.
   9049 fn rvalue(
   9050     gz: *GenZir,
   9051     rl: ResultLoc,
   9052     raw_result: Zir.Inst.Ref,
   9053     src_node: Ast.Node.Index,
   9054 ) InnerError!Zir.Inst.Ref {
   9055     const result = r: {
   9056         if (refToIndex(raw_result)) |result_index| {
   9057             const zir_tags = gz.astgen.instructions.items(.tag);
   9058             const data = gz.astgen.instructions.items(.data)[result_index];
   9059             if (zir_tags[result_index].isAlwaysVoid(data)) {
   9060                 break :r Zir.Inst.Ref.void_value;
   9061             }
   9062         }
   9063         break :r raw_result;
   9064     };
   9065     if (gz.endsWithNoReturn()) return result;
   9066     switch (rl) {
   9067         .none, .coerced_ty => return result,
   9068         .discard => {
   9069             // Emit a compile error for discarding error values.
   9070             _ = try gz.addUnNode(.ensure_result_non_error, result, src_node);
   9071             return result;
   9072         },
   9073         .ref => {
   9074             // We need a pointer but we have a value.
   9075             // Unfortunately it's not quite as simple as directly emitting a ref
   9076             // instruction here because we need subsequent address-of operator on
   9077             // const locals to return the same address.
   9078             const astgen = gz.astgen;
   9079             const tree = astgen.tree;
   9080             const src_token = tree.firstToken(src_node);
   9081             const result_index = refToIndex(result) orelse
   9082                 return gz.addUnTok(.ref, result, src_token);
   9083             const zir_tags = gz.astgen.instructions.items(.tag);
   9084             if (zir_tags[result_index].isParam())
   9085                 return gz.addUnTok(.ref, result, src_token);
   9086             const gop = try astgen.ref_table.getOrPut(astgen.gpa, result_index);
   9087             if (!gop.found_existing) {
   9088                 gop.value_ptr.* = try gz.makeUnTok(.ref, result, src_token);
   9089             }
   9090             return indexToRef(gop.value_ptr.*);
   9091         },
   9092         .ty => |ty_inst| {
   9093             // Quickly eliminate some common, unnecessary type coercion.
   9094             const as_ty = @as(u64, @enumToInt(Zir.Inst.Ref.type_type)) << 32;
   9095             const as_comptime_int = @as(u64, @enumToInt(Zir.Inst.Ref.comptime_int_type)) << 32;
   9096             const as_bool = @as(u64, @enumToInt(Zir.Inst.Ref.bool_type)) << 32;
   9097             const as_usize = @as(u64, @enumToInt(Zir.Inst.Ref.usize_type)) << 32;
   9098             const as_void = @as(u64, @enumToInt(Zir.Inst.Ref.void_type)) << 32;
   9099             switch ((@as(u64, @enumToInt(ty_inst)) << 32) | @as(u64, @enumToInt(result))) {
   9100                 as_ty | @enumToInt(Zir.Inst.Ref.u1_type),
   9101                 as_ty | @enumToInt(Zir.Inst.Ref.u8_type),
   9102                 as_ty | @enumToInt(Zir.Inst.Ref.i8_type),
   9103                 as_ty | @enumToInt(Zir.Inst.Ref.u16_type),
   9104                 as_ty | @enumToInt(Zir.Inst.Ref.u29_type),
   9105                 as_ty | @enumToInt(Zir.Inst.Ref.i16_type),
   9106                 as_ty | @enumToInt(Zir.Inst.Ref.u32_type),
   9107                 as_ty | @enumToInt(Zir.Inst.Ref.i32_type),
   9108                 as_ty | @enumToInt(Zir.Inst.Ref.u64_type),
   9109                 as_ty | @enumToInt(Zir.Inst.Ref.i64_type),
   9110                 as_ty | @enumToInt(Zir.Inst.Ref.usize_type),
   9111                 as_ty | @enumToInt(Zir.Inst.Ref.isize_type),
   9112                 as_ty | @enumToInt(Zir.Inst.Ref.c_short_type),
   9113                 as_ty | @enumToInt(Zir.Inst.Ref.c_ushort_type),
   9114                 as_ty | @enumToInt(Zir.Inst.Ref.c_int_type),
   9115                 as_ty | @enumToInt(Zir.Inst.Ref.c_uint_type),
   9116                 as_ty | @enumToInt(Zir.Inst.Ref.c_long_type),
   9117                 as_ty | @enumToInt(Zir.Inst.Ref.c_ulong_type),
   9118                 as_ty | @enumToInt(Zir.Inst.Ref.c_longlong_type),
   9119                 as_ty | @enumToInt(Zir.Inst.Ref.c_ulonglong_type),
   9120                 as_ty | @enumToInt(Zir.Inst.Ref.c_longdouble_type),
   9121                 as_ty | @enumToInt(Zir.Inst.Ref.f16_type),
   9122                 as_ty | @enumToInt(Zir.Inst.Ref.f32_type),
   9123                 as_ty | @enumToInt(Zir.Inst.Ref.f64_type),
   9124                 as_ty | @enumToInt(Zir.Inst.Ref.f80_type),
   9125                 as_ty | @enumToInt(Zir.Inst.Ref.f128_type),
   9126                 as_ty | @enumToInt(Zir.Inst.Ref.anyopaque_type),
   9127                 as_ty | @enumToInt(Zir.Inst.Ref.bool_type),
   9128                 as_ty | @enumToInt(Zir.Inst.Ref.void_type),
   9129                 as_ty | @enumToInt(Zir.Inst.Ref.type_type),
   9130                 as_ty | @enumToInt(Zir.Inst.Ref.anyerror_type),
   9131                 as_ty | @enumToInt(Zir.Inst.Ref.comptime_int_type),
   9132                 as_ty | @enumToInt(Zir.Inst.Ref.comptime_float_type),
   9133                 as_ty | @enumToInt(Zir.Inst.Ref.noreturn_type),
   9134                 as_ty | @enumToInt(Zir.Inst.Ref.null_type),
   9135                 as_ty | @enumToInt(Zir.Inst.Ref.undefined_type),
   9136                 as_ty | @enumToInt(Zir.Inst.Ref.fn_noreturn_no_args_type),
   9137                 as_ty | @enumToInt(Zir.Inst.Ref.fn_void_no_args_type),
   9138                 as_ty | @enumToInt(Zir.Inst.Ref.fn_naked_noreturn_no_args_type),
   9139                 as_ty | @enumToInt(Zir.Inst.Ref.fn_ccc_void_no_args_type),
   9140                 as_ty | @enumToInt(Zir.Inst.Ref.single_const_pointer_to_comptime_int_type),
   9141                 as_ty | @enumToInt(Zir.Inst.Ref.const_slice_u8_type),
   9142                 as_ty | @enumToInt(Zir.Inst.Ref.enum_literal_type),
   9143                 as_comptime_int | @enumToInt(Zir.Inst.Ref.zero),
   9144                 as_comptime_int | @enumToInt(Zir.Inst.Ref.one),
   9145                 as_bool | @enumToInt(Zir.Inst.Ref.bool_true),
   9146                 as_bool | @enumToInt(Zir.Inst.Ref.bool_false),
   9147                 as_usize | @enumToInt(Zir.Inst.Ref.zero_usize),
   9148                 as_usize | @enumToInt(Zir.Inst.Ref.one_usize),
   9149                 as_void | @enumToInt(Zir.Inst.Ref.void_value),
   9150                 => return result, // type of result is already correct
   9151 
   9152                 // Need an explicit type coercion instruction.
   9153                 else => return gz.addPlNode(.as_node, src_node, Zir.Inst.As{
   9154                     .dest_type = ty_inst,
   9155                     .operand = result,
   9156                 }),
   9157             }
   9158         },
   9159         .ptr => |ptr_inst| {
   9160             if (gz.rvalue_noresult != ptr_inst) {
   9161                 _ = try gz.addPlNode(.store_node, src_node, Zir.Inst.Bin{
   9162                     .lhs = ptr_inst,
   9163                     .rhs = result,
   9164                 });
   9165             }
   9166             return result;
   9167         },
   9168         .inferred_ptr => |alloc| {
   9169             if (gz.rvalue_noresult != alloc) {
   9170                 _ = try gz.addBin(.store_to_inferred_ptr, alloc, result);
   9171             }
   9172             return result;
   9173         },
   9174         .block_ptr => |block_scope| {
   9175             if (gz.rvalue_noresult != block_scope.rl_ptr) {
   9176                 block_scope.rvalue_rl_count += 1;
   9177                 _ = try gz.addBin(.store_to_block_ptr, block_scope.rl_ptr, result);
   9178             }
   9179             return result;
   9180         },
   9181     }
   9182 }
   9183 
   9184 /// Given an identifier token, obtain the string for it.
   9185 /// If the token uses @"" syntax, parses as a string, reports errors if applicable,
   9186 /// and allocates the result within `astgen.arena`.
   9187 /// Otherwise, returns a reference to the source code bytes directly.
   9188 /// See also `appendIdentStr` and `parseStrLit`.
   9189 fn identifierTokenString(astgen: *AstGen, token: Ast.TokenIndex) InnerError![]const u8 {
   9190     const tree = astgen.tree;
   9191     const token_tags = tree.tokens.items(.tag);
   9192     assert(token_tags[token] == .identifier);
   9193     const ident_name = tree.tokenSlice(token);
   9194     if (!mem.startsWith(u8, ident_name, "@")) {
   9195         return ident_name;
   9196     }
   9197     var buf: ArrayListUnmanaged(u8) = .{};
   9198     defer buf.deinit(astgen.gpa);
   9199     try astgen.parseStrLit(token, &buf, ident_name, 1);
   9200     const duped = try astgen.arena.dupe(u8, buf.items);
   9201     return duped;
   9202 }
   9203 
   9204 /// Given an identifier token, obtain the string for it (possibly parsing as a string
   9205 /// literal if it is @"" syntax), and append the string to `buf`.
   9206 /// See also `identifierTokenString` and `parseStrLit`.
   9207 fn appendIdentStr(
   9208     astgen: *AstGen,
   9209     token: Ast.TokenIndex,
   9210     buf: *ArrayListUnmanaged(u8),
   9211 ) InnerError!void {
   9212     const tree = astgen.tree;
   9213     const token_tags = tree.tokens.items(.tag);
   9214     assert(token_tags[token] == .identifier);
   9215     const ident_name = tree.tokenSlice(token);
   9216     if (!mem.startsWith(u8, ident_name, "@")) {
   9217         return buf.appendSlice(astgen.gpa, ident_name);
   9218     } else {
   9219         return astgen.parseStrLit(token, buf, ident_name, 1);
   9220     }
   9221 }
   9222 
   9223 /// Appends the result to `buf`.
   9224 fn parseStrLit(
   9225     astgen: *AstGen,
   9226     token: Ast.TokenIndex,
   9227     buf: *ArrayListUnmanaged(u8),
   9228     bytes: []const u8,
   9229     offset: u32,
   9230 ) InnerError!void {
   9231     const raw_string = bytes[offset..];
   9232     var buf_managed = buf.toManaged(astgen.gpa);
   9233     const result = std.zig.string_literal.parseAppend(&buf_managed, raw_string);
   9234     buf.* = buf_managed.moveToUnmanaged();
   9235     switch (try result) {
   9236         .success => return,
   9237         .failure => |err| return astgen.failWithStrLitError(err, token, bytes, offset),
   9238     }
   9239 }
   9240 
   9241 fn failWithStrLitError(astgen: *AstGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, bytes: []const u8, offset: u32) InnerError {
   9242     const raw_string = bytes[offset..];
   9243     switch (err) {
   9244         .invalid_escape_character => |bad_index| {
   9245             return astgen.failOff(
   9246                 token,
   9247                 offset + @intCast(u32, bad_index),
   9248                 "invalid escape character: '{c}'",
   9249                 .{raw_string[bad_index]},
   9250             );
   9251         },
   9252         .expected_hex_digit => |bad_index| {
   9253             return astgen.failOff(
   9254                 token,
   9255                 offset + @intCast(u32, bad_index),
   9256                 "expected hex digit, found '{c}'",
   9257                 .{raw_string[bad_index]},
   9258             );
   9259         },
   9260         .empty_unicode_escape_sequence => |bad_index| {
   9261             return astgen.failOff(
   9262                 token,
   9263                 offset + @intCast(u32, bad_index),
   9264                 "empty unicode escape sequence",
   9265                 .{},
   9266             );
   9267         },
   9268         .expected_hex_digit_or_rbrace => |bad_index| {
   9269             return astgen.failOff(
   9270                 token,
   9271                 offset + @intCast(u32, bad_index),
   9272                 "expected hex digit or '}}', found '{c}'",
   9273                 .{raw_string[bad_index]},
   9274             );
   9275         },
   9276         .invalid_unicode_codepoint => |bad_index| {
   9277             return astgen.failOff(
   9278                 token,
   9279                 offset + @intCast(u32, bad_index),
   9280                 "unicode escape does not correspond to a valid codepoint",
   9281                 .{},
   9282             );
   9283         },
   9284         .expected_lbrace => |bad_index| {
   9285             return astgen.failOff(
   9286                 token,
   9287                 offset + @intCast(u32, bad_index),
   9288                 "expected '{{', found '{c}",
   9289                 .{raw_string[bad_index]},
   9290             );
   9291         },
   9292         .expected_rbrace => |bad_index| {
   9293             return astgen.failOff(
   9294                 token,
   9295                 offset + @intCast(u32, bad_index),
   9296                 "expected '}}', found '{c}",
   9297                 .{raw_string[bad_index]},
   9298             );
   9299         },
   9300         .expected_single_quote => |bad_index| {
   9301             return astgen.failOff(
   9302                 token,
   9303                 offset + @intCast(u32, bad_index),
   9304                 "expected single quote ('), found '{c}",
   9305                 .{raw_string[bad_index]},
   9306             );
   9307         },
   9308         .invalid_character => |bad_index| {
   9309             return astgen.failOff(
   9310                 token,
   9311                 offset + @intCast(u32, bad_index),
   9312                 "invalid byte in string or character literal: '{c}'",
   9313                 .{raw_string[bad_index]},
   9314             );
   9315         },
   9316     }
   9317 }
   9318 
   9319 fn failNode(
   9320     astgen: *AstGen,
   9321     node: Ast.Node.Index,
   9322     comptime format: []const u8,
   9323     args: anytype,
   9324 ) InnerError {
   9325     return astgen.failNodeNotes(node, format, args, &[0]u32{});
   9326 }
   9327 
   9328 fn appendErrorNode(
   9329     astgen: *AstGen,
   9330     node: Ast.Node.Index,
   9331     comptime format: []const u8,
   9332     args: anytype,
   9333 ) Allocator.Error!void {
   9334     try astgen.appendErrorNodeNotes(node, format, args, &[0]u32{});
   9335 }
   9336 
   9337 fn appendErrorNodeNotes(
   9338     astgen: *AstGen,
   9339     node: Ast.Node.Index,
   9340     comptime format: []const u8,
   9341     args: anytype,
   9342     notes: []const u32,
   9343 ) Allocator.Error!void {
   9344     @setCold(true);
   9345     const string_bytes = &astgen.string_bytes;
   9346     const msg = @intCast(u32, string_bytes.items.len);
   9347     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
   9348     const notes_index: u32 = if (notes.len != 0) blk: {
   9349         const notes_start = astgen.extra.items.len;
   9350         try astgen.extra.ensureTotalCapacity(astgen.gpa, notes_start + 1 + notes.len);
   9351         astgen.extra.appendAssumeCapacity(@intCast(u32, notes.len));
   9352         astgen.extra.appendSliceAssumeCapacity(notes);
   9353         break :blk @intCast(u32, notes_start);
   9354     } else 0;
   9355     try astgen.compile_errors.append(astgen.gpa, .{
   9356         .msg = msg,
   9357         .node = node,
   9358         .token = 0,
   9359         .byte_offset = 0,
   9360         .notes = notes_index,
   9361     });
   9362 }
   9363 
   9364 fn failNodeNotes(
   9365     astgen: *AstGen,
   9366     node: Ast.Node.Index,
   9367     comptime format: []const u8,
   9368     args: anytype,
   9369     notes: []const u32,
   9370 ) InnerError {
   9371     try appendErrorNodeNotes(astgen, node, format, args, notes);
   9372     return error.AnalysisFail;
   9373 }
   9374 
   9375 fn failTok(
   9376     astgen: *AstGen,
   9377     token: Ast.TokenIndex,
   9378     comptime format: []const u8,
   9379     args: anytype,
   9380 ) InnerError {
   9381     return astgen.failTokNotes(token, format, args, &[0]u32{});
   9382 }
   9383 
   9384 fn appendErrorTok(
   9385     astgen: *AstGen,
   9386     token: Ast.TokenIndex,
   9387     comptime format: []const u8,
   9388     args: anytype,
   9389 ) !void {
   9390     try astgen.appendErrorTokNotes(token, format, args, &[0]u32{});
   9391 }
   9392 
   9393 fn failTokNotes(
   9394     astgen: *AstGen,
   9395     token: Ast.TokenIndex,
   9396     comptime format: []const u8,
   9397     args: anytype,
   9398     notes: []const u32,
   9399 ) InnerError {
   9400     try appendErrorTokNotes(astgen, token, format, args, notes);
   9401     return error.AnalysisFail;
   9402 }
   9403 
   9404 fn appendErrorTokNotes(
   9405     astgen: *AstGen,
   9406     token: Ast.TokenIndex,
   9407     comptime format: []const u8,
   9408     args: anytype,
   9409     notes: []const u32,
   9410 ) !void {
   9411     @setCold(true);
   9412     const string_bytes = &astgen.string_bytes;
   9413     const msg = @intCast(u32, string_bytes.items.len);
   9414     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
   9415     const notes_index: u32 = if (notes.len != 0) blk: {
   9416         const notes_start = astgen.extra.items.len;
   9417         try astgen.extra.ensureTotalCapacity(astgen.gpa, notes_start + 1 + notes.len);
   9418         astgen.extra.appendAssumeCapacity(@intCast(u32, notes.len));
   9419         astgen.extra.appendSliceAssumeCapacity(notes);
   9420         break :blk @intCast(u32, notes_start);
   9421     } else 0;
   9422     try astgen.compile_errors.append(astgen.gpa, .{
   9423         .msg = msg,
   9424         .node = 0,
   9425         .token = token,
   9426         .byte_offset = 0,
   9427         .notes = notes_index,
   9428     });
   9429 }
   9430 
   9431 /// Same as `fail`, except given an absolute byte offset.
   9432 fn failOff(
   9433     astgen: *AstGen,
   9434     token: Ast.TokenIndex,
   9435     byte_offset: u32,
   9436     comptime format: []const u8,
   9437     args: anytype,
   9438 ) InnerError {
   9439     try appendErrorOff(astgen, token, byte_offset, format, args);
   9440     return error.AnalysisFail;
   9441 }
   9442 
   9443 fn appendErrorOff(
   9444     astgen: *AstGen,
   9445     token: Ast.TokenIndex,
   9446     byte_offset: u32,
   9447     comptime format: []const u8,
   9448     args: anytype,
   9449 ) Allocator.Error!void {
   9450     @setCold(true);
   9451     const string_bytes = &astgen.string_bytes;
   9452     const msg = @intCast(u32, string_bytes.items.len);
   9453     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
   9454     try astgen.compile_errors.append(astgen.gpa, .{
   9455         .msg = msg,
   9456         .node = 0,
   9457         .token = token,
   9458         .byte_offset = byte_offset,
   9459         .notes = 0,
   9460     });
   9461 }
   9462 
   9463 fn errNoteTok(
   9464     astgen: *AstGen,
   9465     token: Ast.TokenIndex,
   9466     comptime format: []const u8,
   9467     args: anytype,
   9468 ) Allocator.Error!u32 {
   9469     @setCold(true);
   9470     const string_bytes = &astgen.string_bytes;
   9471     const msg = @intCast(u32, string_bytes.items.len);
   9472     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
   9473     return astgen.addExtra(Zir.Inst.CompileErrors.Item{
   9474         .msg = msg,
   9475         .node = 0,
   9476         .token = token,
   9477         .byte_offset = 0,
   9478         .notes = 0,
   9479     });
   9480 }
   9481 
   9482 fn errNoteNode(
   9483     astgen: *AstGen,
   9484     node: Ast.Node.Index,
   9485     comptime format: []const u8,
   9486     args: anytype,
   9487 ) Allocator.Error!u32 {
   9488     @setCold(true);
   9489     const string_bytes = &astgen.string_bytes;
   9490     const msg = @intCast(u32, string_bytes.items.len);
   9491     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
   9492     return astgen.addExtra(Zir.Inst.CompileErrors.Item{
   9493         .msg = msg,
   9494         .node = node,
   9495         .token = 0,
   9496         .byte_offset = 0,
   9497         .notes = 0,
   9498     });
   9499 }
   9500 
   9501 fn identAsString(astgen: *AstGen, ident_token: Ast.TokenIndex) !u32 {
   9502     const gpa = astgen.gpa;
   9503     const string_bytes = &astgen.string_bytes;
   9504     const str_index = @intCast(u32, string_bytes.items.len);
   9505     try astgen.appendIdentStr(ident_token, string_bytes);
   9506     const key = string_bytes.items[str_index..];
   9507     const gop = try astgen.string_table.getOrPutContextAdapted(gpa, @as([]const u8, key), StringIndexAdapter{
   9508         .bytes = string_bytes,
   9509     }, StringIndexContext{
   9510         .bytes = string_bytes,
   9511     });
   9512     if (gop.found_existing) {
   9513         string_bytes.shrinkRetainingCapacity(str_index);
   9514         return gop.key_ptr.*;
   9515     } else {
   9516         gop.key_ptr.* = str_index;
   9517         try string_bytes.append(gpa, 0);
   9518         return str_index;
   9519     }
   9520 }
   9521 
   9522 /// Adds a doc comment block to `string_bytes` by walking backwards from `end_token`.
   9523 /// `end_token` must point at the first token after the last doc coment line.
   9524 /// Returns 0 if no doc comment is present.
   9525 fn docCommentAsString(astgen: *AstGen, end_token: Ast.TokenIndex) !u32 {
   9526     if (end_token == 0) return @as(u32, 0);
   9527 
   9528     const token_tags = astgen.tree.tokens.items(.tag);
   9529 
   9530     var tok = end_token - 1;
   9531     while (token_tags[tok] == .doc_comment) {
   9532         if (tok == 0) break;
   9533         tok -= 1;
   9534     } else {
   9535         tok += 1;
   9536     }
   9537     return docCommentAsStringFromFirst(astgen, end_token, tok);
   9538 }
   9539 
   9540 /// end_token must be > the index of the last doc comment.
   9541 fn docCommentAsStringFromFirst(
   9542     astgen: *AstGen,
   9543     end_token: Ast.TokenIndex,
   9544     start_token: Ast.TokenIndex,
   9545 ) !u32 {
   9546     if (start_token == end_token) return 0;
   9547 
   9548     const gpa = astgen.gpa;
   9549     const string_bytes = &astgen.string_bytes;
   9550     const str_index = @intCast(u32, string_bytes.items.len);
   9551     const token_starts = astgen.tree.tokens.items(.start);
   9552     const token_tags = astgen.tree.tokens.items(.tag);
   9553 
   9554     const total_bytes = token_starts[end_token] - token_starts[start_token];
   9555     try string_bytes.ensureUnusedCapacity(gpa, total_bytes);
   9556 
   9557     var current_token = start_token;
   9558     while (current_token < end_token) : (current_token += 1) {
   9559         switch (token_tags[current_token]) {
   9560             .doc_comment => {
   9561                 const tok_bytes = astgen.tree.tokenSlice(current_token)[3..];
   9562                 string_bytes.appendSliceAssumeCapacity(tok_bytes);
   9563                 if (current_token != end_token - 1) {
   9564                     string_bytes.appendAssumeCapacity('\n');
   9565                 }
   9566             },
   9567             else => break,
   9568         }
   9569     }
   9570 
   9571     const key = string_bytes.items[str_index..];
   9572     const gop = try astgen.string_table.getOrPutContextAdapted(gpa, @as([]const u8, key), StringIndexAdapter{
   9573         .bytes = string_bytes,
   9574     }, StringIndexContext{
   9575         .bytes = string_bytes,
   9576     });
   9577 
   9578     if (gop.found_existing) {
   9579         string_bytes.shrinkRetainingCapacity(str_index);
   9580         return gop.key_ptr.*;
   9581     } else {
   9582         gop.key_ptr.* = str_index;
   9583         try string_bytes.append(gpa, 0);
   9584         return str_index;
   9585     }
   9586 }
   9587 
   9588 const IndexSlice = struct { index: u32, len: u32 };
   9589 
   9590 fn strLitAsString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !IndexSlice {
   9591     const gpa = astgen.gpa;
   9592     const string_bytes = &astgen.string_bytes;
   9593     const str_index = @intCast(u32, string_bytes.items.len);
   9594     const token_bytes = astgen.tree.tokenSlice(str_lit_token);
   9595     try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0);
   9596     const key = string_bytes.items[str_index..];
   9597     const gop = try astgen.string_table.getOrPutContextAdapted(gpa, @as([]const u8, key), StringIndexAdapter{
   9598         .bytes = string_bytes,
   9599     }, StringIndexContext{
   9600         .bytes = string_bytes,
   9601     });
   9602     if (gop.found_existing) {
   9603         string_bytes.shrinkRetainingCapacity(str_index);
   9604         return IndexSlice{
   9605             .index = gop.key_ptr.*,
   9606             .len = @intCast(u32, key.len),
   9607         };
   9608     } else {
   9609         gop.key_ptr.* = str_index;
   9610         // Still need a null byte because we are using the same table
   9611         // to lookup null terminated strings, so if we get a match, it has to
   9612         // be null terminated for that to work.
   9613         try string_bytes.append(gpa, 0);
   9614         return IndexSlice{
   9615             .index = str_index,
   9616             .len = @intCast(u32, key.len),
   9617         };
   9618     }
   9619 }
   9620 
   9621 fn strLitNodeAsString(astgen: *AstGen, node: Ast.Node.Index) !IndexSlice {
   9622     const tree = astgen.tree;
   9623     const node_datas = tree.nodes.items(.data);
   9624 
   9625     const start = node_datas[node].lhs;
   9626     const end = node_datas[node].rhs;
   9627 
   9628     const gpa = astgen.gpa;
   9629     const string_bytes = &astgen.string_bytes;
   9630     const str_index = string_bytes.items.len;
   9631 
   9632     // First line: do not append a newline.
   9633     var tok_i = start;
   9634     {
   9635         const slice = tree.tokenSlice(tok_i);
   9636         const line_bytes = slice[2 .. slice.len - 1];
   9637         try string_bytes.appendSlice(gpa, line_bytes);
   9638         tok_i += 1;
   9639     }
   9640     // Following lines: each line prepends a newline.
   9641     while (tok_i <= end) : (tok_i += 1) {
   9642         const slice = tree.tokenSlice(tok_i);
   9643         const line_bytes = slice[2 .. slice.len - 1];
   9644         try string_bytes.ensureUnusedCapacity(gpa, line_bytes.len + 1);
   9645         string_bytes.appendAssumeCapacity('\n');
   9646         string_bytes.appendSliceAssumeCapacity(line_bytes);
   9647     }
   9648     const len = string_bytes.items.len - str_index;
   9649     try string_bytes.append(gpa, 0);
   9650     return IndexSlice{
   9651         .index = @intCast(u32, str_index),
   9652         .len = @intCast(u32, len),
   9653     };
   9654 }
   9655 
   9656 fn testNameString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !u32 {
   9657     const gpa = astgen.gpa;
   9658     const string_bytes = &astgen.string_bytes;
   9659     const str_index = @intCast(u32, string_bytes.items.len);
   9660     const token_bytes = astgen.tree.tokenSlice(str_lit_token);
   9661     try string_bytes.append(gpa, 0); // Indicates this is a test.
   9662     try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0);
   9663     try string_bytes.append(gpa, 0);
   9664     return str_index;
   9665 }
   9666 
   9667 const Scope = struct {
   9668     tag: Tag,
   9669 
   9670     fn cast(base: *Scope, comptime T: type) ?*T {
   9671         if (T == Defer) {
   9672             switch (base.tag) {
   9673                 .defer_normal, .defer_error => return @fieldParentPtr(T, "base", base),
   9674                 else => return null,
   9675             }
   9676         }
   9677         if (base.tag != T.base_tag)
   9678             return null;
   9679 
   9680         return @fieldParentPtr(T, "base", base);
   9681     }
   9682 
   9683     fn parent(base: *Scope) ?*Scope {
   9684         return switch (base.tag) {
   9685             .gen_zir => base.cast(GenZir).?.parent,
   9686             .local_val => base.cast(LocalVal).?.parent,
   9687             .local_ptr => base.cast(LocalPtr).?.parent,
   9688             .defer_normal, .defer_error => base.cast(Defer).?.parent,
   9689             .namespace => base.cast(Namespace).?.parent,
   9690             .top => null,
   9691         };
   9692     }
   9693 
   9694     const Tag = enum {
   9695         gen_zir,
   9696         local_val,
   9697         local_ptr,
   9698         defer_normal,
   9699         defer_error,
   9700         namespace,
   9701         top,
   9702     };
   9703 
   9704     /// The category of identifier. These tag names are user-visible in compile errors.
   9705     const IdCat = enum {
   9706         @"function parameter",
   9707         @"local constant",
   9708         @"local variable",
   9709         @"loop index capture",
   9710         @"capture",
   9711     };
   9712 
   9713     /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.
   9714     /// This structure lives as long as the AST generation of the Block
   9715     /// node that contains the variable.
   9716     const LocalVal = struct {
   9717         const base_tag: Tag = .local_val;
   9718         base: Scope = Scope{ .tag = base_tag },
   9719         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
   9720         parent: *Scope,
   9721         gen_zir: *GenZir,
   9722         inst: Zir.Inst.Ref,
   9723         /// Source location of the corresponding variable declaration.
   9724         token_src: Ast.TokenIndex,
   9725         /// String table index.
   9726         name: u32,
   9727         id_cat: IdCat,
   9728         /// Track whether the name has been referenced.
   9729         used: bool = false,
   9730     };
   9731 
   9732     /// This could be a `const` or `var` local. It has a pointer instead of a value.
   9733     /// This structure lives as long as the AST generation of the Block
   9734     /// node that contains the variable.
   9735     const LocalPtr = struct {
   9736         const base_tag: Tag = .local_ptr;
   9737         base: Scope = Scope{ .tag = base_tag },
   9738         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
   9739         parent: *Scope,
   9740         gen_zir: *GenZir,
   9741         ptr: Zir.Inst.Ref,
   9742         /// Source location of the corresponding variable declaration.
   9743         token_src: Ast.TokenIndex,
   9744         /// String table index.
   9745         name: u32,
   9746         id_cat: IdCat,
   9747         /// true means we find out during Sema whether the value is comptime.
   9748         /// false means it is already known at AstGen the value is runtime-known.
   9749         maybe_comptime: bool,
   9750         /// Track whether the name has been referenced.
   9751         used: bool = false,
   9752     };
   9753 
   9754     const Defer = struct {
   9755         base: Scope,
   9756         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
   9757         parent: *Scope,
   9758         defer_node: Ast.Node.Index,
   9759         source_offset: u32,
   9760         source_line: u32,
   9761         source_column: u32,
   9762     };
   9763 
   9764     /// Represents a global scope that has any number of declarations in it.
   9765     /// Each declaration has this as the parent scope.
   9766     const Namespace = struct {
   9767         const base_tag: Tag = .namespace;
   9768         base: Scope = Scope{ .tag = base_tag },
   9769 
   9770         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
   9771         parent: *Scope,
   9772         /// Maps string table index to the source location of declaration,
   9773         /// for the purposes of reporting name shadowing compile errors.
   9774         decls: std.AutoHashMapUnmanaged(u32, Ast.Node.Index) = .{},
   9775         node: Ast.Node.Index,
   9776         inst: Zir.Inst.Index,
   9777 
   9778         /// The astgen scope containing this namespace.
   9779         /// Only valid during astgen.
   9780         declaring_gz: ?*GenZir,
   9781 
   9782         /// Map from the raw captured value to the instruction
   9783         /// ref of the capture for decls in this namespace
   9784         captures: std.AutoArrayHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},
   9785 
   9786         pub fn deinit(self: *Namespace, gpa: Allocator) void {
   9787             self.decls.deinit(gpa);
   9788             self.captures.deinit(gpa);
   9789             self.* = undefined;
   9790         }
   9791     };
   9792 
   9793     const Top = struct {
   9794         const base_tag: Scope.Tag = .top;
   9795         base: Scope = Scope{ .tag = base_tag },
   9796     };
   9797 };
   9798 
   9799 /// This is a temporary structure; references to it are valid only
   9800 /// while constructing a `Zir`.
   9801 const GenZir = struct {
   9802     const base_tag: Scope.Tag = .gen_zir;
   9803     base: Scope = Scope{ .tag = base_tag },
   9804     force_comptime: bool,
   9805     /// This is set to true for inline loops; false otherwise.
   9806     is_inline: bool = false,
   9807     in_defer: bool,
   9808     c_import: bool = false,
   9809     /// How decls created in this scope should be named.
   9810     anon_name_strategy: Zir.Inst.NameStrategy = .anon,
   9811     /// The containing decl AST node.
   9812     decl_node_index: Ast.Node.Index,
   9813     /// The containing decl line index, absolute.
   9814     decl_line: u32,
   9815     /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
   9816     parent: *Scope,
   9817     /// All `GenZir` scopes for the same ZIR share this.
   9818     astgen: *AstGen,
   9819     /// Keeps track of the list of instructions in this scope. Possibly shared.
   9820     /// Indexes to instructions in `astgen`.
   9821     instructions: *ArrayListUnmanaged(Zir.Inst.Index),
   9822     /// A sub-block may share its instructions ArrayList with containing GenZir,
   9823     /// if use is strictly nested. This saves prior size of list for unstacking.
   9824     instructions_top: usize,
   9825     label: ?Label = null,
   9826     break_block: Zir.Inst.Index = 0,
   9827     continue_block: Zir.Inst.Index = 0,
   9828     /// Only valid when setBreakResultLoc is called.
   9829     break_result_loc: AstGen.ResultLoc = undefined,
   9830     /// When a block has a pointer result location, here it is.
   9831     rl_ptr: Zir.Inst.Ref = .none,
   9832     /// When a block has a type result location, here it is.
   9833     rl_ty_inst: Zir.Inst.Ref = .none,
   9834     rvalue_noresult: Zir.Inst.Ref = .none,
   9835     /// Keeps track of how many branches of a block did not actually
   9836     /// consume the result location. astgen uses this to figure out
   9837     /// whether to rely on break instructions or writing to the result
   9838     /// pointer for the result instruction.
   9839     rvalue_rl_count: usize = 0,
   9840     /// Keeps track of how many break instructions there are. When astgen is finished
   9841     /// with a block, it can check this against rvalue_rl_count to find out whether
   9842     /// the break instructions should be downgraded to break_void.
   9843     break_count: usize = 0,
   9844     /// Tracks `break :foo bar` instructions so they can possibly be elided later if
   9845     /// the labeled block ends up not needing a result location pointer.
   9846     labeled_breaks: ArrayListUnmanaged(struct { br: Zir.Inst.Index, search: Zir.Inst.Index }) = .{},
   9847 
   9848     suspend_node: Ast.Node.Index = 0,
   9849     nosuspend_node: Ast.Node.Index = 0,
   9850 
   9851     /// Namespace members are lazy.  When executing a decl within a namespace,
   9852     /// any references to external instructions need to be treated specially.
   9853     /// This list tracks those references.  See also .closure_capture and .closure_get.
   9854     /// Keys are the raw instruction index, values are the closure_capture instruction.
   9855     captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},
   9856 
   9857     const unstacked_top = std.math.maxInt(usize);
   9858     /// Call unstack before adding any new instructions to containing GenZir.
   9859     fn unstack(self: *GenZir) void {
   9860         if (self.instructions_top != unstacked_top) {
   9861             self.instructions.items.len = self.instructions_top;
   9862             self.instructions_top = unstacked_top;
   9863         }
   9864     }
   9865 
   9866     fn isEmpty(self: *const GenZir) bool {
   9867         return (self.instructions_top == unstacked_top) or
   9868             (self.instructions.items.len == self.instructions_top);
   9869     }
   9870 
   9871     fn instructionsSlice(self: *const GenZir) []Zir.Inst.Index {
   9872         return if (self.instructions_top == unstacked_top)
   9873             &[0]Zir.Inst.Index{}
   9874         else
   9875             self.instructions.items[self.instructions_top..];
   9876     }
   9877 
   9878     fn instructionsSliceUpto(self: *const GenZir, stacked_gz: *GenZir) []Zir.Inst.Index {
   9879         return if (self.instructions_top == unstacked_top)
   9880             &[0]Zir.Inst.Index{}
   9881         else if (self.instructions == stacked_gz.instructions and stacked_gz.instructions_top != unstacked_top)
   9882             self.instructions.items[self.instructions_top..stacked_gz.instructions_top]
   9883         else
   9884             self.instructions.items[self.instructions_top..];
   9885     }
   9886 
   9887     fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {
   9888         return .{
   9889             .force_comptime = gz.force_comptime,
   9890             .in_defer = gz.in_defer,
   9891             .c_import = gz.c_import,
   9892             .decl_node_index = gz.decl_node_index,
   9893             .decl_line = gz.decl_line,
   9894             .parent = scope,
   9895             .rl_ty_inst = gz.rl_ty_inst,
   9896             .astgen = gz.astgen,
   9897             .suspend_node = gz.suspend_node,
   9898             .nosuspend_node = gz.nosuspend_node,
   9899             .instructions = gz.instructions,
   9900             .instructions_top = gz.instructions.items.len,
   9901         };
   9902     }
   9903 
   9904     fn makeCoercionScope(
   9905         parent_gz: *GenZir,
   9906         scope: *Scope,
   9907         dest_type: Zir.Inst.Ref,
   9908         result_ptr: Zir.Inst.Ref,
   9909         src_node: Ast.Node.Index,
   9910     ) !GenZir {
   9911         // Detect whether this expr() call goes into rvalue() to store the result into the
   9912         // result location. If it does, elide the coerce_result_ptr instruction
   9913         // as well as the store instruction, instead passing the result as an rvalue.
   9914         var as_scope = parent_gz.makeSubBlock(scope);
   9915         errdefer as_scope.unstack();
   9916         as_scope.rl_ptr = try as_scope.addPlNode(.coerce_result_ptr, src_node, Zir.Inst.Bin{ .lhs = dest_type, .rhs = result_ptr });
   9917 
   9918         // `rl_ty_inst` needs to be set in case the stores to `rl_ptr` are eliminated.
   9919         as_scope.rl_ty_inst = dest_type;
   9920 
   9921         return as_scope;
   9922     }
   9923 
   9924     /// Assumes `as_scope` is stacked immediately on top of `parent_gz`. Unstacks `as_scope`.
   9925     fn finishCoercion(
   9926         as_scope: *GenZir,
   9927         parent_gz: *GenZir,
   9928         rl: ResultLoc,
   9929         src_node: Ast.Node.Index,
   9930         result: Zir.Inst.Ref,
   9931         dest_type: Zir.Inst.Ref,
   9932     ) InnerError!Zir.Inst.Ref {
   9933         assert(as_scope.instructions == parent_gz.instructions);
   9934         const astgen = as_scope.astgen;
   9935         if (as_scope.rvalue_rl_count == 1) {
   9936             // Busted! This expression didn't actually need a pointer.
   9937             const zir_tags = astgen.instructions.items(.tag);
   9938             const zir_datas = astgen.instructions.items(.data);
   9939             var src: usize = as_scope.instructions_top;
   9940             var dst: usize = src;
   9941             while (src < as_scope.instructions.items.len) : (src += 1) {
   9942                 const src_inst = as_scope.instructions.items[src];
   9943                 if (indexToRef(src_inst) == as_scope.rl_ptr) continue;
   9944                 if (zir_tags[src_inst] == .store_to_block_ptr) {
   9945                     if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue;
   9946                 }
   9947                 as_scope.instructions.items[dst] = src_inst;
   9948                 dst += 1;
   9949             }
   9950             parent_gz.instructions.items.len -= src - dst;
   9951             as_scope.instructions_top = GenZir.unstacked_top;
   9952             // as_scope now unstacked, can add new instructions to parent_gz
   9953             const casted_result = try parent_gz.addBin(.as, dest_type, result);
   9954             return rvalue(parent_gz, rl, casted_result, src_node);
   9955         } else {
   9956             // implicitly move all as_scope instructions to parent_gz
   9957             as_scope.instructions_top = GenZir.unstacked_top;
   9958             return result;
   9959         }
   9960     }
   9961 
   9962     const Label = struct {
   9963         token: Ast.TokenIndex,
   9964         block_inst: Zir.Inst.Index,
   9965         used: bool = false,
   9966     };
   9967 
   9968     /// Assumes nothing stacked on `gz`.
   9969     fn endsWithNoReturn(gz: GenZir) bool {
   9970         if (gz.isEmpty()) return false;
   9971         const tags = gz.astgen.instructions.items(.tag);
   9972         const last_inst = gz.instructions.items[gz.instructions.items.len - 1];
   9973         return tags[last_inst].isNoReturn();
   9974     }
   9975 
   9976     /// TODO all uses of this should be replaced with uses of `endsWithNoReturn`.
   9977     fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool {
   9978         if (inst_ref == .unreachable_value) return true;
   9979         if (refToIndex(inst_ref)) |inst_index| {
   9980             return gz.astgen.instructions.items(.tag)[inst_index].isNoReturn();
   9981         }
   9982         return false;
   9983     }
   9984 
   9985     fn nodeIndexToRelative(gz: GenZir, node_index: Ast.Node.Index) i32 {
   9986         return @bitCast(i32, node_index) - @bitCast(i32, gz.decl_node_index);
   9987     }
   9988 
   9989     fn tokenIndexToRelative(gz: GenZir, token: Ast.TokenIndex) u32 {
   9990         return token - gz.srcToken();
   9991     }
   9992 
   9993     fn srcToken(gz: GenZir) Ast.TokenIndex {
   9994         return gz.astgen.tree.firstToken(gz.decl_node_index);
   9995     }
   9996 
   9997     fn setBreakResultLoc(gz: *GenZir, parent_rl: AstGen.ResultLoc) void {
   9998         // Depending on whether the result location is a pointer or value, different
   9999         // ZIR needs to be generated. In the former case we rely on storing to the
  10000         // pointer to communicate the result, and use breakvoid; in the latter case
  10001         // the block break instructions will have the result values.
  10002         // One more complication: when the result location is a pointer, we detect
  10003         // the scenario where the result location is not consumed. In this case
  10004         // we emit ZIR for the block break instructions to have the result values,
  10005         // and then rvalue() on that to pass the value to the result location.
  10006         switch (parent_rl) {
  10007             .ty, .coerced_ty => |ty_inst| {
  10008                 gz.rl_ty_inst = ty_inst;
  10009                 gz.break_result_loc = parent_rl;
  10010             },
  10011 
  10012             .discard, .none, .ptr, .ref => {
  10013                 gz.rl_ty_inst = .none;
  10014                 gz.break_result_loc = parent_rl;
  10015             },
  10016 
  10017             .inferred_ptr => |ptr| {
  10018                 gz.rl_ty_inst = .none;
  10019                 gz.rl_ptr = ptr;
  10020                 gz.break_result_loc = .{ .block_ptr = gz };
  10021             },
  10022 
  10023             .block_ptr => |parent_block_scope| {
  10024                 gz.rl_ty_inst = parent_block_scope.rl_ty_inst;
  10025                 gz.rl_ptr = parent_block_scope.rl_ptr;
  10026                 gz.break_result_loc = .{ .block_ptr = gz };
  10027             },
  10028         }
  10029     }
  10030 
  10031     /// Assumes nothing stacked on `gz`. Unstacks `gz`.
  10032     fn setBoolBrBody(gz: *GenZir, inst: Zir.Inst.Index) !void {
  10033         const astgen = gz.astgen;
  10034         const gpa = astgen.gpa;
  10035         const body = gz.instructionsSlice();
  10036         const body_len = astgen.countBodyLenAfterFixups(body);
  10037         try astgen.extra.ensureUnusedCapacity(
  10038             gpa,
  10039             @typeInfo(Zir.Inst.Block).Struct.fields.len + body_len,
  10040         );
  10041         const zir_datas = astgen.instructions.items(.data);
  10042         zir_datas[inst].bool_br.payload_index = astgen.addExtraAssumeCapacity(
  10043             Zir.Inst.Block{ .body_len = body_len },
  10044         );
  10045         astgen.appendBodyWithFixups(body);
  10046         gz.unstack();
  10047     }
  10048 
  10049     /// Assumes nothing stacked on `gz`. Unstacks `gz`.
  10050     fn setBlockBody(gz: *GenZir, inst: Zir.Inst.Index) !void {
  10051         const astgen = gz.astgen;
  10052         const gpa = astgen.gpa;
  10053         const body = gz.instructionsSlice();
  10054         const body_len = astgen.countBodyLenAfterFixups(body);
  10055         try astgen.extra.ensureUnusedCapacity(
  10056             gpa,
  10057             @typeInfo(Zir.Inst.Block).Struct.fields.len + body_len,
  10058         );
  10059         const zir_datas = astgen.instructions.items(.data);
  10060         zir_datas[inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(
  10061             Zir.Inst.Block{ .body_len = body_len },
  10062         );
  10063         astgen.appendBodyWithFixups(body);
  10064         gz.unstack();
  10065     }
  10066 
  10067     /// Assumes nothing stacked on `gz`. Unstacks `gz`.
  10068     fn setTryBody(gz: *GenZir, inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !void {
  10069         const astgen = gz.astgen;
  10070         const gpa = astgen.gpa;
  10071         const body = gz.instructionsSlice();
  10072         const body_len = astgen.countBodyLenAfterFixups(body);
  10073         try astgen.extra.ensureUnusedCapacity(
  10074             gpa,
  10075             @typeInfo(Zir.Inst.Try).Struct.fields.len + body_len,
  10076         );
  10077         const zir_datas = astgen.instructions.items(.data);
  10078         zir_datas[inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(
  10079             Zir.Inst.Try{
  10080                 .operand = operand,
  10081                 .body_len = body_len,
  10082             },
  10083         );
  10084         astgen.appendBodyWithFixups(body);
  10085         gz.unstack();
  10086     }
  10087 
  10088     /// Must be called with the following stack set up:
  10089     ///  * gz (bottom)
  10090     ///  * align_gz
  10091     ///  * addrspace_gz
  10092     ///  * section_gz
  10093     ///  * cc_gz
  10094     ///  * ret_gz
  10095     ///  * body_gz (top)
  10096     /// Unstacks all of those except for `gz`.
  10097     fn addFunc(gz: *GenZir, args: struct {
  10098         src_node: Ast.Node.Index,
  10099         lbrace_line: u32 = 0,
  10100         lbrace_column: u32 = 0,
  10101         param_block: Zir.Inst.Index,
  10102 
  10103         align_gz: ?*GenZir,
  10104         addrspace_gz: ?*GenZir,
  10105         section_gz: ?*GenZir,
  10106         cc_gz: ?*GenZir,
  10107         ret_gz: ?*GenZir,
  10108         body_gz: ?*GenZir,
  10109 
  10110         align_ref: Zir.Inst.Ref,
  10111         addrspace_ref: Zir.Inst.Ref,
  10112         section_ref: Zir.Inst.Ref,
  10113         cc_ref: Zir.Inst.Ref,
  10114         ret_ref: Zir.Inst.Ref,
  10115 
  10116         lib_name: u32,
  10117         noalias_bits: u32,
  10118         is_var_args: bool,
  10119         is_inferred_error: bool,
  10120         is_test: bool,
  10121         is_extern: bool,
  10122     }) !Zir.Inst.Ref {
  10123         assert(args.src_node != 0);
  10124         const astgen = gz.astgen;
  10125         const gpa = astgen.gpa;
  10126         const ret_ref = if (args.ret_ref == .void_type) .none else args.ret_ref;
  10127         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  10128 
  10129         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10130 
  10131         var body: []Zir.Inst.Index = &[0]Zir.Inst.Index{};
  10132         var ret_body: []Zir.Inst.Index = &[0]Zir.Inst.Index{};
  10133         var src_locs_buffer: [3]u32 = undefined;
  10134         var src_locs: []u32 = src_locs_buffer[0..0];
  10135         if (args.body_gz) |body_gz| {
  10136             const tree = astgen.tree;
  10137             const node_tags = tree.nodes.items(.tag);
  10138             const node_datas = tree.nodes.items(.data);
  10139             const token_starts = tree.tokens.items(.start);
  10140             const fn_decl = args.src_node;
  10141             assert(node_tags[fn_decl] == .fn_decl or node_tags[fn_decl] == .test_decl);
  10142             const block = node_datas[fn_decl].rhs;
  10143             const rbrace_start = token_starts[tree.lastToken(block)];
  10144             astgen.advanceSourceCursor(rbrace_start);
  10145             const rbrace_line = @intCast(u32, astgen.source_line - gz.decl_line);
  10146             const rbrace_column = @intCast(u32, astgen.source_column);
  10147 
  10148             const columns = args.lbrace_column | (rbrace_column << 16);
  10149             src_locs_buffer[0] = args.lbrace_line;
  10150             src_locs_buffer[1] = rbrace_line;
  10151             src_locs_buffer[2] = columns;
  10152             src_locs = &src_locs_buffer;
  10153 
  10154             body = body_gz.instructionsSlice();
  10155             if (args.ret_gz) |ret_gz|
  10156                 ret_body = ret_gz.instructionsSliceUpto(body_gz);
  10157         } else {
  10158             if (args.ret_gz) |ret_gz|
  10159                 ret_body = ret_gz.instructionsSlice();
  10160         }
  10161         const body_len = astgen.countBodyLenAfterFixups(body);
  10162 
  10163         if (args.cc_ref != .none or args.lib_name != 0 or
  10164             args.is_var_args or args.is_test or args.is_extern or
  10165             args.align_ref != .none or args.section_ref != .none or
  10166             args.addrspace_ref != .none or args.noalias_bits != 0)
  10167         {
  10168             var align_body: []Zir.Inst.Index = &.{};
  10169             var addrspace_body: []Zir.Inst.Index = &.{};
  10170             var section_body: []Zir.Inst.Index = &.{};
  10171             var cc_body: []Zir.Inst.Index = &.{};
  10172             if (args.ret_gz != null) {
  10173                 align_body = args.align_gz.?.instructionsSliceUpto(args.addrspace_gz.?);
  10174                 addrspace_body = args.addrspace_gz.?.instructionsSliceUpto(args.section_gz.?);
  10175                 section_body = args.section_gz.?.instructionsSliceUpto(args.cc_gz.?);
  10176                 cc_body = args.cc_gz.?.instructionsSliceUpto(args.ret_gz.?);
  10177             }
  10178 
  10179             try astgen.extra.ensureUnusedCapacity(
  10180                 gpa,
  10181                 @typeInfo(Zir.Inst.FuncFancy).Struct.fields.len +
  10182                     fancyFnExprExtraLen(align_body, args.align_ref) +
  10183                     fancyFnExprExtraLen(addrspace_body, args.addrspace_ref) +
  10184                     fancyFnExprExtraLen(section_body, args.section_ref) +
  10185                     fancyFnExprExtraLen(cc_body, args.cc_ref) +
  10186                     fancyFnExprExtraLen(ret_body, ret_ref) +
  10187                     body_len + src_locs.len +
  10188                     @boolToInt(args.lib_name != 0) +
  10189                     @boolToInt(args.noalias_bits != 0),
  10190             );
  10191             const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.FuncFancy{
  10192                 .param_block = args.param_block,
  10193                 .body_len = body_len,
  10194                 .bits = .{
  10195                     .is_var_args = args.is_var_args,
  10196                     .is_inferred_error = args.is_inferred_error,
  10197                     .is_test = args.is_test,
  10198                     .is_extern = args.is_extern,
  10199                     .has_lib_name = args.lib_name != 0,
  10200                     .has_any_noalias = args.noalias_bits != 0,
  10201 
  10202                     .has_align_ref = args.align_ref != .none,
  10203                     .has_addrspace_ref = args.addrspace_ref != .none,
  10204                     .has_section_ref = args.section_ref != .none,
  10205                     .has_cc_ref = args.cc_ref != .none,
  10206                     .has_ret_ty_ref = ret_ref != .none,
  10207 
  10208                     .has_align_body = align_body.len != 0,
  10209                     .has_addrspace_body = addrspace_body.len != 0,
  10210                     .has_section_body = section_body.len != 0,
  10211                     .has_cc_body = cc_body.len != 0,
  10212                     .has_ret_ty_body = ret_body.len != 0,
  10213                 },
  10214             });
  10215             if (args.lib_name != 0) {
  10216                 astgen.extra.appendAssumeCapacity(args.lib_name);
  10217             }
  10218 
  10219             const zir_datas = astgen.instructions.items(.data);
  10220             if (align_body.len != 0) {
  10221                 astgen.extra.appendAssumeCapacity(@intCast(u32, align_body.len));
  10222                 astgen.extra.appendSliceAssumeCapacity(align_body);
  10223                 zir_datas[align_body[align_body.len - 1]].@"break".block_inst = new_index;
  10224             } else if (args.align_ref != .none) {
  10225                 astgen.extra.appendAssumeCapacity(@enumToInt(args.align_ref));
  10226             }
  10227             if (addrspace_body.len != 0) {
  10228                 astgen.extra.appendAssumeCapacity(@intCast(u32, addrspace_body.len));
  10229                 astgen.extra.appendSliceAssumeCapacity(addrspace_body);
  10230                 zir_datas[addrspace_body[addrspace_body.len - 1]].@"break".block_inst = new_index;
  10231             } else if (args.addrspace_ref != .none) {
  10232                 astgen.extra.appendAssumeCapacity(@enumToInt(args.addrspace_ref));
  10233             }
  10234             if (section_body.len != 0) {
  10235                 astgen.extra.appendAssumeCapacity(@intCast(u32, section_body.len));
  10236                 astgen.extra.appendSliceAssumeCapacity(section_body);
  10237                 zir_datas[section_body[section_body.len - 1]].@"break".block_inst = new_index;
  10238             } else if (args.section_ref != .none) {
  10239                 astgen.extra.appendAssumeCapacity(@enumToInt(args.section_ref));
  10240             }
  10241             if (cc_body.len != 0) {
  10242                 astgen.extra.appendAssumeCapacity(@intCast(u32, cc_body.len));
  10243                 astgen.extra.appendSliceAssumeCapacity(cc_body);
  10244                 zir_datas[cc_body[cc_body.len - 1]].@"break".block_inst = new_index;
  10245             } else if (args.cc_ref != .none) {
  10246                 astgen.extra.appendAssumeCapacity(@enumToInt(args.cc_ref));
  10247             }
  10248             if (ret_body.len != 0) {
  10249                 astgen.extra.appendAssumeCapacity(@intCast(u32, ret_body.len));
  10250                 astgen.extra.appendSliceAssumeCapacity(ret_body);
  10251                 zir_datas[ret_body[ret_body.len - 1]].@"break".block_inst = new_index;
  10252             } else if (ret_ref != .none) {
  10253                 astgen.extra.appendAssumeCapacity(@enumToInt(ret_ref));
  10254             }
  10255 
  10256             if (args.noalias_bits != 0) {
  10257                 astgen.extra.appendAssumeCapacity(args.noalias_bits);
  10258             }
  10259 
  10260             astgen.appendBodyWithFixups(body);
  10261             astgen.extra.appendSliceAssumeCapacity(src_locs);
  10262 
  10263             // Order is important when unstacking.
  10264             if (args.body_gz) |body_gz| body_gz.unstack();
  10265             if (args.ret_gz != null) {
  10266                 args.ret_gz.?.unstack();
  10267                 args.cc_gz.?.unstack();
  10268                 args.section_gz.?.unstack();
  10269                 args.addrspace_gz.?.unstack();
  10270                 args.align_gz.?.unstack();
  10271             }
  10272 
  10273             try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10274 
  10275             astgen.instructions.appendAssumeCapacity(.{
  10276                 .tag = .func_fancy,
  10277                 .data = .{ .pl_node = .{
  10278                     .src_node = gz.nodeIndexToRelative(args.src_node),
  10279                     .payload_index = payload_index,
  10280                 } },
  10281             });
  10282             gz.instructions.appendAssumeCapacity(new_index);
  10283             return indexToRef(new_index);
  10284         } else {
  10285             try astgen.extra.ensureUnusedCapacity(
  10286                 gpa,
  10287                 @typeInfo(Zir.Inst.Func).Struct.fields.len + 1 +
  10288                     @maximum(ret_body.len, @boolToInt(ret_ref != .none)) +
  10289                     body_len + src_locs.len,
  10290             );
  10291             const ret_body_len = if (ret_body.len != 0)
  10292                 @intCast(u32, ret_body.len)
  10293             else
  10294                 @boolToInt(ret_ref != .none);
  10295 
  10296             const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.Func{
  10297                 .param_block = args.param_block,
  10298                 .ret_body_len = ret_body_len,
  10299                 .body_len = body_len,
  10300             });
  10301             const zir_datas = astgen.instructions.items(.data);
  10302             if (ret_body.len != 0) {
  10303                 astgen.extra.appendSliceAssumeCapacity(ret_body);
  10304                 zir_datas[ret_body[ret_body.len - 1]].@"break".block_inst = new_index;
  10305             } else if (ret_ref != .none) {
  10306                 astgen.extra.appendAssumeCapacity(@enumToInt(ret_ref));
  10307             }
  10308             astgen.appendBodyWithFixups(body);
  10309             astgen.extra.appendSliceAssumeCapacity(src_locs);
  10310 
  10311             // Order is important when unstacking.
  10312             if (args.body_gz) |body_gz| body_gz.unstack();
  10313             if (args.ret_gz) |ret_gz| ret_gz.unstack();
  10314             if (args.cc_gz) |cc_gz| cc_gz.unstack();
  10315             if (args.section_gz) |section_gz| section_gz.unstack();
  10316             if (args.addrspace_gz) |addrspace_gz| addrspace_gz.unstack();
  10317             if (args.align_gz) |align_gz| align_gz.unstack();
  10318 
  10319             try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10320 
  10321             const tag: Zir.Inst.Tag = if (args.is_inferred_error) .func_inferred else .func;
  10322             astgen.instructions.appendAssumeCapacity(.{
  10323                 .tag = tag,
  10324                 .data = .{ .pl_node = .{
  10325                     .src_node = gz.nodeIndexToRelative(args.src_node),
  10326                     .payload_index = payload_index,
  10327                 } },
  10328             });
  10329             gz.instructions.appendAssumeCapacity(new_index);
  10330             return indexToRef(new_index);
  10331         }
  10332     }
  10333 
  10334     fn fancyFnExprExtraLen(body: []Zir.Inst.Index, ref: Zir.Inst.Ref) usize {
  10335         // In the case of non-empty body, there is one for the body length,
  10336         // and then one for each instruction.
  10337         return body.len + @boolToInt(ref != .none);
  10338     }
  10339 
  10340     fn addVar(gz: *GenZir, args: struct {
  10341         align_inst: Zir.Inst.Ref,
  10342         lib_name: u32,
  10343         var_type: Zir.Inst.Ref,
  10344         init: Zir.Inst.Ref,
  10345         is_extern: bool,
  10346         is_threadlocal: bool,
  10347     }) !Zir.Inst.Ref {
  10348         const astgen = gz.astgen;
  10349         const gpa = astgen.gpa;
  10350 
  10351         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10352         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10353 
  10354         try astgen.extra.ensureUnusedCapacity(
  10355             gpa,
  10356             @typeInfo(Zir.Inst.ExtendedVar).Struct.fields.len +
  10357                 @boolToInt(args.lib_name != 0) +
  10358                 @boolToInt(args.align_inst != .none) +
  10359                 @boolToInt(args.init != .none),
  10360         );
  10361         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.ExtendedVar{
  10362             .var_type = args.var_type,
  10363         });
  10364         if (args.lib_name != 0) {
  10365             astgen.extra.appendAssumeCapacity(args.lib_name);
  10366         }
  10367         if (args.align_inst != .none) {
  10368             astgen.extra.appendAssumeCapacity(@enumToInt(args.align_inst));
  10369         }
  10370         if (args.init != .none) {
  10371             astgen.extra.appendAssumeCapacity(@enumToInt(args.init));
  10372         }
  10373 
  10374         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  10375         astgen.instructions.appendAssumeCapacity(.{
  10376             .tag = .extended,
  10377             .data = .{ .extended = .{
  10378                 .opcode = .variable,
  10379                 .small = @bitCast(u16, Zir.Inst.ExtendedVar.Small{
  10380                     .has_lib_name = args.lib_name != 0,
  10381                     .has_align = args.align_inst != .none,
  10382                     .has_init = args.init != .none,
  10383                     .is_extern = args.is_extern,
  10384                     .is_threadlocal = args.is_threadlocal,
  10385                 }),
  10386                 .operand = payload_index,
  10387             } },
  10388         });
  10389         gz.instructions.appendAssumeCapacity(new_index);
  10390         return indexToRef(new_index);
  10391     }
  10392 
  10393     /// Note that this returns a `Zir.Inst.Index` not a ref.
  10394     /// Leaves the `payload_index` field undefined.
  10395     fn addBoolBr(
  10396         gz: *GenZir,
  10397         tag: Zir.Inst.Tag,
  10398         lhs: Zir.Inst.Ref,
  10399     ) !Zir.Inst.Index {
  10400         assert(lhs != .none);
  10401         const gpa = gz.astgen.gpa;
  10402         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10403         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10404 
  10405         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  10406         gz.astgen.instructions.appendAssumeCapacity(.{
  10407             .tag = tag,
  10408             .data = .{ .bool_br = .{
  10409                 .lhs = lhs,
  10410                 .payload_index = undefined,
  10411             } },
  10412         });
  10413         gz.instructions.appendAssumeCapacity(new_index);
  10414         return new_index;
  10415     }
  10416 
  10417     fn addInt(gz: *GenZir, integer: u64) !Zir.Inst.Ref {
  10418         return gz.add(.{
  10419             .tag = .int,
  10420             .data = .{ .int = integer },
  10421         });
  10422     }
  10423 
  10424     fn addIntBig(gz: *GenZir, limbs: []const std.math.big.Limb) !Zir.Inst.Ref {
  10425         const astgen = gz.astgen;
  10426         const gpa = astgen.gpa;
  10427         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10428         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10429         try astgen.string_bytes.ensureUnusedCapacity(gpa, @sizeOf(std.math.big.Limb) * limbs.len);
  10430 
  10431         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  10432         astgen.instructions.appendAssumeCapacity(.{
  10433             .tag = .int_big,
  10434             .data = .{ .str = .{
  10435                 .start = @intCast(u32, astgen.string_bytes.items.len),
  10436                 .len = @intCast(u32, limbs.len),
  10437             } },
  10438         });
  10439         gz.instructions.appendAssumeCapacity(new_index);
  10440         astgen.string_bytes.appendSliceAssumeCapacity(mem.sliceAsBytes(limbs));
  10441         return indexToRef(new_index);
  10442     }
  10443 
  10444     fn addFloat(gz: *GenZir, number: f64) !Zir.Inst.Ref {
  10445         return gz.add(.{
  10446             .tag = .float,
  10447             .data = .{ .float = number },
  10448         });
  10449     }
  10450 
  10451     fn addUnNode(
  10452         gz: *GenZir,
  10453         tag: Zir.Inst.Tag,
  10454         operand: Zir.Inst.Ref,
  10455         /// Absolute node index. This function does the conversion to offset from Decl.
  10456         src_node: Ast.Node.Index,
  10457     ) !Zir.Inst.Ref {
  10458         assert(operand != .none);
  10459         return gz.add(.{
  10460             .tag = tag,
  10461             .data = .{ .un_node = .{
  10462                 .operand = operand,
  10463                 .src_node = gz.nodeIndexToRelative(src_node),
  10464             } },
  10465         });
  10466     }
  10467 
  10468     fn makeUnNode(
  10469         gz: *GenZir,
  10470         tag: Zir.Inst.Tag,
  10471         operand: Zir.Inst.Ref,
  10472         /// Absolute node index. This function does the conversion to offset from Decl.
  10473         src_node: Ast.Node.Index,
  10474     ) !Zir.Inst.Index {
  10475         assert(operand != .none);
  10476         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  10477         try gz.astgen.instructions.append(gz.astgen.gpa, .{
  10478             .tag = tag,
  10479             .data = .{ .un_node = .{
  10480                 .operand = operand,
  10481                 .src_node = gz.nodeIndexToRelative(src_node),
  10482             } },
  10483         });
  10484         return new_index;
  10485     }
  10486 
  10487     fn addPlNode(
  10488         gz: *GenZir,
  10489         tag: Zir.Inst.Tag,
  10490         /// Absolute node index. This function does the conversion to offset from Decl.
  10491         src_node: Ast.Node.Index,
  10492         extra: anytype,
  10493     ) !Zir.Inst.Ref {
  10494         const gpa = gz.astgen.gpa;
  10495         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10496         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10497 
  10498         const payload_index = try gz.astgen.addExtra(extra);
  10499         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  10500         gz.astgen.instructions.appendAssumeCapacity(.{
  10501             .tag = tag,
  10502             .data = .{ .pl_node = .{
  10503                 .src_node = gz.nodeIndexToRelative(src_node),
  10504                 .payload_index = payload_index,
  10505             } },
  10506         });
  10507         gz.instructions.appendAssumeCapacity(new_index);
  10508         return indexToRef(new_index);
  10509     }
  10510 
  10511     fn addPlNodePayloadIndex(
  10512         gz: *GenZir,
  10513         tag: Zir.Inst.Tag,
  10514         /// Absolute node index. This function does the conversion to offset from Decl.
  10515         src_node: Ast.Node.Index,
  10516         payload_index: u32,
  10517     ) !Zir.Inst.Ref {
  10518         return try gz.add(.{
  10519             .tag = tag,
  10520             .data = .{ .pl_node = .{
  10521                 .src_node = gz.nodeIndexToRelative(src_node),
  10522                 .payload_index = payload_index,
  10523             } },
  10524         });
  10525     }
  10526 
  10527     /// Supports `param_gz` stacked on `gz`. Assumes nothing stacked on `param_gz`. Unstacks `param_gz`.
  10528     fn addParam(
  10529         gz: *GenZir,
  10530         param_gz: *GenZir,
  10531         tag: Zir.Inst.Tag,
  10532         /// Absolute token index. This function does the conversion to Decl offset.
  10533         abs_tok_index: Ast.TokenIndex,
  10534         name: u32,
  10535         first_doc_comment: ?Ast.TokenIndex,
  10536     ) !Zir.Inst.Index {
  10537         const gpa = gz.astgen.gpa;
  10538         const param_body = param_gz.instructionsSlice();
  10539         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10540         try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Param).Struct.fields.len +
  10541             param_body.len);
  10542 
  10543         const doc_comment_index = if (first_doc_comment) |first|
  10544             try gz.astgen.docCommentAsStringFromFirst(abs_tok_index, first)
  10545         else
  10546             0;
  10547 
  10548         const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Param{
  10549             .name = name,
  10550             .doc_comment = doc_comment_index,
  10551             .body_len = @intCast(u32, param_body.len),
  10552         });
  10553         gz.astgen.extra.appendSliceAssumeCapacity(param_body);
  10554         param_gz.unstack();
  10555 
  10556         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  10557         gz.astgen.instructions.appendAssumeCapacity(.{
  10558             .tag = tag,
  10559             .data = .{ .pl_tok = .{
  10560                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  10561                 .payload_index = payload_index,
  10562             } },
  10563         });
  10564         gz.instructions.appendAssumeCapacity(new_index);
  10565         return new_index;
  10566     }
  10567 
  10568     fn addExtendedPayload(
  10569         gz: *GenZir,
  10570         opcode: Zir.Inst.Extended,
  10571         extra: anytype,
  10572     ) !Zir.Inst.Ref {
  10573         const gpa = gz.astgen.gpa;
  10574 
  10575         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10576         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10577 
  10578         const payload_index = try gz.astgen.addExtra(extra);
  10579         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  10580         gz.astgen.instructions.appendAssumeCapacity(.{
  10581             .tag = .extended,
  10582             .data = .{ .extended = .{
  10583                 .opcode = opcode,
  10584                 .small = undefined,
  10585                 .operand = payload_index,
  10586             } },
  10587         });
  10588         gz.instructions.appendAssumeCapacity(new_index);
  10589         return indexToRef(new_index);
  10590     }
  10591 
  10592     fn addExtendedMultiOp(
  10593         gz: *GenZir,
  10594         opcode: Zir.Inst.Extended,
  10595         node: Ast.Node.Index,
  10596         operands: []const Zir.Inst.Ref,
  10597     ) !Zir.Inst.Ref {
  10598         const astgen = gz.astgen;
  10599         const gpa = astgen.gpa;
  10600 
  10601         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10602         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10603         try astgen.extra.ensureUnusedCapacity(
  10604             gpa,
  10605             @typeInfo(Zir.Inst.NodeMultiOp).Struct.fields.len + operands.len,
  10606         );
  10607 
  10608         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.NodeMultiOp{
  10609             .src_node = gz.nodeIndexToRelative(node),
  10610         });
  10611         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  10612         astgen.instructions.appendAssumeCapacity(.{
  10613             .tag = .extended,
  10614             .data = .{ .extended = .{
  10615                 .opcode = opcode,
  10616                 .small = @intCast(u16, operands.len),
  10617                 .operand = payload_index,
  10618             } },
  10619         });
  10620         gz.instructions.appendAssumeCapacity(new_index);
  10621         astgen.appendRefsAssumeCapacity(operands);
  10622         return indexToRef(new_index);
  10623     }
  10624 
  10625     fn addExtendedMultiOpPayloadIndex(
  10626         gz: *GenZir,
  10627         opcode: Zir.Inst.Extended,
  10628         payload_index: u32,
  10629         trailing_len: usize,
  10630     ) !Zir.Inst.Ref {
  10631         const astgen = gz.astgen;
  10632         const gpa = astgen.gpa;
  10633 
  10634         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10635         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10636         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  10637         astgen.instructions.appendAssumeCapacity(.{
  10638             .tag = .extended,
  10639             .data = .{ .extended = .{
  10640                 .opcode = opcode,
  10641                 .small = @intCast(u16, trailing_len),
  10642                 .operand = payload_index,
  10643             } },
  10644         });
  10645         gz.instructions.appendAssumeCapacity(new_index);
  10646         return indexToRef(new_index);
  10647     }
  10648 
  10649     fn addUnTok(
  10650         gz: *GenZir,
  10651         tag: Zir.Inst.Tag,
  10652         operand: Zir.Inst.Ref,
  10653         /// Absolute token index. This function does the conversion to Decl offset.
  10654         abs_tok_index: Ast.TokenIndex,
  10655     ) !Zir.Inst.Ref {
  10656         assert(operand != .none);
  10657         return gz.add(.{
  10658             .tag = tag,
  10659             .data = .{ .un_tok = .{
  10660                 .operand = operand,
  10661                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  10662             } },
  10663         });
  10664     }
  10665 
  10666     fn makeUnTok(
  10667         gz: *GenZir,
  10668         tag: Zir.Inst.Tag,
  10669         operand: Zir.Inst.Ref,
  10670         /// Absolute token index. This function does the conversion to Decl offset.
  10671         abs_tok_index: Ast.TokenIndex,
  10672     ) !Zir.Inst.Index {
  10673         const astgen = gz.astgen;
  10674         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  10675         assert(operand != .none);
  10676         try astgen.instructions.append(astgen.gpa, .{
  10677             .tag = tag,
  10678             .data = .{ .un_tok = .{
  10679                 .operand = operand,
  10680                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  10681             } },
  10682         });
  10683         return new_index;
  10684     }
  10685 
  10686     fn addStrTok(
  10687         gz: *GenZir,
  10688         tag: Zir.Inst.Tag,
  10689         str_index: u32,
  10690         /// Absolute token index. This function does the conversion to Decl offset.
  10691         abs_tok_index: Ast.TokenIndex,
  10692     ) !Zir.Inst.Ref {
  10693         return gz.add(.{
  10694             .tag = tag,
  10695             .data = .{ .str_tok = .{
  10696                 .start = str_index,
  10697                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  10698             } },
  10699         });
  10700     }
  10701 
  10702     fn addBreak(
  10703         gz: *GenZir,
  10704         tag: Zir.Inst.Tag,
  10705         break_block: Zir.Inst.Index,
  10706         operand: Zir.Inst.Ref,
  10707     ) !Zir.Inst.Index {
  10708         return gz.addAsIndex(.{
  10709             .tag = tag,
  10710             .data = .{ .@"break" = .{
  10711                 .block_inst = break_block,
  10712                 .operand = operand,
  10713             } },
  10714         });
  10715     }
  10716 
  10717     fn makeBreak(
  10718         gz: *GenZir,
  10719         tag: Zir.Inst.Tag,
  10720         break_block: Zir.Inst.Index,
  10721         operand: Zir.Inst.Ref,
  10722     ) !Zir.Inst.Index {
  10723         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  10724         try gz.astgen.instructions.append(gz.astgen.gpa, .{
  10725             .tag = tag,
  10726             .data = .{ .@"break" = .{
  10727                 .block_inst = break_block,
  10728                 .operand = operand,
  10729             } },
  10730         });
  10731         return new_index;
  10732     }
  10733 
  10734     fn addBin(
  10735         gz: *GenZir,
  10736         tag: Zir.Inst.Tag,
  10737         lhs: Zir.Inst.Ref,
  10738         rhs: Zir.Inst.Ref,
  10739     ) !Zir.Inst.Ref {
  10740         assert(lhs != .none);
  10741         assert(rhs != .none);
  10742         return gz.add(.{
  10743             .tag = tag,
  10744             .data = .{ .bin = .{
  10745                 .lhs = lhs,
  10746                 .rhs = rhs,
  10747             } },
  10748         });
  10749     }
  10750 
  10751     fn addDecl(
  10752         gz: *GenZir,
  10753         tag: Zir.Inst.Tag,
  10754         decl_index: u32,
  10755         src_node: Ast.Node.Index,
  10756     ) !Zir.Inst.Ref {
  10757         return gz.add(.{
  10758             .tag = tag,
  10759             .data = .{ .pl_node = .{
  10760                 .src_node = gz.nodeIndexToRelative(src_node),
  10761                 .payload_index = decl_index,
  10762             } },
  10763         });
  10764     }
  10765 
  10766     fn addNode(
  10767         gz: *GenZir,
  10768         tag: Zir.Inst.Tag,
  10769         /// Absolute node index. This function does the conversion to offset from Decl.
  10770         src_node: Ast.Node.Index,
  10771     ) !Zir.Inst.Ref {
  10772         return gz.add(.{
  10773             .tag = tag,
  10774             .data = .{ .node = gz.nodeIndexToRelative(src_node) },
  10775         });
  10776     }
  10777 
  10778     fn addInstNode(
  10779         gz: *GenZir,
  10780         tag: Zir.Inst.Tag,
  10781         inst: Zir.Inst.Index,
  10782         /// Absolute node index. This function does the conversion to offset from Decl.
  10783         src_node: Ast.Node.Index,
  10784     ) !Zir.Inst.Ref {
  10785         return gz.add(.{
  10786             .tag = tag,
  10787             .data = .{ .inst_node = .{
  10788                 .inst = inst,
  10789                 .src_node = gz.nodeIndexToRelative(src_node),
  10790             } },
  10791         });
  10792     }
  10793 
  10794     fn addNodeExtended(
  10795         gz: *GenZir,
  10796         opcode: Zir.Inst.Extended,
  10797         /// Absolute node index. This function does the conversion to offset from Decl.
  10798         src_node: Ast.Node.Index,
  10799     ) !Zir.Inst.Ref {
  10800         return gz.add(.{
  10801             .tag = .extended,
  10802             .data = .{ .extended = .{
  10803                 .opcode = opcode,
  10804                 .small = undefined,
  10805                 .operand = @bitCast(u32, gz.nodeIndexToRelative(src_node)),
  10806             } },
  10807         });
  10808     }
  10809 
  10810     fn addAllocExtended(
  10811         gz: *GenZir,
  10812         args: struct {
  10813             /// Absolute node index. This function does the conversion to offset from Decl.
  10814             node: Ast.Node.Index,
  10815             type_inst: Zir.Inst.Ref,
  10816             align_inst: Zir.Inst.Ref,
  10817             is_const: bool,
  10818             is_comptime: bool,
  10819         },
  10820     ) !Zir.Inst.Ref {
  10821         const astgen = gz.astgen;
  10822         const gpa = astgen.gpa;
  10823 
  10824         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10825         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10826         try astgen.extra.ensureUnusedCapacity(
  10827             gpa,
  10828             @typeInfo(Zir.Inst.AllocExtended).Struct.fields.len +
  10829                 @as(usize, @boolToInt(args.type_inst != .none)) +
  10830                 @as(usize, @boolToInt(args.align_inst != .none)),
  10831         );
  10832         const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.AllocExtended{
  10833             .src_node = gz.nodeIndexToRelative(args.node),
  10834         });
  10835         if (args.type_inst != .none) {
  10836             astgen.extra.appendAssumeCapacity(@enumToInt(args.type_inst));
  10837         }
  10838         if (args.align_inst != .none) {
  10839             astgen.extra.appendAssumeCapacity(@enumToInt(args.align_inst));
  10840         }
  10841 
  10842         const has_type: u4 = @boolToInt(args.type_inst != .none);
  10843         const has_align: u4 = @boolToInt(args.align_inst != .none);
  10844         const is_const: u4 = @boolToInt(args.is_const);
  10845         const is_comptime: u4 = @boolToInt(args.is_comptime);
  10846         const small: u16 = has_type | (has_align << 1) | (is_const << 2) | (is_comptime << 3);
  10847 
  10848         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  10849         astgen.instructions.appendAssumeCapacity(.{
  10850             .tag = .extended,
  10851             .data = .{ .extended = .{
  10852                 .opcode = .alloc,
  10853                 .small = small,
  10854                 .operand = payload_index,
  10855             } },
  10856         });
  10857         gz.instructions.appendAssumeCapacity(new_index);
  10858         return indexToRef(new_index);
  10859     }
  10860 
  10861     fn addAsm(
  10862         gz: *GenZir,
  10863         args: struct {
  10864             /// Absolute node index. This function does the conversion to offset from Decl.
  10865             node: Ast.Node.Index,
  10866             asm_source: u32,
  10867             output_type_bits: u32,
  10868             is_volatile: bool,
  10869             outputs: []const Zir.Inst.Asm.Output,
  10870             inputs: []const Zir.Inst.Asm.Input,
  10871             clobbers: []const u32,
  10872         },
  10873     ) !Zir.Inst.Ref {
  10874         const astgen = gz.astgen;
  10875         const gpa = astgen.gpa;
  10876 
  10877         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10878         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10879         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Asm).Struct.fields.len +
  10880             args.outputs.len * @typeInfo(Zir.Inst.Asm.Output).Struct.fields.len +
  10881             args.inputs.len * @typeInfo(Zir.Inst.Asm.Input).Struct.fields.len +
  10882             args.clobbers.len);
  10883 
  10884         const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Asm{
  10885             .src_node = gz.nodeIndexToRelative(args.node),
  10886             .asm_source = args.asm_source,
  10887             .output_type_bits = args.output_type_bits,
  10888         });
  10889         for (args.outputs) |output| {
  10890             _ = gz.astgen.addExtraAssumeCapacity(output);
  10891         }
  10892         for (args.inputs) |input| {
  10893             _ = gz.astgen.addExtraAssumeCapacity(input);
  10894         }
  10895         gz.astgen.extra.appendSliceAssumeCapacity(args.clobbers);
  10896 
  10897         //  * 0b00000000_000XXXXX - `outputs_len`.
  10898         //  * 0b000000XX_XXX00000 - `inputs_len`.
  10899         //  * 0b0XXXXX00_00000000 - `clobbers_len`.
  10900         //  * 0bX0000000_00000000 - is volatile
  10901         const small: u16 = @intCast(u16, args.outputs.len) |
  10902             @intCast(u16, args.inputs.len << 5) |
  10903             @intCast(u16, args.clobbers.len << 10) |
  10904             (@as(u16, @boolToInt(args.is_volatile)) << 15);
  10905 
  10906         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  10907         astgen.instructions.appendAssumeCapacity(.{
  10908             .tag = .extended,
  10909             .data = .{ .extended = .{
  10910                 .opcode = .@"asm",
  10911                 .small = small,
  10912                 .operand = payload_index,
  10913             } },
  10914         });
  10915         gz.instructions.appendAssumeCapacity(new_index);
  10916         return indexToRef(new_index);
  10917     }
  10918 
  10919     /// Note that this returns a `Zir.Inst.Index` not a ref.
  10920     /// Does *not* append the block instruction to the scope.
  10921     /// Leaves the `payload_index` field undefined.
  10922     fn makeBlockInst(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
  10923         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  10924         const gpa = gz.astgen.gpa;
  10925         try gz.astgen.instructions.append(gpa, .{
  10926             .tag = tag,
  10927             .data = .{ .pl_node = .{
  10928                 .src_node = gz.nodeIndexToRelative(node),
  10929                 .payload_index = undefined,
  10930             } },
  10931         });
  10932         return new_index;
  10933     }
  10934 
  10935     /// Note that this returns a `Zir.Inst.Index` not a ref.
  10936     /// Leaves the `payload_index` field undefined.
  10937     fn addCondBr(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
  10938         const gpa = gz.astgen.gpa;
  10939         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10940         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  10941         try gz.astgen.instructions.append(gpa, .{
  10942             .tag = tag,
  10943             .data = .{ .pl_node = .{
  10944                 .src_node = gz.nodeIndexToRelative(node),
  10945                 .payload_index = undefined,
  10946             } },
  10947         });
  10948         gz.instructions.appendAssumeCapacity(new_index);
  10949         return new_index;
  10950     }
  10951 
  10952     fn setStruct(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  10953         src_node: Ast.Node.Index,
  10954         body_len: u32,
  10955         fields_len: u32,
  10956         decls_len: u32,
  10957         layout: std.builtin.Type.ContainerLayout,
  10958         known_non_opv: bool,
  10959         known_comptime_only: bool,
  10960     }) !void {
  10961         const astgen = gz.astgen;
  10962         const gpa = astgen.gpa;
  10963 
  10964         try astgen.extra.ensureUnusedCapacity(gpa, 4);
  10965         const payload_index = @intCast(u32, astgen.extra.items.len);
  10966 
  10967         if (args.src_node != 0) {
  10968             const node_offset = gz.nodeIndexToRelative(args.src_node);
  10969             astgen.extra.appendAssumeCapacity(@bitCast(u32, node_offset));
  10970         }
  10971         if (args.body_len != 0) {
  10972             astgen.extra.appendAssumeCapacity(args.body_len);
  10973         }
  10974         if (args.fields_len != 0) {
  10975             astgen.extra.appendAssumeCapacity(args.fields_len);
  10976         }
  10977         if (args.decls_len != 0) {
  10978             astgen.extra.appendAssumeCapacity(args.decls_len);
  10979         }
  10980         astgen.instructions.set(inst, .{
  10981             .tag = .extended,
  10982             .data = .{ .extended = .{
  10983                 .opcode = .struct_decl,
  10984                 .small = @bitCast(u16, Zir.Inst.StructDecl.Small{
  10985                     .has_src_node = args.src_node != 0,
  10986                     .has_body_len = args.body_len != 0,
  10987                     .has_fields_len = args.fields_len != 0,
  10988                     .has_decls_len = args.decls_len != 0,
  10989                     .known_non_opv = args.known_non_opv,
  10990                     .known_comptime_only = args.known_comptime_only,
  10991                     .name_strategy = gz.anon_name_strategy,
  10992                     .layout = args.layout,
  10993                 }),
  10994                 .operand = payload_index,
  10995             } },
  10996         });
  10997     }
  10998 
  10999     fn setUnion(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  11000         src_node: Ast.Node.Index,
  11001         tag_type: Zir.Inst.Ref,
  11002         body_len: u32,
  11003         fields_len: u32,
  11004         decls_len: u32,
  11005         layout: std.builtin.Type.ContainerLayout,
  11006         auto_enum_tag: bool,
  11007     }) !void {
  11008         const astgen = gz.astgen;
  11009         const gpa = astgen.gpa;
  11010 
  11011         try astgen.extra.ensureUnusedCapacity(gpa, 5);
  11012         const payload_index = @intCast(u32, astgen.extra.items.len);
  11013 
  11014         if (args.src_node != 0) {
  11015             const node_offset = gz.nodeIndexToRelative(args.src_node);
  11016             astgen.extra.appendAssumeCapacity(@bitCast(u32, node_offset));
  11017         }
  11018         if (args.tag_type != .none) {
  11019             astgen.extra.appendAssumeCapacity(@enumToInt(args.tag_type));
  11020         }
  11021         if (args.body_len != 0) {
  11022             astgen.extra.appendAssumeCapacity(args.body_len);
  11023         }
  11024         if (args.fields_len != 0) {
  11025             astgen.extra.appendAssumeCapacity(args.fields_len);
  11026         }
  11027         if (args.decls_len != 0) {
  11028             astgen.extra.appendAssumeCapacity(args.decls_len);
  11029         }
  11030         astgen.instructions.set(inst, .{
  11031             .tag = .extended,
  11032             .data = .{ .extended = .{
  11033                 .opcode = .union_decl,
  11034                 .small = @bitCast(u16, Zir.Inst.UnionDecl.Small{
  11035                     .has_src_node = args.src_node != 0,
  11036                     .has_tag_type = args.tag_type != .none,
  11037                     .has_body_len = args.body_len != 0,
  11038                     .has_fields_len = args.fields_len != 0,
  11039                     .has_decls_len = args.decls_len != 0,
  11040                     .name_strategy = gz.anon_name_strategy,
  11041                     .layout = args.layout,
  11042                     .auto_enum_tag = args.auto_enum_tag,
  11043                 }),
  11044                 .operand = payload_index,
  11045             } },
  11046         });
  11047     }
  11048 
  11049     fn setEnum(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  11050         src_node: Ast.Node.Index,
  11051         tag_type: Zir.Inst.Ref,
  11052         body_len: u32,
  11053         fields_len: u32,
  11054         decls_len: u32,
  11055         nonexhaustive: bool,
  11056     }) !void {
  11057         const astgen = gz.astgen;
  11058         const gpa = astgen.gpa;
  11059 
  11060         try astgen.extra.ensureUnusedCapacity(gpa, 5);
  11061         const payload_index = @intCast(u32, astgen.extra.items.len);
  11062 
  11063         if (args.src_node != 0) {
  11064             const node_offset = gz.nodeIndexToRelative(args.src_node);
  11065             astgen.extra.appendAssumeCapacity(@bitCast(u32, node_offset));
  11066         }
  11067         if (args.tag_type != .none) {
  11068             astgen.extra.appendAssumeCapacity(@enumToInt(args.tag_type));
  11069         }
  11070         if (args.body_len != 0) {
  11071             astgen.extra.appendAssumeCapacity(args.body_len);
  11072         }
  11073         if (args.fields_len != 0) {
  11074             astgen.extra.appendAssumeCapacity(args.fields_len);
  11075         }
  11076         if (args.decls_len != 0) {
  11077             astgen.extra.appendAssumeCapacity(args.decls_len);
  11078         }
  11079         astgen.instructions.set(inst, .{
  11080             .tag = .extended,
  11081             .data = .{ .extended = .{
  11082                 .opcode = .enum_decl,
  11083                 .small = @bitCast(u16, Zir.Inst.EnumDecl.Small{
  11084                     .has_src_node = args.src_node != 0,
  11085                     .has_tag_type = args.tag_type != .none,
  11086                     .has_body_len = args.body_len != 0,
  11087                     .has_fields_len = args.fields_len != 0,
  11088                     .has_decls_len = args.decls_len != 0,
  11089                     .name_strategy = gz.anon_name_strategy,
  11090                     .nonexhaustive = args.nonexhaustive,
  11091                 }),
  11092                 .operand = payload_index,
  11093             } },
  11094         });
  11095     }
  11096 
  11097     fn setOpaque(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  11098         src_node: Ast.Node.Index,
  11099         decls_len: u32,
  11100     }) !void {
  11101         const astgen = gz.astgen;
  11102         const gpa = astgen.gpa;
  11103 
  11104         try astgen.extra.ensureUnusedCapacity(gpa, 2);
  11105         const payload_index = @intCast(u32, astgen.extra.items.len);
  11106 
  11107         if (args.src_node != 0) {
  11108             const node_offset = gz.nodeIndexToRelative(args.src_node);
  11109             astgen.extra.appendAssumeCapacity(@bitCast(u32, node_offset));
  11110         }
  11111         if (args.decls_len != 0) {
  11112             astgen.extra.appendAssumeCapacity(args.decls_len);
  11113         }
  11114         astgen.instructions.set(inst, .{
  11115             .tag = .extended,
  11116             .data = .{ .extended = .{
  11117                 .opcode = .opaque_decl,
  11118                 .small = @bitCast(u16, Zir.Inst.OpaqueDecl.Small{
  11119                     .has_src_node = args.src_node != 0,
  11120                     .has_decls_len = args.decls_len != 0,
  11121                     .name_strategy = gz.anon_name_strategy,
  11122                 }),
  11123                 .operand = payload_index,
  11124             } },
  11125         });
  11126     }
  11127 
  11128     fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref {
  11129         return indexToRef(try gz.addAsIndex(inst));
  11130     }
  11131 
  11132     fn addAsIndex(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Index {
  11133         const gpa = gz.astgen.gpa;
  11134         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  11135         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  11136 
  11137         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  11138         gz.astgen.instructions.appendAssumeCapacity(inst);
  11139         gz.instructions.appendAssumeCapacity(new_index);
  11140         return new_index;
  11141     }
  11142 
  11143     fn reserveInstructionIndex(gz: *GenZir) !Zir.Inst.Index {
  11144         const gpa = gz.astgen.gpa;
  11145         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  11146         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  11147 
  11148         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  11149         gz.astgen.instructions.len += 1;
  11150         gz.instructions.appendAssumeCapacity(new_index);
  11151         return new_index;
  11152     }
  11153 
  11154     fn addRet(gz: *GenZir, rl: ResultLoc, operand: Zir.Inst.Ref, node: Ast.Node.Index) !void {
  11155         switch (rl) {
  11156             .ptr => |ret_ptr| _ = try gz.addUnNode(.ret_load, ret_ptr, node),
  11157             .ty => _ = try gz.addUnNode(.ret_node, operand, node),
  11158             else => unreachable,
  11159         }
  11160     }
  11161 
  11162     fn addNamespaceCaptures(gz: *GenZir, namespace: *Scope.Namespace) !void {
  11163         if (namespace.captures.count() > 0) {
  11164             try gz.instructions.ensureUnusedCapacity(gz.astgen.gpa, namespace.captures.count());
  11165             for (namespace.captures.values()) |capture| {
  11166                 gz.instructions.appendAssumeCapacity(capture);
  11167             }
  11168         }
  11169     }
  11170 
  11171     fn addDbgVar(gz: *GenZir, tag: Zir.Inst.Tag, name: u32, inst: Zir.Inst.Ref) !void {
  11172         if (gz.force_comptime) return;
  11173 
  11174         _ = try gz.add(.{ .tag = tag, .data = .{
  11175             .str_op = .{
  11176                 .str = name,
  11177                 .operand = inst,
  11178             },
  11179         } });
  11180     }
  11181 
  11182     fn addDbgBlockBegin(gz: *GenZir) !void {
  11183         if (gz.force_comptime) return;
  11184 
  11185         _ = try gz.add(.{ .tag = .dbg_block_begin, .data = undefined });
  11186     }
  11187 
  11188     fn addDbgBlockEnd(gz: *GenZir) !void {
  11189         if (gz.force_comptime) return;
  11190         const gpa = gz.astgen.gpa;
  11191 
  11192         const tags = gz.astgen.instructions.items(.tag);
  11193         const last_inst = gz.instructions.items[gz.instructions.items.len - 1];
  11194         // remove dbg_block_begin immediately followed by dbg_block_end
  11195         if (tags[last_inst] == .dbg_block_begin) {
  11196             _ = gz.instructions.pop();
  11197             return;
  11198         }
  11199 
  11200         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  11201         try gz.astgen.instructions.append(gpa, .{ .tag = .dbg_block_end, .data = undefined });
  11202         try gz.instructions.insert(gpa, gz.instructions.items.len - 1, new_index);
  11203     }
  11204 
  11205     /// Control flow does not fall through the "then" block of a loop; it continues
  11206     /// back to the while condition. This prevents `rvalue` from
  11207     /// adding an invalid store to the result location of `then_scope`.
  11208     fn markAsLoopBody(gz: *GenZir, loop_scope: GenZir) void {
  11209         gz.rvalue_noresult = switch (loop_scope.break_result_loc) {
  11210             .ptr, .inferred_ptr => |ptr| ptr,
  11211             .block_ptr => |block| block.rl_ptr,
  11212             else => .none,
  11213         };
  11214     }
  11215 };
  11216 
  11217 /// This can only be for short-lived references; the memory becomes invalidated
  11218 /// when another string is added.
  11219 fn nullTerminatedString(astgen: AstGen, index: usize) [*:0]const u8 {
  11220     return @ptrCast([*:0]const u8, astgen.string_bytes.items.ptr) + index;
  11221 }
  11222 
  11223 pub fn isPrimitive(name: []const u8) bool {
  11224     if (primitives.get(name) != null) return true;
  11225     if (name.len < 2) return false;
  11226     const first_c = name[0];
  11227     if (first_c != 'i' and first_c != 'u') return false;
  11228     if (parseBitCount(name[1..])) |_| {
  11229         return true;
  11230     } else |err| switch (err) {
  11231         error.Overflow => return true,
  11232         error.InvalidCharacter => return false,
  11233     }
  11234 }
  11235 
  11236 /// Local variables shadowing detection, including function parameters.
  11237 fn detectLocalShadowing(
  11238     astgen: *AstGen,
  11239     scope: *Scope,
  11240     ident_name: u32,
  11241     name_token: Ast.TokenIndex,
  11242     token_bytes: []const u8,
  11243 ) !void {
  11244     const gpa = astgen.gpa;
  11245     if (token_bytes[0] != '@' and isPrimitive(token_bytes)) {
  11246         return astgen.failTokNotes(name_token, "name shadows primitive '{s}'", .{
  11247             token_bytes,
  11248         }, &[_]u32{
  11249             try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{
  11250                 token_bytes,
  11251             }),
  11252         });
  11253     }
  11254 
  11255     var s = scope;
  11256     while (true) switch (s.tag) {
  11257         .local_val => {
  11258             const local_val = s.cast(Scope.LocalVal).?;
  11259             if (local_val.name == ident_name) {
  11260                 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
  11261                 const name = try gpa.dupe(u8, name_slice);
  11262                 defer gpa.free(name);
  11263                 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{
  11264                     @tagName(local_val.id_cat), name,
  11265                 }, &[_]u32{
  11266                     try astgen.errNoteTok(
  11267                         local_val.token_src,
  11268                         "previous declaration here",
  11269                         .{},
  11270                     ),
  11271                 });
  11272             }
  11273             s = local_val.parent;
  11274         },
  11275         .local_ptr => {
  11276             const local_ptr = s.cast(Scope.LocalPtr).?;
  11277             if (local_ptr.name == ident_name) {
  11278                 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
  11279                 const name = try gpa.dupe(u8, name_slice);
  11280                 defer gpa.free(name);
  11281                 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{
  11282                     @tagName(local_ptr.id_cat), name,
  11283                 }, &[_]u32{
  11284                     try astgen.errNoteTok(
  11285                         local_ptr.token_src,
  11286                         "previous declaration here",
  11287                         .{},
  11288                     ),
  11289                 });
  11290             }
  11291             s = local_ptr.parent;
  11292         },
  11293         .namespace => {
  11294             const ns = s.cast(Scope.Namespace).?;
  11295             const decl_node = ns.decls.get(ident_name) orelse {
  11296                 s = ns.parent;
  11297                 continue;
  11298             };
  11299             const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
  11300             const name = try gpa.dupe(u8, name_slice);
  11301             defer gpa.free(name);
  11302             return astgen.failTokNotes(name_token, "local shadows declaration of '{s}'", .{
  11303                 name,
  11304             }, &[_]u32{
  11305                 try astgen.errNoteNode(decl_node, "declared here", .{}),
  11306             });
  11307         },
  11308         .gen_zir => s = s.cast(GenZir).?.parent,
  11309         .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
  11310         .top => break,
  11311     };
  11312 }
  11313 
  11314 /// Advances the source cursor to the beginning of `node`.
  11315 fn advanceSourceCursorToNode(astgen: *AstGen, node: Ast.Node.Index) void {
  11316     const tree = astgen.tree;
  11317     const token_starts = tree.tokens.items(.start);
  11318     const node_start = token_starts[tree.firstToken(node)];
  11319     astgen.advanceSourceCursor(node_start);
  11320 }
  11321 
  11322 /// Advances the source cursor to an absolute byte offset `end` in the file.
  11323 fn advanceSourceCursor(astgen: *AstGen, end: usize) void {
  11324     const source = astgen.tree.source;
  11325     var i = astgen.source_offset;
  11326     var line = astgen.source_line;
  11327     var column = astgen.source_column;
  11328     assert(i <= end);
  11329     while (i < end) : (i += 1) {
  11330         if (source[i] == '\n') {
  11331             line += 1;
  11332             column = 0;
  11333         } else {
  11334             column += 1;
  11335         }
  11336     }
  11337     astgen.source_offset = i;
  11338     astgen.source_line = line;
  11339     astgen.source_column = column;
  11340 }
  11341 
  11342 fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.Node.Index) !u32 {
  11343     const gpa = astgen.gpa;
  11344     const tree = astgen.tree;
  11345     const node_tags = tree.nodes.items(.tag);
  11346     const main_tokens = tree.nodes.items(.main_token);
  11347     const token_tags = tree.tokens.items(.tag);
  11348     var decl_count: u32 = 0;
  11349     for (members) |member_node| {
  11350         const name_token = switch (node_tags[member_node]) {
  11351             .fn_proto_simple,
  11352             .fn_proto_multi,
  11353             .fn_proto_one,
  11354             .fn_proto,
  11355             .global_var_decl,
  11356             .local_var_decl,
  11357             .simple_var_decl,
  11358             .aligned_var_decl,
  11359             => blk: {
  11360                 decl_count += 1;
  11361                 break :blk main_tokens[member_node] + 1;
  11362             },
  11363 
  11364             .fn_decl => blk: {
  11365                 decl_count += 1;
  11366                 const ident = main_tokens[member_node] + 1;
  11367                 if (token_tags[ident] != .identifier) {
  11368                     switch (astgen.failNode(member_node, "missing function name", .{})) {
  11369                         error.AnalysisFail => continue,
  11370                         error.OutOfMemory => return error.OutOfMemory,
  11371                     }
  11372                 }
  11373                 break :blk ident;
  11374             },
  11375 
  11376             .@"comptime", .@"usingnamespace", .test_decl => {
  11377                 decl_count += 1;
  11378                 continue;
  11379             },
  11380 
  11381             else => continue,
  11382         };
  11383 
  11384         const token_bytes = astgen.tree.tokenSlice(name_token);
  11385         if (token_bytes[0] != '@' and isPrimitive(token_bytes)) {
  11386             switch (astgen.failTokNotes(name_token, "name shadows primitive '{s}'", .{
  11387                 token_bytes,
  11388             }, &[_]u32{
  11389                 try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{
  11390                     token_bytes,
  11391                 }),
  11392             })) {
  11393                 error.AnalysisFail => continue,
  11394                 error.OutOfMemory => return error.OutOfMemory,
  11395             }
  11396         }
  11397 
  11398         const name_str_index = try astgen.identAsString(name_token);
  11399         const gop = try namespace.decls.getOrPut(gpa, name_str_index);
  11400         if (gop.found_existing) {
  11401             const name = try gpa.dupe(u8, mem.span(astgen.nullTerminatedString(name_str_index)));
  11402             defer gpa.free(name);
  11403             switch (astgen.failNodeNotes(member_node, "redeclaration of '{s}'", .{
  11404                 name,
  11405             }, &[_]u32{
  11406                 try astgen.errNoteNode(gop.value_ptr.*, "other declaration here", .{}),
  11407             })) {
  11408                 error.AnalysisFail => continue,
  11409                 error.OutOfMemory => return error.OutOfMemory,
  11410             }
  11411         }
  11412         gop.value_ptr.* = member_node;
  11413     }
  11414     return decl_count;
  11415 }
  11416 
  11417 fn isInferred(astgen: *AstGen, ref: Zir.Inst.Ref) bool {
  11418     const inst = refToIndex(ref) orelse return false;
  11419     const zir_tags = astgen.instructions.items(.tag);
  11420     return switch (zir_tags[inst]) {
  11421         .alloc_inferred,
  11422         .alloc_inferred_mut,
  11423         .alloc_inferred_comptime,
  11424         .alloc_inferred_comptime_mut,
  11425         => true,
  11426 
  11427         else => false,
  11428     };
  11429 }
  11430 
  11431 /// Assumes capacity for body has already been added. Needed capacity taking into
  11432 /// account fixups can be found with `countBodyLenAfterFixups`.
  11433 fn appendBodyWithFixups(astgen: *AstGen, body: []const Zir.Inst.Index) void {
  11434     return appendBodyWithFixupsArrayList(astgen, &astgen.extra, body);
  11435 }
  11436 
  11437 fn appendBodyWithFixupsArrayList(
  11438     astgen: *AstGen,
  11439     list: *std.ArrayListUnmanaged(u32),
  11440     body: []const Zir.Inst.Index,
  11441 ) void {
  11442     for (body) |body_inst| {
  11443         appendPossiblyRefdBodyInst(astgen, list, body_inst);
  11444     }
  11445 }
  11446 
  11447 fn appendPossiblyRefdBodyInst(
  11448     astgen: *AstGen,
  11449     list: *std.ArrayListUnmanaged(u32),
  11450     body_inst: Zir.Inst.Index,
  11451 ) void {
  11452     list.appendAssumeCapacity(body_inst);
  11453     const kv = astgen.ref_table.fetchRemove(body_inst) orelse return;
  11454     const ref_inst = kv.value;
  11455     return appendPossiblyRefdBodyInst(astgen, list, ref_inst);
  11456 }
  11457 
  11458 fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 {
  11459     var count = body.len;
  11460     for (body) |body_inst| {
  11461         var check_inst = body_inst;
  11462         while (astgen.ref_table.get(check_inst)) |ref_inst| {
  11463             count += 1;
  11464             check_inst = ref_inst;
  11465         }
  11466     }
  11467     return @intCast(u32, count);
  11468 }