zig

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

blob af0fac04 (449374B) - 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.addUnNode(.validate_deref, lhs, 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.addPlNode(.array_type, array_init.ast.type_expr, Zir.Inst.Bin{
   1324                         .lhs = len_inst,
   1325                         .rhs = elem_type,
   1326                     });
   1327                     break :inst .{
   1328                         .array = array_type_inst,
   1329                         .elem = elem_type,
   1330                     };
   1331                 } else {
   1332                     const sentinel = try comptimeExpr(gz, scope, .{ .ty = elem_type }, array_type.ast.sentinel);
   1333                     const array_type_inst = try gz.addPlNode(
   1334                         .array_type_sentinel,
   1335                         array_init.ast.type_expr,
   1336                         Zir.Inst.ArrayTypeSentinel{
   1337                             .len = len_inst,
   1338                             .elem_type = elem_type,
   1339                             .sentinel = sentinel,
   1340                         },
   1341                     );
   1342                     break :inst .{
   1343                         .array = array_type_inst,
   1344                         .elem = elem_type,
   1345                     };
   1346                 }
   1347             }
   1348         }
   1349         const array_type_inst = try typeExpr(gz, scope, array_init.ast.type_expr);
   1350         _ = try gz.addUnNode(.validate_array_init_ty, array_type_inst, array_init.ast.type_expr);
   1351         break :inst .{
   1352             .array = array_type_inst,
   1353             .elem = .none,
   1354         };
   1355     };
   1356 
   1357     switch (rl) {
   1358         .discard => {
   1359             // TODO elements should still be coerced if type is provided
   1360             for (array_init.ast.elements) |elem_init| {
   1361                 _ = try expr(gz, scope, .discard, elem_init);
   1362             }
   1363             return Zir.Inst.Ref.void_value;
   1364         },
   1365         .ref => {
   1366             const tag: Zir.Inst.Tag = if (types.array != .none) .array_init_ref else .array_init_anon_ref;
   1367             return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
   1368         },
   1369         .none => {
   1370             const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;
   1371             return arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
   1372         },
   1373         .ty, .coerced_ty => {
   1374             const tag: Zir.Inst.Tag = if (types.array != .none) .array_init else .array_init_anon;
   1375             const result = try arrayInitExprInner(gz, scope, node, array_init.ast.elements, types.array, types.elem, tag);
   1376             return rvalue(gz, rl, result, node);
   1377         },
   1378         .ptr => |ptr_inst| {
   1379             return arrayInitExprRlPtr(gz, scope, rl, node, ptr_inst, array_init.ast.elements, types.array);
   1380         },
   1381         .inferred_ptr => |ptr_inst| {
   1382             if (types.array == .none) {
   1383                 // We treat this case differently so that we don't get a crash when
   1384                 // analyzing array_base_ptr against an alloc_inferred_mut.
   1385                 // See corresponding logic in structInitExpr.
   1386                 const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon);
   1387                 return rvalue(gz, rl, result, node);
   1388             } else {
   1389                 return arrayInitExprRlPtr(gz, scope, rl, node, ptr_inst, array_init.ast.elements, types.array);
   1390             }
   1391         },
   1392         .block_ptr => |block_gz| {
   1393             // This condition is here for the same reason as the above condition in `inferred_ptr`.
   1394             // See corresponding logic in structInitExpr.
   1395             if (types.array == .none and astgen.isInferred(block_gz.rl_ptr)) {
   1396                 const result = try arrayInitExprRlNone(gz, scope, node, array_init.ast.elements, .array_init_anon);
   1397                 return rvalue(gz, rl, result, node);
   1398             }
   1399             return arrayInitExprRlPtr(gz, scope, rl, node, block_gz.rl_ptr, array_init.ast.elements, types.array);
   1400         },
   1401     }
   1402 }
   1403 
   1404 fn arrayInitExprRlNone(
   1405     gz: *GenZir,
   1406     scope: *Scope,
   1407     node: Ast.Node.Index,
   1408     elements: []const Ast.Node.Index,
   1409     tag: Zir.Inst.Tag,
   1410 ) InnerError!Zir.Inst.Ref {
   1411     const astgen = gz.astgen;
   1412 
   1413     const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{
   1414         .operands_len = @intCast(u32, elements.len),
   1415     });
   1416     var extra_index = try reserveExtra(astgen, elements.len);
   1417 
   1418     for (elements) |elem_init| {
   1419         const elem_ref = try expr(gz, scope, .none, elem_init);
   1420         astgen.extra.items[extra_index] = @enumToInt(elem_ref);
   1421         extra_index += 1;
   1422     }
   1423     return try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1424 }
   1425 
   1426 fn arrayInitExprInner(
   1427     gz: *GenZir,
   1428     scope: *Scope,
   1429     node: Ast.Node.Index,
   1430     elements: []const Ast.Node.Index,
   1431     array_ty_inst: Zir.Inst.Ref,
   1432     elem_ty: Zir.Inst.Ref,
   1433     tag: Zir.Inst.Tag,
   1434 ) InnerError!Zir.Inst.Ref {
   1435     const astgen = gz.astgen;
   1436 
   1437     const len = elements.len + @boolToInt(array_ty_inst != .none);
   1438     const payload_index = try addExtra(astgen, Zir.Inst.MultiOp{
   1439         .operands_len = @intCast(u32, len),
   1440     });
   1441     var extra_index = try reserveExtra(astgen, len);
   1442     if (array_ty_inst != .none) {
   1443         astgen.extra.items[extra_index] = @enumToInt(array_ty_inst);
   1444         extra_index += 1;
   1445     }
   1446 
   1447     for (elements) |elem_init, i| {
   1448         const rl = if (elem_ty != .none)
   1449             ResultLoc{ .coerced_ty = elem_ty }
   1450         else if (array_ty_inst != .none and nodeMayNeedMemoryLocation(astgen.tree, elem_init, true)) rl: {
   1451             const ty_expr = try gz.add(.{
   1452                 .tag = .elem_type_index,
   1453                 .data = .{ .bin = .{
   1454                     .lhs = array_ty_inst,
   1455                     .rhs = @intToEnum(Zir.Inst.Ref, i),
   1456                 } },
   1457             });
   1458             break :rl ResultLoc{ .coerced_ty = ty_expr };
   1459         } else ResultLoc{ .none = {} };
   1460 
   1461         const elem_ref = try expr(gz, scope, rl, elem_init);
   1462         astgen.extra.items[extra_index] = @enumToInt(elem_ref);
   1463         extra_index += 1;
   1464     }
   1465 
   1466     return try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1467 }
   1468 
   1469 fn arrayInitExprRlPtr(
   1470     gz: *GenZir,
   1471     scope: *Scope,
   1472     rl: ResultLoc,
   1473     node: Ast.Node.Index,
   1474     result_ptr: Zir.Inst.Ref,
   1475     elements: []const Ast.Node.Index,
   1476     array_ty: Zir.Inst.Ref,
   1477 ) InnerError!Zir.Inst.Ref {
   1478     if (array_ty == .none) {
   1479         const base_ptr = try gz.addUnNode(.array_base_ptr, result_ptr, node);
   1480         return arrayInitExprRlPtrInner(gz, scope, node, base_ptr, elements);
   1481     }
   1482 
   1483     var as_scope = try gz.makeCoercionScope(scope, array_ty, result_ptr, node);
   1484     defer as_scope.unstack();
   1485 
   1486     const result = try arrayInitExprRlPtrInner(&as_scope, scope, node, as_scope.rl_ptr, elements);
   1487     return as_scope.finishCoercion(gz, rl, node, result, array_ty);
   1488 }
   1489 
   1490 fn arrayInitExprRlPtrInner(
   1491     gz: *GenZir,
   1492     scope: *Scope,
   1493     node: Ast.Node.Index,
   1494     result_ptr: Zir.Inst.Ref,
   1495     elements: []const Ast.Node.Index,
   1496 ) InnerError!Zir.Inst.Ref {
   1497     const astgen = gz.astgen;
   1498 
   1499     const payload_index = try addExtra(astgen, Zir.Inst.Block{
   1500         .body_len = @intCast(u32, elements.len),
   1501     });
   1502     var extra_index = try reserveExtra(astgen, elements.len);
   1503 
   1504     for (elements) |elem_init, i| {
   1505         const elem_ptr = try gz.addPlNode(.elem_ptr_imm, elem_init, Zir.Inst.ElemPtrImm{
   1506             .ptr = result_ptr,
   1507             .index = @intCast(u32, i),
   1508         });
   1509         astgen.extra.items[extra_index] = refToIndex(elem_ptr).?;
   1510         extra_index += 1;
   1511         _ = try expr(gz, scope, .{ .ptr = elem_ptr }, elem_init);
   1512     }
   1513 
   1514     const tag: Zir.Inst.Tag = if (gz.force_comptime)
   1515         .validate_array_init_comptime
   1516     else
   1517         .validate_array_init;
   1518 
   1519     _ = try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1520     return .void_value;
   1521 }
   1522 
   1523 fn structInitExpr(
   1524     gz: *GenZir,
   1525     scope: *Scope,
   1526     rl: ResultLoc,
   1527     node: Ast.Node.Index,
   1528     struct_init: Ast.full.StructInit,
   1529 ) InnerError!Zir.Inst.Ref {
   1530     const astgen = gz.astgen;
   1531     const tree = astgen.tree;
   1532 
   1533     if (struct_init.ast.type_expr == 0) {
   1534         if (struct_init.ast.fields.len == 0) {
   1535             return rvalue(gz, rl, .empty_struct, node);
   1536         }
   1537     } else array: {
   1538         const node_tags = tree.nodes.items(.tag);
   1539         const main_tokens = tree.nodes.items(.main_token);
   1540         const array_type: Ast.full.ArrayType = switch (node_tags[struct_init.ast.type_expr]) {
   1541             .array_type => tree.arrayType(struct_init.ast.type_expr),
   1542             .array_type_sentinel => tree.arrayTypeSentinel(struct_init.ast.type_expr),
   1543             else => {
   1544                 if (struct_init.ast.fields.len == 0) {
   1545                     const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1546                     const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
   1547                     return rvalue(gz, rl, result, node);
   1548                 }
   1549                 break :array;
   1550             },
   1551         };
   1552         const is_inferred_array_len = node_tags[array_type.ast.elem_count] == .identifier and
   1553             // This intentionally does not support `@"_"` syntax.
   1554             mem.eql(u8, tree.tokenSlice(main_tokens[array_type.ast.elem_count]), "_");
   1555         if (struct_init.ast.fields.len == 0) {
   1556             if (is_inferred_array_len) {
   1557                 const elem_type = try typeExpr(gz, scope, array_type.ast.elem_type);
   1558                 const array_type_inst = if (array_type.ast.sentinel == 0) blk: {
   1559                     break :blk try gz.addPlNode(.array_type, struct_init.ast.type_expr, Zir.Inst.Bin{
   1560                         .lhs = .zero_usize,
   1561                         .rhs = elem_type,
   1562                     });
   1563                 } else blk: {
   1564                     const sentinel = try comptimeExpr(gz, scope, .{ .ty = elem_type }, array_type.ast.sentinel);
   1565                     break :blk try gz.addPlNode(
   1566                         .array_type_sentinel,
   1567                         struct_init.ast.type_expr,
   1568                         Zir.Inst.ArrayTypeSentinel{
   1569                             .len = .zero_usize,
   1570                             .elem_type = elem_type,
   1571                             .sentinel = sentinel,
   1572                         },
   1573                     );
   1574                 };
   1575                 const result = try gz.addUnNode(.struct_init_empty, array_type_inst, node);
   1576                 return rvalue(gz, rl, result, node);
   1577             }
   1578             const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1579             const result = try gz.addUnNode(.struct_init_empty, ty_inst, node);
   1580             return rvalue(gz, rl, result, node);
   1581         } else {
   1582             return astgen.failNode(
   1583                 struct_init.ast.type_expr,
   1584                 "initializing array with struct syntax",
   1585                 .{},
   1586             );
   1587         }
   1588     }
   1589 
   1590     switch (rl) {
   1591         .discard => {
   1592             // TODO if a type expr is given the fields should be validated for that type
   1593             if (struct_init.ast.type_expr != 0) {
   1594                 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1595                 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
   1596             }
   1597             for (struct_init.ast.fields) |field_init| {
   1598                 _ = try expr(gz, scope, .discard, field_init);
   1599             }
   1600             return Zir.Inst.Ref.void_value;
   1601         },
   1602         .ref => {
   1603             if (struct_init.ast.type_expr != 0) {
   1604                 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1605                 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
   1606                 return structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init_ref);
   1607             } else {
   1608                 return structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon_ref);
   1609             }
   1610         },
   1611         .none => {
   1612             if (struct_init.ast.type_expr != 0) {
   1613                 const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1614                 _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
   1615                 return structInitExprRlTy(gz, scope, node, struct_init, ty_inst, .struct_init);
   1616             } else {
   1617                 return structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon);
   1618             }
   1619         },
   1620         .ty, .coerced_ty => |ty_inst| {
   1621             if (struct_init.ast.type_expr == 0) {
   1622                 const result = try structInitExprRlNone(gz, scope, node, struct_init, ty_inst, .struct_init_anon);
   1623                 return rvalue(gz, rl, result, node);
   1624             }
   1625             const inner_ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1626             _ = try gz.addUnNode(.validate_struct_init_ty, inner_ty_inst, node);
   1627             const result = try structInitExprRlTy(gz, scope, node, struct_init, inner_ty_inst, .struct_init);
   1628             return rvalue(gz, rl, result, node);
   1629         },
   1630         .ptr => |ptr_inst| return structInitExprRlPtr(gz, scope, rl, node, struct_init, ptr_inst),
   1631         .inferred_ptr => |ptr_inst| {
   1632             if (struct_init.ast.type_expr == 0) {
   1633                 // We treat this case differently so that we don't get a crash when
   1634                 // analyzing field_base_ptr against an alloc_inferred_mut.
   1635                 // See corresponding logic in arrayInitExpr.
   1636                 const result = try structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon);
   1637                 return rvalue(gz, rl, result, node);
   1638             } else {
   1639                 return structInitExprRlPtr(gz, scope, rl, node, struct_init, ptr_inst);
   1640             }
   1641         },
   1642         .block_ptr => |block_gz| {
   1643             // This condition is here for the same reason as the above condition in `inferred_ptr`.
   1644             // See corresponding logic in arrayInitExpr.
   1645             if (struct_init.ast.type_expr == 0 and astgen.isInferred(block_gz.rl_ptr)) {
   1646                 const result = try structInitExprRlNone(gz, scope, node, struct_init, .none, .struct_init_anon);
   1647                 return rvalue(gz, rl, result, node);
   1648             }
   1649 
   1650             return structInitExprRlPtr(gz, scope, rl, node, struct_init, block_gz.rl_ptr);
   1651         },
   1652     }
   1653 }
   1654 
   1655 fn structInitExprRlNone(
   1656     gz: *GenZir,
   1657     scope: *Scope,
   1658     node: Ast.Node.Index,
   1659     struct_init: Ast.full.StructInit,
   1660     ty_inst: Zir.Inst.Ref,
   1661     tag: Zir.Inst.Tag,
   1662 ) InnerError!Zir.Inst.Ref {
   1663     const astgen = gz.astgen;
   1664     const tree = astgen.tree;
   1665 
   1666     const payload_index = try addExtra(astgen, Zir.Inst.StructInitAnon{
   1667         .fields_len = @intCast(u32, struct_init.ast.fields.len),
   1668     });
   1669     const field_size = @typeInfo(Zir.Inst.StructInitAnon.Item).Struct.fields.len;
   1670     var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size);
   1671 
   1672     for (struct_init.ast.fields) |field_init| {
   1673         const name_token = tree.firstToken(field_init) - 2;
   1674         const str_index = try astgen.identAsString(name_token);
   1675         const sub_rl: ResultLoc = if (ty_inst != .none)
   1676             ResultLoc{ .ty = try gz.addPlNode(.field_type, field_init, Zir.Inst.FieldType{
   1677                 .container_type = ty_inst,
   1678                 .name_start = str_index,
   1679             }) }
   1680         else
   1681             .none;
   1682         setExtra(astgen, extra_index, Zir.Inst.StructInitAnon.Item{
   1683             .field_name = str_index,
   1684             .init = try expr(gz, scope, sub_rl, field_init),
   1685         });
   1686         extra_index += field_size;
   1687     }
   1688 
   1689     return try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1690 }
   1691 
   1692 fn structInitExprRlPtr(
   1693     gz: *GenZir,
   1694     scope: *Scope,
   1695     rl: ResultLoc,
   1696     node: Ast.Node.Index,
   1697     struct_init: Ast.full.StructInit,
   1698     result_ptr: Zir.Inst.Ref,
   1699 ) InnerError!Zir.Inst.Ref {
   1700     if (struct_init.ast.type_expr == 0) {
   1701         const base_ptr = try gz.addUnNode(.field_base_ptr, result_ptr, node);
   1702         return structInitExprRlPtrInner(gz, scope, node, struct_init, base_ptr);
   1703     }
   1704     const ty_inst = try typeExpr(gz, scope, struct_init.ast.type_expr);
   1705     _ = try gz.addUnNode(.validate_struct_init_ty, ty_inst, node);
   1706 
   1707     var as_scope = try gz.makeCoercionScope(scope, ty_inst, result_ptr, node);
   1708     defer as_scope.unstack();
   1709 
   1710     const result = try structInitExprRlPtrInner(&as_scope, scope, node, struct_init, as_scope.rl_ptr);
   1711     return as_scope.finishCoercion(gz, rl, node, result, ty_inst);
   1712 }
   1713 
   1714 fn structInitExprRlPtrInner(
   1715     gz: *GenZir,
   1716     scope: *Scope,
   1717     node: Ast.Node.Index,
   1718     struct_init: Ast.full.StructInit,
   1719     result_ptr: Zir.Inst.Ref,
   1720 ) InnerError!Zir.Inst.Ref {
   1721     const astgen = gz.astgen;
   1722     const tree = astgen.tree;
   1723 
   1724     const payload_index = try addExtra(astgen, Zir.Inst.Block{
   1725         .body_len = @intCast(u32, struct_init.ast.fields.len),
   1726     });
   1727     var extra_index = try reserveExtra(astgen, struct_init.ast.fields.len);
   1728 
   1729     for (struct_init.ast.fields) |field_init| {
   1730         const name_token = tree.firstToken(field_init) - 2;
   1731         const str_index = try astgen.identAsString(name_token);
   1732         const field_ptr = try gz.addPlNode(.field_ptr, field_init, Zir.Inst.Field{
   1733             .lhs = result_ptr,
   1734             .field_name_start = str_index,
   1735         });
   1736         astgen.extra.items[extra_index] = refToIndex(field_ptr).?;
   1737         extra_index += 1;
   1738         _ = try expr(gz, scope, .{ .ptr = field_ptr }, field_init);
   1739     }
   1740 
   1741     const tag: Zir.Inst.Tag = if (gz.force_comptime)
   1742         .validate_struct_init_comptime
   1743     else
   1744         .validate_struct_init;
   1745 
   1746     _ = try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1747     return Zir.Inst.Ref.void_value;
   1748 }
   1749 
   1750 fn structInitExprRlTy(
   1751     gz: *GenZir,
   1752     scope: *Scope,
   1753     node: Ast.Node.Index,
   1754     struct_init: Ast.full.StructInit,
   1755     ty_inst: Zir.Inst.Ref,
   1756     tag: Zir.Inst.Tag,
   1757 ) InnerError!Zir.Inst.Ref {
   1758     const astgen = gz.astgen;
   1759     const tree = astgen.tree;
   1760 
   1761     const payload_index = try addExtra(astgen, Zir.Inst.StructInit{
   1762         .fields_len = @intCast(u32, struct_init.ast.fields.len),
   1763     });
   1764     const field_size = @typeInfo(Zir.Inst.StructInit.Item).Struct.fields.len;
   1765     var extra_index: usize = try reserveExtra(astgen, struct_init.ast.fields.len * field_size);
   1766 
   1767     for (struct_init.ast.fields) |field_init| {
   1768         const name_token = tree.firstToken(field_init) - 2;
   1769         const str_index = try astgen.identAsString(name_token);
   1770         const field_ty_inst = try gz.addPlNode(.field_type, field_init, Zir.Inst.FieldType{
   1771             .container_type = ty_inst,
   1772             .name_start = str_index,
   1773         });
   1774         setExtra(astgen, extra_index, Zir.Inst.StructInit.Item{
   1775             .field_type = refToIndex(field_ty_inst).?,
   1776             .init = try expr(gz, scope, .{ .ty = field_ty_inst }, field_init),
   1777         });
   1778         extra_index += field_size;
   1779     }
   1780 
   1781     return try gz.addPlNodePayloadIndex(tag, node, payload_index);
   1782 }
   1783 
   1784 /// This calls expr in a comptime scope, and is intended to be called as a helper function.
   1785 /// The one that corresponds to `comptime` expression syntax is `comptimeExprAst`.
   1786 fn comptimeExpr(
   1787     gz: *GenZir,
   1788     scope: *Scope,
   1789     rl: ResultLoc,
   1790     node: Ast.Node.Index,
   1791 ) InnerError!Zir.Inst.Ref {
   1792     const prev_force_comptime = gz.force_comptime;
   1793     gz.force_comptime = true;
   1794     defer gz.force_comptime = prev_force_comptime;
   1795 
   1796     return expr(gz, scope, rl, node);
   1797 }
   1798 
   1799 /// This one is for an actual `comptime` syntax, and will emit a compile error if
   1800 /// the scope already has `force_comptime=true`.
   1801 /// See `comptimeExpr` for the helper function for calling expr in a comptime scope.
   1802 fn comptimeExprAst(
   1803     gz: *GenZir,
   1804     scope: *Scope,
   1805     rl: ResultLoc,
   1806     node: Ast.Node.Index,
   1807 ) InnerError!Zir.Inst.Ref {
   1808     const astgen = gz.astgen;
   1809     if (gz.force_comptime) {
   1810         return astgen.failNode(node, "redundant comptime keyword in already comptime scope", .{});
   1811     }
   1812     const tree = astgen.tree;
   1813     const node_datas = tree.nodes.items(.data);
   1814     const body_node = node_datas[node].lhs;
   1815     gz.force_comptime = true;
   1816     const result = try expr(gz, scope, rl, body_node);
   1817     gz.force_comptime = false;
   1818     return result;
   1819 }
   1820 
   1821 fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   1822     const astgen = parent_gz.astgen;
   1823     const tree = astgen.tree;
   1824     const node_datas = tree.nodes.items(.data);
   1825     const break_label = node_datas[node].lhs;
   1826     const rhs = node_datas[node].rhs;
   1827 
   1828     // Look for the label in the scope.
   1829     var scope = parent_scope;
   1830     while (true) {
   1831         switch (scope.tag) {
   1832             .gen_zir => {
   1833                 const block_gz = scope.cast(GenZir).?;
   1834 
   1835                 const block_inst = blk: {
   1836                     if (break_label != 0) {
   1837                         if (block_gz.label) |*label| {
   1838                             if (try astgen.tokenIdentEql(label.token, break_label)) {
   1839                                 label.used = true;
   1840                                 break :blk label.block_inst;
   1841                             }
   1842                         }
   1843                     } else if (block_gz.break_block != 0) {
   1844                         break :blk block_gz.break_block;
   1845                     }
   1846                     scope = block_gz.parent;
   1847                     continue;
   1848                 };
   1849 
   1850                 const break_tag: Zir.Inst.Tag = if (block_gz.is_inline or block_gz.force_comptime)
   1851                     .break_inline
   1852                 else
   1853                     .@"break";
   1854 
   1855                 if (rhs == 0) {
   1856                     try genDefers(parent_gz, scope, parent_scope, .normal_only);
   1857 
   1858                     _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
   1859                     return Zir.Inst.Ref.unreachable_value;
   1860                 }
   1861                 block_gz.break_count += 1;
   1862 
   1863                 // The loop scope has a mechanism to prevent rvalue() from emitting a
   1864                 // store to the result location for the loop body (since it is continues
   1865                 // rather than returning a result from the loop) but here is a `break`
   1866                 // which needs to override this behavior.
   1867                 const prev_rvalue_noresult = parent_gz.rvalue_noresult;
   1868                 parent_gz.rvalue_noresult = .none;
   1869                 const operand = try reachableExpr(parent_gz, parent_scope, block_gz.break_result_loc, rhs, node);
   1870                 const search_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
   1871                 parent_gz.rvalue_noresult = prev_rvalue_noresult;
   1872 
   1873                 try genDefers(parent_gz, scope, parent_scope, .normal_only);
   1874 
   1875                 switch (block_gz.break_result_loc) {
   1876                     .block_ptr => {
   1877                         const br = try parent_gz.addBreak(break_tag, block_inst, operand);
   1878                         try block_gz.labeled_breaks.append(astgen.gpa, .{ .br = br, .search = search_index });
   1879                     },
   1880                     .ptr => {
   1881                         // In this case we don't have any mechanism to intercept it;
   1882                         // we assume the result location is written, and we break with void.
   1883                         _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
   1884                     },
   1885                     .discard => {
   1886                         _ = try parent_gz.addBreak(break_tag, block_inst, .void_value);
   1887                     },
   1888                     else => {
   1889                         _ = try parent_gz.addBreak(break_tag, block_inst, operand);
   1890                     },
   1891                 }
   1892                 return Zir.Inst.Ref.unreachable_value;
   1893             },
   1894             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   1895             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   1896             .namespace => break,
   1897             .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   1898             .top => unreachable,
   1899         }
   1900     }
   1901     if (break_label != 0) {
   1902         const label_name = try astgen.identifierTokenString(break_label);
   1903         return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
   1904     } else {
   1905         return astgen.failNode(node, "break expression outside loop", .{});
   1906     }
   1907 }
   1908 
   1909 fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   1910     const astgen = parent_gz.astgen;
   1911     const tree = astgen.tree;
   1912     const node_datas = tree.nodes.items(.data);
   1913     const break_label = node_datas[node].lhs;
   1914 
   1915     // Look for the label in the scope.
   1916     var scope = parent_scope;
   1917     while (true) {
   1918         switch (scope.tag) {
   1919             .gen_zir => {
   1920                 const gen_zir = scope.cast(GenZir).?;
   1921                 const continue_block = gen_zir.continue_block;
   1922                 if (continue_block == 0) {
   1923                     scope = gen_zir.parent;
   1924                     continue;
   1925                 }
   1926                 if (break_label != 0) blk: {
   1927                     if (gen_zir.label) |*label| {
   1928                         if (try astgen.tokenIdentEql(label.token, break_label)) {
   1929                             label.used = true;
   1930                             break :blk;
   1931                         }
   1932                     }
   1933                     // found continue but either it has a different label, or no label
   1934                     scope = gen_zir.parent;
   1935                     continue;
   1936                 }
   1937 
   1938                 const break_tag: Zir.Inst.Tag = if (gen_zir.is_inline or gen_zir.force_comptime)
   1939                     .break_inline
   1940                 else
   1941                     .@"break";
   1942                 _ = try parent_gz.addBreak(break_tag, continue_block, .void_value);
   1943                 return Zir.Inst.Ref.unreachable_value;
   1944             },
   1945             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   1946             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   1947             .defer_normal => {
   1948                 const defer_scope = scope.cast(Scope.Defer).?;
   1949                 scope = defer_scope.parent;
   1950                 const expr_node = node_datas[defer_scope.defer_node].rhs;
   1951                 try unusedResultDeferExpr(parent_gz, defer_scope, defer_scope.parent, expr_node);
   1952             },
   1953             .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   1954             .namespace => break,
   1955             .top => unreachable,
   1956         }
   1957     }
   1958     if (break_label != 0) {
   1959         const label_name = try astgen.identifierTokenString(break_label);
   1960         return astgen.failTok(break_label, "label not found: '{s}'", .{label_name});
   1961     } else {
   1962         return astgen.failNode(node, "continue expression outside loop", .{});
   1963     }
   1964 }
   1965 
   1966 fn blockExpr(
   1967     gz: *GenZir,
   1968     scope: *Scope,
   1969     rl: ResultLoc,
   1970     block_node: Ast.Node.Index,
   1971     statements: []const Ast.Node.Index,
   1972 ) InnerError!Zir.Inst.Ref {
   1973     const tracy = trace(@src());
   1974     defer tracy.end();
   1975 
   1976     const astgen = gz.astgen;
   1977     const tree = astgen.tree;
   1978     const main_tokens = tree.nodes.items(.main_token);
   1979     const token_tags = tree.tokens.items(.tag);
   1980 
   1981     const lbrace = main_tokens[block_node];
   1982     if (token_tags[lbrace - 1] == .colon and
   1983         token_tags[lbrace - 2] == .identifier)
   1984     {
   1985         return labeledBlockExpr(gz, scope, rl, block_node, statements);
   1986     }
   1987 
   1988     try blockExprStmts(gz, scope, statements);
   1989     return rvalue(gz, rl, .void_value, block_node);
   1990 }
   1991 
   1992 fn checkLabelRedefinition(astgen: *AstGen, parent_scope: *Scope, label: Ast.TokenIndex) !void {
   1993     // Look for the label in the scope.
   1994     var scope = parent_scope;
   1995     while (true) {
   1996         switch (scope.tag) {
   1997             .gen_zir => {
   1998                 const gen_zir = scope.cast(GenZir).?;
   1999                 if (gen_zir.label) |prev_label| {
   2000                     if (try astgen.tokenIdentEql(label, prev_label.token)) {
   2001                         const label_name = try astgen.identifierTokenString(label);
   2002                         return astgen.failTokNotes(label, "redefinition of label '{s}'", .{
   2003                             label_name,
   2004                         }, &[_]u32{
   2005                             try astgen.errNoteTok(
   2006                                 prev_label.token,
   2007                                 "previous definition here",
   2008                                 .{},
   2009                             ),
   2010                         });
   2011                     }
   2012                 }
   2013                 scope = gen_zir.parent;
   2014             },
   2015             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   2016             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   2017             .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   2018             .namespace => break,
   2019             .top => unreachable,
   2020         }
   2021     }
   2022 }
   2023 
   2024 fn labeledBlockExpr(
   2025     gz: *GenZir,
   2026     parent_scope: *Scope,
   2027     rl: ResultLoc,
   2028     block_node: Ast.Node.Index,
   2029     statements: []const Ast.Node.Index,
   2030 ) InnerError!Zir.Inst.Ref {
   2031     const tracy = trace(@src());
   2032     defer tracy.end();
   2033 
   2034     const astgen = gz.astgen;
   2035     const tree = astgen.tree;
   2036     const main_tokens = tree.nodes.items(.main_token);
   2037     const token_tags = tree.tokens.items(.tag);
   2038 
   2039     const lbrace = main_tokens[block_node];
   2040     const label_token = lbrace - 2;
   2041     assert(token_tags[label_token] == .identifier);
   2042 
   2043     try astgen.checkLabelRedefinition(parent_scope, label_token);
   2044 
   2045     // Reserve the Block ZIR instruction index so that we can put it into the GenZir struct
   2046     // so that break statements can reference it.
   2047     const block_tag: Zir.Inst.Tag = if (gz.force_comptime) .block_inline else .block;
   2048     const block_inst = try gz.makeBlockInst(block_tag, block_node);
   2049     try gz.instructions.append(astgen.gpa, block_inst);
   2050 
   2051     var block_scope = gz.makeSubBlock(parent_scope);
   2052     block_scope.label = GenZir.Label{
   2053         .token = label_token,
   2054         .block_inst = block_inst,
   2055     };
   2056     block_scope.setBreakResultLoc(rl);
   2057     defer block_scope.unstack();
   2058     defer block_scope.labeled_breaks.deinit(astgen.gpa);
   2059 
   2060     try blockExprStmts(&block_scope, &block_scope.base, statements);
   2061     if (!block_scope.endsWithNoReturn()) {
   2062         const break_tag: Zir.Inst.Tag = if (block_scope.force_comptime) .break_inline else .@"break";
   2063         _ = try block_scope.addBreak(break_tag, block_inst, .void_value);
   2064     }
   2065 
   2066     if (!block_scope.label.?.used) {
   2067         try astgen.appendErrorTok(label_token, "unused block label", .{});
   2068     }
   2069 
   2070     const zir_datas = gz.astgen.instructions.items(.data);
   2071     const zir_tags = gz.astgen.instructions.items(.tag);
   2072     const strat = rl.strategy(&block_scope);
   2073     switch (strat.tag) {
   2074         .break_void => {
   2075             // The code took advantage of the result location as a pointer.
   2076             // Turn the break instruction operands into void.
   2077             for (block_scope.labeled_breaks.items) |br| {
   2078                 zir_datas[br.br].@"break".operand = .void_value;
   2079             }
   2080             try block_scope.setBlockBody(block_inst);
   2081 
   2082             return indexToRef(block_inst);
   2083         },
   2084         .break_operand => {
   2085             // All break operands are values that did not use the result location pointer.
   2086             // The break instructions need to have their operands coerced if the
   2087             // block's result location is a `ty`. In this case we overwrite the
   2088             // `store_to_block_ptr` instruction with an `as` instruction and repurpose
   2089             // it as the break operand.
   2090             // This corresponds to similar code in `setCondBrPayloadElideBlockStorePtr`.
   2091             if (block_scope.rl_ty_inst != .none) {
   2092                 for (block_scope.labeled_breaks.items) |br| {
   2093                     // We expect the `store_to_block_ptr` to be created between 1-3 instructions
   2094                     // prior to the break.
   2095                     var search_index = br.search -| 3;
   2096                     while (search_index < br.search) : (search_index += 1) {
   2097                         if (zir_tags[search_index] == .store_to_block_ptr and
   2098                             zir_datas[search_index].bin.lhs == block_scope.rl_ptr)
   2099                         {
   2100                             zir_tags[search_index] = .as;
   2101                             zir_datas[search_index].bin = .{
   2102                                 .lhs = block_scope.rl_ty_inst,
   2103                                 .rhs = zir_datas[br.br].@"break".operand,
   2104                             };
   2105                             zir_datas[br.br].@"break".operand = indexToRef(search_index);
   2106                             break;
   2107                         }
   2108                     } else unreachable;
   2109                 }
   2110             }
   2111             try block_scope.setBlockBody(block_inst);
   2112             const block_ref = indexToRef(block_inst);
   2113             switch (rl) {
   2114                 .ref => return block_ref,
   2115                 else => return rvalue(gz, rl, block_ref, block_node),
   2116             }
   2117         },
   2118     }
   2119 }
   2120 
   2121 fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const Ast.Node.Index) !void {
   2122     const astgen = gz.astgen;
   2123     const tree = astgen.tree;
   2124     const node_tags = tree.nodes.items(.tag);
   2125 
   2126     if (statements.len == 0) return;
   2127 
   2128     try gz.addDbgBlockBegin();
   2129 
   2130     var block_arena = std.heap.ArenaAllocator.init(gz.astgen.gpa);
   2131     defer block_arena.deinit();
   2132     const block_arena_allocator = block_arena.allocator();
   2133 
   2134     var noreturn_src_node: Ast.Node.Index = 0;
   2135     var scope = parent_scope;
   2136     for (statements) |statement| {
   2137         if (noreturn_src_node != 0) {
   2138             try astgen.appendErrorNodeNotes(
   2139                 statement,
   2140                 "unreachable code",
   2141                 .{},
   2142                 &[_]u32{
   2143                     try astgen.errNoteNode(
   2144                         noreturn_src_node,
   2145                         "control flow is diverted here",
   2146                         .{},
   2147                     ),
   2148                 },
   2149             );
   2150         }
   2151         switch (node_tags[statement]) {
   2152             // zig fmt: off
   2153             .global_var_decl  => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.globalVarDecl(statement)),
   2154             .local_var_decl   => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.localVarDecl(statement)),
   2155             .simple_var_decl  => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.simpleVarDecl(statement)),
   2156             .aligned_var_decl => scope = try varDecl(gz, scope, statement, block_arena_allocator, tree.alignedVarDecl(statement)),
   2157 
   2158             .@"defer"    => scope = try makeDeferScope(gz.astgen, scope, statement, block_arena_allocator, .defer_normal),
   2159             .@"errdefer" => scope = try makeDeferScope(gz.astgen, scope, statement, block_arena_allocator, .defer_error),
   2160 
   2161             .assign => try assign(gz, scope, statement),
   2162 
   2163             .assign_shl => try assignShift(gz, scope, statement, .shl),
   2164             .assign_shr => try assignShift(gz, scope, statement, .shr),
   2165 
   2166             .assign_bit_and  => try assignOp(gz, scope, statement, .bit_and),
   2167             .assign_bit_or   => try assignOp(gz, scope, statement, .bit_or),
   2168             .assign_bit_xor  => try assignOp(gz, scope, statement, .xor),
   2169             .assign_div      => try assignOp(gz, scope, statement, .div),
   2170             .assign_sub      => try assignOp(gz, scope, statement, .sub),
   2171             .assign_sub_wrap => try assignOp(gz, scope, statement, .subwrap),
   2172             .assign_mod      => try assignOp(gz, scope, statement, .mod_rem),
   2173             .assign_add      => try assignOp(gz, scope, statement, .add),
   2174             .assign_add_wrap => try assignOp(gz, scope, statement, .addwrap),
   2175             .assign_mul      => try assignOp(gz, scope, statement, .mul),
   2176             .assign_mul_wrap => try assignOp(gz, scope, statement, .mulwrap),
   2177 
   2178             else => noreturn_src_node = try unusedResultExpr(gz, scope, statement),
   2179             // zig fmt: on
   2180         }
   2181     }
   2182 
   2183     try gz.addDbgBlockEnd();
   2184 
   2185     try genDefers(gz, parent_scope, scope, .normal_only);
   2186     try checkUsed(gz, parent_scope, scope);
   2187 }
   2188 
   2189 fn unusedResultDeferExpr(gz: *GenZir, defer_scope: *Scope.Defer, expr_scope: *Scope, expr_node: Ast.Node.Index) InnerError!void {
   2190     const astgen = gz.astgen;
   2191     const prev_offset = astgen.source_offset;
   2192     const prev_line = astgen.source_line;
   2193     const prev_column = astgen.source_column;
   2194     defer {
   2195         astgen.source_offset = prev_offset;
   2196         astgen.source_line = prev_line;
   2197         astgen.source_column = prev_column;
   2198     }
   2199     astgen.source_offset = defer_scope.source_offset;
   2200     astgen.source_line = defer_scope.source_line;
   2201     astgen.source_column = defer_scope.source_column;
   2202     _ = try unusedResultExpr(gz, expr_scope, expr_node);
   2203 }
   2204 
   2205 /// Returns AST source node of the thing that is noreturn if the statement is
   2206 /// definitely `noreturn`. Otherwise returns 0.
   2207 fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) InnerError!Ast.Node.Index {
   2208     try emitDbgNode(gz, statement);
   2209     // We need to emit an error if the result is not `noreturn` or `void`, but
   2210     // we want to avoid adding the ZIR instruction if possible for performance.
   2211     const maybe_unused_result = try expr(gz, scope, .none, statement);
   2212     var noreturn_src_node: Ast.Node.Index = 0;
   2213     const elide_check = if (refToIndex(maybe_unused_result)) |inst| b: {
   2214         // Note that this array becomes invalid after appending more items to it
   2215         // in the above while loop.
   2216         const zir_tags = gz.astgen.instructions.items(.tag);
   2217         switch (zir_tags[inst]) {
   2218             // For some instructions, modify the zir data
   2219             // so we can avoid a separate ensure_result_used instruction.
   2220             .call => {
   2221                 const extra_index = gz.astgen.instructions.items(.data)[inst].pl_node.payload_index;
   2222                 const slot = &gz.astgen.extra.items[extra_index];
   2223                 var flags = @bitCast(Zir.Inst.Call.Flags, slot.*);
   2224                 flags.ensure_result_used = true;
   2225                 slot.* = @bitCast(u32, flags);
   2226                 break :b true;
   2227             },
   2228             .builtin_call => {
   2229                 const extra_index = gz.astgen.instructions.items(.data)[inst].pl_node.payload_index;
   2230                 const slot = &gz.astgen.extra.items[extra_index];
   2231                 var flags = @bitCast(Zir.Inst.BuiltinCall.Flags, slot.*);
   2232                 flags.ensure_result_used = true;
   2233                 slot.* = @bitCast(u32, flags);
   2234                 break :b true;
   2235             },
   2236 
   2237             // ZIR instructions that might be a type other than `noreturn` or `void`.
   2238             .add,
   2239             .addwrap,
   2240             .add_sat,
   2241             .param,
   2242             .param_comptime,
   2243             .param_anytype,
   2244             .param_anytype_comptime,
   2245             .alloc,
   2246             .alloc_mut,
   2247             .alloc_comptime_mut,
   2248             .alloc_inferred,
   2249             .alloc_inferred_mut,
   2250             .alloc_inferred_comptime,
   2251             .alloc_inferred_comptime_mut,
   2252             .make_ptr_const,
   2253             .array_cat,
   2254             .array_mul,
   2255             .array_type,
   2256             .array_type_sentinel,
   2257             .elem_type_index,
   2258             .vector_type,
   2259             .indexable_ptr_len,
   2260             .anyframe_type,
   2261             .as,
   2262             .as_node,
   2263             .bit_and,
   2264             .bitcast,
   2265             .bit_or,
   2266             .block,
   2267             .block_inline,
   2268             .suspend_block,
   2269             .loop,
   2270             .bool_br_and,
   2271             .bool_br_or,
   2272             .bool_not,
   2273             .cmp_lt,
   2274             .cmp_lte,
   2275             .cmp_eq,
   2276             .cmp_gte,
   2277             .cmp_gt,
   2278             .cmp_neq,
   2279             .coerce_result_ptr,
   2280             .decl_ref,
   2281             .decl_val,
   2282             .load,
   2283             .div,
   2284             .elem_ptr,
   2285             .elem_val,
   2286             .elem_ptr_node,
   2287             .elem_ptr_imm,
   2288             .elem_val_node,
   2289             .field_ptr,
   2290             .field_val,
   2291             .field_call_bind,
   2292             .field_ptr_named,
   2293             .field_val_named,
   2294             .func,
   2295             .func_inferred,
   2296             .func_fancy,
   2297             .int,
   2298             .int_big,
   2299             .float,
   2300             .float128,
   2301             .int_type,
   2302             .is_non_null,
   2303             .is_non_null_ptr,
   2304             .is_non_err,
   2305             .is_non_err_ptr,
   2306             .mod_rem,
   2307             .mul,
   2308             .mulwrap,
   2309             .mul_sat,
   2310             .ref,
   2311             .shl,
   2312             .shl_sat,
   2313             .shr,
   2314             .str,
   2315             .sub,
   2316             .subwrap,
   2317             .sub_sat,
   2318             .negate,
   2319             .negate_wrap,
   2320             .typeof,
   2321             .typeof_builtin,
   2322             .xor,
   2323             .optional_type,
   2324             .optional_payload_safe,
   2325             .optional_payload_unsafe,
   2326             .optional_payload_safe_ptr,
   2327             .optional_payload_unsafe_ptr,
   2328             .err_union_payload_safe,
   2329             .err_union_payload_unsafe,
   2330             .err_union_payload_safe_ptr,
   2331             .err_union_payload_unsafe_ptr,
   2332             .err_union_code,
   2333             .err_union_code_ptr,
   2334             .ptr_type,
   2335             .overflow_arithmetic_ptr,
   2336             .enum_literal,
   2337             .merge_error_sets,
   2338             .error_union_type,
   2339             .bit_not,
   2340             .error_value,
   2341             .slice_start,
   2342             .slice_end,
   2343             .slice_sentinel,
   2344             .import,
   2345             .switch_block,
   2346             .switch_cond,
   2347             .switch_cond_ref,
   2348             .switch_capture,
   2349             .switch_capture_ref,
   2350             .switch_capture_multi,
   2351             .switch_capture_multi_ref,
   2352             .struct_init_empty,
   2353             .struct_init,
   2354             .struct_init_ref,
   2355             .struct_init_anon,
   2356             .struct_init_anon_ref,
   2357             .array_init,
   2358             .array_init_anon,
   2359             .array_init_ref,
   2360             .array_init_anon_ref,
   2361             .union_init,
   2362             .field_type,
   2363             .field_type_ref,
   2364             .error_set_decl,
   2365             .error_set_decl_anon,
   2366             .error_set_decl_func,
   2367             .int_to_enum,
   2368             .enum_to_int,
   2369             .type_info,
   2370             .size_of,
   2371             .bit_size_of,
   2372             .log2_int_type,
   2373             .typeof_log2_int_type,
   2374             .ptr_to_int,
   2375             .align_of,
   2376             .bool_to_int,
   2377             .embed_file,
   2378             .error_name,
   2379             .sqrt,
   2380             .sin,
   2381             .cos,
   2382             .tan,
   2383             .exp,
   2384             .exp2,
   2385             .log,
   2386             .log2,
   2387             .log10,
   2388             .fabs,
   2389             .floor,
   2390             .ceil,
   2391             .trunc,
   2392             .round,
   2393             .tag_name,
   2394             .reify,
   2395             .type_name,
   2396             .frame_type,
   2397             .frame_size,
   2398             .float_to_int,
   2399             .int_to_float,
   2400             .int_to_ptr,
   2401             .float_cast,
   2402             .int_cast,
   2403             .ptr_cast,
   2404             .truncate,
   2405             .align_cast,
   2406             .has_decl,
   2407             .has_field,
   2408             .clz,
   2409             .ctz,
   2410             .pop_count,
   2411             .byte_swap,
   2412             .bit_reverse,
   2413             .div_exact,
   2414             .div_floor,
   2415             .div_trunc,
   2416             .mod,
   2417             .rem,
   2418             .shl_exact,
   2419             .shr_exact,
   2420             .bit_offset_of,
   2421             .offset_of,
   2422             .cmpxchg_strong,
   2423             .cmpxchg_weak,
   2424             .splat,
   2425             .reduce,
   2426             .shuffle,
   2427             .atomic_load,
   2428             .atomic_rmw,
   2429             .mul_add,
   2430             .field_parent_ptr,
   2431             .maximum,
   2432             .minimum,
   2433             .builtin_async_call,
   2434             .c_import,
   2435             .@"resume",
   2436             .@"await",
   2437             .ret_err_value_code,
   2438             .closure_get,
   2439             .array_base_ptr,
   2440             .field_base_ptr,
   2441             .param_type,
   2442             .ret_ptr,
   2443             .ret_type,
   2444             .@"try",
   2445             .try_ptr,
   2446             //.try_inline,
   2447             //.try_ptr_inline,
   2448             => break :b false,
   2449 
   2450             .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) {
   2451                 .breakpoint,
   2452                 .fence,
   2453                 .set_align_stack,
   2454                 .set_float_mode,
   2455                 => break :b true,
   2456                 else => break :b false,
   2457             },
   2458 
   2459             // ZIR instructions that are always `noreturn`.
   2460             .@"break",
   2461             .break_inline,
   2462             .condbr,
   2463             .condbr_inline,
   2464             .compile_error,
   2465             .ret_node,
   2466             .ret_load,
   2467             .ret_tok,
   2468             .ret_err_value,
   2469             .@"unreachable",
   2470             .repeat,
   2471             .repeat_inline,
   2472             .panic,
   2473             .panic_comptime,
   2474             => {
   2475                 noreturn_src_node = statement;
   2476                 break :b true;
   2477             },
   2478 
   2479             // ZIR instructions that are always `void`.
   2480             .dbg_stmt,
   2481             .dbg_var_ptr,
   2482             .dbg_var_val,
   2483             .dbg_block_begin,
   2484             .dbg_block_end,
   2485             .ensure_result_used,
   2486             .ensure_result_non_error,
   2487             .@"export",
   2488             .export_value,
   2489             .set_eval_branch_quota,
   2490             .ensure_err_payload_void,
   2491             .atomic_store,
   2492             .store,
   2493             .store_node,
   2494             .store_to_block_ptr,
   2495             .store_to_inferred_ptr,
   2496             .resolve_inferred_alloc,
   2497             .validate_struct_init,
   2498             .validate_struct_init_comptime,
   2499             .validate_array_init,
   2500             .validate_array_init_comptime,
   2501             .set_cold,
   2502             .set_runtime_safety,
   2503             .closure_capture,
   2504             .memcpy,
   2505             .memset,
   2506             .validate_array_init_ty,
   2507             .validate_struct_init_ty,
   2508             .validate_deref,
   2509             => break :b true,
   2510         }
   2511     } else switch (maybe_unused_result) {
   2512         .none => unreachable,
   2513 
   2514         .unreachable_value => b: {
   2515             noreturn_src_node = statement;
   2516             break :b true;
   2517         },
   2518 
   2519         .void_value => true,
   2520 
   2521         else => false,
   2522     };
   2523     if (!elide_check) {
   2524         _ = try gz.addUnNode(.ensure_result_used, maybe_unused_result, statement);
   2525     }
   2526     return noreturn_src_node;
   2527 }
   2528 
   2529 fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct {
   2530     have_any: bool,
   2531     have_normal: bool,
   2532     have_err: bool,
   2533     need_err_code: bool,
   2534 } {
   2535     const tree = astgen.tree;
   2536     const node_datas = tree.nodes.items(.data);
   2537 
   2538     var have_normal = false;
   2539     var have_err = false;
   2540     var need_err_code = false;
   2541     var scope = inner_scope;
   2542     while (scope != outer_scope) {
   2543         switch (scope.tag) {
   2544             .gen_zir => scope = scope.cast(GenZir).?.parent,
   2545             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   2546             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   2547             .defer_normal => {
   2548                 const defer_scope = scope.cast(Scope.Defer).?;
   2549                 scope = defer_scope.parent;
   2550 
   2551                 have_normal = true;
   2552             },
   2553             .defer_error => {
   2554                 const defer_scope = scope.cast(Scope.Defer).?;
   2555                 scope = defer_scope.parent;
   2556 
   2557                 have_err = true;
   2558 
   2559                 const have_err_payload = node_datas[defer_scope.defer_node].lhs != 0;
   2560                 need_err_code = need_err_code or have_err_payload;
   2561             },
   2562             .namespace => unreachable,
   2563             .top => unreachable,
   2564         }
   2565     }
   2566     return .{
   2567         .have_any = have_normal or have_err,
   2568         .have_normal = have_normal,
   2569         .have_err = have_err,
   2570         .need_err_code = need_err_code,
   2571     };
   2572 }
   2573 
   2574 const DefersToEmit = union(enum) {
   2575     both: Zir.Inst.Ref, // err code
   2576     both_sans_err,
   2577     normal_only,
   2578 };
   2579 
   2580 fn genDefers(
   2581     gz: *GenZir,
   2582     outer_scope: *Scope,
   2583     inner_scope: *Scope,
   2584     which_ones: DefersToEmit,
   2585 ) InnerError!void {
   2586     const astgen = gz.astgen;
   2587     const tree = astgen.tree;
   2588     const node_datas = tree.nodes.items(.data);
   2589 
   2590     var scope = inner_scope;
   2591     while (scope != outer_scope) {
   2592         switch (scope.tag) {
   2593             .gen_zir => scope = scope.cast(GenZir).?.parent,
   2594             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
   2595             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
   2596             .defer_normal => {
   2597                 const defer_scope = scope.cast(Scope.Defer).?;
   2598                 scope = defer_scope.parent;
   2599                 const expr_node = node_datas[defer_scope.defer_node].rhs;
   2600                 const prev_in_defer = gz.in_defer;
   2601                 gz.in_defer = true;
   2602                 defer gz.in_defer = prev_in_defer;
   2603                 try unusedResultDeferExpr(gz, defer_scope, defer_scope.parent, expr_node);
   2604             },
   2605             .defer_error => {
   2606                 const defer_scope = scope.cast(Scope.Defer).?;
   2607                 scope = defer_scope.parent;
   2608                 switch (which_ones) {
   2609                     .both_sans_err => {
   2610                         const expr_node = node_datas[defer_scope.defer_node].rhs;
   2611                         const prev_in_defer = gz.in_defer;
   2612                         gz.in_defer = true;
   2613                         defer gz.in_defer = prev_in_defer;
   2614                         try unusedResultDeferExpr(gz, defer_scope, defer_scope.parent, expr_node);
   2615                     },
   2616                     .both => |err_code| {
   2617                         const expr_node = node_datas[defer_scope.defer_node].rhs;
   2618                         const payload_token = node_datas[defer_scope.defer_node].lhs;
   2619                         const prev_in_defer = gz.in_defer;
   2620                         gz.in_defer = true;
   2621                         defer gz.in_defer = prev_in_defer;
   2622                         var local_val_scope: Scope.LocalVal = undefined;
   2623                         try gz.addDbgBlockBegin();
   2624                         const sub_scope = if (payload_token == 0) defer_scope.parent else blk: {
   2625                             const ident_name = try astgen.identAsString(payload_token);
   2626                             local_val_scope = .{
   2627                                 .parent = defer_scope.parent,
   2628                                 .gen_zir = gz,
   2629                                 .name = ident_name,
   2630                                 .inst = err_code,
   2631                                 .token_src = payload_token,
   2632                                 .id_cat = .@"capture",
   2633                             };
   2634                             try gz.addDbgVar(.dbg_var_val, ident_name, err_code);
   2635                             break :blk &local_val_scope.base;
   2636                         };
   2637                         try unusedResultDeferExpr(gz, defer_scope, sub_scope, expr_node);
   2638                         try gz.addDbgBlockEnd();
   2639                     },
   2640                     .normal_only => continue,
   2641                 }
   2642             },
   2643             .namespace => unreachable,
   2644             .top => unreachable,
   2645         }
   2646     }
   2647 }
   2648 
   2649 fn checkUsed(
   2650     gz: *GenZir,
   2651     outer_scope: *Scope,
   2652     inner_scope: *Scope,
   2653 ) InnerError!void {
   2654     const astgen = gz.astgen;
   2655 
   2656     var scope = inner_scope;
   2657     while (scope != outer_scope) {
   2658         switch (scope.tag) {
   2659             .gen_zir => scope = scope.cast(GenZir).?.parent,
   2660             .local_val => {
   2661                 const s = scope.cast(Scope.LocalVal).?;
   2662                 if (!s.used) {
   2663                     try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
   2664                 }
   2665                 scope = s.parent;
   2666             },
   2667             .local_ptr => {
   2668                 const s = scope.cast(Scope.LocalPtr).?;
   2669                 if (!s.used) {
   2670                     try astgen.appendErrorTok(s.token_src, "unused {s}", .{@tagName(s.id_cat)});
   2671                 }
   2672                 scope = s.parent;
   2673             },
   2674             .defer_normal, .defer_error => scope = scope.cast(Scope.Defer).?.parent,
   2675             .namespace => unreachable,
   2676             .top => unreachable,
   2677         }
   2678     }
   2679 }
   2680 
   2681 fn makeDeferScope(
   2682     astgen: *AstGen,
   2683     scope: *Scope,
   2684     node: Ast.Node.Index,
   2685     block_arena: Allocator,
   2686     scope_tag: Scope.Tag,
   2687 ) InnerError!*Scope {
   2688     const tree = astgen.tree;
   2689     const node_datas = tree.nodes.items(.data);
   2690     const expr_node = node_datas[node].rhs;
   2691     const token_starts = tree.tokens.items(.start);
   2692     const node_start = token_starts[tree.firstToken(expr_node)];
   2693     const defer_scope = try block_arena.create(Scope.Defer);
   2694     astgen.advanceSourceCursor(node_start);
   2695 
   2696     defer_scope.* = .{
   2697         .base = .{ .tag = scope_tag },
   2698         .parent = scope,
   2699         .defer_node = node,
   2700         .source_offset = astgen.source_offset,
   2701         .source_line = astgen.source_line,
   2702         .source_column = astgen.source_column,
   2703     };
   2704     return &defer_scope.base;
   2705 }
   2706 
   2707 fn varDecl(
   2708     gz: *GenZir,
   2709     scope: *Scope,
   2710     node: Ast.Node.Index,
   2711     block_arena: Allocator,
   2712     var_decl: Ast.full.VarDecl,
   2713 ) InnerError!*Scope {
   2714     try emitDbgNode(gz, node);
   2715     const astgen = gz.astgen;
   2716     const tree = astgen.tree;
   2717     const token_tags = tree.tokens.items(.tag);
   2718     const main_tokens = tree.nodes.items(.main_token);
   2719 
   2720     const name_token = var_decl.ast.mut_token + 1;
   2721     const ident_name_raw = tree.tokenSlice(name_token);
   2722     if (mem.eql(u8, ident_name_raw, "_")) {
   2723         return astgen.failTok(name_token, "'_' used as an identifier without @\"_\" syntax", .{});
   2724     }
   2725     const ident_name = try astgen.identAsString(name_token);
   2726 
   2727     try astgen.detectLocalShadowing(scope, ident_name, name_token, ident_name_raw);
   2728 
   2729     if (var_decl.ast.init_node == 0) {
   2730         return astgen.failNode(node, "variables must be initialized", .{});
   2731     }
   2732 
   2733     if (var_decl.ast.addrspace_node != 0) {
   2734         return astgen.failTok(main_tokens[var_decl.ast.addrspace_node], "cannot set address space of local variable '{s}'", .{ident_name_raw});
   2735     }
   2736 
   2737     if (var_decl.ast.section_node != 0) {
   2738         return astgen.failTok(main_tokens[var_decl.ast.section_node], "cannot set section of local variable '{s}'", .{ident_name_raw});
   2739     }
   2740 
   2741     const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node != 0)
   2742         try expr(gz, scope, align_rl, var_decl.ast.align_node)
   2743     else
   2744         .none;
   2745 
   2746     switch (token_tags[var_decl.ast.mut_token]) {
   2747         .keyword_const => {
   2748             if (var_decl.comptime_token) |comptime_token| {
   2749                 try astgen.appendErrorTok(comptime_token, "'comptime const' is redundant; instead wrap the initialization expression with 'comptime'", .{});
   2750             }
   2751 
   2752             // Depending on the type of AST the initialization expression is, we may need an lvalue
   2753             // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
   2754             // the variable, no memory location needed.
   2755             const type_node = var_decl.ast.type_node;
   2756             if (align_inst == .none and
   2757                 !nodeMayNeedMemoryLocation(tree, var_decl.ast.init_node, type_node != 0))
   2758             {
   2759                 const result_loc: ResultLoc = if (type_node != 0) .{
   2760                     .ty = try typeExpr(gz, scope, type_node),
   2761                 } else .none;
   2762                 const prev_anon_name_strategy = gz.anon_name_strategy;
   2763                 gz.anon_name_strategy = .dbg_var;
   2764                 const init_inst = try reachableExpr(gz, scope, result_loc, var_decl.ast.init_node, node);
   2765                 gz.anon_name_strategy = prev_anon_name_strategy;
   2766 
   2767                 try gz.addDbgVar(.dbg_var_val, ident_name, init_inst);
   2768 
   2769                 const sub_scope = try block_arena.create(Scope.LocalVal);
   2770                 sub_scope.* = .{
   2771                     .parent = scope,
   2772                     .gen_zir = gz,
   2773                     .name = ident_name,
   2774                     .inst = init_inst,
   2775                     .token_src = name_token,
   2776                     .id_cat = .@"local constant",
   2777                 };
   2778                 return &sub_scope.base;
   2779             }
   2780 
   2781             const is_comptime = gz.force_comptime or
   2782                 tree.nodes.items(.tag)[var_decl.ast.init_node] == .@"comptime";
   2783 
   2784             // Detect whether the initialization expression actually uses the
   2785             // result location pointer.
   2786             var init_scope = gz.makeSubBlock(scope);
   2787             // we may add more instructions to gz before stacking init_scope
   2788             init_scope.instructions_top = GenZir.unstacked_top;
   2789             init_scope.anon_name_strategy = .dbg_var;
   2790             defer init_scope.unstack();
   2791 
   2792             var resolve_inferred_alloc: Zir.Inst.Ref = .none;
   2793             var opt_type_inst: Zir.Inst.Ref = .none;
   2794             if (type_node != 0) {
   2795                 const type_inst = try typeExpr(gz, &init_scope.base, type_node);
   2796                 opt_type_inst = type_inst;
   2797                 if (align_inst == .none) {
   2798                     init_scope.instructions_top = gz.instructions.items.len;
   2799                     init_scope.rl_ptr = try init_scope.addUnNode(.alloc, type_inst, node);
   2800                 } else {
   2801                     init_scope.rl_ptr = try gz.addAllocExtended(.{
   2802                         .node = node,
   2803                         .type_inst = type_inst,
   2804                         .align_inst = align_inst,
   2805                         .is_const = true,
   2806                         .is_comptime = is_comptime,
   2807                     });
   2808                     init_scope.instructions_top = gz.instructions.items.len;
   2809                 }
   2810                 init_scope.rl_ty_inst = type_inst;
   2811             } else {
   2812                 const alloc = if (align_inst == .none) alloc: {
   2813                     init_scope.instructions_top = gz.instructions.items.len;
   2814                     const tag: Zir.Inst.Tag = if (is_comptime)
   2815                         .alloc_inferred_comptime
   2816                     else
   2817                         .alloc_inferred;
   2818                     break :alloc try init_scope.addNode(tag, node);
   2819                 } else alloc: {
   2820                     const ref = try gz.addAllocExtended(.{
   2821                         .node = node,
   2822                         .type_inst = .none,
   2823                         .align_inst = align_inst,
   2824                         .is_const = true,
   2825                         .is_comptime = is_comptime,
   2826                     });
   2827                     init_scope.instructions_top = gz.instructions.items.len;
   2828                     break :alloc ref;
   2829                 };
   2830                 resolve_inferred_alloc = alloc;
   2831                 init_scope.rl_ptr = alloc;
   2832                 init_scope.rl_ty_inst = .none;
   2833             }
   2834             const init_result_loc: ResultLoc = .{ .block_ptr = &init_scope };
   2835             const init_inst = try reachableExpr(&init_scope, &init_scope.base, init_result_loc, var_decl.ast.init_node, node);
   2836 
   2837             const zir_tags = astgen.instructions.items(.tag);
   2838             const zir_datas = astgen.instructions.items(.data);
   2839 
   2840             if (align_inst == .none and init_scope.rvalue_rl_count == 1) {
   2841                 // Result location pointer not used. We don't need an alloc for this
   2842                 // const local, and type inference becomes trivial.
   2843                 // Implicitly move the init_scope instructions into the parent scope,
   2844                 // then elide the alloc instruction and the store_to_block_ptr instruction.
   2845                 var src = init_scope.instructions_top;
   2846                 var dst = src;
   2847                 init_scope.instructions_top = GenZir.unstacked_top;
   2848                 while (src < gz.instructions.items.len) : (src += 1) {
   2849                     const src_inst = gz.instructions.items[src];
   2850                     if (indexToRef(src_inst) == init_scope.rl_ptr) continue;
   2851                     if (zir_tags[src_inst] == .store_to_block_ptr) {
   2852                         if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) continue;
   2853                     }
   2854                     gz.instructions.items[dst] = src_inst;
   2855                     dst += 1;
   2856                 }
   2857                 gz.instructions.items.len = dst;
   2858 
   2859                 // In case the result location did not do the coercion
   2860                 // for us so we must do it here.
   2861                 const coerced_init = if (opt_type_inst != .none)
   2862                     try gz.addBin(.as, opt_type_inst, init_inst)
   2863                 else
   2864                     init_inst;
   2865 
   2866                 try gz.addDbgVar(.dbg_var_val, ident_name, coerced_init);
   2867 
   2868                 const sub_scope = try block_arena.create(Scope.LocalVal);
   2869                 sub_scope.* = .{
   2870                     .parent = scope,
   2871                     .gen_zir = gz,
   2872                     .name = ident_name,
   2873                     .inst = coerced_init,
   2874                     .token_src = name_token,
   2875                     .id_cat = .@"local constant",
   2876                 };
   2877                 return &sub_scope.base;
   2878             }
   2879             // The initialization expression took advantage of the result location
   2880             // of the const local. In this case we will create an alloc and a LocalPtr for it.
   2881             // Implicitly move the init_scope instructions into the parent scope, then swap
   2882             // store_to_block_ptr for store_to_inferred_ptr.
   2883 
   2884             var src = init_scope.instructions_top;
   2885             init_scope.instructions_top = GenZir.unstacked_top;
   2886             while (src < gz.instructions.items.len) : (src += 1) {
   2887                 const src_inst = gz.instructions.items[src];
   2888                 if (zir_tags[src_inst] == .store_to_block_ptr) {
   2889                     if (zir_datas[src_inst].bin.lhs == init_scope.rl_ptr) {
   2890                         if (type_node != 0) {
   2891                             zir_tags[src_inst] = .store;
   2892                         } else {
   2893                             zir_tags[src_inst] = .store_to_inferred_ptr;
   2894                         }
   2895                     }
   2896                 }
   2897             }
   2898             if (resolve_inferred_alloc != .none) {
   2899                 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
   2900             }
   2901             const const_ptr = try gz.addUnNode(.make_ptr_const, init_scope.rl_ptr, node);
   2902 
   2903             try gz.addDbgVar(.dbg_var_ptr, ident_name, const_ptr);
   2904 
   2905             const sub_scope = try block_arena.create(Scope.LocalPtr);
   2906             sub_scope.* = .{
   2907                 .parent = scope,
   2908                 .gen_zir = gz,
   2909                 .name = ident_name,
   2910                 .ptr = const_ptr,
   2911                 .token_src = name_token,
   2912                 .maybe_comptime = true,
   2913                 .id_cat = .@"local constant",
   2914             };
   2915             return &sub_scope.base;
   2916         },
   2917         .keyword_var => {
   2918             const old_rl_ty_inst = gz.rl_ty_inst;
   2919             defer gz.rl_ty_inst = old_rl_ty_inst;
   2920 
   2921             const is_comptime = var_decl.comptime_token != null or gz.force_comptime;
   2922             var resolve_inferred_alloc: Zir.Inst.Ref = .none;
   2923             const var_data: struct {
   2924                 result_loc: ResultLoc,
   2925                 alloc: Zir.Inst.Ref,
   2926             } = if (var_decl.ast.type_node != 0) a: {
   2927                 const type_inst = try typeExpr(gz, scope, var_decl.ast.type_node);
   2928                 const alloc = alloc: {
   2929                     if (align_inst == .none) {
   2930                         const tag: Zir.Inst.Tag = if (is_comptime)
   2931                             .alloc_comptime_mut
   2932                         else
   2933                             .alloc_mut;
   2934                         break :alloc try gz.addUnNode(tag, type_inst, node);
   2935                     } else {
   2936                         break :alloc try gz.addAllocExtended(.{
   2937                             .node = node,
   2938                             .type_inst = type_inst,
   2939                             .align_inst = align_inst,
   2940                             .is_const = false,
   2941                             .is_comptime = is_comptime,
   2942                         });
   2943                     }
   2944                 };
   2945                 gz.rl_ty_inst = type_inst;
   2946                 break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
   2947             } else a: {
   2948                 const alloc = alloc: {
   2949                     if (align_inst == .none) {
   2950                         const tag: Zir.Inst.Tag = if (is_comptime)
   2951                             .alloc_inferred_comptime_mut
   2952                         else
   2953                             .alloc_inferred_mut;
   2954                         break :alloc try gz.addNode(tag, node);
   2955                     } else {
   2956                         break :alloc try gz.addAllocExtended(.{
   2957                             .node = node,
   2958                             .type_inst = .none,
   2959                             .align_inst = align_inst,
   2960                             .is_const = false,
   2961                             .is_comptime = is_comptime,
   2962                         });
   2963                     }
   2964                 };
   2965                 gz.rl_ty_inst = .none;
   2966                 resolve_inferred_alloc = alloc;
   2967                 break :a .{ .alloc = alloc, .result_loc = .{ .inferred_ptr = alloc } };
   2968             };
   2969             const prev_anon_name_strategy = gz.anon_name_strategy;
   2970             gz.anon_name_strategy = .dbg_var;
   2971             _ = try reachableExprComptime(gz, scope, var_data.result_loc, var_decl.ast.init_node, node, is_comptime);
   2972             gz.anon_name_strategy = prev_anon_name_strategy;
   2973             if (resolve_inferred_alloc != .none) {
   2974                 _ = try gz.addUnNode(.resolve_inferred_alloc, resolve_inferred_alloc, node);
   2975             }
   2976 
   2977             try gz.addDbgVar(.dbg_var_ptr, ident_name, var_data.alloc);
   2978 
   2979             const sub_scope = try block_arena.create(Scope.LocalPtr);
   2980             sub_scope.* = .{
   2981                 .parent = scope,
   2982                 .gen_zir = gz,
   2983                 .name = ident_name,
   2984                 .ptr = var_data.alloc,
   2985                 .token_src = name_token,
   2986                 .maybe_comptime = is_comptime,
   2987                 .id_cat = .@"local variable",
   2988             };
   2989             return &sub_scope.base;
   2990         },
   2991         else => unreachable,
   2992     }
   2993 }
   2994 
   2995 fn emitDbgNode(gz: *GenZir, node: Ast.Node.Index) !void {
   2996     // The instruction emitted here is for debugging runtime code.
   2997     // If the current block will be evaluated only during semantic analysis
   2998     // then no dbg_stmt ZIR instruction is needed.
   2999     if (gz.force_comptime) return;
   3000 
   3001     const astgen = gz.astgen;
   3002     astgen.advanceSourceCursorToNode(node);
   3003     const line = astgen.source_line - gz.decl_line;
   3004     const column = astgen.source_column;
   3005 
   3006     _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
   3007         .dbg_stmt = .{
   3008             .line = line,
   3009             .column = column,
   3010         },
   3011     } });
   3012 }
   3013 
   3014 fn assign(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void {
   3015     try emitDbgNode(gz, infix_node);
   3016     const astgen = gz.astgen;
   3017     const tree = astgen.tree;
   3018     const node_datas = tree.nodes.items(.data);
   3019     const main_tokens = tree.nodes.items(.main_token);
   3020     const node_tags = tree.nodes.items(.tag);
   3021 
   3022     const lhs = node_datas[infix_node].lhs;
   3023     const rhs = node_datas[infix_node].rhs;
   3024     if (node_tags[lhs] == .identifier) {
   3025         // This intentionally does not support `@"_"` syntax.
   3026         const ident_name = tree.tokenSlice(main_tokens[lhs]);
   3027         if (mem.eql(u8, ident_name, "_")) {
   3028             _ = try expr(gz, scope, .discard, rhs);
   3029             return;
   3030         }
   3031     }
   3032     const lvalue = try lvalExpr(gz, scope, lhs);
   3033     _ = try expr(gz, scope, .{ .ptr = lvalue }, rhs);
   3034 }
   3035 
   3036 fn assignOp(
   3037     gz: *GenZir,
   3038     scope: *Scope,
   3039     infix_node: Ast.Node.Index,
   3040     op_inst_tag: Zir.Inst.Tag,
   3041 ) InnerError!void {
   3042     try emitDbgNode(gz, infix_node);
   3043     const astgen = gz.astgen;
   3044     const tree = astgen.tree;
   3045     const node_datas = tree.nodes.items(.data);
   3046 
   3047     const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
   3048     const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
   3049     const lhs_type = try gz.addUnNode(.typeof, lhs, infix_node);
   3050     const rhs = try expr(gz, scope, .{ .coerced_ty = lhs_type }, node_datas[infix_node].rhs);
   3051 
   3052     const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{
   3053         .lhs = lhs,
   3054         .rhs = rhs,
   3055     });
   3056     _ = try gz.addBin(.store, lhs_ptr, result);
   3057 }
   3058 
   3059 fn assignShift(
   3060     gz: *GenZir,
   3061     scope: *Scope,
   3062     infix_node: Ast.Node.Index,
   3063     op_inst_tag: Zir.Inst.Tag,
   3064 ) InnerError!void {
   3065     try emitDbgNode(gz, infix_node);
   3066     const astgen = gz.astgen;
   3067     const tree = astgen.tree;
   3068     const node_datas = tree.nodes.items(.data);
   3069 
   3070     const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
   3071     const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
   3072     const rhs_type = try gz.addUnNode(.typeof_log2_int_type, lhs, infix_node);
   3073     const rhs = try expr(gz, scope, .{ .ty = rhs_type }, node_datas[infix_node].rhs);
   3074 
   3075     const result = try gz.addPlNode(op_inst_tag, infix_node, Zir.Inst.Bin{
   3076         .lhs = lhs,
   3077         .rhs = rhs,
   3078     });
   3079     _ = try gz.addBin(.store, lhs_ptr, result);
   3080 }
   3081 
   3082 fn assignShiftSat(gz: *GenZir, scope: *Scope, infix_node: Ast.Node.Index) InnerError!void {
   3083     try emitDbgNode(gz, infix_node);
   3084     const astgen = gz.astgen;
   3085     const tree = astgen.tree;
   3086     const node_datas = tree.nodes.items(.data);
   3087 
   3088     const lhs_ptr = try lvalExpr(gz, scope, node_datas[infix_node].lhs);
   3089     const lhs = try gz.addUnNode(.load, lhs_ptr, infix_node);
   3090     // Saturating shift-left allows any integer type for both the LHS and RHS.
   3091     const rhs = try expr(gz, scope, .none, node_datas[infix_node].rhs);
   3092 
   3093     const result = try gz.addPlNode(.shl_sat, infix_node, Zir.Inst.Bin{
   3094         .lhs = lhs,
   3095         .rhs = rhs,
   3096     });
   3097     _ = try gz.addBin(.store, lhs_ptr, result);
   3098 }
   3099 
   3100 fn ptrType(
   3101     gz: *GenZir,
   3102     scope: *Scope,
   3103     rl: ResultLoc,
   3104     node: Ast.Node.Index,
   3105     ptr_info: Ast.full.PtrType,
   3106 ) InnerError!Zir.Inst.Ref {
   3107     if (ptr_info.size == .C and ptr_info.allowzero_token != null) {
   3108         return gz.astgen.failTok(ptr_info.allowzero_token.?, "C pointers always allow address zero", .{});
   3109     }
   3110 
   3111     const elem_type = try typeExpr(gz, scope, ptr_info.ast.child_type);
   3112 
   3113     var sentinel_ref: Zir.Inst.Ref = .none;
   3114     var align_ref: Zir.Inst.Ref = .none;
   3115     var addrspace_ref: Zir.Inst.Ref = .none;
   3116     var bit_start_ref: Zir.Inst.Ref = .none;
   3117     var bit_end_ref: Zir.Inst.Ref = .none;
   3118     var trailing_count: u32 = 0;
   3119 
   3120     if (ptr_info.ast.sentinel != 0) {
   3121         sentinel_ref = try expr(gz, scope, .{ .ty = elem_type }, ptr_info.ast.sentinel);
   3122         trailing_count += 1;
   3123     }
   3124     if (ptr_info.ast.align_node != 0) {
   3125         align_ref = try expr(gz, scope, coerced_align_rl, ptr_info.ast.align_node);
   3126         trailing_count += 1;
   3127     }
   3128     if (ptr_info.ast.addrspace_node != 0) {
   3129         addrspace_ref = try expr(gz, scope, .{ .ty = .address_space_type }, ptr_info.ast.addrspace_node);
   3130         trailing_count += 1;
   3131     }
   3132     if (ptr_info.ast.bit_range_start != 0) {
   3133         assert(ptr_info.ast.bit_range_end != 0);
   3134         bit_start_ref = try expr(gz, scope, .{ .coerced_ty = .u16_type }, ptr_info.ast.bit_range_start);
   3135         bit_end_ref = try expr(gz, scope, .{ .coerced_ty = .u16_type }, ptr_info.ast.bit_range_end);
   3136         trailing_count += 2;
   3137     }
   3138 
   3139     const gpa = gz.astgen.gpa;
   3140     try gz.instructions.ensureUnusedCapacity(gpa, 1);
   3141     try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
   3142     try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.PtrType).Struct.fields.len +
   3143         trailing_count);
   3144 
   3145     const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.PtrType{
   3146         .elem_type = elem_type,
   3147         .src_node = gz.nodeIndexToRelative(node),
   3148     });
   3149     if (sentinel_ref != .none) {
   3150         gz.astgen.extra.appendAssumeCapacity(@enumToInt(sentinel_ref));
   3151     }
   3152     if (align_ref != .none) {
   3153         gz.astgen.extra.appendAssumeCapacity(@enumToInt(align_ref));
   3154     }
   3155     if (addrspace_ref != .none) {
   3156         gz.astgen.extra.appendAssumeCapacity(@enumToInt(addrspace_ref));
   3157     }
   3158     if (bit_start_ref != .none) {
   3159         gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_start_ref));
   3160         gz.astgen.extra.appendAssumeCapacity(@enumToInt(bit_end_ref));
   3161     }
   3162 
   3163     const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
   3164     const result = indexToRef(new_index);
   3165     gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{
   3166         .ptr_type = .{
   3167             .flags = .{
   3168                 .is_allowzero = ptr_info.allowzero_token != null,
   3169                 .is_mutable = ptr_info.const_token == null,
   3170                 .is_volatile = ptr_info.volatile_token != null,
   3171                 .has_sentinel = sentinel_ref != .none,
   3172                 .has_align = align_ref != .none,
   3173                 .has_addrspace = addrspace_ref != .none,
   3174                 .has_bit_range = bit_start_ref != .none,
   3175             },
   3176             .size = ptr_info.size,
   3177             .payload_index = payload_index,
   3178         },
   3179     } });
   3180     gz.instructions.appendAssumeCapacity(new_index);
   3181 
   3182     return rvalue(gz, rl, result, node);
   3183 }
   3184 
   3185 fn arrayType(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) !Zir.Inst.Ref {
   3186     const astgen = gz.astgen;
   3187     const tree = astgen.tree;
   3188     const node_datas = tree.nodes.items(.data);
   3189     const node_tags = tree.nodes.items(.tag);
   3190     const main_tokens = tree.nodes.items(.main_token);
   3191 
   3192     const len_node = node_datas[node].lhs;
   3193     if (node_tags[len_node] == .identifier and
   3194         mem.eql(u8, tree.tokenSlice(main_tokens[len_node]), "_"))
   3195     {
   3196         return astgen.failNode(len_node, "unable to infer array size", .{});
   3197     }
   3198     const len = try expr(gz, scope, .{ .coerced_ty = .usize_type }, len_node);
   3199     const elem_type = try typeExpr(gz, scope, node_datas[node].rhs);
   3200 
   3201     const result = try gz.addPlNode(.array_type, node, Zir.Inst.Bin{
   3202         .lhs = len,
   3203         .rhs = elem_type,
   3204     });
   3205     return rvalue(gz, rl, result, node);
   3206 }
   3207 
   3208 fn arrayTypeSentinel(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: Ast.Node.Index) !Zir.Inst.Ref {
   3209     const astgen = gz.astgen;
   3210     const tree = astgen.tree;
   3211     const node_datas = tree.nodes.items(.data);
   3212     const node_tags = tree.nodes.items(.tag);
   3213     const main_tokens = tree.nodes.items(.main_token);
   3214     const extra = tree.extraData(node_datas[node].rhs, Ast.Node.ArrayTypeSentinel);
   3215 
   3216     const len_node = node_datas[node].lhs;
   3217     if (node_tags[len_node] == .identifier and
   3218         mem.eql(u8, tree.tokenSlice(main_tokens[len_node]), "_"))
   3219     {
   3220         return astgen.failNode(len_node, "unable to infer array size", .{});
   3221     }
   3222     const len = try reachableExpr(gz, scope, .{ .coerced_ty = .usize_type }, len_node, node);
   3223     const elem_type = try typeExpr(gz, scope, extra.elem_type);
   3224     const sentinel = try reachableExpr(gz, scope, .{ .coerced_ty = elem_type }, extra.sentinel, node);
   3225 
   3226     const result = try gz.addPlNode(.array_type_sentinel, node, Zir.Inst.ArrayTypeSentinel{
   3227         .len = len,
   3228         .elem_type = elem_type,
   3229         .sentinel = sentinel,
   3230     });
   3231     return rvalue(gz, rl, result, node);
   3232 }
   3233 
   3234 const WipMembers = struct {
   3235     payload: *ArrayListUnmanaged(u32),
   3236     payload_top: usize,
   3237     decls_start: u32,
   3238     decls_end: u32,
   3239     field_bits_start: u32,
   3240     fields_start: u32,
   3241     fields_end: u32,
   3242     decl_index: u32 = 0,
   3243     field_index: u32 = 0,
   3244 
   3245     const Self = @This();
   3246     /// struct, union, enum, and opaque decls all use same 4 bits per decl
   3247     const bits_per_decl = 4;
   3248     const decls_per_u32 = 32 / bits_per_decl;
   3249     /// struct, union, enum, and opaque decls all have maximum size of 11 u32 slots
   3250     /// (4 for src_hash + line + name + value + doc_comment + align + link_section + address_space )
   3251     const max_decl_size = 11;
   3252 
   3253     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 {
   3254         const payload_top = @intCast(u32, payload.items.len);
   3255         const decls_start = payload_top + (decl_count + decls_per_u32 - 1) / decls_per_u32;
   3256         const field_bits_start = decls_start + decl_count * max_decl_size;
   3257         const fields_start = field_bits_start + if (bits_per_field > 0) blk: {
   3258             const fields_per_u32 = 32 / bits_per_field;
   3259             break :blk (field_count + fields_per_u32 - 1) / fields_per_u32;
   3260         } else 0;
   3261         const payload_end = fields_start + field_count * max_field_size;
   3262         try payload.resize(gpa, payload_end);
   3263         return Self{
   3264             .payload = payload,
   3265             .payload_top = payload_top,
   3266             .decls_start = decls_start,
   3267             .field_bits_start = field_bits_start,
   3268             .fields_start = fields_start,
   3269             .decls_end = decls_start,
   3270             .fields_end = fields_start,
   3271         };
   3272     }
   3273 
   3274     pub fn nextDecl(self: *Self, is_pub: bool, is_export: bool, has_align: bool, has_section_or_addrspace: bool) void {
   3275         const index = self.payload_top + self.decl_index / decls_per_u32;
   3276         assert(index < self.decls_start);
   3277         const bit_bag: u32 = if (self.decl_index % decls_per_u32 == 0) 0 else self.payload.items[index];
   3278         self.payload.items[index] = (bit_bag >> bits_per_decl) |
   3279             (@as(u32, @boolToInt(is_pub)) << 28) |
   3280             (@as(u32, @boolToInt(is_export)) << 29) |
   3281             (@as(u32, @boolToInt(has_align)) << 30) |
   3282             (@as(u32, @boolToInt(has_section_or_addrspace)) << 31);
   3283         self.decl_index += 1;
   3284     }
   3285 
   3286     pub fn nextField(self: *Self, comptime bits_per_field: u32, bits: [bits_per_field]bool) void {
   3287         const fields_per_u32 = 32 / bits_per_field;
   3288         const index = self.field_bits_start + self.field_index / fields_per_u32;
   3289         assert(index < self.fields_start);
   3290         var bit_bag: u32 = if (self.field_index % fields_per_u32 == 0) 0 else self.payload.items[index];
   3291         bit_bag >>= bits_per_field;
   3292         comptime var i = 0;
   3293         inline while (i < bits_per_field) : (i += 1) {
   3294             bit_bag |= @as(u32, @boolToInt(bits[i])) << (32 - bits_per_field + i);
   3295         }
   3296         self.payload.items[index] = bit_bag;
   3297         self.field_index += 1;
   3298     }
   3299 
   3300     pub fn appendToDecl(self: *Self, data: u32) void {
   3301         assert(self.decls_end < self.field_bits_start);
   3302         self.payload.items[self.decls_end] = data;
   3303         self.decls_end += 1;
   3304     }
   3305 
   3306     pub fn appendToDeclSlice(self: *Self, data: []const u32) void {
   3307         assert(self.decls_end + data.len <= self.field_bits_start);
   3308         mem.copy(u32, self.payload.items[self.decls_end..], data);
   3309         self.decls_end += @intCast(u32, data.len);
   3310     }
   3311 
   3312     pub fn appendToField(self: *Self, data: u32) void {
   3313         assert(self.fields_end < self.payload.items.len);
   3314         self.payload.items[self.fields_end] = data;
   3315         self.fields_end += 1;
   3316     }
   3317 
   3318     pub fn finishBits(self: *Self, comptime bits_per_field: u32) void {
   3319         const empty_decl_slots = decls_per_u32 - (self.decl_index % decls_per_u32);
   3320         if (self.decl_index > 0 and empty_decl_slots < decls_per_u32) {
   3321             const index = self.payload_top + self.decl_index / decls_per_u32;
   3322             self.payload.items[index] >>= @intCast(u5, empty_decl_slots * bits_per_decl);
   3323         }
   3324         if (bits_per_field > 0) {
   3325             const fields_per_u32 = 32 / bits_per_field;
   3326             const empty_field_slots = fields_per_u32 - (self.field_index % fields_per_u32);
   3327             if (self.field_index > 0 and empty_field_slots < fields_per_u32) {
   3328                 const index = self.field_bits_start + self.field_index / fields_per_u32;
   3329                 self.payload.items[index] >>= @intCast(u5, empty_field_slots * bits_per_field);
   3330             }
   3331         }
   3332     }
   3333 
   3334     pub fn declsSlice(self: *Self) []u32 {
   3335         return self.payload.items[self.payload_top..self.decls_end];
   3336     }
   3337 
   3338     pub fn fieldsSlice(self: *Self) []u32 {
   3339         return self.payload.items[self.field_bits_start..self.fields_end];
   3340     }
   3341 
   3342     pub fn deinit(self: *Self) void {
   3343         self.payload.items.len = self.payload_top;
   3344     }
   3345 };
   3346 
   3347 fn fnDecl(
   3348     astgen: *AstGen,
   3349     gz: *GenZir,
   3350     scope: *Scope,
   3351     wip_members: *WipMembers,
   3352     decl_node: Ast.Node.Index,
   3353     body_node: Ast.Node.Index,
   3354     fn_proto: Ast.full.FnProto,
   3355 ) InnerError!void {
   3356     const tree = astgen.tree;
   3357     const token_tags = tree.tokens.items(.tag);
   3358 
   3359     // missing function name already happened in scanDecls()
   3360     const fn_name_token = fn_proto.name_token orelse return error.AnalysisFail;
   3361     const fn_name_str_index = try astgen.identAsString(fn_name_token);
   3362 
   3363     // We insert this at the beginning so that its instruction index marks the
   3364     // start of the top level declaration.
   3365     const block_inst = try gz.makeBlockInst(.block_inline, fn_proto.ast.proto_node);
   3366     astgen.advanceSourceCursorToNode(decl_node);
   3367 
   3368     var decl_gz: GenZir = .{
   3369         .force_comptime = true,
   3370         .in_defer = false,
   3371         .decl_node_index = fn_proto.ast.proto_node,
   3372         .decl_line = astgen.source_line,
   3373         .parent = scope,
   3374         .astgen = astgen,
   3375         .instructions = gz.instructions,
   3376         .instructions_top = gz.instructions.items.len,
   3377     };
   3378     defer decl_gz.unstack();
   3379 
   3380     var fn_gz: GenZir = .{
   3381         .force_comptime = false,
   3382         .in_defer = false,
   3383         .decl_node_index = fn_proto.ast.proto_node,
   3384         .decl_line = decl_gz.decl_line,
   3385         .parent = &decl_gz.base,
   3386         .astgen = astgen,
   3387         .instructions = gz.instructions,
   3388         .instructions_top = GenZir.unstacked_top,
   3389     };
   3390     defer fn_gz.unstack();
   3391 
   3392     // TODO: support noinline
   3393     const is_pub = fn_proto.visib_token != null;
   3394     const is_export = blk: {
   3395         const maybe_export_token = fn_proto.extern_export_inline_token orelse break :blk false;
   3396         break :blk token_tags[maybe_export_token] == .keyword_export;
   3397     };
   3398     const is_extern = blk: {
   3399         const maybe_extern_token = fn_proto.extern_export_inline_token orelse break :blk false;
   3400         break :blk token_tags[maybe_extern_token] == .keyword_extern;
   3401     };
   3402     const has_inline_keyword = blk: {
   3403         const maybe_inline_token = fn_proto.extern_export_inline_token orelse break :blk false;
   3404         break :blk token_tags[maybe_inline_token] == .keyword_inline;
   3405     };
   3406 
   3407     const doc_comment_index = try astgen.docCommentAsString(fn_proto.firstToken());
   3408 
   3409     // align, linksection, and addrspace is passed in the func instruction in this case.
   3410     wip_members.nextDecl(is_pub, is_export, false, false);
   3411 
   3412     var noalias_bits: u32 = 0;
   3413     var params_scope = &fn_gz.base;
   3414     const is_var_args = is_var_args: {
   3415         var param_type_i: usize = 0;
   3416         var it = fn_proto.iterate(tree);
   3417         while (it.next()) |param| : (param_type_i += 1) {
   3418             const is_comptime = if (param.comptime_noalias) |token| switch (token_tags[token]) {
   3419                 .keyword_noalias => is_comptime: {
   3420                     noalias_bits |= @as(u32, 1) << (std.math.cast(u5, param_type_i) orelse
   3421                         return astgen.failTok(token, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
   3422                     break :is_comptime false;
   3423                 },
   3424                 .keyword_comptime => true,
   3425                 else => false,
   3426             } else false;
   3427 
   3428             const is_anytype = if (param.anytype_ellipsis3) |token| blk: {
   3429                 switch (token_tags[token]) {
   3430                     .keyword_anytype => break :blk true,
   3431                     .ellipsis3 => break :is_var_args true,
   3432                     else => unreachable,
   3433                 }
   3434             } else false;
   3435 
   3436             const param_name: u32 = if (param.name_token) |name_token| blk: {
   3437                 const name_bytes = tree.tokenSlice(name_token);
   3438                 if (mem.eql(u8, "_", name_bytes))
   3439                     break :blk 0;
   3440 
   3441                 const param_name = try astgen.identAsString(name_token);
   3442                 if (!is_extern) {
   3443                     try astgen.detectLocalShadowing(params_scope, param_name, name_token, name_bytes);
   3444                 }
   3445                 break :blk param_name;
   3446             } else if (!is_extern) {
   3447                 if (param.anytype_ellipsis3) |tok| {
   3448                     return astgen.failTok(tok, "missing parameter name", .{});
   3449                 } else {
   3450                     return astgen.failNode(param.type_expr, "missing parameter name", .{});
   3451                 }
   3452             } else 0;
   3453 
   3454             const param_inst = if (is_anytype) param: {
   3455                 const name_token = param.name_token orelse param.anytype_ellipsis3.?;
   3456                 const tag: Zir.Inst.Tag = if (is_comptime)
   3457                     .param_anytype_comptime
   3458                 else
   3459                     .param_anytype;
   3460                 break :param try decl_gz.addStrTok(tag, param_name, name_token);
   3461             } else param: {
   3462                 const param_type_node = param.type_expr;
   3463                 assert(param_type_node != 0);
   3464                 var param_gz = decl_gz.makeSubBlock(scope);
   3465                 defer param_gz.unstack();
   3466                 const param_type = try expr(&param_gz, params_scope, coerced_type_rl, param_type_node);
   3467                 const param_inst_expected = @intCast(u32, astgen.instructions.len + 1);
   3468                 _ = try param_gz.addBreak(.break_inline, param_inst_expected, param_type);
   3469 
   3470                 const main_tokens = tree.nodes.items(.main_token);
   3471                 const name_token = param.name_token orelse main_tokens[param_type_node];
   3472                 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param;
   3473                 const param_inst = try decl_gz.addParam(&param_gz, tag, name_token, param_name, param.first_doc_comment);
   3474                 assert(param_inst_expected == param_inst);
   3475                 break :param indexToRef(param_inst);
   3476             };
   3477 
   3478             if (param_name == 0 or is_extern) continue;
   3479 
   3480             const sub_scope = try astgen.arena.create(Scope.LocalVal);
   3481             sub_scope.* = .{
   3482                 .parent = params_scope,
   3483                 .gen_zir = &decl_gz,
   3484                 .name = param_name,
   3485                 .inst = param_inst,
   3486                 .token_src = param.name_token.?,
   3487                 .id_cat = .@"function parameter",
   3488             };
   3489             params_scope = &sub_scope.base;
   3490         }
   3491         break :is_var_args false;
   3492     };
   3493 
   3494     const lib_name: u32 = if (fn_proto.lib_name) |lib_name_token| blk: {
   3495         const lib_name_str = try astgen.strLitAsString(lib_name_token);
   3496         break :blk lib_name_str.index;
   3497     } else 0;
   3498 
   3499     const maybe_bang = tree.firstToken(fn_proto.ast.return_type) - 1;
   3500     const is_inferred_error = token_tags[maybe_bang] == .bang;
   3501 
   3502     // After creating the function ZIR instruction, it will need to update the break
   3503     // instructions inside the expression blocks for align, addrspace, cc, and ret_ty
   3504     // to use the function instruction as the "block" to break from.
   3505 
   3506     var align_gz = decl_gz.makeSubBlock(params_scope);
   3507     defer align_gz.unstack();
   3508     const align_ref: Zir.Inst.Ref = if (fn_proto.ast.align_expr == 0) .none else inst: {
   3509         const inst = try expr(&decl_gz, params_scope, coerced_align_rl, fn_proto.ast.align_expr);
   3510         if (align_gz.instructionsSlice().len == 0) {
   3511             // In this case we will send a len=0 body which can be encoded more efficiently.
   3512             break :inst inst;
   3513         }
   3514         _ = try align_gz.addBreak(.break_inline, 0, inst);
   3515         break :inst inst;
   3516     };
   3517 
   3518     var addrspace_gz = decl_gz.makeSubBlock(params_scope);
   3519     defer addrspace_gz.unstack();
   3520     const addrspace_ref: Zir.Inst.Ref = if (fn_proto.ast.addrspace_expr == 0) .none else inst: {
   3521         const inst = try expr(&decl_gz, params_scope, .{ .coerced_ty = .address_space_type }, fn_proto.ast.addrspace_expr);
   3522         if (addrspace_gz.instructionsSlice().len == 0) {
   3523             // In this case we will send a len=0 body which can be encoded more efficiently.
   3524             break :inst inst;
   3525         }
   3526         _ = try addrspace_gz.addBreak(.break_inline, 0, inst);
   3527         break :inst inst;
   3528     };
   3529 
   3530     var section_gz = decl_gz.makeSubBlock(params_scope);
   3531     defer section_gz.unstack();
   3532     const section_ref: Zir.Inst.Ref = if (fn_proto.ast.section_expr == 0) .none else inst: {
   3533         const inst = try expr(&decl_gz, params_scope, .{ .coerced_ty = .const_slice_u8_type }, fn_proto.ast.section_expr);
   3534         if (section_gz.instructionsSlice().len == 0) {
   3535             // In this case we will send a len=0 body which can be encoded more efficiently.
   3536             break :inst inst;
   3537         }
   3538         _ = try section_gz.addBreak(.break_inline, 0, inst);
   3539         break :inst inst;
   3540     };
   3541 
   3542     var cc_gz = decl_gz.makeSubBlock(params_scope);
   3543     defer cc_gz.unstack();
   3544     const cc_ref: Zir.Inst.Ref = blk: {
   3545         if (fn_proto.ast.callconv_expr != 0) {
   3546             if (has_inline_keyword) {
   3547                 return astgen.failNode(
   3548                     fn_proto.ast.callconv_expr,
   3549                     "explicit callconv incompatible with inline keyword",
   3550                     .{},
   3551                 );
   3552             }
   3553             const inst = try expr(
   3554                 &decl_gz,
   3555                 params_scope,
   3556                 .{ .coerced_ty = .calling_convention_type },
   3557                 fn_proto.ast.callconv_expr,
   3558             );
   3559             if (cc_gz.instructionsSlice().len == 0) {
   3560                 // In this case we will send a len=0 body which can be encoded more efficiently.
   3561                 break :blk inst;
   3562             }
   3563             _ = try cc_gz.addBreak(.break_inline, 0, inst);
   3564             break :blk inst;
   3565         } else if (is_extern) {
   3566             // note: https://github.com/ziglang/zig/issues/5269
   3567             break :blk .calling_convention_c;
   3568         } else if (has_inline_keyword) {
   3569             break :blk .calling_convention_inline;
   3570         } else {
   3571             break :blk .none;
   3572         }
   3573     };
   3574 
   3575     var ret_gz = decl_gz.makeSubBlock(params_scope);
   3576     defer ret_gz.unstack();
   3577     const ret_ref: Zir.Inst.Ref = inst: {
   3578         const inst = try expr(&ret_gz, params_scope, coerced_type_rl, fn_proto.ast.return_type);
   3579         if (ret_gz.instructionsSlice().len == 0) {
   3580             // In this case we will send a len=0 body which can be encoded more efficiently.
   3581             break :inst inst;
   3582         }
   3583         _ = try ret_gz.addBreak(.break_inline, 0, inst);
   3584         break :inst inst;
   3585     };
   3586 
   3587     const func_inst: Zir.Inst.Ref = if (body_node == 0) func: {
   3588         if (!is_extern) {
   3589             return astgen.failTok(fn_proto.ast.fn_token, "non-extern function has no body", .{});
   3590         }
   3591         if (is_inferred_error) {
   3592             return astgen.failTok(maybe_bang, "function prototype may not have inferred error set", .{});
   3593         }
   3594         break :func try decl_gz.addFunc(.{
   3595             .src_node = decl_node,
   3596             .cc_ref = cc_ref,
   3597             .cc_gz = &cc_gz,
   3598             .align_ref = align_ref,
   3599             .align_gz = &align_gz,
   3600             .ret_ref = ret_ref,
   3601             .ret_gz = &ret_gz,
   3602             .section_ref = section_ref,
   3603             .section_gz = &section_gz,
   3604             .addrspace_ref = addrspace_ref,
   3605             .addrspace_gz = &addrspace_gz,
   3606             .param_block = block_inst,
   3607             .body_gz = null,
   3608             .lib_name = lib_name,
   3609             .is_var_args = is_var_args,
   3610             .is_inferred_error = false,
   3611             .is_test = false,
   3612             .is_extern = true,
   3613             .noalias_bits = noalias_bits,
   3614         });
   3615     } else func: {
   3616         if (is_var_args) {
   3617             return astgen.failTok(fn_proto.ast.fn_token, "non-extern function is variadic", .{});
   3618         }
   3619 
   3620         // as a scope, fn_gz encloses ret_gz, but for instruction list, fn_gz stacks on ret_gz
   3621         fn_gz.instructions_top = ret_gz.instructions.items.len;
   3622 
   3623         const prev_fn_block = astgen.fn_block;
   3624         astgen.fn_block = &fn_gz;
   3625         defer astgen.fn_block = prev_fn_block;
   3626 
   3627         astgen.advanceSourceCursorToNode(body_node);
   3628         const lbrace_line = astgen.source_line - decl_gz.decl_line;
   3629         const lbrace_column = astgen.source_column;
   3630 
   3631         _ = try expr(&fn_gz, params_scope, .none, body_node);
   3632         try checkUsed(gz, &fn_gz.base, params_scope);
   3633 
   3634         if (!fn_gz.endsWithNoReturn()) {
   3635             // Since we are adding the return instruction here, we must handle the coercion.
   3636             // We do this by using the `ret_tok` instruction.
   3637             _ = try fn_gz.addUnTok(.ret_tok, .void_value, tree.lastToken(body_node));
   3638         }
   3639 
   3640         break :func try decl_gz.addFunc(.{
   3641             .src_node = decl_node,
   3642             .cc_ref = cc_ref,
   3643             .cc_gz = &cc_gz,
   3644             .align_ref = align_ref,
   3645             .align_gz = &align_gz,
   3646             .ret_ref = ret_ref,
   3647             .ret_gz = &ret_gz,
   3648             .section_ref = section_ref,
   3649             .section_gz = &section_gz,
   3650             .addrspace_ref = addrspace_ref,
   3651             .addrspace_gz = &addrspace_gz,
   3652             .lbrace_line = lbrace_line,
   3653             .lbrace_column = lbrace_column,
   3654             .param_block = block_inst,
   3655             .body_gz = &fn_gz,
   3656             .lib_name = lib_name,
   3657             .is_var_args = is_var_args,
   3658             .is_inferred_error = is_inferred_error,
   3659             .is_test = false,
   3660             .is_extern = false,
   3661             .noalias_bits = noalias_bits,
   3662         });
   3663     };
   3664 
   3665     // We add this at the end so that its instruction index marks the end range
   3666     // of the top level declaration. addFunc already unstacked fn_gz and ret_gz.
   3667     _ = try decl_gz.addBreak(.break_inline, block_inst, func_inst);
   3668     try decl_gz.setBlockBody(block_inst);
   3669 
   3670     {
   3671         const contents_hash = std.zig.hashSrc(tree.getNodeSource(decl_node));
   3672         const casted = @bitCast([4]u32, contents_hash);
   3673         wip_members.appendToDeclSlice(&casted);
   3674     }
   3675     {
   3676         const line_delta = decl_gz.decl_line - gz.decl_line;
   3677         wip_members.appendToDecl(line_delta);
   3678     }
   3679     wip_members.appendToDecl(fn_name_str_index);
   3680     wip_members.appendToDecl(block_inst);
   3681     wip_members.appendToDecl(doc_comment_index);
   3682 }
   3683 
   3684 fn globalVarDecl(
   3685     astgen: *AstGen,
   3686     gz: *GenZir,
   3687     scope: *Scope,
   3688     wip_members: *WipMembers,
   3689     node: Ast.Node.Index,
   3690     var_decl: Ast.full.VarDecl,
   3691 ) InnerError!void {
   3692     const tree = astgen.tree;
   3693     const token_tags = tree.tokens.items(.tag);
   3694 
   3695     const is_mutable = token_tags[var_decl.ast.mut_token] == .keyword_var;
   3696     // We do this at the beginning so that the instruction index marks the range start
   3697     // of the top level declaration.
   3698     const block_inst = try gz.makeBlockInst(.block_inline, node);
   3699 
   3700     const name_token = var_decl.ast.mut_token + 1;
   3701     const name_str_index = try astgen.identAsString(name_token);
   3702     astgen.advanceSourceCursorToNode(node);
   3703 
   3704     var block_scope: GenZir = .{
   3705         .parent = scope,
   3706         .decl_node_index = node,
   3707         .decl_line = astgen.source_line,
   3708         .astgen = astgen,
   3709         .force_comptime = true,
   3710         .in_defer = false,
   3711         .anon_name_strategy = .parent,
   3712         .instructions = gz.instructions,
   3713         .instructions_top = gz.instructions.items.len,
   3714     };
   3715     defer block_scope.unstack();
   3716 
   3717     const is_pub = var_decl.visib_token != null;
   3718     const is_export = blk: {
   3719         const maybe_export_token = var_decl.extern_export_token orelse break :blk false;
   3720         break :blk token_tags[maybe_export_token] == .keyword_export;
   3721     };
   3722     const is_extern = blk: {
   3723         const maybe_extern_token = var_decl.extern_export_token orelse break :blk false;
   3724         break :blk token_tags[maybe_extern_token] == .keyword_extern;
   3725     };
   3726     const align_inst: Zir.Inst.Ref = if (var_decl.ast.align_node == 0) .none else inst: {
   3727         break :inst try expr(&block_scope, &block_scope.base, align_rl, var_decl.ast.align_node);
   3728     };
   3729     const addrspace_inst: Zir.Inst.Ref = if (var_decl.ast.addrspace_node == 0) .none else inst: {
   3730         break :inst try expr(&block_scope, &block_scope.base, .{ .ty = .address_space_type }, var_decl.ast.addrspace_node);
   3731     };
   3732     const section_inst: Zir.Inst.Ref = if (var_decl.ast.section_node == 0) .none else inst: {
   3733         break :inst try comptimeExpr(&block_scope, &block_scope.base, .{ .ty = .const_slice_u8_type }, var_decl.ast.section_node);
   3734     };
   3735     const has_section_or_addrspace = section_inst != .none or addrspace_inst != .none;
   3736     wip_members.nextDecl(is_pub, is_export, align_inst != .none, has_section_or_addrspace);
   3737 
   3738     const is_threadlocal = if (var_decl.threadlocal_token) |tok| blk: {
   3739         if (!is_mutable) {
   3740             return astgen.failTok(tok, "threadlocal variable cannot be constant", .{});
   3741         }
   3742         break :blk true;
   3743     } else false;
   3744 
   3745     const lib_name: u32 = if (var_decl.lib_name) |lib_name_token| blk: {
   3746         const lib_name_str = try astgen.strLitAsString(lib_name_token);
   3747         break :blk lib_name_str.index;
   3748     } else 0;
   3749 
   3750     const doc_comment_index = try astgen.docCommentAsString(var_decl.firstToken());
   3751 
   3752     assert(var_decl.comptime_token == null); // handled by parser
   3753 
   3754     const var_inst: Zir.Inst.Ref = if (var_decl.ast.init_node != 0) vi: {
   3755         if (is_extern) {
   3756             return astgen.failNode(
   3757                 var_decl.ast.init_node,
   3758                 "extern variables have no initializers",
   3759                 .{},
   3760             );
   3761         }
   3762 
   3763         const type_inst: Zir.Inst.Ref = if (var_decl.ast.type_node != 0)
   3764             try expr(
   3765                 &block_scope,
   3766                 &block_scope.base,
   3767                 .{ .ty = .type_type },
   3768                 var_decl.ast.type_node,
   3769             )
   3770         else
   3771             .none;
   3772 
   3773         const init_inst = try expr(
   3774             &block_scope,
   3775             &block_scope.base,
   3776             if (type_inst != .none) .{ .ty = type_inst } else .none,
   3777             var_decl.ast.init_node,
   3778         );
   3779 
   3780         if (is_mutable) {
   3781             const var_inst = try block_scope.addVar(.{
   3782                 .var_type = type_inst,
   3783                 .lib_name = 0,
   3784                 .align_inst = .none, // passed via the decls data
   3785                 .init = init_inst,
   3786                 .is_extern = false,
   3787                 .is_threadlocal = is_threadlocal,
   3788             });
   3789             break :vi var_inst;
   3790         } else {
   3791             break :vi init_inst;
   3792         }
   3793     } else if (!is_extern) {
   3794         return astgen.failNode(node, "variables must be initialized", .{});
   3795     } else if (var_decl.ast.type_node != 0) vi: {
   3796         // Extern variable which has an explicit type.
   3797         const type_inst = try typeExpr(&block_scope, &block_scope.base, var_decl.ast.type_node);
   3798 
   3799         const var_inst = try block_scope.addVar(.{
   3800             .var_type = type_inst,
   3801             .lib_name = lib_name,
   3802             .align_inst = .none, // passed via the decls data
   3803             .init = .none,
   3804             .is_extern = true,
   3805             .is_threadlocal = is_threadlocal,
   3806         });
   3807         break :vi var_inst;
   3808     } else {
   3809         return astgen.failNode(node, "unable to infer variable type", .{});
   3810     };
   3811     // We do this at the end so that the instruction index marks the end
   3812     // range of a top level declaration.
   3813     _ = try block_scope.addBreak(.break_inline, block_inst, var_inst);
   3814     try block_scope.setBlockBody(block_inst);
   3815 
   3816     {
   3817         const contents_hash = std.zig.hashSrc(tree.getNodeSource(node));
   3818         const casted = @bitCast([4]u32, contents_hash);
   3819         wip_members.appendToDeclSlice(&casted);
   3820     }
   3821     {
   3822         const line_delta = block_scope.decl_line - gz.decl_line;
   3823         wip_members.appendToDecl(line_delta);
   3824     }
   3825     wip_members.appendToDecl(name_str_index);
   3826     wip_members.appendToDecl(block_inst);
   3827     wip_members.appendToDecl(doc_comment_index); // doc_comment wip
   3828     if (align_inst != .none) {
   3829         wip_members.appendToDecl(@enumToInt(align_inst));
   3830     }
   3831     if (has_section_or_addrspace) {
   3832         wip_members.appendToDecl(@enumToInt(section_inst));
   3833         wip_members.appendToDecl(@enumToInt(addrspace_inst));
   3834     }
   3835 }
   3836 
   3837 fn comptimeDecl(
   3838     astgen: *AstGen,
   3839     gz: *GenZir,
   3840     scope: *Scope,
   3841     wip_members: *WipMembers,
   3842     node: Ast.Node.Index,
   3843 ) InnerError!void {
   3844     const tree = astgen.tree;
   3845     const node_datas = tree.nodes.items(.data);
   3846     const body_node = node_datas[node].lhs;
   3847 
   3848     // Up top so the ZIR instruction index marks the start range of this
   3849     // top-level declaration.
   3850     const block_inst = try gz.makeBlockInst(.block_inline, node);
   3851     wip_members.nextDecl(false, false, false, false);
   3852     astgen.advanceSourceCursorToNode(node);
   3853 
   3854     var decl_block: GenZir = .{
   3855         .force_comptime = true,
   3856         .in_defer = false,
   3857         .decl_node_index = node,
   3858         .decl_line = astgen.source_line,
   3859         .parent = scope,
   3860         .astgen = astgen,
   3861         .instructions = gz.instructions,
   3862         .instructions_top = gz.instructions.items.len,
   3863     };
   3864     defer decl_block.unstack();
   3865 
   3866     const block_result = try expr(&decl_block, &decl_block.base, .none, body_node);
   3867     if (decl_block.isEmpty() or !decl_block.refIsNoReturn(block_result)) {
   3868         _ = try decl_block.addBreak(.break_inline, block_inst, .void_value);
   3869     }
   3870     try decl_block.setBlockBody(block_inst);
   3871 
   3872     {
   3873         const contents_hash = std.zig.hashSrc(tree.getNodeSource(node));
   3874         const casted = @bitCast([4]u32, contents_hash);
   3875         wip_members.appendToDeclSlice(&casted);
   3876     }
   3877     {
   3878         const line_delta = decl_block.decl_line - gz.decl_line;
   3879         wip_members.appendToDecl(line_delta);
   3880     }
   3881     wip_members.appendToDecl(0);
   3882     wip_members.appendToDecl(block_inst);
   3883     wip_members.appendToDecl(0); // no doc comments on comptime decls
   3884 }
   3885 
   3886 fn usingnamespaceDecl(
   3887     astgen: *AstGen,
   3888     gz: *GenZir,
   3889     scope: *Scope,
   3890     wip_members: *WipMembers,
   3891     node: Ast.Node.Index,
   3892 ) InnerError!void {
   3893     const tree = astgen.tree;
   3894     const node_datas = tree.nodes.items(.data);
   3895 
   3896     const type_expr = node_datas[node].lhs;
   3897     const is_pub = blk: {
   3898         const main_tokens = tree.nodes.items(.main_token);
   3899         const token_tags = tree.tokens.items(.tag);
   3900         const main_token = main_tokens[node];
   3901         break :blk (main_token > 0 and token_tags[main_token - 1] == .keyword_pub);
   3902     };
   3903     // Up top so the ZIR instruction index marks the start range of this
   3904     // top-level declaration.
   3905     const block_inst = try gz.makeBlockInst(.block_inline, node);
   3906     wip_members.nextDecl(is_pub, true, false, false);
   3907     astgen.advanceSourceCursorToNode(node);
   3908 
   3909     var decl_block: GenZir = .{
   3910         .force_comptime = true,
   3911         .in_defer = false,
   3912         .decl_node_index = node,
   3913         .decl_line = astgen.source_line,
   3914         .parent = scope,
   3915         .astgen = astgen,
   3916         .instructions = gz.instructions,
   3917         .instructions_top = gz.instructions.items.len,
   3918     };
   3919     defer decl_block.unstack();
   3920 
   3921     const namespace_inst = try typeExpr(&decl_block, &decl_block.base, type_expr);
   3922     _ = try decl_block.addBreak(.break_inline, block_inst, namespace_inst);
   3923     try decl_block.setBlockBody(block_inst);
   3924 
   3925     {
   3926         const contents_hash = std.zig.hashSrc(tree.getNodeSource(node));
   3927         const casted = @bitCast([4]u32, contents_hash);
   3928         wip_members.appendToDeclSlice(&casted);
   3929     }
   3930     {
   3931         const line_delta = decl_block.decl_line - gz.decl_line;
   3932         wip_members.appendToDecl(line_delta);
   3933     }
   3934     wip_members.appendToDecl(0);
   3935     wip_members.appendToDecl(block_inst);
   3936     wip_members.appendToDecl(0); // no doc comments on usingnamespace decls
   3937 }
   3938 
   3939 fn testDecl(
   3940     astgen: *AstGen,
   3941     gz: *GenZir,
   3942     scope: *Scope,
   3943     wip_members: *WipMembers,
   3944     node: Ast.Node.Index,
   3945 ) InnerError!void {
   3946     const tree = astgen.tree;
   3947     const node_datas = tree.nodes.items(.data);
   3948     const body_node = node_datas[node].rhs;
   3949 
   3950     // Up top so the ZIR instruction index marks the start range of this
   3951     // top-level declaration.
   3952     const block_inst = try gz.makeBlockInst(.block_inline, node);
   3953 
   3954     wip_members.nextDecl(false, false, false, false);
   3955     astgen.advanceSourceCursorToNode(node);
   3956 
   3957     var decl_block: GenZir = .{
   3958         .force_comptime = true,
   3959         .in_defer = false,
   3960         .decl_node_index = node,
   3961         .decl_line = astgen.source_line,
   3962         .parent = scope,
   3963         .astgen = astgen,
   3964         .instructions = gz.instructions,
   3965         .instructions_top = gz.instructions.items.len,
   3966     };
   3967     defer decl_block.unstack();
   3968 
   3969     const main_tokens = tree.nodes.items(.main_token);
   3970     const token_tags = tree.tokens.items(.tag);
   3971     const test_token = main_tokens[node];
   3972     const test_name_token = test_token + 1;
   3973     const test_name_token_tag = token_tags[test_name_token];
   3974     const is_decltest = test_name_token_tag == .identifier;
   3975     const test_name: u32 = blk: {
   3976         if (test_name_token_tag == .string_literal) {
   3977             break :blk try astgen.testNameString(test_name_token);
   3978         } else if (test_name_token_tag == .identifier) {
   3979             const ident_name_raw = tree.tokenSlice(test_name_token);
   3980 
   3981             if (mem.eql(u8, ident_name_raw, "_")) return astgen.failTok(test_name_token, "'_' used as an identifier without @\"_\" syntax", .{});
   3982 
   3983             // if not @"" syntax, just use raw token slice
   3984             if (ident_name_raw[0] != '@') {
   3985                 if (primitives.get(ident_name_raw)) |_| return astgen.failTok(test_name_token, "cannot test a primitive", .{});
   3986 
   3987                 if (ident_name_raw.len >= 2) integer: {
   3988                     const first_c = ident_name_raw[0];
   3989                     if (first_c == 'i' or first_c == 'u') {
   3990                         _ = switch (first_c == 'i') {
   3991                             true => .signed,
   3992                             false => .unsigned,
   3993                         };
   3994                         _ = parseBitCount(ident_name_raw[1..]) catch |err| switch (err) {
   3995                             error.Overflow => return astgen.failTok(
   3996                                 test_name_token,
   3997                                 "primitive integer type '{s}' exceeds maximum bit width of 65535",
   3998                                 .{ident_name_raw},
   3999                             ),
   4000                             error.InvalidCharacter => break :integer,
   4001                         };
   4002                         return astgen.failTok(test_name_token, "cannot test a primitive", .{});
   4003                     }
   4004                 }
   4005             }
   4006 
   4007             // Local variables, including function parameters.
   4008             const name_str_index = try astgen.identAsString(test_name_token);
   4009             var s = scope;
   4010             var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
   4011             var num_namespaces_out: u32 = 0;
   4012             var capturing_namespace: ?*Scope.Namespace = null;
   4013             while (true) switch (s.tag) {
   4014                 .local_val, .local_ptr => unreachable, // a test cannot be in a local scope
   4015                 .gen_zir => s = s.cast(GenZir).?.parent,
   4016                 .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
   4017                 .namespace => {
   4018                     const ns = s.cast(Scope.Namespace).?;
   4019                     if (ns.decls.get(name_str_index)) |i| {
   4020                         if (found_already) |f| {
   4021                             return astgen.failTokNotes(test_name_token, "ambiguous reference", .{}, &.{
   4022                                 try astgen.errNoteNode(f, "declared here", .{}),
   4023                                 try astgen.errNoteNode(i, "also declared here", .{}),
   4024                             });
   4025                         }
   4026                         // We found a match but must continue looking for ambiguous references to decls.
   4027                         found_already = i;
   4028                     }
   4029                     num_namespaces_out += 1;
   4030                     capturing_namespace = ns;
   4031                     s = ns.parent;
   4032                 },
   4033                 .top => break,
   4034             };
   4035             if (found_already == null) {
   4036                 const ident_name = try astgen.identifierTokenString(test_name_token);
   4037                 return astgen.failTok(test_name_token, "use of undeclared identifier '{s}'", .{ident_name});
   4038             }
   4039 
   4040             break :blk name_str_index;
   4041         }
   4042         // String table index 1 has a special meaning here of test decl with no name.
   4043         break :blk 1;
   4044     };
   4045 
   4046     var fn_block: GenZir = .{
   4047         .force_comptime = false,
   4048         .in_defer = false,
   4049         .decl_node_index = node,
   4050         .decl_line = decl_block.decl_line,
   4051         .parent = &decl_block.base,
   4052         .astgen = astgen,
   4053         .instructions = decl_block.instructions,
   4054         .instructions_top = decl_block.instructions.items.len,
   4055     };
   4056     defer fn_block.unstack();
   4057 
   4058     const prev_fn_block = astgen.fn_block;
   4059     astgen.fn_block = &fn_block;
   4060     defer astgen.fn_block = prev_fn_block;
   4061 
   4062     astgen.advanceSourceCursorToNode(body_node);
   4063     const lbrace_line = astgen.source_line - decl_block.decl_line;
   4064     const lbrace_column = astgen.source_column;
   4065 
   4066     const block_result = try expr(&fn_block, &fn_block.base, .none, body_node);
   4067     if (fn_block.isEmpty() or !fn_block.refIsNoReturn(block_result)) {
   4068         // Since we are adding the return instruction here, we must handle the coercion.
   4069         // We do this by using the `ret_tok` instruction.
   4070         _ = try fn_block.addUnTok(.ret_tok, .void_value, tree.lastToken(body_node));
   4071     }
   4072 
   4073     const func_inst = try decl_block.addFunc(.{
   4074         .src_node = node,
   4075 
   4076         .cc_ref = .none,
   4077         .cc_gz = null,
   4078         .align_ref = .none,
   4079         .align_gz = null,
   4080         .ret_ref = .void_type,
   4081         .ret_gz = null,
   4082         .section_ref = .none,
   4083         .section_gz = null,
   4084         .addrspace_ref = .none,
   4085         .addrspace_gz = null,
   4086 
   4087         .lbrace_line = lbrace_line,
   4088         .lbrace_column = lbrace_column,
   4089         .param_block = block_inst,
   4090         .body_gz = &fn_block,
   4091         .lib_name = 0,
   4092         .is_var_args = false,
   4093         .is_inferred_error = true,
   4094         .is_test = true,
   4095         .is_extern = false,
   4096         .noalias_bits = 0,
   4097     });
   4098 
   4099     _ = try decl_block.addBreak(.break_inline, block_inst, func_inst);
   4100     try decl_block.setBlockBody(block_inst);
   4101 
   4102     {
   4103         const contents_hash = std.zig.hashSrc(tree.getNodeSource(node));
   4104         const casted = @bitCast([4]u32, contents_hash);
   4105         wip_members.appendToDeclSlice(&casted);
   4106     }
   4107     {
   4108         const line_delta = decl_block.decl_line - gz.decl_line;
   4109         wip_members.appendToDecl(line_delta);
   4110     }
   4111     if (is_decltest)
   4112         wip_members.appendToDecl(2) // 2 here means that it is a decltest, look at doc comment for name
   4113     else
   4114         wip_members.appendToDecl(test_name);
   4115     wip_members.appendToDecl(block_inst);
   4116     if (is_decltest)
   4117         wip_members.appendToDecl(test_name) // the doc comment on a decltest represents it's name
   4118     else
   4119         wip_members.appendToDecl(0); // no doc comments on test decls
   4120 }
   4121 
   4122 fn structDeclInner(
   4123     gz: *GenZir,
   4124     scope: *Scope,
   4125     node: Ast.Node.Index,
   4126     container_decl: Ast.full.ContainerDecl,
   4127     layout: std.builtin.Type.ContainerLayout,
   4128 ) InnerError!Zir.Inst.Ref {
   4129     const decl_inst = try gz.reserveInstructionIndex();
   4130 
   4131     if (container_decl.ast.members.len == 0) {
   4132         try gz.setStruct(decl_inst, .{
   4133             .src_node = node,
   4134             .layout = layout,
   4135             .fields_len = 0,
   4136             .decls_len = 0,
   4137             .known_non_opv = false,
   4138             .known_comptime_only = false,
   4139         });
   4140         return indexToRef(decl_inst);
   4141     }
   4142 
   4143     const astgen = gz.astgen;
   4144     const gpa = astgen.gpa;
   4145     const tree = astgen.tree;
   4146 
   4147     var namespace: Scope.Namespace = .{
   4148         .parent = scope,
   4149         .node = node,
   4150         .inst = decl_inst,
   4151         .declaring_gz = gz,
   4152     };
   4153     defer namespace.deinit(gpa);
   4154 
   4155     // The struct_decl instruction introduces a scope in which the decls of the struct
   4156     // are in scope, so that field types, alignments, and default value expressions
   4157     // can refer to decls within the struct itself.
   4158     astgen.advanceSourceCursorToNode(node);
   4159     var block_scope: GenZir = .{
   4160         .parent = &namespace.base,
   4161         .decl_node_index = node,
   4162         .decl_line = astgen.source_line,
   4163         .astgen = astgen,
   4164         .force_comptime = true,
   4165         .in_defer = false,
   4166         .instructions = gz.instructions,
   4167         .instructions_top = gz.instructions.items.len,
   4168     };
   4169     defer block_scope.unstack();
   4170 
   4171     const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members);
   4172     const field_count = @intCast(u32, container_decl.ast.members.len - decl_count);
   4173 
   4174     const bits_per_field = 4;
   4175     const max_field_size = 5;
   4176     var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size);
   4177     defer wip_members.deinit();
   4178 
   4179     // We will use the scratch buffer, starting here, for the bodies:
   4180     //    bodies: { // for every fields_len
   4181     //        field_type_body_inst: Inst, // for each field_type_body_len
   4182     //        align_body_inst: Inst, // for each align_body_len
   4183     //        init_body_inst: Inst, // for each init_body_len
   4184     //    }
   4185     // Note that the scratch buffer is simultaneously being used by WipMembers, however
   4186     // it will not access any elements beyond this point in the ArrayList. It also
   4187     // accesses via the ArrayList items field so it can handle the scratch buffer being
   4188     // reallocated.
   4189     // No defer needed here because it is handled by `wip_members.deinit()` above.
   4190     const bodies_start = astgen.scratch.items.len;
   4191 
   4192     var known_non_opv = false;
   4193     var known_comptime_only = false;
   4194     for (container_decl.ast.members) |member_node| {
   4195         const member = switch (try containerMember(gz, &namespace.base, &wip_members, member_node)) {
   4196             .decl => continue,
   4197             .field => |field| field,
   4198         };
   4199 
   4200         const field_name = try astgen.identAsString(member.ast.name_token);
   4201         wip_members.appendToField(field_name);
   4202 
   4203         const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
   4204         wip_members.appendToField(doc_comment_index);
   4205 
   4206         if (member.ast.type_expr == 0) {
   4207             return astgen.failTok(member.ast.name_token, "struct field missing type", .{});
   4208         }
   4209 
   4210         const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr);
   4211         const have_type_body = !block_scope.isEmpty();
   4212         const have_align = member.ast.align_expr != 0;
   4213         const have_value = member.ast.value_expr != 0;
   4214         const is_comptime = member.comptime_token != null;
   4215 
   4216         if (!is_comptime) {
   4217             known_non_opv = known_non_opv or
   4218                 nodeImpliesMoreThanOnePossibleValue(tree, member.ast.type_expr);
   4219             known_comptime_only = known_comptime_only or
   4220                 nodeImpliesComptimeOnly(tree, member.ast.type_expr);
   4221         }
   4222         wip_members.nextField(bits_per_field, .{ have_align, have_value, is_comptime, have_type_body });
   4223 
   4224         if (have_type_body) {
   4225             if (!block_scope.endsWithNoReturn()) {
   4226                 _ = try block_scope.addBreak(.break_inline, decl_inst, field_type);
   4227             }
   4228             const body = block_scope.instructionsSlice();
   4229             const old_scratch_len = astgen.scratch.items.len;
   4230             try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
   4231             appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
   4232             wip_members.appendToField(@intCast(u32, astgen.scratch.items.len - old_scratch_len));
   4233             block_scope.instructions.items.len = block_scope.instructions_top;
   4234         } else {
   4235             wip_members.appendToField(@enumToInt(field_type));
   4236         }
   4237 
   4238         if (have_align) {
   4239             if (layout == .Packed) {
   4240                 try astgen.appendErrorNode(member.ast.align_expr, "unable to override alignment of packed struct fields", .{});
   4241             }
   4242             const align_ref = try expr(&block_scope, &namespace.base, coerced_align_rl, member.ast.align_expr);
   4243             if (!block_scope.endsWithNoReturn()) {
   4244                 _ = try block_scope.addBreak(.break_inline, decl_inst, align_ref);
   4245             }
   4246             const body = block_scope.instructionsSlice();
   4247             const old_scratch_len = astgen.scratch.items.len;
   4248             try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
   4249             appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
   4250             wip_members.appendToField(@intCast(u32, astgen.scratch.items.len - old_scratch_len));
   4251             block_scope.instructions.items.len = block_scope.instructions_top;
   4252         }
   4253 
   4254         if (have_value) {
   4255             const rl: ResultLoc = if (field_type == .none) .none else .{ .coerced_ty = field_type };
   4256 
   4257             const default_inst = try expr(&block_scope, &namespace.base, rl, member.ast.value_expr);
   4258             if (!block_scope.endsWithNoReturn()) {
   4259                 _ = try block_scope.addBreak(.break_inline, decl_inst, default_inst);
   4260             }
   4261             const body = block_scope.instructionsSlice();
   4262             const old_scratch_len = astgen.scratch.items.len;
   4263             try astgen.scratch.ensureUnusedCapacity(gpa, countBodyLenAfterFixups(astgen, body));
   4264             appendBodyWithFixupsArrayList(astgen, &astgen.scratch, body);
   4265             wip_members.appendToField(@intCast(u32, astgen.scratch.items.len - old_scratch_len));
   4266             block_scope.instructions.items.len = block_scope.instructions_top;
   4267         } else if (member.comptime_token) |comptime_token| {
   4268             return astgen.failTok(comptime_token, "comptime field without default initialization value", .{});
   4269         }
   4270     }
   4271 
   4272     try gz.setStruct(decl_inst, .{
   4273         .src_node = node,
   4274         .layout = layout,
   4275         .fields_len = field_count,
   4276         .decls_len = decl_count,
   4277         .known_non_opv = known_non_opv,
   4278         .known_comptime_only = known_comptime_only,
   4279     });
   4280 
   4281     wip_members.finishBits(bits_per_field);
   4282     const decls_slice = wip_members.declsSlice();
   4283     const fields_slice = wip_members.fieldsSlice();
   4284     const bodies_slice = astgen.scratch.items[bodies_start..];
   4285     try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + fields_slice.len + bodies_slice.len);
   4286     astgen.extra.appendSliceAssumeCapacity(decls_slice);
   4287     astgen.extra.appendSliceAssumeCapacity(fields_slice);
   4288     astgen.extra.appendSliceAssumeCapacity(bodies_slice);
   4289 
   4290     block_scope.unstack();
   4291     try gz.addNamespaceCaptures(&namespace);
   4292     return indexToRef(decl_inst);
   4293 }
   4294 
   4295 fn unionDeclInner(
   4296     gz: *GenZir,
   4297     scope: *Scope,
   4298     node: Ast.Node.Index,
   4299     members: []const Ast.Node.Index,
   4300     layout: std.builtin.Type.ContainerLayout,
   4301     arg_node: Ast.Node.Index,
   4302     auto_enum_tok: ?Ast.TokenIndex,
   4303 ) InnerError!Zir.Inst.Ref {
   4304     const decl_inst = try gz.reserveInstructionIndex();
   4305 
   4306     const astgen = gz.astgen;
   4307     const gpa = astgen.gpa;
   4308 
   4309     var namespace: Scope.Namespace = .{
   4310         .parent = scope,
   4311         .node = node,
   4312         .inst = decl_inst,
   4313         .declaring_gz = gz,
   4314     };
   4315     defer namespace.deinit(gpa);
   4316 
   4317     // The union_decl instruction introduces a scope in which the decls of the union
   4318     // are in scope, so that field types, alignments, and default value expressions
   4319     // can refer to decls within the union itself.
   4320     astgen.advanceSourceCursorToNode(node);
   4321     var block_scope: GenZir = .{
   4322         .parent = &namespace.base,
   4323         .decl_node_index = node,
   4324         .decl_line = astgen.source_line,
   4325         .astgen = astgen,
   4326         .force_comptime = true,
   4327         .in_defer = false,
   4328         .instructions = gz.instructions,
   4329         .instructions_top = gz.instructions.items.len,
   4330     };
   4331     defer block_scope.unstack();
   4332 
   4333     const decl_count = try astgen.scanDecls(&namespace, members);
   4334     const field_count = @intCast(u32, members.len - decl_count);
   4335 
   4336     if (layout != .Auto and (auto_enum_tok != null or arg_node != 0)) {
   4337         const layout_str = if (layout == .Extern) "extern" else "packed";
   4338         if (arg_node != 0) {
   4339             return astgen.failNode(arg_node, "{s} union does not support enum tag type", .{layout_str});
   4340         } else {
   4341             return astgen.failTok(auto_enum_tok.?, "{s} union does not support enum tag type", .{layout_str});
   4342         }
   4343     }
   4344 
   4345     const arg_inst: Zir.Inst.Ref = if (arg_node != 0)
   4346         try typeExpr(&block_scope, &namespace.base, arg_node)
   4347     else
   4348         .none;
   4349 
   4350     const bits_per_field = 4;
   4351     const max_field_size = 5;
   4352     var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, field_count, bits_per_field, max_field_size);
   4353     defer wip_members.deinit();
   4354 
   4355     for (members) |member_node| {
   4356         const member = switch (try containerMember(gz, &namespace.base, &wip_members, member_node)) {
   4357             .decl => continue,
   4358             .field => |field| field,
   4359         };
   4360         if (member.comptime_token) |comptime_token| {
   4361             return astgen.failTok(comptime_token, "union fields cannot be marked comptime", .{});
   4362         }
   4363 
   4364         const field_name = try astgen.identAsString(member.ast.name_token);
   4365         wip_members.appendToField(field_name);
   4366 
   4367         const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
   4368         wip_members.appendToField(doc_comment_index);
   4369 
   4370         const have_type = member.ast.type_expr != 0;
   4371         const have_align = member.ast.align_expr != 0;
   4372         const have_value = member.ast.value_expr != 0;
   4373         const unused = false;
   4374         wip_members.nextField(bits_per_field, .{ have_type, have_align, have_value, unused });
   4375 
   4376         if (have_type) {
   4377             const field_type = try typeExpr(&block_scope, &namespace.base, member.ast.type_expr);
   4378             wip_members.appendToField(@enumToInt(field_type));
   4379         } else if (arg_inst == .none and auto_enum_tok == null) {
   4380             return astgen.failNode(member_node, "union field missing type", .{});
   4381         }
   4382         if (have_align) {
   4383             const align_inst = try expr(&block_scope, &block_scope.base, .{ .ty = .u32_type }, member.ast.align_expr);
   4384             wip_members.appendToField(@enumToInt(align_inst));
   4385         }
   4386         if (have_value) {
   4387             if (arg_inst == .none) {
   4388                 return astgen.failNodeNotes(
   4389                     node,
   4390                     "explicitly valued tagged union missing integer tag type",
   4391                     .{},
   4392                     &[_]u32{
   4393                         try astgen.errNoteNode(
   4394                             member.ast.value_expr,
   4395                             "tag value specified here",
   4396                             .{},
   4397                         ),
   4398                     },
   4399                 );
   4400             }
   4401             if (auto_enum_tok == null) {
   4402                 return astgen.failNodeNotes(
   4403                     node,
   4404                     "explicitly valued tagged union requires inferred enum tag type",
   4405                     .{},
   4406                     &[_]u32{
   4407                         try astgen.errNoteNode(
   4408                             member.ast.value_expr,
   4409                             "tag value specified here",
   4410                             .{},
   4411                         ),
   4412                     },
   4413                 );
   4414             }
   4415             const tag_value = try expr(&block_scope, &block_scope.base, .{ .ty = arg_inst }, member.ast.value_expr);
   4416             wip_members.appendToField(@enumToInt(tag_value));
   4417         }
   4418     }
   4419     if (field_count == 0) {
   4420         return astgen.failNode(node, "union declarations must have at least one tag", .{});
   4421     }
   4422 
   4423     if (!block_scope.isEmpty()) {
   4424         _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
   4425     }
   4426 
   4427     const body = block_scope.instructionsSlice();
   4428     const body_len = astgen.countBodyLenAfterFixups(body);
   4429 
   4430     try gz.setUnion(decl_inst, .{
   4431         .src_node = node,
   4432         .layout = layout,
   4433         .tag_type = arg_inst,
   4434         .body_len = body_len,
   4435         .fields_len = field_count,
   4436         .decls_len = decl_count,
   4437         .auto_enum_tag = auto_enum_tok != null,
   4438     });
   4439 
   4440     wip_members.finishBits(bits_per_field);
   4441     const decls_slice = wip_members.declsSlice();
   4442     const fields_slice = wip_members.fieldsSlice();
   4443     try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + body_len + fields_slice.len);
   4444     astgen.extra.appendSliceAssumeCapacity(decls_slice);
   4445     astgen.appendBodyWithFixups(body);
   4446     astgen.extra.appendSliceAssumeCapacity(fields_slice);
   4447 
   4448     block_scope.unstack();
   4449     try gz.addNamespaceCaptures(&namespace);
   4450     return indexToRef(decl_inst);
   4451 }
   4452 
   4453 fn containerDecl(
   4454     gz: *GenZir,
   4455     scope: *Scope,
   4456     rl: ResultLoc,
   4457     node: Ast.Node.Index,
   4458     container_decl: Ast.full.ContainerDecl,
   4459 ) InnerError!Zir.Inst.Ref {
   4460     const astgen = gz.astgen;
   4461     const gpa = astgen.gpa;
   4462     const tree = astgen.tree;
   4463     const token_tags = tree.tokens.items(.tag);
   4464     const node_tags = tree.nodes.items(.tag);
   4465 
   4466     const prev_fn_block = astgen.fn_block;
   4467     astgen.fn_block = null;
   4468     defer astgen.fn_block = prev_fn_block;
   4469 
   4470     // We must not create any types until Sema. Here the goal is only to generate
   4471     // ZIR for all the field types, alignments, and default value expressions.
   4472 
   4473     switch (token_tags[container_decl.ast.main_token]) {
   4474         .keyword_struct => {
   4475             const layout = if (container_decl.layout_token) |t| switch (token_tags[t]) {
   4476                 .keyword_packed => std.builtin.Type.ContainerLayout.Packed,
   4477                 .keyword_extern => std.builtin.Type.ContainerLayout.Extern,
   4478                 else => unreachable,
   4479             } else std.builtin.Type.ContainerLayout.Auto;
   4480 
   4481             assert(container_decl.ast.arg == 0);
   4482 
   4483             const result = try structDeclInner(gz, scope, node, container_decl, layout);
   4484             return rvalue(gz, rl, result, node);
   4485         },
   4486         .keyword_union => {
   4487             const layout = if (container_decl.layout_token) |t| switch (token_tags[t]) {
   4488                 .keyword_packed => std.builtin.Type.ContainerLayout.Packed,
   4489                 .keyword_extern => std.builtin.Type.ContainerLayout.Extern,
   4490                 else => unreachable,
   4491             } else std.builtin.Type.ContainerLayout.Auto;
   4492 
   4493             const result = try unionDeclInner(gz, scope, node, container_decl.ast.members, layout, container_decl.ast.arg, container_decl.ast.enum_token);
   4494             return rvalue(gz, rl, result, node);
   4495         },
   4496         .keyword_enum => {
   4497             if (container_decl.layout_token) |t| {
   4498                 return astgen.failTok(t, "enums do not support 'packed' or 'extern'; instead provide an explicit integer tag type", .{});
   4499             }
   4500             // Count total fields as well as how many have explicitly provided tag values.
   4501             const counts = blk: {
   4502                 var values: usize = 0;
   4503                 var total_fields: usize = 0;
   4504                 var decls: usize = 0;
   4505                 var nonexhaustive_node: Ast.Node.Index = 0;
   4506                 var nonfinal_nonexhaustive = false;
   4507                 for (container_decl.ast.members) |member_node| {
   4508                     const member = switch (node_tags[member_node]) {
   4509                         .container_field_init => tree.containerFieldInit(member_node),
   4510                         .container_field_align => tree.containerFieldAlign(member_node),
   4511                         .container_field => tree.containerField(member_node),
   4512                         else => {
   4513                             decls += 1;
   4514                             continue;
   4515                         },
   4516                     };
   4517                     if (member.comptime_token) |comptime_token| {
   4518                         return astgen.failTok(comptime_token, "enum fields cannot be marked comptime", .{});
   4519                     }
   4520                     if (member.ast.type_expr != 0) {
   4521                         return astgen.failNodeNotes(
   4522                             member.ast.type_expr,
   4523                             "enum fields do not have types",
   4524                             .{},
   4525                             &[_]u32{
   4526                                 try astgen.errNoteNode(
   4527                                     node,
   4528                                     "consider 'union(enum)' here to make it a tagged union",
   4529                                     .{},
   4530                                 ),
   4531                             },
   4532                         );
   4533                     }
   4534                     // Alignment expressions in enums are caught by the parser.
   4535                     assert(member.ast.align_expr == 0);
   4536 
   4537                     const name_token = member.ast.name_token;
   4538                     if (mem.eql(u8, tree.tokenSlice(name_token), "_")) {
   4539                         if (nonexhaustive_node != 0) {
   4540                             return astgen.failNodeNotes(
   4541                                 member_node,
   4542                                 "redundant non-exhaustive enum mark",
   4543                                 .{},
   4544                                 &[_]u32{
   4545                                     try astgen.errNoteNode(
   4546                                         nonexhaustive_node,
   4547                                         "other mark here",
   4548                                         .{},
   4549                                     ),
   4550                                 },
   4551                             );
   4552                         }
   4553                         nonexhaustive_node = member_node;
   4554                         if (member.ast.value_expr != 0) {
   4555                             return astgen.failNode(member.ast.value_expr, "'_' is used to mark an enum as non-exhaustive and cannot be assigned a value", .{});
   4556                         }
   4557                         continue;
   4558                     } else if (nonexhaustive_node != 0) {
   4559                         nonfinal_nonexhaustive = true;
   4560                     }
   4561                     total_fields += 1;
   4562                     if (member.ast.value_expr != 0) {
   4563                         if (container_decl.ast.arg == 0) {
   4564                             return astgen.failNode(member.ast.value_expr, "value assigned to enum tag with inferred tag type", .{});
   4565                         }
   4566                         values += 1;
   4567                     }
   4568                 }
   4569                 if (nonfinal_nonexhaustive) {
   4570                     return astgen.failNode(nonexhaustive_node, "'_' field of non-exhaustive enum must be last", .{});
   4571                 }
   4572                 break :blk .{
   4573                     .total_fields = total_fields,
   4574                     .values = values,
   4575                     .decls = decls,
   4576                     .nonexhaustive_node = nonexhaustive_node,
   4577                 };
   4578             };
   4579             if (counts.total_fields == 0 and counts.nonexhaustive_node == 0) {
   4580                 // One can construct an enum with no tags, and it functions the same as `noreturn`. But
   4581                 // this is only useful for generic code; when explicitly using `enum {}` syntax, there
   4582                 // must be at least one tag.
   4583                 try astgen.appendErrorNode(node, "enum declarations must have at least one tag", .{});
   4584             }
   4585             if (counts.nonexhaustive_node != 0 and container_decl.ast.arg == 0) {
   4586                 try astgen.appendErrorNodeNotes(
   4587                     node,
   4588                     "non-exhaustive enum missing integer tag type",
   4589                     .{},
   4590                     &[_]u32{
   4591                         try astgen.errNoteNode(
   4592                             counts.nonexhaustive_node,
   4593                             "marked non-exhaustive here",
   4594                             .{},
   4595                         ),
   4596                     },
   4597                 );
   4598             }
   4599             // In this case we must generate ZIR code for the tag values, similar to
   4600             // how structs are handled above.
   4601             const nonexhaustive = counts.nonexhaustive_node != 0;
   4602 
   4603             const decl_inst = try gz.reserveInstructionIndex();
   4604 
   4605             var namespace: Scope.Namespace = .{
   4606                 .parent = scope,
   4607                 .node = node,
   4608                 .inst = decl_inst,
   4609                 .declaring_gz = gz,
   4610             };
   4611             defer namespace.deinit(gpa);
   4612 
   4613             // The enum_decl instruction introduces a scope in which the decls of the enum
   4614             // are in scope, so that tag values can refer to decls within the enum itself.
   4615             astgen.advanceSourceCursorToNode(node);
   4616             var block_scope: GenZir = .{
   4617                 .parent = &namespace.base,
   4618                 .decl_node_index = node,
   4619                 .decl_line = astgen.source_line,
   4620                 .astgen = astgen,
   4621                 .force_comptime = true,
   4622                 .in_defer = false,
   4623                 .instructions = gz.instructions,
   4624                 .instructions_top = gz.instructions.items.len,
   4625             };
   4626             defer block_scope.unstack();
   4627 
   4628             _ = try astgen.scanDecls(&namespace, container_decl.ast.members);
   4629 
   4630             const arg_inst: Zir.Inst.Ref = if (container_decl.ast.arg != 0)
   4631                 try comptimeExpr(&block_scope, &namespace.base, .{ .ty = .type_type }, container_decl.ast.arg)
   4632             else
   4633                 .none;
   4634 
   4635             const bits_per_field = 1;
   4636             const max_field_size = 3;
   4637             var wip_members = try WipMembers.init(gpa, &astgen.scratch, @intCast(u32, counts.decls), @intCast(u32, counts.total_fields), bits_per_field, max_field_size);
   4638             defer wip_members.deinit();
   4639 
   4640             for (container_decl.ast.members) |member_node| {
   4641                 if (member_node == counts.nonexhaustive_node)
   4642                     continue;
   4643                 const member = switch (try containerMember(gz, &namespace.base, &wip_members, member_node)) {
   4644                     .decl => continue,
   4645                     .field => |field| field,
   4646                 };
   4647                 assert(member.comptime_token == null);
   4648                 assert(member.ast.type_expr == 0);
   4649                 assert(member.ast.align_expr == 0);
   4650 
   4651                 const field_name = try astgen.identAsString(member.ast.name_token);
   4652                 wip_members.appendToField(field_name);
   4653 
   4654                 const doc_comment_index = try astgen.docCommentAsString(member.firstToken());
   4655                 wip_members.appendToField(doc_comment_index);
   4656 
   4657                 const have_value = member.ast.value_expr != 0;
   4658                 wip_members.nextField(bits_per_field, .{have_value});
   4659 
   4660                 if (have_value) {
   4661                     if (arg_inst == .none) {
   4662                         return astgen.failNodeNotes(
   4663                             node,
   4664                             "explicitly valued enum missing integer tag type",
   4665                             .{},
   4666                             &[_]u32{
   4667                                 try astgen.errNoteNode(
   4668                                     member.ast.value_expr,
   4669                                     "tag value specified here",
   4670                                     .{},
   4671                                 ),
   4672                             },
   4673                         );
   4674                     }
   4675                     const tag_value_inst = try expr(&block_scope, &namespace.base, .{ .ty = arg_inst }, member.ast.value_expr);
   4676                     wip_members.appendToField(@enumToInt(tag_value_inst));
   4677                 }
   4678             }
   4679 
   4680             if (!block_scope.isEmpty()) {
   4681                 _ = try block_scope.addBreak(.break_inline, decl_inst, .void_value);
   4682             }
   4683 
   4684             const body = block_scope.instructionsSlice();
   4685             const body_len = astgen.countBodyLenAfterFixups(body);
   4686 
   4687             try gz.setEnum(decl_inst, .{
   4688                 .src_node = node,
   4689                 .nonexhaustive = nonexhaustive,
   4690                 .tag_type = arg_inst,
   4691                 .body_len = body_len,
   4692                 .fields_len = @intCast(u32, counts.total_fields),
   4693                 .decls_len = @intCast(u32, counts.decls),
   4694             });
   4695 
   4696             wip_members.finishBits(bits_per_field);
   4697             const decls_slice = wip_members.declsSlice();
   4698             const fields_slice = wip_members.fieldsSlice();
   4699             try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len + body_len + fields_slice.len);
   4700             astgen.extra.appendSliceAssumeCapacity(decls_slice);
   4701             astgen.appendBodyWithFixups(body);
   4702             astgen.extra.appendSliceAssumeCapacity(fields_slice);
   4703 
   4704             block_scope.unstack();
   4705             try gz.addNamespaceCaptures(&namespace);
   4706             return rvalue(gz, rl, indexToRef(decl_inst), node);
   4707         },
   4708         .keyword_opaque => {
   4709             assert(container_decl.ast.arg == 0);
   4710 
   4711             const decl_inst = try gz.reserveInstructionIndex();
   4712 
   4713             var namespace: Scope.Namespace = .{
   4714                 .parent = scope,
   4715                 .node = node,
   4716                 .inst = decl_inst,
   4717                 .declaring_gz = gz,
   4718             };
   4719             defer namespace.deinit(gpa);
   4720 
   4721             const decl_count = try astgen.scanDecls(&namespace, container_decl.ast.members);
   4722 
   4723             var wip_members = try WipMembers.init(gpa, &astgen.scratch, decl_count, 0, 0, 0);
   4724             defer wip_members.deinit();
   4725 
   4726             for (container_decl.ast.members) |member_node| {
   4727                 const res = try containerMember(gz, &namespace.base, &wip_members, member_node);
   4728                 if (res == .field) {
   4729                     return astgen.failNode(member_node, "opaque types cannot have fields", .{});
   4730                 }
   4731             }
   4732 
   4733             try gz.setOpaque(decl_inst, .{
   4734                 .src_node = node,
   4735                 .decls_len = decl_count,
   4736             });
   4737 
   4738             wip_members.finishBits(0);
   4739             const decls_slice = wip_members.declsSlice();
   4740             try astgen.extra.ensureUnusedCapacity(gpa, decls_slice.len);
   4741             astgen.extra.appendSliceAssumeCapacity(decls_slice);
   4742 
   4743             try gz.addNamespaceCaptures(&namespace);
   4744             return rvalue(gz, rl, indexToRef(decl_inst), node);
   4745         },
   4746         else => unreachable,
   4747     }
   4748 }
   4749 
   4750 const ContainerMemberResult = union(enum) { decl, field: Ast.full.ContainerField };
   4751 
   4752 fn containerMember(
   4753     gz: *GenZir,
   4754     scope: *Scope,
   4755     wip_members: *WipMembers,
   4756     member_node: Ast.Node.Index,
   4757 ) InnerError!ContainerMemberResult {
   4758     const astgen = gz.astgen;
   4759     const tree = astgen.tree;
   4760     const node_tags = tree.nodes.items(.tag);
   4761     const node_datas = tree.nodes.items(.data);
   4762     switch (node_tags[member_node]) {
   4763         .container_field_init => return ContainerMemberResult{ .field = tree.containerFieldInit(member_node) },
   4764         .container_field_align => return ContainerMemberResult{ .field = tree.containerFieldAlign(member_node) },
   4765         .container_field => return ContainerMemberResult{ .field = tree.containerField(member_node) },
   4766 
   4767         .fn_decl => {
   4768             const fn_proto = node_datas[member_node].lhs;
   4769             const body = node_datas[member_node].rhs;
   4770             switch (node_tags[fn_proto]) {
   4771                 .fn_proto_simple => {
   4772                     var params: [1]Ast.Node.Index = undefined;
   4773                     astgen.fnDecl(gz, scope, wip_members, member_node, body, tree.fnProtoSimple(&params, fn_proto)) catch |err| switch (err) {
   4774                         error.OutOfMemory => return error.OutOfMemory,
   4775                         error.AnalysisFail => {},
   4776                     };
   4777                 },
   4778                 .fn_proto_multi => {
   4779                     astgen.fnDecl(gz, scope, wip_members, member_node, body, tree.fnProtoMulti(fn_proto)) catch |err| switch (err) {
   4780                         error.OutOfMemory => return error.OutOfMemory,
   4781                         error.AnalysisFail => {},
   4782                     };
   4783                 },
   4784                 .fn_proto_one => {
   4785                     var params: [1]Ast.Node.Index = undefined;
   4786                     astgen.fnDecl(gz, scope, wip_members, member_node, body, tree.fnProtoOne(&params, fn_proto)) catch |err| switch (err) {
   4787                         error.OutOfMemory => return error.OutOfMemory,
   4788                         error.AnalysisFail => {},
   4789                     };
   4790                 },
   4791                 .fn_proto => {
   4792                     astgen.fnDecl(gz, scope, wip_members, member_node, body, tree.fnProto(fn_proto)) catch |err| switch (err) {
   4793                         error.OutOfMemory => return error.OutOfMemory,
   4794                         error.AnalysisFail => {},
   4795                     };
   4796                 },
   4797                 else => unreachable,
   4798             }
   4799         },
   4800         .fn_proto_simple => {
   4801             var params: [1]Ast.Node.Index = undefined;
   4802             astgen.fnDecl(gz, scope, wip_members, member_node, 0, tree.fnProtoSimple(&params, member_node)) catch |err| switch (err) {
   4803                 error.OutOfMemory => return error.OutOfMemory,
   4804                 error.AnalysisFail => {},
   4805             };
   4806         },
   4807         .fn_proto_multi => {
   4808             astgen.fnDecl(gz, scope, wip_members, member_node, 0, tree.fnProtoMulti(member_node)) catch |err| switch (err) {
   4809                 error.OutOfMemory => return error.OutOfMemory,
   4810                 error.AnalysisFail => {},
   4811             };
   4812         },
   4813         .fn_proto_one => {
   4814             var params: [1]Ast.Node.Index = undefined;
   4815             astgen.fnDecl(gz, scope, wip_members, member_node, 0, tree.fnProtoOne(&params, member_node)) catch |err| switch (err) {
   4816                 error.OutOfMemory => return error.OutOfMemory,
   4817                 error.AnalysisFail => {},
   4818             };
   4819         },
   4820         .fn_proto => {
   4821             astgen.fnDecl(gz, scope, wip_members, member_node, 0, tree.fnProto(member_node)) catch |err| switch (err) {
   4822                 error.OutOfMemory => return error.OutOfMemory,
   4823                 error.AnalysisFail => {},
   4824             };
   4825         },
   4826 
   4827         .global_var_decl => {
   4828             astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.globalVarDecl(member_node)) catch |err| switch (err) {
   4829                 error.OutOfMemory => return error.OutOfMemory,
   4830                 error.AnalysisFail => {},
   4831             };
   4832         },
   4833         .local_var_decl => {
   4834             astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.localVarDecl(member_node)) catch |err| switch (err) {
   4835                 error.OutOfMemory => return error.OutOfMemory,
   4836                 error.AnalysisFail => {},
   4837             };
   4838         },
   4839         .simple_var_decl => {
   4840             astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.simpleVarDecl(member_node)) catch |err| switch (err) {
   4841                 error.OutOfMemory => return error.OutOfMemory,
   4842                 error.AnalysisFail => {},
   4843             };
   4844         },
   4845         .aligned_var_decl => {
   4846             astgen.globalVarDecl(gz, scope, wip_members, member_node, tree.alignedVarDecl(member_node)) catch |err| switch (err) {
   4847                 error.OutOfMemory => return error.OutOfMemory,
   4848                 error.AnalysisFail => {},
   4849             };
   4850         },
   4851 
   4852         .@"comptime" => {
   4853             astgen.comptimeDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
   4854                 error.OutOfMemory => return error.OutOfMemory,
   4855                 error.AnalysisFail => {},
   4856             };
   4857         },
   4858         .@"usingnamespace" => {
   4859             astgen.usingnamespaceDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
   4860                 error.OutOfMemory => return error.OutOfMemory,
   4861                 error.AnalysisFail => {},
   4862             };
   4863         },
   4864         .test_decl => {
   4865             astgen.testDecl(gz, scope, wip_members, member_node) catch |err| switch (err) {
   4866                 error.OutOfMemory => return error.OutOfMemory,
   4867                 error.AnalysisFail => {},
   4868             };
   4869         },
   4870         else => unreachable,
   4871     }
   4872     return .decl;
   4873 }
   4874 
   4875 fn errorSetDecl(gz: *GenZir, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   4876     const astgen = gz.astgen;
   4877     const gpa = astgen.gpa;
   4878     const tree = astgen.tree;
   4879     const main_tokens = tree.nodes.items(.main_token);
   4880     const token_tags = tree.tokens.items(.tag);
   4881 
   4882     const payload_index = try reserveExtra(astgen, @typeInfo(Zir.Inst.ErrorSetDecl).Struct.fields.len);
   4883     var fields_len: usize = 0;
   4884     {
   4885         var idents: std.AutoHashMapUnmanaged(u32, Ast.TokenIndex) = .{};
   4886         defer idents.deinit(gpa);
   4887 
   4888         const error_token = main_tokens[node];
   4889         var tok_i = error_token + 2;
   4890         while (true) : (tok_i += 1) {
   4891             switch (token_tags[tok_i]) {
   4892                 .doc_comment, .comma => {},
   4893                 .identifier => {
   4894                     const str_index = try astgen.identAsString(tok_i);
   4895                     const gop = try idents.getOrPut(gpa, str_index);
   4896                     if (gop.found_existing) {
   4897                         const name = try gpa.dupe(u8, mem.span(astgen.nullTerminatedString(str_index)));
   4898                         defer gpa.free(name);
   4899                         return astgen.failTokNotes(
   4900                             tok_i,
   4901                             "duplicate error set field '{s}'",
   4902                             .{name},
   4903                             &[_]u32{
   4904                                 try astgen.errNoteTok(
   4905                                     gop.value_ptr.*,
   4906                                     "previous declaration here",
   4907                                     .{},
   4908                                 ),
   4909                             },
   4910                         );
   4911                     }
   4912                     gop.value_ptr.* = tok_i;
   4913 
   4914                     try astgen.extra.ensureUnusedCapacity(gpa, 2);
   4915                     astgen.extra.appendAssumeCapacity(str_index);
   4916                     const doc_comment_index = try astgen.docCommentAsString(tok_i);
   4917                     astgen.extra.appendAssumeCapacity(doc_comment_index);
   4918                     fields_len += 1;
   4919                 },
   4920                 .r_brace => break,
   4921                 else => unreachable,
   4922             }
   4923         }
   4924     }
   4925 
   4926     setExtra(astgen, payload_index, Zir.Inst.ErrorSetDecl{
   4927         .fields_len = @intCast(u32, fields_len),
   4928     });
   4929     const result = try gz.addPlNodePayloadIndex(.error_set_decl, node, payload_index);
   4930     return rvalue(gz, rl, result, node);
   4931 }
   4932 
   4933 fn tryExpr(
   4934     parent_gz: *GenZir,
   4935     scope: *Scope,
   4936     rl: ResultLoc,
   4937     node: Ast.Node.Index,
   4938     operand_node: Ast.Node.Index,
   4939 ) InnerError!Zir.Inst.Ref {
   4940     const astgen = parent_gz.astgen;
   4941 
   4942     const fn_block = astgen.fn_block orelse {
   4943         return astgen.failNode(node, "'try' outside function scope", .{});
   4944     };
   4945 
   4946     if (parent_gz.in_defer) return astgen.failNode(node, "'try' not allowed inside defer expression", .{});
   4947 
   4948     const operand_rl: ResultLoc = switch (rl) {
   4949         .ref => .ref,
   4950         else => .none,
   4951     };
   4952     // This could be a pointer or value depending on the `rl` parameter.
   4953     const operand = try expr(parent_gz, scope, operand_rl, operand_node);
   4954     const is_inline = parent_gz.force_comptime;
   4955     const is_inline_bit = @as(u2, @boolToInt(is_inline));
   4956     const is_ptr_bit = @as(u2, @boolToInt(operand_rl == .ref)) << 1;
   4957     const block_tag: Zir.Inst.Tag = switch (is_inline_bit | is_ptr_bit) {
   4958         0b00 => .@"try",
   4959         0b01 => .@"try",
   4960         //0b01 => .try_inline,
   4961         0b10 => .try_ptr,
   4962         0b11 => .try_ptr,
   4963         //0b11 => .try_ptr_inline,
   4964     };
   4965     const try_inst = try parent_gz.makeBlockInst(block_tag, node);
   4966     try parent_gz.instructions.append(astgen.gpa, try_inst);
   4967 
   4968     var else_scope = parent_gz.makeSubBlock(scope);
   4969     defer else_scope.unstack();
   4970 
   4971     const err_tag = switch (rl) {
   4972         .ref => Zir.Inst.Tag.err_union_code_ptr,
   4973         else => Zir.Inst.Tag.err_union_code,
   4974     };
   4975     const err_code = try else_scope.addUnNode(err_tag, operand, node);
   4976     try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code });
   4977     _ = try else_scope.addUnNode(.ret_node, err_code, node);
   4978 
   4979     try else_scope.setTryBody(try_inst, operand);
   4980     const result = indexToRef(try_inst);
   4981     switch (rl) {
   4982         .ref => return result,
   4983         else => return rvalue(parent_gz, rl, result, node),
   4984     }
   4985 }
   4986 
   4987 fn orelseCatchExpr(
   4988     parent_gz: *GenZir,
   4989     scope: *Scope,
   4990     rl: ResultLoc,
   4991     node: Ast.Node.Index,
   4992     lhs: Ast.Node.Index,
   4993     cond_op: Zir.Inst.Tag,
   4994     unwrap_op: Zir.Inst.Tag,
   4995     unwrap_code_op: Zir.Inst.Tag,
   4996     rhs: Ast.Node.Index,
   4997     payload_token: ?Ast.TokenIndex,
   4998 ) InnerError!Zir.Inst.Ref {
   4999     const astgen = parent_gz.astgen;
   5000     const tree = astgen.tree;
   5001 
   5002     var block_scope = parent_gz.makeSubBlock(scope);
   5003     block_scope.setBreakResultLoc(rl);
   5004     defer block_scope.unstack();
   5005 
   5006     const operand_rl: ResultLoc = switch (block_scope.break_result_loc) {
   5007         .ref => .ref,
   5008         else => .none,
   5009     };
   5010     block_scope.break_count += 1;
   5011     // This could be a pointer or value depending on the `operand_rl` parameter.
   5012     // We cannot use `block_scope.break_result_loc` because that has the bare
   5013     // type, whereas this expression has the optional type. Later we make
   5014     // up for this fact by calling rvalue on the else branch.
   5015     const operand = try reachableExpr(&block_scope, &block_scope.base, operand_rl, lhs, rhs);
   5016     const cond = try block_scope.addUnNode(cond_op, operand, node);
   5017     const condbr = try block_scope.addCondBr(.condbr, node);
   5018 
   5019     const block = try parent_gz.makeBlockInst(.block, node);
   5020     try block_scope.setBlockBody(block);
   5021     // block_scope unstacked now, can add new instructions to parent_gz
   5022     try parent_gz.instructions.append(astgen.gpa, block);
   5023 
   5024     var then_scope = block_scope.makeSubBlock(scope);
   5025     defer then_scope.unstack();
   5026 
   5027     // This could be a pointer or value depending on `unwrap_op`.
   5028     const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node);
   5029     const then_result = switch (rl) {
   5030         .ref => unwrapped_payload,
   5031         else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node),
   5032     };
   5033 
   5034     var else_scope = block_scope.makeSubBlock(scope);
   5035     defer else_scope.unstack();
   5036 
   5037     var err_val_scope: Scope.LocalVal = undefined;
   5038     const else_sub_scope = blk: {
   5039         const payload = payload_token orelse break :blk &else_scope.base;
   5040         const err_str = tree.tokenSlice(payload);
   5041         if (mem.eql(u8, err_str, "_")) {
   5042             return astgen.failTok(payload, "discard of error capture; omit it instead", .{});
   5043         }
   5044         const err_name = try astgen.identAsString(payload);
   5045 
   5046         try astgen.detectLocalShadowing(scope, err_name, payload, err_str);
   5047 
   5048         err_val_scope = .{
   5049             .parent = &else_scope.base,
   5050             .gen_zir = &else_scope,
   5051             .name = err_name,
   5052             .inst = try else_scope.addUnNode(unwrap_code_op, operand, node),
   5053             .token_src = payload,
   5054             .id_cat = .@"capture",
   5055         };
   5056         break :blk &err_val_scope.base;
   5057     };
   5058 
   5059     const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_loc, rhs);
   5060     if (!else_scope.endsWithNoReturn()) {
   5061         block_scope.break_count += 1;
   5062     }
   5063     try checkUsed(parent_gz, &else_scope.base, else_sub_scope);
   5064 
   5065     // We hold off on the break instructions as well as copying the then/else
   5066     // instructions into place until we know whether to keep store_to_block_ptr
   5067     // instructions or not.
   5068 
   5069     const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break";
   5070     return finishThenElseBlock(
   5071         parent_gz,
   5072         rl,
   5073         node,
   5074         &block_scope,
   5075         &then_scope,
   5076         &else_scope,
   5077         condbr,
   5078         cond,
   5079         then_result,
   5080         else_result,
   5081         block,
   5082         block,
   5083         break_tag,
   5084     );
   5085 }
   5086 
   5087 /// Supports `else_scope` stacked on `then_scope` stacked on `block_scope`. Unstacks `else_scope` then `then_scope`.
   5088 fn finishThenElseBlock(
   5089     parent_gz: *GenZir,
   5090     rl: ResultLoc,
   5091     node: Ast.Node.Index,
   5092     block_scope: *GenZir,
   5093     then_scope: *GenZir,
   5094     else_scope: *GenZir,
   5095     condbr: Zir.Inst.Index,
   5096     cond: Zir.Inst.Ref,
   5097     then_result: Zir.Inst.Ref,
   5098     else_result: Zir.Inst.Ref,
   5099     main_block: Zir.Inst.Index,
   5100     then_break_block: Zir.Inst.Index,
   5101     break_tag: Zir.Inst.Tag,
   5102 ) InnerError!Zir.Inst.Ref {
   5103     // We now have enough information to decide whether the result instruction should
   5104     // be communicated via result location pointer or break instructions.
   5105     const strat = rl.strategy(block_scope);
   5106     // else_scope may be stacked on then_scope, so check for no-return on then_scope manually
   5107     const tags = parent_gz.astgen.instructions.items(.tag);
   5108     const then_slice = then_scope.instructionsSliceUpto(else_scope);
   5109     const then_no_return = then_slice.len > 0 and tags[then_slice[then_slice.len - 1]].isNoReturn();
   5110     const else_no_return = else_scope.endsWithNoReturn();
   5111 
   5112     switch (strat.tag) {
   5113         .break_void => {
   5114             const then_break = if (!then_no_return) try then_scope.makeBreak(break_tag, then_break_block, .void_value) else 0;
   5115             const else_break = if (!else_no_return) try else_scope.makeBreak(break_tag, main_block, .void_value) else 0;
   5116             assert(!strat.elide_store_to_block_ptr_instructions);
   5117             try setCondBrPayload(condbr, cond, then_scope, then_break, else_scope, else_break);
   5118             return indexToRef(main_block);
   5119         },
   5120         .break_operand => {
   5121             const then_break = if (!then_no_return) try then_scope.makeBreak(break_tag, then_break_block, then_result) else 0;
   5122             const else_break = if (else_result == .none)
   5123                 try else_scope.makeBreak(break_tag, main_block, .void_value)
   5124             else if (!else_no_return)
   5125                 try else_scope.makeBreak(break_tag, main_block, else_result)
   5126             else
   5127                 0;
   5128 
   5129             if (strat.elide_store_to_block_ptr_instructions) {
   5130                 try setCondBrPayloadElideBlockStorePtr(condbr, cond, then_scope, then_break, else_scope, else_break, block_scope.rl_ptr);
   5131             } else {
   5132                 try setCondBrPayload(condbr, cond, then_scope, then_break, else_scope, else_break);
   5133             }
   5134             const block_ref = indexToRef(main_block);
   5135             switch (rl) {
   5136                 .ref => return block_ref,
   5137                 else => return rvalue(parent_gz, rl, block_ref, node),
   5138             }
   5139         },
   5140     }
   5141 }
   5142 
   5143 /// Return whether the identifier names of two tokens are equal. Resolves @""
   5144 /// tokens without allocating.
   5145 /// OK in theory it could do it without allocating. This implementation
   5146 /// allocates when the @"" form is used.
   5147 fn tokenIdentEql(astgen: *AstGen, token1: Ast.TokenIndex, token2: Ast.TokenIndex) !bool {
   5148     const ident_name_1 = try astgen.identifierTokenString(token1);
   5149     const ident_name_2 = try astgen.identifierTokenString(token2);
   5150     return mem.eql(u8, ident_name_1, ident_name_2);
   5151 }
   5152 
   5153 fn fieldAccess(
   5154     gz: *GenZir,
   5155     scope: *Scope,
   5156     rl: ResultLoc,
   5157     node: Ast.Node.Index,
   5158 ) InnerError!Zir.Inst.Ref {
   5159     switch (rl) {
   5160         .ref => return addFieldAccess(.field_ptr, gz, scope, .ref, node),
   5161         else => {
   5162             const access = try addFieldAccess(.field_val, gz, scope, .none, node);
   5163             return rvalue(gz, rl, access, node);
   5164         },
   5165     }
   5166 }
   5167 
   5168 fn addFieldAccess(
   5169     tag: Zir.Inst.Tag,
   5170     gz: *GenZir,
   5171     scope: *Scope,
   5172     lhs_rl: ResultLoc,
   5173     node: Ast.Node.Index,
   5174 ) InnerError!Zir.Inst.Ref {
   5175     const astgen = gz.astgen;
   5176     const tree = astgen.tree;
   5177     const main_tokens = tree.nodes.items(.main_token);
   5178     const node_datas = tree.nodes.items(.data);
   5179 
   5180     const object_node = node_datas[node].lhs;
   5181     const dot_token = main_tokens[node];
   5182     const field_ident = dot_token + 1;
   5183     const str_index = try astgen.identAsString(field_ident);
   5184 
   5185     return gz.addPlNode(tag, node, Zir.Inst.Field{
   5186         .lhs = try expr(gz, scope, lhs_rl, object_node),
   5187         .field_name_start = str_index,
   5188     });
   5189 }
   5190 
   5191 fn arrayAccess(
   5192     gz: *GenZir,
   5193     scope: *Scope,
   5194     rl: ResultLoc,
   5195     node: Ast.Node.Index,
   5196 ) InnerError!Zir.Inst.Ref {
   5197     const astgen = gz.astgen;
   5198     const tree = astgen.tree;
   5199     const node_datas = tree.nodes.items(.data);
   5200     switch (rl) {
   5201         .ref => return gz.addPlNode(.elem_ptr_node, node, Zir.Inst.Bin{
   5202             .lhs = try expr(gz, scope, .ref, node_datas[node].lhs),
   5203             .rhs = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs),
   5204         }),
   5205         else => return rvalue(gz, rl, try gz.addPlNode(.elem_val_node, node, Zir.Inst.Bin{
   5206             .lhs = try expr(gz, scope, .none, node_datas[node].lhs),
   5207             .rhs = try expr(gz, scope, .{ .ty = .usize_type }, node_datas[node].rhs),
   5208         }), node),
   5209     }
   5210 }
   5211 
   5212 fn simpleBinOp(
   5213     gz: *GenZir,
   5214     scope: *Scope,
   5215     rl: ResultLoc,
   5216     node: Ast.Node.Index,
   5217     op_inst_tag: Zir.Inst.Tag,
   5218 ) InnerError!Zir.Inst.Ref {
   5219     const astgen = gz.astgen;
   5220     const tree = astgen.tree;
   5221     const node_datas = tree.nodes.items(.data);
   5222 
   5223     const result = try gz.addPlNode(op_inst_tag, node, Zir.Inst.Bin{
   5224         .lhs = try reachableExpr(gz, scope, .none, node_datas[node].lhs, node),
   5225         .rhs = try reachableExpr(gz, scope, .none, node_datas[node].rhs, node),
   5226     });
   5227     return rvalue(gz, rl, result, node);
   5228 }
   5229 
   5230 fn simpleStrTok(
   5231     gz: *GenZir,
   5232     rl: ResultLoc,
   5233     ident_token: Ast.TokenIndex,
   5234     node: Ast.Node.Index,
   5235     op_inst_tag: Zir.Inst.Tag,
   5236 ) InnerError!Zir.Inst.Ref {
   5237     const astgen = gz.astgen;
   5238     const str_index = try astgen.identAsString(ident_token);
   5239     const result = try gz.addStrTok(op_inst_tag, str_index, ident_token);
   5240     return rvalue(gz, rl, result, node);
   5241 }
   5242 
   5243 fn boolBinOp(
   5244     gz: *GenZir,
   5245     scope: *Scope,
   5246     rl: ResultLoc,
   5247     node: Ast.Node.Index,
   5248     zir_tag: Zir.Inst.Tag,
   5249 ) InnerError!Zir.Inst.Ref {
   5250     const astgen = gz.astgen;
   5251     const tree = astgen.tree;
   5252     const node_datas = tree.nodes.items(.data);
   5253 
   5254     const lhs = try expr(gz, scope, bool_rl, node_datas[node].lhs);
   5255     const bool_br = try gz.addBoolBr(zir_tag, lhs);
   5256 
   5257     var rhs_scope = gz.makeSubBlock(scope);
   5258     defer rhs_scope.unstack();
   5259     const rhs = try expr(&rhs_scope, &rhs_scope.base, bool_rl, node_datas[node].rhs);
   5260     if (!gz.refIsNoReturn(rhs)) {
   5261         _ = try rhs_scope.addBreak(.break_inline, bool_br, rhs);
   5262     }
   5263     try rhs_scope.setBoolBrBody(bool_br);
   5264 
   5265     const block_ref = indexToRef(bool_br);
   5266     return rvalue(gz, rl, block_ref, node);
   5267 }
   5268 
   5269 fn ifExpr(
   5270     parent_gz: *GenZir,
   5271     scope: *Scope,
   5272     rl: ResultLoc,
   5273     node: Ast.Node.Index,
   5274     if_full: Ast.full.If,
   5275 ) InnerError!Zir.Inst.Ref {
   5276     const astgen = parent_gz.astgen;
   5277     const tree = astgen.tree;
   5278     const token_tags = tree.tokens.items(.tag);
   5279 
   5280     var block_scope = parent_gz.makeSubBlock(scope);
   5281     block_scope.setBreakResultLoc(rl);
   5282     defer block_scope.unstack();
   5283 
   5284     const payload_is_ref = if (if_full.payload_token) |payload_token|
   5285         token_tags[payload_token] == .asterisk
   5286     else
   5287         false;
   5288 
   5289     try emitDbgNode(parent_gz, if_full.ast.cond_expr);
   5290     const cond: struct {
   5291         inst: Zir.Inst.Ref,
   5292         bool_bit: Zir.Inst.Ref,
   5293     } = c: {
   5294         if (if_full.error_token) |_| {
   5295             const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
   5296             const err_union = try expr(&block_scope, &block_scope.base, cond_rl, if_full.ast.cond_expr);
   5297             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
   5298             break :c .{
   5299                 .inst = err_union,
   5300                 .bool_bit = try block_scope.addUnNode(tag, err_union, node),
   5301             };
   5302         } else if (if_full.payload_token) |_| {
   5303             const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
   5304             const optional = try expr(&block_scope, &block_scope.base, cond_rl, if_full.ast.cond_expr);
   5305             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
   5306             break :c .{
   5307                 .inst = optional,
   5308                 .bool_bit = try block_scope.addUnNode(tag, optional, node),
   5309             };
   5310         } else {
   5311             const cond = try expr(&block_scope, &block_scope.base, bool_rl, if_full.ast.cond_expr);
   5312             break :c .{
   5313                 .inst = cond,
   5314                 .bool_bit = cond,
   5315             };
   5316         }
   5317     };
   5318 
   5319     const condbr = try block_scope.addCondBr(.condbr, node);
   5320 
   5321     const block = try parent_gz.makeBlockInst(.block, node);
   5322     try block_scope.setBlockBody(block);
   5323     // block_scope unstacked now, can add new instructions to parent_gz
   5324     try parent_gz.instructions.append(astgen.gpa, block);
   5325 
   5326     var then_scope = parent_gz.makeSubBlock(scope);
   5327     defer then_scope.unstack();
   5328 
   5329     var payload_val_scope: Scope.LocalVal = undefined;
   5330 
   5331     try then_scope.addDbgBlockBegin();
   5332     const then_sub_scope = s: {
   5333         if (if_full.error_token != null) {
   5334             if (if_full.payload_token) |payload_token| {
   5335                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   5336                     .err_union_payload_unsafe_ptr
   5337                 else
   5338                     .err_union_payload_unsafe;
   5339                 const payload_inst = try then_scope.addUnNode(tag, cond.inst, node);
   5340                 const token_name_index = payload_token + @boolToInt(payload_is_ref);
   5341                 const ident_name = try astgen.identAsString(token_name_index);
   5342                 const token_name_str = tree.tokenSlice(token_name_index);
   5343                 if (mem.eql(u8, "_", token_name_str))
   5344                     break :s &then_scope.base;
   5345                 try astgen.detectLocalShadowing(&then_scope.base, ident_name, token_name_index, token_name_str);
   5346                 payload_val_scope = .{
   5347                     .parent = &then_scope.base,
   5348                     .gen_zir = &then_scope,
   5349                     .name = ident_name,
   5350                     .inst = payload_inst,
   5351                     .token_src = payload_token,
   5352                     .id_cat = .@"capture",
   5353                 };
   5354                 try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
   5355                 break :s &payload_val_scope.base;
   5356             } else {
   5357                 break :s &then_scope.base;
   5358             }
   5359         } else if (if_full.payload_token) |payload_token| {
   5360             const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
   5361             const tag: Zir.Inst.Tag = if (payload_is_ref)
   5362                 .optional_payload_unsafe_ptr
   5363             else
   5364                 .optional_payload_unsafe;
   5365             const ident_bytes = tree.tokenSlice(ident_token);
   5366             if (mem.eql(u8, "_", ident_bytes))
   5367                 break :s &then_scope.base;
   5368             const payload_inst = try then_scope.addUnNode(tag, cond.inst, node);
   5369             const ident_name = try astgen.identAsString(ident_token);
   5370             try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes);
   5371             payload_val_scope = .{
   5372                 .parent = &then_scope.base,
   5373                 .gen_zir = &then_scope,
   5374                 .name = ident_name,
   5375                 .inst = payload_inst,
   5376                 .token_src = ident_token,
   5377                 .id_cat = .@"capture",
   5378             };
   5379             try then_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
   5380             break :s &payload_val_scope.base;
   5381         } else {
   5382             break :s &then_scope.base;
   5383         }
   5384     };
   5385 
   5386     const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_loc, if_full.ast.then_expr);
   5387     if (!then_scope.endsWithNoReturn()) {
   5388         block_scope.break_count += 1;
   5389     }
   5390     try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
   5391     try then_scope.addDbgBlockEnd();
   5392     // We hold off on the break instructions as well as copying the then/else
   5393     // instructions into place until we know whether to keep store_to_block_ptr
   5394     // instructions or not.
   5395 
   5396     var else_scope = parent_gz.makeSubBlock(scope);
   5397     defer else_scope.unstack();
   5398 
   5399     const else_node = if_full.ast.else_expr;
   5400     const else_info: struct {
   5401         src: Ast.Node.Index,
   5402         result: Zir.Inst.Ref,
   5403     } = if (else_node != 0) blk: {
   5404         try else_scope.addDbgBlockBegin();
   5405         const sub_scope = s: {
   5406             if (if_full.error_token) |error_token| {
   5407                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   5408                     .err_union_code_ptr
   5409                 else
   5410                     .err_union_code;
   5411                 const payload_inst = try else_scope.addUnNode(tag, cond.inst, node);
   5412                 const ident_name = try astgen.identAsString(error_token);
   5413                 const error_token_str = tree.tokenSlice(error_token);
   5414                 if (mem.eql(u8, "_", error_token_str))
   5415                     break :s &else_scope.base;
   5416                 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, error_token_str);
   5417                 payload_val_scope = .{
   5418                     .parent = &else_scope.base,
   5419                     .gen_zir = &else_scope,
   5420                     .name = ident_name,
   5421                     .inst = payload_inst,
   5422                     .token_src = error_token,
   5423                     .id_cat = .@"capture",
   5424                 };
   5425                 try else_scope.addDbgVar(.dbg_var_val, ident_name, payload_inst);
   5426                 break :s &payload_val_scope.base;
   5427             } else {
   5428                 break :s &else_scope.base;
   5429             }
   5430         };
   5431         const e = try expr(&else_scope, sub_scope, block_scope.break_result_loc, else_node);
   5432         if (!else_scope.endsWithNoReturn()) {
   5433             block_scope.break_count += 1;
   5434         }
   5435         try checkUsed(parent_gz, &else_scope.base, sub_scope);
   5436         try else_scope.addDbgBlockEnd();
   5437         break :blk .{
   5438             .src = else_node,
   5439             .result = e,
   5440         };
   5441     } else .{
   5442         .src = if_full.ast.then_expr,
   5443         .result = .none,
   5444     };
   5445 
   5446     const break_tag: Zir.Inst.Tag = if (parent_gz.force_comptime) .break_inline else .@"break";
   5447     return finishThenElseBlock(
   5448         parent_gz,
   5449         rl,
   5450         node,
   5451         &block_scope,
   5452         &then_scope,
   5453         &else_scope,
   5454         condbr,
   5455         cond.bool_bit,
   5456         then_result,
   5457         else_info.result,
   5458         block,
   5459         block,
   5460         break_tag,
   5461     );
   5462 }
   5463 
   5464 /// Supports `else_scope` stacked on `then_scope`. Unstacks `else_scope` then `then_scope`.
   5465 fn setCondBrPayload(
   5466     condbr: Zir.Inst.Index,
   5467     cond: Zir.Inst.Ref,
   5468     then_scope: *GenZir,
   5469     then_break: Zir.Inst.Index,
   5470     else_scope: *GenZir,
   5471     else_break: Zir.Inst.Index,
   5472 ) !void {
   5473     defer then_scope.unstack();
   5474     defer else_scope.unstack();
   5475     const astgen = then_scope.astgen;
   5476     const then_body = then_scope.instructionsSliceUpto(else_scope);
   5477     const else_body = else_scope.instructionsSlice();
   5478     const then_body_len = astgen.countBodyLenAfterFixups(then_body) + @boolToInt(then_break != 0);
   5479     const else_body_len = astgen.countBodyLenAfterFixups(else_body) + @boolToInt(else_break != 0);
   5480     try astgen.extra.ensureUnusedCapacity(
   5481         astgen.gpa,
   5482         @typeInfo(Zir.Inst.CondBr).Struct.fields.len + then_body_len + else_body_len,
   5483     );
   5484 
   5485     const zir_datas = astgen.instructions.items(.data);
   5486     zir_datas[condbr].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.CondBr{
   5487         .condition = cond,
   5488         .then_body_len = then_body_len,
   5489         .else_body_len = else_body_len,
   5490     });
   5491     astgen.appendBodyWithFixups(then_body);
   5492     if (then_break != 0) astgen.extra.appendAssumeCapacity(then_break);
   5493     astgen.appendBodyWithFixups(else_body);
   5494     if (else_break != 0) astgen.extra.appendAssumeCapacity(else_break);
   5495 }
   5496 
   5497 /// Supports `else_scope` stacked on `then_scope`. Unstacks `else_scope` then `then_scope`.
   5498 fn setCondBrPayloadElideBlockStorePtr(
   5499     condbr: Zir.Inst.Index,
   5500     cond: Zir.Inst.Ref,
   5501     then_scope: *GenZir,
   5502     then_break: Zir.Inst.Index,
   5503     else_scope: *GenZir,
   5504     else_break: Zir.Inst.Index,
   5505     block_ptr: Zir.Inst.Ref,
   5506 ) !void {
   5507     defer then_scope.unstack();
   5508     defer else_scope.unstack();
   5509     const astgen = then_scope.astgen;
   5510     const then_body = then_scope.instructionsSliceUpto(else_scope);
   5511     const else_body = else_scope.instructionsSlice();
   5512     const has_then_break = then_break != 0;
   5513     const has_else_break = else_break != 0;
   5514     const then_body_len = astgen.countBodyLenAfterFixups(then_body) + @boolToInt(has_then_break);
   5515     const else_body_len = astgen.countBodyLenAfterFixups(else_body) + @boolToInt(has_else_break);
   5516     try astgen.extra.ensureUnusedCapacity(
   5517         astgen.gpa,
   5518         @typeInfo(Zir.Inst.CondBr).Struct.fields.len + then_body_len + else_body_len,
   5519     );
   5520 
   5521     const zir_tags = astgen.instructions.items(.tag);
   5522     const zir_datas = astgen.instructions.items(.data);
   5523 
   5524     const condbr_pl = astgen.addExtraAssumeCapacity(Zir.Inst.CondBr{
   5525         .condition = cond,
   5526         .then_body_len = then_body_len,
   5527         .else_body_len = else_body_len,
   5528     });
   5529     zir_datas[condbr].pl_node.payload_index = condbr_pl;
   5530     const then_body_len_index = condbr_pl + 1;
   5531     const else_body_len_index = condbr_pl + 2;
   5532 
   5533     // The break instructions need to have their operands coerced if the
   5534     // switch's result location is a `ty`. In this case we overwrite the
   5535     // `store_to_block_ptr` instruction with an `as` instruction and repurpose
   5536     // it as the break operand.
   5537     // This corresponds to similar code in `labeledBlockExpr`.
   5538     for (then_body) |src_inst| {
   5539         if (zir_tags[src_inst] == .store_to_block_ptr and
   5540             zir_datas[src_inst].bin.lhs == block_ptr)
   5541         {
   5542             if (then_scope.rl_ty_inst != .none and has_then_break) {
   5543                 zir_tags[src_inst] = .as;
   5544                 zir_datas[src_inst].bin = .{
   5545                     .lhs = then_scope.rl_ty_inst,
   5546                     .rhs = zir_datas[then_break].@"break".operand,
   5547                 };
   5548                 zir_datas[then_break].@"break".operand = indexToRef(src_inst);
   5549             } else {
   5550                 astgen.extra.items[then_body_len_index] -= 1;
   5551                 continue;
   5552             }
   5553         }
   5554         appendPossiblyRefdBodyInst(astgen, &astgen.extra, src_inst);
   5555     }
   5556     if (has_then_break) astgen.extra.appendAssumeCapacity(then_break);
   5557 
   5558     for (else_body) |src_inst| {
   5559         if (zir_tags[src_inst] == .store_to_block_ptr and
   5560             zir_datas[src_inst].bin.lhs == block_ptr)
   5561         {
   5562             if (else_scope.rl_ty_inst != .none and has_else_break) {
   5563                 zir_tags[src_inst] = .as;
   5564                 zir_datas[src_inst].bin = .{
   5565                     .lhs = else_scope.rl_ty_inst,
   5566                     .rhs = zir_datas[else_break].@"break".operand,
   5567                 };
   5568                 zir_datas[else_break].@"break".operand = indexToRef(src_inst);
   5569             } else {
   5570                 astgen.extra.items[else_body_len_index] -= 1;
   5571                 continue;
   5572             }
   5573         }
   5574         appendPossiblyRefdBodyInst(astgen, &astgen.extra, src_inst);
   5575     }
   5576     if (has_else_break) astgen.extra.appendAssumeCapacity(else_break);
   5577 }
   5578 
   5579 fn whileExpr(
   5580     parent_gz: *GenZir,
   5581     scope: *Scope,
   5582     rl: ResultLoc,
   5583     node: Ast.Node.Index,
   5584     while_full: Ast.full.While,
   5585 ) InnerError!Zir.Inst.Ref {
   5586     const astgen = parent_gz.astgen;
   5587     const tree = astgen.tree;
   5588     const token_tags = tree.tokens.items(.tag);
   5589 
   5590     if (while_full.label_token) |label_token| {
   5591         try astgen.checkLabelRedefinition(scope, label_token);
   5592     }
   5593 
   5594     const is_inline = parent_gz.force_comptime or while_full.inline_token != null;
   5595     const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop;
   5596     const loop_block = try parent_gz.makeBlockInst(loop_tag, node);
   5597     try parent_gz.instructions.append(astgen.gpa, loop_block);
   5598 
   5599     var loop_scope = parent_gz.makeSubBlock(scope);
   5600     loop_scope.is_inline = is_inline;
   5601     loop_scope.setBreakResultLoc(rl);
   5602     defer loop_scope.unstack();
   5603     defer loop_scope.labeled_breaks.deinit(astgen.gpa);
   5604 
   5605     var continue_scope = parent_gz.makeSubBlock(&loop_scope.base);
   5606     defer continue_scope.unstack();
   5607 
   5608     const payload_is_ref = if (while_full.payload_token) |payload_token|
   5609         token_tags[payload_token] == .asterisk
   5610     else
   5611         false;
   5612 
   5613     try emitDbgNode(parent_gz, while_full.ast.cond_expr);
   5614     const cond: struct {
   5615         inst: Zir.Inst.Ref,
   5616         bool_bit: Zir.Inst.Ref,
   5617     } = c: {
   5618         if (while_full.error_token) |_| {
   5619             const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
   5620             const err_union = try expr(&continue_scope, &continue_scope.base, cond_rl, while_full.ast.cond_expr);
   5621             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err;
   5622             break :c .{
   5623                 .inst = err_union,
   5624                 .bool_bit = try continue_scope.addUnNode(tag, err_union, node),
   5625             };
   5626         } else if (while_full.payload_token) |_| {
   5627             const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
   5628             const optional = try expr(&continue_scope, &continue_scope.base, cond_rl, while_full.ast.cond_expr);
   5629             const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_null_ptr else .is_non_null;
   5630             break :c .{
   5631                 .inst = optional,
   5632                 .bool_bit = try continue_scope.addUnNode(tag, optional, node),
   5633             };
   5634         } else {
   5635             const cond = try expr(&continue_scope, &continue_scope.base, bool_rl, while_full.ast.cond_expr);
   5636             break :c .{
   5637                 .inst = cond,
   5638                 .bool_bit = cond,
   5639             };
   5640         }
   5641     };
   5642 
   5643     const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
   5644     const condbr = try continue_scope.addCondBr(condbr_tag, node);
   5645     const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block;
   5646     const cond_block = try loop_scope.makeBlockInst(block_tag, node);
   5647     try continue_scope.setBlockBody(cond_block);
   5648     // continue_scope unstacked now, can add new instructions to loop_scope
   5649     try loop_scope.instructions.append(astgen.gpa, cond_block);
   5650 
   5651     // make scope now but don't stack on parent_gz until loop_scope
   5652     // gets unstacked after cont_expr is emitted and added below
   5653     var then_scope = parent_gz.makeSubBlock(&continue_scope.base);
   5654     then_scope.markAsLoopBody(loop_scope);
   5655     then_scope.instructions_top = GenZir.unstacked_top;
   5656     defer then_scope.unstack();
   5657 
   5658     var dbg_var_name: ?u32 = null;
   5659     var dbg_var_inst: Zir.Inst.Ref = undefined;
   5660     var payload_inst: Zir.Inst.Index = 0;
   5661     var payload_val_scope: Scope.LocalVal = undefined;
   5662     const then_sub_scope = s: {
   5663         if (while_full.error_token != null) {
   5664             if (while_full.payload_token) |payload_token| {
   5665                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   5666                     .err_union_payload_unsafe_ptr
   5667                 else
   5668                     .err_union_payload_unsafe;
   5669                 // will add this instruction to then_scope.instructions below
   5670                 payload_inst = try then_scope.makeUnNode(tag, cond.inst, node);
   5671                 const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
   5672                 const ident_bytes = tree.tokenSlice(ident_token);
   5673                 if (mem.eql(u8, "_", ident_bytes))
   5674                     break :s &then_scope.base;
   5675                 const payload_name_loc = payload_token + @boolToInt(payload_is_ref);
   5676                 const ident_name = try astgen.identAsString(payload_name_loc);
   5677                 try astgen.detectLocalShadowing(&then_scope.base, ident_name, payload_name_loc, ident_bytes);
   5678                 payload_val_scope = .{
   5679                     .parent = &then_scope.base,
   5680                     .gen_zir = &then_scope,
   5681                     .name = ident_name,
   5682                     .inst = indexToRef(payload_inst),
   5683                     .token_src = payload_token,
   5684                     .id_cat = .@"capture",
   5685                 };
   5686                 dbg_var_name = ident_name;
   5687                 dbg_var_inst = indexToRef(payload_inst);
   5688                 break :s &payload_val_scope.base;
   5689             } else {
   5690                 break :s &then_scope.base;
   5691             }
   5692         } else if (while_full.payload_token) |payload_token| {
   5693             const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
   5694             const tag: Zir.Inst.Tag = if (payload_is_ref)
   5695                 .optional_payload_unsafe_ptr
   5696             else
   5697                 .optional_payload_unsafe;
   5698             // will add this instruction to then_scope.instructions below
   5699             payload_inst = try then_scope.makeUnNode(tag, cond.inst, node);
   5700             const ident_name = try astgen.identAsString(ident_token);
   5701             const ident_bytes = tree.tokenSlice(ident_token);
   5702             if (mem.eql(u8, "_", ident_bytes))
   5703                 break :s &then_scope.base;
   5704             try astgen.detectLocalShadowing(&then_scope.base, ident_name, ident_token, ident_bytes);
   5705             payload_val_scope = .{
   5706                 .parent = &then_scope.base,
   5707                 .gen_zir = &then_scope,
   5708                 .name = ident_name,
   5709                 .inst = indexToRef(payload_inst),
   5710                 .token_src = ident_token,
   5711                 .id_cat = .@"capture",
   5712             };
   5713             dbg_var_name = ident_name;
   5714             dbg_var_inst = indexToRef(payload_inst);
   5715             break :s &payload_val_scope.base;
   5716         } else {
   5717             break :s &then_scope.base;
   5718         }
   5719     };
   5720 
   5721     // This code could be improved to avoid emitting the continue expr when there
   5722     // are no jumps to it. This happens when the last statement of a while body is noreturn
   5723     // and there are no `continue` statements.
   5724     // Tracking issue: https://github.com/ziglang/zig/issues/9185
   5725     try then_scope.addDbgBlockBegin();
   5726     if (dbg_var_name) |some| {
   5727         try then_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
   5728     }
   5729     if (while_full.ast.cont_expr != 0) {
   5730         _ = try unusedResultExpr(&loop_scope, then_sub_scope, while_full.ast.cont_expr);
   5731     }
   5732     try then_scope.addDbgBlockEnd();
   5733     const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
   5734     _ = try loop_scope.addNode(repeat_tag, node);
   5735 
   5736     try loop_scope.setBlockBody(loop_block);
   5737     loop_scope.break_block = loop_block;
   5738     loop_scope.continue_block = cond_block;
   5739     if (while_full.label_token) |label_token| {
   5740         loop_scope.label = @as(?GenZir.Label, GenZir.Label{
   5741             .token = label_token,
   5742             .block_inst = loop_block,
   5743         });
   5744     }
   5745 
   5746     // done adding instructions to loop_scope, can now stack then_scope
   5747     then_scope.instructions_top = then_scope.instructions.items.len;
   5748 
   5749     if (payload_inst != 0) try then_scope.instructions.append(astgen.gpa, payload_inst);
   5750     try then_scope.addDbgBlockBegin();
   5751     if (dbg_var_name) |some| {
   5752         try then_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
   5753     }
   5754     const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, while_full.ast.then_expr);
   5755     try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
   5756     try then_scope.addDbgBlockEnd();
   5757 
   5758     var else_scope = parent_gz.makeSubBlock(&continue_scope.base);
   5759     defer else_scope.unstack();
   5760 
   5761     const else_node = while_full.ast.else_expr;
   5762     const else_info: struct {
   5763         src: Ast.Node.Index,
   5764         result: Zir.Inst.Ref,
   5765     } = if (else_node != 0) blk: {
   5766         try else_scope.addDbgBlockBegin();
   5767         const sub_scope = s: {
   5768             if (while_full.error_token) |error_token| {
   5769                 const tag: Zir.Inst.Tag = if (payload_is_ref)
   5770                     .err_union_code_ptr
   5771                 else
   5772                     .err_union_code;
   5773                 const else_payload_inst = try else_scope.addUnNode(tag, cond.inst, node);
   5774                 const ident_name = try astgen.identAsString(error_token);
   5775                 const ident_bytes = tree.tokenSlice(error_token);
   5776                 if (mem.eql(u8, ident_bytes, "_"))
   5777                     break :s &else_scope.base;
   5778                 try astgen.detectLocalShadowing(&else_scope.base, ident_name, error_token, ident_bytes);
   5779                 payload_val_scope = .{
   5780                     .parent = &else_scope.base,
   5781                     .gen_zir = &else_scope,
   5782                     .name = ident_name,
   5783                     .inst = else_payload_inst,
   5784                     .token_src = error_token,
   5785                     .id_cat = .@"capture",
   5786                 };
   5787                 try else_scope.addDbgVar(.dbg_var_val, ident_name, else_payload_inst);
   5788                 break :s &payload_val_scope.base;
   5789             } else {
   5790                 break :s &else_scope.base;
   5791             }
   5792         };
   5793         // Remove the continue block and break block so that `continue` and `break`
   5794         // control flow apply to outer loops; not this one.
   5795         loop_scope.continue_block = 0;
   5796         loop_scope.break_block = 0;
   5797         const e = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node);
   5798         if (!else_scope.endsWithNoReturn()) {
   5799             loop_scope.break_count += 1;
   5800         }
   5801         try checkUsed(parent_gz, &else_scope.base, sub_scope);
   5802         try else_scope.addDbgBlockEnd();
   5803         break :blk .{
   5804             .src = else_node,
   5805             .result = e,
   5806         };
   5807     } else .{
   5808         .src = while_full.ast.then_expr,
   5809         .result = .none,
   5810     };
   5811 
   5812     if (loop_scope.label) |some| {
   5813         if (!some.used) {
   5814             try astgen.appendErrorTok(some.token, "unused while loop label", .{});
   5815         }
   5816     }
   5817     const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
   5818     return finishThenElseBlock(
   5819         parent_gz,
   5820         rl,
   5821         node,
   5822         &loop_scope,
   5823         &then_scope,
   5824         &else_scope,
   5825         condbr,
   5826         cond.bool_bit,
   5827         then_result,
   5828         else_info.result,
   5829         loop_block,
   5830         cond_block,
   5831         break_tag,
   5832     );
   5833 }
   5834 
   5835 fn forExpr(
   5836     parent_gz: *GenZir,
   5837     scope: *Scope,
   5838     rl: ResultLoc,
   5839     node: Ast.Node.Index,
   5840     for_full: Ast.full.While,
   5841 ) InnerError!Zir.Inst.Ref {
   5842     const astgen = parent_gz.astgen;
   5843 
   5844     if (for_full.label_token) |label_token| {
   5845         try astgen.checkLabelRedefinition(scope, label_token);
   5846     }
   5847 
   5848     // Set up variables and constants.
   5849     const is_inline = parent_gz.force_comptime or for_full.inline_token != null;
   5850     const tree = astgen.tree;
   5851     const token_tags = tree.tokens.items(.tag);
   5852 
   5853     const payload_is_ref = if (for_full.payload_token) |payload_token|
   5854         token_tags[payload_token] == .asterisk
   5855     else
   5856         false;
   5857 
   5858     try emitDbgNode(parent_gz, for_full.ast.cond_expr);
   5859 
   5860     const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none;
   5861     const array_ptr = try expr(parent_gz, scope, cond_rl, for_full.ast.cond_expr);
   5862     const len = try parent_gz.addUnNode(.indexable_ptr_len, array_ptr, for_full.ast.cond_expr);
   5863 
   5864     const index_ptr = blk: {
   5865         const alloc_tag: Zir.Inst.Tag = if (is_inline) .alloc_comptime_mut else .alloc;
   5866         const index_ptr = try parent_gz.addUnNode(alloc_tag, .usize_type, node);
   5867         // initialize to zero
   5868         _ = try parent_gz.addBin(.store, index_ptr, .zero_usize);
   5869         break :blk index_ptr;
   5870     };
   5871 
   5872     const loop_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .loop;
   5873     const loop_block = try parent_gz.makeBlockInst(loop_tag, node);
   5874     try parent_gz.instructions.append(astgen.gpa, loop_block);
   5875 
   5876     var loop_scope = parent_gz.makeSubBlock(scope);
   5877     loop_scope.is_inline = is_inline;
   5878     loop_scope.setBreakResultLoc(rl);
   5879     defer loop_scope.unstack();
   5880     defer loop_scope.labeled_breaks.deinit(astgen.gpa);
   5881 
   5882     var cond_scope = parent_gz.makeSubBlock(&loop_scope.base);
   5883     defer cond_scope.unstack();
   5884 
   5885     // check condition i < array_expr.len
   5886     const index = try cond_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr);
   5887     const cond = try cond_scope.addPlNode(.cmp_lt, for_full.ast.cond_expr, Zir.Inst.Bin{
   5888         .lhs = index,
   5889         .rhs = len,
   5890     });
   5891 
   5892     const condbr_tag: Zir.Inst.Tag = if (is_inline) .condbr_inline else .condbr;
   5893     const condbr = try cond_scope.addCondBr(condbr_tag, node);
   5894     const block_tag: Zir.Inst.Tag = if (is_inline) .block_inline else .block;
   5895     const cond_block = try loop_scope.makeBlockInst(block_tag, node);
   5896     try cond_scope.setBlockBody(cond_block);
   5897     // cond_block unstacked now, can add new instructions to loop_scope
   5898     try loop_scope.instructions.append(astgen.gpa, cond_block);
   5899 
   5900     // Increment the index variable.
   5901     const index_2 = try loop_scope.addUnNode(.load, index_ptr, for_full.ast.cond_expr);
   5902     const index_plus_one = try loop_scope.addPlNode(.add, node, Zir.Inst.Bin{
   5903         .lhs = index_2,
   5904         .rhs = .one_usize,
   5905     });
   5906     _ = try loop_scope.addBin(.store, index_ptr, index_plus_one);
   5907     const repeat_tag: Zir.Inst.Tag = if (is_inline) .repeat_inline else .repeat;
   5908     _ = try loop_scope.addNode(repeat_tag, node);
   5909 
   5910     try loop_scope.setBlockBody(loop_block);
   5911     loop_scope.break_block = loop_block;
   5912     loop_scope.continue_block = cond_block;
   5913     if (for_full.label_token) |label_token| {
   5914         loop_scope.label = @as(?GenZir.Label, GenZir.Label{
   5915             .token = label_token,
   5916             .block_inst = loop_block,
   5917         });
   5918     }
   5919 
   5920     var then_scope = parent_gz.makeSubBlock(&cond_scope.base);
   5921     then_scope.markAsLoopBody(loop_scope);
   5922     defer then_scope.unstack();
   5923 
   5924     try then_scope.addDbgBlockBegin();
   5925     var payload_val_scope: Scope.LocalVal = undefined;
   5926     var index_scope: Scope.LocalPtr = undefined;
   5927     const then_sub_scope = blk: {
   5928         const payload_token = for_full.payload_token.?;
   5929         const ident = if (token_tags[payload_token] == .asterisk)
   5930             payload_token + 1
   5931         else
   5932             payload_token;
   5933         const is_ptr = ident != payload_token;
   5934         const value_name = tree.tokenSlice(ident);
   5935         var payload_sub_scope: *Scope = undefined;
   5936         if (!mem.eql(u8, value_name, "_")) {
   5937             const name_str_index = try astgen.identAsString(ident);
   5938             const tag: Zir.Inst.Tag = if (is_ptr) .elem_ptr else .elem_val;
   5939             const payload_inst = try then_scope.addPlNode(tag, for_full.ast.cond_expr, Zir.Inst.Bin{
   5940                 .lhs = array_ptr,
   5941                 .rhs = index,
   5942             });
   5943             try astgen.detectLocalShadowing(&then_scope.base, name_str_index, ident, value_name);
   5944             payload_val_scope = .{
   5945                 .parent = &then_scope.base,
   5946                 .gen_zir = &then_scope,
   5947                 .name = name_str_index,
   5948                 .inst = payload_inst,
   5949                 .token_src = ident,
   5950                 .id_cat = .@"capture",
   5951             };
   5952             try then_scope.addDbgVar(.dbg_var_val, name_str_index, payload_inst);
   5953             payload_sub_scope = &payload_val_scope.base;
   5954         } else if (is_ptr) {
   5955             return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   5956         } else {
   5957             payload_sub_scope = &then_scope.base;
   5958         }
   5959 
   5960         const index_token = if (token_tags[ident + 1] == .comma)
   5961             ident + 2
   5962         else
   5963             break :blk payload_sub_scope;
   5964         const token_bytes = tree.tokenSlice(index_token);
   5965         if (mem.eql(u8, token_bytes, "_")) {
   5966             return astgen.failTok(index_token, "discard of index capture; omit it instead", .{});
   5967         }
   5968         const index_name = try astgen.identAsString(index_token);
   5969         try astgen.detectLocalShadowing(payload_sub_scope, index_name, index_token, token_bytes);
   5970         index_scope = .{
   5971             .parent = payload_sub_scope,
   5972             .gen_zir = &then_scope,
   5973             .name = index_name,
   5974             .ptr = index_ptr,
   5975             .token_src = index_token,
   5976             .maybe_comptime = is_inline,
   5977             .id_cat = .@"loop index capture",
   5978         };
   5979         try then_scope.addDbgVar(.dbg_var_val, index_name, index_ptr);
   5980         break :blk &index_scope.base;
   5981     };
   5982 
   5983     const then_result = try expr(&then_scope, then_sub_scope, loop_scope.break_result_loc, for_full.ast.then_expr);
   5984     try checkUsed(parent_gz, &then_scope.base, then_sub_scope);
   5985     try then_scope.addDbgBlockEnd();
   5986 
   5987     var else_scope = parent_gz.makeSubBlock(&cond_scope.base);
   5988     defer else_scope.unstack();
   5989 
   5990     const else_node = for_full.ast.else_expr;
   5991     const else_info: struct {
   5992         src: Ast.Node.Index,
   5993         result: Zir.Inst.Ref,
   5994     } = if (else_node != 0) blk: {
   5995         const sub_scope = &else_scope.base;
   5996         // Remove the continue block and break block so that `continue` and `break`
   5997         // control flow apply to outer loops; not this one.
   5998         loop_scope.continue_block = 0;
   5999         loop_scope.break_block = 0;
   6000         const else_result = try expr(&else_scope, sub_scope, loop_scope.break_result_loc, else_node);
   6001         if (!else_scope.endsWithNoReturn()) {
   6002             loop_scope.break_count += 1;
   6003         }
   6004         break :blk .{
   6005             .src = else_node,
   6006             .result = else_result,
   6007         };
   6008     } else .{
   6009         .src = for_full.ast.then_expr,
   6010         .result = .none,
   6011     };
   6012 
   6013     if (loop_scope.label) |some| {
   6014         if (!some.used) {
   6015             try astgen.appendErrorTok(some.token, "unused for loop label", .{});
   6016         }
   6017     }
   6018     const break_tag: Zir.Inst.Tag = if (is_inline) .break_inline else .@"break";
   6019     return finishThenElseBlock(
   6020         parent_gz,
   6021         rl,
   6022         node,
   6023         &loop_scope,
   6024         &then_scope,
   6025         &else_scope,
   6026         condbr,
   6027         cond,
   6028         then_result,
   6029         else_info.result,
   6030         loop_block,
   6031         cond_block,
   6032         break_tag,
   6033     );
   6034 }
   6035 
   6036 fn switchExpr(
   6037     parent_gz: *GenZir,
   6038     scope: *Scope,
   6039     rl: ResultLoc,
   6040     switch_node: Ast.Node.Index,
   6041 ) InnerError!Zir.Inst.Ref {
   6042     const astgen = parent_gz.astgen;
   6043     const gpa = astgen.gpa;
   6044     const tree = astgen.tree;
   6045     const node_datas = tree.nodes.items(.data);
   6046     const node_tags = tree.nodes.items(.tag);
   6047     const main_tokens = tree.nodes.items(.main_token);
   6048     const token_tags = tree.tokens.items(.tag);
   6049     const operand_node = node_datas[switch_node].lhs;
   6050     const extra = tree.extraData(node_datas[switch_node].rhs, Ast.Node.SubRange);
   6051     const case_nodes = tree.extra_data[extra.start..extra.end];
   6052 
   6053     // We perform two passes over the AST. This first pass is to collect information
   6054     // for the following variables, make note of the special prong AST node index,
   6055     // and bail out with a compile error if there are multiple special prongs present.
   6056     var any_payload_is_ref = false;
   6057     var scalar_cases_len: u32 = 0;
   6058     var multi_cases_len: u32 = 0;
   6059     var special_prong: Zir.SpecialProng = .none;
   6060     var special_node: Ast.Node.Index = 0;
   6061     var else_src: ?Ast.TokenIndex = null;
   6062     var underscore_src: ?Ast.TokenIndex = null;
   6063     for (case_nodes) |case_node| {
   6064         const case = switch (node_tags[case_node]) {
   6065             .switch_case_one => tree.switchCaseOne(case_node),
   6066             .switch_case => tree.switchCase(case_node),
   6067             else => unreachable,
   6068         };
   6069         if (case.payload_token) |payload_token| {
   6070             if (token_tags[payload_token] == .asterisk) {
   6071                 any_payload_is_ref = true;
   6072             }
   6073         }
   6074         // Check for else/`_` prong.
   6075         if (case.ast.values.len == 0) {
   6076             const case_src = case.ast.arrow_token - 1;
   6077             if (else_src) |src| {
   6078                 return astgen.failTokNotes(
   6079                     case_src,
   6080                     "multiple else prongs in switch expression",
   6081                     .{},
   6082                     &[_]u32{
   6083                         try astgen.errNoteTok(
   6084                             src,
   6085                             "previous else prong here",
   6086                             .{},
   6087                         ),
   6088                     },
   6089                 );
   6090             } else if (underscore_src) |some_underscore| {
   6091                 return astgen.failNodeNotes(
   6092                     switch_node,
   6093                     "else and '_' prong in switch expression",
   6094                     .{},
   6095                     &[_]u32{
   6096                         try astgen.errNoteTok(
   6097                             case_src,
   6098                             "else prong here",
   6099                             .{},
   6100                         ),
   6101                         try astgen.errNoteTok(
   6102                             some_underscore,
   6103                             "'_' prong here",
   6104                             .{},
   6105                         ),
   6106                     },
   6107                 );
   6108             }
   6109             special_node = case_node;
   6110             special_prong = .@"else";
   6111             else_src = case_src;
   6112             continue;
   6113         } else if (case.ast.values.len == 1 and
   6114             node_tags[case.ast.values[0]] == .identifier and
   6115             mem.eql(u8, tree.tokenSlice(main_tokens[case.ast.values[0]]), "_"))
   6116         {
   6117             const case_src = case.ast.arrow_token - 1;
   6118             if (underscore_src) |src| {
   6119                 return astgen.failTokNotes(
   6120                     case_src,
   6121                     "multiple '_' prongs in switch expression",
   6122                     .{},
   6123                     &[_]u32{
   6124                         try astgen.errNoteTok(
   6125                             src,
   6126                             "previous '_' prong here",
   6127                             .{},
   6128                         ),
   6129                     },
   6130                 );
   6131             } else if (else_src) |some_else| {
   6132                 return astgen.failNodeNotes(
   6133                     switch_node,
   6134                     "else and '_' prong in switch expression",
   6135                     .{},
   6136                     &[_]u32{
   6137                         try astgen.errNoteTok(
   6138                             some_else,
   6139                             "else prong here",
   6140                             .{},
   6141                         ),
   6142                         try astgen.errNoteTok(
   6143                             case_src,
   6144                             "'_' prong here",
   6145                             .{},
   6146                         ),
   6147                     },
   6148                 );
   6149             }
   6150             special_node = case_node;
   6151             special_prong = .under;
   6152             underscore_src = case_src;
   6153             continue;
   6154         }
   6155 
   6156         if (case.ast.values.len == 1 and node_tags[case.ast.values[0]] != .switch_range) {
   6157             scalar_cases_len += 1;
   6158         } else {
   6159             multi_cases_len += 1;
   6160         }
   6161     }
   6162 
   6163     const operand_rl: ResultLoc = if (any_payload_is_ref) .ref else .none;
   6164     const raw_operand = try expr(parent_gz, scope, operand_rl, operand_node);
   6165     const cond_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_cond_ref else .switch_cond;
   6166     const cond = try parent_gz.addUnNode(cond_tag, raw_operand, operand_node);
   6167     // We need the type of the operand to use as the result location for all the prong items.
   6168     const cond_ty_inst = try parent_gz.addUnNode(.typeof, cond, operand_node);
   6169     const item_rl: ResultLoc = .{ .ty = cond_ty_inst };
   6170 
   6171     // This contains the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti,
   6172     // except the first cases_nodes.len slots are a table that indexes payloads later in the array, with
   6173     // the special case index coming first, then scalar_case_len indexes, then multi_cases_len indexes
   6174     const payloads = &astgen.scratch;
   6175     const scratch_top = astgen.scratch.items.len;
   6176     const case_table_start = scratch_top;
   6177     const scalar_case_table = case_table_start + @boolToInt(special_prong != .none);
   6178     const multi_case_table = scalar_case_table + scalar_cases_len;
   6179     const case_table_end = multi_case_table + multi_cases_len;
   6180     try astgen.scratch.resize(gpa, case_table_end);
   6181     defer astgen.scratch.items.len = scratch_top;
   6182 
   6183     var block_scope = parent_gz.makeSubBlock(scope);
   6184     // block_scope not used for collecting instructions
   6185     block_scope.instructions_top = GenZir.unstacked_top;
   6186     block_scope.setBreakResultLoc(rl);
   6187 
   6188     // This gets added to the parent block later, after the item expressions.
   6189     const switch_block = try parent_gz.makeBlockInst(.switch_block, switch_node);
   6190 
   6191     // We re-use this same scope for all cases, including the special prong, if any.
   6192     var case_scope = parent_gz.makeSubBlock(&block_scope.base);
   6193     case_scope.instructions_top = GenZir.unstacked_top;
   6194 
   6195     // In this pass we generate all the item and prong expressions.
   6196     var multi_case_index: u32 = 0;
   6197     var scalar_case_index: u32 = 0;
   6198     for (case_nodes) |case_node| {
   6199         const case = switch (node_tags[case_node]) {
   6200             .switch_case_one => tree.switchCaseOne(case_node),
   6201             .switch_case => tree.switchCase(case_node),
   6202             else => unreachable,
   6203         };
   6204 
   6205         const is_multi_case = case.ast.values.len > 1 or
   6206             (case.ast.values.len == 1 and node_tags[case.ast.values[0]] == .switch_range);
   6207 
   6208         var dbg_var_name: ?u32 = null;
   6209         var dbg_var_inst: Zir.Inst.Ref = undefined;
   6210         var capture_inst: Zir.Inst.Index = 0;
   6211         var capture_val_scope: Scope.LocalVal = undefined;
   6212         const sub_scope = blk: {
   6213             const payload_token = case.payload_token orelse break :blk &case_scope.base;
   6214             const ident = if (token_tags[payload_token] == .asterisk)
   6215                 payload_token + 1
   6216             else
   6217                 payload_token;
   6218             const is_ptr = ident != payload_token;
   6219             if (mem.eql(u8, tree.tokenSlice(ident), "_")) {
   6220                 if (is_ptr) {
   6221                     return astgen.failTok(payload_token, "pointer modifier invalid on discard", .{});
   6222                 }
   6223                 break :blk &case_scope.base;
   6224             }
   6225             if (case_node == special_node) {
   6226                 const capture_tag: Zir.Inst.Tag = if (is_ptr)
   6227                     .switch_capture_ref
   6228                 else
   6229                     .switch_capture;
   6230                 capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
   6231                 try astgen.instructions.append(gpa, .{
   6232                     .tag = capture_tag,
   6233                     .data = .{
   6234                         .switch_capture = .{
   6235                             .switch_inst = switch_block,
   6236                             // Max int communicates that this is the else/underscore prong.
   6237                             .prong_index = std.math.maxInt(u32),
   6238                         },
   6239                     },
   6240                 });
   6241             } else {
   6242                 const is_multi_case_bits: u2 = @boolToInt(is_multi_case);
   6243                 const is_ptr_bits: u2 = @boolToInt(is_ptr);
   6244                 const capture_tag: Zir.Inst.Tag = switch ((is_multi_case_bits << 1) | is_ptr_bits) {
   6245                     0b00 => .switch_capture,
   6246                     0b01 => .switch_capture_ref,
   6247                     0b10 => .switch_capture_multi,
   6248                     0b11 => .switch_capture_multi_ref,
   6249                 };
   6250                 const capture_index = if (is_multi_case) multi_case_index else scalar_case_index;
   6251                 capture_inst = @intCast(Zir.Inst.Index, astgen.instructions.len);
   6252                 try astgen.instructions.append(gpa, .{
   6253                     .tag = capture_tag,
   6254                     .data = .{ .switch_capture = .{
   6255                         .switch_inst = switch_block,
   6256                         .prong_index = capture_index,
   6257                     } },
   6258                 });
   6259             }
   6260             const capture_name = try astgen.identAsString(ident);
   6261             capture_val_scope = .{
   6262                 .parent = &case_scope.base,
   6263                 .gen_zir = &case_scope,
   6264                 .name = capture_name,
   6265                 .inst = indexToRef(capture_inst),
   6266                 .token_src = payload_token,
   6267                 .id_cat = .@"capture",
   6268             };
   6269             dbg_var_name = capture_name;
   6270             dbg_var_inst = indexToRef(capture_inst);
   6271             break :blk &capture_val_scope.base;
   6272         };
   6273 
   6274         const header_index = @intCast(u32, payloads.items.len);
   6275         const body_len_index = if (is_multi_case) blk: {
   6276             payloads.items[multi_case_table + multi_case_index] = header_index;
   6277             multi_case_index += 1;
   6278             try payloads.resize(gpa, header_index + 3); // items_len, ranges_len, body_len
   6279 
   6280             // items
   6281             var items_len: u32 = 0;
   6282             for (case.ast.values) |item_node| {
   6283                 if (node_tags[item_node] == .switch_range) continue;
   6284                 items_len += 1;
   6285 
   6286                 const item_inst = try comptimeExpr(parent_gz, scope, item_rl, item_node);
   6287                 try payloads.append(gpa, @enumToInt(item_inst));
   6288             }
   6289 
   6290             // ranges
   6291             var ranges_len: u32 = 0;
   6292             for (case.ast.values) |range| {
   6293                 if (node_tags[range] != .switch_range) continue;
   6294                 ranges_len += 1;
   6295 
   6296                 const first = try comptimeExpr(parent_gz, scope, item_rl, node_datas[range].lhs);
   6297                 const last = try comptimeExpr(parent_gz, scope, item_rl, node_datas[range].rhs);
   6298                 try payloads.appendSlice(gpa, &[_]u32{
   6299                     @enumToInt(first), @enumToInt(last),
   6300                 });
   6301             }
   6302 
   6303             payloads.items[header_index] = items_len;
   6304             payloads.items[header_index + 1] = ranges_len;
   6305             break :blk header_index + 2;
   6306         } else if (case_node == special_node) blk: {
   6307             payloads.items[case_table_start] = header_index;
   6308             try payloads.resize(gpa, header_index + 1); // body_len
   6309             break :blk header_index;
   6310         } else blk: {
   6311             payloads.items[scalar_case_table + scalar_case_index] = header_index;
   6312             scalar_case_index += 1;
   6313             try payloads.resize(gpa, header_index + 2); // item, body_len
   6314             const item_node = case.ast.values[0];
   6315             const item_inst = try comptimeExpr(parent_gz, scope, item_rl, item_node);
   6316             payloads.items[header_index] = @enumToInt(item_inst);
   6317             break :blk header_index + 1;
   6318         };
   6319 
   6320         {
   6321             // temporarily stack case_scope on parent_gz
   6322             case_scope.instructions_top = parent_gz.instructions.items.len;
   6323             defer case_scope.unstack();
   6324 
   6325             if (capture_inst != 0) try case_scope.instructions.append(gpa, capture_inst);
   6326             try case_scope.addDbgBlockBegin();
   6327             if (dbg_var_name) |some| {
   6328                 try case_scope.addDbgVar(.dbg_var_val, some, dbg_var_inst);
   6329             }
   6330             const case_result = try expr(&case_scope, sub_scope, block_scope.break_result_loc, case.ast.target_expr);
   6331             try checkUsed(parent_gz, &case_scope.base, sub_scope);
   6332             try case_scope.addDbgBlockEnd();
   6333             if (!parent_gz.refIsNoReturn(case_result)) {
   6334                 block_scope.break_count += 1;
   6335                 _ = try case_scope.addBreak(.@"break", switch_block, case_result);
   6336             }
   6337 
   6338             const case_slice = case_scope.instructionsSlice();
   6339             const body_len = astgen.countBodyLenAfterFixups(case_slice);
   6340             try payloads.ensureUnusedCapacity(gpa, body_len);
   6341             payloads.items[body_len_index] = body_len;
   6342             appendBodyWithFixupsArrayList(astgen, payloads, case_slice);
   6343         }
   6344     }
   6345     // Now that the item expressions are generated we can add this.
   6346     try parent_gz.instructions.append(gpa, switch_block);
   6347 
   6348     try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.SwitchBlock).Struct.fields.len +
   6349         @boolToInt(multi_cases_len != 0) +
   6350         payloads.items.len - case_table_end);
   6351 
   6352     const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.SwitchBlock{
   6353         .operand = cond,
   6354         .bits = Zir.Inst.SwitchBlock.Bits{
   6355             .is_ref = any_payload_is_ref,
   6356             .has_multi_cases = multi_cases_len != 0,
   6357             .has_else = special_prong == .@"else",
   6358             .has_under = special_prong == .under,
   6359             .scalar_cases_len = @intCast(Zir.Inst.SwitchBlock.Bits.ScalarCasesLen, scalar_cases_len),
   6360         },
   6361     });
   6362 
   6363     if (multi_cases_len != 0) {
   6364         astgen.extra.appendAssumeCapacity(multi_cases_len);
   6365     }
   6366 
   6367     const zir_datas = astgen.instructions.items(.data);
   6368     const zir_tags = astgen.instructions.items(.tag);
   6369 
   6370     zir_datas[switch_block].pl_node.payload_index = payload_index;
   6371 
   6372     const strat = rl.strategy(&block_scope);
   6373     for (payloads.items[case_table_start..case_table_end]) |start_index, i| {
   6374         var body_len_index = start_index;
   6375         var end_index = start_index;
   6376         const table_index = case_table_start + i;
   6377         if (table_index < scalar_case_table) {
   6378             end_index += 1;
   6379         } else if (table_index < multi_case_table) {
   6380             body_len_index += 1;
   6381             end_index += 2;
   6382         } else {
   6383             body_len_index += 2;
   6384             const items_len = payloads.items[start_index];
   6385             const ranges_len = payloads.items[start_index + 1];
   6386             end_index += 3 + items_len + 2 * ranges_len;
   6387         }
   6388 
   6389         const body_len = payloads.items[body_len_index];
   6390         end_index += body_len;
   6391 
   6392         switch (strat.tag) {
   6393             .break_operand => blk: {
   6394                 // Switch expressions return `true` for `nodeMayNeedMemoryLocation` thus
   6395                 // `elide_store_to_block_ptr_instructions` will either be true,
   6396                 // or all prongs are noreturn.
   6397                 if (!strat.elide_store_to_block_ptr_instructions)
   6398                     break :blk;
   6399 
   6400                 // There will necessarily be a store_to_block_ptr for
   6401                 // all prongs, except for prongs that ended with a noreturn instruction.
   6402                 // Elide all the `store_to_block_ptr` instructions.
   6403 
   6404                 // The break instructions need to have their operands coerced if the
   6405                 // switch's result location is a `ty`. In this case we overwrite the
   6406                 // `store_to_block_ptr` instruction with an `as` instruction and repurpose
   6407                 // it as the break operand.
   6408                 if (body_len < 2)
   6409                     break :blk;
   6410                 const store_inst = payloads.items[end_index - 2];
   6411                 if (zir_tags[store_inst] != .store_to_block_ptr or
   6412                     zir_datas[store_inst].bin.lhs != block_scope.rl_ptr)
   6413                     break :blk;
   6414                 const break_inst = payloads.items[end_index - 1];
   6415                 if (block_scope.rl_ty_inst != .none) {
   6416                     zir_tags[store_inst] = .as;
   6417                     zir_datas[store_inst].bin = .{
   6418                         .lhs = block_scope.rl_ty_inst,
   6419                         .rhs = zir_datas[break_inst].@"break".operand,
   6420                     };
   6421                     zir_datas[break_inst].@"break".operand = indexToRef(store_inst);
   6422                 } else {
   6423                     payloads.items[body_len_index] -= 1;
   6424                     astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index .. end_index - 2]);
   6425                     astgen.extra.appendAssumeCapacity(break_inst);
   6426                     continue;
   6427                 }
   6428             },
   6429             .break_void => {
   6430                 assert(!strat.elide_store_to_block_ptr_instructions);
   6431                 const last_inst = payloads.items[end_index - 1];
   6432                 if (zir_tags[last_inst] == .@"break" and
   6433                     zir_datas[last_inst].@"break".block_inst == switch_block)
   6434                 {
   6435                     zir_datas[last_inst].@"break".operand = .void_value;
   6436                 }
   6437             },
   6438         }
   6439 
   6440         astgen.extra.appendSliceAssumeCapacity(payloads.items[start_index..end_index]);
   6441     }
   6442 
   6443     const block_ref = indexToRef(switch_block);
   6444     if (strat.tag == .break_operand and strat.elide_store_to_block_ptr_instructions and rl != .ref)
   6445         return rvalue(parent_gz, rl, block_ref, switch_node);
   6446     return block_ref;
   6447 }
   6448 
   6449 fn ret(gz: *GenZir, scope: *Scope, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   6450     const astgen = gz.astgen;
   6451     const tree = astgen.tree;
   6452     const node_datas = tree.nodes.items(.data);
   6453     const node_tags = tree.nodes.items(.tag);
   6454 
   6455     if (astgen.fn_block == null) {
   6456         return astgen.failNode(node, "'return' outside function scope", .{});
   6457     }
   6458 
   6459     if (gz.in_defer) return astgen.failNode(node, "cannot return from defer expression", .{});
   6460 
   6461     const defer_outer = &astgen.fn_block.?.base;
   6462 
   6463     const operand_node = node_datas[node].lhs;
   6464     if (operand_node == 0) {
   6465         // Returning a void value; skip error defers.
   6466         try genDefers(gz, defer_outer, scope, .normal_only);
   6467         _ = try gz.addUnNode(.ret_node, .void_value, node);
   6468         return Zir.Inst.Ref.unreachable_value;
   6469     }
   6470 
   6471     if (node_tags[operand_node] == .error_value) {
   6472         // Hot path for `return error.Foo`. This bypasses result location logic as well as logic
   6473         // for detecting whether to add something to the function's inferred error set.
   6474         const ident_token = node_datas[operand_node].rhs;
   6475         const err_name_str_index = try astgen.identAsString(ident_token);
   6476         const defer_counts = countDefers(astgen, defer_outer, scope);
   6477         if (!defer_counts.need_err_code) {
   6478             try genDefers(gz, defer_outer, scope, .both_sans_err);
   6479             _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token);
   6480             return Zir.Inst.Ref.unreachable_value;
   6481         }
   6482         const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token);
   6483         try genDefers(gz, defer_outer, scope, .{ .both = err_code });
   6484         _ = try gz.addUnNode(.ret_node, err_code, node);
   6485         return Zir.Inst.Ref.unreachable_value;
   6486     }
   6487 
   6488     const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node, true)) .{
   6489         .ptr = try gz.addNode(.ret_ptr, node),
   6490     } else .{
   6491         .ty = try gz.addNode(.ret_type, node),
   6492     };
   6493     const prev_anon_name_strategy = gz.anon_name_strategy;
   6494     gz.anon_name_strategy = .func;
   6495     const operand = try reachableExpr(gz, scope, rl, operand_node, node);
   6496     gz.anon_name_strategy = prev_anon_name_strategy;
   6497 
   6498     switch (nodeMayEvalToError(tree, operand_node)) {
   6499         .never => {
   6500             // Returning a value that cannot be an error; skip error defers.
   6501             try genDefers(gz, defer_outer, scope, .normal_only);
   6502             try gz.addRet(rl, operand, node);
   6503             return Zir.Inst.Ref.unreachable_value;
   6504         },
   6505         .always => {
   6506             // Value is always an error. Emit both error defers and regular defers.
   6507             const result = if (rl == .ptr) try gz.addUnNode(.load, rl.ptr, node) else operand;
   6508             const err_code = try gz.addUnNode(.err_union_code, result, node);
   6509             try genDefers(gz, defer_outer, scope, .{ .both = err_code });
   6510             try gz.addRet(rl, operand, node);
   6511             return Zir.Inst.Ref.unreachable_value;
   6512         },
   6513         .maybe => {
   6514             const defer_counts = countDefers(astgen, defer_outer, scope);
   6515             if (!defer_counts.have_err) {
   6516                 // Only regular defers; no branch needed.
   6517                 try genDefers(gz, defer_outer, scope, .normal_only);
   6518                 try gz.addRet(rl, operand, node);
   6519                 return Zir.Inst.Ref.unreachable_value;
   6520             }
   6521 
   6522             // Emit conditional branch for generating errdefers.
   6523             const result = if (rl == .ptr) try gz.addUnNode(.load, rl.ptr, node) else operand;
   6524             const is_non_err = try gz.addUnNode(.is_non_err, result, node);
   6525             const condbr = try gz.addCondBr(.condbr, node);
   6526 
   6527             var then_scope = gz.makeSubBlock(scope);
   6528             defer then_scope.unstack();
   6529 
   6530             try genDefers(&then_scope, defer_outer, scope, .normal_only);
   6531             try then_scope.addRet(rl, operand, node);
   6532 
   6533             var else_scope = gz.makeSubBlock(scope);
   6534             defer else_scope.unstack();
   6535 
   6536             const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{
   6537                 .both = try else_scope.addUnNode(.err_union_code, result, node),
   6538             };
   6539             try genDefers(&else_scope, defer_outer, scope, which_ones);
   6540             try else_scope.addRet(rl, operand, node);
   6541 
   6542             try setCondBrPayload(condbr, is_non_err, &then_scope, 0, &else_scope, 0);
   6543 
   6544             return Zir.Inst.Ref.unreachable_value;
   6545         },
   6546     }
   6547 }
   6548 
   6549 /// Parses the string `buf` as a base 10 integer of type `u16`.
   6550 ///
   6551 /// Unlike std.fmt.parseInt, does not allow the '_' character in `buf`.
   6552 fn parseBitCount(buf: []const u8) std.fmt.ParseIntError!u16 {
   6553     if (buf.len == 0) return error.InvalidCharacter;
   6554 
   6555     var x: u16 = 0;
   6556 
   6557     for (buf) |c| {
   6558         const digit = switch (c) {
   6559             '0'...'9' => c - '0',
   6560             else => return error.InvalidCharacter,
   6561         };
   6562 
   6563         if (x != 0) x = try std.math.mul(u16, x, 10);
   6564         x = try std.math.add(u16, x, @as(u16, digit));
   6565     }
   6566 
   6567     return x;
   6568 }
   6569 
   6570 fn identifier(
   6571     gz: *GenZir,
   6572     scope: *Scope,
   6573     rl: ResultLoc,
   6574     ident: Ast.Node.Index,
   6575 ) InnerError!Zir.Inst.Ref {
   6576     const tracy = trace(@src());
   6577     defer tracy.end();
   6578 
   6579     const astgen = gz.astgen;
   6580     const tree = astgen.tree;
   6581     const main_tokens = tree.nodes.items(.main_token);
   6582 
   6583     const ident_token = main_tokens[ident];
   6584     const ident_name_raw = tree.tokenSlice(ident_token);
   6585     if (mem.eql(u8, ident_name_raw, "_")) {
   6586         return astgen.failNode(ident, "'_' used as an identifier without @\"_\" syntax", .{});
   6587     }
   6588 
   6589     // if not @"" syntax, just use raw token slice
   6590     if (ident_name_raw[0] != '@') {
   6591         if (primitives.get(ident_name_raw)) |zir_const_ref| {
   6592             return rvalue(gz, rl, zir_const_ref, ident);
   6593         }
   6594 
   6595         if (ident_name_raw.len >= 2) integer: {
   6596             const first_c = ident_name_raw[0];
   6597             if (first_c == 'i' or first_c == 'u') {
   6598                 const signedness: std.builtin.Signedness = switch (first_c == 'i') {
   6599                     true => .signed,
   6600                     false => .unsigned,
   6601                 };
   6602                 const bit_count = parseBitCount(ident_name_raw[1..]) catch |err| switch (err) {
   6603                     error.Overflow => return astgen.failNode(
   6604                         ident,
   6605                         "primitive integer type '{s}' exceeds maximum bit width of 65535",
   6606                         .{ident_name_raw},
   6607                     ),
   6608                     error.InvalidCharacter => break :integer,
   6609                 };
   6610                 const result = try gz.add(.{
   6611                     .tag = .int_type,
   6612                     .data = .{ .int_type = .{
   6613                         .src_node = gz.nodeIndexToRelative(ident),
   6614                         .signedness = signedness,
   6615                         .bit_count = bit_count,
   6616                     } },
   6617                 });
   6618                 return rvalue(gz, rl, result, ident);
   6619             }
   6620         }
   6621     }
   6622 
   6623     // Local variables, including function parameters.
   6624     return localVarRef(gz, scope, rl, ident, ident_token);
   6625 }
   6626 
   6627 fn localVarRef(
   6628     gz: *GenZir,
   6629     scope: *Scope,
   6630     rl: ResultLoc,
   6631     ident: Ast.Node.Index,
   6632     ident_token: Ast.Node.Index,
   6633 ) InnerError!Zir.Inst.Ref {
   6634     const astgen = gz.astgen;
   6635     const gpa = astgen.gpa;
   6636 
   6637     const name_str_index = try astgen.identAsString(ident_token);
   6638     var s = scope;
   6639     var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
   6640     var num_namespaces_out: u32 = 0;
   6641     var capturing_namespace: ?*Scope.Namespace = null;
   6642     while (true) switch (s.tag) {
   6643         .local_val => {
   6644             const local_val = s.cast(Scope.LocalVal).?;
   6645 
   6646             if (local_val.name == name_str_index) {
   6647                 // Locals cannot shadow anything, so we do not need to look for ambiguous
   6648                 // references in this case.
   6649                 local_val.used = true;
   6650 
   6651                 const value_inst = try tunnelThroughClosure(
   6652                     gz,
   6653                     ident,
   6654                     num_namespaces_out,
   6655                     capturing_namespace,
   6656                     local_val.inst,
   6657                     local_val.token_src,
   6658                     gpa,
   6659                 );
   6660 
   6661                 return rvalue(gz, rl, value_inst, ident);
   6662             }
   6663             s = local_val.parent;
   6664         },
   6665         .local_ptr => {
   6666             const local_ptr = s.cast(Scope.LocalPtr).?;
   6667             if (local_ptr.name == name_str_index) {
   6668                 local_ptr.used = true;
   6669 
   6670                 // Can't close over a runtime variable
   6671                 if (num_namespaces_out != 0 and !local_ptr.maybe_comptime) {
   6672                     const ident_name = try astgen.identifierTokenString(ident_token);
   6673                     return astgen.failNodeNotes(ident, "mutable '{s}' not accessible from here", .{ident_name}, &.{
   6674                         try astgen.errNoteTok(local_ptr.token_src, "declared mutable here", .{}),
   6675                         try astgen.errNoteNode(capturing_namespace.?.node, "crosses namespace boundary here", .{}),
   6676                     });
   6677                 }
   6678 
   6679                 const ptr_inst = try tunnelThroughClosure(
   6680                     gz,
   6681                     ident,
   6682                     num_namespaces_out,
   6683                     capturing_namespace,
   6684                     local_ptr.ptr,
   6685                     local_ptr.token_src,
   6686                     gpa,
   6687                 );
   6688 
   6689                 switch (rl) {
   6690                     .ref => return ptr_inst,
   6691                     else => {
   6692                         const loaded = try gz.addUnNode(.load, ptr_inst, ident);
   6693                         return rvalue(gz, rl, loaded, ident);
   6694                     },
   6695                 }
   6696             }
   6697             s = local_ptr.parent;
   6698         },
   6699         .gen_zir => s = s.cast(GenZir).?.parent,
   6700         .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
   6701         .namespace => {
   6702             const ns = s.cast(Scope.Namespace).?;
   6703             if (ns.decls.get(name_str_index)) |i| {
   6704                 if (found_already) |f| {
   6705                     return astgen.failNodeNotes(ident, "ambiguous reference", .{}, &.{
   6706                         try astgen.errNoteNode(f, "declared here", .{}),
   6707                         try astgen.errNoteNode(i, "also declared here", .{}),
   6708                     });
   6709                 }
   6710                 // We found a match but must continue looking for ambiguous references to decls.
   6711                 found_already = i;
   6712             }
   6713             num_namespaces_out += 1;
   6714             capturing_namespace = ns;
   6715             s = ns.parent;
   6716         },
   6717         .top => break,
   6718     };
   6719     if (found_already == null) {
   6720         const ident_name = try astgen.identifierTokenString(ident_token);
   6721         return astgen.failNode(ident, "use of undeclared identifier '{s}'", .{ident_name});
   6722     }
   6723 
   6724     // Decl references happen by name rather than ZIR index so that when unrelated
   6725     // decls are modified, ZIR code containing references to them can be unmodified.
   6726     switch (rl) {
   6727         .ref => return gz.addStrTok(.decl_ref, name_str_index, ident_token),
   6728         else => {
   6729             const result = try gz.addStrTok(.decl_val, name_str_index, ident_token);
   6730             return rvalue(gz, rl, result, ident);
   6731         },
   6732     }
   6733 }
   6734 
   6735 /// Adds a capture to a namespace, if needed.
   6736 /// Returns the index of the closure_capture instruction.
   6737 fn tunnelThroughClosure(
   6738     gz: *GenZir,
   6739     inner_ref_node: Ast.Node.Index,
   6740     num_tunnels: u32,
   6741     ns: ?*Scope.Namespace,
   6742     value: Zir.Inst.Ref,
   6743     token: Ast.TokenIndex,
   6744     gpa: Allocator,
   6745 ) !Zir.Inst.Ref {
   6746     // For trivial values, we don't need a tunnel.
   6747     // Just return the ref.
   6748     if (num_tunnels == 0 or refToIndex(value) == null) {
   6749         return value;
   6750     }
   6751 
   6752     // Otherwise we need a tunnel.  Check if this namespace
   6753     // already has one for this value.
   6754     const gop = try ns.?.captures.getOrPut(gpa, refToIndex(value).?);
   6755     if (!gop.found_existing) {
   6756         // Make a new capture for this value but don't add it to the declaring_gz yet
   6757         try gz.astgen.instructions.append(gz.astgen.gpa, .{
   6758             .tag = .closure_capture,
   6759             .data = .{ .un_tok = .{
   6760                 .operand = value,
   6761                 .src_tok = ns.?.declaring_gz.?.tokenIndexToRelative(token),
   6762             } },
   6763         });
   6764         gop.value_ptr.* = @intCast(Zir.Inst.Index, gz.astgen.instructions.len - 1);
   6765     }
   6766 
   6767     // Add an instruction to get the value from the closure into
   6768     // our current context
   6769     return try gz.addInstNode(.closure_get, gop.value_ptr.*, inner_ref_node);
   6770 }
   6771 
   6772 fn stringLiteral(
   6773     gz: *GenZir,
   6774     rl: ResultLoc,
   6775     node: Ast.Node.Index,
   6776 ) InnerError!Zir.Inst.Ref {
   6777     const astgen = gz.astgen;
   6778     const tree = astgen.tree;
   6779     const main_tokens = tree.nodes.items(.main_token);
   6780     const str_lit_token = main_tokens[node];
   6781     const str = try astgen.strLitAsString(str_lit_token);
   6782     const result = try gz.add(.{
   6783         .tag = .str,
   6784         .data = .{ .str = .{
   6785             .start = str.index,
   6786             .len = str.len,
   6787         } },
   6788     });
   6789     return rvalue(gz, rl, result, node);
   6790 }
   6791 
   6792 fn multilineStringLiteral(
   6793     gz: *GenZir,
   6794     rl: ResultLoc,
   6795     node: Ast.Node.Index,
   6796 ) InnerError!Zir.Inst.Ref {
   6797     const astgen = gz.astgen;
   6798     const str = try astgen.strLitNodeAsString(node);
   6799     const result = try gz.add(.{
   6800         .tag = .str,
   6801         .data = .{ .str = .{
   6802             .start = str.index,
   6803             .len = str.len,
   6804         } },
   6805     });
   6806     return rvalue(gz, rl, result, node);
   6807 }
   6808 
   6809 fn charLiteral(gz: *GenZir, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   6810     const astgen = gz.astgen;
   6811     const tree = astgen.tree;
   6812     const main_tokens = tree.nodes.items(.main_token);
   6813     const main_token = main_tokens[node];
   6814     const slice = tree.tokenSlice(main_token);
   6815 
   6816     switch (std.zig.parseCharLiteral(slice)) {
   6817         .success => |codepoint| {
   6818             const result = try gz.addInt(codepoint);
   6819             return rvalue(gz, rl, result, node);
   6820         },
   6821         .failure => |err| return astgen.failWithStrLitError(err, main_token, slice, 0),
   6822     }
   6823 }
   6824 
   6825 fn integerLiteral(gz: *GenZir, rl: ResultLoc, node: Ast.Node.Index) InnerError!Zir.Inst.Ref {
   6826     const astgen = gz.astgen;
   6827     const tree = astgen.tree;
   6828     const main_tokens = tree.nodes.items(.main_token);
   6829     const int_token = main_tokens[node];
   6830     const prefixed_bytes = tree.tokenSlice(int_token);
   6831     if (std.fmt.parseInt(u64, prefixed_bytes, 0)) |small_int| {
   6832         const result: Zir.Inst.Ref = switch (small_int) {
   6833             0 => .zero,
   6834             1 => .one,
   6835             else => try gz.addInt(small_int),
   6836         };
   6837         return rvalue(gz, rl, result, node);
   6838     } else |err| switch (err) {
   6839         error.InvalidCharacter => unreachable, // Caught by the parser.
   6840         error.Overflow => {},
   6841     }
   6842 
   6843     var base: u8 = 10;
   6844     var non_prefixed: []const u8 = prefixed_bytes;
   6845     if (mem.startsWith(u8, prefixed_bytes, "0x")) {
   6846         base = 16;
   6847         non_prefixed = prefixed_bytes[2..];
   6848     } else if (mem.startsWith(u8, prefixed_bytes, "0o")) {
   6849         base = 8;
   6850         non_prefixed = prefixed_bytes[2..];
   6851     } else if (mem.startsWith(u8, prefixed_bytes, "0b")) {
   6852         base = 2;
   6853         non_prefixed = prefixed_bytes[2..];
   6854     }
   6855 
   6856     const gpa = astgen.gpa;
   6857     var big_int = try std.math.big.int.Managed.init(gpa);
   6858     defer big_int.deinit();
   6859     big_int.setString(base, non_prefixed) catch |err| switch (err) {
   6860         error.InvalidCharacter => unreachable, // caught by parser
   6861         error.InvalidBase => unreachable, // we only pass 16, 8, 2, see above
   6862         error.OutOfMemory => return error.OutOfMemory,
   6863     };
   6864 
   6865     const limbs = big_int.limbs[0..big_int.len()];
   6866     assert(big_int.isPositive());
   6867     const result = try gz.addIntBig(limbs);
   6868     return rvalue(gz, rl, result, node);
   6869 }
   6870 
   6871 const Sign = enum { negative, positive };
   6872 
   6873 fn floatLiteral(gz: *GenZir, rl: ResultLoc, node: Ast.Node.Index, sign: Sign) InnerError!Zir.Inst.Ref {
   6874     const astgen = gz.astgen;
   6875     const tree = astgen.tree;
   6876     const main_tokens = tree.nodes.items(.main_token);
   6877 
   6878     const main_token = main_tokens[node];
   6879     const bytes = tree.tokenSlice(main_token);
   6880     const unsigned_float_number = std.fmt.parseFloat(f128, bytes) catch |err| switch (err) {
   6881         error.InvalidCharacter => unreachable, // validated by tokenizer
   6882     };
   6883     const float_number = switch (sign) {
   6884         .negative => -unsigned_float_number,
   6885         .positive => unsigned_float_number,
   6886     };
   6887     // If the value fits into a f64 without losing any precision, store it that way.
   6888     @setFloatMode(.Strict);
   6889     const smaller_float = @floatCast(f64, float_number);
   6890     const bigger_again: f128 = smaller_float;
   6891     if (bigger_again == float_number) {
   6892         const result = try gz.addFloat(smaller_float);
   6893         return rvalue(gz, rl, result, node);
   6894     }
   6895     // We need to use 128 bits. Break the float into 4 u32 values so we can
   6896     // put it into the `extra` array.
   6897     const int_bits = @bitCast(u128, float_number);
   6898     const result = try gz.addPlNode(.float128, node, Zir.Inst.Float128{
   6899         .piece0 = @truncate(u32, int_bits),
   6900         .piece1 = @truncate(u32, int_bits >> 32),
   6901         .piece2 = @truncate(u32, int_bits >> 64),
   6902         .piece3 = @truncate(u32, int_bits >> 96),
   6903     });
   6904     return rvalue(gz, rl, result, node);
   6905 }
   6906 
   6907 fn asmExpr(
   6908     gz: *GenZir,
   6909     scope: *Scope,
   6910     rl: ResultLoc,
   6911     node: Ast.Node.Index,
   6912     full: Ast.full.Asm,
   6913 ) InnerError!Zir.Inst.Ref {
   6914     const astgen = gz.astgen;
   6915     const tree = astgen.tree;
   6916     const main_tokens = tree.nodes.items(.main_token);
   6917     const node_datas = tree.nodes.items(.data);
   6918     const node_tags = tree.nodes.items(.tag);
   6919     const token_tags = tree.tokens.items(.tag);
   6920 
   6921     const asm_source = switch (node_tags[full.ast.template]) {
   6922         .string_literal => try astgen.strLitAsString(main_tokens[full.ast.template]),
   6923         .multiline_string_literal => try astgen.strLitNodeAsString(full.ast.template),
   6924         else => blk: {
   6925             // stage1 allows this, and until we do another design iteration on inline assembly
   6926             // in stage2 to improve support for the various needed use cases, we allow inline
   6927             // assembly templates to be an expression. Once stage2 addresses the real world needs
   6928             // of people using inline assembly (primarily OS developers) then we can re-institute
   6929             // the rule into AstGen that assembly code must use string literal syntax.
   6930             //return astgen.failNode(full.ast.template, "assembly code must use string literal syntax", .{}),
   6931             // We still need to trigger all the expr() calls here to avoid errors for unused things.
   6932             // So we pass 0 as the asm source and stage2 Sema will notice this and
   6933             // report the error.
   6934             _ = try comptimeExpr(gz, scope, .none, full.ast.template);
   6935             break :blk IndexSlice{ .index = 0, .len = 0 };
   6936         },
   6937     };
   6938 
   6939     // See https://github.com/ziglang/zig/issues/215 and related issues discussing
   6940     // possible inline assembly improvements. Until then here is status quo AstGen
   6941     // for assembly syntax. It's used by std lib crypto aesni.zig.
   6942     const is_container_asm = astgen.fn_block == null;
   6943     if (is_container_asm) {
   6944         if (full.volatile_token) |t|
   6945             return astgen.failTok(t, "volatile is meaningless on global assembly", .{});
   6946         if (full.outputs.len != 0 or full.inputs.len != 0 or full.first_clobber != null)
   6947             return astgen.failNode(node, "global assembly cannot have inputs, outputs, or clobbers", .{});
   6948     } else {
   6949         if (full.outputs.len == 0 and full.volatile_token == null) {
   6950             return astgen.failNode(node, "assembly expression with no output must be marked volatile", .{});
   6951         }
   6952     }
   6953     if (full.outputs.len > 32) {
   6954         return astgen.failNode(full.outputs[32], "too many asm outputs", .{});
   6955     }
   6956     var outputs_buffer: [32]Zir.Inst.Asm.Output = undefined;
   6957     const outputs = outputs_buffer[0..full.outputs.len];
   6958 
   6959     var output_type_bits: u32 = 0;
   6960 
   6961     for (full.outputs) |output_node, i| {
   6962         const symbolic_name = main_tokens[output_node];
   6963         const name = try astgen.identAsString(symbolic_name);
   6964         const constraint_token = symbolic_name + 2;
   6965         const constraint = (try astgen.strLitAsString(constraint_token)).index;
   6966         const has_arrow = token_tags[symbolic_name + 4] == .arrow;
   6967         if (has_arrow) {
   6968             if (output_type_bits != 0) {
   6969                 return astgen.failNode(output_node, "inline assembly allows up to one output value", .{});
   6970             }
   6971             output_type_bits |= @as(u32, 1) << @intCast(u5, i);
   6972             const out_type_node = node_datas[output_node].lhs;
   6973             const out_type_inst = try typeExpr(gz, scope, out_type_node);
   6974             outputs[i] = .{
   6975                 .name = name,
   6976                 .constraint = constraint,
   6977                 .operand = out_type_inst,
   6978             };
   6979         } else {
   6980             const ident_token = symbolic_name + 4;
   6981             // TODO have a look at #215 and related issues and decide how to
   6982             // handle outputs. Do we want this to be identifiers?
   6983             // Or maybe we want to force this to be expressions with a pointer type.
   6984             outputs[i] = .{
   6985                 .name = name,
   6986                 .constraint = constraint,
   6987                 .operand = try localVarRef(gz, scope, .ref, node, ident_token),
   6988             };
   6989         }
   6990     }
   6991 
   6992     if (full.inputs.len > 32) {
   6993         return astgen.failNode(full.inputs[32], "too many asm inputs", .{});
   6994     }
   6995     var inputs_buffer: [32]Zir.Inst.Asm.Input = undefined;
   6996     const inputs = inputs_buffer[0..full.inputs.len];
   6997 
   6998     for (full.inputs) |input_node, i| {
   6999         const symbolic_name = main_tokens[input_node];
   7000         const name = try astgen.identAsString(symbolic_name);
   7001         const constraint_token = symbolic_name + 2;
   7002         const constraint = (try astgen.strLitAsString(constraint_token)).index;
   7003         const operand = try expr(gz, scope, .none, node_datas[input_node].lhs);
   7004         inputs[i] = .{
   7005             .name = name,
   7006             .constraint = constraint,
   7007             .operand = operand,
   7008         };
   7009     }
   7010 
   7011     var clobbers_buffer: [32]u32 = undefined;
   7012     var clobber_i: usize = 0;
   7013     if (full.first_clobber) |first_clobber| clobbers: {
   7014         // asm ("foo" ::: "a", "b")
   7015         // asm ("foo" ::: "a", "b",)
   7016         var tok_i = first_clobber;
   7017         while (true) : (tok_i += 1) {
   7018             if (clobber_i >= clobbers_buffer.len) {
   7019                 return astgen.failTok(tok_i, "too many asm clobbers", .{});
   7020             }
   7021             clobbers_buffer[clobber_i] = (try astgen.strLitAsString(tok_i)).index;
   7022             clobber_i += 1;
   7023             tok_i += 1;
   7024             switch (token_tags[tok_i]) {
   7025                 .r_paren => break :clobbers,
   7026                 .comma => {
   7027                     if (token_tags[tok_i + 1] == .r_paren) {
   7028                         break :clobbers;
   7029                     } else {
   7030                         continue;
   7031                     }
   7032                 },
   7033                 else => unreachable,
   7034             }
   7035         }
   7036     }
   7037 
   7038     const result = try gz.addAsm(.{
   7039         .node = node,
   7040         .asm_source = asm_source.index,
   7041         .is_volatile = full.volatile_token != null,
   7042         .output_type_bits = output_type_bits,
   7043         .outputs = outputs,
   7044         .inputs = inputs,
   7045         .clobbers = clobbers_buffer[0..clobber_i],
   7046     });
   7047     return rvalue(gz, rl, result, node);
   7048 }
   7049 
   7050 fn as(
   7051     gz: *GenZir,
   7052     scope: *Scope,
   7053     rl: ResultLoc,
   7054     node: Ast.Node.Index,
   7055     lhs: Ast.Node.Index,
   7056     rhs: Ast.Node.Index,
   7057 ) InnerError!Zir.Inst.Ref {
   7058     const dest_type = try typeExpr(gz, scope, lhs);
   7059     switch (rl) {
   7060         .none, .discard, .ref, .ty, .coerced_ty => {
   7061             const result = try reachableExpr(gz, scope, .{ .ty = dest_type }, rhs, node);
   7062             return rvalue(gz, rl, result, node);
   7063         },
   7064         .ptr, .inferred_ptr => |result_ptr| {
   7065             return asRlPtr(gz, scope, rl, node, result_ptr, rhs, dest_type);
   7066         },
   7067         .block_ptr => |block_scope| {
   7068             return asRlPtr(gz, scope, rl, node, block_scope.rl_ptr, rhs, dest_type);
   7069         },
   7070     }
   7071 }
   7072 
   7073 fn unionInit(
   7074     gz: *GenZir,
   7075     scope: *Scope,
   7076     rl: ResultLoc,
   7077     node: Ast.Node.Index,
   7078     params: []const Ast.Node.Index,
   7079 ) InnerError!Zir.Inst.Ref {
   7080     const union_type = try typeExpr(gz, scope, params[0]);
   7081     const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);
   7082     const field_type = try gz.addPlNode(.field_type_ref, params[1], Zir.Inst.FieldTypeRef{
   7083         .container_type = union_type,
   7084         .field_name = field_name,
   7085     });
   7086     const init = try reachableExpr(gz, scope, .{ .ty = field_type }, params[2], node);
   7087     const result = try gz.addPlNode(.union_init, node, Zir.Inst.UnionInit{
   7088         .union_type = union_type,
   7089         .init = init,
   7090         .field_name = field_name,
   7091     });
   7092     return rvalue(gz, rl, result, node);
   7093 }
   7094 
   7095 fn asRlPtr(
   7096     parent_gz: *GenZir,
   7097     scope: *Scope,
   7098     rl: ResultLoc,
   7099     src_node: Ast.Node.Index,
   7100     result_ptr: Zir.Inst.Ref,
   7101     operand_node: Ast.Node.Index,
   7102     dest_type: Zir.Inst.Ref,
   7103 ) InnerError!Zir.Inst.Ref {
   7104     var as_scope = try parent_gz.makeCoercionScope(scope, dest_type, result_ptr, src_node);
   7105     defer as_scope.unstack();
   7106 
   7107     const result = try reachableExpr(&as_scope, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node, src_node);
   7108     return as_scope.finishCoercion(parent_gz, rl, operand_node, result, dest_type);
   7109 }
   7110 
   7111 fn bitCast(
   7112     gz: *GenZir,
   7113     scope: *Scope,
   7114     rl: ResultLoc,
   7115     node: Ast.Node.Index,
   7116     lhs: Ast.Node.Index,
   7117     rhs: Ast.Node.Index,
   7118 ) InnerError!Zir.Inst.Ref {
   7119     const dest_type = try reachableTypeExpr(gz, scope, lhs, node);
   7120     const operand = try reachableExpr(gz, scope, .none, rhs, node);
   7121     const result = try gz.addPlNode(.bitcast, node, Zir.Inst.Bin{
   7122         .lhs = dest_type,
   7123         .rhs = operand,
   7124     });
   7125     return rvalue(gz, rl, result, node);
   7126 }
   7127 
   7128 fn typeOf(
   7129     gz: *GenZir,
   7130     scope: *Scope,
   7131     rl: ResultLoc,
   7132     node: Ast.Node.Index,
   7133     args: []const Ast.Node.Index,
   7134 ) InnerError!Zir.Inst.Ref {
   7135     const astgen = gz.astgen;
   7136     if (args.len < 1) {
   7137         return astgen.failNode(node, "expected at least 1 argument, found 0", .{});
   7138     }
   7139     const gpa = astgen.gpa;
   7140     if (args.len == 1) {
   7141         const typeof_inst = try gz.makeBlockInst(.typeof_builtin, node);
   7142 
   7143         var typeof_scope = gz.makeSubBlock(scope);
   7144         typeof_scope.force_comptime = false;
   7145         defer typeof_scope.unstack();
   7146 
   7147         const ty_expr = try reachableExpr(&typeof_scope, &typeof_scope.base, .none, args[0], node);
   7148         if (!gz.refIsNoReturn(ty_expr)) {
   7149             _ = try typeof_scope.addBreak(.break_inline, typeof_inst, ty_expr);
   7150         }
   7151         try typeof_scope.setBlockBody(typeof_inst);
   7152 
   7153         // typeof_scope unstacked now, can add new instructions to gz
   7154         try gz.instructions.append(gpa, typeof_inst);
   7155         return rvalue(gz, rl, indexToRef(typeof_inst), node);
   7156     }
   7157     const payload_size: u32 = std.meta.fields(Zir.Inst.TypeOfPeer).len;
   7158     const payload_index = try reserveExtra(astgen, payload_size + args.len);
   7159     var args_index = payload_index + payload_size;
   7160 
   7161     const typeof_inst = try gz.addExtendedMultiOpPayloadIndex(.typeof_peer, payload_index, args.len);
   7162 
   7163     var typeof_scope = gz.makeSubBlock(scope);
   7164     typeof_scope.force_comptime = false;
   7165 
   7166     for (args) |arg, i| {
   7167         const param_ref = try reachableExpr(&typeof_scope, &typeof_scope.base, .none, arg, node);
   7168         astgen.extra.items[args_index + i] = @enumToInt(param_ref);
   7169     }
   7170     _ = try typeof_scope.addBreak(.break_inline, refToIndex(typeof_inst).?, .void_value);
   7171 
   7172     const body = typeof_scope.instructionsSlice();
   7173     const body_len = astgen.countBodyLenAfterFixups(body);
   7174     astgen.setExtra(payload_index, Zir.Inst.TypeOfPeer{
   7175         .body_len = @intCast(u32, body_len),
   7176         .body_index = @intCast(u32, astgen.extra.items.len),
   7177         .src_node = gz.nodeIndexToRelative(node),
   7178     });
   7179     try astgen.extra.ensureUnusedCapacity(gpa, body_len);
   7180     astgen.appendBodyWithFixups(body);
   7181     typeof_scope.unstack();
   7182 
   7183     return rvalue(gz, rl, typeof_inst, node);
   7184 }
   7185 
   7186 fn builtinCall(
   7187     gz: *GenZir,
   7188     scope: *Scope,
   7189     rl: ResultLoc,
   7190     node: Ast.Node.Index,
   7191     params: []const Ast.Node.Index,
   7192 ) InnerError!Zir.Inst.Ref {
   7193     const astgen = gz.astgen;
   7194     const tree = astgen.tree;
   7195     const main_tokens = tree.nodes.items(.main_token);
   7196 
   7197     const builtin_token = main_tokens[node];
   7198     const builtin_name = tree.tokenSlice(builtin_token);
   7199 
   7200     // We handle the different builtins manually because they have different semantics depending
   7201     // on the function. For example, `@as` and others participate in result location semantics,
   7202     // and `@cImport` creates a special scope that collects a .c source code text buffer.
   7203     // Also, some builtins have a variable number of parameters.
   7204 
   7205     const info = BuiltinFn.list.get(builtin_name) orelse {
   7206         return astgen.failNode(node, "invalid builtin function: '{s}'", .{
   7207             builtin_name,
   7208         });
   7209     };
   7210     if (info.param_count) |expected| {
   7211         if (expected != params.len) {
   7212             const s = if (expected == 1) "" else "s";
   7213             return astgen.failNode(node, "expected {d} argument{s}, found {d}", .{
   7214                 expected, s, params.len,
   7215             });
   7216         }
   7217     }
   7218 
   7219     switch (info.tag) {
   7220         .import => {
   7221             const node_tags = tree.nodes.items(.tag);
   7222             const operand_node = params[0];
   7223 
   7224             if (node_tags[operand_node] != .string_literal) {
   7225                 // Spec reference: https://github.com/ziglang/zig/issues/2206
   7226                 return astgen.failNode(operand_node, "@import operand must be a string literal", .{});
   7227             }
   7228             const str_lit_token = main_tokens[operand_node];
   7229             const str = try astgen.strLitAsString(str_lit_token);
   7230             const result = try gz.addStrTok(.import, str.index, str_lit_token);
   7231             const gop = try astgen.imports.getOrPut(astgen.gpa, str.index);
   7232             if (!gop.found_existing) {
   7233                 gop.value_ptr.* = str_lit_token;
   7234             }
   7235             return rvalue(gz, rl, result, node);
   7236         },
   7237         .compile_log => {
   7238             const payload_index = try addExtra(gz.astgen, Zir.Inst.NodeMultiOp{
   7239                 .src_node = gz.nodeIndexToRelative(node),
   7240             });
   7241             var extra_index = try reserveExtra(gz.astgen, params.len);
   7242             for (params) |param| {
   7243                 const param_ref = try expr(gz, scope, .none, param);
   7244                 astgen.extra.items[extra_index] = @enumToInt(param_ref);
   7245                 extra_index += 1;
   7246             }
   7247             const result = try gz.addExtendedMultiOpPayloadIndex(.compile_log, payload_index, params.len);
   7248             return rvalue(gz, rl, result, node);
   7249         },
   7250         .field => {
   7251             if (rl == .ref) {
   7252                 return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{
   7253                     .lhs = try expr(gz, scope, .ref, params[0]),
   7254                     .field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]),
   7255                 });
   7256             }
   7257             const result = try gz.addPlNode(.field_val_named, node, Zir.Inst.FieldNamed{
   7258                 .lhs = try expr(gz, scope, .none, params[0]),
   7259                 .field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]),
   7260             });
   7261             return rvalue(gz, rl, result, node);
   7262         },
   7263 
   7264         // zig fmt: off
   7265         .as         => return as(       gz, scope, rl, node, params[0], params[1]),
   7266         .bit_cast   => return bitCast(  gz, scope, rl, node, params[0], params[1]),
   7267         .TypeOf     => return typeOf(   gz, scope, rl, node, params),
   7268         .union_init => return unionInit(gz, scope, rl, node, params),
   7269         .c_import   => return cImport(  gz, scope,     node, params[0]),
   7270         // zig fmt: on
   7271 
   7272         .@"export" => {
   7273             const node_tags = tree.nodes.items(.tag);
   7274             const node_datas = tree.nodes.items(.data);
   7275             // This function causes a Decl to be exported. The first parameter is not an expression,
   7276             // but an identifier of the Decl to be exported.
   7277             var namespace: Zir.Inst.Ref = .none;
   7278             var decl_name: u32 = 0;
   7279             switch (node_tags[params[0]]) {
   7280                 .identifier => {
   7281                     const ident_token = main_tokens[params[0]];
   7282                     decl_name = try astgen.identAsString(ident_token);
   7283 
   7284                     var s = scope;
   7285                     var found_already: ?Ast.Node.Index = null; // we have found a decl with the same name already
   7286                     while (true) switch (s.tag) {
   7287                         .local_val => {
   7288                             const local_val = s.cast(Scope.LocalVal).?;
   7289                             if (local_val.name == decl_name) {
   7290                                 local_val.used = true;
   7291                                 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
   7292                                     .operand = local_val.inst,
   7293                                     .options = try comptimeExpr(gz, scope, .{ .coerced_ty = .export_options_type }, params[1]),
   7294                                 });
   7295                                 return rvalue(gz, rl, .void_value, node);
   7296                             }
   7297                             s = local_val.parent;
   7298                         },
   7299                         .local_ptr => {
   7300                             const local_ptr = s.cast(Scope.LocalPtr).?;
   7301                             if (local_ptr.name == decl_name) {
   7302                                 if (!local_ptr.maybe_comptime)
   7303                                     return astgen.failNode(params[0], "unable to export runtime-known value", .{});
   7304                                 local_ptr.used = true;
   7305                                 const loaded = try gz.addUnNode(.load, local_ptr.ptr, node);
   7306                                 _ = try gz.addPlNode(.export_value, node, Zir.Inst.ExportValue{
   7307                                     .operand = loaded,
   7308                                     .options = try comptimeExpr(gz, scope, .{ .coerced_ty = .export_options_type }, params[1]),
   7309                                 });
   7310                                 return rvalue(gz, rl, .void_value, node);
   7311                             }
   7312                             s = local_ptr.parent;
   7313                         },
   7314                         .gen_zir => s = s.cast(GenZir).?.parent,
   7315                         .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
   7316                         .namespace => {
   7317                             const ns = s.cast(Scope.Namespace).?;
   7318                             if (ns.decls.get(decl_name)) |i| {
   7319                                 if (found_already) |f| {
   7320                                     return astgen.failNodeNotes(node, "ambiguous reference", .{}, &.{
   7321                                         try astgen.errNoteNode(f, "declared here", .{}),
   7322                                         try astgen.errNoteNode(i, "also declared here", .{}),
   7323                                     });
   7324                                 }
   7325                                 // We found a match but must continue looking for ambiguous references to decls.
   7326                                 found_already = i;
   7327                             }
   7328                             s = ns.parent;
   7329                         },
   7330                         .top => break,
   7331                     };
   7332                 },
   7333                 .field_access => {
   7334                     const namespace_node = node_datas[params[0]].lhs;
   7335                     namespace = try typeExpr(gz, scope, namespace_node);
   7336                     const dot_token = main_tokens[params[0]];
   7337                     const field_ident = dot_token + 1;
   7338                     decl_name = try astgen.identAsString(field_ident);
   7339                 },
   7340                 else => return astgen.failNode(params[0], "symbol to export must identify a declaration", .{}),
   7341             }
   7342             const options = try comptimeExpr(gz, scope, .{ .ty = .export_options_type }, params[1]);
   7343             _ = try gz.addPlNode(.@"export", node, Zir.Inst.Export{
   7344                 .namespace = namespace,
   7345                 .decl_name = decl_name,
   7346                 .options = options,
   7347             });
   7348             return rvalue(gz, rl, .void_value, node);
   7349         },
   7350         .@"extern" => {
   7351             const type_inst = try typeExpr(gz, scope, params[0]);
   7352             const options = try comptimeExpr(gz, scope, .{ .ty = .extern_options_type }, params[1]);
   7353             const result = try gz.addExtendedPayload(.builtin_extern, Zir.Inst.BinNode{
   7354                 .node = gz.nodeIndexToRelative(node),
   7355                 .lhs = type_inst,
   7356                 .rhs = options,
   7357             });
   7358             return rvalue(gz, rl, result, node);
   7359         },
   7360         .fence => {
   7361             const order = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[0]);
   7362             const result = try gz.addExtendedPayload(.fence, Zir.Inst.UnNode{
   7363                 .node = gz.nodeIndexToRelative(node),
   7364                 .operand = order,
   7365             });
   7366             return rvalue(gz, rl, result, node);
   7367         },
   7368         .set_float_mode => {
   7369             const order = try expr(gz, scope, .{ .coerced_ty = .float_mode_type }, params[0]);
   7370             const result = try gz.addExtendedPayload(.set_float_mode, Zir.Inst.UnNode{
   7371                 .node = gz.nodeIndexToRelative(node),
   7372                 .operand = order,
   7373             });
   7374             return rvalue(gz, rl, result, node);
   7375         },
   7376         .set_align_stack => {
   7377             const order = try expr(gz, scope, align_rl, params[0]);
   7378             const result = try gz.addExtendedPayload(.set_align_stack, Zir.Inst.UnNode{
   7379                 .node = gz.nodeIndexToRelative(node),
   7380                 .operand = order,
   7381             });
   7382             return rvalue(gz, rl, result, node);
   7383         },
   7384 
   7385         .src => {
   7386             const token_starts = tree.tokens.items(.start);
   7387             const node_start = token_starts[tree.firstToken(node)];
   7388             astgen.advanceSourceCursor(node_start);
   7389             const result = try gz.addExtendedPayload(.builtin_src, Zir.Inst.LineColumn{
   7390                 .line = astgen.source_line,
   7391                 .column = astgen.source_column,
   7392             });
   7393             return rvalue(gz, rl, result, node);
   7394         },
   7395 
   7396         // zig fmt: off
   7397         .This               => return rvalue(gz, rl, try gz.addNodeExtended(.this,               node), node),
   7398         .return_address     => return rvalue(gz, rl, try gz.addNodeExtended(.ret_addr,           node), node),
   7399         .error_return_trace => return rvalue(gz, rl, try gz.addNodeExtended(.error_return_trace, node), node),
   7400         .frame              => return rvalue(gz, rl, try gz.addNodeExtended(.frame,              node), node),
   7401         .frame_address      => return rvalue(gz, rl, try gz.addNodeExtended(.frame_address,      node), node),
   7402         .breakpoint         => return rvalue(gz, rl, try gz.addNodeExtended(.breakpoint, node), node),
   7403 
   7404         .type_info   => return simpleUnOpType(gz, scope, rl, node, params[0], .type_info),
   7405         .size_of     => return simpleUnOpType(gz, scope, rl, node, params[0], .size_of),
   7406         .bit_size_of => return simpleUnOpType(gz, scope, rl, node, params[0], .bit_size_of),
   7407         .align_of    => return simpleUnOpType(gz, scope, rl, node, params[0], .align_of),
   7408 
   7409         .ptr_to_int            => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .ptr_to_int),
   7410         .compile_error         => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type },     params[0], .compile_error),
   7411         .set_eval_branch_quota => return simpleUnOp(gz, scope, rl, node, .{ .coerced_ty = .u32_type },        params[0], .set_eval_branch_quota),
   7412         .enum_to_int           => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .enum_to_int),
   7413         .bool_to_int           => return simpleUnOp(gz, scope, rl, node, bool_rl,                             params[0], .bool_to_int),
   7414         .embed_file            => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type },     params[0], .embed_file),
   7415         .error_name            => return simpleUnOp(gz, scope, rl, node, .{ .ty = .anyerror_type },           params[0], .error_name),
   7416         .panic                 => return simpleUnOp(gz, scope, rl, node, .{ .ty = .const_slice_u8_type },     params[0], if (gz.force_comptime) .panic_comptime else .panic),
   7417         .set_cold              => return simpleUnOp(gz, scope, rl, node, bool_rl,                             params[0], .set_cold),
   7418         .set_runtime_safety    => return simpleUnOp(gz, scope, rl, node, bool_rl,                             params[0], .set_runtime_safety),
   7419         .sqrt                  => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .sqrt),
   7420         .sin                   => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .sin),
   7421         .cos                   => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .cos),
   7422         .tan                   => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .tan),
   7423         .exp                   => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .exp),
   7424         .exp2                  => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .exp2),
   7425         .log                   => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .log),
   7426         .log2                  => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .log2),
   7427         .log10                 => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .log10),
   7428         .fabs                  => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .fabs),
   7429         .floor                 => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .floor),
   7430         .ceil                  => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .ceil),
   7431         .trunc                 => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .trunc),
   7432         .round                 => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .round),
   7433         .tag_name              => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .tag_name),
   7434         .Type                  => return simpleUnOp(gz, scope, rl, node, .{ .coerced_ty = .type_info_type },  params[0], .reify),
   7435         .type_name             => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .type_name),
   7436         .Frame                 => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .frame_type),
   7437         .frame_size            => return simpleUnOp(gz, scope, rl, node, .none,                               params[0], .frame_size),
   7438 
   7439         .float_to_int => return typeCast(gz, scope, rl, node, params[0], params[1], .float_to_int),
   7440         .int_to_float => return typeCast(gz, scope, rl, node, params[0], params[1], .int_to_float),
   7441         .int_to_ptr   => return typeCast(gz, scope, rl, node, params[0], params[1], .int_to_ptr),
   7442         .int_to_enum  => return typeCast(gz, scope, rl, node, params[0], params[1], .int_to_enum),
   7443         .float_cast   => return typeCast(gz, scope, rl, node, params[0], params[1], .float_cast),
   7444         .int_cast     => return typeCast(gz, scope, rl, node, params[0], params[1], .int_cast),
   7445         .ptr_cast     => return typeCast(gz, scope, rl, node, params[0], params[1], .ptr_cast),
   7446         .truncate     => return typeCast(gz, scope, rl, node, params[0], params[1], .truncate),
   7447         // zig fmt: on
   7448 
   7449         .error_to_int => {
   7450             const operand = try expr(gz, scope, .none, params[0]);
   7451             const result = try gz.addExtendedPayload(.error_to_int, Zir.Inst.UnNode{
   7452                 .node = gz.nodeIndexToRelative(node),
   7453                 .operand = operand,
   7454             });
   7455             return rvalue(gz, rl, result, node);
   7456         },
   7457         .int_to_error => {
   7458             const operand = try expr(gz, scope, .{ .coerced_ty = .u16_type }, params[0]);
   7459             const result = try gz.addExtendedPayload(.int_to_error, Zir.Inst.UnNode{
   7460                 .node = gz.nodeIndexToRelative(node),
   7461                 .operand = operand,
   7462             });
   7463             return rvalue(gz, rl, result, node);
   7464         },
   7465         .align_cast => {
   7466             const dest_align = try comptimeExpr(gz, scope, align_rl, params[0]);
   7467             const rhs = try expr(gz, scope, .none, params[1]);
   7468             const result = try gz.addPlNode(.align_cast, node, Zir.Inst.Bin{
   7469                 .lhs = dest_align,
   7470                 .rhs = rhs,
   7471             });
   7472             return rvalue(gz, rl, result, node);
   7473         },
   7474         .err_set_cast => {
   7475             const result = try gz.addExtendedPayload(.err_set_cast, Zir.Inst.BinNode{
   7476                 .lhs = try typeExpr(gz, scope, params[0]),
   7477                 .rhs = try expr(gz, scope, .none, params[1]),
   7478                 .node = gz.nodeIndexToRelative(node),
   7479             });
   7480             return rvalue(gz, rl, result, node);
   7481         },
   7482 
   7483         // zig fmt: off
   7484         .has_decl  => return hasDeclOrField(gz, scope, rl, node, params[0], params[1], .has_decl),
   7485         .has_field => return hasDeclOrField(gz, scope, rl, node, params[0], params[1], .has_field),
   7486 
   7487         .clz         => return bitBuiltin(gz, scope, rl, node, params[0], params[1], .clz),
   7488         .ctz         => return bitBuiltin(gz, scope, rl, node, params[0], params[1], .ctz),
   7489         .pop_count   => return bitBuiltin(gz, scope, rl, node, params[0], params[1], .pop_count),
   7490         .byte_swap   => return bitBuiltin(gz, scope, rl, node, params[0], params[1], .byte_swap),
   7491         .bit_reverse => return bitBuiltin(gz, scope, rl, node, params[0], params[1], .bit_reverse),
   7492 
   7493         .div_exact => return divBuiltin(gz, scope, rl, node, params[0], params[1], .div_exact),
   7494         .div_floor => return divBuiltin(gz, scope, rl, node, params[0], params[1], .div_floor),
   7495         .div_trunc => return divBuiltin(gz, scope, rl, node, params[0], params[1], .div_trunc),
   7496         .mod       => return divBuiltin(gz, scope, rl, node, params[0], params[1], .mod),
   7497         .rem       => return divBuiltin(gz, scope, rl, node, params[0], params[1], .rem),
   7498 
   7499         .shl_exact => return shiftOp(gz, scope, rl, node, params[0], params[1], .shl_exact),
   7500         .shr_exact => return shiftOp(gz, scope, rl, node, params[0], params[1], .shr_exact),
   7501 
   7502         .bit_offset_of  => return offsetOf(gz, scope, rl, node, params[0], params[1], .bit_offset_of),
   7503         .offset_of => return offsetOf(gz, scope, rl, node, params[0], params[1], .offset_of),
   7504 
   7505         .c_undef   => return simpleCBuiltin(gz, scope, rl, node, params[0], .c_undef),
   7506         .c_include => return simpleCBuiltin(gz, scope, rl, node, params[0], .c_include),
   7507 
   7508         .cmpxchg_strong => return cmpxchg(gz, scope, rl, node, params, .cmpxchg_strong),
   7509         .cmpxchg_weak   => return cmpxchg(gz, scope, rl, node, params, .cmpxchg_weak),
   7510         // zig fmt: on
   7511 
   7512         .wasm_memory_size => {
   7513             const operand = try comptimeExpr(gz, scope, .{ .coerced_ty = .u32_type }, params[0]);
   7514             const result = try gz.addExtendedPayload(.wasm_memory_size, Zir.Inst.UnNode{
   7515                 .node = gz.nodeIndexToRelative(node),
   7516                 .operand = operand,
   7517             });
   7518             return rvalue(gz, rl, result, node);
   7519         },
   7520         .wasm_memory_grow => {
   7521             const index_arg = try comptimeExpr(gz, scope, .{ .coerced_ty = .u32_type }, params[0]);
   7522             const delta_arg = try expr(gz, scope, .{ .coerced_ty = .u32_type }, params[1]);
   7523             const result = try gz.addExtendedPayload(.wasm_memory_grow, Zir.Inst.BinNode{
   7524                 .node = gz.nodeIndexToRelative(node),
   7525                 .lhs = index_arg,
   7526                 .rhs = delta_arg,
   7527             });
   7528             return rvalue(gz, rl, result, node);
   7529         },
   7530         .c_define => {
   7531             if (!gz.c_import) return gz.astgen.failNode(node, "C define valid only inside C import block", .{});
   7532             const name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[0]);
   7533             const value = try comptimeExpr(gz, scope, .none, params[1]);
   7534             const result = try gz.addExtendedPayload(.c_define, Zir.Inst.BinNode{
   7535                 .node = gz.nodeIndexToRelative(node),
   7536                 .lhs = name,
   7537                 .rhs = value,
   7538             });
   7539             return rvalue(gz, rl, result, node);
   7540         },
   7541 
   7542         .splat => {
   7543             const len = try expr(gz, scope, .{ .coerced_ty = .u32_type }, params[0]);
   7544             const scalar = try expr(gz, scope, .none, params[1]);
   7545             const result = try gz.addPlNode(.splat, node, Zir.Inst.Bin{
   7546                 .lhs = len,
   7547                 .rhs = scalar,
   7548             });
   7549             return rvalue(gz, rl, result, node);
   7550         },
   7551         .reduce => {
   7552             const op = try expr(gz, scope, .{ .ty = .reduce_op_type }, params[0]);
   7553             const scalar = try expr(gz, scope, .none, params[1]);
   7554             const result = try gz.addPlNode(.reduce, node, Zir.Inst.Bin{
   7555                 .lhs = op,
   7556                 .rhs = scalar,
   7557             });
   7558             return rvalue(gz, rl, result, node);
   7559         },
   7560 
   7561         .maximum => {
   7562             const a = try expr(gz, scope, .none, params[0]);
   7563             const b = try expr(gz, scope, .none, params[1]);
   7564             const result = try gz.addPlNode(.maximum, node, Zir.Inst.Bin{
   7565                 .lhs = a,
   7566                 .rhs = b,
   7567             });
   7568             return rvalue(gz, rl, result, node);
   7569         },
   7570         .minimum => {
   7571             const a = try expr(gz, scope, .none, params[0]);
   7572             const b = try expr(gz, scope, .none, params[1]);
   7573             const result = try gz.addPlNode(.minimum, node, Zir.Inst.Bin{
   7574                 .lhs = a,
   7575                 .rhs = b,
   7576             });
   7577             return rvalue(gz, rl, result, node);
   7578         },
   7579 
   7580         .add_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .add_with_overflow),
   7581         .sub_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .sub_with_overflow),
   7582         .mul_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .mul_with_overflow),
   7583         .shl_with_overflow => {
   7584             const int_type = try typeExpr(gz, scope, params[0]);
   7585             const log2_int_type = try gz.addUnNode(.log2_int_type, int_type, params[0]);
   7586             const ptr_type = try gz.addUnNode(.overflow_arithmetic_ptr, int_type, params[0]);
   7587             const lhs = try expr(gz, scope, .{ .ty = int_type }, params[1]);
   7588             const rhs = try expr(gz, scope, .{ .ty = log2_int_type }, params[2]);
   7589             const ptr = try expr(gz, scope, .{ .ty = ptr_type }, params[3]);
   7590             const result = try gz.addExtendedPayload(.shl_with_overflow, Zir.Inst.OverflowArithmetic{
   7591                 .node = gz.nodeIndexToRelative(node),
   7592                 .lhs = lhs,
   7593                 .rhs = rhs,
   7594                 .ptr = ptr,
   7595             });
   7596             return rvalue(gz, rl, result, node);
   7597         },
   7598 
   7599         .atomic_load => {
   7600             const result = try gz.addPlNode(.atomic_load, node, Zir.Inst.AtomicLoad{
   7601                 // zig fmt: off
   7602                 .elem_type = try typeExpr(gz, scope,                                        params[0]),
   7603                 .ptr       = try expr    (gz, scope, .none,                                 params[1]),
   7604                 .ordering  = try expr    (gz, scope, .{ .coerced_ty = .atomic_order_type }, params[2]),
   7605                 // zig fmt: on
   7606             });
   7607             return rvalue(gz, rl, result, node);
   7608         },
   7609         .atomic_rmw => {
   7610             const int_type = try typeExpr(gz, scope, params[0]);
   7611             const result = try gz.addPlNode(.atomic_rmw, node, Zir.Inst.AtomicRmw{
   7612                 // zig fmt: off
   7613                 .ptr       = try expr(gz, scope, .none,                                  params[1]),
   7614                 .operation = try expr(gz, scope, .{ .coerced_ty = .atomic_rmw_op_type }, params[2]),
   7615                 .operand   = try expr(gz, scope, .{ .ty = int_type },                    params[3]),
   7616                 .ordering  = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type },  params[4]),
   7617                 // zig fmt: on
   7618             });
   7619             return rvalue(gz, rl, result, node);
   7620         },
   7621         .atomic_store => {
   7622             const int_type = try typeExpr(gz, scope, params[0]);
   7623             const result = try gz.addPlNode(.atomic_store, node, Zir.Inst.AtomicStore{
   7624                 // zig fmt: off
   7625                 .ptr      = try expr(gz, scope, .none,                                 params[1]),
   7626                 .operand  = try expr(gz, scope, .{ .ty = int_type },                   params[2]),
   7627                 .ordering = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[3]),
   7628                 // zig fmt: on
   7629             });
   7630             return rvalue(gz, rl, result, node);
   7631         },
   7632         .mul_add => {
   7633             const float_type = try typeExpr(gz, scope, params[0]);
   7634             const mulend1 = try expr(gz, scope, .{ .coerced_ty = float_type }, params[1]);
   7635             const mulend2 = try expr(gz, scope, .{ .coerced_ty = float_type }, params[2]);
   7636             const addend = try expr(gz, scope, .{ .ty = float_type }, params[3]);
   7637             const result = try gz.addPlNode(.mul_add, node, Zir.Inst.MulAdd{
   7638                 .mulend1 = mulend1,
   7639                 .mulend2 = mulend2,
   7640                 .addend = addend,
   7641             });
   7642             return rvalue(gz, rl, result, node);
   7643         },
   7644         .call => {
   7645             const options = try comptimeExpr(gz, scope, .{ .ty = .call_options_type }, params[0]);
   7646             const callee = try calleeExpr(gz, scope, params[1]);
   7647             const args = try expr(gz, scope, .none, params[2]);
   7648             const result = try gz.addPlNode(.builtin_call, node, Zir.Inst.BuiltinCall{
   7649                 .options = options,
   7650                 .callee = callee,
   7651                 .args = args,
   7652                 .flags = .{
   7653                     .is_nosuspend = gz.nosuspend_node != 0,
   7654                     .is_comptime = gz.force_comptime,
   7655                     .ensure_result_used = false,
   7656                 },
   7657             });
   7658             return rvalue(gz, rl, result, node);
   7659         },
   7660         .field_parent_ptr => {
   7661             const parent_type = try typeExpr(gz, scope, params[0]);
   7662             const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);
   7663             const result = try gz.addPlNode(.field_parent_ptr, node, Zir.Inst.FieldParentPtr{
   7664                 .parent_type = parent_type,
   7665                 .field_name = field_name,
   7666                 .field_ptr = try expr(gz, scope, .none, params[2]),
   7667             });
   7668             return rvalue(gz, rl, result, node);
   7669         },
   7670         .memcpy => {
   7671             const result = try gz.addPlNode(.memcpy, node, Zir.Inst.Memcpy{
   7672                 .dest = try expr(gz, scope, .{ .coerced_ty = .manyptr_u8_type }, params[0]),
   7673                 .source = try expr(gz, scope, .{ .coerced_ty = .manyptr_const_u8_type }, params[1]),
   7674                 .byte_count = try expr(gz, scope, .{ .coerced_ty = .usize_type }, params[2]),
   7675             });
   7676             return rvalue(gz, rl, result, node);
   7677         },
   7678         .memset => {
   7679             const result = try gz.addPlNode(.memset, node, Zir.Inst.Memset{
   7680                 .dest = try expr(gz, scope, .{ .coerced_ty = .manyptr_u8_type }, params[0]),
   7681                 .byte = try expr(gz, scope, .{ .coerced_ty = .u8_type }, params[1]),
   7682                 .byte_count = try expr(gz, scope, .{ .coerced_ty = .usize_type }, params[2]),
   7683             });
   7684             return rvalue(gz, rl, result, node);
   7685         },
   7686         .shuffle => {
   7687             const result = try gz.addPlNode(.shuffle, node, Zir.Inst.Shuffle{
   7688                 .elem_type = try typeExpr(gz, scope, params[0]),
   7689                 .a = try expr(gz, scope, .none, params[1]),
   7690                 .b = try expr(gz, scope, .none, params[2]),
   7691                 .mask = try comptimeExpr(gz, scope, .none, params[3]),
   7692             });
   7693             return rvalue(gz, rl, result, node);
   7694         },
   7695         .select => {
   7696             const result = try gz.addExtendedPayload(.select, Zir.Inst.Select{
   7697                 .node = gz.nodeIndexToRelative(node),
   7698                 .elem_type = try typeExpr(gz, scope, params[0]),
   7699                 .pred = try expr(gz, scope, .none, params[1]),
   7700                 .a = try expr(gz, scope, .none, params[2]),
   7701                 .b = try expr(gz, scope, .none, params[3]),
   7702             });
   7703             return rvalue(gz, rl, result, node);
   7704         },
   7705         .async_call => {
   7706             const result = try gz.addPlNode(.builtin_async_call, node, Zir.Inst.AsyncCall{
   7707                 .frame_buffer = try expr(gz, scope, .none, params[0]),
   7708                 .result_ptr = try expr(gz, scope, .none, params[1]),
   7709                 .fn_ptr = try expr(gz, scope, .none, params[2]),
   7710                 .args = try expr(gz, scope, .none, params[3]),
   7711             });
   7712             return rvalue(gz, rl, result, node);
   7713         },
   7714         .Vector => {
   7715             const result = try gz.addPlNode(.vector_type, node, Zir.Inst.Bin{
   7716                 .lhs = try comptimeExpr(gz, scope, .{ .coerced_ty = .u32_type }, params[0]),
   7717                 .rhs = try typeExpr(gz, scope, params[1]),
   7718             });
   7719             return rvalue(gz, rl, result, node);
   7720         },
   7721         .prefetch => {
   7722             const ptr = try expr(gz, scope, .none, params[0]);
   7723             const options = try comptimeExpr(gz, scope, .{ .ty = .prefetch_options_type }, params[1]);
   7724             const result = try gz.addExtendedPayload(.prefetch, Zir.Inst.BinNode{
   7725                 .node = gz.nodeIndexToRelative(node),
   7726                 .lhs = ptr,
   7727                 .rhs = options,
   7728             });
   7729             return rvalue(gz, rl, result, node);
   7730         },
   7731     }
   7732 }
   7733 
   7734 fn simpleNoOpVoid(
   7735     gz: *GenZir,
   7736     rl: ResultLoc,
   7737     node: Ast.Node.Index,
   7738     tag: Zir.Inst.Tag,
   7739 ) InnerError!Zir.Inst.Ref {
   7740     _ = try gz.addNode(tag, node);
   7741     return rvalue(gz, rl, .void_value, node);
   7742 }
   7743 
   7744 fn hasDeclOrField(
   7745     gz: *GenZir,
   7746     scope: *Scope,
   7747     rl: ResultLoc,
   7748     node: Ast.Node.Index,
   7749     lhs_node: Ast.Node.Index,
   7750     rhs_node: Ast.Node.Index,
   7751     tag: Zir.Inst.Tag,
   7752 ) InnerError!Zir.Inst.Ref {
   7753     const container_type = try typeExpr(gz, scope, lhs_node);
   7754     const name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, rhs_node);
   7755     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   7756         .lhs = container_type,
   7757         .rhs = name,
   7758     });
   7759     return rvalue(gz, rl, result, node);
   7760 }
   7761 
   7762 fn typeCast(
   7763     gz: *GenZir,
   7764     scope: *Scope,
   7765     rl: ResultLoc,
   7766     node: Ast.Node.Index,
   7767     lhs_node: Ast.Node.Index,
   7768     rhs_node: Ast.Node.Index,
   7769     tag: Zir.Inst.Tag,
   7770 ) InnerError!Zir.Inst.Ref {
   7771     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   7772         .lhs = try typeExpr(gz, scope, lhs_node),
   7773         .rhs = try expr(gz, scope, .none, rhs_node),
   7774     });
   7775     return rvalue(gz, rl, result, node);
   7776 }
   7777 
   7778 fn simpleUnOpType(
   7779     gz: *GenZir,
   7780     scope: *Scope,
   7781     rl: ResultLoc,
   7782     node: Ast.Node.Index,
   7783     operand_node: Ast.Node.Index,
   7784     tag: Zir.Inst.Tag,
   7785 ) InnerError!Zir.Inst.Ref {
   7786     const operand = try typeExpr(gz, scope, operand_node);
   7787     const result = try gz.addUnNode(tag, operand, node);
   7788     return rvalue(gz, rl, result, node);
   7789 }
   7790 
   7791 fn simpleUnOp(
   7792     gz: *GenZir,
   7793     scope: *Scope,
   7794     rl: ResultLoc,
   7795     node: Ast.Node.Index,
   7796     operand_rl: ResultLoc,
   7797     operand_node: Ast.Node.Index,
   7798     tag: Zir.Inst.Tag,
   7799 ) InnerError!Zir.Inst.Ref {
   7800     const operand = try expr(gz, scope, operand_rl, operand_node);
   7801     const result = try gz.addUnNode(tag, operand, node);
   7802     return rvalue(gz, rl, result, node);
   7803 }
   7804 
   7805 fn negation(
   7806     gz: *GenZir,
   7807     scope: *Scope,
   7808     rl: ResultLoc,
   7809     node: Ast.Node.Index,
   7810 ) InnerError!Zir.Inst.Ref {
   7811     const astgen = gz.astgen;
   7812     const tree = astgen.tree;
   7813     const node_tags = tree.nodes.items(.tag);
   7814     const node_datas = tree.nodes.items(.data);
   7815 
   7816     // Check for float literal as the sub-expression because we want to preserve
   7817     // its negativity rather than having it go through comptime subtraction.
   7818     const operand_node = node_datas[node].lhs;
   7819     if (node_tags[operand_node] == .float_literal) {
   7820         return floatLiteral(gz, rl, operand_node, .negative);
   7821     }
   7822 
   7823     const operand = try expr(gz, scope, .none, operand_node);
   7824     const result = try gz.addUnNode(.negate, operand, node);
   7825     return rvalue(gz, rl, result, node);
   7826 }
   7827 
   7828 fn cmpxchg(
   7829     gz: *GenZir,
   7830     scope: *Scope,
   7831     rl: ResultLoc,
   7832     node: Ast.Node.Index,
   7833     params: []const Ast.Node.Index,
   7834     tag: Zir.Inst.Tag,
   7835 ) InnerError!Zir.Inst.Ref {
   7836     const int_type = try typeExpr(gz, scope, params[0]);
   7837     const result = try gz.addPlNode(tag, node, Zir.Inst.Cmpxchg{
   7838         // zig fmt: off
   7839         .ptr            = try expr(gz, scope, .none,                                 params[1]),
   7840         .expected_value = try expr(gz, scope, .{ .ty = int_type },                   params[2]),
   7841         .new_value      = try expr(gz, scope, .{ .coerced_ty = int_type },           params[3]),
   7842         .success_order  = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[4]),
   7843         .failure_order  = try expr(gz, scope, .{ .coerced_ty = .atomic_order_type }, params[5]),
   7844         // zig fmt: on
   7845     });
   7846     return rvalue(gz, rl, result, node);
   7847 }
   7848 
   7849 fn bitBuiltin(
   7850     gz: *GenZir,
   7851     scope: *Scope,
   7852     rl: ResultLoc,
   7853     node: Ast.Node.Index,
   7854     int_type_node: Ast.Node.Index,
   7855     operand_node: Ast.Node.Index,
   7856     tag: Zir.Inst.Tag,
   7857 ) InnerError!Zir.Inst.Ref {
   7858     // The accepted proposal https://github.com/ziglang/zig/issues/6835
   7859     // tells us to remove the type parameter from these builtins. To stay
   7860     // source-compatible with stage1, we still observe the parameter here,
   7861     // but we do not encode it into the ZIR. To implement this proposal in
   7862     // stage2, only AstGen code will need to be changed.
   7863     _ = try typeExpr(gz, scope, int_type_node);
   7864 
   7865     const operand = try expr(gz, scope, .none, operand_node);
   7866     const result = try gz.addUnNode(tag, operand, node);
   7867     return rvalue(gz, rl, result, node);
   7868 }
   7869 
   7870 fn divBuiltin(
   7871     gz: *GenZir,
   7872     scope: *Scope,
   7873     rl: ResultLoc,
   7874     node: Ast.Node.Index,
   7875     lhs_node: Ast.Node.Index,
   7876     rhs_node: Ast.Node.Index,
   7877     tag: Zir.Inst.Tag,
   7878 ) InnerError!Zir.Inst.Ref {
   7879     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   7880         .lhs = try expr(gz, scope, .none, lhs_node),
   7881         .rhs = try expr(gz, scope, .none, rhs_node),
   7882     });
   7883     return rvalue(gz, rl, result, node);
   7884 }
   7885 
   7886 fn simpleCBuiltin(
   7887     gz: *GenZir,
   7888     scope: *Scope,
   7889     rl: ResultLoc,
   7890     node: Ast.Node.Index,
   7891     operand_node: Ast.Node.Index,
   7892     tag: Zir.Inst.Extended,
   7893 ) InnerError!Zir.Inst.Ref {
   7894     const name: []const u8 = if (tag == .c_undef) "C undef" else "C include";
   7895     if (!gz.c_import) return gz.astgen.failNode(node, "{s} valid only inside C import block", .{name});
   7896     const operand = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, operand_node);
   7897     _ = try gz.addExtendedPayload(tag, Zir.Inst.UnNode{
   7898         .node = gz.nodeIndexToRelative(node),
   7899         .operand = operand,
   7900     });
   7901     return rvalue(gz, rl, .void_value, node);
   7902 }
   7903 
   7904 fn offsetOf(
   7905     gz: *GenZir,
   7906     scope: *Scope,
   7907     rl: ResultLoc,
   7908     node: Ast.Node.Index,
   7909     lhs_node: Ast.Node.Index,
   7910     rhs_node: Ast.Node.Index,
   7911     tag: Zir.Inst.Tag,
   7912 ) InnerError!Zir.Inst.Ref {
   7913     const type_inst = try typeExpr(gz, scope, lhs_node);
   7914     const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, rhs_node);
   7915     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   7916         .lhs = type_inst,
   7917         .rhs = field_name,
   7918     });
   7919     return rvalue(gz, rl, result, node);
   7920 }
   7921 
   7922 fn shiftOp(
   7923     gz: *GenZir,
   7924     scope: *Scope,
   7925     rl: ResultLoc,
   7926     node: Ast.Node.Index,
   7927     lhs_node: Ast.Node.Index,
   7928     rhs_node: Ast.Node.Index,
   7929     tag: Zir.Inst.Tag,
   7930 ) InnerError!Zir.Inst.Ref {
   7931     const lhs = try expr(gz, scope, .none, lhs_node);
   7932     const log2_int_type = try gz.addUnNode(.typeof_log2_int_type, lhs, lhs_node);
   7933     const rhs = try expr(gz, scope, .{ .ty = log2_int_type }, rhs_node);
   7934     const result = try gz.addPlNode(tag, node, Zir.Inst.Bin{
   7935         .lhs = lhs,
   7936         .rhs = rhs,
   7937     });
   7938     return rvalue(gz, rl, result, node);
   7939 }
   7940 
   7941 fn cImport(
   7942     gz: *GenZir,
   7943     scope: *Scope,
   7944     node: Ast.Node.Index,
   7945     body_node: Ast.Node.Index,
   7946 ) InnerError!Zir.Inst.Ref {
   7947     const astgen = gz.astgen;
   7948     const gpa = astgen.gpa;
   7949 
   7950     var block_scope = gz.makeSubBlock(scope);
   7951     block_scope.force_comptime = true;
   7952     block_scope.c_import = true;
   7953     defer block_scope.unstack();
   7954 
   7955     const block_inst = try gz.makeBlockInst(.c_import, node);
   7956     const block_result = try expr(&block_scope, &block_scope.base, .none, body_node);
   7957     _ = try gz.addUnNode(.ensure_result_used, block_result, node);
   7958     if (!gz.refIsNoReturn(block_result)) {
   7959         _ = try block_scope.addBreak(.break_inline, block_inst, .void_value);
   7960     }
   7961     try block_scope.setBlockBody(block_inst);
   7962     // block_scope unstacked now, can add new instructions to gz
   7963     try gz.instructions.append(gpa, block_inst);
   7964 
   7965     return indexToRef(block_inst);
   7966 }
   7967 
   7968 fn overflowArithmetic(
   7969     gz: *GenZir,
   7970     scope: *Scope,
   7971     rl: ResultLoc,
   7972     node: Ast.Node.Index,
   7973     params: []const Ast.Node.Index,
   7974     tag: Zir.Inst.Extended,
   7975 ) InnerError!Zir.Inst.Ref {
   7976     const int_type = try typeExpr(gz, scope, params[0]);
   7977     const ptr_type = try gz.addUnNode(.overflow_arithmetic_ptr, int_type, params[0]);
   7978     const lhs = try expr(gz, scope, .{ .ty = int_type }, params[1]);
   7979     const rhs = try expr(gz, scope, .{ .ty = int_type }, params[2]);
   7980     const ptr = try expr(gz, scope, .{ .ty = ptr_type }, params[3]);
   7981     const result = try gz.addExtendedPayload(tag, Zir.Inst.OverflowArithmetic{
   7982         .node = gz.nodeIndexToRelative(node),
   7983         .lhs = lhs,
   7984         .rhs = rhs,
   7985         .ptr = ptr,
   7986     });
   7987     return rvalue(gz, rl, result, node);
   7988 }
   7989 
   7990 fn callExpr(
   7991     gz: *GenZir,
   7992     scope: *Scope,
   7993     rl: ResultLoc,
   7994     node: Ast.Node.Index,
   7995     call: Ast.full.Call,
   7996 ) InnerError!Zir.Inst.Ref {
   7997     const astgen = gz.astgen;
   7998 
   7999     const callee = try calleeExpr(gz, scope, call.ast.fn_expr);
   8000     const modifier: std.builtin.CallOptions.Modifier = blk: {
   8001         if (gz.force_comptime) {
   8002             break :blk .compile_time;
   8003         }
   8004         if (call.async_token != null) {
   8005             break :blk .async_kw;
   8006         }
   8007         if (gz.nosuspend_node != 0) {
   8008             break :blk .no_async;
   8009         }
   8010         break :blk .auto;
   8011     };
   8012 
   8013     {
   8014         astgen.advanceSourceCursor(astgen.tree.tokens.items(.start)[call.ast.lparen]);
   8015         const line = astgen.source_line - gz.decl_line;
   8016         const column = astgen.source_column;
   8017 
   8018         _ = try gz.add(.{ .tag = .dbg_stmt, .data = .{
   8019             .dbg_stmt = .{
   8020                 .line = line,
   8021                 .column = column,
   8022             },
   8023         } });
   8024     }
   8025 
   8026     assert(callee != .none);
   8027     assert(node != 0);
   8028 
   8029     const payload_index = try addExtra(astgen, Zir.Inst.Call{
   8030         .callee = callee,
   8031         .flags = .{
   8032             .packed_modifier = @intCast(Zir.Inst.Call.Flags.PackedModifier, @enumToInt(modifier)),
   8033             .args_len = @intCast(Zir.Inst.Call.Flags.PackedArgsLen, call.ast.params.len),
   8034         },
   8035     });
   8036     var extra_index = try reserveExtra(astgen, call.ast.params.len);
   8037 
   8038     for (call.ast.params) |param_node, i| {
   8039         const param_type = try gz.add(.{
   8040             .tag = .param_type,
   8041             .data = .{ .param_type = .{
   8042                 .callee = callee,
   8043                 .param_index = @intCast(u32, i),
   8044             } },
   8045         });
   8046         const arg_ref = try expr(gz, scope, .{ .coerced_ty = param_type }, param_node);
   8047         astgen.extra.items[extra_index] = @enumToInt(arg_ref);
   8048         extra_index += 1;
   8049     }
   8050 
   8051     const call_inst = try gz.addPlNodePayloadIndex(.call, node, payload_index);
   8052     return rvalue(gz, rl, call_inst, node); // TODO function call with result location
   8053 }
   8054 
   8055 /// calleeExpr generates the function part of a call expression (f in f(x)), or the
   8056 /// callee argument to the @call() builtin. If the lhs is a field access or the
   8057 /// @field() builtin, we need to generate a special field_call_bind instruction
   8058 /// instead of the normal field_val or field_ptr.  If this is a inst.func() call,
   8059 /// this instruction will capture the value of the first argument before evaluating
   8060 /// the other arguments. We need to use .ref here to guarantee we will be able to
   8061 /// promote an lvalue to an address if the first parameter requires it.  This
   8062 /// unfortunately also means we need to take a reference to any types on the lhs.
   8063 fn calleeExpr(
   8064     gz: *GenZir,
   8065     scope: *Scope,
   8066     node: Ast.Node.Index,
   8067 ) InnerError!Zir.Inst.Ref {
   8068     const astgen = gz.astgen;
   8069     const tree = astgen.tree;
   8070 
   8071     const tag = tree.nodes.items(.tag)[node];
   8072     switch (tag) {
   8073         .field_access => return addFieldAccess(.field_call_bind, gz, scope, .ref, node),
   8074 
   8075         .builtin_call_two,
   8076         .builtin_call_two_comma,
   8077         .builtin_call,
   8078         .builtin_call_comma,
   8079         => {
   8080             const node_datas = tree.nodes.items(.data);
   8081             const main_tokens = tree.nodes.items(.main_token);
   8082             const builtin_token = main_tokens[node];
   8083             const builtin_name = tree.tokenSlice(builtin_token);
   8084 
   8085             var inline_params: [2]Ast.Node.Index = undefined;
   8086             var params: []Ast.Node.Index = switch (tag) {
   8087                 .builtin_call,
   8088                 .builtin_call_comma,
   8089                 => tree.extra_data[node_datas[node].lhs..node_datas[node].rhs],
   8090 
   8091                 .builtin_call_two,
   8092                 .builtin_call_two_comma,
   8093                 => blk: {
   8094                     inline_params = .{ node_datas[node].lhs, node_datas[node].rhs };
   8095                     const len: usize = if (inline_params[0] == 0) @as(usize, 0) else if (inline_params[1] == 0) @as(usize, 1) else @as(usize, 2);
   8096                     break :blk inline_params[0..len];
   8097                 },
   8098 
   8099                 else => unreachable,
   8100             };
   8101 
   8102             // If anything is wrong, fall back to builtinCall.
   8103             // It will emit any necessary compile errors and notes.
   8104             if (std.mem.eql(u8, builtin_name, "@field") and params.len == 2) {
   8105                 const lhs = try expr(gz, scope, .ref, params[0]);
   8106                 const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]);
   8107                 return gz.addExtendedPayload(.field_call_bind_named, Zir.Inst.FieldNamedNode{
   8108                     .node = gz.nodeIndexToRelative(node),
   8109                     .lhs = lhs,
   8110                     .field_name = field_name,
   8111                 });
   8112             }
   8113 
   8114             return builtinCall(gz, scope, .none, node, params);
   8115         },
   8116         else => return expr(gz, scope, .none, node),
   8117     }
   8118 }
   8119 
   8120 const primitives = std.ComptimeStringMap(Zir.Inst.Ref, .{
   8121     .{ "anyerror", .anyerror_type },
   8122     .{ "anyframe", .anyframe_type },
   8123     .{ "anyopaque", .anyopaque_type },
   8124     .{ "bool", .bool_type },
   8125     .{ "c_int", .c_int_type },
   8126     .{ "c_long", .c_long_type },
   8127     .{ "c_longdouble", .c_longdouble_type },
   8128     .{ "c_longlong", .c_longlong_type },
   8129     .{ "c_short", .c_short_type },
   8130     .{ "c_uint", .c_uint_type },
   8131     .{ "c_ulong", .c_ulong_type },
   8132     .{ "c_ulonglong", .c_ulonglong_type },
   8133     .{ "c_ushort", .c_ushort_type },
   8134     .{ "comptime_float", .comptime_float_type },
   8135     .{ "comptime_int", .comptime_int_type },
   8136     .{ "f128", .f128_type },
   8137     .{ "f16", .f16_type },
   8138     .{ "f32", .f32_type },
   8139     .{ "f64", .f64_type },
   8140     .{ "f80", .f80_type },
   8141     .{ "false", .bool_false },
   8142     .{ "i16", .i16_type },
   8143     .{ "i32", .i32_type },
   8144     .{ "i64", .i64_type },
   8145     .{ "i128", .i128_type },
   8146     .{ "i8", .i8_type },
   8147     .{ "isize", .isize_type },
   8148     .{ "noreturn", .noreturn_type },
   8149     .{ "null", .null_value },
   8150     .{ "true", .bool_true },
   8151     .{ "type", .type_type },
   8152     .{ "u16", .u16_type },
   8153     .{ "u29", .u29_type },
   8154     .{ "u32", .u32_type },
   8155     .{ "u64", .u64_type },
   8156     .{ "u128", .u128_type },
   8157     .{ "u1", .u1_type },
   8158     .{ "u8", .u8_type },
   8159     .{ "undefined", .undef },
   8160     .{ "usize", .usize_type },
   8161     .{ "void", .void_type },
   8162 });
   8163 
   8164 fn nodeMayNeedMemoryLocation(tree: *const Ast, start_node: Ast.Node.Index, have_res_ty: bool) bool {
   8165     const node_tags = tree.nodes.items(.tag);
   8166     const node_datas = tree.nodes.items(.data);
   8167     const main_tokens = tree.nodes.items(.main_token);
   8168     const token_tags = tree.tokens.items(.tag);
   8169 
   8170     var node = start_node;
   8171     while (true) {
   8172         switch (node_tags[node]) {
   8173             .root,
   8174             .@"usingnamespace",
   8175             .test_decl,
   8176             .switch_case,
   8177             .switch_case_one,
   8178             .container_field_init,
   8179             .container_field_align,
   8180             .container_field,
   8181             .asm_output,
   8182             .asm_input,
   8183             => unreachable,
   8184 
   8185             .@"return",
   8186             .@"break",
   8187             .@"continue",
   8188             .bit_not,
   8189             .bool_not,
   8190             .global_var_decl,
   8191             .local_var_decl,
   8192             .simple_var_decl,
   8193             .aligned_var_decl,
   8194             .@"defer",
   8195             .@"errdefer",
   8196             .address_of,
   8197             .optional_type,
   8198             .negation,
   8199             .negation_wrap,
   8200             .@"resume",
   8201             .array_type,
   8202             .array_type_sentinel,
   8203             .ptr_type_aligned,
   8204             .ptr_type_sentinel,
   8205             .ptr_type,
   8206             .ptr_type_bit_range,
   8207             .@"suspend",
   8208             .fn_proto_simple,
   8209             .fn_proto_multi,
   8210             .fn_proto_one,
   8211             .fn_proto,
   8212             .fn_decl,
   8213             .anyframe_type,
   8214             .anyframe_literal,
   8215             .integer_literal,
   8216             .float_literal,
   8217             .enum_literal,
   8218             .string_literal,
   8219             .multiline_string_literal,
   8220             .char_literal,
   8221             .unreachable_literal,
   8222             .identifier,
   8223             .error_set_decl,
   8224             .container_decl,
   8225             .container_decl_trailing,
   8226             .container_decl_two,
   8227             .container_decl_two_trailing,
   8228             .container_decl_arg,
   8229             .container_decl_arg_trailing,
   8230             .tagged_union,
   8231             .tagged_union_trailing,
   8232             .tagged_union_two,
   8233             .tagged_union_two_trailing,
   8234             .tagged_union_enum_tag,
   8235             .tagged_union_enum_tag_trailing,
   8236             .@"asm",
   8237             .asm_simple,
   8238             .add,
   8239             .add_wrap,
   8240             .add_sat,
   8241             .array_cat,
   8242             .array_mult,
   8243             .assign,
   8244             .assign_bit_and,
   8245             .assign_bit_or,
   8246             .assign_shl,
   8247             .assign_shl_sat,
   8248             .assign_shr,
   8249             .assign_bit_xor,
   8250             .assign_div,
   8251             .assign_sub,
   8252             .assign_sub_wrap,
   8253             .assign_sub_sat,
   8254             .assign_mod,
   8255             .assign_add,
   8256             .assign_add_wrap,
   8257             .assign_add_sat,
   8258             .assign_mul,
   8259             .assign_mul_wrap,
   8260             .assign_mul_sat,
   8261             .bang_equal,
   8262             .bit_and,
   8263             .bit_or,
   8264             .shl,
   8265             .shl_sat,
   8266             .shr,
   8267             .bit_xor,
   8268             .bool_and,
   8269             .bool_or,
   8270             .div,
   8271             .equal_equal,
   8272             .error_union,
   8273             .greater_or_equal,
   8274             .greater_than,
   8275             .less_or_equal,
   8276             .less_than,
   8277             .merge_error_sets,
   8278             .mod,
   8279             .mul,
   8280             .mul_wrap,
   8281             .mul_sat,
   8282             .switch_range,
   8283             .field_access,
   8284             .sub,
   8285             .sub_wrap,
   8286             .sub_sat,
   8287             .slice,
   8288             .slice_open,
   8289             .slice_sentinel,
   8290             .deref,
   8291             .array_access,
   8292             .error_value,
   8293             .while_simple, // This variant cannot have an else expression.
   8294             .while_cont, // This variant cannot have an else expression.
   8295             .for_simple, // This variant cannot have an else expression.
   8296             .if_simple, // This variant cannot have an else expression.
   8297             => return false,
   8298 
   8299             // Forward the question to the LHS sub-expression.
   8300             .grouped_expression,
   8301             .@"try",
   8302             .@"await",
   8303             .@"comptime",
   8304             .@"nosuspend",
   8305             .unwrap_optional,
   8306             => node = node_datas[node].lhs,
   8307 
   8308             // Forward the question to the RHS sub-expression.
   8309             .@"catch",
   8310             .@"orelse",
   8311             => node = node_datas[node].rhs,
   8312 
   8313             // Array and struct init exprs write to result locs, but anon literals do not.
   8314             .array_init_one,
   8315             .array_init_one_comma,
   8316             .struct_init_one,
   8317             .struct_init_one_comma,
   8318             .array_init,
   8319             .array_init_comma,
   8320             .struct_init,
   8321             .struct_init_comma,
   8322             => return have_res_ty or node_datas[node].lhs != 0,
   8323 
   8324             // Anon literals do not need result location.
   8325             .array_init_dot_two,
   8326             .array_init_dot_two_comma,
   8327             .array_init_dot,
   8328             .array_init_dot_comma,
   8329             .struct_init_dot_two,
   8330             .struct_init_dot_two_comma,
   8331             .struct_init_dot,
   8332             .struct_init_dot_comma,
   8333             => return have_res_ty,
   8334 
   8335             // True because depending on comptime conditions, sub-expressions
   8336             // may be the kind that need memory locations.
   8337             .@"while", // This variant always has an else expression.
   8338             .@"if", // This variant always has an else expression.
   8339             .@"for", // This variant always has an else expression.
   8340             .@"switch",
   8341             .switch_comma,
   8342             .call_one,
   8343             .call_one_comma,
   8344             .async_call_one,
   8345             .async_call_one_comma,
   8346             .call,
   8347             .call_comma,
   8348             .async_call,
   8349             .async_call_comma,
   8350             => return true,
   8351 
   8352             .block_two,
   8353             .block_two_semicolon,
   8354             .block,
   8355             .block_semicolon,
   8356             => {
   8357                 const lbrace = main_tokens[node];
   8358                 if (token_tags[lbrace - 1] == .colon) {
   8359                     // Labeled blocks may need a memory location to forward
   8360                     // to their break statements.
   8361                     return true;
   8362                 } else {
   8363                     return false;
   8364                 }
   8365             },
   8366 
   8367             .builtin_call_two, .builtin_call_two_comma => {
   8368                 const builtin_token = main_tokens[node];
   8369                 const builtin_name = tree.tokenSlice(builtin_token);
   8370                 // If the builtin is an invalid name, we don't cause an error here; instead
   8371                 // let it pass, and the error will be "invalid builtin function" later.
   8372                 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false;
   8373                 switch (builtin_info.needs_mem_loc) {
   8374                     .never => return false,
   8375                     .always => return true,
   8376                     .forward1 => node = node_datas[node].rhs,
   8377                 }
   8378             },
   8379 
   8380             .builtin_call, .builtin_call_comma => {
   8381                 const params = tree.extra_data[node_datas[node].lhs..node_datas[node].rhs];
   8382                 const builtin_token = main_tokens[node];
   8383                 const builtin_name = tree.tokenSlice(builtin_token);
   8384                 // If the builtin is an invalid name, we don't cause an error here; instead
   8385                 // let it pass, and the error will be "invalid builtin function" later.
   8386                 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return false;
   8387                 switch (builtin_info.needs_mem_loc) {
   8388                     .never => return false,
   8389                     .always => return true,
   8390                     .forward1 => node = params[1],
   8391                 }
   8392             },
   8393         }
   8394     }
   8395 }
   8396 
   8397 fn nodeMayEvalToError(tree: *const Ast, start_node: Ast.Node.Index) BuiltinFn.EvalToError {
   8398     const node_tags = tree.nodes.items(.tag);
   8399     const node_datas = tree.nodes.items(.data);
   8400     const main_tokens = tree.nodes.items(.main_token);
   8401     const token_tags = tree.tokens.items(.tag);
   8402 
   8403     var node = start_node;
   8404     while (true) {
   8405         switch (node_tags[node]) {
   8406             .root,
   8407             .@"usingnamespace",
   8408             .test_decl,
   8409             .switch_case,
   8410             .switch_case_one,
   8411             .container_field_init,
   8412             .container_field_align,
   8413             .container_field,
   8414             .asm_output,
   8415             .asm_input,
   8416             => unreachable,
   8417 
   8418             .error_value => return .always,
   8419 
   8420             .@"asm",
   8421             .asm_simple,
   8422             .identifier,
   8423             .field_access,
   8424             .deref,
   8425             .array_access,
   8426             .while_simple,
   8427             .while_cont,
   8428             .for_simple,
   8429             .if_simple,
   8430             .@"while",
   8431             .@"if",
   8432             .@"for",
   8433             .@"switch",
   8434             .switch_comma,
   8435             .call_one,
   8436             .call_one_comma,
   8437             .async_call_one,
   8438             .async_call_one_comma,
   8439             .call,
   8440             .call_comma,
   8441             .async_call,
   8442             .async_call_comma,
   8443             => return .maybe,
   8444 
   8445             .@"return",
   8446             .@"break",
   8447             .@"continue",
   8448             .bit_not,
   8449             .bool_not,
   8450             .global_var_decl,
   8451             .local_var_decl,
   8452             .simple_var_decl,
   8453             .aligned_var_decl,
   8454             .@"defer",
   8455             .@"errdefer",
   8456             .address_of,
   8457             .optional_type,
   8458             .negation,
   8459             .negation_wrap,
   8460             .@"resume",
   8461             .array_type,
   8462             .array_type_sentinel,
   8463             .ptr_type_aligned,
   8464             .ptr_type_sentinel,
   8465             .ptr_type,
   8466             .ptr_type_bit_range,
   8467             .@"suspend",
   8468             .fn_proto_simple,
   8469             .fn_proto_multi,
   8470             .fn_proto_one,
   8471             .fn_proto,
   8472             .fn_decl,
   8473             .anyframe_type,
   8474             .anyframe_literal,
   8475             .integer_literal,
   8476             .float_literal,
   8477             .enum_literal,
   8478             .string_literal,
   8479             .multiline_string_literal,
   8480             .char_literal,
   8481             .unreachable_literal,
   8482             .error_set_decl,
   8483             .container_decl,
   8484             .container_decl_trailing,
   8485             .container_decl_two,
   8486             .container_decl_two_trailing,
   8487             .container_decl_arg,
   8488             .container_decl_arg_trailing,
   8489             .tagged_union,
   8490             .tagged_union_trailing,
   8491             .tagged_union_two,
   8492             .tagged_union_two_trailing,
   8493             .tagged_union_enum_tag,
   8494             .tagged_union_enum_tag_trailing,
   8495             .add,
   8496             .add_wrap,
   8497             .add_sat,
   8498             .array_cat,
   8499             .array_mult,
   8500             .assign,
   8501             .assign_bit_and,
   8502             .assign_bit_or,
   8503             .assign_shl,
   8504             .assign_shl_sat,
   8505             .assign_shr,
   8506             .assign_bit_xor,
   8507             .assign_div,
   8508             .assign_sub,
   8509             .assign_sub_wrap,
   8510             .assign_sub_sat,
   8511             .assign_mod,
   8512             .assign_add,
   8513             .assign_add_wrap,
   8514             .assign_add_sat,
   8515             .assign_mul,
   8516             .assign_mul_wrap,
   8517             .assign_mul_sat,
   8518             .bang_equal,
   8519             .bit_and,
   8520             .bit_or,
   8521             .shl,
   8522             .shl_sat,
   8523             .shr,
   8524             .bit_xor,
   8525             .bool_and,
   8526             .bool_or,
   8527             .div,
   8528             .equal_equal,
   8529             .error_union,
   8530             .greater_or_equal,
   8531             .greater_than,
   8532             .less_or_equal,
   8533             .less_than,
   8534             .merge_error_sets,
   8535             .mod,
   8536             .mul,
   8537             .mul_wrap,
   8538             .mul_sat,
   8539             .switch_range,
   8540             .sub,
   8541             .sub_wrap,
   8542             .sub_sat,
   8543             .slice,
   8544             .slice_open,
   8545             .slice_sentinel,
   8546             .array_init_one,
   8547             .array_init_one_comma,
   8548             .array_init_dot_two,
   8549             .array_init_dot_two_comma,
   8550             .array_init_dot,
   8551             .array_init_dot_comma,
   8552             .array_init,
   8553             .array_init_comma,
   8554             .struct_init_one,
   8555             .struct_init_one_comma,
   8556             .struct_init_dot_two,
   8557             .struct_init_dot_two_comma,
   8558             .struct_init_dot,
   8559             .struct_init_dot_comma,
   8560             .struct_init,
   8561             .struct_init_comma,
   8562             => return .never,
   8563 
   8564             // Forward the question to the LHS sub-expression.
   8565             .grouped_expression,
   8566             .@"try",
   8567             .@"await",
   8568             .@"comptime",
   8569             .@"nosuspend",
   8570             .unwrap_optional,
   8571             => node = node_datas[node].lhs,
   8572 
   8573             // LHS sub-expression may still be an error under the outer optional or error union
   8574             .@"catch",
   8575             .@"orelse",
   8576             => return .maybe,
   8577 
   8578             .block_two,
   8579             .block_two_semicolon,
   8580             .block,
   8581             .block_semicolon,
   8582             => {
   8583                 const lbrace = main_tokens[node];
   8584                 if (token_tags[lbrace - 1] == .colon) {
   8585                     // Labeled blocks may need a memory location to forward
   8586                     // to their break statements.
   8587                     return .maybe;
   8588                 } else {
   8589                     return .never;
   8590                 }
   8591             },
   8592 
   8593             .builtin_call,
   8594             .builtin_call_comma,
   8595             .builtin_call_two,
   8596             .builtin_call_two_comma,
   8597             => {
   8598                 const builtin_token = main_tokens[node];
   8599                 const builtin_name = tree.tokenSlice(builtin_token);
   8600                 // If the builtin is an invalid name, we don't cause an error here; instead
   8601                 // let it pass, and the error will be "invalid builtin function" later.
   8602                 const builtin_info = BuiltinFn.list.get(builtin_name) orelse return .maybe;
   8603                 return builtin_info.eval_to_error;
   8604             },
   8605         }
   8606     }
   8607 }
   8608 
   8609 /// Returns `true` if it is known the type expression has more than one possible value;
   8610 /// `false` otherwise.
   8611 fn nodeImpliesMoreThanOnePossibleValue(tree: *const Ast, start_node: Ast.Node.Index) bool {
   8612     const node_tags = tree.nodes.items(.tag);
   8613     const node_datas = tree.nodes.items(.data);
   8614 
   8615     var node = start_node;
   8616     while (true) {
   8617         switch (node_tags[node]) {
   8618             .root,
   8619             .@"usingnamespace",
   8620             .test_decl,
   8621             .switch_case,
   8622             .switch_case_one,
   8623             .container_field_init,
   8624             .container_field_align,
   8625             .container_field,
   8626             .asm_output,
   8627             .asm_input,
   8628             .global_var_decl,
   8629             .local_var_decl,
   8630             .simple_var_decl,
   8631             .aligned_var_decl,
   8632             => unreachable,
   8633 
   8634             .@"return",
   8635             .@"break",
   8636             .@"continue",
   8637             .bit_not,
   8638             .bool_not,
   8639             .@"defer",
   8640             .@"errdefer",
   8641             .address_of,
   8642             .negation,
   8643             .negation_wrap,
   8644             .@"resume",
   8645             .array_type,
   8646             .@"suspend",
   8647             .fn_decl,
   8648             .anyframe_literal,
   8649             .integer_literal,
   8650             .float_literal,
   8651             .enum_literal,
   8652             .string_literal,
   8653             .multiline_string_literal,
   8654             .char_literal,
   8655             .unreachable_literal,
   8656             .error_set_decl,
   8657             .container_decl,
   8658             .container_decl_trailing,
   8659             .container_decl_two,
   8660             .container_decl_two_trailing,
   8661             .container_decl_arg,
   8662             .container_decl_arg_trailing,
   8663             .tagged_union,
   8664             .tagged_union_trailing,
   8665             .tagged_union_two,
   8666             .tagged_union_two_trailing,
   8667             .tagged_union_enum_tag,
   8668             .tagged_union_enum_tag_trailing,
   8669             .@"asm",
   8670             .asm_simple,
   8671             .add,
   8672             .add_wrap,
   8673             .add_sat,
   8674             .array_cat,
   8675             .array_mult,
   8676             .assign,
   8677             .assign_bit_and,
   8678             .assign_bit_or,
   8679             .assign_shl,
   8680             .assign_shl_sat,
   8681             .assign_shr,
   8682             .assign_bit_xor,
   8683             .assign_div,
   8684             .assign_sub,
   8685             .assign_sub_wrap,
   8686             .assign_sub_sat,
   8687             .assign_mod,
   8688             .assign_add,
   8689             .assign_add_wrap,
   8690             .assign_add_sat,
   8691             .assign_mul,
   8692             .assign_mul_wrap,
   8693             .assign_mul_sat,
   8694             .bang_equal,
   8695             .bit_and,
   8696             .bit_or,
   8697             .shl,
   8698             .shl_sat,
   8699             .shr,
   8700             .bit_xor,
   8701             .bool_and,
   8702             .bool_or,
   8703             .div,
   8704             .equal_equal,
   8705             .error_union,
   8706             .greater_or_equal,
   8707             .greater_than,
   8708             .less_or_equal,
   8709             .less_than,
   8710             .merge_error_sets,
   8711             .mod,
   8712             .mul,
   8713             .mul_wrap,
   8714             .mul_sat,
   8715             .switch_range,
   8716             .field_access,
   8717             .sub,
   8718             .sub_wrap,
   8719             .sub_sat,
   8720             .slice,
   8721             .slice_open,
   8722             .slice_sentinel,
   8723             .deref,
   8724             .array_access,
   8725             .error_value,
   8726             .while_simple,
   8727             .while_cont,
   8728             .for_simple,
   8729             .if_simple,
   8730             .@"catch",
   8731             .@"orelse",
   8732             .array_init_one,
   8733             .array_init_one_comma,
   8734             .array_init_dot_two,
   8735             .array_init_dot_two_comma,
   8736             .array_init_dot,
   8737             .array_init_dot_comma,
   8738             .array_init,
   8739             .array_init_comma,
   8740             .struct_init_one,
   8741             .struct_init_one_comma,
   8742             .struct_init_dot_two,
   8743             .struct_init_dot_two_comma,
   8744             .struct_init_dot,
   8745             .struct_init_dot_comma,
   8746             .struct_init,
   8747             .struct_init_comma,
   8748             .@"while",
   8749             .@"if",
   8750             .@"for",
   8751             .@"switch",
   8752             .switch_comma,
   8753             .call_one,
   8754             .call_one_comma,
   8755             .async_call_one,
   8756             .async_call_one_comma,
   8757             .call,
   8758             .call_comma,
   8759             .async_call,
   8760             .async_call_comma,
   8761             .block_two,
   8762             .block_two_semicolon,
   8763             .block,
   8764             .block_semicolon,
   8765             .builtin_call,
   8766             .builtin_call_comma,
   8767             .builtin_call_two,
   8768             .builtin_call_two_comma,
   8769             // these are function bodies, not pointers
   8770             .fn_proto_simple,
   8771             .fn_proto_multi,
   8772             .fn_proto_one,
   8773             .fn_proto,
   8774             => return false,
   8775 
   8776             // Forward the question to the LHS sub-expression.
   8777             .grouped_expression,
   8778             .@"try",
   8779             .@"await",
   8780             .@"comptime",
   8781             .@"nosuspend",
   8782             .unwrap_optional,
   8783             => node = node_datas[node].lhs,
   8784 
   8785             .ptr_type_aligned,
   8786             .ptr_type_sentinel,
   8787             .ptr_type,
   8788             .ptr_type_bit_range,
   8789             .optional_type,
   8790             .anyframe_type,
   8791             .array_type_sentinel,
   8792             => return true,
   8793 
   8794             .identifier => {
   8795                 const main_tokens = tree.nodes.items(.main_token);
   8796                 const ident_bytes = tree.tokenSlice(main_tokens[node]);
   8797                 if (primitives.get(ident_bytes)) |primitive| switch (primitive) {
   8798                     .anyerror_type,
   8799                     .anyframe_type,
   8800                     .anyopaque_type,
   8801                     .bool_type,
   8802                     .c_int_type,
   8803                     .c_long_type,
   8804                     .c_longdouble_type,
   8805                     .c_longlong_type,
   8806                     .c_short_type,
   8807                     .c_uint_type,
   8808                     .c_ulong_type,
   8809                     .c_ulonglong_type,
   8810                     .c_ushort_type,
   8811                     .comptime_float_type,
   8812                     .comptime_int_type,
   8813                     .f16_type,
   8814                     .f32_type,
   8815                     .f64_type,
   8816                     .f80_type,
   8817                     .f128_type,
   8818                     .i16_type,
   8819                     .i32_type,
   8820                     .i64_type,
   8821                     .i128_type,
   8822                     .i8_type,
   8823                     .isize_type,
   8824                     .type_type,
   8825                     .u16_type,
   8826                     .u29_type,
   8827                     .u32_type,
   8828                     .u64_type,
   8829                     .u128_type,
   8830                     .u1_type,
   8831                     .u8_type,
   8832                     .usize_type,
   8833                     => return true,
   8834 
   8835                     .void_type,
   8836                     .bool_false,
   8837                     .bool_true,
   8838                     .null_value,
   8839                     .undef,
   8840                     .noreturn_type,
   8841                     => return false,
   8842 
   8843                     else => unreachable, // that's all the values from `primitives`.
   8844                 } else {
   8845                     return false;
   8846                 }
   8847             },
   8848         }
   8849     }
   8850 }
   8851 
   8852 /// Returns `true` if it is known the expression is a type that cannot be used at runtime;
   8853 /// `false` otherwise.
   8854 fn nodeImpliesComptimeOnly(tree: *const Ast, start_node: Ast.Node.Index) bool {
   8855     const node_tags = tree.nodes.items(.tag);
   8856     const node_datas = tree.nodes.items(.data);
   8857 
   8858     var node = start_node;
   8859     while (true) {
   8860         switch (node_tags[node]) {
   8861             .root,
   8862             .@"usingnamespace",
   8863             .test_decl,
   8864             .switch_case,
   8865             .switch_case_one,
   8866             .container_field_init,
   8867             .container_field_align,
   8868             .container_field,
   8869             .asm_output,
   8870             .asm_input,
   8871             .global_var_decl,
   8872             .local_var_decl,
   8873             .simple_var_decl,
   8874             .aligned_var_decl,
   8875             => unreachable,
   8876 
   8877             .@"return",
   8878             .@"break",
   8879             .@"continue",
   8880             .bit_not,
   8881             .bool_not,
   8882             .@"defer",
   8883             .@"errdefer",
   8884             .address_of,
   8885             .negation,
   8886             .negation_wrap,
   8887             .@"resume",
   8888             .array_type,
   8889             .@"suspend",
   8890             .fn_decl,
   8891             .anyframe_literal,
   8892             .integer_literal,
   8893             .float_literal,
   8894             .enum_literal,
   8895             .string_literal,
   8896             .multiline_string_literal,
   8897             .char_literal,
   8898             .unreachable_literal,
   8899             .error_set_decl,
   8900             .container_decl,
   8901             .container_decl_trailing,
   8902             .container_decl_two,
   8903             .container_decl_two_trailing,
   8904             .container_decl_arg,
   8905             .container_decl_arg_trailing,
   8906             .tagged_union,
   8907             .tagged_union_trailing,
   8908             .tagged_union_two,
   8909             .tagged_union_two_trailing,
   8910             .tagged_union_enum_tag,
   8911             .tagged_union_enum_tag_trailing,
   8912             .@"asm",
   8913             .asm_simple,
   8914             .add,
   8915             .add_wrap,
   8916             .add_sat,
   8917             .array_cat,
   8918             .array_mult,
   8919             .assign,
   8920             .assign_bit_and,
   8921             .assign_bit_or,
   8922             .assign_shl,
   8923             .assign_shl_sat,
   8924             .assign_shr,
   8925             .assign_bit_xor,
   8926             .assign_div,
   8927             .assign_sub,
   8928             .assign_sub_wrap,
   8929             .assign_sub_sat,
   8930             .assign_mod,
   8931             .assign_add,
   8932             .assign_add_wrap,
   8933             .assign_add_sat,
   8934             .assign_mul,
   8935             .assign_mul_wrap,
   8936             .assign_mul_sat,
   8937             .bang_equal,
   8938             .bit_and,
   8939             .bit_or,
   8940             .shl,
   8941             .shl_sat,
   8942             .shr,
   8943             .bit_xor,
   8944             .bool_and,
   8945             .bool_or,
   8946             .div,
   8947             .equal_equal,
   8948             .error_union,
   8949             .greater_or_equal,
   8950             .greater_than,
   8951             .less_or_equal,
   8952             .less_than,
   8953             .merge_error_sets,
   8954             .mod,
   8955             .mul,
   8956             .mul_wrap,
   8957             .mul_sat,
   8958             .switch_range,
   8959             .field_access,
   8960             .sub,
   8961             .sub_wrap,
   8962             .sub_sat,
   8963             .slice,
   8964             .slice_open,
   8965             .slice_sentinel,
   8966             .deref,
   8967             .array_access,
   8968             .error_value,
   8969             .while_simple,
   8970             .while_cont,
   8971             .for_simple,
   8972             .if_simple,
   8973             .@"catch",
   8974             .@"orelse",
   8975             .array_init_one,
   8976             .array_init_one_comma,
   8977             .array_init_dot_two,
   8978             .array_init_dot_two_comma,
   8979             .array_init_dot,
   8980             .array_init_dot_comma,
   8981             .array_init,
   8982             .array_init_comma,
   8983             .struct_init_one,
   8984             .struct_init_one_comma,
   8985             .struct_init_dot_two,
   8986             .struct_init_dot_two_comma,
   8987             .struct_init_dot,
   8988             .struct_init_dot_comma,
   8989             .struct_init,
   8990             .struct_init_comma,
   8991             .@"while",
   8992             .@"if",
   8993             .@"for",
   8994             .@"switch",
   8995             .switch_comma,
   8996             .call_one,
   8997             .call_one_comma,
   8998             .async_call_one,
   8999             .async_call_one_comma,
   9000             .call,
   9001             .call_comma,
   9002             .async_call,
   9003             .async_call_comma,
   9004             .block_two,
   9005             .block_two_semicolon,
   9006             .block,
   9007             .block_semicolon,
   9008             .builtin_call,
   9009             .builtin_call_comma,
   9010             .builtin_call_two,
   9011             .builtin_call_two_comma,
   9012             .ptr_type_aligned,
   9013             .ptr_type_sentinel,
   9014             .ptr_type,
   9015             .ptr_type_bit_range,
   9016             .optional_type,
   9017             .anyframe_type,
   9018             .array_type_sentinel,
   9019             => return false,
   9020 
   9021             // these are function bodies, not pointers
   9022             .fn_proto_simple,
   9023             .fn_proto_multi,
   9024             .fn_proto_one,
   9025             .fn_proto,
   9026             => return true,
   9027 
   9028             // Forward the question to the LHS sub-expression.
   9029             .grouped_expression,
   9030             .@"try",
   9031             .@"await",
   9032             .@"comptime",
   9033             .@"nosuspend",
   9034             .unwrap_optional,
   9035             => node = node_datas[node].lhs,
   9036 
   9037             .identifier => {
   9038                 const main_tokens = tree.nodes.items(.main_token);
   9039                 const ident_bytes = tree.tokenSlice(main_tokens[node]);
   9040                 if (primitives.get(ident_bytes)) |primitive| switch (primitive) {
   9041                     .anyerror_type,
   9042                     .anyframe_type,
   9043                     .anyopaque_type,
   9044                     .bool_type,
   9045                     .c_int_type,
   9046                     .c_long_type,
   9047                     .c_longdouble_type,
   9048                     .c_longlong_type,
   9049                     .c_short_type,
   9050                     .c_uint_type,
   9051                     .c_ulong_type,
   9052                     .c_ulonglong_type,
   9053                     .c_ushort_type,
   9054                     .f16_type,
   9055                     .f32_type,
   9056                     .f64_type,
   9057                     .f80_type,
   9058                     .f128_type,
   9059                     .i16_type,
   9060                     .i32_type,
   9061                     .i64_type,
   9062                     .i128_type,
   9063                     .i8_type,
   9064                     .isize_type,
   9065                     .u16_type,
   9066                     .u29_type,
   9067                     .u32_type,
   9068                     .u64_type,
   9069                     .u128_type,
   9070                     .u1_type,
   9071                     .u8_type,
   9072                     .usize_type,
   9073                     .void_type,
   9074                     .bool_false,
   9075                     .bool_true,
   9076                     .null_value,
   9077                     .undef,
   9078                     .noreturn_type,
   9079                     => return false,
   9080 
   9081                     .comptime_float_type,
   9082                     .comptime_int_type,
   9083                     .type_type,
   9084                     => return true,
   9085 
   9086                     else => unreachable, // that's all the values from `primitives`.
   9087                 } else {
   9088                     return false;
   9089                 }
   9090             },
   9091         }
   9092     }
   9093 }
   9094 
   9095 /// Applies `rl` semantics to `result`. Expressions which do not do their own handling of
   9096 /// result locations must call this function on their result.
   9097 /// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer.
   9098 /// If the `ResultLoc` is `ty`, it will coerce the result to the type.
   9099 /// Assumes nothing stacked on `gz`.
   9100 fn rvalue(
   9101     gz: *GenZir,
   9102     rl: ResultLoc,
   9103     raw_result: Zir.Inst.Ref,
   9104     src_node: Ast.Node.Index,
   9105 ) InnerError!Zir.Inst.Ref {
   9106     const result = r: {
   9107         if (refToIndex(raw_result)) |result_index| {
   9108             const zir_tags = gz.astgen.instructions.items(.tag);
   9109             const data = gz.astgen.instructions.items(.data)[result_index];
   9110             if (zir_tags[result_index].isAlwaysVoid(data)) {
   9111                 break :r Zir.Inst.Ref.void_value;
   9112             }
   9113         }
   9114         break :r raw_result;
   9115     };
   9116     if (gz.endsWithNoReturn()) return result;
   9117     switch (rl) {
   9118         .none, .coerced_ty => return result,
   9119         .discard => {
   9120             // Emit a compile error for discarding error values.
   9121             _ = try gz.addUnNode(.ensure_result_non_error, result, src_node);
   9122             return result;
   9123         },
   9124         .ref => {
   9125             // We need a pointer but we have a value.
   9126             // Unfortunately it's not quite as simple as directly emitting a ref
   9127             // instruction here because we need subsequent address-of operator on
   9128             // const locals to return the same address.
   9129             const astgen = gz.astgen;
   9130             const tree = astgen.tree;
   9131             const src_token = tree.firstToken(src_node);
   9132             const result_index = refToIndex(result) orelse
   9133                 return gz.addUnTok(.ref, result, src_token);
   9134             const zir_tags = gz.astgen.instructions.items(.tag);
   9135             if (zir_tags[result_index].isParam())
   9136                 return gz.addUnTok(.ref, result, src_token);
   9137             const gop = try astgen.ref_table.getOrPut(astgen.gpa, result_index);
   9138             if (!gop.found_existing) {
   9139                 gop.value_ptr.* = try gz.makeUnTok(.ref, result, src_token);
   9140             }
   9141             return indexToRef(gop.value_ptr.*);
   9142         },
   9143         .ty => |ty_inst| {
   9144             // Quickly eliminate some common, unnecessary type coercion.
   9145             const as_ty = @as(u64, @enumToInt(Zir.Inst.Ref.type_type)) << 32;
   9146             const as_comptime_int = @as(u64, @enumToInt(Zir.Inst.Ref.comptime_int_type)) << 32;
   9147             const as_bool = @as(u64, @enumToInt(Zir.Inst.Ref.bool_type)) << 32;
   9148             const as_usize = @as(u64, @enumToInt(Zir.Inst.Ref.usize_type)) << 32;
   9149             const as_void = @as(u64, @enumToInt(Zir.Inst.Ref.void_type)) << 32;
   9150             switch ((@as(u64, @enumToInt(ty_inst)) << 32) | @as(u64, @enumToInt(result))) {
   9151                 as_ty | @enumToInt(Zir.Inst.Ref.u1_type),
   9152                 as_ty | @enumToInt(Zir.Inst.Ref.u8_type),
   9153                 as_ty | @enumToInt(Zir.Inst.Ref.i8_type),
   9154                 as_ty | @enumToInt(Zir.Inst.Ref.u16_type),
   9155                 as_ty | @enumToInt(Zir.Inst.Ref.u29_type),
   9156                 as_ty | @enumToInt(Zir.Inst.Ref.i16_type),
   9157                 as_ty | @enumToInt(Zir.Inst.Ref.u32_type),
   9158                 as_ty | @enumToInt(Zir.Inst.Ref.i32_type),
   9159                 as_ty | @enumToInt(Zir.Inst.Ref.u64_type),
   9160                 as_ty | @enumToInt(Zir.Inst.Ref.i64_type),
   9161                 as_ty | @enumToInt(Zir.Inst.Ref.usize_type),
   9162                 as_ty | @enumToInt(Zir.Inst.Ref.isize_type),
   9163                 as_ty | @enumToInt(Zir.Inst.Ref.c_short_type),
   9164                 as_ty | @enumToInt(Zir.Inst.Ref.c_ushort_type),
   9165                 as_ty | @enumToInt(Zir.Inst.Ref.c_int_type),
   9166                 as_ty | @enumToInt(Zir.Inst.Ref.c_uint_type),
   9167                 as_ty | @enumToInt(Zir.Inst.Ref.c_long_type),
   9168                 as_ty | @enumToInt(Zir.Inst.Ref.c_ulong_type),
   9169                 as_ty | @enumToInt(Zir.Inst.Ref.c_longlong_type),
   9170                 as_ty | @enumToInt(Zir.Inst.Ref.c_ulonglong_type),
   9171                 as_ty | @enumToInt(Zir.Inst.Ref.c_longdouble_type),
   9172                 as_ty | @enumToInt(Zir.Inst.Ref.f16_type),
   9173                 as_ty | @enumToInt(Zir.Inst.Ref.f32_type),
   9174                 as_ty | @enumToInt(Zir.Inst.Ref.f64_type),
   9175                 as_ty | @enumToInt(Zir.Inst.Ref.f80_type),
   9176                 as_ty | @enumToInt(Zir.Inst.Ref.f128_type),
   9177                 as_ty | @enumToInt(Zir.Inst.Ref.anyopaque_type),
   9178                 as_ty | @enumToInt(Zir.Inst.Ref.bool_type),
   9179                 as_ty | @enumToInt(Zir.Inst.Ref.void_type),
   9180                 as_ty | @enumToInt(Zir.Inst.Ref.type_type),
   9181                 as_ty | @enumToInt(Zir.Inst.Ref.anyerror_type),
   9182                 as_ty | @enumToInt(Zir.Inst.Ref.comptime_int_type),
   9183                 as_ty | @enumToInt(Zir.Inst.Ref.comptime_float_type),
   9184                 as_ty | @enumToInt(Zir.Inst.Ref.noreturn_type),
   9185                 as_ty | @enumToInt(Zir.Inst.Ref.null_type),
   9186                 as_ty | @enumToInt(Zir.Inst.Ref.undefined_type),
   9187                 as_ty | @enumToInt(Zir.Inst.Ref.fn_noreturn_no_args_type),
   9188                 as_ty | @enumToInt(Zir.Inst.Ref.fn_void_no_args_type),
   9189                 as_ty | @enumToInt(Zir.Inst.Ref.fn_naked_noreturn_no_args_type),
   9190                 as_ty | @enumToInt(Zir.Inst.Ref.fn_ccc_void_no_args_type),
   9191                 as_ty | @enumToInt(Zir.Inst.Ref.single_const_pointer_to_comptime_int_type),
   9192                 as_ty | @enumToInt(Zir.Inst.Ref.const_slice_u8_type),
   9193                 as_ty | @enumToInt(Zir.Inst.Ref.enum_literal_type),
   9194                 as_comptime_int | @enumToInt(Zir.Inst.Ref.zero),
   9195                 as_comptime_int | @enumToInt(Zir.Inst.Ref.one),
   9196                 as_bool | @enumToInt(Zir.Inst.Ref.bool_true),
   9197                 as_bool | @enumToInt(Zir.Inst.Ref.bool_false),
   9198                 as_usize | @enumToInt(Zir.Inst.Ref.zero_usize),
   9199                 as_usize | @enumToInt(Zir.Inst.Ref.one_usize),
   9200                 as_void | @enumToInt(Zir.Inst.Ref.void_value),
   9201                 => return result, // type of result is already correct
   9202 
   9203                 // Need an explicit type coercion instruction.
   9204                 else => return gz.addPlNode(.as_node, src_node, Zir.Inst.As{
   9205                     .dest_type = ty_inst,
   9206                     .operand = result,
   9207                 }),
   9208             }
   9209         },
   9210         .ptr => |ptr_inst| {
   9211             if (gz.rvalue_noresult != ptr_inst) {
   9212                 _ = try gz.addPlNode(.store_node, src_node, Zir.Inst.Bin{
   9213                     .lhs = ptr_inst,
   9214                     .rhs = result,
   9215                 });
   9216             }
   9217             return result;
   9218         },
   9219         .inferred_ptr => |alloc| {
   9220             if (gz.rvalue_noresult != alloc) {
   9221                 _ = try gz.addBin(.store_to_inferred_ptr, alloc, result);
   9222             }
   9223             return result;
   9224         },
   9225         .block_ptr => |block_scope| {
   9226             if (gz.rvalue_noresult != block_scope.rl_ptr) {
   9227                 block_scope.rvalue_rl_count += 1;
   9228                 _ = try gz.addBin(.store_to_block_ptr, block_scope.rl_ptr, result);
   9229             }
   9230             return result;
   9231         },
   9232     }
   9233 }
   9234 
   9235 /// Given an identifier token, obtain the string for it.
   9236 /// If the token uses @"" syntax, parses as a string, reports errors if applicable,
   9237 /// and allocates the result within `astgen.arena`.
   9238 /// Otherwise, returns a reference to the source code bytes directly.
   9239 /// See also `appendIdentStr` and `parseStrLit`.
   9240 fn identifierTokenString(astgen: *AstGen, token: Ast.TokenIndex) InnerError![]const u8 {
   9241     const tree = astgen.tree;
   9242     const token_tags = tree.tokens.items(.tag);
   9243     assert(token_tags[token] == .identifier);
   9244     const ident_name = tree.tokenSlice(token);
   9245     if (!mem.startsWith(u8, ident_name, "@")) {
   9246         return ident_name;
   9247     }
   9248     var buf: ArrayListUnmanaged(u8) = .{};
   9249     defer buf.deinit(astgen.gpa);
   9250     try astgen.parseStrLit(token, &buf, ident_name, 1);
   9251     const duped = try astgen.arena.dupe(u8, buf.items);
   9252     return duped;
   9253 }
   9254 
   9255 /// Given an identifier token, obtain the string for it (possibly parsing as a string
   9256 /// literal if it is @"" syntax), and append the string to `buf`.
   9257 /// See also `identifierTokenString` and `parseStrLit`.
   9258 fn appendIdentStr(
   9259     astgen: *AstGen,
   9260     token: Ast.TokenIndex,
   9261     buf: *ArrayListUnmanaged(u8),
   9262 ) InnerError!void {
   9263     const tree = astgen.tree;
   9264     const token_tags = tree.tokens.items(.tag);
   9265     assert(token_tags[token] == .identifier);
   9266     const ident_name = tree.tokenSlice(token);
   9267     if (!mem.startsWith(u8, ident_name, "@")) {
   9268         return buf.appendSlice(astgen.gpa, ident_name);
   9269     } else {
   9270         return astgen.parseStrLit(token, buf, ident_name, 1);
   9271     }
   9272 }
   9273 
   9274 /// Appends the result to `buf`.
   9275 fn parseStrLit(
   9276     astgen: *AstGen,
   9277     token: Ast.TokenIndex,
   9278     buf: *ArrayListUnmanaged(u8),
   9279     bytes: []const u8,
   9280     offset: u32,
   9281 ) InnerError!void {
   9282     const raw_string = bytes[offset..];
   9283     var buf_managed = buf.toManaged(astgen.gpa);
   9284     const result = std.zig.string_literal.parseAppend(&buf_managed, raw_string);
   9285     buf.* = buf_managed.moveToUnmanaged();
   9286     switch (try result) {
   9287         .success => return,
   9288         .failure => |err| return astgen.failWithStrLitError(err, token, bytes, offset),
   9289     }
   9290 }
   9291 
   9292 fn failWithStrLitError(astgen: *AstGen, err: std.zig.string_literal.Error, token: Ast.TokenIndex, bytes: []const u8, offset: u32) InnerError {
   9293     const raw_string = bytes[offset..];
   9294     switch (err) {
   9295         .invalid_escape_character => |bad_index| {
   9296             return astgen.failOff(
   9297                 token,
   9298                 offset + @intCast(u32, bad_index),
   9299                 "invalid escape character: '{c}'",
   9300                 .{raw_string[bad_index]},
   9301             );
   9302         },
   9303         .expected_hex_digit => |bad_index| {
   9304             return astgen.failOff(
   9305                 token,
   9306                 offset + @intCast(u32, bad_index),
   9307                 "expected hex digit, found '{c}'",
   9308                 .{raw_string[bad_index]},
   9309             );
   9310         },
   9311         .empty_unicode_escape_sequence => |bad_index| {
   9312             return astgen.failOff(
   9313                 token,
   9314                 offset + @intCast(u32, bad_index),
   9315                 "empty unicode escape sequence",
   9316                 .{},
   9317             );
   9318         },
   9319         .expected_hex_digit_or_rbrace => |bad_index| {
   9320             return astgen.failOff(
   9321                 token,
   9322                 offset + @intCast(u32, bad_index),
   9323                 "expected hex digit or '}}', found '{c}'",
   9324                 .{raw_string[bad_index]},
   9325             );
   9326         },
   9327         .invalid_unicode_codepoint => |bad_index| {
   9328             return astgen.failOff(
   9329                 token,
   9330                 offset + @intCast(u32, bad_index),
   9331                 "unicode escape does not correspond to a valid codepoint",
   9332                 .{},
   9333             );
   9334         },
   9335         .expected_lbrace => |bad_index| {
   9336             return astgen.failOff(
   9337                 token,
   9338                 offset + @intCast(u32, bad_index),
   9339                 "expected '{{', found '{c}",
   9340                 .{raw_string[bad_index]},
   9341             );
   9342         },
   9343         .expected_rbrace => |bad_index| {
   9344             return astgen.failOff(
   9345                 token,
   9346                 offset + @intCast(u32, bad_index),
   9347                 "expected '}}', found '{c}",
   9348                 .{raw_string[bad_index]},
   9349             );
   9350         },
   9351         .expected_single_quote => |bad_index| {
   9352             return astgen.failOff(
   9353                 token,
   9354                 offset + @intCast(u32, bad_index),
   9355                 "expected single quote ('), found '{c}",
   9356                 .{raw_string[bad_index]},
   9357             );
   9358         },
   9359         .invalid_character => |bad_index| {
   9360             return astgen.failOff(
   9361                 token,
   9362                 offset + @intCast(u32, bad_index),
   9363                 "invalid byte in string or character literal: '{c}'",
   9364                 .{raw_string[bad_index]},
   9365             );
   9366         },
   9367     }
   9368 }
   9369 
   9370 fn failNode(
   9371     astgen: *AstGen,
   9372     node: Ast.Node.Index,
   9373     comptime format: []const u8,
   9374     args: anytype,
   9375 ) InnerError {
   9376     return astgen.failNodeNotes(node, format, args, &[0]u32{});
   9377 }
   9378 
   9379 fn appendErrorNode(
   9380     astgen: *AstGen,
   9381     node: Ast.Node.Index,
   9382     comptime format: []const u8,
   9383     args: anytype,
   9384 ) Allocator.Error!void {
   9385     try astgen.appendErrorNodeNotes(node, format, args, &[0]u32{});
   9386 }
   9387 
   9388 fn appendErrorNodeNotes(
   9389     astgen: *AstGen,
   9390     node: Ast.Node.Index,
   9391     comptime format: []const u8,
   9392     args: anytype,
   9393     notes: []const u32,
   9394 ) Allocator.Error!void {
   9395     @setCold(true);
   9396     const string_bytes = &astgen.string_bytes;
   9397     const msg = @intCast(u32, string_bytes.items.len);
   9398     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
   9399     const notes_index: u32 = if (notes.len != 0) blk: {
   9400         const notes_start = astgen.extra.items.len;
   9401         try astgen.extra.ensureTotalCapacity(astgen.gpa, notes_start + 1 + notes.len);
   9402         astgen.extra.appendAssumeCapacity(@intCast(u32, notes.len));
   9403         astgen.extra.appendSliceAssumeCapacity(notes);
   9404         break :blk @intCast(u32, notes_start);
   9405     } else 0;
   9406     try astgen.compile_errors.append(astgen.gpa, .{
   9407         .msg = msg,
   9408         .node = node,
   9409         .token = 0,
   9410         .byte_offset = 0,
   9411         .notes = notes_index,
   9412     });
   9413 }
   9414 
   9415 fn failNodeNotes(
   9416     astgen: *AstGen,
   9417     node: Ast.Node.Index,
   9418     comptime format: []const u8,
   9419     args: anytype,
   9420     notes: []const u32,
   9421 ) InnerError {
   9422     try appendErrorNodeNotes(astgen, node, format, args, notes);
   9423     return error.AnalysisFail;
   9424 }
   9425 
   9426 fn failTok(
   9427     astgen: *AstGen,
   9428     token: Ast.TokenIndex,
   9429     comptime format: []const u8,
   9430     args: anytype,
   9431 ) InnerError {
   9432     return astgen.failTokNotes(token, format, args, &[0]u32{});
   9433 }
   9434 
   9435 fn appendErrorTok(
   9436     astgen: *AstGen,
   9437     token: Ast.TokenIndex,
   9438     comptime format: []const u8,
   9439     args: anytype,
   9440 ) !void {
   9441     try astgen.appendErrorTokNotes(token, format, args, &[0]u32{});
   9442 }
   9443 
   9444 fn failTokNotes(
   9445     astgen: *AstGen,
   9446     token: Ast.TokenIndex,
   9447     comptime format: []const u8,
   9448     args: anytype,
   9449     notes: []const u32,
   9450 ) InnerError {
   9451     try appendErrorTokNotes(astgen, token, format, args, notes);
   9452     return error.AnalysisFail;
   9453 }
   9454 
   9455 fn appendErrorTokNotes(
   9456     astgen: *AstGen,
   9457     token: Ast.TokenIndex,
   9458     comptime format: []const u8,
   9459     args: anytype,
   9460     notes: []const u32,
   9461 ) !void {
   9462     @setCold(true);
   9463     const string_bytes = &astgen.string_bytes;
   9464     const msg = @intCast(u32, string_bytes.items.len);
   9465     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
   9466     const notes_index: u32 = if (notes.len != 0) blk: {
   9467         const notes_start = astgen.extra.items.len;
   9468         try astgen.extra.ensureTotalCapacity(astgen.gpa, notes_start + 1 + notes.len);
   9469         astgen.extra.appendAssumeCapacity(@intCast(u32, notes.len));
   9470         astgen.extra.appendSliceAssumeCapacity(notes);
   9471         break :blk @intCast(u32, notes_start);
   9472     } else 0;
   9473     try astgen.compile_errors.append(astgen.gpa, .{
   9474         .msg = msg,
   9475         .node = 0,
   9476         .token = token,
   9477         .byte_offset = 0,
   9478         .notes = notes_index,
   9479     });
   9480 }
   9481 
   9482 /// Same as `fail`, except given an absolute byte offset.
   9483 fn failOff(
   9484     astgen: *AstGen,
   9485     token: Ast.TokenIndex,
   9486     byte_offset: u32,
   9487     comptime format: []const u8,
   9488     args: anytype,
   9489 ) InnerError {
   9490     try appendErrorOff(astgen, token, byte_offset, format, args);
   9491     return error.AnalysisFail;
   9492 }
   9493 
   9494 fn appendErrorOff(
   9495     astgen: *AstGen,
   9496     token: Ast.TokenIndex,
   9497     byte_offset: u32,
   9498     comptime format: []const u8,
   9499     args: anytype,
   9500 ) Allocator.Error!void {
   9501     @setCold(true);
   9502     const string_bytes = &astgen.string_bytes;
   9503     const msg = @intCast(u32, string_bytes.items.len);
   9504     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
   9505     try astgen.compile_errors.append(astgen.gpa, .{
   9506         .msg = msg,
   9507         .node = 0,
   9508         .token = token,
   9509         .byte_offset = byte_offset,
   9510         .notes = 0,
   9511     });
   9512 }
   9513 
   9514 fn errNoteTok(
   9515     astgen: *AstGen,
   9516     token: Ast.TokenIndex,
   9517     comptime format: []const u8,
   9518     args: anytype,
   9519 ) Allocator.Error!u32 {
   9520     @setCold(true);
   9521     const string_bytes = &astgen.string_bytes;
   9522     const msg = @intCast(u32, string_bytes.items.len);
   9523     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
   9524     return astgen.addExtra(Zir.Inst.CompileErrors.Item{
   9525         .msg = msg,
   9526         .node = 0,
   9527         .token = token,
   9528         .byte_offset = 0,
   9529         .notes = 0,
   9530     });
   9531 }
   9532 
   9533 fn errNoteNode(
   9534     astgen: *AstGen,
   9535     node: Ast.Node.Index,
   9536     comptime format: []const u8,
   9537     args: anytype,
   9538 ) Allocator.Error!u32 {
   9539     @setCold(true);
   9540     const string_bytes = &astgen.string_bytes;
   9541     const msg = @intCast(u32, string_bytes.items.len);
   9542     try string_bytes.writer(astgen.gpa).print(format ++ "\x00", args);
   9543     return astgen.addExtra(Zir.Inst.CompileErrors.Item{
   9544         .msg = msg,
   9545         .node = node,
   9546         .token = 0,
   9547         .byte_offset = 0,
   9548         .notes = 0,
   9549     });
   9550 }
   9551 
   9552 fn identAsString(astgen: *AstGen, ident_token: Ast.TokenIndex) !u32 {
   9553     const gpa = astgen.gpa;
   9554     const string_bytes = &astgen.string_bytes;
   9555     const str_index = @intCast(u32, string_bytes.items.len);
   9556     try astgen.appendIdentStr(ident_token, string_bytes);
   9557     const key = string_bytes.items[str_index..];
   9558     const gop = try astgen.string_table.getOrPutContextAdapted(gpa, @as([]const u8, key), StringIndexAdapter{
   9559         .bytes = string_bytes,
   9560     }, StringIndexContext{
   9561         .bytes = string_bytes,
   9562     });
   9563     if (gop.found_existing) {
   9564         string_bytes.shrinkRetainingCapacity(str_index);
   9565         return gop.key_ptr.*;
   9566     } else {
   9567         gop.key_ptr.* = str_index;
   9568         try string_bytes.append(gpa, 0);
   9569         return str_index;
   9570     }
   9571 }
   9572 
   9573 /// Adds a doc comment block to `string_bytes` by walking backwards from `end_token`.
   9574 /// `end_token` must point at the first token after the last doc coment line.
   9575 /// Returns 0 if no doc comment is present.
   9576 fn docCommentAsString(astgen: *AstGen, end_token: Ast.TokenIndex) !u32 {
   9577     if (end_token == 0) return @as(u32, 0);
   9578 
   9579     const token_tags = astgen.tree.tokens.items(.tag);
   9580 
   9581     var tok = end_token - 1;
   9582     while (token_tags[tok] == .doc_comment) {
   9583         if (tok == 0) break;
   9584         tok -= 1;
   9585     } else {
   9586         tok += 1;
   9587     }
   9588     return docCommentAsStringFromFirst(astgen, end_token, tok);
   9589 }
   9590 
   9591 /// end_token must be > the index of the last doc comment.
   9592 fn docCommentAsStringFromFirst(
   9593     astgen: *AstGen,
   9594     end_token: Ast.TokenIndex,
   9595     start_token: Ast.TokenIndex,
   9596 ) !u32 {
   9597     if (start_token == end_token) return 0;
   9598 
   9599     const gpa = astgen.gpa;
   9600     const string_bytes = &astgen.string_bytes;
   9601     const str_index = @intCast(u32, string_bytes.items.len);
   9602     const token_starts = astgen.tree.tokens.items(.start);
   9603     const token_tags = astgen.tree.tokens.items(.tag);
   9604 
   9605     const total_bytes = token_starts[end_token] - token_starts[start_token];
   9606     try string_bytes.ensureUnusedCapacity(gpa, total_bytes);
   9607 
   9608     var current_token = start_token;
   9609     while (current_token < end_token) : (current_token += 1) {
   9610         switch (token_tags[current_token]) {
   9611             .doc_comment => {
   9612                 const tok_bytes = astgen.tree.tokenSlice(current_token)[3..];
   9613                 string_bytes.appendSliceAssumeCapacity(tok_bytes);
   9614                 if (current_token != end_token - 1) {
   9615                     string_bytes.appendAssumeCapacity('\n');
   9616                 }
   9617             },
   9618             else => break,
   9619         }
   9620     }
   9621 
   9622     const key = string_bytes.items[str_index..];
   9623     const gop = try astgen.string_table.getOrPutContextAdapted(gpa, @as([]const u8, key), StringIndexAdapter{
   9624         .bytes = string_bytes,
   9625     }, StringIndexContext{
   9626         .bytes = string_bytes,
   9627     });
   9628 
   9629     if (gop.found_existing) {
   9630         string_bytes.shrinkRetainingCapacity(str_index);
   9631         return gop.key_ptr.*;
   9632     } else {
   9633         gop.key_ptr.* = str_index;
   9634         try string_bytes.append(gpa, 0);
   9635         return str_index;
   9636     }
   9637 }
   9638 
   9639 const IndexSlice = struct { index: u32, len: u32 };
   9640 
   9641 fn strLitAsString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !IndexSlice {
   9642     const gpa = astgen.gpa;
   9643     const string_bytes = &astgen.string_bytes;
   9644     const str_index = @intCast(u32, string_bytes.items.len);
   9645     const token_bytes = astgen.tree.tokenSlice(str_lit_token);
   9646     try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0);
   9647     const key = string_bytes.items[str_index..];
   9648     const gop = try astgen.string_table.getOrPutContextAdapted(gpa, @as([]const u8, key), StringIndexAdapter{
   9649         .bytes = string_bytes,
   9650     }, StringIndexContext{
   9651         .bytes = string_bytes,
   9652     });
   9653     if (gop.found_existing) {
   9654         string_bytes.shrinkRetainingCapacity(str_index);
   9655         return IndexSlice{
   9656             .index = gop.key_ptr.*,
   9657             .len = @intCast(u32, key.len),
   9658         };
   9659     } else {
   9660         gop.key_ptr.* = str_index;
   9661         // Still need a null byte because we are using the same table
   9662         // to lookup null terminated strings, so if we get a match, it has to
   9663         // be null terminated for that to work.
   9664         try string_bytes.append(gpa, 0);
   9665         return IndexSlice{
   9666             .index = str_index,
   9667             .len = @intCast(u32, key.len),
   9668         };
   9669     }
   9670 }
   9671 
   9672 fn strLitNodeAsString(astgen: *AstGen, node: Ast.Node.Index) !IndexSlice {
   9673     const tree = astgen.tree;
   9674     const node_datas = tree.nodes.items(.data);
   9675 
   9676     const start = node_datas[node].lhs;
   9677     const end = node_datas[node].rhs;
   9678 
   9679     const gpa = astgen.gpa;
   9680     const string_bytes = &astgen.string_bytes;
   9681     const str_index = string_bytes.items.len;
   9682 
   9683     // First line: do not append a newline.
   9684     var tok_i = start;
   9685     {
   9686         const slice = tree.tokenSlice(tok_i);
   9687         const line_bytes = slice[2 .. slice.len - 1];
   9688         try string_bytes.appendSlice(gpa, line_bytes);
   9689         tok_i += 1;
   9690     }
   9691     // Following lines: each line prepends a newline.
   9692     while (tok_i <= end) : (tok_i += 1) {
   9693         const slice = tree.tokenSlice(tok_i);
   9694         const line_bytes = slice[2 .. slice.len - 1];
   9695         try string_bytes.ensureUnusedCapacity(gpa, line_bytes.len + 1);
   9696         string_bytes.appendAssumeCapacity('\n');
   9697         string_bytes.appendSliceAssumeCapacity(line_bytes);
   9698     }
   9699     const len = string_bytes.items.len - str_index;
   9700     try string_bytes.append(gpa, 0);
   9701     return IndexSlice{
   9702         .index = @intCast(u32, str_index),
   9703         .len = @intCast(u32, len),
   9704     };
   9705 }
   9706 
   9707 fn testNameString(astgen: *AstGen, str_lit_token: Ast.TokenIndex) !u32 {
   9708     const gpa = astgen.gpa;
   9709     const string_bytes = &astgen.string_bytes;
   9710     const str_index = @intCast(u32, string_bytes.items.len);
   9711     const token_bytes = astgen.tree.tokenSlice(str_lit_token);
   9712     try string_bytes.append(gpa, 0); // Indicates this is a test.
   9713     try astgen.parseStrLit(str_lit_token, string_bytes, token_bytes, 0);
   9714     try string_bytes.append(gpa, 0);
   9715     return str_index;
   9716 }
   9717 
   9718 const Scope = struct {
   9719     tag: Tag,
   9720 
   9721     fn cast(base: *Scope, comptime T: type) ?*T {
   9722         if (T == Defer) {
   9723             switch (base.tag) {
   9724                 .defer_normal, .defer_error => return @fieldParentPtr(T, "base", base),
   9725                 else => return null,
   9726             }
   9727         }
   9728         if (base.tag != T.base_tag)
   9729             return null;
   9730 
   9731         return @fieldParentPtr(T, "base", base);
   9732     }
   9733 
   9734     fn parent(base: *Scope) ?*Scope {
   9735         return switch (base.tag) {
   9736             .gen_zir => base.cast(GenZir).?.parent,
   9737             .local_val => base.cast(LocalVal).?.parent,
   9738             .local_ptr => base.cast(LocalPtr).?.parent,
   9739             .defer_normal, .defer_error => base.cast(Defer).?.parent,
   9740             .namespace => base.cast(Namespace).?.parent,
   9741             .top => null,
   9742         };
   9743     }
   9744 
   9745     const Tag = enum {
   9746         gen_zir,
   9747         local_val,
   9748         local_ptr,
   9749         defer_normal,
   9750         defer_error,
   9751         namespace,
   9752         top,
   9753     };
   9754 
   9755     /// The category of identifier. These tag names are user-visible in compile errors.
   9756     const IdCat = enum {
   9757         @"function parameter",
   9758         @"local constant",
   9759         @"local variable",
   9760         @"loop index capture",
   9761         @"capture",
   9762     };
   9763 
   9764     /// This is always a `const` local and importantly the `inst` is a value type, not a pointer.
   9765     /// This structure lives as long as the AST generation of the Block
   9766     /// node that contains the variable.
   9767     const LocalVal = struct {
   9768         const base_tag: Tag = .local_val;
   9769         base: Scope = Scope{ .tag = base_tag },
   9770         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
   9771         parent: *Scope,
   9772         gen_zir: *GenZir,
   9773         inst: Zir.Inst.Ref,
   9774         /// Source location of the corresponding variable declaration.
   9775         token_src: Ast.TokenIndex,
   9776         /// String table index.
   9777         name: u32,
   9778         id_cat: IdCat,
   9779         /// Track whether the name has been referenced.
   9780         used: bool = false,
   9781     };
   9782 
   9783     /// This could be a `const` or `var` local. It has a pointer instead of a value.
   9784     /// This structure lives as long as the AST generation of the Block
   9785     /// node that contains the variable.
   9786     const LocalPtr = struct {
   9787         const base_tag: Tag = .local_ptr;
   9788         base: Scope = Scope{ .tag = base_tag },
   9789         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
   9790         parent: *Scope,
   9791         gen_zir: *GenZir,
   9792         ptr: Zir.Inst.Ref,
   9793         /// Source location of the corresponding variable declaration.
   9794         token_src: Ast.TokenIndex,
   9795         /// String table index.
   9796         name: u32,
   9797         id_cat: IdCat,
   9798         /// true means we find out during Sema whether the value is comptime.
   9799         /// false means it is already known at AstGen the value is runtime-known.
   9800         maybe_comptime: bool,
   9801         /// Track whether the name has been referenced.
   9802         used: bool = false,
   9803     };
   9804 
   9805     const Defer = struct {
   9806         base: Scope,
   9807         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
   9808         parent: *Scope,
   9809         defer_node: Ast.Node.Index,
   9810         source_offset: u32,
   9811         source_line: u32,
   9812         source_column: u32,
   9813     };
   9814 
   9815     /// Represents a global scope that has any number of declarations in it.
   9816     /// Each declaration has this as the parent scope.
   9817     const Namespace = struct {
   9818         const base_tag: Tag = .namespace;
   9819         base: Scope = Scope{ .tag = base_tag },
   9820 
   9821         /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
   9822         parent: *Scope,
   9823         /// Maps string table index to the source location of declaration,
   9824         /// for the purposes of reporting name shadowing compile errors.
   9825         decls: std.AutoHashMapUnmanaged(u32, Ast.Node.Index) = .{},
   9826         node: Ast.Node.Index,
   9827         inst: Zir.Inst.Index,
   9828 
   9829         /// The astgen scope containing this namespace.
   9830         /// Only valid during astgen.
   9831         declaring_gz: ?*GenZir,
   9832 
   9833         /// Map from the raw captured value to the instruction
   9834         /// ref of the capture for decls in this namespace
   9835         captures: std.AutoArrayHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},
   9836 
   9837         pub fn deinit(self: *Namespace, gpa: Allocator) void {
   9838             self.decls.deinit(gpa);
   9839             self.captures.deinit(gpa);
   9840             self.* = undefined;
   9841         }
   9842     };
   9843 
   9844     const Top = struct {
   9845         const base_tag: Scope.Tag = .top;
   9846         base: Scope = Scope{ .tag = base_tag },
   9847     };
   9848 };
   9849 
   9850 /// This is a temporary structure; references to it are valid only
   9851 /// while constructing a `Zir`.
   9852 const GenZir = struct {
   9853     const base_tag: Scope.Tag = .gen_zir;
   9854     base: Scope = Scope{ .tag = base_tag },
   9855     force_comptime: bool,
   9856     /// This is set to true for inline loops; false otherwise.
   9857     is_inline: bool = false,
   9858     in_defer: bool,
   9859     c_import: bool = false,
   9860     /// How decls created in this scope should be named.
   9861     anon_name_strategy: Zir.Inst.NameStrategy = .anon,
   9862     /// The containing decl AST node.
   9863     decl_node_index: Ast.Node.Index,
   9864     /// The containing decl line index, absolute.
   9865     decl_line: u32,
   9866     /// Parents can be: `LocalVal`, `LocalPtr`, `GenZir`, `Defer`, `Namespace`.
   9867     parent: *Scope,
   9868     /// All `GenZir` scopes for the same ZIR share this.
   9869     astgen: *AstGen,
   9870     /// Keeps track of the list of instructions in this scope. Possibly shared.
   9871     /// Indexes to instructions in `astgen`.
   9872     instructions: *ArrayListUnmanaged(Zir.Inst.Index),
   9873     /// A sub-block may share its instructions ArrayList with containing GenZir,
   9874     /// if use is strictly nested. This saves prior size of list for unstacking.
   9875     instructions_top: usize,
   9876     label: ?Label = null,
   9877     break_block: Zir.Inst.Index = 0,
   9878     continue_block: Zir.Inst.Index = 0,
   9879     /// Only valid when setBreakResultLoc is called.
   9880     break_result_loc: AstGen.ResultLoc = undefined,
   9881     /// When a block has a pointer result location, here it is.
   9882     rl_ptr: Zir.Inst.Ref = .none,
   9883     /// When a block has a type result location, here it is.
   9884     rl_ty_inst: Zir.Inst.Ref = .none,
   9885     rvalue_noresult: Zir.Inst.Ref = .none,
   9886     /// Keeps track of how many branches of a block did not actually
   9887     /// consume the result location. astgen uses this to figure out
   9888     /// whether to rely on break instructions or writing to the result
   9889     /// pointer for the result instruction.
   9890     rvalue_rl_count: usize = 0,
   9891     /// Keeps track of how many break instructions there are. When astgen is finished
   9892     /// with a block, it can check this against rvalue_rl_count to find out whether
   9893     /// the break instructions should be downgraded to break_void.
   9894     break_count: usize = 0,
   9895     /// Tracks `break :foo bar` instructions so they can possibly be elided later if
   9896     /// the labeled block ends up not needing a result location pointer.
   9897     labeled_breaks: ArrayListUnmanaged(struct { br: Zir.Inst.Index, search: Zir.Inst.Index }) = .{},
   9898 
   9899     suspend_node: Ast.Node.Index = 0,
   9900     nosuspend_node: Ast.Node.Index = 0,
   9901 
   9902     /// Namespace members are lazy.  When executing a decl within a namespace,
   9903     /// any references to external instructions need to be treated specially.
   9904     /// This list tracks those references.  See also .closure_capture and .closure_get.
   9905     /// Keys are the raw instruction index, values are the closure_capture instruction.
   9906     captures: std.AutoHashMapUnmanaged(Zir.Inst.Index, Zir.Inst.Index) = .{},
   9907 
   9908     const unstacked_top = std.math.maxInt(usize);
   9909     /// Call unstack before adding any new instructions to containing GenZir.
   9910     fn unstack(self: *GenZir) void {
   9911         if (self.instructions_top != unstacked_top) {
   9912             self.instructions.items.len = self.instructions_top;
   9913             self.instructions_top = unstacked_top;
   9914         }
   9915     }
   9916 
   9917     fn isEmpty(self: *const GenZir) bool {
   9918         return (self.instructions_top == unstacked_top) or
   9919             (self.instructions.items.len == self.instructions_top);
   9920     }
   9921 
   9922     fn instructionsSlice(self: *const GenZir) []Zir.Inst.Index {
   9923         return if (self.instructions_top == unstacked_top)
   9924             &[0]Zir.Inst.Index{}
   9925         else
   9926             self.instructions.items[self.instructions_top..];
   9927     }
   9928 
   9929     fn instructionsSliceUpto(self: *const GenZir, stacked_gz: *GenZir) []Zir.Inst.Index {
   9930         return if (self.instructions_top == unstacked_top)
   9931             &[0]Zir.Inst.Index{}
   9932         else if (self.instructions == stacked_gz.instructions and stacked_gz.instructions_top != unstacked_top)
   9933             self.instructions.items[self.instructions_top..stacked_gz.instructions_top]
   9934         else
   9935             self.instructions.items[self.instructions_top..];
   9936     }
   9937 
   9938     fn makeSubBlock(gz: *GenZir, scope: *Scope) GenZir {
   9939         return .{
   9940             .force_comptime = gz.force_comptime,
   9941             .in_defer = gz.in_defer,
   9942             .c_import = gz.c_import,
   9943             .decl_node_index = gz.decl_node_index,
   9944             .decl_line = gz.decl_line,
   9945             .parent = scope,
   9946             .rl_ty_inst = gz.rl_ty_inst,
   9947             .astgen = gz.astgen,
   9948             .suspend_node = gz.suspend_node,
   9949             .nosuspend_node = gz.nosuspend_node,
   9950             .instructions = gz.instructions,
   9951             .instructions_top = gz.instructions.items.len,
   9952         };
   9953     }
   9954 
   9955     fn makeCoercionScope(
   9956         parent_gz: *GenZir,
   9957         scope: *Scope,
   9958         dest_type: Zir.Inst.Ref,
   9959         result_ptr: Zir.Inst.Ref,
   9960         src_node: Ast.Node.Index,
   9961     ) !GenZir {
   9962         // Detect whether this expr() call goes into rvalue() to store the result into the
   9963         // result location. If it does, elide the coerce_result_ptr instruction
   9964         // as well as the store instruction, instead passing the result as an rvalue.
   9965         var as_scope = parent_gz.makeSubBlock(scope);
   9966         errdefer as_scope.unstack();
   9967         as_scope.rl_ptr = try as_scope.addPlNode(.coerce_result_ptr, src_node, Zir.Inst.Bin{ .lhs = dest_type, .rhs = result_ptr });
   9968 
   9969         // `rl_ty_inst` needs to be set in case the stores to `rl_ptr` are eliminated.
   9970         as_scope.rl_ty_inst = dest_type;
   9971 
   9972         return as_scope;
   9973     }
   9974 
   9975     /// Assumes `as_scope` is stacked immediately on top of `parent_gz`. Unstacks `as_scope`.
   9976     fn finishCoercion(
   9977         as_scope: *GenZir,
   9978         parent_gz: *GenZir,
   9979         rl: ResultLoc,
   9980         src_node: Ast.Node.Index,
   9981         result: Zir.Inst.Ref,
   9982         dest_type: Zir.Inst.Ref,
   9983     ) InnerError!Zir.Inst.Ref {
   9984         assert(as_scope.instructions == parent_gz.instructions);
   9985         const astgen = as_scope.astgen;
   9986         if (as_scope.rvalue_rl_count == 1) {
   9987             // Busted! This expression didn't actually need a pointer.
   9988             const zir_tags = astgen.instructions.items(.tag);
   9989             const zir_datas = astgen.instructions.items(.data);
   9990             var src: usize = as_scope.instructions_top;
   9991             var dst: usize = src;
   9992             while (src < as_scope.instructions.items.len) : (src += 1) {
   9993                 const src_inst = as_scope.instructions.items[src];
   9994                 if (indexToRef(src_inst) == as_scope.rl_ptr) continue;
   9995                 if (zir_tags[src_inst] == .store_to_block_ptr) {
   9996                     if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue;
   9997                 }
   9998                 as_scope.instructions.items[dst] = src_inst;
   9999                 dst += 1;
  10000             }
  10001             parent_gz.instructions.items.len -= src - dst;
  10002             as_scope.instructions_top = GenZir.unstacked_top;
  10003             // as_scope now unstacked, can add new instructions to parent_gz
  10004             const casted_result = try parent_gz.addBin(.as, dest_type, result);
  10005             return rvalue(parent_gz, rl, casted_result, src_node);
  10006         } else {
  10007             // implicitly move all as_scope instructions to parent_gz
  10008             as_scope.instructions_top = GenZir.unstacked_top;
  10009             return result;
  10010         }
  10011     }
  10012 
  10013     const Label = struct {
  10014         token: Ast.TokenIndex,
  10015         block_inst: Zir.Inst.Index,
  10016         used: bool = false,
  10017     };
  10018 
  10019     /// Assumes nothing stacked on `gz`.
  10020     fn endsWithNoReturn(gz: GenZir) bool {
  10021         if (gz.isEmpty()) return false;
  10022         const tags = gz.astgen.instructions.items(.tag);
  10023         const last_inst = gz.instructions.items[gz.instructions.items.len - 1];
  10024         return tags[last_inst].isNoReturn();
  10025     }
  10026 
  10027     /// TODO all uses of this should be replaced with uses of `endsWithNoReturn`.
  10028     fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool {
  10029         if (inst_ref == .unreachable_value) return true;
  10030         if (refToIndex(inst_ref)) |inst_index| {
  10031             return gz.astgen.instructions.items(.tag)[inst_index].isNoReturn();
  10032         }
  10033         return false;
  10034     }
  10035 
  10036     fn nodeIndexToRelative(gz: GenZir, node_index: Ast.Node.Index) i32 {
  10037         return @bitCast(i32, node_index) - @bitCast(i32, gz.decl_node_index);
  10038     }
  10039 
  10040     fn tokenIndexToRelative(gz: GenZir, token: Ast.TokenIndex) u32 {
  10041         return token - gz.srcToken();
  10042     }
  10043 
  10044     fn srcToken(gz: GenZir) Ast.TokenIndex {
  10045         return gz.astgen.tree.firstToken(gz.decl_node_index);
  10046     }
  10047 
  10048     fn setBreakResultLoc(gz: *GenZir, parent_rl: AstGen.ResultLoc) void {
  10049         // Depending on whether the result location is a pointer or value, different
  10050         // ZIR needs to be generated. In the former case we rely on storing to the
  10051         // pointer to communicate the result, and use breakvoid; in the latter case
  10052         // the block break instructions will have the result values.
  10053         // One more complication: when the result location is a pointer, we detect
  10054         // the scenario where the result location is not consumed. In this case
  10055         // we emit ZIR for the block break instructions to have the result values,
  10056         // and then rvalue() on that to pass the value to the result location.
  10057         switch (parent_rl) {
  10058             .ty, .coerced_ty => |ty_inst| {
  10059                 gz.rl_ty_inst = ty_inst;
  10060                 gz.break_result_loc = parent_rl;
  10061             },
  10062 
  10063             .discard, .none, .ptr, .ref => {
  10064                 gz.rl_ty_inst = .none;
  10065                 gz.break_result_loc = parent_rl;
  10066             },
  10067 
  10068             .inferred_ptr => |ptr| {
  10069                 gz.rl_ty_inst = .none;
  10070                 gz.rl_ptr = ptr;
  10071                 gz.break_result_loc = .{ .block_ptr = gz };
  10072             },
  10073 
  10074             .block_ptr => |parent_block_scope| {
  10075                 gz.rl_ty_inst = parent_block_scope.rl_ty_inst;
  10076                 gz.rl_ptr = parent_block_scope.rl_ptr;
  10077                 gz.break_result_loc = .{ .block_ptr = gz };
  10078             },
  10079         }
  10080     }
  10081 
  10082     /// Assumes nothing stacked on `gz`. Unstacks `gz`.
  10083     fn setBoolBrBody(gz: *GenZir, inst: Zir.Inst.Index) !void {
  10084         const astgen = gz.astgen;
  10085         const gpa = astgen.gpa;
  10086         const body = gz.instructionsSlice();
  10087         const body_len = astgen.countBodyLenAfterFixups(body);
  10088         try astgen.extra.ensureUnusedCapacity(
  10089             gpa,
  10090             @typeInfo(Zir.Inst.Block).Struct.fields.len + body_len,
  10091         );
  10092         const zir_datas = astgen.instructions.items(.data);
  10093         zir_datas[inst].bool_br.payload_index = astgen.addExtraAssumeCapacity(
  10094             Zir.Inst.Block{ .body_len = body_len },
  10095         );
  10096         astgen.appendBodyWithFixups(body);
  10097         gz.unstack();
  10098     }
  10099 
  10100     /// Assumes nothing stacked on `gz`. Unstacks `gz`.
  10101     fn setBlockBody(gz: *GenZir, inst: Zir.Inst.Index) !void {
  10102         const astgen = gz.astgen;
  10103         const gpa = astgen.gpa;
  10104         const body = gz.instructionsSlice();
  10105         const body_len = astgen.countBodyLenAfterFixups(body);
  10106         try astgen.extra.ensureUnusedCapacity(
  10107             gpa,
  10108             @typeInfo(Zir.Inst.Block).Struct.fields.len + body_len,
  10109         );
  10110         const zir_datas = astgen.instructions.items(.data);
  10111         zir_datas[inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(
  10112             Zir.Inst.Block{ .body_len = body_len },
  10113         );
  10114         astgen.appendBodyWithFixups(body);
  10115         gz.unstack();
  10116     }
  10117 
  10118     /// Assumes nothing stacked on `gz`. Unstacks `gz`.
  10119     fn setTryBody(gz: *GenZir, inst: Zir.Inst.Index, operand: Zir.Inst.Ref) !void {
  10120         const astgen = gz.astgen;
  10121         const gpa = astgen.gpa;
  10122         const body = gz.instructionsSlice();
  10123         const body_len = astgen.countBodyLenAfterFixups(body);
  10124         try astgen.extra.ensureUnusedCapacity(
  10125             gpa,
  10126             @typeInfo(Zir.Inst.Try).Struct.fields.len + body_len,
  10127         );
  10128         const zir_datas = astgen.instructions.items(.data);
  10129         zir_datas[inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(
  10130             Zir.Inst.Try{
  10131                 .operand = operand,
  10132                 .body_len = body_len,
  10133             },
  10134         );
  10135         astgen.appendBodyWithFixups(body);
  10136         gz.unstack();
  10137     }
  10138 
  10139     /// Must be called with the following stack set up:
  10140     ///  * gz (bottom)
  10141     ///  * align_gz
  10142     ///  * addrspace_gz
  10143     ///  * section_gz
  10144     ///  * cc_gz
  10145     ///  * ret_gz
  10146     ///  * body_gz (top)
  10147     /// Unstacks all of those except for `gz`.
  10148     fn addFunc(gz: *GenZir, args: struct {
  10149         src_node: Ast.Node.Index,
  10150         lbrace_line: u32 = 0,
  10151         lbrace_column: u32 = 0,
  10152         param_block: Zir.Inst.Index,
  10153 
  10154         align_gz: ?*GenZir,
  10155         addrspace_gz: ?*GenZir,
  10156         section_gz: ?*GenZir,
  10157         cc_gz: ?*GenZir,
  10158         ret_gz: ?*GenZir,
  10159         body_gz: ?*GenZir,
  10160 
  10161         align_ref: Zir.Inst.Ref,
  10162         addrspace_ref: Zir.Inst.Ref,
  10163         section_ref: Zir.Inst.Ref,
  10164         cc_ref: Zir.Inst.Ref,
  10165         ret_ref: Zir.Inst.Ref,
  10166 
  10167         lib_name: u32,
  10168         noalias_bits: u32,
  10169         is_var_args: bool,
  10170         is_inferred_error: bool,
  10171         is_test: bool,
  10172         is_extern: bool,
  10173     }) !Zir.Inst.Ref {
  10174         assert(args.src_node != 0);
  10175         const astgen = gz.astgen;
  10176         const gpa = astgen.gpa;
  10177         const ret_ref = if (args.ret_ref == .void_type) .none else args.ret_ref;
  10178         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  10179 
  10180         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10181 
  10182         var body: []Zir.Inst.Index = &[0]Zir.Inst.Index{};
  10183         var ret_body: []Zir.Inst.Index = &[0]Zir.Inst.Index{};
  10184         var src_locs_buffer: [3]u32 = undefined;
  10185         var src_locs: []u32 = src_locs_buffer[0..0];
  10186         if (args.body_gz) |body_gz| {
  10187             const tree = astgen.tree;
  10188             const node_tags = tree.nodes.items(.tag);
  10189             const node_datas = tree.nodes.items(.data);
  10190             const token_starts = tree.tokens.items(.start);
  10191             const fn_decl = args.src_node;
  10192             assert(node_tags[fn_decl] == .fn_decl or node_tags[fn_decl] == .test_decl);
  10193             const block = node_datas[fn_decl].rhs;
  10194             const rbrace_start = token_starts[tree.lastToken(block)];
  10195             astgen.advanceSourceCursor(rbrace_start);
  10196             const rbrace_line = @intCast(u32, astgen.source_line - gz.decl_line);
  10197             const rbrace_column = @intCast(u32, astgen.source_column);
  10198 
  10199             const columns = args.lbrace_column | (rbrace_column << 16);
  10200             src_locs_buffer[0] = args.lbrace_line;
  10201             src_locs_buffer[1] = rbrace_line;
  10202             src_locs_buffer[2] = columns;
  10203             src_locs = &src_locs_buffer;
  10204 
  10205             body = body_gz.instructionsSlice();
  10206             if (args.ret_gz) |ret_gz|
  10207                 ret_body = ret_gz.instructionsSliceUpto(body_gz);
  10208         } else {
  10209             if (args.ret_gz) |ret_gz|
  10210                 ret_body = ret_gz.instructionsSlice();
  10211         }
  10212         const body_len = astgen.countBodyLenAfterFixups(body);
  10213 
  10214         if (args.cc_ref != .none or args.lib_name != 0 or
  10215             args.is_var_args or args.is_test or args.is_extern or
  10216             args.align_ref != .none or args.section_ref != .none or
  10217             args.addrspace_ref != .none or args.noalias_bits != 0)
  10218         {
  10219             var align_body: []Zir.Inst.Index = &.{};
  10220             var addrspace_body: []Zir.Inst.Index = &.{};
  10221             var section_body: []Zir.Inst.Index = &.{};
  10222             var cc_body: []Zir.Inst.Index = &.{};
  10223             if (args.ret_gz != null) {
  10224                 align_body = args.align_gz.?.instructionsSliceUpto(args.addrspace_gz.?);
  10225                 addrspace_body = args.addrspace_gz.?.instructionsSliceUpto(args.section_gz.?);
  10226                 section_body = args.section_gz.?.instructionsSliceUpto(args.cc_gz.?);
  10227                 cc_body = args.cc_gz.?.instructionsSliceUpto(args.ret_gz.?);
  10228             }
  10229 
  10230             try astgen.extra.ensureUnusedCapacity(
  10231                 gpa,
  10232                 @typeInfo(Zir.Inst.FuncFancy).Struct.fields.len +
  10233                     fancyFnExprExtraLen(align_body, args.align_ref) +
  10234                     fancyFnExprExtraLen(addrspace_body, args.addrspace_ref) +
  10235                     fancyFnExprExtraLen(section_body, args.section_ref) +
  10236                     fancyFnExprExtraLen(cc_body, args.cc_ref) +
  10237                     fancyFnExprExtraLen(ret_body, ret_ref) +
  10238                     body_len + src_locs.len +
  10239                     @boolToInt(args.lib_name != 0) +
  10240                     @boolToInt(args.noalias_bits != 0),
  10241             );
  10242             const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.FuncFancy{
  10243                 .param_block = args.param_block,
  10244                 .body_len = body_len,
  10245                 .bits = .{
  10246                     .is_var_args = args.is_var_args,
  10247                     .is_inferred_error = args.is_inferred_error,
  10248                     .is_test = args.is_test,
  10249                     .is_extern = args.is_extern,
  10250                     .has_lib_name = args.lib_name != 0,
  10251                     .has_any_noalias = args.noalias_bits != 0,
  10252 
  10253                     .has_align_ref = args.align_ref != .none,
  10254                     .has_addrspace_ref = args.addrspace_ref != .none,
  10255                     .has_section_ref = args.section_ref != .none,
  10256                     .has_cc_ref = args.cc_ref != .none,
  10257                     .has_ret_ty_ref = ret_ref != .none,
  10258 
  10259                     .has_align_body = align_body.len != 0,
  10260                     .has_addrspace_body = addrspace_body.len != 0,
  10261                     .has_section_body = section_body.len != 0,
  10262                     .has_cc_body = cc_body.len != 0,
  10263                     .has_ret_ty_body = ret_body.len != 0,
  10264                 },
  10265             });
  10266             if (args.lib_name != 0) {
  10267                 astgen.extra.appendAssumeCapacity(args.lib_name);
  10268             }
  10269 
  10270             const zir_datas = astgen.instructions.items(.data);
  10271             if (align_body.len != 0) {
  10272                 astgen.extra.appendAssumeCapacity(@intCast(u32, align_body.len));
  10273                 astgen.extra.appendSliceAssumeCapacity(align_body);
  10274                 zir_datas[align_body[align_body.len - 1]].@"break".block_inst = new_index;
  10275             } else if (args.align_ref != .none) {
  10276                 astgen.extra.appendAssumeCapacity(@enumToInt(args.align_ref));
  10277             }
  10278             if (addrspace_body.len != 0) {
  10279                 astgen.extra.appendAssumeCapacity(@intCast(u32, addrspace_body.len));
  10280                 astgen.extra.appendSliceAssumeCapacity(addrspace_body);
  10281                 zir_datas[addrspace_body[addrspace_body.len - 1]].@"break".block_inst = new_index;
  10282             } else if (args.addrspace_ref != .none) {
  10283                 astgen.extra.appendAssumeCapacity(@enumToInt(args.addrspace_ref));
  10284             }
  10285             if (section_body.len != 0) {
  10286                 astgen.extra.appendAssumeCapacity(@intCast(u32, section_body.len));
  10287                 astgen.extra.appendSliceAssumeCapacity(section_body);
  10288                 zir_datas[section_body[section_body.len - 1]].@"break".block_inst = new_index;
  10289             } else if (args.section_ref != .none) {
  10290                 astgen.extra.appendAssumeCapacity(@enumToInt(args.section_ref));
  10291             }
  10292             if (cc_body.len != 0) {
  10293                 astgen.extra.appendAssumeCapacity(@intCast(u32, cc_body.len));
  10294                 astgen.extra.appendSliceAssumeCapacity(cc_body);
  10295                 zir_datas[cc_body[cc_body.len - 1]].@"break".block_inst = new_index;
  10296             } else if (args.cc_ref != .none) {
  10297                 astgen.extra.appendAssumeCapacity(@enumToInt(args.cc_ref));
  10298             }
  10299             if (ret_body.len != 0) {
  10300                 astgen.extra.appendAssumeCapacity(@intCast(u32, ret_body.len));
  10301                 astgen.extra.appendSliceAssumeCapacity(ret_body);
  10302                 zir_datas[ret_body[ret_body.len - 1]].@"break".block_inst = new_index;
  10303             } else if (ret_ref != .none) {
  10304                 astgen.extra.appendAssumeCapacity(@enumToInt(ret_ref));
  10305             }
  10306 
  10307             if (args.noalias_bits != 0) {
  10308                 astgen.extra.appendAssumeCapacity(args.noalias_bits);
  10309             }
  10310 
  10311             astgen.appendBodyWithFixups(body);
  10312             astgen.extra.appendSliceAssumeCapacity(src_locs);
  10313 
  10314             // Order is important when unstacking.
  10315             if (args.body_gz) |body_gz| body_gz.unstack();
  10316             if (args.ret_gz != null) {
  10317                 args.ret_gz.?.unstack();
  10318                 args.cc_gz.?.unstack();
  10319                 args.section_gz.?.unstack();
  10320                 args.addrspace_gz.?.unstack();
  10321                 args.align_gz.?.unstack();
  10322             }
  10323 
  10324             try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10325 
  10326             astgen.instructions.appendAssumeCapacity(.{
  10327                 .tag = .func_fancy,
  10328                 .data = .{ .pl_node = .{
  10329                     .src_node = gz.nodeIndexToRelative(args.src_node),
  10330                     .payload_index = payload_index,
  10331                 } },
  10332             });
  10333             gz.instructions.appendAssumeCapacity(new_index);
  10334             return indexToRef(new_index);
  10335         } else {
  10336             try astgen.extra.ensureUnusedCapacity(
  10337                 gpa,
  10338                 @typeInfo(Zir.Inst.Func).Struct.fields.len + 1 +
  10339                     @maximum(ret_body.len, @boolToInt(ret_ref != .none)) +
  10340                     body_len + src_locs.len,
  10341             );
  10342             const ret_body_len = if (ret_body.len != 0)
  10343                 @intCast(u32, ret_body.len)
  10344             else
  10345                 @boolToInt(ret_ref != .none);
  10346 
  10347             const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.Func{
  10348                 .param_block = args.param_block,
  10349                 .ret_body_len = ret_body_len,
  10350                 .body_len = body_len,
  10351             });
  10352             const zir_datas = astgen.instructions.items(.data);
  10353             if (ret_body.len != 0) {
  10354                 astgen.extra.appendSliceAssumeCapacity(ret_body);
  10355                 zir_datas[ret_body[ret_body.len - 1]].@"break".block_inst = new_index;
  10356             } else if (ret_ref != .none) {
  10357                 astgen.extra.appendAssumeCapacity(@enumToInt(ret_ref));
  10358             }
  10359             astgen.appendBodyWithFixups(body);
  10360             astgen.extra.appendSliceAssumeCapacity(src_locs);
  10361 
  10362             // Order is important when unstacking.
  10363             if (args.body_gz) |body_gz| body_gz.unstack();
  10364             if (args.ret_gz) |ret_gz| ret_gz.unstack();
  10365             if (args.cc_gz) |cc_gz| cc_gz.unstack();
  10366             if (args.section_gz) |section_gz| section_gz.unstack();
  10367             if (args.addrspace_gz) |addrspace_gz| addrspace_gz.unstack();
  10368             if (args.align_gz) |align_gz| align_gz.unstack();
  10369 
  10370             try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10371 
  10372             const tag: Zir.Inst.Tag = if (args.is_inferred_error) .func_inferred else .func;
  10373             astgen.instructions.appendAssumeCapacity(.{
  10374                 .tag = tag,
  10375                 .data = .{ .pl_node = .{
  10376                     .src_node = gz.nodeIndexToRelative(args.src_node),
  10377                     .payload_index = payload_index,
  10378                 } },
  10379             });
  10380             gz.instructions.appendAssumeCapacity(new_index);
  10381             return indexToRef(new_index);
  10382         }
  10383     }
  10384 
  10385     fn fancyFnExprExtraLen(body: []Zir.Inst.Index, ref: Zir.Inst.Ref) usize {
  10386         // In the case of non-empty body, there is one for the body length,
  10387         // and then one for each instruction.
  10388         return body.len + @boolToInt(ref != .none);
  10389     }
  10390 
  10391     fn addVar(gz: *GenZir, args: struct {
  10392         align_inst: Zir.Inst.Ref,
  10393         lib_name: u32,
  10394         var_type: Zir.Inst.Ref,
  10395         init: Zir.Inst.Ref,
  10396         is_extern: bool,
  10397         is_threadlocal: bool,
  10398     }) !Zir.Inst.Ref {
  10399         const astgen = gz.astgen;
  10400         const gpa = astgen.gpa;
  10401 
  10402         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10403         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10404 
  10405         try astgen.extra.ensureUnusedCapacity(
  10406             gpa,
  10407             @typeInfo(Zir.Inst.ExtendedVar).Struct.fields.len +
  10408                 @boolToInt(args.lib_name != 0) +
  10409                 @boolToInt(args.align_inst != .none) +
  10410                 @boolToInt(args.init != .none),
  10411         );
  10412         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.ExtendedVar{
  10413             .var_type = args.var_type,
  10414         });
  10415         if (args.lib_name != 0) {
  10416             astgen.extra.appendAssumeCapacity(args.lib_name);
  10417         }
  10418         if (args.align_inst != .none) {
  10419             astgen.extra.appendAssumeCapacity(@enumToInt(args.align_inst));
  10420         }
  10421         if (args.init != .none) {
  10422             astgen.extra.appendAssumeCapacity(@enumToInt(args.init));
  10423         }
  10424 
  10425         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  10426         astgen.instructions.appendAssumeCapacity(.{
  10427             .tag = .extended,
  10428             .data = .{ .extended = .{
  10429                 .opcode = .variable,
  10430                 .small = @bitCast(u16, Zir.Inst.ExtendedVar.Small{
  10431                     .has_lib_name = args.lib_name != 0,
  10432                     .has_align = args.align_inst != .none,
  10433                     .has_init = args.init != .none,
  10434                     .is_extern = args.is_extern,
  10435                     .is_threadlocal = args.is_threadlocal,
  10436                 }),
  10437                 .operand = payload_index,
  10438             } },
  10439         });
  10440         gz.instructions.appendAssumeCapacity(new_index);
  10441         return indexToRef(new_index);
  10442     }
  10443 
  10444     /// Note that this returns a `Zir.Inst.Index` not a ref.
  10445     /// Leaves the `payload_index` field undefined.
  10446     fn addBoolBr(
  10447         gz: *GenZir,
  10448         tag: Zir.Inst.Tag,
  10449         lhs: Zir.Inst.Ref,
  10450     ) !Zir.Inst.Index {
  10451         assert(lhs != .none);
  10452         const gpa = gz.astgen.gpa;
  10453         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10454         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10455 
  10456         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  10457         gz.astgen.instructions.appendAssumeCapacity(.{
  10458             .tag = tag,
  10459             .data = .{ .bool_br = .{
  10460                 .lhs = lhs,
  10461                 .payload_index = undefined,
  10462             } },
  10463         });
  10464         gz.instructions.appendAssumeCapacity(new_index);
  10465         return new_index;
  10466     }
  10467 
  10468     fn addInt(gz: *GenZir, integer: u64) !Zir.Inst.Ref {
  10469         return gz.add(.{
  10470             .tag = .int,
  10471             .data = .{ .int = integer },
  10472         });
  10473     }
  10474 
  10475     fn addIntBig(gz: *GenZir, limbs: []const std.math.big.Limb) !Zir.Inst.Ref {
  10476         const astgen = gz.astgen;
  10477         const gpa = astgen.gpa;
  10478         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10479         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10480         try astgen.string_bytes.ensureUnusedCapacity(gpa, @sizeOf(std.math.big.Limb) * limbs.len);
  10481 
  10482         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  10483         astgen.instructions.appendAssumeCapacity(.{
  10484             .tag = .int_big,
  10485             .data = .{ .str = .{
  10486                 .start = @intCast(u32, astgen.string_bytes.items.len),
  10487                 .len = @intCast(u32, limbs.len),
  10488             } },
  10489         });
  10490         gz.instructions.appendAssumeCapacity(new_index);
  10491         astgen.string_bytes.appendSliceAssumeCapacity(mem.sliceAsBytes(limbs));
  10492         return indexToRef(new_index);
  10493     }
  10494 
  10495     fn addFloat(gz: *GenZir, number: f64) !Zir.Inst.Ref {
  10496         return gz.add(.{
  10497             .tag = .float,
  10498             .data = .{ .float = number },
  10499         });
  10500     }
  10501 
  10502     fn addUnNode(
  10503         gz: *GenZir,
  10504         tag: Zir.Inst.Tag,
  10505         operand: Zir.Inst.Ref,
  10506         /// Absolute node index. This function does the conversion to offset from Decl.
  10507         src_node: Ast.Node.Index,
  10508     ) !Zir.Inst.Ref {
  10509         assert(operand != .none);
  10510         return gz.add(.{
  10511             .tag = tag,
  10512             .data = .{ .un_node = .{
  10513                 .operand = operand,
  10514                 .src_node = gz.nodeIndexToRelative(src_node),
  10515             } },
  10516         });
  10517     }
  10518 
  10519     fn makeUnNode(
  10520         gz: *GenZir,
  10521         tag: Zir.Inst.Tag,
  10522         operand: Zir.Inst.Ref,
  10523         /// Absolute node index. This function does the conversion to offset from Decl.
  10524         src_node: Ast.Node.Index,
  10525     ) !Zir.Inst.Index {
  10526         assert(operand != .none);
  10527         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  10528         try gz.astgen.instructions.append(gz.astgen.gpa, .{
  10529             .tag = tag,
  10530             .data = .{ .un_node = .{
  10531                 .operand = operand,
  10532                 .src_node = gz.nodeIndexToRelative(src_node),
  10533             } },
  10534         });
  10535         return new_index;
  10536     }
  10537 
  10538     fn addPlNode(
  10539         gz: *GenZir,
  10540         tag: Zir.Inst.Tag,
  10541         /// Absolute node index. This function does the conversion to offset from Decl.
  10542         src_node: Ast.Node.Index,
  10543         extra: anytype,
  10544     ) !Zir.Inst.Ref {
  10545         const gpa = gz.astgen.gpa;
  10546         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10547         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10548 
  10549         const payload_index = try gz.astgen.addExtra(extra);
  10550         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  10551         gz.astgen.instructions.appendAssumeCapacity(.{
  10552             .tag = tag,
  10553             .data = .{ .pl_node = .{
  10554                 .src_node = gz.nodeIndexToRelative(src_node),
  10555                 .payload_index = payload_index,
  10556             } },
  10557         });
  10558         gz.instructions.appendAssumeCapacity(new_index);
  10559         return indexToRef(new_index);
  10560     }
  10561 
  10562     fn addPlNodePayloadIndex(
  10563         gz: *GenZir,
  10564         tag: Zir.Inst.Tag,
  10565         /// Absolute node index. This function does the conversion to offset from Decl.
  10566         src_node: Ast.Node.Index,
  10567         payload_index: u32,
  10568     ) !Zir.Inst.Ref {
  10569         return try gz.add(.{
  10570             .tag = tag,
  10571             .data = .{ .pl_node = .{
  10572                 .src_node = gz.nodeIndexToRelative(src_node),
  10573                 .payload_index = payload_index,
  10574             } },
  10575         });
  10576     }
  10577 
  10578     /// Supports `param_gz` stacked on `gz`. Assumes nothing stacked on `param_gz`. Unstacks `param_gz`.
  10579     fn addParam(
  10580         gz: *GenZir,
  10581         param_gz: *GenZir,
  10582         tag: Zir.Inst.Tag,
  10583         /// Absolute token index. This function does the conversion to Decl offset.
  10584         abs_tok_index: Ast.TokenIndex,
  10585         name: u32,
  10586         first_doc_comment: ?Ast.TokenIndex,
  10587     ) !Zir.Inst.Index {
  10588         const gpa = gz.astgen.gpa;
  10589         const param_body = param_gz.instructionsSlice();
  10590         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10591         try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Param).Struct.fields.len +
  10592             param_body.len);
  10593 
  10594         const doc_comment_index = if (first_doc_comment) |first|
  10595             try gz.astgen.docCommentAsStringFromFirst(abs_tok_index, first)
  10596         else
  10597             0;
  10598 
  10599         const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Param{
  10600             .name = name,
  10601             .doc_comment = doc_comment_index,
  10602             .body_len = @intCast(u32, param_body.len),
  10603         });
  10604         gz.astgen.extra.appendSliceAssumeCapacity(param_body);
  10605         param_gz.unstack();
  10606 
  10607         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  10608         gz.astgen.instructions.appendAssumeCapacity(.{
  10609             .tag = tag,
  10610             .data = .{ .pl_tok = .{
  10611                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  10612                 .payload_index = payload_index,
  10613             } },
  10614         });
  10615         gz.instructions.appendAssumeCapacity(new_index);
  10616         return new_index;
  10617     }
  10618 
  10619     fn addExtendedPayload(
  10620         gz: *GenZir,
  10621         opcode: Zir.Inst.Extended,
  10622         extra: anytype,
  10623     ) !Zir.Inst.Ref {
  10624         const gpa = gz.astgen.gpa;
  10625 
  10626         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10627         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10628 
  10629         const payload_index = try gz.astgen.addExtra(extra);
  10630         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  10631         gz.astgen.instructions.appendAssumeCapacity(.{
  10632             .tag = .extended,
  10633             .data = .{ .extended = .{
  10634                 .opcode = opcode,
  10635                 .small = undefined,
  10636                 .operand = payload_index,
  10637             } },
  10638         });
  10639         gz.instructions.appendAssumeCapacity(new_index);
  10640         return indexToRef(new_index);
  10641     }
  10642 
  10643     fn addExtendedMultiOp(
  10644         gz: *GenZir,
  10645         opcode: Zir.Inst.Extended,
  10646         node: Ast.Node.Index,
  10647         operands: []const Zir.Inst.Ref,
  10648     ) !Zir.Inst.Ref {
  10649         const astgen = gz.astgen;
  10650         const gpa = astgen.gpa;
  10651 
  10652         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10653         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10654         try astgen.extra.ensureUnusedCapacity(
  10655             gpa,
  10656             @typeInfo(Zir.Inst.NodeMultiOp).Struct.fields.len + operands.len,
  10657         );
  10658 
  10659         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.NodeMultiOp{
  10660             .src_node = gz.nodeIndexToRelative(node),
  10661         });
  10662         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  10663         astgen.instructions.appendAssumeCapacity(.{
  10664             .tag = .extended,
  10665             .data = .{ .extended = .{
  10666                 .opcode = opcode,
  10667                 .small = @intCast(u16, operands.len),
  10668                 .operand = payload_index,
  10669             } },
  10670         });
  10671         gz.instructions.appendAssumeCapacity(new_index);
  10672         astgen.appendRefsAssumeCapacity(operands);
  10673         return indexToRef(new_index);
  10674     }
  10675 
  10676     fn addExtendedMultiOpPayloadIndex(
  10677         gz: *GenZir,
  10678         opcode: Zir.Inst.Extended,
  10679         payload_index: u32,
  10680         trailing_len: usize,
  10681     ) !Zir.Inst.Ref {
  10682         const astgen = gz.astgen;
  10683         const gpa = astgen.gpa;
  10684 
  10685         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10686         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10687         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  10688         astgen.instructions.appendAssumeCapacity(.{
  10689             .tag = .extended,
  10690             .data = .{ .extended = .{
  10691                 .opcode = opcode,
  10692                 .small = @intCast(u16, trailing_len),
  10693                 .operand = payload_index,
  10694             } },
  10695         });
  10696         gz.instructions.appendAssumeCapacity(new_index);
  10697         return indexToRef(new_index);
  10698     }
  10699 
  10700     fn addUnTok(
  10701         gz: *GenZir,
  10702         tag: Zir.Inst.Tag,
  10703         operand: Zir.Inst.Ref,
  10704         /// Absolute token index. This function does the conversion to Decl offset.
  10705         abs_tok_index: Ast.TokenIndex,
  10706     ) !Zir.Inst.Ref {
  10707         assert(operand != .none);
  10708         return gz.add(.{
  10709             .tag = tag,
  10710             .data = .{ .un_tok = .{
  10711                 .operand = operand,
  10712                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  10713             } },
  10714         });
  10715     }
  10716 
  10717     fn makeUnTok(
  10718         gz: *GenZir,
  10719         tag: Zir.Inst.Tag,
  10720         operand: Zir.Inst.Ref,
  10721         /// Absolute token index. This function does the conversion to Decl offset.
  10722         abs_tok_index: Ast.TokenIndex,
  10723     ) !Zir.Inst.Index {
  10724         const astgen = gz.astgen;
  10725         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  10726         assert(operand != .none);
  10727         try astgen.instructions.append(astgen.gpa, .{
  10728             .tag = tag,
  10729             .data = .{ .un_tok = .{
  10730                 .operand = operand,
  10731                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  10732             } },
  10733         });
  10734         return new_index;
  10735     }
  10736 
  10737     fn addStrTok(
  10738         gz: *GenZir,
  10739         tag: Zir.Inst.Tag,
  10740         str_index: u32,
  10741         /// Absolute token index. This function does the conversion to Decl offset.
  10742         abs_tok_index: Ast.TokenIndex,
  10743     ) !Zir.Inst.Ref {
  10744         return gz.add(.{
  10745             .tag = tag,
  10746             .data = .{ .str_tok = .{
  10747                 .start = str_index,
  10748                 .src_tok = gz.tokenIndexToRelative(abs_tok_index),
  10749             } },
  10750         });
  10751     }
  10752 
  10753     fn addBreak(
  10754         gz: *GenZir,
  10755         tag: Zir.Inst.Tag,
  10756         break_block: Zir.Inst.Index,
  10757         operand: Zir.Inst.Ref,
  10758     ) !Zir.Inst.Index {
  10759         return gz.addAsIndex(.{
  10760             .tag = tag,
  10761             .data = .{ .@"break" = .{
  10762                 .block_inst = break_block,
  10763                 .operand = operand,
  10764             } },
  10765         });
  10766     }
  10767 
  10768     fn makeBreak(
  10769         gz: *GenZir,
  10770         tag: Zir.Inst.Tag,
  10771         break_block: Zir.Inst.Index,
  10772         operand: Zir.Inst.Ref,
  10773     ) !Zir.Inst.Index {
  10774         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  10775         try gz.astgen.instructions.append(gz.astgen.gpa, .{
  10776             .tag = tag,
  10777             .data = .{ .@"break" = .{
  10778                 .block_inst = break_block,
  10779                 .operand = operand,
  10780             } },
  10781         });
  10782         return new_index;
  10783     }
  10784 
  10785     fn addBin(
  10786         gz: *GenZir,
  10787         tag: Zir.Inst.Tag,
  10788         lhs: Zir.Inst.Ref,
  10789         rhs: Zir.Inst.Ref,
  10790     ) !Zir.Inst.Ref {
  10791         assert(lhs != .none);
  10792         assert(rhs != .none);
  10793         return gz.add(.{
  10794             .tag = tag,
  10795             .data = .{ .bin = .{
  10796                 .lhs = lhs,
  10797                 .rhs = rhs,
  10798             } },
  10799         });
  10800     }
  10801 
  10802     fn addDecl(
  10803         gz: *GenZir,
  10804         tag: Zir.Inst.Tag,
  10805         decl_index: u32,
  10806         src_node: Ast.Node.Index,
  10807     ) !Zir.Inst.Ref {
  10808         return gz.add(.{
  10809             .tag = tag,
  10810             .data = .{ .pl_node = .{
  10811                 .src_node = gz.nodeIndexToRelative(src_node),
  10812                 .payload_index = decl_index,
  10813             } },
  10814         });
  10815     }
  10816 
  10817     fn addNode(
  10818         gz: *GenZir,
  10819         tag: Zir.Inst.Tag,
  10820         /// Absolute node index. This function does the conversion to offset from Decl.
  10821         src_node: Ast.Node.Index,
  10822     ) !Zir.Inst.Ref {
  10823         return gz.add(.{
  10824             .tag = tag,
  10825             .data = .{ .node = gz.nodeIndexToRelative(src_node) },
  10826         });
  10827     }
  10828 
  10829     fn addInstNode(
  10830         gz: *GenZir,
  10831         tag: Zir.Inst.Tag,
  10832         inst: Zir.Inst.Index,
  10833         /// Absolute node index. This function does the conversion to offset from Decl.
  10834         src_node: Ast.Node.Index,
  10835     ) !Zir.Inst.Ref {
  10836         return gz.add(.{
  10837             .tag = tag,
  10838             .data = .{ .inst_node = .{
  10839                 .inst = inst,
  10840                 .src_node = gz.nodeIndexToRelative(src_node),
  10841             } },
  10842         });
  10843     }
  10844 
  10845     fn addNodeExtended(
  10846         gz: *GenZir,
  10847         opcode: Zir.Inst.Extended,
  10848         /// Absolute node index. This function does the conversion to offset from Decl.
  10849         src_node: Ast.Node.Index,
  10850     ) !Zir.Inst.Ref {
  10851         return gz.add(.{
  10852             .tag = .extended,
  10853             .data = .{ .extended = .{
  10854                 .opcode = opcode,
  10855                 .small = undefined,
  10856                 .operand = @bitCast(u32, gz.nodeIndexToRelative(src_node)),
  10857             } },
  10858         });
  10859     }
  10860 
  10861     fn addAllocExtended(
  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             type_inst: Zir.Inst.Ref,
  10867             align_inst: Zir.Inst.Ref,
  10868             is_const: bool,
  10869             is_comptime: bool,
  10870         },
  10871     ) !Zir.Inst.Ref {
  10872         const astgen = gz.astgen;
  10873         const gpa = astgen.gpa;
  10874 
  10875         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10876         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10877         try astgen.extra.ensureUnusedCapacity(
  10878             gpa,
  10879             @typeInfo(Zir.Inst.AllocExtended).Struct.fields.len +
  10880                 @as(usize, @boolToInt(args.type_inst != .none)) +
  10881                 @as(usize, @boolToInt(args.align_inst != .none)),
  10882         );
  10883         const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.AllocExtended{
  10884             .src_node = gz.nodeIndexToRelative(args.node),
  10885         });
  10886         if (args.type_inst != .none) {
  10887             astgen.extra.appendAssumeCapacity(@enumToInt(args.type_inst));
  10888         }
  10889         if (args.align_inst != .none) {
  10890             astgen.extra.appendAssumeCapacity(@enumToInt(args.align_inst));
  10891         }
  10892 
  10893         const has_type: u4 = @boolToInt(args.type_inst != .none);
  10894         const has_align: u4 = @boolToInt(args.align_inst != .none);
  10895         const is_const: u4 = @boolToInt(args.is_const);
  10896         const is_comptime: u4 = @boolToInt(args.is_comptime);
  10897         const small: u16 = has_type | (has_align << 1) | (is_const << 2) | (is_comptime << 3);
  10898 
  10899         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  10900         astgen.instructions.appendAssumeCapacity(.{
  10901             .tag = .extended,
  10902             .data = .{ .extended = .{
  10903                 .opcode = .alloc,
  10904                 .small = small,
  10905                 .operand = payload_index,
  10906             } },
  10907         });
  10908         gz.instructions.appendAssumeCapacity(new_index);
  10909         return indexToRef(new_index);
  10910     }
  10911 
  10912     fn addAsm(
  10913         gz: *GenZir,
  10914         args: struct {
  10915             /// Absolute node index. This function does the conversion to offset from Decl.
  10916             node: Ast.Node.Index,
  10917             asm_source: u32,
  10918             output_type_bits: u32,
  10919             is_volatile: bool,
  10920             outputs: []const Zir.Inst.Asm.Output,
  10921             inputs: []const Zir.Inst.Asm.Input,
  10922             clobbers: []const u32,
  10923         },
  10924     ) !Zir.Inst.Ref {
  10925         const astgen = gz.astgen;
  10926         const gpa = astgen.gpa;
  10927 
  10928         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10929         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
  10930         try astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Asm).Struct.fields.len +
  10931             args.outputs.len * @typeInfo(Zir.Inst.Asm.Output).Struct.fields.len +
  10932             args.inputs.len * @typeInfo(Zir.Inst.Asm.Input).Struct.fields.len +
  10933             args.clobbers.len);
  10934 
  10935         const payload_index = gz.astgen.addExtraAssumeCapacity(Zir.Inst.Asm{
  10936             .src_node = gz.nodeIndexToRelative(args.node),
  10937             .asm_source = args.asm_source,
  10938             .output_type_bits = args.output_type_bits,
  10939         });
  10940         for (args.outputs) |output| {
  10941             _ = gz.astgen.addExtraAssumeCapacity(output);
  10942         }
  10943         for (args.inputs) |input| {
  10944             _ = gz.astgen.addExtraAssumeCapacity(input);
  10945         }
  10946         gz.astgen.extra.appendSliceAssumeCapacity(args.clobbers);
  10947 
  10948         //  * 0b00000000_000XXXXX - `outputs_len`.
  10949         //  * 0b000000XX_XXX00000 - `inputs_len`.
  10950         //  * 0b0XXXXX00_00000000 - `clobbers_len`.
  10951         //  * 0bX0000000_00000000 - is volatile
  10952         const small: u16 = @intCast(u16, args.outputs.len) |
  10953             @intCast(u16, args.inputs.len << 5) |
  10954             @intCast(u16, args.clobbers.len << 10) |
  10955             (@as(u16, @boolToInt(args.is_volatile)) << 15);
  10956 
  10957         const new_index = @intCast(Zir.Inst.Index, astgen.instructions.len);
  10958         astgen.instructions.appendAssumeCapacity(.{
  10959             .tag = .extended,
  10960             .data = .{ .extended = .{
  10961                 .opcode = .@"asm",
  10962                 .small = small,
  10963                 .operand = payload_index,
  10964             } },
  10965         });
  10966         gz.instructions.appendAssumeCapacity(new_index);
  10967         return indexToRef(new_index);
  10968     }
  10969 
  10970     /// Note that this returns a `Zir.Inst.Index` not a ref.
  10971     /// Does *not* append the block instruction to the scope.
  10972     /// Leaves the `payload_index` field undefined.
  10973     fn makeBlockInst(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
  10974         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  10975         const gpa = gz.astgen.gpa;
  10976         try gz.astgen.instructions.append(gpa, .{
  10977             .tag = tag,
  10978             .data = .{ .pl_node = .{
  10979                 .src_node = gz.nodeIndexToRelative(node),
  10980                 .payload_index = undefined,
  10981             } },
  10982         });
  10983         return new_index;
  10984     }
  10985 
  10986     /// Note that this returns a `Zir.Inst.Index` not a ref.
  10987     /// Leaves the `payload_index` field undefined.
  10988     fn addCondBr(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
  10989         const gpa = gz.astgen.gpa;
  10990         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  10991         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  10992         try gz.astgen.instructions.append(gpa, .{
  10993             .tag = tag,
  10994             .data = .{ .pl_node = .{
  10995                 .src_node = gz.nodeIndexToRelative(node),
  10996                 .payload_index = undefined,
  10997             } },
  10998         });
  10999         gz.instructions.appendAssumeCapacity(new_index);
  11000         return new_index;
  11001     }
  11002 
  11003     fn setStruct(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  11004         src_node: Ast.Node.Index,
  11005         fields_len: u32,
  11006         decls_len: u32,
  11007         layout: std.builtin.Type.ContainerLayout,
  11008         known_non_opv: bool,
  11009         known_comptime_only: bool,
  11010     }) !void {
  11011         const astgen = gz.astgen;
  11012         const gpa = astgen.gpa;
  11013 
  11014         try astgen.extra.ensureUnusedCapacity(gpa, 4);
  11015         const payload_index = @intCast(u32, astgen.extra.items.len);
  11016 
  11017         if (args.src_node != 0) {
  11018             const node_offset = gz.nodeIndexToRelative(args.src_node);
  11019             astgen.extra.appendAssumeCapacity(@bitCast(u32, node_offset));
  11020         }
  11021         if (args.fields_len != 0) {
  11022             astgen.extra.appendAssumeCapacity(args.fields_len);
  11023         }
  11024         if (args.decls_len != 0) {
  11025             astgen.extra.appendAssumeCapacity(args.decls_len);
  11026         }
  11027         astgen.instructions.set(inst, .{
  11028             .tag = .extended,
  11029             .data = .{ .extended = .{
  11030                 .opcode = .struct_decl,
  11031                 .small = @bitCast(u16, Zir.Inst.StructDecl.Small{
  11032                     .has_src_node = args.src_node != 0,
  11033                     .has_fields_len = args.fields_len != 0,
  11034                     .has_decls_len = args.decls_len != 0,
  11035                     .known_non_opv = args.known_non_opv,
  11036                     .known_comptime_only = args.known_comptime_only,
  11037                     .name_strategy = gz.anon_name_strategy,
  11038                     .layout = args.layout,
  11039                 }),
  11040                 .operand = payload_index,
  11041             } },
  11042         });
  11043     }
  11044 
  11045     fn setUnion(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  11046         src_node: Ast.Node.Index,
  11047         tag_type: Zir.Inst.Ref,
  11048         body_len: u32,
  11049         fields_len: u32,
  11050         decls_len: u32,
  11051         layout: std.builtin.Type.ContainerLayout,
  11052         auto_enum_tag: bool,
  11053     }) !void {
  11054         const astgen = gz.astgen;
  11055         const gpa = astgen.gpa;
  11056 
  11057         try astgen.extra.ensureUnusedCapacity(gpa, 5);
  11058         const payload_index = @intCast(u32, astgen.extra.items.len);
  11059 
  11060         if (args.src_node != 0) {
  11061             const node_offset = gz.nodeIndexToRelative(args.src_node);
  11062             astgen.extra.appendAssumeCapacity(@bitCast(u32, node_offset));
  11063         }
  11064         if (args.tag_type != .none) {
  11065             astgen.extra.appendAssumeCapacity(@enumToInt(args.tag_type));
  11066         }
  11067         if (args.body_len != 0) {
  11068             astgen.extra.appendAssumeCapacity(args.body_len);
  11069         }
  11070         if (args.fields_len != 0) {
  11071             astgen.extra.appendAssumeCapacity(args.fields_len);
  11072         }
  11073         if (args.decls_len != 0) {
  11074             astgen.extra.appendAssumeCapacity(args.decls_len);
  11075         }
  11076         astgen.instructions.set(inst, .{
  11077             .tag = .extended,
  11078             .data = .{ .extended = .{
  11079                 .opcode = .union_decl,
  11080                 .small = @bitCast(u16, Zir.Inst.UnionDecl.Small{
  11081                     .has_src_node = args.src_node != 0,
  11082                     .has_tag_type = args.tag_type != .none,
  11083                     .has_body_len = args.body_len != 0,
  11084                     .has_fields_len = args.fields_len != 0,
  11085                     .has_decls_len = args.decls_len != 0,
  11086                     .name_strategy = gz.anon_name_strategy,
  11087                     .layout = args.layout,
  11088                     .auto_enum_tag = args.auto_enum_tag,
  11089                 }),
  11090                 .operand = payload_index,
  11091             } },
  11092         });
  11093     }
  11094 
  11095     fn setEnum(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  11096         src_node: Ast.Node.Index,
  11097         tag_type: Zir.Inst.Ref,
  11098         body_len: u32,
  11099         fields_len: u32,
  11100         decls_len: u32,
  11101         nonexhaustive: bool,
  11102     }) !void {
  11103         const astgen = gz.astgen;
  11104         const gpa = astgen.gpa;
  11105 
  11106         try astgen.extra.ensureUnusedCapacity(gpa, 5);
  11107         const payload_index = @intCast(u32, astgen.extra.items.len);
  11108 
  11109         if (args.src_node != 0) {
  11110             const node_offset = gz.nodeIndexToRelative(args.src_node);
  11111             astgen.extra.appendAssumeCapacity(@bitCast(u32, node_offset));
  11112         }
  11113         if (args.tag_type != .none) {
  11114             astgen.extra.appendAssumeCapacity(@enumToInt(args.tag_type));
  11115         }
  11116         if (args.body_len != 0) {
  11117             astgen.extra.appendAssumeCapacity(args.body_len);
  11118         }
  11119         if (args.fields_len != 0) {
  11120             astgen.extra.appendAssumeCapacity(args.fields_len);
  11121         }
  11122         if (args.decls_len != 0) {
  11123             astgen.extra.appendAssumeCapacity(args.decls_len);
  11124         }
  11125         astgen.instructions.set(inst, .{
  11126             .tag = .extended,
  11127             .data = .{ .extended = .{
  11128                 .opcode = .enum_decl,
  11129                 .small = @bitCast(u16, Zir.Inst.EnumDecl.Small{
  11130                     .has_src_node = args.src_node != 0,
  11131                     .has_tag_type = args.tag_type != .none,
  11132                     .has_body_len = args.body_len != 0,
  11133                     .has_fields_len = args.fields_len != 0,
  11134                     .has_decls_len = args.decls_len != 0,
  11135                     .name_strategy = gz.anon_name_strategy,
  11136                     .nonexhaustive = args.nonexhaustive,
  11137                 }),
  11138                 .operand = payload_index,
  11139             } },
  11140         });
  11141     }
  11142 
  11143     fn setOpaque(gz: *GenZir, inst: Zir.Inst.Index, args: struct {
  11144         src_node: Ast.Node.Index,
  11145         decls_len: u32,
  11146     }) !void {
  11147         const astgen = gz.astgen;
  11148         const gpa = astgen.gpa;
  11149 
  11150         try astgen.extra.ensureUnusedCapacity(gpa, 2);
  11151         const payload_index = @intCast(u32, astgen.extra.items.len);
  11152 
  11153         if (args.src_node != 0) {
  11154             const node_offset = gz.nodeIndexToRelative(args.src_node);
  11155             astgen.extra.appendAssumeCapacity(@bitCast(u32, node_offset));
  11156         }
  11157         if (args.decls_len != 0) {
  11158             astgen.extra.appendAssumeCapacity(args.decls_len);
  11159         }
  11160         astgen.instructions.set(inst, .{
  11161             .tag = .extended,
  11162             .data = .{ .extended = .{
  11163                 .opcode = .opaque_decl,
  11164                 .small = @bitCast(u16, Zir.Inst.OpaqueDecl.Small{
  11165                     .has_src_node = args.src_node != 0,
  11166                     .has_decls_len = args.decls_len != 0,
  11167                     .name_strategy = gz.anon_name_strategy,
  11168                 }),
  11169                 .operand = payload_index,
  11170             } },
  11171         });
  11172     }
  11173 
  11174     fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref {
  11175         return indexToRef(try gz.addAsIndex(inst));
  11176     }
  11177 
  11178     fn addAsIndex(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Index {
  11179         const gpa = gz.astgen.gpa;
  11180         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  11181         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  11182 
  11183         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  11184         gz.astgen.instructions.appendAssumeCapacity(inst);
  11185         gz.instructions.appendAssumeCapacity(new_index);
  11186         return new_index;
  11187     }
  11188 
  11189     fn reserveInstructionIndex(gz: *GenZir) !Zir.Inst.Index {
  11190         const gpa = gz.astgen.gpa;
  11191         try gz.instructions.ensureUnusedCapacity(gpa, 1);
  11192         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
  11193 
  11194         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  11195         gz.astgen.instructions.len += 1;
  11196         gz.instructions.appendAssumeCapacity(new_index);
  11197         return new_index;
  11198     }
  11199 
  11200     fn addRet(gz: *GenZir, rl: ResultLoc, operand: Zir.Inst.Ref, node: Ast.Node.Index) !void {
  11201         switch (rl) {
  11202             .ptr => |ret_ptr| _ = try gz.addUnNode(.ret_load, ret_ptr, node),
  11203             .ty => _ = try gz.addUnNode(.ret_node, operand, node),
  11204             else => unreachable,
  11205         }
  11206     }
  11207 
  11208     fn addNamespaceCaptures(gz: *GenZir, namespace: *Scope.Namespace) !void {
  11209         if (namespace.captures.count() > 0) {
  11210             try gz.instructions.ensureUnusedCapacity(gz.astgen.gpa, namespace.captures.count());
  11211             for (namespace.captures.values()) |capture| {
  11212                 gz.instructions.appendAssumeCapacity(capture);
  11213             }
  11214         }
  11215     }
  11216 
  11217     fn addDbgVar(gz: *GenZir, tag: Zir.Inst.Tag, name: u32, inst: Zir.Inst.Ref) !void {
  11218         if (gz.force_comptime) return;
  11219 
  11220         _ = try gz.add(.{ .tag = tag, .data = .{
  11221             .str_op = .{
  11222                 .str = name,
  11223                 .operand = inst,
  11224             },
  11225         } });
  11226     }
  11227 
  11228     fn addDbgBlockBegin(gz: *GenZir) !void {
  11229         if (gz.force_comptime) return;
  11230 
  11231         _ = try gz.add(.{ .tag = .dbg_block_begin, .data = undefined });
  11232     }
  11233 
  11234     fn addDbgBlockEnd(gz: *GenZir) !void {
  11235         if (gz.force_comptime) return;
  11236         const gpa = gz.astgen.gpa;
  11237 
  11238         const tags = gz.astgen.instructions.items(.tag);
  11239         const last_inst = gz.instructions.items[gz.instructions.items.len - 1];
  11240         // remove dbg_block_begin immediately followed by dbg_block_end
  11241         if (tags[last_inst] == .dbg_block_begin) {
  11242             _ = gz.instructions.pop();
  11243             return;
  11244         }
  11245 
  11246         const new_index = @intCast(Zir.Inst.Index, gz.astgen.instructions.len);
  11247         try gz.astgen.instructions.append(gpa, .{ .tag = .dbg_block_end, .data = undefined });
  11248         try gz.instructions.insert(gpa, gz.instructions.items.len - 1, new_index);
  11249     }
  11250 
  11251     /// Control flow does not fall through the "then" block of a loop; it continues
  11252     /// back to the while condition. This prevents `rvalue` from
  11253     /// adding an invalid store to the result location of `then_scope`.
  11254     fn markAsLoopBody(gz: *GenZir, loop_scope: GenZir) void {
  11255         gz.rvalue_noresult = switch (loop_scope.break_result_loc) {
  11256             .ptr, .inferred_ptr => |ptr| ptr,
  11257             .block_ptr => |block| block.rl_ptr,
  11258             else => .none,
  11259         };
  11260     }
  11261 };
  11262 
  11263 /// This can only be for short-lived references; the memory becomes invalidated
  11264 /// when another string is added.
  11265 fn nullTerminatedString(astgen: AstGen, index: usize) [*:0]const u8 {
  11266     return @ptrCast([*:0]const u8, astgen.string_bytes.items.ptr) + index;
  11267 }
  11268 
  11269 pub fn isPrimitive(name: []const u8) bool {
  11270     if (primitives.get(name) != null) return true;
  11271     if (name.len < 2) return false;
  11272     const first_c = name[0];
  11273     if (first_c != 'i' and first_c != 'u') return false;
  11274     if (parseBitCount(name[1..])) |_| {
  11275         return true;
  11276     } else |err| switch (err) {
  11277         error.Overflow => return true,
  11278         error.InvalidCharacter => return false,
  11279     }
  11280 }
  11281 
  11282 /// Local variables shadowing detection, including function parameters.
  11283 fn detectLocalShadowing(
  11284     astgen: *AstGen,
  11285     scope: *Scope,
  11286     ident_name: u32,
  11287     name_token: Ast.TokenIndex,
  11288     token_bytes: []const u8,
  11289 ) !void {
  11290     const gpa = astgen.gpa;
  11291     if (token_bytes[0] != '@' and isPrimitive(token_bytes)) {
  11292         return astgen.failTokNotes(name_token, "name shadows primitive '{s}'", .{
  11293             token_bytes,
  11294         }, &[_]u32{
  11295             try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{
  11296                 token_bytes,
  11297             }),
  11298         });
  11299     }
  11300 
  11301     var s = scope;
  11302     while (true) switch (s.tag) {
  11303         .local_val => {
  11304             const local_val = s.cast(Scope.LocalVal).?;
  11305             if (local_val.name == ident_name) {
  11306                 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
  11307                 const name = try gpa.dupe(u8, name_slice);
  11308                 defer gpa.free(name);
  11309                 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{
  11310                     @tagName(local_val.id_cat), name,
  11311                 }, &[_]u32{
  11312                     try astgen.errNoteTok(
  11313                         local_val.token_src,
  11314                         "previous declaration here",
  11315                         .{},
  11316                     ),
  11317                 });
  11318             }
  11319             s = local_val.parent;
  11320         },
  11321         .local_ptr => {
  11322             const local_ptr = s.cast(Scope.LocalPtr).?;
  11323             if (local_ptr.name == ident_name) {
  11324                 const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
  11325                 const name = try gpa.dupe(u8, name_slice);
  11326                 defer gpa.free(name);
  11327                 return astgen.failTokNotes(name_token, "redeclaration of {s} '{s}'", .{
  11328                     @tagName(local_ptr.id_cat), name,
  11329                 }, &[_]u32{
  11330                     try astgen.errNoteTok(
  11331                         local_ptr.token_src,
  11332                         "previous declaration here",
  11333                         .{},
  11334                     ),
  11335                 });
  11336             }
  11337             s = local_ptr.parent;
  11338         },
  11339         .namespace => {
  11340             const ns = s.cast(Scope.Namespace).?;
  11341             const decl_node = ns.decls.get(ident_name) orelse {
  11342                 s = ns.parent;
  11343                 continue;
  11344             };
  11345             const name_slice = mem.span(astgen.nullTerminatedString(ident_name));
  11346             const name = try gpa.dupe(u8, name_slice);
  11347             defer gpa.free(name);
  11348             return astgen.failTokNotes(name_token, "local shadows declaration of '{s}'", .{
  11349                 name,
  11350             }, &[_]u32{
  11351                 try astgen.errNoteNode(decl_node, "declared here", .{}),
  11352             });
  11353         },
  11354         .gen_zir => s = s.cast(GenZir).?.parent,
  11355         .defer_normal, .defer_error => s = s.cast(Scope.Defer).?.parent,
  11356         .top => break,
  11357     };
  11358 }
  11359 
  11360 /// Advances the source cursor to the beginning of `node`.
  11361 fn advanceSourceCursorToNode(astgen: *AstGen, node: Ast.Node.Index) void {
  11362     const tree = astgen.tree;
  11363     const token_starts = tree.tokens.items(.start);
  11364     const node_start = token_starts[tree.firstToken(node)];
  11365     astgen.advanceSourceCursor(node_start);
  11366 }
  11367 
  11368 /// Advances the source cursor to an absolute byte offset `end` in the file.
  11369 fn advanceSourceCursor(astgen: *AstGen, end: usize) void {
  11370     const source = astgen.tree.source;
  11371     var i = astgen.source_offset;
  11372     var line = astgen.source_line;
  11373     var column = astgen.source_column;
  11374     assert(i <= end);
  11375     while (i < end) : (i += 1) {
  11376         if (source[i] == '\n') {
  11377             line += 1;
  11378             column = 0;
  11379         } else {
  11380             column += 1;
  11381         }
  11382     }
  11383     astgen.source_offset = i;
  11384     astgen.source_line = line;
  11385     astgen.source_column = column;
  11386 }
  11387 
  11388 fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.Node.Index) !u32 {
  11389     const gpa = astgen.gpa;
  11390     const tree = astgen.tree;
  11391     const node_tags = tree.nodes.items(.tag);
  11392     const main_tokens = tree.nodes.items(.main_token);
  11393     const token_tags = tree.tokens.items(.tag);
  11394     var decl_count: u32 = 0;
  11395     for (members) |member_node| {
  11396         const name_token = switch (node_tags[member_node]) {
  11397             .fn_proto_simple,
  11398             .fn_proto_multi,
  11399             .fn_proto_one,
  11400             .fn_proto,
  11401             .global_var_decl,
  11402             .local_var_decl,
  11403             .simple_var_decl,
  11404             .aligned_var_decl,
  11405             => blk: {
  11406                 decl_count += 1;
  11407                 break :blk main_tokens[member_node] + 1;
  11408             },
  11409 
  11410             .fn_decl => blk: {
  11411                 decl_count += 1;
  11412                 const ident = main_tokens[member_node] + 1;
  11413                 if (token_tags[ident] != .identifier) {
  11414                     switch (astgen.failNode(member_node, "missing function name", .{})) {
  11415                         error.AnalysisFail => continue,
  11416                         error.OutOfMemory => return error.OutOfMemory,
  11417                     }
  11418                 }
  11419                 break :blk ident;
  11420             },
  11421 
  11422             .@"comptime", .@"usingnamespace", .test_decl => {
  11423                 decl_count += 1;
  11424                 continue;
  11425             },
  11426 
  11427             else => continue,
  11428         };
  11429 
  11430         const token_bytes = astgen.tree.tokenSlice(name_token);
  11431         if (token_bytes[0] != '@' and isPrimitive(token_bytes)) {
  11432             switch (astgen.failTokNotes(name_token, "name shadows primitive '{s}'", .{
  11433                 token_bytes,
  11434             }, &[_]u32{
  11435                 try astgen.errNoteTok(name_token, "consider using @\"{s}\" to disambiguate", .{
  11436                     token_bytes,
  11437                 }),
  11438             })) {
  11439                 error.AnalysisFail => continue,
  11440                 error.OutOfMemory => return error.OutOfMemory,
  11441             }
  11442         }
  11443 
  11444         const name_str_index = try astgen.identAsString(name_token);
  11445         const gop = try namespace.decls.getOrPut(gpa, name_str_index);
  11446         if (gop.found_existing) {
  11447             const name = try gpa.dupe(u8, mem.span(astgen.nullTerminatedString(name_str_index)));
  11448             defer gpa.free(name);
  11449             switch (astgen.failNodeNotes(member_node, "redeclaration of '{s}'", .{
  11450                 name,
  11451             }, &[_]u32{
  11452                 try astgen.errNoteNode(gop.value_ptr.*, "other declaration here", .{}),
  11453             })) {
  11454                 error.AnalysisFail => continue,
  11455                 error.OutOfMemory => return error.OutOfMemory,
  11456             }
  11457         }
  11458         gop.value_ptr.* = member_node;
  11459     }
  11460     return decl_count;
  11461 }
  11462 
  11463 fn isInferred(astgen: *AstGen, ref: Zir.Inst.Ref) bool {
  11464     const inst = refToIndex(ref) orelse return false;
  11465     const zir_tags = astgen.instructions.items(.tag);
  11466     return switch (zir_tags[inst]) {
  11467         .alloc_inferred,
  11468         .alloc_inferred_mut,
  11469         .alloc_inferred_comptime,
  11470         .alloc_inferred_comptime_mut,
  11471         => true,
  11472 
  11473         else => false,
  11474     };
  11475 }
  11476 
  11477 /// Assumes capacity for body has already been added. Needed capacity taking into
  11478 /// account fixups can be found with `countBodyLenAfterFixups`.
  11479 fn appendBodyWithFixups(astgen: *AstGen, body: []const Zir.Inst.Index) void {
  11480     return appendBodyWithFixupsArrayList(astgen, &astgen.extra, body);
  11481 }
  11482 
  11483 fn appendBodyWithFixupsArrayList(
  11484     astgen: *AstGen,
  11485     list: *std.ArrayListUnmanaged(u32),
  11486     body: []const Zir.Inst.Index,
  11487 ) void {
  11488     for (body) |body_inst| {
  11489         appendPossiblyRefdBodyInst(astgen, list, body_inst);
  11490     }
  11491 }
  11492 
  11493 fn appendPossiblyRefdBodyInst(
  11494     astgen: *AstGen,
  11495     list: *std.ArrayListUnmanaged(u32),
  11496     body_inst: Zir.Inst.Index,
  11497 ) void {
  11498     list.appendAssumeCapacity(body_inst);
  11499     const kv = astgen.ref_table.fetchRemove(body_inst) orelse return;
  11500     const ref_inst = kv.value;
  11501     return appendPossiblyRefdBodyInst(astgen, list, ref_inst);
  11502 }
  11503 
  11504 fn countBodyLenAfterFixups(astgen: *AstGen, body: []const Zir.Inst.Index) u32 {
  11505     var count = body.len;
  11506     for (body) |body_inst| {
  11507         var check_inst = body_inst;
  11508         while (astgen.ref_table.get(check_inst)) |ref_inst| {
  11509             count += 1;
  11510             check_inst = ref_inst;
  11511         }
  11512     }
  11513     return @intCast(u32, count);
  11514 }