zig

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

blob ee4591ef (142280B) - Raw


      1 const std = @import("std");
      2 const mem = std.mem;
      3 const Allocator = std.mem.Allocator;
      4 const Value = @import("value.zig").Value;
      5 const Type = @import("type.zig").Type;
      6 const TypedValue = @import("TypedValue.zig");
      7 const assert = std.debug.assert;
      8 const zir = @import("zir.zig");
      9 const Module = @import("Module.zig");
     10 const ast = std.zig.ast;
     11 const trace = @import("tracy.zig").trace;
     12 const Scope = Module.Scope;
     13 const InnerError = Module.InnerError;
     14 
     15 pub const ResultLoc = union(enum) {
     16     /// The expression is the right-hand side of assignment to `_`. Only the side-effects of the
     17     /// expression should be generated. The result instruction from the expression must
     18     /// be ignored.
     19     discard,
     20     /// The expression has an inferred type, and it will be evaluated as an rvalue.
     21     none,
     22     /// The expression must generate a pointer rather than a value. For example, the left hand side
     23     /// of an assignment uses this kind of result location.
     24     ref,
     25     /// The expression will be coerced into this type, but it will be evaluated as an rvalue.
     26     ty: *zir.Inst,
     27     /// The expression must store its result into this typed pointer. The result instruction
     28     /// from the expression must be ignored.
     29     ptr: *zir.Inst,
     30     /// The expression must store its result into this allocation, which has an inferred type.
     31     /// The result instruction from the expression must be ignored.
     32     inferred_ptr: *zir.Inst.Tag.alloc_inferred.Type(),
     33     /// The expression must store its result into this pointer, which is a typed pointer that
     34     /// has been bitcasted to whatever the expression's type is.
     35     /// The result instruction from the expression must be ignored.
     36     bitcasted_ptr: *zir.Inst.UnOp,
     37     /// There is a pointer for the expression to store its result into, however, its type
     38     /// is inferred based on peer type resolution for a `zir.Inst.Block`.
     39     /// The result instruction from the expression must be ignored.
     40     block_ptr: *Module.Scope.GenZIR,
     41 };
     42 
     43 pub fn typeExpr(mod: *Module, scope: *Scope, type_node: *ast.Node) InnerError!*zir.Inst {
     44     const type_src = scope.tree().token_locs[type_node.firstToken()].start;
     45     const type_type = try addZIRInstConst(mod, scope, type_src, .{
     46         .ty = Type.initTag(.type),
     47         .val = Value.initTag(.type_type),
     48     });
     49     const type_rl: ResultLoc = .{ .ty = type_type };
     50     return expr(mod, scope, type_rl, type_node);
     51 }
     52 
     53 fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
     54     switch (node.tag) {
     55         .Root => unreachable,
     56         .Use => unreachable,
     57         .TestDecl => unreachable,
     58         .DocComment => unreachable,
     59         .VarDecl => unreachable,
     60         .SwitchCase => unreachable,
     61         .SwitchElse => unreachable,
     62         .Else => unreachable,
     63         .Payload => unreachable,
     64         .PointerPayload => unreachable,
     65         .PointerIndexPayload => unreachable,
     66         .ErrorTag => unreachable,
     67         .FieldInitializer => unreachable,
     68         .ContainerField => unreachable,
     69 
     70         .Assign,
     71         .AssignBitAnd,
     72         .AssignBitOr,
     73         .AssignBitShiftLeft,
     74         .AssignBitShiftRight,
     75         .AssignBitXor,
     76         .AssignDiv,
     77         .AssignSub,
     78         .AssignSubWrap,
     79         .AssignMod,
     80         .AssignAdd,
     81         .AssignAddWrap,
     82         .AssignMul,
     83         .AssignMulWrap,
     84         .Add,
     85         .AddWrap,
     86         .Sub,
     87         .SubWrap,
     88         .Mul,
     89         .MulWrap,
     90         .Div,
     91         .Mod,
     92         .BitAnd,
     93         .BitOr,
     94         .BitShiftLeft,
     95         .BitShiftRight,
     96         .BitXor,
     97         .BangEqual,
     98         .EqualEqual,
     99         .GreaterThan,
    100         .GreaterOrEqual,
    101         .LessThan,
    102         .LessOrEqual,
    103         .ArrayCat,
    104         .ArrayMult,
    105         .BoolAnd,
    106         .BoolOr,
    107         .Asm,
    108         .StringLiteral,
    109         .IntegerLiteral,
    110         .Call,
    111         .Unreachable,
    112         .Return,
    113         .If,
    114         .While,
    115         .BoolNot,
    116         .AddressOf,
    117         .FloatLiteral,
    118         .UndefinedLiteral,
    119         .BoolLiteral,
    120         .NullLiteral,
    121         .OptionalType,
    122         .Block,
    123         .LabeledBlock,
    124         .Break,
    125         .PtrType,
    126         .ArrayType,
    127         .ArrayTypeSentinel,
    128         .EnumLiteral,
    129         .MultilineStringLiteral,
    130         .CharLiteral,
    131         .Defer,
    132         .Catch,
    133         .ErrorUnion,
    134         .MergeErrorSets,
    135         .Range,
    136         .Await,
    137         .BitNot,
    138         .Negation,
    139         .NegationWrap,
    140         .Resume,
    141         .Try,
    142         .SliceType,
    143         .Slice,
    144         .ArrayInitializer,
    145         .ArrayInitializerDot,
    146         .StructInitializer,
    147         .StructInitializerDot,
    148         .Switch,
    149         .For,
    150         .Suspend,
    151         .Continue,
    152         .AnyType,
    153         .ErrorType,
    154         .FnProto,
    155         .AnyFrameType,
    156         .ErrorSetDecl,
    157         .ContainerDecl,
    158         .Comptime,
    159         .Nosuspend,
    160         => return mod.failNode(scope, node, "invalid left-hand side to assignment", .{}),
    161 
    162         // @field can be assigned to
    163         .BuiltinCall => {
    164             const call = node.castTag(.BuiltinCall).?;
    165             const tree = scope.tree();
    166             const builtin_name = tree.tokenSlice(call.builtin_token);
    167 
    168             if (!mem.eql(u8, builtin_name, "@field")) {
    169                 return mod.failNode(scope, node, "invalid left-hand side to assignment", .{});
    170             }
    171         },
    172 
    173         // can be assigned to
    174         .UnwrapOptional,
    175         .Deref,
    176         .Period,
    177         .ArrayAccess,
    178         .Identifier,
    179         .GroupedExpression,
    180         .OrElse,
    181         => {},
    182     }
    183     return expr(mod, scope, .ref, node);
    184 }
    185 
    186 /// Turn Zig AST into untyped ZIR istructions.
    187 /// When `rl` is discard, ptr, inferred_ptr, bitcasted_ptr, or inferred_ptr, the
    188 /// result instruction can be used to inspect whether it is isNoReturn() but that is it,
    189 /// it must otherwise not be used.
    190 pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {
    191     switch (node.tag) {
    192         .Root => unreachable, // Top-level declaration.
    193         .Use => unreachable, // Top-level declaration.
    194         .TestDecl => unreachable, // Top-level declaration.
    195         .DocComment => unreachable, // Top-level declaration.
    196         .VarDecl => unreachable, // Handled in `blockExpr`.
    197         .SwitchCase => unreachable, // Handled in `switchExpr`.
    198         .SwitchElse => unreachable, // Handled in `switchExpr`.
    199         .Range => unreachable, // Handled in `switchExpr`.
    200         .Else => unreachable, // Handled explicitly the control flow expression functions.
    201         .Payload => unreachable, // Handled explicitly.
    202         .PointerPayload => unreachable, // Handled explicitly.
    203         .PointerIndexPayload => unreachable, // Handled explicitly.
    204         .ErrorTag => unreachable, // Handled explicitly.
    205         .FieldInitializer => unreachable, // Handled explicitly.
    206         .ContainerField => unreachable, // Handled explicitly.
    207 
    208         .Assign => return rvalueVoid(mod, scope, rl, node, try assign(mod, scope, node.castTag(.Assign).?)),
    209         .AssignBitAnd => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitAnd).?, .bit_and)),
    210         .AssignBitOr => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitOr).?, .bit_or)),
    211         .AssignBitShiftLeft => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftLeft).?, .shl)),
    212         .AssignBitShiftRight => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitShiftRight).?, .shr)),
    213         .AssignBitXor => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignBitXor).?, .xor)),
    214         .AssignDiv => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignDiv).?, .div)),
    215         .AssignSub => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSub).?, .sub)),
    216         .AssignSubWrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignSubWrap).?, .subwrap)),
    217         .AssignMod => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMod).?, .mod_rem)),
    218         .AssignAdd => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAdd).?, .add)),
    219         .AssignAddWrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignAddWrap).?, .addwrap)),
    220         .AssignMul => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMul).?, .mul)),
    221         .AssignMulWrap => return rvalueVoid(mod, scope, rl, node, try assignOp(mod, scope, node.castTag(.AssignMulWrap).?, .mulwrap)),
    222 
    223         .Add => return simpleBinOp(mod, scope, rl, node.castTag(.Add).?, .add),
    224         .AddWrap => return simpleBinOp(mod, scope, rl, node.castTag(.AddWrap).?, .addwrap),
    225         .Sub => return simpleBinOp(mod, scope, rl, node.castTag(.Sub).?, .sub),
    226         .SubWrap => return simpleBinOp(mod, scope, rl, node.castTag(.SubWrap).?, .subwrap),
    227         .Mul => return simpleBinOp(mod, scope, rl, node.castTag(.Mul).?, .mul),
    228         .MulWrap => return simpleBinOp(mod, scope, rl, node.castTag(.MulWrap).?, .mulwrap),
    229         .Div => return simpleBinOp(mod, scope, rl, node.castTag(.Div).?, .div),
    230         .Mod => return simpleBinOp(mod, scope, rl, node.castTag(.Mod).?, .mod_rem),
    231         .BitAnd => return simpleBinOp(mod, scope, rl, node.castTag(.BitAnd).?, .bit_and),
    232         .BitOr => return simpleBinOp(mod, scope, rl, node.castTag(.BitOr).?, .bit_or),
    233         .BitShiftLeft => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftLeft).?, .shl),
    234         .BitShiftRight => return simpleBinOp(mod, scope, rl, node.castTag(.BitShiftRight).?, .shr),
    235         .BitXor => return simpleBinOp(mod, scope, rl, node.castTag(.BitXor).?, .xor),
    236 
    237         .BangEqual => return simpleBinOp(mod, scope, rl, node.castTag(.BangEqual).?, .cmp_neq),
    238         .EqualEqual => return simpleBinOp(mod, scope, rl, node.castTag(.EqualEqual).?, .cmp_eq),
    239         .GreaterThan => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterThan).?, .cmp_gt),
    240         .GreaterOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.GreaterOrEqual).?, .cmp_gte),
    241         .LessThan => return simpleBinOp(mod, scope, rl, node.castTag(.LessThan).?, .cmp_lt),
    242         .LessOrEqual => return simpleBinOp(mod, scope, rl, node.castTag(.LessOrEqual).?, .cmp_lte),
    243 
    244         .ArrayCat => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayCat).?, .array_cat),
    245         .ArrayMult => return simpleBinOp(mod, scope, rl, node.castTag(.ArrayMult).?, .array_mul),
    246 
    247         .BoolAnd => return boolBinOp(mod, scope, rl, node.castTag(.BoolAnd).?),
    248         .BoolOr => return boolBinOp(mod, scope, rl, node.castTag(.BoolOr).?),
    249 
    250         .BoolNot => return rvalue(mod, scope, rl, try boolNot(mod, scope, node.castTag(.BoolNot).?)),
    251         .BitNot => return rvalue(mod, scope, rl, try bitNot(mod, scope, node.castTag(.BitNot).?)),
    252         .Negation => return rvalue(mod, scope, rl, try negation(mod, scope, node.castTag(.Negation).?, .sub)),
    253         .NegationWrap => return rvalue(mod, scope, rl, try negation(mod, scope, node.castTag(.NegationWrap).?, .subwrap)),
    254 
    255         .Identifier => return try identifier(mod, scope, rl, node.castTag(.Identifier).?),
    256         .Asm => return rvalue(mod, scope, rl, try assembly(mod, scope, node.castTag(.Asm).?)),
    257         .StringLiteral => return rvalue(mod, scope, rl, try stringLiteral(mod, scope, node.castTag(.StringLiteral).?)),
    258         .IntegerLiteral => return rvalue(mod, scope, rl, try integerLiteral(mod, scope, node.castTag(.IntegerLiteral).?)),
    259         .BuiltinCall => return builtinCall(mod, scope, rl, node.castTag(.BuiltinCall).?),
    260         .Call => return callExpr(mod, scope, rl, node.castTag(.Call).?),
    261         .Unreachable => return unreach(mod, scope, node.castTag(.Unreachable).?),
    262         .Return => return ret(mod, scope, node.castTag(.Return).?),
    263         .If => return ifExpr(mod, scope, rl, node.castTag(.If).?),
    264         .While => return whileExpr(mod, scope, rl, node.castTag(.While).?),
    265         .Period => return field(mod, scope, rl, node.castTag(.Period).?),
    266         .Deref => return rvalue(mod, scope, rl, try deref(mod, scope, node.castTag(.Deref).?)),
    267         .AddressOf => return rvalue(mod, scope, rl, try addressOf(mod, scope, node.castTag(.AddressOf).?)),
    268         .FloatLiteral => return rvalue(mod, scope, rl, try floatLiteral(mod, scope, node.castTag(.FloatLiteral).?)),
    269         .UndefinedLiteral => return rvalue(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)),
    270         .BoolLiteral => return rvalue(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)),
    271         .NullLiteral => return rvalue(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)),
    272         .OptionalType => return rvalue(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)),
    273         .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?),
    274         .Block => return rvalueVoid(mod, scope, rl, node, try blockExpr(mod, scope, node.castTag(.Block).?)),
    275         .LabeledBlock => return labeledBlockExpr(mod, scope, rl, node.castTag(.LabeledBlock).?, .block),
    276         .Break => return rvalue(mod, scope, rl, try breakExpr(mod, scope, node.castTag(.Break).?)),
    277         .Continue => return rvalue(mod, scope, rl, try continueExpr(mod, scope, node.castTag(.Continue).?)),
    278         .PtrType => return rvalue(mod, scope, rl, try ptrType(mod, scope, node.castTag(.PtrType).?)),
    279         .GroupedExpression => return expr(mod, scope, rl, node.castTag(.GroupedExpression).?.expr),
    280         .ArrayType => return rvalue(mod, scope, rl, try arrayType(mod, scope, node.castTag(.ArrayType).?)),
    281         .ArrayTypeSentinel => return rvalue(mod, scope, rl, try arrayTypeSentinel(mod, scope, node.castTag(.ArrayTypeSentinel).?)),
    282         .EnumLiteral => return rvalue(mod, scope, rl, try enumLiteral(mod, scope, node.castTag(.EnumLiteral).?)),
    283         .MultilineStringLiteral => return rvalue(mod, scope, rl, try multilineStrLiteral(mod, scope, node.castTag(.MultilineStringLiteral).?)),
    284         .CharLiteral => return rvalue(mod, scope, rl, try charLiteral(mod, scope, node.castTag(.CharLiteral).?)),
    285         .SliceType => return rvalue(mod, scope, rl, try sliceType(mod, scope, node.castTag(.SliceType).?)),
    286         .ErrorUnion => return rvalue(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.ErrorUnion).?, .error_union_type)),
    287         .MergeErrorSets => return rvalue(mod, scope, rl, try typeInixOp(mod, scope, node.castTag(.MergeErrorSets).?, .merge_error_sets)),
    288         .AnyFrameType => return rvalue(mod, scope, rl, try anyFrameType(mod, scope, node.castTag(.AnyFrameType).?)),
    289         .ErrorSetDecl => return rvalue(mod, scope, rl, try errorSetDecl(mod, scope, node.castTag(.ErrorSetDecl).?)),
    290         .ErrorType => return rvalue(mod, scope, rl, try errorType(mod, scope, node.castTag(.ErrorType).?)),
    291         .For => return forExpr(mod, scope, rl, node.castTag(.For).?),
    292         .ArrayAccess => return arrayAccess(mod, scope, rl, node.castTag(.ArrayAccess).?),
    293         .Slice => return rvalue(mod, scope, rl, try sliceExpr(mod, scope, node.castTag(.Slice).?)),
    294         .Catch => return catchExpr(mod, scope, rl, node.castTag(.Catch).?),
    295         .Comptime => return comptimeKeyword(mod, scope, rl, node.castTag(.Comptime).?),
    296         .OrElse => return orelseExpr(mod, scope, rl, node.castTag(.OrElse).?),
    297         .Switch => return switchExpr(mod, scope, rl, node.castTag(.Switch).?),
    298         .ContainerDecl => return containerDecl(mod, scope, rl, node.castTag(.ContainerDecl).?),
    299 
    300         .Defer => return mod.failNode(scope, node, "TODO implement astgen.expr for .Defer", .{}),
    301         .Await => return mod.failNode(scope, node, "TODO implement astgen.expr for .Await", .{}),
    302         .Resume => return mod.failNode(scope, node, "TODO implement astgen.expr for .Resume", .{}),
    303         .Try => return mod.failNode(scope, node, "TODO implement astgen.expr for .Try", .{}),
    304         .ArrayInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializer", .{}),
    305         .ArrayInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .ArrayInitializerDot", .{}),
    306         .StructInitializer => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializer", .{}),
    307         .StructInitializerDot => return mod.failNode(scope, node, "TODO implement astgen.expr for .StructInitializerDot", .{}),
    308         .Suspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Suspend", .{}),
    309         .AnyType => return mod.failNode(scope, node, "TODO implement astgen.expr for .AnyType", .{}),
    310         .FnProto => return mod.failNode(scope, node, "TODO implement astgen.expr for .FnProto", .{}),
    311         .Nosuspend => return mod.failNode(scope, node, "TODO implement astgen.expr for .Nosuspend", .{}),
    312     }
    313 }
    314 
    315 fn comptimeKeyword(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Comptime) InnerError!*zir.Inst {
    316     const tracy = trace(@src());
    317     defer tracy.end();
    318 
    319     return comptimeExpr(mod, scope, rl, node.expr);
    320 }
    321 
    322 pub fn comptimeExpr(mod: *Module, parent_scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerError!*zir.Inst {
    323     const tree = parent_scope.tree();
    324     const src = tree.token_locs[node.firstToken()].start;
    325 
    326     // Optimization for labeled blocks: don't need to have 2 layers of blocks, we can reuse the existing one.
    327     if (node.castTag(.LabeledBlock)) |block_node| {
    328         return labeledBlockExpr(mod, parent_scope, rl, block_node, .block_comptime);
    329     }
    330 
    331     // Make a scope to collect generated instructions in the sub-expression.
    332     var block_scope: Scope.GenZIR = .{
    333         .parent = parent_scope,
    334         .decl = parent_scope.ownerDecl().?,
    335         .arena = parent_scope.arena(),
    336         .instructions = .{},
    337     };
    338     defer block_scope.instructions.deinit(mod.gpa);
    339 
    340     // No need to capture the result here because block_comptime_flat implies that the final
    341     // instruction is the block's result value.
    342     _ = try expr(mod, &block_scope.base, rl, node);
    343 
    344     const block = try addZIRInstBlock(mod, parent_scope, src, .block_comptime_flat, .{
    345         .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
    346     });
    347 
    348     return &block.base;
    349 }
    350 
    351 fn breakExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
    352     if (true) {
    353         @panic("TODO reimplement this");
    354     }
    355     const tree = parent_scope.tree();
    356     const src = tree.token_locs[node.ltoken].start;
    357 
    358     // Look for the label in the scope.
    359     var scope = parent_scope;
    360     while (true) {
    361         switch (scope.tag) {
    362             .gen_zir => {
    363                 const gen_zir = scope.cast(Scope.GenZIR).?;
    364 
    365                 const block_inst = blk: {
    366                     if (node.getLabel()) |break_label| {
    367                         if (gen_zir.label) |*label| {
    368                             if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
    369                                 label.used = true;
    370                                 break :blk label.block_inst;
    371                             }
    372                         }
    373                     } else if (gen_zir.break_block) |inst| {
    374                         break :blk inst;
    375                     }
    376                     scope = gen_zir.parent;
    377                     continue;
    378                 };
    379 
    380                 if (node.getRHS()) |rhs| {
    381                     // Most result location types can be forwarded directly; however
    382                     // if we need to write to a pointer which has an inferred type,
    383                     // proper type inference requires peer type resolution on the block's
    384                     // break operand expressions.
    385                     const branch_rl: ResultLoc = switch (gen_zir.break_result_loc) {
    386                         .discard, .none, .ty, .ptr, .ref => gen_zir.break_result_loc,
    387                         .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block_inst },
    388                     };
    389                     const operand = try expr(mod, parent_scope, branch_rl, rhs);
    390                     return try addZIRInst(mod, parent_scope, src, zir.Inst.Break, .{
    391                         .block = block_inst,
    392                         .operand = operand,
    393                     }, .{});
    394                 } else {
    395                     return try addZIRInst(mod, parent_scope, src, zir.Inst.BreakVoid, .{
    396                         .block = block_inst,
    397                     }, .{});
    398                 }
    399             },
    400             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
    401             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
    402             else => if (node.getLabel()) |break_label| {
    403                 const label_name = try mod.identifierTokenString(parent_scope, break_label);
    404                 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
    405             } else {
    406                 return mod.failTok(parent_scope, src, "break expression outside loop", .{});
    407             },
    408         }
    409     }
    410 }
    411 
    412 fn continueExpr(mod: *Module, parent_scope: *Scope, node: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
    413     const tree = parent_scope.tree();
    414     const src = tree.token_locs[node.ltoken].start;
    415 
    416     // Look for the label in the scope.
    417     var scope = parent_scope;
    418     while (true) {
    419         switch (scope.tag) {
    420             .gen_zir => {
    421                 const gen_zir = scope.cast(Scope.GenZIR).?;
    422                 const continue_block = gen_zir.continue_block orelse {
    423                     scope = gen_zir.parent;
    424                     continue;
    425                 };
    426                 if (node.getLabel()) |break_label| blk: {
    427                     if (gen_zir.label) |*label| {
    428                         if (try tokenIdentEql(mod, parent_scope, label.token, break_label)) {
    429                             label.used = true;
    430                             break :blk;
    431                         }
    432                     }
    433                     // found continue but either it has a different label, or no label
    434                     scope = gen_zir.parent;
    435                     continue;
    436                 }
    437 
    438                 return addZIRInst(mod, parent_scope, src, zir.Inst.BreakVoid, .{
    439                     .block = continue_block,
    440                 }, .{});
    441             },
    442             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
    443             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
    444             else => if (node.getLabel()) |break_label| {
    445                 const label_name = try mod.identifierTokenString(parent_scope, break_label);
    446                 return mod.failTok(parent_scope, break_label, "label not found: '{s}'", .{label_name});
    447             } else {
    448                 return mod.failTok(parent_scope, src, "continue expression outside loop", .{});
    449             },
    450         }
    451     }
    452 }
    453 
    454 pub fn blockExpr(mod: *Module, parent_scope: *Scope, block_node: *ast.Node.Block) InnerError!void {
    455     const tracy = trace(@src());
    456     defer tracy.end();
    457 
    458     try blockExprStmts(mod, parent_scope, &block_node.base, block_node.statements());
    459 }
    460 
    461 fn checkLabelRedefinition(mod: *Module, parent_scope: *Scope, label: ast.TokenIndex) !void {
    462     // Look for the label in the scope.
    463     var scope = parent_scope;
    464     while (true) {
    465         switch (scope.tag) {
    466             .gen_zir => {
    467                 const gen_zir = scope.cast(Scope.GenZIR).?;
    468                 if (gen_zir.label) |prev_label| {
    469                     if (try tokenIdentEql(mod, parent_scope, label, prev_label.token)) {
    470                         const tree = parent_scope.tree();
    471                         const label_src = tree.token_locs[label].start;
    472                         const prev_label_src = tree.token_locs[prev_label.token].start;
    473 
    474                         const label_name = try mod.identifierTokenString(parent_scope, label);
    475                         const msg = msg: {
    476                             const msg = try mod.errMsg(
    477                                 parent_scope,
    478                                 label_src,
    479                                 "redefinition of label '{s}'",
    480                                 .{label_name},
    481                             );
    482                             errdefer msg.destroy(mod.gpa);
    483                             try mod.errNote(
    484                                 parent_scope,
    485                                 prev_label_src,
    486                                 msg,
    487                                 "previous definition is here",
    488                                 .{},
    489                             );
    490                             break :msg msg;
    491                         };
    492                         return mod.failWithOwnedErrorMsg(parent_scope, msg);
    493                     }
    494                 }
    495                 scope = gen_zir.parent;
    496             },
    497             .local_val => scope = scope.cast(Scope.LocalVal).?.parent,
    498             .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent,
    499             else => return,
    500         }
    501     }
    502 }
    503 
    504 fn labeledBlockExpr(
    505     mod: *Module,
    506     parent_scope: *Scope,
    507     rl: ResultLoc,
    508     block_node: *ast.Node.LabeledBlock,
    509     zir_tag: zir.Inst.Tag,
    510 ) InnerError!*zir.Inst {
    511     const tracy = trace(@src());
    512     defer tracy.end();
    513 
    514     assert(zir_tag == .block or zir_tag == .block_comptime);
    515 
    516     const tree = parent_scope.tree();
    517     const src = tree.token_locs[block_node.lbrace].start;
    518 
    519     try checkLabelRedefinition(mod, parent_scope, block_node.label);
    520 
    521     // Create the Block ZIR instruction so that we can put it into the GenZIR struct
    522     // so that break statements can reference it.
    523     const gen_zir = parent_scope.getGenZIR();
    524     const block_inst = try gen_zir.arena.create(zir.Inst.Block);
    525     block_inst.* = .{
    526         .base = .{
    527             .tag = zir_tag,
    528             .src = src,
    529         },
    530         .positionals = .{
    531             .body = .{ .instructions = undefined },
    532         },
    533         .kw_args = .{},
    534     };
    535 
    536     var block_scope: Scope.GenZIR = .{
    537         .parent = parent_scope,
    538         .decl = parent_scope.ownerDecl().?,
    539         .arena = gen_zir.arena,
    540         .instructions = .{},
    541         .break_result_loc = rl,
    542         // TODO @as here is working around a stage1 miscompilation bug :(
    543         .label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
    544             .token = block_node.label,
    545             .block_inst = block_inst,
    546         }),
    547     };
    548     defer block_scope.instructions.deinit(mod.gpa);
    549 
    550     try blockExprStmts(mod, &block_scope.base, &block_node.base, block_node.statements());
    551     if (!block_scope.label.?.used) {
    552         return mod.fail(parent_scope, tree.token_locs[block_node.label].start, "unused block label", .{});
    553     }
    554 
    555     block_inst.positionals.body.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items);
    556     try gen_zir.instructions.append(mod.gpa, &block_inst.base);
    557 
    558     return &block_inst.base;
    559 }
    560 
    561 fn blockExprStmts(mod: *Module, parent_scope: *Scope, node: *ast.Node, statements: []*ast.Node) !void {
    562     const tree = parent_scope.tree();
    563 
    564     var block_arena = std.heap.ArenaAllocator.init(mod.gpa);
    565     defer block_arena.deinit();
    566 
    567     var scope = parent_scope;
    568     for (statements) |statement| {
    569         const src = tree.token_locs[statement.firstToken()].start;
    570         _ = try addZIRNoOp(mod, scope, src, .dbg_stmt);
    571         switch (statement.tag) {
    572             .VarDecl => {
    573                 const var_decl_node = statement.castTag(.VarDecl).?;
    574                 scope = try varDecl(mod, scope, var_decl_node, &block_arena.allocator);
    575             },
    576             .Assign => try assign(mod, scope, statement.castTag(.Assign).?),
    577             .AssignBitAnd => try assignOp(mod, scope, statement.castTag(.AssignBitAnd).?, .bit_and),
    578             .AssignBitOr => try assignOp(mod, scope, statement.castTag(.AssignBitOr).?, .bit_or),
    579             .AssignBitShiftLeft => try assignOp(mod, scope, statement.castTag(.AssignBitShiftLeft).?, .shl),
    580             .AssignBitShiftRight => try assignOp(mod, scope, statement.castTag(.AssignBitShiftRight).?, .shr),
    581             .AssignBitXor => try assignOp(mod, scope, statement.castTag(.AssignBitXor).?, .xor),
    582             .AssignDiv => try assignOp(mod, scope, statement.castTag(.AssignDiv).?, .div),
    583             .AssignSub => try assignOp(mod, scope, statement.castTag(.AssignSub).?, .sub),
    584             .AssignSubWrap => try assignOp(mod, scope, statement.castTag(.AssignSubWrap).?, .subwrap),
    585             .AssignMod => try assignOp(mod, scope, statement.castTag(.AssignMod).?, .mod_rem),
    586             .AssignAdd => try assignOp(mod, scope, statement.castTag(.AssignAdd).?, .add),
    587             .AssignAddWrap => try assignOp(mod, scope, statement.castTag(.AssignAddWrap).?, .addwrap),
    588             .AssignMul => try assignOp(mod, scope, statement.castTag(.AssignMul).?, .mul),
    589             .AssignMulWrap => try assignOp(mod, scope, statement.castTag(.AssignMulWrap).?, .mulwrap),
    590 
    591             else => {
    592                 const possibly_unused_result = try expr(mod, scope, .none, statement);
    593                 if (!possibly_unused_result.tag.isNoReturn()) {
    594                     _ = try addZIRUnOp(mod, scope, src, .ensure_result_used, possibly_unused_result);
    595                 }
    596             },
    597         }
    598     }
    599 }
    600 
    601 fn varDecl(
    602     mod: *Module,
    603     scope: *Scope,
    604     node: *ast.Node.VarDecl,
    605     block_arena: *Allocator,
    606 ) InnerError!*Scope {
    607     if (node.getComptimeToken()) |comptime_token| {
    608         return mod.failTok(scope, comptime_token, "TODO implement comptime locals", .{});
    609     }
    610     if (node.getAlignNode()) |align_node| {
    611         return mod.failNode(scope, align_node, "TODO implement alignment on locals", .{});
    612     }
    613     const tree = scope.tree();
    614     const name_src = tree.token_locs[node.name_token].start;
    615     const ident_name = try mod.identifierTokenString(scope, node.name_token);
    616 
    617     // Local variables shadowing detection, including function parameters.
    618     {
    619         var s = scope;
    620         while (true) switch (s.tag) {
    621             .local_val => {
    622                 const local_val = s.cast(Scope.LocalVal).?;
    623                 if (mem.eql(u8, local_val.name, ident_name)) {
    624                     const msg = msg: {
    625                         const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{
    626                             ident_name,
    627                         });
    628                         errdefer msg.destroy(mod.gpa);
    629                         try mod.errNote(scope, local_val.inst.src, msg, "previous definition is here", .{});
    630                         break :msg msg;
    631                     };
    632                     return mod.failWithOwnedErrorMsg(scope, msg);
    633                 }
    634                 s = local_val.parent;
    635             },
    636             .local_ptr => {
    637                 const local_ptr = s.cast(Scope.LocalPtr).?;
    638                 if (mem.eql(u8, local_ptr.name, ident_name)) {
    639                     const msg = msg: {
    640                         const msg = try mod.errMsg(scope, name_src, "redefinition of '{s}'", .{
    641                             ident_name,
    642                         });
    643                         errdefer msg.destroy(mod.gpa);
    644                         try mod.errNote(scope, local_ptr.ptr.src, msg, "previous definition is here", .{});
    645                         break :msg msg;
    646                     };
    647                     return mod.failWithOwnedErrorMsg(scope, msg);
    648                 }
    649                 s = local_ptr.parent;
    650             },
    651             .gen_zir => s = s.cast(Scope.GenZIR).?.parent,
    652             else => break,
    653         };
    654     }
    655 
    656     // Namespace vars shadowing detection
    657     if (mod.lookupDeclName(scope, ident_name)) |_| {
    658         // TODO add note for other definition
    659         return mod.fail(scope, name_src, "redefinition of '{s}'", .{ident_name});
    660     }
    661     const init_node = node.getInitNode() orelse
    662         return mod.fail(scope, name_src, "variables must be initialized", .{});
    663 
    664     switch (tree.token_ids[node.mut_token]) {
    665         .Keyword_const => {
    666             // Depending on the type of AST the initialization expression is, we may need an lvalue
    667             // or an rvalue as a result location. If it is an rvalue, we can use the instruction as
    668             // the variable, no memory location needed.
    669             if (!nodeMayNeedMemoryLocation(init_node, scope)) {
    670                 const result_loc: ResultLoc = if (node.getTypeNode()) |type_node|
    671                     .{ .ty = try typeExpr(mod, scope, type_node) }
    672                 else
    673                     .none;
    674                 const init_inst = try expr(mod, scope, result_loc, init_node);
    675                 const sub_scope = try block_arena.create(Scope.LocalVal);
    676                 sub_scope.* = .{
    677                     .parent = scope,
    678                     .gen_zir = scope.getGenZIR(),
    679                     .name = ident_name,
    680                     .inst = init_inst,
    681                 };
    682                 return &sub_scope.base;
    683             }
    684 
    685             var resolve_inferred_alloc: ?*zir.Inst = null;
    686             const result_loc = r: {
    687                 if (node.getTypeNode()) |type_node| {
    688                     const type_inst = try typeExpr(mod, scope, type_node);
    689                     const alloc = try addZIRUnOp(mod, scope, name_src, .alloc, type_inst);
    690                     break :r ResultLoc{ .ptr = alloc };
    691                 } else {
    692                     const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred);
    693                     resolve_inferred_alloc = &alloc.base;
    694                     break :r ResultLoc{ .inferred_ptr = alloc };
    695                 }
    696             };
    697             const init_inst = try expr(mod, scope, result_loc, init_node);
    698             if (resolve_inferred_alloc) |inst| {
    699                 _ = try addZIRUnOp(mod, scope, name_src, .resolve_inferred_alloc, inst);
    700             }
    701             const sub_scope = try block_arena.create(Scope.LocalVal);
    702             sub_scope.* = .{
    703                 .parent = scope,
    704                 .gen_zir = scope.getGenZIR(),
    705                 .name = ident_name,
    706                 .inst = init_inst,
    707             };
    708             return &sub_scope.base;
    709         },
    710         .Keyword_var => {
    711             var resolve_inferred_alloc: ?*zir.Inst = null;
    712             const var_data: struct { result_loc: ResultLoc, alloc: *zir.Inst } = if (node.getTypeNode()) |type_node| a: {
    713                 const type_inst = try typeExpr(mod, scope, type_node);
    714                 const alloc = try addZIRUnOp(mod, scope, name_src, .alloc_mut, type_inst);
    715                 break :a .{ .alloc = alloc, .result_loc = .{ .ptr = alloc } };
    716             } else a: {
    717                 const alloc = try addZIRNoOpT(mod, scope, name_src, .alloc_inferred_mut);
    718                 resolve_inferred_alloc = &alloc.base;
    719                 break :a .{ .alloc = &alloc.base, .result_loc = .{ .inferred_ptr = alloc } };
    720             };
    721             const init_inst = try expr(mod, scope, var_data.result_loc, init_node);
    722             if (resolve_inferred_alloc) |inst| {
    723                 _ = try addZIRUnOp(mod, scope, name_src, .resolve_inferred_alloc, inst);
    724             }
    725             const sub_scope = try block_arena.create(Scope.LocalPtr);
    726             sub_scope.* = .{
    727                 .parent = scope,
    728                 .gen_zir = scope.getGenZIR(),
    729                 .name = ident_name,
    730                 .ptr = var_data.alloc,
    731             };
    732             return &sub_scope.base;
    733         },
    734         else => unreachable,
    735     }
    736 }
    737 
    738 fn assign(mod: *Module, scope: *Scope, infix_node: *ast.Node.SimpleInfixOp) InnerError!void {
    739     if (infix_node.lhs.castTag(.Identifier)) |ident| {
    740         // This intentionally does not support @"_" syntax.
    741         const ident_name = scope.tree().tokenSlice(ident.token);
    742         if (mem.eql(u8, ident_name, "_")) {
    743             _ = try expr(mod, scope, .discard, infix_node.rhs);
    744             return;
    745         }
    746     }
    747     const lvalue = try lvalExpr(mod, scope, infix_node.lhs);
    748     _ = try expr(mod, scope, .{ .ptr = lvalue }, infix_node.rhs);
    749 }
    750 
    751 fn assignOp(
    752     mod: *Module,
    753     scope: *Scope,
    754     infix_node: *ast.Node.SimpleInfixOp,
    755     op_inst_tag: zir.Inst.Tag,
    756 ) InnerError!void {
    757     const lhs_ptr = try lvalExpr(mod, scope, infix_node.lhs);
    758     const lhs = try addZIRUnOp(mod, scope, lhs_ptr.src, .deref, lhs_ptr);
    759     const lhs_type = try addZIRUnOp(mod, scope, lhs_ptr.src, .typeof, lhs);
    760     const rhs = try expr(mod, scope, .{ .ty = lhs_type }, infix_node.rhs);
    761 
    762     const tree = scope.tree();
    763     const src = tree.token_locs[infix_node.op_token].start;
    764 
    765     const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
    766     _ = try addZIRBinOp(mod, scope, src, .store, lhs_ptr, result);
    767 }
    768 
    769 fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
    770     const tree = scope.tree();
    771     const src = tree.token_locs[node.op_token].start;
    772     const bool_type = try addZIRInstConst(mod, scope, src, .{
    773         .ty = Type.initTag(.type),
    774         .val = Value.initTag(.bool_type),
    775     });
    776     const operand = try expr(mod, scope, .{ .ty = bool_type }, node.rhs);
    777     return addZIRUnOp(mod, scope, src, .bool_not, operand);
    778 }
    779 
    780 fn bitNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
    781     const tree = scope.tree();
    782     const src = tree.token_locs[node.op_token].start;
    783     const operand = try expr(mod, scope, .none, node.rhs);
    784     return addZIRUnOp(mod, scope, src, .bit_not, operand);
    785 }
    786 
    787 fn negation(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst {
    788     const tree = scope.tree();
    789     const src = tree.token_locs[node.op_token].start;
    790 
    791     const lhs = try addZIRInstConst(mod, scope, src, .{
    792         .ty = Type.initTag(.comptime_int),
    793         .val = Value.initTag(.zero),
    794     });
    795     const rhs = try expr(mod, scope, .none, node.rhs);
    796 
    797     return addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
    798 }
    799 
    800 fn addressOf(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
    801     return expr(mod, scope, .ref, node.rhs);
    802 }
    803 
    804 fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst {
    805     const tree = scope.tree();
    806     const src = tree.token_locs[node.op_token].start;
    807     const operand = try typeExpr(mod, scope, node.rhs);
    808     return addZIRUnOp(mod, scope, src, .optional_type, operand);
    809 }
    810 
    811 fn sliceType(mod: *Module, scope: *Scope, node: *ast.Node.SliceType) InnerError!*zir.Inst {
    812     const tree = scope.tree();
    813     const src = tree.token_locs[node.op_token].start;
    814     return ptrSliceType(mod, scope, src, &node.ptr_info, node.rhs, .Slice);
    815 }
    816 
    817 fn ptrType(mod: *Module, scope: *Scope, node: *ast.Node.PtrType) InnerError!*zir.Inst {
    818     const tree = scope.tree();
    819     const src = tree.token_locs[node.op_token].start;
    820     return ptrSliceType(mod, scope, src, &node.ptr_info, node.rhs, switch (tree.token_ids[node.op_token]) {
    821         .Asterisk, .AsteriskAsterisk => .One,
    822         // TODO stage1 type inference bug
    823         .LBracket => @as(std.builtin.TypeInfo.Pointer.Size, switch (tree.token_ids[node.op_token + 2]) {
    824             .Identifier => .C,
    825             else => .Many,
    826         }),
    827         else => unreachable,
    828     });
    829 }
    830 
    831 fn ptrSliceType(mod: *Module, scope: *Scope, src: usize, ptr_info: *ast.PtrInfo, rhs: *ast.Node, size: std.builtin.TypeInfo.Pointer.Size) InnerError!*zir.Inst {
    832     const simple = ptr_info.allowzero_token == null and
    833         ptr_info.align_info == null and
    834         ptr_info.volatile_token == null and
    835         ptr_info.sentinel == null;
    836 
    837     if (simple) {
    838         const child_type = try typeExpr(mod, scope, rhs);
    839         const mutable = ptr_info.const_token == null;
    840         // TODO stage1 type inference bug
    841         const T = zir.Inst.Tag;
    842         return addZIRUnOp(mod, scope, src, switch (size) {
    843             .One => if (mutable) T.single_mut_ptr_type else T.single_const_ptr_type,
    844             .Many => if (mutable) T.many_mut_ptr_type else T.many_const_ptr_type,
    845             .C => if (mutable) T.c_mut_ptr_type else T.c_const_ptr_type,
    846             .Slice => if (mutable) T.mut_slice_type else T.const_slice_type,
    847         }, child_type);
    848     }
    849 
    850     var kw_args: std.meta.fieldInfo(zir.Inst.PtrType, .kw_args).field_type = .{};
    851     kw_args.size = size;
    852     kw_args.@"allowzero" = ptr_info.allowzero_token != null;
    853     if (ptr_info.align_info) |some| {
    854         kw_args.@"align" = try expr(mod, scope, .none, some.node);
    855         if (some.bit_range) |bit_range| {
    856             kw_args.align_bit_start = try expr(mod, scope, .none, bit_range.start);
    857             kw_args.align_bit_end = try expr(mod, scope, .none, bit_range.end);
    858         }
    859     }
    860     kw_args.mutable = ptr_info.const_token == null;
    861     kw_args.@"volatile" = ptr_info.volatile_token != null;
    862     if (ptr_info.sentinel) |some| {
    863         kw_args.sentinel = try expr(mod, scope, .none, some);
    864     }
    865 
    866     const child_type = try typeExpr(mod, scope, rhs);
    867     if (kw_args.sentinel) |some| {
    868         kw_args.sentinel = try addZIRBinOp(mod, scope, some.src, .as, child_type, some);
    869     }
    870 
    871     return addZIRInst(mod, scope, src, zir.Inst.PtrType, .{ .child_type = child_type }, kw_args);
    872 }
    873 
    874 fn arrayType(mod: *Module, scope: *Scope, node: *ast.Node.ArrayType) !*zir.Inst {
    875     const tree = scope.tree();
    876     const src = tree.token_locs[node.op_token].start;
    877     const usize_type = try addZIRInstConst(mod, scope, src, .{
    878         .ty = Type.initTag(.type),
    879         .val = Value.initTag(.usize_type),
    880     });
    881 
    882     // TODO check for [_]T
    883     const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr);
    884     const elem_type = try typeExpr(mod, scope, node.rhs);
    885 
    886     return addZIRBinOp(mod, scope, src, .array_type, len, elem_type);
    887 }
    888 
    889 fn arrayTypeSentinel(mod: *Module, scope: *Scope, node: *ast.Node.ArrayTypeSentinel) !*zir.Inst {
    890     const tree = scope.tree();
    891     const src = tree.token_locs[node.op_token].start;
    892     const usize_type = try addZIRInstConst(mod, scope, src, .{
    893         .ty = Type.initTag(.type),
    894         .val = Value.initTag(.usize_type),
    895     });
    896 
    897     // TODO check for [_]T
    898     const len = try expr(mod, scope, .{ .ty = usize_type }, node.len_expr);
    899     const sentinel_uncasted = try expr(mod, scope, .none, node.sentinel);
    900     const elem_type = try typeExpr(mod, scope, node.rhs);
    901     const sentinel = try addZIRBinOp(mod, scope, src, .as, elem_type, sentinel_uncasted);
    902 
    903     return addZIRInst(mod, scope, src, zir.Inst.ArrayTypeSentinel, .{
    904         .len = len,
    905         .sentinel = sentinel,
    906         .elem_type = elem_type,
    907     }, .{});
    908 }
    909 
    910 fn anyFrameType(mod: *Module, scope: *Scope, node: *ast.Node.AnyFrameType) InnerError!*zir.Inst {
    911     const tree = scope.tree();
    912     const src = tree.token_locs[node.anyframe_token].start;
    913     if (node.result) |some| {
    914         const return_type = try typeExpr(mod, scope, some.return_type);
    915         return addZIRUnOp(mod, scope, src, .anyframe_type, return_type);
    916     } else {
    917         return addZIRInstConst(mod, scope, src, .{
    918             .ty = Type.initTag(.type),
    919             .val = Value.initTag(.anyframe_type),
    920         });
    921     }
    922 }
    923 
    924 fn typeInixOp(mod: *Module, scope: *Scope, node: *ast.Node.SimpleInfixOp, op_inst_tag: zir.Inst.Tag) InnerError!*zir.Inst {
    925     const tree = scope.tree();
    926     const src = tree.token_locs[node.op_token].start;
    927     const error_set = try typeExpr(mod, scope, node.lhs);
    928     const payload = try typeExpr(mod, scope, node.rhs);
    929     return addZIRBinOp(mod, scope, src, op_inst_tag, error_set, payload);
    930 }
    931 
    932 fn enumLiteral(mod: *Module, scope: *Scope, node: *ast.Node.EnumLiteral) !*zir.Inst {
    933     const tree = scope.tree();
    934     const src = tree.token_locs[node.name].start;
    935     const name = try mod.identifierTokenString(scope, node.name);
    936 
    937     return addZIRInst(mod, scope, src, zir.Inst.EnumLiteral, .{ .name = name }, .{});
    938 }
    939 
    940 fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
    941     const tree = scope.tree();
    942     const src = tree.token_locs[node.rtoken].start;
    943 
    944     const operand = try expr(mod, scope, rl, node.lhs);
    945     const op: zir.Inst.Tag = switch (rl) {
    946         .ref => .optional_payload_safe_ptr,
    947         else => .optional_payload_safe,
    948     };
    949     return addZIRUnOp(mod, scope, src, op, operand);
    950 }
    951 
    952 fn containerField(
    953     mod: *Module,
    954     scope: *Scope,
    955     node: *ast.Node.ContainerField,
    956 ) InnerError!*zir.Inst {
    957     const tree = scope.tree();
    958     const src = tree.token_locs[node.firstToken()].start;
    959     const name = try mod.identifierTokenString(scope, node.name_token);
    960 
    961     if (node.comptime_token == null and node.value_expr == null and node.align_expr == null) {
    962         if (node.type_expr) |some| {
    963             const ty = try typeExpr(mod, scope, some);
    964             return addZIRInst(mod, scope, src, zir.Inst.ContainerFieldTyped, .{
    965                 .bytes = name,
    966                 .ty = ty,
    967             }, .{});
    968         } else {
    969             return addZIRInst(mod, scope, src, zir.Inst.ContainerFieldNamed, .{
    970                 .bytes = name,
    971             }, .{});
    972         }
    973     }
    974 
    975     const ty = if (node.type_expr) |some| try typeExpr(mod, scope, some) else null;
    976     const alignment = if (node.align_expr) |some| try expr(mod, scope, .none, some) else null;
    977     const init = if (node.value_expr) |some| try expr(mod, scope, .none, some) else null;
    978 
    979     return addZIRInst(mod, scope, src, zir.Inst.ContainerField, .{
    980         .bytes = name,
    981     }, .{
    982         .ty = ty,
    983         .init = init,
    984         .alignment = alignment,
    985         .is_comptime = node.comptime_token != null,
    986     });
    987 }
    988 
    989 fn containerDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ContainerDecl) InnerError!*zir.Inst {
    990     const tree = scope.tree();
    991     const src = tree.token_locs[node.kind_token].start;
    992 
    993     var gen_scope: Scope.GenZIR = .{
    994         .parent = scope,
    995         .decl = scope.ownerDecl().?,
    996         .arena = scope.arena(),
    997         .instructions = .{},
    998     };
    999     defer gen_scope.instructions.deinit(mod.gpa);
   1000 
   1001     var fields = std.ArrayList(*zir.Inst).init(mod.gpa);
   1002     defer fields.deinit();
   1003 
   1004     for (node.fieldsAndDecls()) |fd| {
   1005         if (fd.castTag(.ContainerField)) |f| {
   1006             try fields.append(try containerField(mod, &gen_scope.base, f));
   1007         }
   1008     }
   1009 
   1010     var decl_arena = std.heap.ArenaAllocator.init(mod.gpa);
   1011     errdefer decl_arena.deinit();
   1012     const arena = &decl_arena.allocator;
   1013 
   1014     var layout: std.builtin.TypeInfo.ContainerLayout = .Auto;
   1015     if (node.layout_token) |some| switch (tree.token_ids[some]) {
   1016         .Keyword_extern => layout = .Extern,
   1017         .Keyword_packed => layout = .Packed,
   1018         else => unreachable,
   1019     };
   1020 
   1021     const container_type = switch (tree.token_ids[node.kind_token]) {
   1022         .Keyword_enum => blk: {
   1023             const tag_type: ?*zir.Inst = switch (node.init_arg_expr) {
   1024                 .Type => |t| try typeExpr(mod, &gen_scope.base, t),
   1025                 .None => null,
   1026                 .Enum => unreachable,
   1027             };
   1028             const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.EnumType, .{
   1029                 .fields = try arena.dupe(*zir.Inst, fields.items),
   1030             }, .{
   1031                 .layout = layout,
   1032                 .tag_type = tag_type,
   1033             });
   1034             const enum_type = try arena.create(Type.Payload.Enum);
   1035             enum_type.* = .{
   1036                 .analysis = .{
   1037                     .queued = .{
   1038                         .body = .{ .instructions = try arena.dupe(*zir.Inst, gen_scope.instructions.items) },
   1039                         .inst = inst,
   1040                     },
   1041                 },
   1042                 .scope = .{
   1043                     .file_scope = scope.getFileScope(),
   1044                     .ty = Type.initPayload(&enum_type.base),
   1045                 },
   1046             };
   1047             break :blk Type.initPayload(&enum_type.base);
   1048         },
   1049         .Keyword_struct => blk: {
   1050             assert(node.init_arg_expr == .None);
   1051             const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.StructType, .{
   1052                 .fields = try arena.dupe(*zir.Inst, fields.items),
   1053             }, .{
   1054                 .layout = layout,
   1055             });
   1056             const struct_type = try arena.create(Type.Payload.Struct);
   1057             struct_type.* = .{
   1058                 .analysis = .{
   1059                     .queued = .{
   1060                         .body = .{ .instructions = try arena.dupe(*zir.Inst, gen_scope.instructions.items) },
   1061                         .inst = inst,
   1062                     },
   1063                 },
   1064                 .scope = .{
   1065                     .file_scope = scope.getFileScope(),
   1066                     .ty = Type.initPayload(&struct_type.base),
   1067                 },
   1068             };
   1069             break :blk Type.initPayload(&struct_type.base);
   1070         },
   1071         .Keyword_union => blk: {
   1072             const init_inst = switch (node.init_arg_expr) {
   1073                 .Enum => |e| if (e) |t| try typeExpr(mod, &gen_scope.base, t) else null,
   1074                 .None => null,
   1075                 .Type => |t| try typeExpr(mod, &gen_scope.base, t),
   1076             };
   1077             const init_kind: zir.Inst.UnionType.InitKind = switch (node.init_arg_expr) {
   1078                 .Enum => .enum_type,
   1079                 .None => .none,
   1080                 .Type => .tag_type,
   1081             };
   1082             const inst = try addZIRInst(mod, &gen_scope.base, src, zir.Inst.UnionType, .{
   1083                 .fields = try arena.dupe(*zir.Inst, fields.items),
   1084             }, .{
   1085                 .layout = layout,
   1086                 .init_kind = init_kind,
   1087                 .init_inst = init_inst,
   1088             });
   1089             const union_type = try arena.create(Type.Payload.Union);
   1090             union_type.* = .{
   1091                 .analysis = .{
   1092                     .queued = .{
   1093                         .body = .{ .instructions = try arena.dupe(*zir.Inst, gen_scope.instructions.items) },
   1094                         .inst = inst,
   1095                     },
   1096                 },
   1097                 .scope = .{
   1098                     .file_scope = scope.getFileScope(),
   1099                     .ty = Type.initPayload(&union_type.base),
   1100                 },
   1101             };
   1102             break :blk Type.initPayload(&union_type.base);
   1103         },
   1104         .Keyword_opaque => blk: {
   1105             if (fields.items.len > 0) {
   1106                 return mod.fail(scope, fields.items[0].src, "opaque types cannot have fields", .{});
   1107             }
   1108             const opaque_type = try arena.create(Type.Payload.Opaque);
   1109             opaque_type.* = .{
   1110                 .scope = .{
   1111                     .file_scope = scope.getFileScope(),
   1112                     .ty = Type.initPayload(&opaque_type.base),
   1113                 },
   1114             };
   1115             break :blk Type.initPayload(&opaque_type.base);
   1116         },
   1117         else => unreachable,
   1118     };
   1119     const val = try Value.Tag.ty.create(arena, container_type);
   1120     const decl = try mod.createContainerDecl(scope, node.kind_token, &decl_arena, .{
   1121         .ty = Type.initTag(.type),
   1122         .val = val,
   1123     });
   1124     if (rl == .ref) {
   1125         return addZIRInst(mod, scope, src, zir.Inst.DeclRef, .{ .decl = decl }, .{});
   1126     } else {
   1127         return rvalue(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclVal, .{
   1128             .decl = decl,
   1129         }, .{}));
   1130     }
   1131 }
   1132 
   1133 fn errorSetDecl(mod: *Module, scope: *Scope, node: *ast.Node.ErrorSetDecl) InnerError!*zir.Inst {
   1134     const tree = scope.tree();
   1135     const src = tree.token_locs[node.error_token].start;
   1136     const decls = node.decls();
   1137     const fields = try scope.arena().alloc([]const u8, decls.len);
   1138 
   1139     for (decls) |decl, i| {
   1140         const tag = decl.castTag(.ErrorTag).?;
   1141         fields[i] = try mod.identifierTokenString(scope, tag.name_token);
   1142     }
   1143 
   1144     return addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{});
   1145 }
   1146 
   1147 fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
   1148     const tree = scope.tree();
   1149     const src = tree.token_locs[node.token].start;
   1150     return addZIRInstConst(mod, scope, src, .{
   1151         .ty = Type.initTag(.type),
   1152         .val = Value.initTag(.anyerror_type),
   1153     });
   1154 }
   1155 
   1156 fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch) InnerError!*zir.Inst {
   1157     switch (rl) {
   1158         .ref => return orelseCatchExpr(
   1159             mod,
   1160             scope,
   1161             rl,
   1162             node.lhs,
   1163             node.op_token,
   1164             .is_err_ptr,
   1165             .err_union_payload_unsafe_ptr,
   1166             .err_union_code_ptr,
   1167             node.rhs,
   1168             node.payload,
   1169         ),
   1170         else => return orelseCatchExpr(
   1171             mod,
   1172             scope,
   1173             rl,
   1174             node.lhs,
   1175             node.op_token,
   1176             .is_err,
   1177             .err_union_payload_unsafe,
   1178             .err_union_code,
   1179             node.rhs,
   1180             node.payload,
   1181         ),
   1182     }
   1183 }
   1184 
   1185 fn orelseExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
   1186     switch (rl) {
   1187         .ref => return orelseCatchExpr(
   1188             mod,
   1189             scope,
   1190             rl,
   1191             node.lhs,
   1192             node.op_token,
   1193             .is_null_ptr,
   1194             .optional_payload_unsafe_ptr,
   1195             undefined,
   1196             node.rhs,
   1197             null,
   1198         ),
   1199         else => return orelseCatchExpr(
   1200             mod,
   1201             scope,
   1202             rl,
   1203             node.lhs,
   1204             node.op_token,
   1205             .is_null,
   1206             .optional_payload_unsafe,
   1207             undefined,
   1208             node.rhs,
   1209             null,
   1210         ),
   1211     }
   1212 }
   1213 
   1214 fn orelseCatchExpr(
   1215     mod: *Module,
   1216     scope: *Scope,
   1217     rl: ResultLoc,
   1218     lhs: *ast.Node,
   1219     op_token: ast.TokenIndex,
   1220     cond_op: zir.Inst.Tag,
   1221     unwrap_op: zir.Inst.Tag,
   1222     unwrap_code_op: zir.Inst.Tag,
   1223     rhs: *ast.Node,
   1224     payload_node: ?*ast.Node,
   1225 ) InnerError!*zir.Inst {
   1226     if (true) {
   1227         @panic("TODO reimplement this");
   1228     }
   1229     const tree = scope.tree();
   1230     const src = tree.token_locs[op_token].start;
   1231 
   1232     var block_scope: Scope.GenZIR = .{
   1233         .parent = scope,
   1234         .decl = scope.ownerDecl().?,
   1235         .arena = scope.arena(),
   1236         .instructions = .{},
   1237     };
   1238     defer block_scope.instructions.deinit(mod.gpa);
   1239 
   1240     const block = try addZIRInstBlock(mod, scope, src, .block, .{
   1241         .instructions = undefined, // populated below
   1242     });
   1243 
   1244     // Most result location types can be forwarded directly; however
   1245     // if we need to write to a pointer which has an inferred type,
   1246     // proper type inference requires peer type resolution on the if's
   1247     // branches.
   1248     const branch_rl: ResultLoc = switch (rl) {
   1249         .discard, .none, .ty, .ptr, .ref => rl,
   1250         .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
   1251     };
   1252     // This could be a pointer or value depending on the `rl` parameter.
   1253     const operand = try expr(mod, &block_scope.base, branch_rl, lhs);
   1254     const cond = try addZIRUnOp(mod, &block_scope.base, src, cond_op, operand);
   1255 
   1256     const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
   1257         .condition = cond,
   1258         .then_body = undefined, // populated below
   1259         .else_body = undefined, // populated below
   1260     }, .{});
   1261 
   1262     var then_scope: Scope.GenZIR = .{
   1263         .parent = &block_scope.base,
   1264         .decl = block_scope.decl,
   1265         .arena = block_scope.arena,
   1266         .instructions = .{},
   1267     };
   1268     defer then_scope.instructions.deinit(mod.gpa);
   1269 
   1270     var err_val_scope: Scope.LocalVal = undefined;
   1271     const then_sub_scope = blk: {
   1272         const payload = payload_node orelse
   1273             break :blk &then_scope.base;
   1274 
   1275         const err_name = tree.tokenSlice(payload.castTag(.Payload).?.error_symbol.firstToken());
   1276         if (mem.eql(u8, err_name, "_"))
   1277             break :blk &then_scope.base;
   1278 
   1279         err_val_scope = .{
   1280             .parent = &then_scope.base,
   1281             .gen_zir = &then_scope,
   1282             .name = err_name,
   1283             .inst = try addZIRUnOp(mod, &then_scope.base, src, unwrap_code_op, operand),
   1284         };
   1285         break :blk &err_val_scope.base;
   1286     };
   1287 
   1288     _ = try addZIRInst(mod, &then_scope.base, src, zir.Inst.Break, .{
   1289         .block = block,
   1290         .operand = try expr(mod, then_sub_scope, branch_rl, rhs),
   1291     }, .{});
   1292 
   1293     var else_scope: Scope.GenZIR = .{
   1294         .parent = &block_scope.base,
   1295         .decl = block_scope.decl,
   1296         .arena = block_scope.arena,
   1297         .instructions = .{},
   1298     };
   1299     defer else_scope.instructions.deinit(mod.gpa);
   1300 
   1301     // This could be a pointer or value depending on `unwrap_op`.
   1302     const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand);
   1303     _ = try addZIRInst(mod, &else_scope.base, src, zir.Inst.Break, .{
   1304         .block = block,
   1305         .operand = unwrapped_payload,
   1306     }, .{});
   1307 
   1308     // All branches have been generated, add the instructions to the block.
   1309     block.positionals.body.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items);
   1310 
   1311     condbr.positionals.then_body = .{ .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items) };
   1312     condbr.positionals.else_body = .{ .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items) };
   1313     return &block.base;
   1314 }
   1315 
   1316 /// Return whether the identifier names of two tokens are equal. Resolves @""
   1317 /// tokens without allocating.
   1318 /// OK in theory it could do it without allocating. This implementation
   1319 /// allocates when the @"" form is used.
   1320 fn tokenIdentEql(mod: *Module, scope: *Scope, token1: ast.TokenIndex, token2: ast.TokenIndex) !bool {
   1321     const ident_name_1 = try mod.identifierTokenString(scope, token1);
   1322     const ident_name_2 = try mod.identifierTokenString(scope, token2);
   1323     return mem.eql(u8, ident_name_1, ident_name_2);
   1324 }
   1325 
   1326 pub fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {
   1327     const tree = scope.tree();
   1328     const src = tree.token_locs[node.op_token].start;
   1329     // TODO custom AST node for field access so that we don't have to go through a node cast here
   1330     const field_name = try mod.identifierTokenString(scope, node.rhs.castTag(.Identifier).?.token);
   1331     if (rl == .ref) {
   1332         return addZirInstTag(mod, scope, src, .field_ptr, .{
   1333             .object = try expr(mod, scope, .ref, node.lhs),
   1334             .field_name = field_name,
   1335         });
   1336     }
   1337     return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val, .{
   1338         .object = try expr(mod, scope, .none, node.lhs),
   1339         .field_name = field_name,
   1340     }));
   1341 }
   1342 
   1343 fn namedField(
   1344     mod: *Module,
   1345     scope: *Scope,
   1346     rl: ResultLoc,
   1347     call: *ast.Node.BuiltinCall,
   1348 ) InnerError!*zir.Inst {
   1349     try ensureBuiltinParamCount(mod, scope, call, 2);
   1350 
   1351     const tree = scope.tree();
   1352     const src = tree.token_locs[call.builtin_token].start;
   1353     const params = call.params();
   1354 
   1355     const string_type = try addZIRInstConst(mod, scope, src, .{
   1356         .ty = Type.initTag(.type),
   1357         .val = Value.initTag(.const_slice_u8_type),
   1358     });
   1359     const string_rl: ResultLoc = .{ .ty = string_type };
   1360 
   1361     if (rl == .ref) {
   1362         return addZirInstTag(mod, scope, src, .field_ptr_named, .{
   1363             .object = try expr(mod, scope, .ref, params[0]),
   1364             .field_name = try comptimeExpr(mod, scope, string_rl, params[1]),
   1365         });
   1366     }
   1367     return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .field_val_named, .{
   1368         .object = try expr(mod, scope, .none, params[0]),
   1369         .field_name = try comptimeExpr(mod, scope, string_rl, params[1]),
   1370     }));
   1371 }
   1372 
   1373 fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.ArrayAccess) InnerError!*zir.Inst {
   1374     const tree = scope.tree();
   1375     const src = tree.token_locs[node.rtoken].start;
   1376     const usize_type = try addZIRInstConst(mod, scope, src, .{
   1377         .ty = Type.initTag(.type),
   1378         .val = Value.initTag(.usize_type),
   1379     });
   1380     const index_rl: ResultLoc = .{ .ty = usize_type };
   1381 
   1382     if (rl == .ref) {
   1383         return addZirInstTag(mod, scope, src, .elem_ptr, .{
   1384             .array = try expr(mod, scope, .ref, node.lhs),
   1385             .index = try expr(mod, scope, index_rl, node.index_expr),
   1386         });
   1387     }
   1388     return rvalue(mod, scope, rl, try addZirInstTag(mod, scope, src, .elem_val, .{
   1389         .array = try expr(mod, scope, .none, node.lhs),
   1390         .index = try expr(mod, scope, index_rl, node.index_expr),
   1391     }));
   1392 }
   1393 
   1394 fn sliceExpr(mod: *Module, scope: *Scope, node: *ast.Node.Slice) InnerError!*zir.Inst {
   1395     const tree = scope.tree();
   1396     const src = tree.token_locs[node.rtoken].start;
   1397 
   1398     const usize_type = try addZIRInstConst(mod, scope, src, .{
   1399         .ty = Type.initTag(.type),
   1400         .val = Value.initTag(.usize_type),
   1401     });
   1402 
   1403     const array_ptr = try expr(mod, scope, .ref, node.lhs);
   1404     const start = try expr(mod, scope, .{ .ty = usize_type }, node.start);
   1405 
   1406     if (node.end == null and node.sentinel == null) {
   1407         return try addZIRBinOp(mod, scope, src, .slice_start, array_ptr, start);
   1408     }
   1409 
   1410     const end = if (node.end) |end| try expr(mod, scope, .{ .ty = usize_type }, end) else null;
   1411     // we could get the child type here, but it is easier to just do it in semantic analysis.
   1412     const sentinel = if (node.sentinel) |sentinel| try expr(mod, scope, .none, sentinel) else null;
   1413 
   1414     return try addZIRInst(
   1415         mod,
   1416         scope,
   1417         src,
   1418         zir.Inst.Slice,
   1419         .{ .array_ptr = array_ptr, .start = start },
   1420         .{ .end = end, .sentinel = sentinel },
   1421     );
   1422 }
   1423 
   1424 fn deref(mod: *Module, scope: *Scope, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst {
   1425     const tree = scope.tree();
   1426     const src = tree.token_locs[node.rtoken].start;
   1427     const lhs = try expr(mod, scope, .none, node.lhs);
   1428     return addZIRUnOp(mod, scope, src, .deref, lhs);
   1429 }
   1430 
   1431 fn simpleBinOp(
   1432     mod: *Module,
   1433     scope: *Scope,
   1434     rl: ResultLoc,
   1435     infix_node: *ast.Node.SimpleInfixOp,
   1436     op_inst_tag: zir.Inst.Tag,
   1437 ) InnerError!*zir.Inst {
   1438     const tree = scope.tree();
   1439     const src = tree.token_locs[infix_node.op_token].start;
   1440 
   1441     const lhs = try expr(mod, scope, .none, infix_node.lhs);
   1442     const rhs = try expr(mod, scope, .none, infix_node.rhs);
   1443 
   1444     const result = try addZIRBinOp(mod, scope, src, op_inst_tag, lhs, rhs);
   1445     return rvalue(mod, scope, rl, result);
   1446 }
   1447 
   1448 fn boolBinOp(
   1449     mod: *Module,
   1450     scope: *Scope,
   1451     rl: ResultLoc,
   1452     infix_node: *ast.Node.SimpleInfixOp,
   1453 ) InnerError!*zir.Inst {
   1454     const tree = scope.tree();
   1455     const src = tree.token_locs[infix_node.op_token].start;
   1456     const bool_type = try addZIRInstConst(mod, scope, src, .{
   1457         .ty = Type.initTag(.type),
   1458         .val = Value.initTag(.bool_type),
   1459     });
   1460 
   1461     var block_scope: Scope.GenZIR = .{
   1462         .parent = scope,
   1463         .decl = scope.ownerDecl().?,
   1464         .arena = scope.arena(),
   1465         .instructions = .{},
   1466     };
   1467     defer block_scope.instructions.deinit(mod.gpa);
   1468 
   1469     const lhs = try expr(mod, scope, .{ .ty = bool_type }, infix_node.lhs);
   1470     const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
   1471         .condition = lhs,
   1472         .then_body = undefined, // populated below
   1473         .else_body = undefined, // populated below
   1474     }, .{});
   1475 
   1476     const block = try addZIRInstBlock(mod, scope, src, .block, .{
   1477         .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
   1478     });
   1479 
   1480     var rhs_scope: Scope.GenZIR = .{
   1481         .parent = scope,
   1482         .decl = block_scope.decl,
   1483         .arena = block_scope.arena,
   1484         .instructions = .{},
   1485     };
   1486     defer rhs_scope.instructions.deinit(mod.gpa);
   1487 
   1488     const rhs = try expr(mod, &rhs_scope.base, .{ .ty = bool_type }, infix_node.rhs);
   1489     _ = try addZIRInst(mod, &rhs_scope.base, src, zir.Inst.Break, .{
   1490         .block = block,
   1491         .operand = rhs,
   1492     }, .{});
   1493 
   1494     var const_scope: Scope.GenZIR = .{
   1495         .parent = scope,
   1496         .decl = block_scope.decl,
   1497         .arena = block_scope.arena,
   1498         .instructions = .{},
   1499     };
   1500     defer const_scope.instructions.deinit(mod.gpa);
   1501 
   1502     const is_bool_and = infix_node.base.tag == .BoolAnd;
   1503     _ = try addZIRInst(mod, &const_scope.base, src, zir.Inst.Break, .{
   1504         .block = block,
   1505         .operand = try addZIRInstConst(mod, &const_scope.base, src, .{
   1506             .ty = Type.initTag(.bool),
   1507             .val = if (is_bool_and) Value.initTag(.bool_false) else Value.initTag(.bool_true),
   1508         }),
   1509     }, .{});
   1510 
   1511     if (is_bool_and) {
   1512         // if lhs // AND
   1513         //     break rhs
   1514         // else
   1515         //     break false
   1516         condbr.positionals.then_body = .{ .instructions = try rhs_scope.arena.dupe(*zir.Inst, rhs_scope.instructions.items) };
   1517         condbr.positionals.else_body = .{ .instructions = try const_scope.arena.dupe(*zir.Inst, const_scope.instructions.items) };
   1518     } else {
   1519         // if lhs // OR
   1520         //     break true
   1521         // else
   1522         //     break rhs
   1523         condbr.positionals.then_body = .{ .instructions = try const_scope.arena.dupe(*zir.Inst, const_scope.instructions.items) };
   1524         condbr.positionals.else_body = .{ .instructions = try rhs_scope.arena.dupe(*zir.Inst, rhs_scope.instructions.items) };
   1525     }
   1526 
   1527     return rvalue(mod, scope, rl, &block.base);
   1528 }
   1529 
   1530 const CondKind = union(enum) {
   1531     bool,
   1532     optional: ?*zir.Inst,
   1533     err_union: ?*zir.Inst,
   1534 
   1535     fn cond(self: *CondKind, mod: *Module, block_scope: *Scope.GenZIR, src: usize, cond_node: *ast.Node) !*zir.Inst {
   1536         switch (self.*) {
   1537             .bool => {
   1538                 const bool_type = try addZIRInstConst(mod, &block_scope.base, src, .{
   1539                     .ty = Type.initTag(.type),
   1540                     .val = Value.initTag(.bool_type),
   1541                 });
   1542                 return try expr(mod, &block_scope.base, .{ .ty = bool_type }, cond_node);
   1543             },
   1544             .optional => {
   1545                 const cond_ptr = try expr(mod, &block_scope.base, .ref, cond_node);
   1546                 self.* = .{ .optional = cond_ptr };
   1547                 const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, cond_ptr);
   1548                 return try addZIRUnOp(mod, &block_scope.base, src, .is_non_null, result);
   1549             },
   1550             .err_union => {
   1551                 const err_ptr = try expr(mod, &block_scope.base, .ref, cond_node);
   1552                 self.* = .{ .err_union = err_ptr };
   1553                 const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, err_ptr);
   1554                 return try addZIRUnOp(mod, &block_scope.base, src, .is_err, result);
   1555             },
   1556         }
   1557     }
   1558 
   1559     fn thenSubScope(self: CondKind, mod: *Module, then_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope {
   1560         if (self == .bool) return &then_scope.base;
   1561 
   1562         const payload = payload_node.?.castTag(.PointerPayload) orelse {
   1563             // condition is error union and payload is not explicitly ignored
   1564             _ = try addZIRUnOp(mod, &then_scope.base, src, .ensure_err_payload_void, self.err_union.?);
   1565             return &then_scope.base;
   1566         };
   1567         const is_ptr = payload.ptr_token != null;
   1568         const ident_node = payload.value_symbol.castTag(.Identifier).?;
   1569 
   1570         // This intentionally does not support @"_" syntax.
   1571         const ident_name = then_scope.base.tree().tokenSlice(ident_node.token);
   1572         if (mem.eql(u8, ident_name, "_")) {
   1573             if (is_ptr)
   1574                 return mod.failTok(&then_scope.base, payload.ptr_token.?, "pointer modifier invalid on discard", .{});
   1575             return &then_scope.base;
   1576         }
   1577 
   1578         return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement payload symbols", .{});
   1579     }
   1580 
   1581     fn elseSubScope(self: CondKind, mod: *Module, else_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope {
   1582         if (self != .err_union) return &else_scope.base;
   1583 
   1584         const payload_ptr = try addZIRUnOp(mod, &else_scope.base, src, .err_union_payload_unsafe_ptr, self.err_union.?);
   1585 
   1586         const payload = payload_node.?.castTag(.Payload).?;
   1587         const ident_node = payload.error_symbol.castTag(.Identifier).?;
   1588 
   1589         // This intentionally does not support @"_" syntax.
   1590         const ident_name = else_scope.base.tree().tokenSlice(ident_node.token);
   1591         if (mem.eql(u8, ident_name, "_")) {
   1592             return &else_scope.base;
   1593         }
   1594 
   1595         return mod.failNode(&else_scope.base, payload.error_symbol, "TODO implement payload symbols", .{});
   1596     }
   1597 };
   1598 
   1599 fn ifExpr(mod: *Module, scope: *Scope, rl: ResultLoc, if_node: *ast.Node.If) InnerError!*zir.Inst {
   1600     var cond_kind: CondKind = .bool;
   1601     if (if_node.payload) |_| cond_kind = .{ .optional = null };
   1602     if (if_node.@"else") |else_node| {
   1603         if (else_node.payload) |payload| {
   1604             cond_kind = .{ .err_union = null };
   1605         }
   1606     }
   1607     const block_branch_count = 2; // then and else
   1608     var block_scope: Scope.GenZIR = .{
   1609         .parent = scope,
   1610         .decl = scope.ownerDecl().?,
   1611         .arena = scope.arena(),
   1612         .instructions = .{},
   1613     };
   1614     defer block_scope.instructions.deinit(mod.gpa);
   1615 
   1616     const tree = scope.tree();
   1617     const if_src = tree.token_locs[if_node.if_token].start;
   1618     const cond = try cond_kind.cond(mod, &block_scope, if_src, if_node.condition);
   1619 
   1620     const condbr = try addZIRInstSpecial(mod, &block_scope.base, if_src, zir.Inst.CondBr, .{
   1621         .condition = cond,
   1622         .then_body = undefined, // populated below
   1623         .else_body = undefined, // populated below
   1624     }, .{});
   1625 
   1626     const block = try addZIRInstBlock(mod, scope, if_src, .block, .{
   1627         .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
   1628     });
   1629 
   1630     // Depending on whether the result location is a pointer or value, different
   1631     // ZIR needs to be generated. In the former case we rely on storing to the
   1632     // pointer to communicate the result, and use breakvoid; in the latter case
   1633     // the block break instructions will have the result values.
   1634     // One more complication: when the result location is a pointer, we detect
   1635     // the scenario where the result location is not consumed. In this case
   1636     // we emit ZIR for the block break instructions to have the result values,
   1637     // and then rvalue() on that to pass the value to the result location.
   1638     const branch_rl: ResultLoc = switch (rl) {
   1639         .discard, .none, .ty, .ptr, .ref => rl,
   1640 
   1641         .inferred_ptr => |ptr| blk: {
   1642             block_scope.rl_ptr = &ptr.base;
   1643             break :blk .{ .block_ptr = &block_scope };
   1644         },
   1645 
   1646         .bitcasted_ptr => |ptr| blk: {
   1647             block_scope.rl_ptr = &ptr.base;
   1648             break :blk .{ .block_ptr = &block_scope };
   1649         },
   1650 
   1651         .block_ptr => |parent_block_scope| blk: {
   1652             block_scope.rl_ptr = parent_block_scope.rl_ptr.?;
   1653             break :blk .{ .block_ptr = &block_scope };
   1654         },
   1655     };
   1656 
   1657     const then_src = tree.token_locs[if_node.body.lastToken()].start;
   1658     var then_scope: Scope.GenZIR = .{
   1659         .parent = scope,
   1660         .decl = block_scope.decl,
   1661         .arena = block_scope.arena,
   1662         .instructions = .{},
   1663     };
   1664     defer then_scope.instructions.deinit(mod.gpa);
   1665 
   1666     // declare payload to the then_scope
   1667     const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, if_node.payload);
   1668 
   1669     const then_result = try expr(mod, then_sub_scope, branch_rl, if_node.body);
   1670     // We hold off on the break instructions as well as copying the then/else
   1671     // instructions into place until we know whether to keep store_to_block_ptr
   1672     // instructions or not.
   1673 
   1674     var else_scope: Scope.GenZIR = .{
   1675         .parent = scope,
   1676         .decl = block_scope.decl,
   1677         .arena = block_scope.arena,
   1678         .instructions = .{},
   1679     };
   1680     defer else_scope.instructions.deinit(mod.gpa);
   1681 
   1682     var else_src: usize = undefined;
   1683     var else_sub_scope: *Module.Scope = undefined;
   1684     const else_result: ?*zir.Inst = if (if_node.@"else") |else_node| blk: {
   1685         else_src = tree.token_locs[else_node.body.lastToken()].start;
   1686         // declare payload to the then_scope
   1687         else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload);
   1688 
   1689         break :blk try expr(mod, else_sub_scope, branch_rl, else_node.body);
   1690     } else blk: {
   1691         else_src = tree.token_locs[if_node.lastToken()].start;
   1692         else_sub_scope = &else_scope.base;
   1693         block_scope.rvalue_rl_count += 1;
   1694         break :blk null;
   1695     };
   1696 
   1697     // We now have enough information to decide whether the result instruction should
   1698     // be communicated via result location pointer or break instructions.
   1699     const Strategy = enum {
   1700         /// Both branches will use break_void; result location is used to communicate the
   1701         /// result instruction.
   1702         break_void,
   1703         /// Use break statements to pass the block result value, and call rvalue() at
   1704         /// the end depending on rl. Also elide the store_to_block_ptr instructions
   1705         /// depending on rl.
   1706         break_operand,
   1707     };
   1708     var elide_store_to_block_ptr_instructions = false;
   1709     const strategy: Strategy = switch (rl) {
   1710         // In this branch there will not be any store_to_block_ptr instructions.
   1711         .discard, .none, .ty, .ref => .break_operand,
   1712         // The pointer got passed through to the sub-expressions, so we will use
   1713         // break_void here.
   1714         // In this branch there will not be any store_to_block_ptr instructions.
   1715         .ptr => .break_void,
   1716         .inferred_ptr, .bitcasted_ptr, .block_ptr => blk: {
   1717             if (block_scope.rvalue_rl_count == 2) {
   1718                 // Neither prong of the if consumed the result location, so we can
   1719                 // use break instructions to create an rvalue.
   1720                 elide_store_to_block_ptr_instructions = true;
   1721                 break :blk Strategy.break_operand;
   1722             } else {
   1723                 // Allow the store_to_block_ptr instructions to remain so that
   1724                 // semantic analysis can turn them into bitcasts.
   1725                 break :blk Strategy.break_void;
   1726             }
   1727         },
   1728     };
   1729     switch (strategy) {
   1730         .break_void => {
   1731             if (!then_result.tag.isNoReturn()) {
   1732                 _ = try addZirInstTag(mod, then_sub_scope, then_src, .break_void, .{
   1733                     .block = block,
   1734                 });
   1735             }
   1736             if (else_result) |inst| {
   1737                 if (!inst.tag.isNoReturn()) {
   1738                     _ = try addZirInstTag(mod, else_sub_scope, else_src, .break_void, .{
   1739                         .block = block,
   1740                     });
   1741                 }
   1742             } else {
   1743                 _ = try addZirInstTag(mod, else_sub_scope, else_src, .break_void, .{
   1744                     .block = block,
   1745                 });
   1746             }
   1747             assert(!elide_store_to_block_ptr_instructions);
   1748             try copyBodyNoEliding(&condbr.positionals.then_body, then_scope);
   1749             try copyBodyNoEliding(&condbr.positionals.else_body, else_scope);
   1750             return &block.base;
   1751         },
   1752         .break_operand => {
   1753             if (!then_result.tag.isNoReturn()) {
   1754                 _ = try addZirInstTag(mod, then_sub_scope, then_src, .@"break", .{
   1755                     .block = block,
   1756                     .operand = then_result,
   1757                 });
   1758             }
   1759             if (else_result) |inst| {
   1760                 if (!inst.tag.isNoReturn()) {
   1761                     _ = try addZirInstTag(mod, else_sub_scope, else_src, .@"break", .{
   1762                         .block = block,
   1763                         .operand = inst,
   1764                     });
   1765                 }
   1766             } else {
   1767                 _ = try addZirInstTag(mod, else_sub_scope, else_src, .break_void, .{
   1768                     .block = block,
   1769                 });
   1770             }
   1771             if (elide_store_to_block_ptr_instructions) {
   1772                 try copyBodyWithElidedStoreBlockPtr(&condbr.positionals.then_body, then_scope);
   1773                 try copyBodyWithElidedStoreBlockPtr(&condbr.positionals.else_body, else_scope);
   1774             } else {
   1775                 try copyBodyNoEliding(&condbr.positionals.then_body, then_scope);
   1776                 try copyBodyNoEliding(&condbr.positionals.else_body, else_scope);
   1777             }
   1778             switch (rl) {
   1779                 .ref => return &block.base,
   1780                 else => return rvalue(mod, scope, rl, &block.base),
   1781             }
   1782         },
   1783     }
   1784 }
   1785 
   1786 /// Expects to find exactly 1 .store_to_block_ptr instruction.
   1787 fn copyBodyWithElidedStoreBlockPtr(body: *zir.Body, scope: Module.Scope.GenZIR) !void {
   1788     body.* = .{
   1789         .instructions = try scope.arena.alloc(*zir.Inst, scope.instructions.items.len - 1),
   1790     };
   1791     var dst_index: usize = 0;
   1792     for (scope.instructions.items) |src_inst| {
   1793         if (src_inst.tag != .store_to_block_ptr) {
   1794             body.instructions[dst_index] = src_inst;
   1795             dst_index += 1;
   1796         }
   1797     }
   1798     assert(dst_index == body.instructions.len);
   1799 }
   1800 
   1801 fn copyBodyNoEliding(body: *zir.Body, scope: Module.Scope.GenZIR) !void {
   1802     body.* = .{
   1803         .instructions = try scope.arena.dupe(*zir.Inst, scope.instructions.items),
   1804     };
   1805 }
   1806 
   1807 fn whileExpr(mod: *Module, scope: *Scope, rl: ResultLoc, while_node: *ast.Node.While) InnerError!*zir.Inst {
   1808     if (true) {
   1809         @panic("TODO reimplement this");
   1810     }
   1811     var cond_kind: CondKind = .bool;
   1812     if (while_node.payload) |_| cond_kind = .{ .optional = null };
   1813     if (while_node.@"else") |else_node| {
   1814         if (else_node.payload) |payload| {
   1815             cond_kind = .{ .err_union = null };
   1816         }
   1817     }
   1818 
   1819     if (while_node.label) |label| {
   1820         try checkLabelRedefinition(mod, scope, label);
   1821     }
   1822 
   1823     if (while_node.inline_token) |tok|
   1824         return mod.failTok(scope, tok, "TODO inline while", .{});
   1825 
   1826     var expr_scope: Scope.GenZIR = .{
   1827         .parent = scope,
   1828         .decl = scope.ownerDecl().?,
   1829         .arena = scope.arena(),
   1830         .instructions = .{},
   1831     };
   1832     defer expr_scope.instructions.deinit(mod.gpa);
   1833 
   1834     var loop_scope: Scope.GenZIR = .{
   1835         .parent = &expr_scope.base,
   1836         .decl = expr_scope.decl,
   1837         .arena = expr_scope.arena,
   1838         .instructions = .{},
   1839         .break_result_loc = rl,
   1840     };
   1841     defer loop_scope.instructions.deinit(mod.gpa);
   1842 
   1843     var continue_scope: Scope.GenZIR = .{
   1844         .parent = &loop_scope.base,
   1845         .decl = loop_scope.decl,
   1846         .arena = loop_scope.arena,
   1847         .instructions = .{},
   1848     };
   1849     defer continue_scope.instructions.deinit(mod.gpa);
   1850 
   1851     const tree = scope.tree();
   1852     const while_src = tree.token_locs[while_node.while_token].start;
   1853     const void_type = try addZIRInstConst(mod, scope, while_src, .{
   1854         .ty = Type.initTag(.type),
   1855         .val = Value.initTag(.void_type),
   1856     });
   1857     const cond = try cond_kind.cond(mod, &continue_scope, while_src, while_node.condition);
   1858 
   1859     const condbr = try addZIRInstSpecial(mod, &continue_scope.base, while_src, zir.Inst.CondBr, .{
   1860         .condition = cond,
   1861         .then_body = undefined, // populated below
   1862         .else_body = undefined, // populated below
   1863     }, .{});
   1864     const cond_block = try addZIRInstBlock(mod, &loop_scope.base, while_src, .block, .{
   1865         .instructions = try loop_scope.arena.dupe(*zir.Inst, continue_scope.instructions.items),
   1866     });
   1867     // TODO avoid emitting the continue expr when there
   1868     // are no jumps to it. This happens when the last statement of a while body is noreturn
   1869     // and there are no `continue` statements.
   1870     // The "repeat" at the end of a loop body is implied.
   1871     if (while_node.continue_expr) |cont_expr| {
   1872         _ = try expr(mod, &loop_scope.base, .{ .ty = void_type }, cont_expr);
   1873     }
   1874     const loop = try addZIRInstLoop(mod, &expr_scope.base, while_src, .{
   1875         .instructions = try expr_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items),
   1876     });
   1877     const while_block = try addZIRInstBlock(mod, scope, while_src, .block, .{
   1878         .instructions = try expr_scope.arena.dupe(*zir.Inst, expr_scope.instructions.items),
   1879     });
   1880     loop_scope.break_block = while_block;
   1881     loop_scope.continue_block = cond_block;
   1882     if (while_node.label) |some| {
   1883         loop_scope.label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
   1884             .token = some,
   1885             .block_inst = while_block,
   1886         });
   1887     }
   1888 
   1889     const then_src = tree.token_locs[while_node.body.lastToken()].start;
   1890     var then_scope: Scope.GenZIR = .{
   1891         .parent = &continue_scope.base,
   1892         .decl = continue_scope.decl,
   1893         .arena = continue_scope.arena,
   1894         .instructions = .{},
   1895     };
   1896     defer then_scope.instructions.deinit(mod.gpa);
   1897 
   1898     // declare payload to the then_scope
   1899     const then_sub_scope = try cond_kind.thenSubScope(mod, &then_scope, then_src, while_node.payload);
   1900 
   1901     // Most result location types can be forwarded directly; however
   1902     // if we need to write to a pointer which has an inferred type,
   1903     // proper type inference requires peer type resolution on the while's
   1904     // branches.
   1905     const branch_rl: ResultLoc = switch (rl) {
   1906         .discard, .none, .ty, .ptr, .ref => rl,
   1907         .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = while_block },
   1908     };
   1909 
   1910     const then_result = try expr(mod, then_sub_scope, branch_rl, while_node.body);
   1911     if (!then_result.tag.isNoReturn()) {
   1912         _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
   1913             .block = cond_block,
   1914             .operand = then_result,
   1915         }, .{});
   1916     }
   1917     condbr.positionals.then_body = .{
   1918         .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
   1919     };
   1920 
   1921     var else_scope: Scope.GenZIR = .{
   1922         .parent = &continue_scope.base,
   1923         .decl = continue_scope.decl,
   1924         .arena = continue_scope.arena,
   1925         .instructions = .{},
   1926     };
   1927     defer else_scope.instructions.deinit(mod.gpa);
   1928 
   1929     if (while_node.@"else") |else_node| {
   1930         const else_src = tree.token_locs[else_node.body.lastToken()].start;
   1931         // declare payload to the then_scope
   1932         const else_sub_scope = try cond_kind.elseSubScope(mod, &else_scope, else_src, else_node.payload);
   1933 
   1934         const else_result = try expr(mod, else_sub_scope, branch_rl, else_node.body);
   1935         if (!else_result.tag.isNoReturn()) {
   1936             _ = try addZIRInst(mod, else_sub_scope, else_src, zir.Inst.Break, .{
   1937                 .block = while_block,
   1938                 .operand = else_result,
   1939             }, .{});
   1940         }
   1941     } else {
   1942         const else_src = tree.token_locs[while_node.lastToken()].start;
   1943         _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{
   1944             .block = while_block,
   1945         }, .{});
   1946     }
   1947     condbr.positionals.else_body = .{
   1948         .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
   1949     };
   1950     if (loop_scope.label) |some| {
   1951         if (!some.used) {
   1952             return mod.fail(scope, tree.token_locs[some.token].start, "unused while label", .{});
   1953         }
   1954     }
   1955     return &while_block.base;
   1956 }
   1957 
   1958 fn forExpr(
   1959     mod: *Module,
   1960     scope: *Scope,
   1961     rl: ResultLoc,
   1962     for_node: *ast.Node.For,
   1963 ) InnerError!*zir.Inst {
   1964     if (true) {
   1965         @panic("TODO reimplement this");
   1966     }
   1967     if (for_node.label) |label| {
   1968         try checkLabelRedefinition(mod, scope, label);
   1969     }
   1970 
   1971     if (for_node.inline_token) |tok|
   1972         return mod.failTok(scope, tok, "TODO inline for", .{});
   1973 
   1974     var for_scope: Scope.GenZIR = .{
   1975         .parent = scope,
   1976         .decl = scope.ownerDecl().?,
   1977         .arena = scope.arena(),
   1978         .instructions = .{},
   1979     };
   1980     defer for_scope.instructions.deinit(mod.gpa);
   1981 
   1982     // setup variables and constants
   1983     const tree = scope.tree();
   1984     const for_src = tree.token_locs[for_node.for_token].start;
   1985     const index_ptr = blk: {
   1986         const usize_type = try addZIRInstConst(mod, &for_scope.base, for_src, .{
   1987             .ty = Type.initTag(.type),
   1988             .val = Value.initTag(.usize_type),
   1989         });
   1990         const index_ptr = try addZIRUnOp(mod, &for_scope.base, for_src, .alloc, usize_type);
   1991         // initialize to zero
   1992         const zero = try addZIRInstConst(mod, &for_scope.base, for_src, .{
   1993             .ty = Type.initTag(.usize),
   1994             .val = Value.initTag(.zero),
   1995         });
   1996         _ = try addZIRBinOp(mod, &for_scope.base, for_src, .store, index_ptr, zero);
   1997         break :blk index_ptr;
   1998     };
   1999     const array_ptr = try expr(mod, &for_scope.base, .ref, for_node.array_expr);
   2000     const cond_src = tree.token_locs[for_node.array_expr.firstToken()].start;
   2001     const len = try addZIRUnOp(mod, &for_scope.base, cond_src, .indexable_ptr_len, array_ptr);
   2002 
   2003     var loop_scope: Scope.GenZIR = .{
   2004         .parent = &for_scope.base,
   2005         .decl = for_scope.decl,
   2006         .arena = for_scope.arena,
   2007         .instructions = .{},
   2008         .break_result_loc = rl,
   2009     };
   2010     defer loop_scope.instructions.deinit(mod.gpa);
   2011 
   2012     var cond_scope: Scope.GenZIR = .{
   2013         .parent = &loop_scope.base,
   2014         .decl = loop_scope.decl,
   2015         .arena = loop_scope.arena,
   2016         .instructions = .{},
   2017     };
   2018     defer cond_scope.instructions.deinit(mod.gpa);
   2019 
   2020     // check condition i < array_expr.len
   2021     const index = try addZIRUnOp(mod, &cond_scope.base, cond_src, .deref, index_ptr);
   2022     const cond = try addZIRBinOp(mod, &cond_scope.base, cond_src, .cmp_lt, index, len);
   2023 
   2024     const condbr = try addZIRInstSpecial(mod, &cond_scope.base, for_src, zir.Inst.CondBr, .{
   2025         .condition = cond,
   2026         .then_body = undefined, // populated below
   2027         .else_body = undefined, // populated below
   2028     }, .{});
   2029     const cond_block = try addZIRInstBlock(mod, &loop_scope.base, for_src, .block, .{
   2030         .instructions = try loop_scope.arena.dupe(*zir.Inst, cond_scope.instructions.items),
   2031     });
   2032 
   2033     // increment index variable
   2034     const one = try addZIRInstConst(mod, &loop_scope.base, for_src, .{
   2035         .ty = Type.initTag(.usize),
   2036         .val = Value.initTag(.one),
   2037     });
   2038     const index_2 = try addZIRUnOp(mod, &loop_scope.base, cond_src, .deref, index_ptr);
   2039     const index_plus_one = try addZIRBinOp(mod, &loop_scope.base, for_src, .add, index_2, one);
   2040     _ = try addZIRBinOp(mod, &loop_scope.base, for_src, .store, index_ptr, index_plus_one);
   2041 
   2042     // looping stuff
   2043     const loop = try addZIRInstLoop(mod, &for_scope.base, for_src, .{
   2044         .instructions = try for_scope.arena.dupe(*zir.Inst, loop_scope.instructions.items),
   2045     });
   2046     const for_block = try addZIRInstBlock(mod, scope, for_src, .block, .{
   2047         .instructions = try for_scope.arena.dupe(*zir.Inst, for_scope.instructions.items),
   2048     });
   2049     loop_scope.break_block = for_block;
   2050     loop_scope.continue_block = cond_block;
   2051     if (for_node.label) |some| {
   2052         loop_scope.label = @as(?Scope.GenZIR.Label, Scope.GenZIR.Label{
   2053             .token = some,
   2054             .block_inst = for_block,
   2055         });
   2056     }
   2057 
   2058     // while body
   2059     const then_src = tree.token_locs[for_node.body.lastToken()].start;
   2060     var then_scope: Scope.GenZIR = .{
   2061         .parent = &cond_scope.base,
   2062         .decl = cond_scope.decl,
   2063         .arena = cond_scope.arena,
   2064         .instructions = .{},
   2065     };
   2066     defer then_scope.instructions.deinit(mod.gpa);
   2067 
   2068     // Most result location types can be forwarded directly; however
   2069     // if we need to write to a pointer which has an inferred type,
   2070     // proper type inference requires peer type resolution on the while's
   2071     // branches.
   2072     const branch_rl: ResultLoc = switch (rl) {
   2073         .discard, .none, .ty, .ptr, .ref => rl,
   2074         .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = for_block },
   2075     };
   2076 
   2077     var index_scope: Scope.LocalPtr = undefined;
   2078     const then_sub_scope = blk: {
   2079         const payload = for_node.payload.castTag(.PointerIndexPayload).?;
   2080         const is_ptr = payload.ptr_token != null;
   2081         const value_name = tree.tokenSlice(payload.value_symbol.firstToken());
   2082         if (!mem.eql(u8, value_name, "_")) {
   2083             return mod.failNode(&then_scope.base, payload.value_symbol, "TODO implement for value payload", .{});
   2084         } else if (is_ptr) {
   2085             return mod.failTok(&then_scope.base, payload.ptr_token.?, "pointer modifier invalid on discard", .{});
   2086         }
   2087 
   2088         const index_symbol_node = payload.index_symbol orelse
   2089             break :blk &then_scope.base;
   2090 
   2091         const index_name = tree.tokenSlice(index_symbol_node.firstToken());
   2092         if (mem.eql(u8, index_name, "_")) {
   2093             break :blk &then_scope.base;
   2094         }
   2095         // TODO make this const without an extra copy?
   2096         index_scope = .{
   2097             .parent = &then_scope.base,
   2098             .gen_zir = &then_scope,
   2099             .name = index_name,
   2100             .ptr = index_ptr,
   2101         };
   2102         break :blk &index_scope.base;
   2103     };
   2104 
   2105     const then_result = try expr(mod, then_sub_scope, branch_rl, for_node.body);
   2106     if (!then_result.tag.isNoReturn()) {
   2107         _ = try addZIRInst(mod, then_sub_scope, then_src, zir.Inst.Break, .{
   2108             .block = cond_block,
   2109             .operand = then_result,
   2110         }, .{});
   2111     }
   2112     condbr.positionals.then_body = .{
   2113         .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items),
   2114     };
   2115 
   2116     // else branch
   2117     var else_scope: Scope.GenZIR = .{
   2118         .parent = &cond_scope.base,
   2119         .decl = cond_scope.decl,
   2120         .arena = cond_scope.arena,
   2121         .instructions = .{},
   2122     };
   2123     defer else_scope.instructions.deinit(mod.gpa);
   2124 
   2125     if (for_node.@"else") |else_node| {
   2126         const else_src = tree.token_locs[else_node.body.lastToken()].start;
   2127         const else_result = try expr(mod, &else_scope.base, branch_rl, else_node.body);
   2128         if (!else_result.tag.isNoReturn()) {
   2129             _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.Break, .{
   2130                 .block = for_block,
   2131                 .operand = else_result,
   2132             }, .{});
   2133         }
   2134     } else {
   2135         const else_src = tree.token_locs[for_node.lastToken()].start;
   2136         _ = try addZIRInst(mod, &else_scope.base, else_src, zir.Inst.BreakVoid, .{
   2137             .block = for_block,
   2138         }, .{});
   2139     }
   2140     condbr.positionals.else_body = .{
   2141         .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
   2142     };
   2143     if (loop_scope.label) |some| {
   2144         if (!some.used) {
   2145             return mod.fail(scope, tree.token_locs[some.token].start, "unused for label", .{});
   2146         }
   2147     }
   2148     return &for_block.base;
   2149 }
   2150 
   2151 fn getRangeNode(node: *ast.Node) ?*ast.Node.SimpleInfixOp {
   2152     var cur = node;
   2153     while (true) {
   2154         switch (cur.tag) {
   2155             .Range => return @fieldParentPtr(ast.Node.SimpleInfixOp, "base", cur),
   2156             .GroupedExpression => cur = @fieldParentPtr(ast.Node.GroupedExpression, "base", cur).expr,
   2157             else => return null,
   2158         }
   2159     }
   2160 }
   2161 
   2162 fn switchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, switch_node: *ast.Node.Switch) InnerError!*zir.Inst {
   2163     if (true) {
   2164         @panic("TODO reimplement this");
   2165     }
   2166     var block_scope: Scope.GenZIR = .{
   2167         .parent = scope,
   2168         .decl = scope.ownerDecl().?,
   2169         .arena = scope.arena(),
   2170         .instructions = .{},
   2171     };
   2172     defer block_scope.instructions.deinit(mod.gpa);
   2173 
   2174     const tree = scope.tree();
   2175     const switch_src = tree.token_locs[switch_node.switch_token].start;
   2176     const target_ptr = try expr(mod, &block_scope.base, .ref, switch_node.expr);
   2177     const target = try addZIRUnOp(mod, &block_scope.base, target_ptr.src, .deref, target_ptr);
   2178     // Add the switch instruction here so that it comes before any range checks.
   2179     const switch_inst = (try addZIRInst(mod, &block_scope.base, switch_src, zir.Inst.SwitchBr, .{
   2180         .target_ptr = target_ptr,
   2181         .cases = undefined, // populated below
   2182         .items = &[_]*zir.Inst{}, // populated below
   2183         .else_body = undefined, // populated below
   2184     }, .{})).castTag(.switchbr).?;
   2185 
   2186     var items = std.ArrayList(*zir.Inst).init(mod.gpa);
   2187     defer items.deinit();
   2188     var cases = std.ArrayList(zir.Inst.SwitchBr.Case).init(mod.gpa);
   2189     defer cases.deinit();
   2190 
   2191     // Add comptime block containing all prong items first,
   2192     const item_block = try addZIRInstBlock(mod, scope, switch_src, .block_comptime_flat, .{
   2193         .instructions = undefined, // populated below
   2194     });
   2195     // then add block containing the switch.
   2196     const block = try addZIRInstBlock(mod, scope, switch_src, .block, .{
   2197         .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items),
   2198     });
   2199 
   2200     // Most result location types can be forwarded directly; however
   2201     // if we need to write to a pointer which has an inferred type,
   2202     // proper type inference requires peer type resolution on the switch case.
   2203     const case_rl: ResultLoc = switch (rl) {
   2204         .discard, .none, .ty, .ptr, .ref => rl,
   2205         .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },
   2206     };
   2207 
   2208     var item_scope: Scope.GenZIR = .{
   2209         .parent = scope,
   2210         .decl = scope.ownerDecl().?,
   2211         .arena = scope.arena(),
   2212         .instructions = .{},
   2213     };
   2214     defer item_scope.instructions.deinit(mod.gpa);
   2215 
   2216     var case_scope: Scope.GenZIR = .{
   2217         .parent = scope,
   2218         .decl = block_scope.decl,
   2219         .arena = block_scope.arena,
   2220         .instructions = .{},
   2221     };
   2222     defer case_scope.instructions.deinit(mod.gpa);
   2223 
   2224     var else_scope: Scope.GenZIR = .{
   2225         .parent = scope,
   2226         .decl = block_scope.decl,
   2227         .arena = block_scope.arena,
   2228         .instructions = .{},
   2229     };
   2230     defer else_scope.instructions.deinit(mod.gpa);
   2231 
   2232     // first we gather all the switch items and check else/'_' prongs
   2233     var else_src: ?usize = null;
   2234     var underscore_src: ?usize = null;
   2235     var first_range: ?*zir.Inst = null;
   2236     var special_case: ?*ast.Node.SwitchCase = null;
   2237     for (switch_node.cases()) |uncasted_case| {
   2238         const case = uncasted_case.castTag(.SwitchCase).?;
   2239         const case_src = tree.token_locs[case.firstToken()].start;
   2240         // reset without freeing to reduce allocations.
   2241         case_scope.instructions.items.len = 0;
   2242         assert(case.items_len != 0);
   2243 
   2244         // Check for else/_ prong, those are handled last.
   2245         if (case.items_len == 1 and case.items()[0].tag == .SwitchElse) {
   2246             if (else_src) |src| {
   2247                 const msg = msg: {
   2248                     const msg = try mod.errMsg(
   2249                         scope,
   2250                         case_src,
   2251                         "multiple else prongs in switch expression",
   2252                         .{},
   2253                     );
   2254                     errdefer msg.destroy(mod.gpa);
   2255                     try mod.errNote(scope, src, msg, "previous else prong is here", .{});
   2256                     break :msg msg;
   2257                 };
   2258                 return mod.failWithOwnedErrorMsg(scope, msg);
   2259             }
   2260             else_src = case_src;
   2261             special_case = case;
   2262             continue;
   2263         } else if (case.items_len == 1 and case.items()[0].tag == .Identifier and
   2264             mem.eql(u8, tree.tokenSlice(case.items()[0].firstToken()), "_"))
   2265         {
   2266             if (underscore_src) |src| {
   2267                 const msg = msg: {
   2268                     const msg = try mod.errMsg(
   2269                         scope,
   2270                         case_src,
   2271                         "multiple '_' prongs in switch expression",
   2272                         .{},
   2273                     );
   2274                     errdefer msg.destroy(mod.gpa);
   2275                     try mod.errNote(scope, src, msg, "previous '_' prong is here", .{});
   2276                     break :msg msg;
   2277                 };
   2278                 return mod.failWithOwnedErrorMsg(scope, msg);
   2279             }
   2280             underscore_src = case_src;
   2281             special_case = case;
   2282             continue;
   2283         }
   2284 
   2285         if (else_src) |some_else| {
   2286             if (underscore_src) |some_underscore| {
   2287                 const msg = msg: {
   2288                     const msg = try mod.errMsg(
   2289                         scope,
   2290                         switch_src,
   2291                         "else and '_' prong in switch expression",
   2292                         .{},
   2293                     );
   2294                     errdefer msg.destroy(mod.gpa);
   2295                     try mod.errNote(scope, some_else, msg, "else prong is here", .{});
   2296                     try mod.errNote(scope, some_underscore, msg, "'_' prong is here", .{});
   2297                     break :msg msg;
   2298                 };
   2299                 return mod.failWithOwnedErrorMsg(scope, msg);
   2300             }
   2301         }
   2302 
   2303         // If this is a simple one item prong then it is handled by the switchbr.
   2304         if (case.items_len == 1 and getRangeNode(case.items()[0]) == null) {
   2305             const item = try expr(mod, &item_scope.base, .none, case.items()[0]);
   2306             try items.append(item);
   2307             try switchCaseExpr(mod, &case_scope.base, case_rl, block, case);
   2308 
   2309             try cases.append(.{
   2310                 .item = item,
   2311                 .body = .{ .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items) },
   2312             });
   2313             continue;
   2314         }
   2315 
   2316         // TODO if the case has few items and no ranges it might be better
   2317         // to just handle them as switch prongs.
   2318 
   2319         // Check if the target matches any of the items.
   2320         // 1, 2, 3..6 will result in
   2321         // target == 1 or target == 2 or (target >= 3 and target <= 6)
   2322         var any_ok: ?*zir.Inst = null;
   2323         for (case.items()) |item| {
   2324             if (getRangeNode(item)) |range| {
   2325                 const start = try expr(mod, &item_scope.base, .none, range.lhs);
   2326                 const end = try expr(mod, &item_scope.base, .none, range.rhs);
   2327                 const range_src = tree.token_locs[range.op_token].start;
   2328                 const range_inst = try addZIRBinOp(mod, &item_scope.base, range_src, .switch_range, start, end);
   2329                 try items.append(range_inst);
   2330                 if (first_range == null) first_range = range_inst;
   2331 
   2332                 // target >= start and target <= end
   2333                 const range_start_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_gte, target, start);
   2334                 const range_end_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .cmp_lte, target, end);
   2335                 const range_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_and, range_start_ok, range_end_ok);
   2336 
   2337                 if (any_ok) |some| {
   2338                     any_ok = try addZIRBinOp(mod, &else_scope.base, range_src, .bool_or, some, range_ok);
   2339                 } else {
   2340                     any_ok = range_ok;
   2341                 }
   2342                 continue;
   2343             }
   2344 
   2345             const item_inst = try expr(mod, &item_scope.base, .none, item);
   2346             try items.append(item_inst);
   2347             const cpm_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .cmp_eq, target, item_inst);
   2348 
   2349             if (any_ok) |some| {
   2350                 any_ok = try addZIRBinOp(mod, &else_scope.base, item_inst.src, .bool_or, some, cpm_ok);
   2351             } else {
   2352                 any_ok = cpm_ok;
   2353             }
   2354         }
   2355 
   2356         const condbr = try addZIRInstSpecial(mod, &case_scope.base, case_src, zir.Inst.CondBr, .{
   2357             .condition = any_ok.?,
   2358             .then_body = undefined, // populated below
   2359             .else_body = undefined, // populated below
   2360         }, .{});
   2361         const cond_block = try addZIRInstBlock(mod, &else_scope.base, case_src, .block, .{
   2362             .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items),
   2363         });
   2364 
   2365         // reset cond_scope for then_body
   2366         case_scope.instructions.items.len = 0;
   2367         try switchCaseExpr(mod, &case_scope.base, case_rl, block, case);
   2368         condbr.positionals.then_body = .{
   2369             .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items),
   2370         };
   2371 
   2372         // reset cond_scope for else_body
   2373         case_scope.instructions.items.len = 0;
   2374         _ = try addZIRInst(mod, &case_scope.base, case_src, zir.Inst.BreakVoid, .{
   2375             .block = cond_block,
   2376         }, .{});
   2377         condbr.positionals.else_body = .{
   2378             .instructions = try scope.arena().dupe(*zir.Inst, case_scope.instructions.items),
   2379         };
   2380     }
   2381 
   2382     // Generate else block or a break last to finish the block.
   2383     if (special_case) |case| {
   2384         try switchCaseExpr(mod, &else_scope.base, case_rl, block, case);
   2385     } else {
   2386         // Not handling all possible cases is a compile error.
   2387         _ = try addZIRNoOp(mod, &else_scope.base, switch_src, .unreachable_unsafe);
   2388     }
   2389 
   2390     // All items have been generated, add the instructions to the comptime block.
   2391     item_block.positionals.body = .{
   2392         .instructions = try block_scope.arena.dupe(*zir.Inst, item_scope.instructions.items),
   2393     };
   2394 
   2395     // Actually populate switch instruction values.
   2396     if (else_src != null) switch_inst.kw_args.special_prong = .@"else";
   2397     if (underscore_src != null) switch_inst.kw_args.special_prong = .underscore;
   2398     switch_inst.positionals.cases = try block_scope.arena.dupe(zir.Inst.SwitchBr.Case, cases.items);
   2399     switch_inst.positionals.items = try block_scope.arena.dupe(*zir.Inst, items.items);
   2400     switch_inst.kw_args.range = first_range;
   2401     switch_inst.positionals.else_body = .{
   2402         .instructions = try block_scope.arena.dupe(*zir.Inst, else_scope.instructions.items),
   2403     };
   2404     return &block.base;
   2405 }
   2406 
   2407 fn switchCaseExpr(mod: *Module, scope: *Scope, rl: ResultLoc, block: *zir.Inst.Block, case: *ast.Node.SwitchCase) !void {
   2408     const tree = scope.tree();
   2409     const case_src = tree.token_locs[case.firstToken()].start;
   2410     if (case.payload != null) {
   2411         return mod.fail(scope, case_src, "TODO switch case payload capture", .{});
   2412     }
   2413 
   2414     const case_body = try expr(mod, scope, rl, case.expr);
   2415     if (!case_body.tag.isNoReturn()) {
   2416         _ = try addZIRInst(mod, scope, case_src, zir.Inst.Break, .{
   2417             .block = block,
   2418             .operand = case_body,
   2419         }, .{});
   2420     }
   2421 }
   2422 
   2423 fn ret(mod: *Module, scope: *Scope, cfe: *ast.Node.ControlFlowExpression) InnerError!*zir.Inst {
   2424     const tree = scope.tree();
   2425     const src = tree.token_locs[cfe.ltoken].start;
   2426     if (cfe.getRHS()) |rhs_node| {
   2427         if (nodeMayNeedMemoryLocation(rhs_node, scope)) {
   2428             const ret_ptr = try addZIRNoOp(mod, scope, src, .ret_ptr);
   2429             const operand = try expr(mod, scope, .{ .ptr = ret_ptr }, rhs_node);
   2430             return addZIRUnOp(mod, scope, src, .@"return", operand);
   2431         } else {
   2432             const fn_ret_ty = try addZIRNoOp(mod, scope, src, .ret_type);
   2433             const operand = try expr(mod, scope, .{ .ty = fn_ret_ty }, rhs_node);
   2434             return addZIRUnOp(mod, scope, src, .@"return", operand);
   2435         }
   2436     } else {
   2437         return addZIRNoOp(mod, scope, src, .return_void);
   2438     }
   2439 }
   2440 
   2441 fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneToken) InnerError!*zir.Inst {
   2442     const tracy = trace(@src());
   2443     defer tracy.end();
   2444 
   2445     const tree = scope.tree();
   2446     const ident_name = try mod.identifierTokenString(scope, ident.token);
   2447     const src = tree.token_locs[ident.token].start;
   2448     if (mem.eql(u8, ident_name, "_")) {
   2449         return mod.failNode(scope, &ident.base, "TODO implement '_' identifier", .{});
   2450     }
   2451 
   2452     if (getSimplePrimitiveValue(ident_name)) |typed_value| {
   2453         const result = try addZIRInstConst(mod, scope, src, typed_value);
   2454         return rvalue(mod, scope, rl, result);
   2455     }
   2456 
   2457     if (ident_name.len >= 2) integer: {
   2458         const first_c = ident_name[0];
   2459         if (first_c == 'i' or first_c == 'u') {
   2460             const is_signed = first_c == 'i';
   2461             const bit_count = std.fmt.parseInt(u16, ident_name[1..], 10) catch |err| switch (err) {
   2462                 error.Overflow => return mod.failNode(
   2463                     scope,
   2464                     &ident.base,
   2465                     "primitive integer type '{s}' exceeds maximum bit width of 65535",
   2466                     .{ident_name},
   2467                 ),
   2468                 error.InvalidCharacter => break :integer,
   2469             };
   2470             const val = switch (bit_count) {
   2471                 8 => if (is_signed) Value.initTag(.i8_type) else Value.initTag(.u8_type),
   2472                 16 => if (is_signed) Value.initTag(.i16_type) else Value.initTag(.u16_type),
   2473                 32 => if (is_signed) Value.initTag(.i32_type) else Value.initTag(.u32_type),
   2474                 64 => if (is_signed) Value.initTag(.i64_type) else Value.initTag(.u64_type),
   2475                 else => {
   2476                     return rvalue(mod, scope, rl, try addZIRInstConst(mod, scope, src, .{
   2477                         .ty = Type.initTag(.type),
   2478                         .val = try Value.Tag.int_type.create(scope.arena(), .{
   2479                             .signed = is_signed,
   2480                             .bits = bit_count,
   2481                         }),
   2482                     }));
   2483                 },
   2484             };
   2485             const result = try addZIRInstConst(mod, scope, src, .{
   2486                 .ty = Type.initTag(.type),
   2487                 .val = val,
   2488             });
   2489             return rvalue(mod, scope, rl, result);
   2490         }
   2491     }
   2492 
   2493     // Local variables, including function parameters.
   2494     {
   2495         var s = scope;
   2496         while (true) switch (s.tag) {
   2497             .local_val => {
   2498                 const local_val = s.cast(Scope.LocalVal).?;
   2499                 if (mem.eql(u8, local_val.name, ident_name)) {
   2500                     return rvalue(mod, scope, rl, local_val.inst);
   2501                 }
   2502                 s = local_val.parent;
   2503             },
   2504             .local_ptr => {
   2505                 const local_ptr = s.cast(Scope.LocalPtr).?;
   2506                 if (mem.eql(u8, local_ptr.name, ident_name)) {
   2507                     if (rl == .ref) return local_ptr.ptr;
   2508                     const loaded = try addZIRUnOp(mod, scope, src, .deref, local_ptr.ptr);
   2509                     return rvalue(mod, scope, rl, loaded);
   2510                 }
   2511                 s = local_ptr.parent;
   2512             },
   2513             .gen_zir => s = s.cast(Scope.GenZIR).?.parent,
   2514             else => break,
   2515         };
   2516     }
   2517 
   2518     if (mod.lookupDeclName(scope, ident_name)) |decl| {
   2519         if (rl == .ref) {
   2520             return addZIRInst(mod, scope, src, zir.Inst.DeclRef, .{ .decl = decl }, .{});
   2521         } else {
   2522             return rvalue(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.DeclVal, .{
   2523                 .decl = decl,
   2524             }, .{}));
   2525         }
   2526     }
   2527 
   2528     return mod.failNode(scope, &ident.base, "use of undeclared identifier '{s}'", .{ident_name});
   2529 }
   2530 
   2531 fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
   2532     const tree = scope.tree();
   2533     const unparsed_bytes = tree.tokenSlice(str_lit.token);
   2534     const arena = scope.arena();
   2535 
   2536     var bad_index: usize = undefined;
   2537     const bytes = std.zig.parseStringLiteral(arena, unparsed_bytes, &bad_index) catch |err| switch (err) {
   2538         error.InvalidCharacter => {
   2539             const bad_byte = unparsed_bytes[bad_index];
   2540             const src = tree.token_locs[str_lit.token].start;
   2541             return mod.fail(scope, src + bad_index, "invalid string literal character: '{c}'\n", .{bad_byte});
   2542         },
   2543         else => |e| return e,
   2544     };
   2545 
   2546     const src = tree.token_locs[str_lit.token].start;
   2547     return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
   2548 }
   2549 
   2550 fn multilineStrLiteral(mod: *Module, scope: *Scope, node: *ast.Node.MultilineStringLiteral) !*zir.Inst {
   2551     const tree = scope.tree();
   2552     const lines = node.linesConst();
   2553     const src = tree.token_locs[lines[0]].start;
   2554 
   2555     // line lengths and new lines
   2556     var len = lines.len - 1;
   2557     for (lines) |line| {
   2558         // 2 for the '//' + 1 for '\n'
   2559         len += tree.tokenSlice(line).len - 3;
   2560     }
   2561 
   2562     const bytes = try scope.arena().alloc(u8, len);
   2563     var i: usize = 0;
   2564     for (lines) |line, line_i| {
   2565         if (line_i != 0) {
   2566             bytes[i] = '\n';
   2567             i += 1;
   2568         }
   2569         const slice = tree.tokenSlice(line);
   2570         mem.copy(u8, bytes[i..], slice[2 .. slice.len - 1]);
   2571         i += slice.len - 3;
   2572     }
   2573 
   2574     return addZIRInst(mod, scope, src, zir.Inst.Str, .{ .bytes = bytes }, .{});
   2575 }
   2576 
   2577 fn charLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) !*zir.Inst {
   2578     const tree = scope.tree();
   2579     const src = tree.token_locs[node.token].start;
   2580     const slice = tree.tokenSlice(node.token);
   2581 
   2582     var bad_index: usize = undefined;
   2583     const value = std.zig.parseCharLiteral(slice, &bad_index) catch |err| switch (err) {
   2584         error.InvalidCharacter => {
   2585             const bad_byte = slice[bad_index];
   2586             return mod.fail(scope, src + bad_index, "invalid character: '{c}'\n", .{bad_byte});
   2587         },
   2588     };
   2589 
   2590     return addZIRInstConst(mod, scope, src, .{
   2591         .ty = Type.initTag(.comptime_int),
   2592         .val = try Value.Tag.int_u64.create(scope.arena(), value),
   2593     });
   2594 }
   2595 
   2596 fn integerLiteral(mod: *Module, scope: *Scope, int_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
   2597     const arena = scope.arena();
   2598     const tree = scope.tree();
   2599     const prefixed_bytes = tree.tokenSlice(int_lit.token);
   2600     const base = if (mem.startsWith(u8, prefixed_bytes, "0x"))
   2601         16
   2602     else if (mem.startsWith(u8, prefixed_bytes, "0o"))
   2603         8
   2604     else if (mem.startsWith(u8, prefixed_bytes, "0b"))
   2605         2
   2606     else
   2607         @as(u8, 10);
   2608 
   2609     const bytes = if (base == 10)
   2610         prefixed_bytes
   2611     else
   2612         prefixed_bytes[2..];
   2613 
   2614     if (std.fmt.parseInt(u64, bytes, base)) |small_int| {
   2615         const src = tree.token_locs[int_lit.token].start;
   2616         return addZIRInstConst(mod, scope, src, .{
   2617             .ty = Type.initTag(.comptime_int),
   2618             .val = try Value.Tag.int_u64.create(arena, small_int),
   2619         });
   2620     } else |err| {
   2621         return mod.failTok(scope, int_lit.token, "TODO implement int literals that don't fit in a u64", .{});
   2622     }
   2623 }
   2624 
   2625 fn floatLiteral(mod: *Module, scope: *Scope, float_lit: *ast.Node.OneToken) InnerError!*zir.Inst {
   2626     const arena = scope.arena();
   2627     const tree = scope.tree();
   2628     const bytes = tree.tokenSlice(float_lit.token);
   2629     if (bytes.len > 2 and bytes[1] == 'x') {
   2630         return mod.failTok(scope, float_lit.token, "TODO hex floats", .{});
   2631     }
   2632 
   2633     const float_number = std.fmt.parseFloat(f128, bytes) catch |e| switch (e) {
   2634         error.InvalidCharacter => unreachable, // validated by tokenizer
   2635     };
   2636     const src = tree.token_locs[float_lit.token].start;
   2637     return addZIRInstConst(mod, scope, src, .{
   2638         .ty = Type.initTag(.comptime_float),
   2639         .val = try Value.Tag.float_128.create(arena, float_number),
   2640     });
   2641 }
   2642 
   2643 fn undefLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
   2644     const arena = scope.arena();
   2645     const tree = scope.tree();
   2646     const src = tree.token_locs[node.token].start;
   2647     return addZIRInstConst(mod, scope, src, .{
   2648         .ty = Type.initTag(.@"undefined"),
   2649         .val = Value.initTag(.undef),
   2650     });
   2651 }
   2652 
   2653 fn boolLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
   2654     const arena = scope.arena();
   2655     const tree = scope.tree();
   2656     const src = tree.token_locs[node.token].start;
   2657     return addZIRInstConst(mod, scope, src, .{
   2658         .ty = Type.initTag(.bool),
   2659         .val = switch (tree.token_ids[node.token]) {
   2660             .Keyword_true => Value.initTag(.bool_true),
   2661             .Keyword_false => Value.initTag(.bool_false),
   2662             else => unreachable,
   2663         },
   2664     });
   2665 }
   2666 
   2667 fn nullLiteral(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*zir.Inst {
   2668     const arena = scope.arena();
   2669     const tree = scope.tree();
   2670     const src = tree.token_locs[node.token].start;
   2671     return addZIRInstConst(mod, scope, src, .{
   2672         .ty = Type.initTag(.@"null"),
   2673         .val = Value.initTag(.null_value),
   2674     });
   2675 }
   2676 
   2677 fn assembly(mod: *Module, scope: *Scope, asm_node: *ast.Node.Asm) InnerError!*zir.Inst {
   2678     if (asm_node.outputs.len != 0) {
   2679         return mod.failNode(scope, &asm_node.base, "TODO implement asm with an output", .{});
   2680     }
   2681     const arena = scope.arena();
   2682     const tree = scope.tree();
   2683 
   2684     const inputs = try arena.alloc(*zir.Inst, asm_node.inputs.len);
   2685     const args = try arena.alloc(*zir.Inst, asm_node.inputs.len);
   2686 
   2687     const src = tree.token_locs[asm_node.asm_token].start;
   2688 
   2689     const str_type = try addZIRInstConst(mod, scope, src, .{
   2690         .ty = Type.initTag(.type),
   2691         .val = Value.initTag(.const_slice_u8_type),
   2692     });
   2693     const str_type_rl: ResultLoc = .{ .ty = str_type };
   2694 
   2695     for (asm_node.inputs) |input, i| {
   2696         // TODO semantically analyze constraints
   2697         inputs[i] = try expr(mod, scope, str_type_rl, input.constraint);
   2698         args[i] = try expr(mod, scope, .none, input.expr);
   2699     }
   2700 
   2701     const return_type = try addZIRInstConst(mod, scope, src, .{
   2702         .ty = Type.initTag(.type),
   2703         .val = Value.initTag(.void_type),
   2704     });
   2705     const asm_inst = try addZIRInst(mod, scope, src, zir.Inst.Asm, .{
   2706         .asm_source = try expr(mod, scope, str_type_rl, asm_node.template),
   2707         .return_type = return_type,
   2708     }, .{
   2709         .@"volatile" = asm_node.volatile_token != null,
   2710         //.clobbers =  TODO handle clobbers
   2711         .inputs = inputs,
   2712         .args = args,
   2713     });
   2714     return asm_inst;
   2715 }
   2716 
   2717 fn ensureBuiltinParamCount(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall, count: u32) !void {
   2718     if (call.params_len == count)
   2719         return;
   2720 
   2721     const s = if (count == 1) "" else "s";
   2722     return mod.failTok(scope, call.builtin_token, "expected {d} parameter{s}, found {d}", .{ count, s, call.params_len });
   2723 }
   2724 
   2725 fn simpleCast(
   2726     mod: *Module,
   2727     scope: *Scope,
   2728     rl: ResultLoc,
   2729     call: *ast.Node.BuiltinCall,
   2730     inst_tag: zir.Inst.Tag,
   2731 ) InnerError!*zir.Inst {
   2732     try ensureBuiltinParamCount(mod, scope, call, 2);
   2733     const tree = scope.tree();
   2734     const src = tree.token_locs[call.builtin_token].start;
   2735     const params = call.params();
   2736     const dest_type = try typeExpr(mod, scope, params[0]);
   2737     const rhs = try expr(mod, scope, .none, params[1]);
   2738     const result = try addZIRBinOp(mod, scope, src, inst_tag, dest_type, rhs);
   2739     return rvalue(mod, scope, rl, result);
   2740 }
   2741 
   2742 fn ptrToInt(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
   2743     try ensureBuiltinParamCount(mod, scope, call, 1);
   2744     const operand = try expr(mod, scope, .none, call.params()[0]);
   2745     const tree = scope.tree();
   2746     const src = tree.token_locs[call.builtin_token].start;
   2747     return addZIRUnOp(mod, scope, src, .ptrtoint, operand);
   2748 }
   2749 
   2750 fn as(
   2751     mod: *Module,
   2752     scope: *Scope,
   2753     rl: ResultLoc,
   2754     call: *ast.Node.BuiltinCall,
   2755 ) InnerError!*zir.Inst {
   2756     try ensureBuiltinParamCount(mod, scope, call, 2);
   2757     const tree = scope.tree();
   2758     const src = tree.token_locs[call.builtin_token].start;
   2759     const params = call.params();
   2760     const dest_type = try typeExpr(mod, scope, params[0]);
   2761     switch (rl) {
   2762         .none, .discard, .ref, .ty => {
   2763             const result = try expr(mod, scope, .{ .ty = dest_type }, params[1]);
   2764             return rvalue(mod, scope, rl, result);
   2765         },
   2766 
   2767         .ptr => |result_ptr| {
   2768             return asRlPtr(mod, scope, rl, src, result_ptr, params[1], dest_type);
   2769         },
   2770         .block_ptr => |block_scope| {
   2771             return asRlPtr(mod, scope, rl, src, block_scope.rl_ptr.?, params[1], dest_type);
   2772         },
   2773 
   2774         .bitcasted_ptr => |bitcasted_ptr| {
   2775             // TODO here we should be able to resolve the inference; we now have a type for the result.
   2776             return mod.failTok(scope, call.builtin_token, "TODO implement @as with result location @bitCast", .{});
   2777         },
   2778         .inferred_ptr => |result_alloc| {
   2779             // TODO here we should be able to resolve the inference; we now have a type for the result.
   2780             return mod.failTok(scope, call.builtin_token, "TODO implement @as with inferred-type result location pointer", .{});
   2781         },
   2782     }
   2783 }
   2784 
   2785 fn asRlPtr(
   2786     mod: *Module,
   2787     scope: *Scope,
   2788     rl: ResultLoc,
   2789     src: usize,
   2790     result_ptr: *zir.Inst,
   2791     operand_node: *ast.Node,
   2792     dest_type: *zir.Inst,
   2793 ) InnerError!*zir.Inst {
   2794     // Detect whether this expr() call goes into rvalue() to store the result into the
   2795     // result location. If it does, elide the coerce_result_ptr instruction
   2796     // as well as the store instruction, instead passing the result as an rvalue.
   2797     var as_scope: Scope.GenZIR = .{
   2798         .parent = scope,
   2799         .decl = scope.ownerDecl().?,
   2800         .arena = scope.arena(),
   2801         .instructions = .{},
   2802     };
   2803     defer as_scope.instructions.deinit(mod.gpa);
   2804 
   2805     as_scope.rl_ptr = try addZIRBinOp(mod, &as_scope.base, src, .coerce_result_ptr, dest_type, result_ptr);
   2806     const result = try expr(mod, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node);
   2807     const parent_zir = &scope.getGenZIR().instructions;
   2808     if (as_scope.rvalue_rl_count == 1) {
   2809         // Busted! This expression didn't actually need a pointer.
   2810         const expected_len = parent_zir.items.len + as_scope.instructions.items.len - 2;
   2811         try parent_zir.ensureCapacity(mod.gpa, expected_len);
   2812         for (as_scope.instructions.items) |src_inst| {
   2813             switch (src_inst.tag) {
   2814                 .store_to_block_ptr, .coerce_result_ptr => continue,
   2815                 else => parent_zir.appendAssumeCapacity(src_inst),
   2816             }
   2817         }
   2818         assert(parent_zir.items.len == expected_len);
   2819         return rvalue(mod, scope, rl, result);
   2820     } else {
   2821         try parent_zir.appendSlice(mod.gpa, as_scope.instructions.items);
   2822         return result;
   2823     }
   2824 }
   2825 
   2826 fn bitCast(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
   2827     try ensureBuiltinParamCount(mod, scope, call, 2);
   2828     const tree = scope.tree();
   2829     const src = tree.token_locs[call.builtin_token].start;
   2830     const params = call.params();
   2831     const dest_type = try typeExpr(mod, scope, params[0]);
   2832     switch (rl) {
   2833         .none => {
   2834             const operand = try expr(mod, scope, .none, params[1]);
   2835             return addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
   2836         },
   2837         .discard => {
   2838             const operand = try expr(mod, scope, .none, params[1]);
   2839             const result = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, operand);
   2840             _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
   2841             return result;
   2842         },
   2843         .ref => {
   2844             const operand = try expr(mod, scope, .ref, params[1]);
   2845             const result = try addZIRBinOp(mod, scope, src, .bitcast_ref, dest_type, operand);
   2846             return result;
   2847         },
   2848         .ty => |result_ty| {
   2849             const result = try expr(mod, scope, .none, params[1]);
   2850             const bitcasted = try addZIRBinOp(mod, scope, src, .bitcast, dest_type, result);
   2851             return addZIRBinOp(mod, scope, src, .as, result_ty, bitcasted);
   2852         },
   2853         .ptr => |result_ptr| {
   2854             const casted_result_ptr = try addZIRUnOp(mod, scope, src, .bitcast_result_ptr, result_ptr);
   2855             return expr(mod, scope, .{ .bitcasted_ptr = casted_result_ptr.castTag(.bitcast_result_ptr).? }, params[1]);
   2856         },
   2857         .bitcasted_ptr => |bitcasted_ptr| {
   2858             return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with result location another @bitCast", .{});
   2859         },
   2860         .block_ptr => |block_ptr| {
   2861             return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with result location inferred peer types", .{});
   2862         },
   2863         .inferred_ptr => |result_alloc| {
   2864             // TODO here we should be able to resolve the inference; we now have a type for the result.
   2865             return mod.failTok(scope, call.builtin_token, "TODO implement @bitCast with inferred-type result location pointer", .{});
   2866         },
   2867     }
   2868 }
   2869 
   2870 fn import(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
   2871     try ensureBuiltinParamCount(mod, scope, call, 1);
   2872     const tree = scope.tree();
   2873     const src = tree.token_locs[call.builtin_token].start;
   2874     const params = call.params();
   2875     const target = try expr(mod, scope, .none, params[0]);
   2876     return addZIRUnOp(mod, scope, src, .import, target);
   2877 }
   2878 
   2879 fn compileError(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
   2880     try ensureBuiltinParamCount(mod, scope, call, 1);
   2881     const tree = scope.tree();
   2882     const src = tree.token_locs[call.builtin_token].start;
   2883     const params = call.params();
   2884     const target = try expr(mod, scope, .none, params[0]);
   2885     return addZIRUnOp(mod, scope, src, .compile_error, target);
   2886 }
   2887 
   2888 fn setEvalBranchQuota(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
   2889     try ensureBuiltinParamCount(mod, scope, call, 1);
   2890     const tree = scope.tree();
   2891     const src = tree.token_locs[call.builtin_token].start;
   2892     const params = call.params();
   2893     const u32_type = try addZIRInstConst(mod, scope, src, .{
   2894         .ty = Type.initTag(.type),
   2895         .val = Value.initTag(.u32_type),
   2896     });
   2897     const quota = try expr(mod, scope, .{ .ty = u32_type }, params[0]);
   2898     return addZIRUnOp(mod, scope, src, .set_eval_branch_quota, quota);
   2899 }
   2900 
   2901 fn typeOf(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
   2902     const tree = scope.tree();
   2903     const arena = scope.arena();
   2904     const src = tree.token_locs[call.builtin_token].start;
   2905     const params = call.params();
   2906     if (params.len < 1) {
   2907         return mod.failTok(scope, call.builtin_token, "expected at least 1 argument, found 0", .{});
   2908     }
   2909     if (params.len == 1) {
   2910         return rvalue(mod, scope, rl, try addZIRUnOp(mod, scope, src, .typeof, try expr(mod, scope, .none, params[0])));
   2911     }
   2912     var items = try arena.alloc(*zir.Inst, params.len);
   2913     for (params) |param, param_i|
   2914         items[param_i] = try expr(mod, scope, .none, param);
   2915     return rvalue(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.TypeOfPeer, .{ .items = items }, .{}));
   2916 }
   2917 fn compileLog(mod: *Module, scope: *Scope, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
   2918     const tree = scope.tree();
   2919     const arena = scope.arena();
   2920     const src = tree.token_locs[call.builtin_token].start;
   2921     const params = call.params();
   2922     var targets = try arena.alloc(*zir.Inst, params.len);
   2923     for (params) |param, param_i|
   2924         targets[param_i] = try expr(mod, scope, .none, param);
   2925     return addZIRInst(mod, scope, src, zir.Inst.CompileLog, .{ .to_log = targets }, .{});
   2926 }
   2927 
   2928 fn builtinCall(mod: *Module, scope: *Scope, rl: ResultLoc, call: *ast.Node.BuiltinCall) InnerError!*zir.Inst {
   2929     const tree = scope.tree();
   2930     const builtin_name = tree.tokenSlice(call.builtin_token);
   2931 
   2932     // We handle the different builtins manually because they have different semantics depending
   2933     // on the function. For example, `@as` and others participate in result location semantics,
   2934     // and `@cImport` creates a special scope that collects a .c source code text buffer.
   2935     // Also, some builtins have a variable number of parameters.
   2936 
   2937     if (mem.eql(u8, builtin_name, "@ptrToInt")) {
   2938         return rvalue(mod, scope, rl, try ptrToInt(mod, scope, call));
   2939     } else if (mem.eql(u8, builtin_name, "@as")) {
   2940         return as(mod, scope, rl, call);
   2941     } else if (mem.eql(u8, builtin_name, "@floatCast")) {
   2942         return simpleCast(mod, scope, rl, call, .floatcast);
   2943     } else if (mem.eql(u8, builtin_name, "@intCast")) {
   2944         return simpleCast(mod, scope, rl, call, .intcast);
   2945     } else if (mem.eql(u8, builtin_name, "@bitCast")) {
   2946         return bitCast(mod, scope, rl, call);
   2947     } else if (mem.eql(u8, builtin_name, "@TypeOf")) {
   2948         return typeOf(mod, scope, rl, call);
   2949     } else if (mem.eql(u8, builtin_name, "@breakpoint")) {
   2950         const src = tree.token_locs[call.builtin_token].start;
   2951         return rvalue(mod, scope, rl, try addZIRNoOp(mod, scope, src, .breakpoint));
   2952     } else if (mem.eql(u8, builtin_name, "@import")) {
   2953         return rvalue(mod, scope, rl, try import(mod, scope, call));
   2954     } else if (mem.eql(u8, builtin_name, "@compileError")) {
   2955         return compileError(mod, scope, call);
   2956     } else if (mem.eql(u8, builtin_name, "@setEvalBranchQuota")) {
   2957         return setEvalBranchQuota(mod, scope, call);
   2958     } else if (mem.eql(u8, builtin_name, "@compileLog")) {
   2959         return compileLog(mod, scope, call);
   2960     } else if (mem.eql(u8, builtin_name, "@field")) {
   2961         return namedField(mod, scope, rl, call);
   2962     } else {
   2963         return mod.failTok(scope, call.builtin_token, "invalid builtin function: '{s}'", .{builtin_name});
   2964     }
   2965 }
   2966 
   2967 fn callExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Call) InnerError!*zir.Inst {
   2968     const tree = scope.tree();
   2969     const lhs = try expr(mod, scope, .none, node.lhs);
   2970 
   2971     const param_nodes = node.params();
   2972     const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len);
   2973     for (param_nodes) |param_node, i| {
   2974         const param_src = tree.token_locs[param_node.firstToken()].start;
   2975         const param_type = try addZIRInst(mod, scope, param_src, zir.Inst.ParamType, .{
   2976             .func = lhs,
   2977             .arg_index = i,
   2978         }, .{});
   2979         args[i] = try expr(mod, scope, .{ .ty = param_type }, param_node);
   2980     }
   2981 
   2982     const src = tree.token_locs[node.lhs.firstToken()].start;
   2983     const result = try addZIRInst(mod, scope, src, zir.Inst.Call, .{
   2984         .func = lhs,
   2985         .args = args,
   2986     }, .{});
   2987     // TODO function call with result location
   2988     return rvalue(mod, scope, rl, result);
   2989 }
   2990 
   2991 fn unreach(mod: *Module, scope: *Scope, unreach_node: *ast.Node.OneToken) InnerError!*zir.Inst {
   2992     const tree = scope.tree();
   2993     const src = tree.token_locs[unreach_node.token].start;
   2994     return addZIRNoOp(mod, scope, src, .unreachable_safe);
   2995 }
   2996 
   2997 fn getSimplePrimitiveValue(name: []const u8) ?TypedValue {
   2998     const simple_types = std.ComptimeStringMap(Value.Tag, .{
   2999         .{ "u8", .u8_type },
   3000         .{ "i8", .i8_type },
   3001         .{ "isize", .isize_type },
   3002         .{ "usize", .usize_type },
   3003         .{ "c_short", .c_short_type },
   3004         .{ "c_ushort", .c_ushort_type },
   3005         .{ "c_int", .c_int_type },
   3006         .{ "c_uint", .c_uint_type },
   3007         .{ "c_long", .c_long_type },
   3008         .{ "c_ulong", .c_ulong_type },
   3009         .{ "c_longlong", .c_longlong_type },
   3010         .{ "c_ulonglong", .c_ulonglong_type },
   3011         .{ "c_longdouble", .c_longdouble_type },
   3012         .{ "f16", .f16_type },
   3013         .{ "f32", .f32_type },
   3014         .{ "f64", .f64_type },
   3015         .{ "f128", .f128_type },
   3016         .{ "c_void", .c_void_type },
   3017         .{ "bool", .bool_type },
   3018         .{ "void", .void_type },
   3019         .{ "type", .type_type },
   3020         .{ "anyerror", .anyerror_type },
   3021         .{ "comptime_int", .comptime_int_type },
   3022         .{ "comptime_float", .comptime_float_type },
   3023         .{ "noreturn", .noreturn_type },
   3024     });
   3025     if (simple_types.get(name)) |tag| {
   3026         return TypedValue{
   3027             .ty = Type.initTag(.type),
   3028             .val = Value.initTag(tag),
   3029         };
   3030     }
   3031     return null;
   3032 }
   3033 
   3034 fn nodeMayNeedMemoryLocation(start_node: *ast.Node, scope: *Scope) bool {
   3035     var node = start_node;
   3036     while (true) {
   3037         switch (node.tag) {
   3038             .Root,
   3039             .Use,
   3040             .TestDecl,
   3041             .DocComment,
   3042             .SwitchCase,
   3043             .SwitchElse,
   3044             .Else,
   3045             .Payload,
   3046             .PointerPayload,
   3047             .PointerIndexPayload,
   3048             .ContainerField,
   3049             .ErrorTag,
   3050             .FieldInitializer,
   3051             => unreachable,
   3052 
   3053             .Return,
   3054             .Break,
   3055             .Continue,
   3056             .BitNot,
   3057             .BoolNot,
   3058             .VarDecl,
   3059             .Defer,
   3060             .AddressOf,
   3061             .OptionalType,
   3062             .Negation,
   3063             .NegationWrap,
   3064             .Resume,
   3065             .ArrayType,
   3066             .ArrayTypeSentinel,
   3067             .PtrType,
   3068             .SliceType,
   3069             .Suspend,
   3070             .AnyType,
   3071             .ErrorType,
   3072             .FnProto,
   3073             .AnyFrameType,
   3074             .IntegerLiteral,
   3075             .FloatLiteral,
   3076             .EnumLiteral,
   3077             .StringLiteral,
   3078             .MultilineStringLiteral,
   3079             .CharLiteral,
   3080             .BoolLiteral,
   3081             .NullLiteral,
   3082             .UndefinedLiteral,
   3083             .Unreachable,
   3084             .Identifier,
   3085             .ErrorSetDecl,
   3086             .ContainerDecl,
   3087             .Asm,
   3088             .Add,
   3089             .AddWrap,
   3090             .ArrayCat,
   3091             .ArrayMult,
   3092             .Assign,
   3093             .AssignBitAnd,
   3094             .AssignBitOr,
   3095             .AssignBitShiftLeft,
   3096             .AssignBitShiftRight,
   3097             .AssignBitXor,
   3098             .AssignDiv,
   3099             .AssignSub,
   3100             .AssignSubWrap,
   3101             .AssignMod,
   3102             .AssignAdd,
   3103             .AssignAddWrap,
   3104             .AssignMul,
   3105             .AssignMulWrap,
   3106             .BangEqual,
   3107             .BitAnd,
   3108             .BitOr,
   3109             .BitShiftLeft,
   3110             .BitShiftRight,
   3111             .BitXor,
   3112             .BoolAnd,
   3113             .BoolOr,
   3114             .Div,
   3115             .EqualEqual,
   3116             .ErrorUnion,
   3117             .GreaterOrEqual,
   3118             .GreaterThan,
   3119             .LessOrEqual,
   3120             .LessThan,
   3121             .MergeErrorSets,
   3122             .Mod,
   3123             .Mul,
   3124             .MulWrap,
   3125             .Range,
   3126             .Period,
   3127             .Sub,
   3128             .SubWrap,
   3129             .Slice,
   3130             .Deref,
   3131             .ArrayAccess,
   3132             .Block,
   3133             => return false,
   3134 
   3135             // Forward the question to a sub-expression.
   3136             .GroupedExpression => node = node.castTag(.GroupedExpression).?.expr,
   3137             .Try => node = node.castTag(.Try).?.rhs,
   3138             .Await => node = node.castTag(.Await).?.rhs,
   3139             .Catch => node = node.castTag(.Catch).?.rhs,
   3140             .OrElse => node = node.castTag(.OrElse).?.rhs,
   3141             .Comptime => node = node.castTag(.Comptime).?.expr,
   3142             .Nosuspend => node = node.castTag(.Nosuspend).?.expr,
   3143             .UnwrapOptional => node = node.castTag(.UnwrapOptional).?.lhs,
   3144 
   3145             // True because these are exactly the expressions we need memory locations for.
   3146             .ArrayInitializer,
   3147             .ArrayInitializerDot,
   3148             .StructInitializer,
   3149             .StructInitializerDot,
   3150             => return true,
   3151 
   3152             // True because depending on comptime conditions, sub-expressions
   3153             // may be the kind that need memory locations.
   3154             .While,
   3155             .For,
   3156             .Switch,
   3157             .Call,
   3158             .LabeledBlock,
   3159             => return true,
   3160 
   3161             .BuiltinCall => {
   3162                 @setEvalBranchQuota(5000);
   3163                 const builtin_needs_mem_loc = std.ComptimeStringMap(bool, .{
   3164                     .{ "@addWithOverflow", false },
   3165                     .{ "@alignCast", false },
   3166                     .{ "@alignOf", false },
   3167                     .{ "@as", true },
   3168                     .{ "@asyncCall", false },
   3169                     .{ "@atomicLoad", false },
   3170                     .{ "@atomicRmw", false },
   3171                     .{ "@atomicStore", false },
   3172                     .{ "@bitCast", true },
   3173                     .{ "@bitOffsetOf", false },
   3174                     .{ "@boolToInt", false },
   3175                     .{ "@bitSizeOf", false },
   3176                     .{ "@breakpoint", false },
   3177                     .{ "@mulAdd", false },
   3178                     .{ "@byteSwap", false },
   3179                     .{ "@bitReverse", false },
   3180                     .{ "@byteOffsetOf", false },
   3181                     .{ "@call", true },
   3182                     .{ "@cDefine", false },
   3183                     .{ "@cImport", false },
   3184                     .{ "@cInclude", false },
   3185                     .{ "@clz", false },
   3186                     .{ "@cmpxchgStrong", false },
   3187                     .{ "@cmpxchgWeak", false },
   3188                     .{ "@compileError", false },
   3189                     .{ "@compileLog", false },
   3190                     .{ "@ctz", false },
   3191                     .{ "@cUndef", false },
   3192                     .{ "@divExact", false },
   3193                     .{ "@divFloor", false },
   3194                     .{ "@divTrunc", false },
   3195                     .{ "@embedFile", false },
   3196                     .{ "@enumToInt", false },
   3197                     .{ "@errorName", false },
   3198                     .{ "@errorReturnTrace", false },
   3199                     .{ "@errorToInt", false },
   3200                     .{ "@errSetCast", false },
   3201                     .{ "@export", false },
   3202                     .{ "@fence", false },
   3203                     .{ "@field", true },
   3204                     .{ "@fieldParentPtr", false },
   3205                     .{ "@floatCast", false },
   3206                     .{ "@floatToInt", false },
   3207                     .{ "@frame", false },
   3208                     .{ "@Frame", false },
   3209                     .{ "@frameAddress", false },
   3210                     .{ "@frameSize", false },
   3211                     .{ "@hasDecl", false },
   3212                     .{ "@hasField", false },
   3213                     .{ "@import", false },
   3214                     .{ "@intCast", false },
   3215                     .{ "@intToEnum", false },
   3216                     .{ "@intToError", false },
   3217                     .{ "@intToFloat", false },
   3218                     .{ "@intToPtr", false },
   3219                     .{ "@memcpy", false },
   3220                     .{ "@memset", false },
   3221                     .{ "@wasmMemorySize", false },
   3222                     .{ "@wasmMemoryGrow", false },
   3223                     .{ "@mod", false },
   3224                     .{ "@mulWithOverflow", false },
   3225                     .{ "@panic", false },
   3226                     .{ "@popCount", false },
   3227                     .{ "@ptrCast", false },
   3228                     .{ "@ptrToInt", false },
   3229                     .{ "@rem", false },
   3230                     .{ "@returnAddress", false },
   3231                     .{ "@setAlignStack", false },
   3232                     .{ "@setCold", false },
   3233                     .{ "@setEvalBranchQuota", false },
   3234                     .{ "@setFloatMode", false },
   3235                     .{ "@setRuntimeSafety", false },
   3236                     .{ "@shlExact", false },
   3237                     .{ "@shlWithOverflow", false },
   3238                     .{ "@shrExact", false },
   3239                     .{ "@shuffle", false },
   3240                     .{ "@sizeOf", false },
   3241                     .{ "@splat", true },
   3242                     .{ "@reduce", false },
   3243                     .{ "@src", true },
   3244                     .{ "@sqrt", false },
   3245                     .{ "@sin", false },
   3246                     .{ "@cos", false },
   3247                     .{ "@exp", false },
   3248                     .{ "@exp2", false },
   3249                     .{ "@log", false },
   3250                     .{ "@log2", false },
   3251                     .{ "@log10", false },
   3252                     .{ "@fabs", false },
   3253                     .{ "@floor", false },
   3254                     .{ "@ceil", false },
   3255                     .{ "@trunc", false },
   3256                     .{ "@round", false },
   3257                     .{ "@subWithOverflow", false },
   3258                     .{ "@tagName", false },
   3259                     .{ "@This", false },
   3260                     .{ "@truncate", false },
   3261                     .{ "@Type", false },
   3262                     .{ "@typeInfo", false },
   3263                     .{ "@typeName", false },
   3264                     .{ "@TypeOf", false },
   3265                     .{ "@unionInit", true },
   3266                 });
   3267                 const name = scope.tree().tokenSlice(node.castTag(.BuiltinCall).?.builtin_token);
   3268                 return builtin_needs_mem_loc.get(name).?;
   3269             },
   3270 
   3271             // Depending on AST properties, they may need memory locations.
   3272             .If => return node.castTag(.If).?.@"else" != null,
   3273         }
   3274     }
   3275 }
   3276 
   3277 /// Applies `rl` semantics to `inst`. Expressions which do not do their own handling of
   3278 /// result locations must call this function on their result.
   3279 /// As an example, if the `ResultLoc` is `ptr`, it will write the result to the pointer.
   3280 /// If the `ResultLoc` is `ty`, it will coerce the result to the type.
   3281 fn rvalue(mod: *Module, scope: *Scope, rl: ResultLoc, result: *zir.Inst) InnerError!*zir.Inst {
   3282     switch (rl) {
   3283         .none => return result,
   3284         .discard => {
   3285             // Emit a compile error for discarding error values.
   3286             _ = try addZIRUnOp(mod, scope, result.src, .ensure_result_non_error, result);
   3287             return result;
   3288         },
   3289         .ref => {
   3290             // We need a pointer but we have a value.
   3291             return addZIRUnOp(mod, scope, result.src, .ref, result);
   3292         },
   3293         .ty => |ty_inst| return addZIRBinOp(mod, scope, result.src, .as, ty_inst, result),
   3294         .ptr => |ptr_inst| {
   3295             _ = try addZIRBinOp(mod, scope, result.src, .store, ptr_inst, result);
   3296             return result;
   3297         },
   3298         .bitcasted_ptr => |bitcasted_ptr| {
   3299             return mod.fail(scope, result.src, "TODO implement rvalue .bitcasted_ptr", .{});
   3300         },
   3301         .inferred_ptr => |alloc| {
   3302             _ = try addZIRBinOp(mod, scope, result.src, .store_to_inferred_ptr, &alloc.base, result);
   3303             return result;
   3304         },
   3305         .block_ptr => |block_scope| {
   3306             block_scope.rvalue_rl_count += 1;
   3307             _ = try addZIRBinOp(mod, scope, result.src, .store_to_block_ptr, block_scope.rl_ptr.?, result);
   3308             return result;
   3309         },
   3310     }
   3311 }
   3312 
   3313 fn rvalueVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, result: void) InnerError!*zir.Inst {
   3314     const src = scope.tree().token_locs[node.firstToken()].start;
   3315     const void_inst = try addZIRInstConst(mod, scope, src, .{
   3316         .ty = Type.initTag(.void),
   3317         .val = Value.initTag(.void_value),
   3318     });
   3319     return rvalue(mod, scope, rl, void_inst);
   3320 }
   3321 
   3322 pub fn addZirInstTag(
   3323     mod: *Module,
   3324     scope: *Scope,
   3325     src: usize,
   3326     comptime tag: zir.Inst.Tag,
   3327     positionals: std.meta.fieldInfo(tag.Type(), .positionals).field_type,
   3328 ) !*zir.Inst {
   3329     const gen_zir = scope.getGenZIR();
   3330     try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
   3331     const inst = try gen_zir.arena.create(tag.Type());
   3332     inst.* = .{
   3333         .base = .{
   3334             .tag = tag,
   3335             .src = src,
   3336         },
   3337         .positionals = positionals,
   3338         .kw_args = .{},
   3339     };
   3340     gen_zir.instructions.appendAssumeCapacity(&inst.base);
   3341     return &inst.base;
   3342 }
   3343 
   3344 pub fn addZIRInstSpecial(
   3345     mod: *Module,
   3346     scope: *Scope,
   3347     src: usize,
   3348     comptime T: type,
   3349     positionals: std.meta.fieldInfo(T, .positionals).field_type,
   3350     kw_args: std.meta.fieldInfo(T, .kw_args).field_type,
   3351 ) !*T {
   3352     const gen_zir = scope.getGenZIR();
   3353     try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
   3354     const inst = try gen_zir.arena.create(T);
   3355     inst.* = .{
   3356         .base = .{
   3357             .tag = T.base_tag,
   3358             .src = src,
   3359         },
   3360         .positionals = positionals,
   3361         .kw_args = kw_args,
   3362     };
   3363     gen_zir.instructions.appendAssumeCapacity(&inst.base);
   3364     return inst;
   3365 }
   3366 
   3367 pub fn addZIRNoOpT(mod: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst.NoOp {
   3368     const gen_zir = scope.getGenZIR();
   3369     try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
   3370     const inst = try gen_zir.arena.create(zir.Inst.NoOp);
   3371     inst.* = .{
   3372         .base = .{
   3373             .tag = tag,
   3374             .src = src,
   3375         },
   3376         .positionals = .{},
   3377         .kw_args = .{},
   3378     };
   3379     gen_zir.instructions.appendAssumeCapacity(&inst.base);
   3380     return inst;
   3381 }
   3382 
   3383 pub fn addZIRNoOp(mod: *Module, scope: *Scope, src: usize, tag: zir.Inst.Tag) !*zir.Inst {
   3384     const inst = try addZIRNoOpT(mod, scope, src, tag);
   3385     return &inst.base;
   3386 }
   3387 
   3388 pub fn addZIRUnOp(
   3389     mod: *Module,
   3390     scope: *Scope,
   3391     src: usize,
   3392     tag: zir.Inst.Tag,
   3393     operand: *zir.Inst,
   3394 ) !*zir.Inst {
   3395     const gen_zir = scope.getGenZIR();
   3396     try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
   3397     const inst = try gen_zir.arena.create(zir.Inst.UnOp);
   3398     inst.* = .{
   3399         .base = .{
   3400             .tag = tag,
   3401             .src = src,
   3402         },
   3403         .positionals = .{
   3404             .operand = operand,
   3405         },
   3406         .kw_args = .{},
   3407     };
   3408     gen_zir.instructions.appendAssumeCapacity(&inst.base);
   3409     return &inst.base;
   3410 }
   3411 
   3412 pub fn addZIRBinOp(
   3413     mod: *Module,
   3414     scope: *Scope,
   3415     src: usize,
   3416     tag: zir.Inst.Tag,
   3417     lhs: *zir.Inst,
   3418     rhs: *zir.Inst,
   3419 ) !*zir.Inst {
   3420     const gen_zir = scope.getGenZIR();
   3421     try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
   3422     const inst = try gen_zir.arena.create(zir.Inst.BinOp);
   3423     inst.* = .{
   3424         .base = .{
   3425             .tag = tag,
   3426             .src = src,
   3427         },
   3428         .positionals = .{
   3429             .lhs = lhs,
   3430             .rhs = rhs,
   3431         },
   3432         .kw_args = .{},
   3433     };
   3434     gen_zir.instructions.appendAssumeCapacity(&inst.base);
   3435     return &inst.base;
   3436 }
   3437 
   3438 pub fn addZIRInstBlock(
   3439     mod: *Module,
   3440     scope: *Scope,
   3441     src: usize,
   3442     tag: zir.Inst.Tag,
   3443     body: zir.Body,
   3444 ) !*zir.Inst.Block {
   3445     const gen_zir = scope.getGenZIR();
   3446     try gen_zir.instructions.ensureCapacity(mod.gpa, gen_zir.instructions.items.len + 1);
   3447     const inst = try gen_zir.arena.create(zir.Inst.Block);
   3448     inst.* = .{
   3449         .base = .{
   3450             .tag = tag,
   3451             .src = src,
   3452         },
   3453         .positionals = .{
   3454             .body = body,
   3455         },
   3456         .kw_args = .{},
   3457     };
   3458     gen_zir.instructions.appendAssumeCapacity(&inst.base);
   3459     return inst;
   3460 }
   3461 
   3462 pub fn addZIRInst(
   3463     mod: *Module,
   3464     scope: *Scope,
   3465     src: usize,
   3466     comptime T: type,
   3467     positionals: std.meta.fieldInfo(T, .positionals).field_type,
   3468     kw_args: std.meta.fieldInfo(T, .kw_args).field_type,
   3469 ) !*zir.Inst {
   3470     const inst_special = try addZIRInstSpecial(mod, scope, src, T, positionals, kw_args);
   3471     return &inst_special.base;
   3472 }
   3473 
   3474 /// TODO The existence of this function is a workaround for a bug in stage1.
   3475 pub fn addZIRInstConst(mod: *Module, scope: *Scope, src: usize, typed_value: TypedValue) !*zir.Inst {
   3476     const P = std.meta.fieldInfo(zir.Inst.Const, .positionals).field_type;
   3477     return addZIRInst(mod, scope, src, zir.Inst.Const, P{ .typed_value = typed_value }, .{});
   3478 }
   3479 
   3480 /// TODO The existence of this function is a workaround for a bug in stage1.
   3481 pub fn addZIRInstLoop(mod: *Module, scope: *Scope, src: usize, body: zir.Body) !*zir.Inst.Loop {
   3482     const P = std.meta.fieldInfo(zir.Inst.Loop, .positionals).field_type;
   3483     return addZIRInstSpecial(mod, scope, src, zir.Inst.Loop, P{ .body = body }, .{});
   3484 }