Commit 62f45b802c

Andrew Kelley <andrew@ziglang.org>
2023-10-28 08:18:55
make Zir.Inst.Index typed
This commit starts by making Zir.Inst.Index a nonexhaustive enum rather than a u32 alias for type safety purposes, and the rest of the changes are needed to get everything compiling again.
1 parent 49b9e1e
src/AstGen.zig
@@ -13,8 +13,6 @@ const StringIndexContext = std.hash_map.StringIndexContext;
 const isPrimitive = std.zig.primitives.isPrimitive;
 
 const Zir = @import("Zir.zig");
-const refToIndex = Zir.refToIndex;
-const indexToRef = Zir.indexToRef;
 const trace = @import("tracy.zig").trace;
 const BuiltinFn = @import("BuiltinFn.zig");
 const AstRlAnnotate = @import("AstRlAnnotate.zig");
@@ -86,13 +84,18 @@ fn setExtra(astgen: *AstGen, index: usize, extra: anytype) void {
     inline for (fields) |field| {
         astgen.extra.items[i] = switch (field.type) {
             u32 => @field(extra, field.name),
-            Zir.Inst.Ref => @intFromEnum(@field(extra, field.name)),
+
+            Zir.Inst.Ref,
+            Zir.Inst.Index,
+            => @intFromEnum(@field(extra, field.name)),
+
             i32,
             Zir.Inst.Call.Flags,
             Zir.Inst.BuiltinCall.Flags,
             Zir.Inst.SwitchBlock.Bits,
             Zir.Inst.FuncFancy.Bits,
             => @bitCast(@field(extra, field.name)),
+
             else => @compileError("bad field type"),
         };
         i += 1;
@@ -166,7 +169,7 @@ pub fn generate(gpa: Allocator, tree: Ast) Allocator.Error!Zir {
             .Auto,
             0,
         )) |struct_decl_ref| {
-            assert(refToIndex(struct_decl_ref).? == 0);
+            assert(struct_decl_ref.toIndex().? == .main_struct_inst);
         } else |err| switch (err) {
             error.OutOfMemory => return error.OutOfMemory,
             error.AnalysisFail => {}, // Handled via compile_errors below.
@@ -1200,7 +1203,7 @@ fn suspendExpr(
     }
     try suspend_scope.setBlockBody(suspend_inst);
 
-    return indexToRef(suspend_inst);
+    return suspend_inst.toRef();
 }
 
 fn awaitExpr(
@@ -1316,7 +1319,7 @@ fn fnProtoExpr(
                 var param_gz = block_scope.makeSubBlock(scope);
                 defer param_gz.unstack();
                 const param_type = try expr(&param_gz, scope, coerced_type_ri, param_type_node);
-                const param_inst_expected: u32 = @intCast(astgen.instructions.len + 1);
+                const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1);
                 _ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node);
                 const main_tokens = tree.nodes.items(.main_token);
                 const name_token = param.name_token orelse main_tokens[param_type_node];
@@ -1386,7 +1389,7 @@ fn fnProtoExpr(
     try block_scope.setBlockBody(block_inst);
     try gz.instructions.append(astgen.gpa, block_inst);
 
-    return rvalue(gz, ri, indexToRef(block_inst), fn_proto.ast.proto_node);
+    return rvalue(gz, ri, block_inst.toRef(), fn_proto.ast.proto_node);
 }
 
 fn arrayInitExpr(
@@ -1625,7 +1628,7 @@ fn arrayInitExprPtr(
             .ptr = array_ptr_inst,
             .index = @intCast(i),
         });
-        astgen.extra.items[extra_index] = refToIndex(elem_ptr_inst).?;
+        astgen.extra.items[extra_index] = @intFromEnum(elem_ptr_inst.toIndex().?);
         extra_index += 1;
         _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = elem_ptr_inst } } }, elem_init);
     }
@@ -1825,7 +1828,7 @@ fn structInitExprTyped(
             .name_start = str_index,
         });
         setExtra(astgen, extra_index, Zir.Inst.StructInit.Item{
-            .field_type = refToIndex(field_ty_inst).?,
+            .field_type = field_ty_inst.toIndex().?,
             .init = try expr(gz, scope, .{ .rl = .{ .coerced_ty = field_ty_inst } }, field_init),
         });
         extra_index += field_size;
@@ -1860,7 +1863,7 @@ fn structInitExprPtr(
             .lhs = struct_ptr_inst,
             .field_name_start = str_index,
         });
-        astgen.extra.items[extra_index] = refToIndex(field_ptr).?;
+        astgen.extra.items[extra_index] = @intFromEnum(field_ptr.toIndex().?);
         extra_index += 1;
         _ = try expr(gz, scope, .{ .rl = .{ .ptr = .{ .inst = field_ptr } } }, field_init);
     }
@@ -1962,7 +1965,7 @@ fn comptimeExpr(
     try block_scope.setBlockBody(block_inst);
     try gz.instructions.append(gz.astgen.gpa, block_inst);
 
-    return rvalue(gz, ri, indexToRef(block_inst), node);
+    return rvalue(gz, ri, block_inst.toRef(), node);
 }
 
 /// This one is for an actual `comptime` syntax, and will emit a compile error if
@@ -2048,8 +2051,8 @@ fn breakExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index) Inn
                                 break :blk label.block_inst;
                             }
                         }
-                    } else if (block_gz.break_block != 0) {
-                        break :blk block_gz.break_block;
+                    } else if (block_gz.break_block.unwrap()) |i| {
+                        break :blk i;
                     }
                     // If not the target, start over with the parent
                     scope = block_gz.parent;
@@ -2135,11 +2138,10 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
                         ),
                     });
                 }
-                const continue_block = gen_zir.continue_block;
-                if (continue_block == 0) {
+                const continue_block = gen_zir.continue_block.unwrap() orelse {
                     scope = gen_zir.parent;
                     continue;
-                }
+                };
                 if (break_label != 0) blk: {
                     if (gen_zir.label) |*label| {
                         if (try astgen.tokenIdentEql(label.token, break_label)) {
@@ -2157,7 +2159,7 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
                 else
                     .@"break";
                 if (break_tag == .break_inline) {
-                    _ = try parent_gz.addUnNode(.check_comptime_control_flow, Zir.indexToRef(continue_block), node);
+                    _ = try parent_gz.addUnNode(.check_comptime_control_flow, continue_block.toRef(), node);
                 }
 
                 // As our last action before the continue, "pop" the error trace if needed
@@ -2333,9 +2335,9 @@ fn labeledBlockExpr(
 
     try block_scope.setBlockBody(block_inst);
     if (need_result_rvalue) {
-        return rvalue(gz, ri, indexToRef(block_inst), block_node);
+        return rvalue(gz, ri, block_inst.toRef(), block_node);
     } else {
-        return indexToRef(block_inst);
+        return block_inst.toRef();
     }
 }
 
@@ -2438,15 +2440,15 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
 
 fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: Ast.Node.Index) InnerError!Ast.Node.Index {
     var noreturn_src_node: Ast.Node.Index = 0;
-    const elide_check = if (refToIndex(maybe_unused_result)) |inst| b: {
+    const elide_check = if (maybe_unused_result.toIndex()) |inst| b: {
         // Note that this array becomes invalid after appending more items to it
         // in the above while loop.
         const zir_tags = gz.astgen.instructions.items(.tag);
-        switch (zir_tags[inst]) {
+        switch (zir_tags[@intFromEnum(inst)]) {
             // For some instructions, modify the zir data
             // so we can avoid a separate ensure_result_used instruction.
             .call, .field_call => {
-                const break_extra = gz.astgen.instructions.items(.data)[inst].pl_node.payload_index;
+                const break_extra = gz.astgen.instructions.items(.data)[@intFromEnum(inst)].pl_node.payload_index;
                 comptime assert(std.meta.fieldIndex(Zir.Inst.Call, "flags") ==
                     std.meta.fieldIndex(Zir.Inst.FieldCall, "flags"));
                 const flags: *Zir.Inst.Call.Flags = @ptrCast(&gz.astgen.extra.items[
@@ -2456,7 +2458,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
                 break :b true;
             },
             .builtin_call => {
-                const break_extra = gz.astgen.instructions.items(.data)[inst].pl_node.payload_index;
+                const break_extra = gz.astgen.instructions.items(.data)[@intFromEnum(inst)].pl_node.payload_index;
                 const flags: *Zir.Inst.BuiltinCall.Flags = @ptrCast(&gz.astgen.extra.items[
                     break_extra + std.meta.fieldIndex(Zir.Inst.BuiltinCall, "flags").?
                 ]);
@@ -2670,7 +2672,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
             .array_init_elem_ptr,
             => break :b false,
 
-            .extended => switch (gz.astgen.instructions.items(.data)[inst].extended.opcode) {
+            .extended => switch (gz.astgen.instructions.items(.data)[@intFromEnum(inst)].extended.opcode) {
                 .breakpoint,
                 .fence,
                 .set_float_mode,
@@ -2783,7 +2785,7 @@ fn countDefers(outer_scope: *Scope, inner_scope: *Scope) struct {
 
                 have_err = true;
 
-                const have_err_payload = defer_scope.remapped_err_code != 0;
+                const have_err_payload = defer_scope.remapped_err_code != .none;
                 need_err_code = need_err_code or have_err_payload;
             },
             .namespace, .enum_namespace => unreachable,
@@ -2831,18 +2833,16 @@ fn genDefers(
                         try gz.addDefer(defer_scope.index, defer_scope.len);
                     },
                     .both => |err_code| {
-                        if (defer_scope.remapped_err_code == 0) {
-                            try gz.addDefer(defer_scope.index, defer_scope.len);
-                        } else {
+                        if (defer_scope.remapped_err_code.unwrap()) |remapped_err_code| {
                             try gz.instructions.ensureUnusedCapacity(gpa, 1);
                             try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
 
                             const payload_index = try gz.astgen.addExtra(Zir.Inst.DeferErrCode{
-                                .remapped_err_code = defer_scope.remapped_err_code,
+                                .remapped_err_code = remapped_err_code,
                                 .index = defer_scope.index,
                                 .len = defer_scope.len,
                             });
-                            const new_index: Zir.Inst.Index = @intCast(gz.astgen.instructions.len);
+                            const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
                             gz.astgen.instructions.appendAssumeCapacity(.{
                                 .tag = .defer_err_code,
                                 .data = .{ .defer_err_code = .{
@@ -2851,6 +2851,8 @@ fn genDefers(
                                 } },
                             });
                             gz.instructions.appendAssumeCapacity(new_index);
+                        } else {
+                            try gz.addDefer(defer_scope.index, defer_scope.len);
                         }
                     },
                     .normal_only => continue,
@@ -2916,12 +2918,13 @@ fn deferStmt(
 
     const payload_token = node_datas[node].lhs;
     var local_val_scope: Scope.LocalVal = undefined;
-    var remapped_err_code: Zir.Inst.Index = 0;
+    var opt_remapped_err_code: Zir.Inst.OptionalIndex = .none;
     const have_err_code = scope_tag == .defer_error and payload_token != 0;
     const sub_scope = if (!have_err_code) &defer_gen.base else blk: {
         try gz.addDbgBlockBegin();
         const ident_name = try gz.astgen.identAsString(payload_token);
-        remapped_err_code = @intCast(gz.astgen.instructions.len);
+        const remapped_err_code: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
+        opt_remapped_err_code = remapped_err_code.toOptional();
         try gz.astgen.instructions.append(gz.astgen.gpa, .{
             .tag = .extended,
             .data = .{ .extended = .{
@@ -2930,7 +2933,7 @@ fn deferStmt(
                 .operand = undefined,
             } },
         });
-        const remapped_err_code_ref = Zir.indexToRef(remapped_err_code);
+        const remapped_err_code_ref = remapped_err_code.toRef();
         local_val_scope = .{
             .parent = &defer_gen.base,
             .gen_zir = gz,
@@ -2945,13 +2948,13 @@ fn deferStmt(
     _ = try unusedResultExpr(&defer_gen, sub_scope, expr_node);
     try checkUsed(gz, scope, sub_scope);
     if (have_err_code) try gz.addDbgBlockEnd();
-    _ = try defer_gen.addBreak(.break_inline, 0, .void_value);
+    _ = try defer_gen.addBreak(.break_inline, @enumFromInt(0), .void_value);
 
     // We must handle ref_table for remapped_err_code manually.
     const body = defer_gen.instructionsSlice();
     const body_len = blk: {
         var refs: u32 = 0;
-        if (have_err_code) {
+        if (opt_remapped_err_code.unwrap()) |remapped_err_code| {
             var cur_inst = remapped_err_code;
             while (gz.astgen.ref_table.get(cur_inst)) |ref_inst| {
                 refs += 1;
@@ -2963,7 +2966,7 @@ fn deferStmt(
 
     const index: u32 = @intCast(gz.astgen.extra.items.len);
     try gz.astgen.extra.ensureUnusedCapacity(gz.astgen.gpa, body_len);
-    if (have_err_code) {
+    if (opt_remapped_err_code.unwrap()) |remapped_err_code| {
         if (gz.astgen.ref_table.fetchRemove(remapped_err_code)) |kv| {
             gz.astgen.appendPossiblyRefdBodyInst(&gz.astgen.extra, kv.value);
         }
@@ -2977,7 +2980,7 @@ fn deferStmt(
         .parent = scope,
         .index = index,
         .len = body_len,
-        .remapped_err_code = remapped_err_code,
+        .remapped_err_code = opt_remapped_err_code,
     };
     return &defer_scope.base;
 }
@@ -3226,9 +3229,9 @@ fn emitDbgNode(gz: *GenZir, node: Ast.Node.Index) !void {
     if (gz.instructions.items.len > 0) {
         const last = gz.instructions.items[gz.instructions.items.len - 1];
         const zir_tags = astgen.instructions.items(.tag);
-        if (zir_tags[last] == .dbg_stmt) {
+        if (zir_tags[@intFromEnum(last)] == .dbg_stmt) {
             const zir_datas = astgen.instructions.items(.data);
-            zir_datas[last].dbg_stmt = .{
+            zir_datas[@intFromEnum(last)].dbg_stmt = .{
                 .line = line,
                 .column = column,
             };
@@ -3721,8 +3724,8 @@ fn ptrType(
         gz.astgen.extra.appendAssumeCapacity(@intFromEnum(bit_end_ref));
     }
 
-    const new_index: Zir.Inst.Index = @intCast(gz.astgen.instructions.len);
-    const result = indexToRef(new_index);
+    const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
+    const result = new_index.toRef();
     gz.astgen.instructions.appendAssumeCapacity(.{ .tag = .ptr_type, .data = .{
         .ptr_type = .{
             .flags = .{
@@ -4049,7 +4052,7 @@ fn fnDecl(
                 var param_gz = decl_gz.makeSubBlock(scope);
                 defer param_gz.unstack();
                 const param_type = try expr(&param_gz, params_scope, coerced_type_ri, param_type_node);
-                const param_inst_expected: u32 = @intCast(astgen.instructions.len + 1);
+                const param_inst_expected: Zir.Inst.Index = @enumFromInt(astgen.instructions.len + 1);
                 _ = try param_gz.addBreakWithSrcNode(.break_inline, param_inst_expected, param_type, param_type_node);
 
                 const main_tokens = tree.nodes.items(.main_token);
@@ -4057,7 +4060,7 @@ fn fnDecl(
                 const tag: Zir.Inst.Tag = if (is_comptime) .param_comptime else .param;
                 const param_inst = try decl_gz.addParam(&param_gz, tag, name_token, param_name, param.first_doc_comment);
                 assert(param_inst_expected == param_inst);
-                break :param indexToRef(param_inst);
+                break :param param_inst.toRef();
             };
 
             if (param_name == 0 or is_extern) continue;
@@ -4102,7 +4105,7 @@ fn fnDecl(
             // In this case we will send a len=0 body which can be encoded more efficiently.
             break :inst inst;
         }
-        _ = try align_gz.addBreak(.break_inline, 0, inst);
+        _ = try align_gz.addBreak(.break_inline, @enumFromInt(0), inst);
         break :inst inst;
     };
 
@@ -4114,7 +4117,7 @@ fn fnDecl(
             // In this case we will send a len=0 body which can be encoded more efficiently.
             break :inst inst;
         }
-        _ = try addrspace_gz.addBreak(.break_inline, 0, inst);
+        _ = try addrspace_gz.addBreak(.break_inline, @enumFromInt(0), inst);
         break :inst inst;
     };
 
@@ -4126,7 +4129,7 @@ fn fnDecl(
             // In this case we will send a len=0 body which can be encoded more efficiently.
             break :inst inst;
         }
-        _ = try section_gz.addBreak(.break_inline, 0, inst);
+        _ = try section_gz.addBreak(.break_inline, @enumFromInt(0), inst);
         break :inst inst;
     };
 
@@ -4151,7 +4154,7 @@ fn fnDecl(
                 // In this case we will send a len=0 body which can be encoded more efficiently.
                 break :blk inst;
             }
-            _ = try cc_gz.addBreak(.break_inline, 0, inst);
+            _ = try cc_gz.addBreak(.break_inline, @enumFromInt(0), inst);
             break :blk inst;
         } else if (is_extern) {
             // note: https://github.com/ziglang/zig/issues/5269
@@ -4171,7 +4174,7 @@ fn fnDecl(
             // In this case we will send a len=0 body which can be encoded more efficiently.
             break :inst inst;
         }
-        _ = try ret_gz.addBreak(.break_inline, 0, inst);
+        _ = try ret_gz.addBreak(.break_inline, @enumFromInt(0), inst);
         break :inst inst;
     };
 
@@ -4271,7 +4274,7 @@ fn fnDecl(
         wip_members.appendToDecl(line_delta);
     }
     wip_members.appendToDecl(fn_name_str_index);
-    wip_members.appendToDecl(block_inst);
+    wip_members.appendToDecl(@intFromEnum(block_inst));
     wip_members.appendToDecl(doc_comment_index);
 }
 
@@ -4421,7 +4424,7 @@ fn globalVarDecl(
         wip_members.appendToDecl(line_delta);
     }
     wip_members.appendToDecl(name_str_index);
-    wip_members.appendToDecl(block_inst);
+    wip_members.appendToDecl(@intFromEnum(block_inst));
     wip_members.appendToDecl(doc_comment_index); // doc_comment wip
     if (align_inst != .none) {
         wip_members.appendToDecl(@intFromEnum(align_inst));
@@ -4475,7 +4478,7 @@ fn comptimeDecl(
         wip_members.appendToDecl(line_delta);
     }
     wip_members.appendToDecl(0);
-    wip_members.appendToDecl(block_inst);
+    wip_members.appendToDecl(@intFromEnum(block_inst));
     wip_members.appendToDecl(0); // no doc comments on comptime decls
 }
 
@@ -4526,7 +4529,7 @@ fn usingnamespaceDecl(
         wip_members.appendToDecl(line_delta);
     }
     wip_members.appendToDecl(0);
-    wip_members.appendToDecl(block_inst);
+    wip_members.appendToDecl(@intFromEnum(block_inst));
     wip_members.appendToDecl(0); // no doc comments on usingnamespace decls
 }
 
@@ -4715,7 +4718,7 @@ fn testDecl(
         wip_members.appendToDecl(2) // 2 here means that it is a decltest, look at doc comment for name
     else
         wip_members.appendToDecl(test_name);
-    wip_members.appendToDecl(block_inst);
+    wip_members.appendToDecl(@intFromEnum(block_inst));
     if (is_decltest)
         wip_members.appendToDecl(test_name) // the doc comment on a decltest represents it's name
     else
@@ -4747,7 +4750,7 @@ fn structDeclInner(
             .any_default_inits = false,
             .any_aligned_fields = false,
         });
-        return indexToRef(decl_inst);
+        return decl_inst.toRef();
     }
 
     const astgen = gz.astgen;
@@ -4993,7 +4996,7 @@ fn structDeclInner(
 
     block_scope.unstack();
     try gz.addNamespaceCaptures(&namespace);
-    return indexToRef(decl_inst);
+    return decl_inst.toRef();
 }
 
 fn unionDeclInner(
@@ -5154,7 +5157,7 @@ fn unionDeclInner(
 
     block_scope.unstack();
     try gz.addNamespaceCaptures(&namespace);
-    return indexToRef(decl_inst);
+    return decl_inst.toRef();
 }
 
 fn containerDecl(
@@ -5404,7 +5407,7 @@ fn containerDecl(
 
             block_scope.unstack();
             try gz.addNamespaceCaptures(&namespace);
-            return rvalue(gz, ri, indexToRef(decl_inst), node);
+            return rvalue(gz, ri, decl_inst.toRef(), node);
         },
         .keyword_opaque => {
             assert(container_decl.ast.arg == 0);
@@ -5455,7 +5458,7 @@ fn containerDecl(
 
             block_scope.unstack();
             try gz.addNamespaceCaptures(&namespace);
-            return rvalue(gz, ri, indexToRef(decl_inst), node);
+            return rvalue(gz, ri, decl_inst.toRef(), node);
         },
         else => unreachable,
     }
@@ -5642,7 +5645,7 @@ fn tryExpr(
     _ = try else_scope.addUnNode(.ret_node, err_code, node);
 
     try else_scope.setTryBody(try_inst, operand);
-    const result = indexToRef(try_inst);
+    const result = try_inst.toRef();
     switch (ri.rl) {
         .ref, .ref_coerced_ty => return result,
         else => return rvalue(parent_gz, ri, result, node),
@@ -5755,9 +5758,9 @@ fn orelseCatchExpr(
     try setCondBrPayload(condbr, cond, &then_scope, &else_scope);
 
     if (need_result_rvalue) {
-        return rvalue(parent_gz, ri, indexToRef(block), node);
+        return rvalue(parent_gz, ri, block.toRef(), node);
     } else {
-        return indexToRef(block);
+        return block.toRef();
     }
 }
 
@@ -5916,7 +5919,7 @@ fn boolBinOp(
     }
     try rhs_scope.setBoolBrBody(bool_br);
 
-    const block_ref = indexToRef(bool_br);
+    const block_ref = bool_br.toRef();
     return rvalue(gz, ri, block_ref, node);
 }
 
@@ -6116,9 +6119,9 @@ fn ifExpr(
     try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope);
 
     if (need_result_rvalue) {
-        return rvalue(parent_gz, ri, indexToRef(block), node);
+        return rvalue(parent_gz, ri, block.toRef(), node);
     } else {
-        return indexToRef(block);
+        return block.toRef();
     }
 }
 
@@ -6142,7 +6145,7 @@ fn setCondBrPayload(
     );
 
     const zir_datas = astgen.instructions.items(.data);
-    zir_datas[condbr].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.CondBr{
+    zir_datas[@intFromEnum(condbr)].pl_node.payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.CondBr{
         .condition = cond,
         .then_body_len = then_body_len,
         .else_body_len = else_body_len,
@@ -6245,7 +6248,7 @@ fn whileExpr(
 
     var dbg_var_name: ?u32 = null;
     var dbg_var_inst: Zir.Inst.Ref = undefined;
-    var payload_inst: Zir.Inst.Index = 0;
+    var opt_payload_inst: Zir.Inst.OptionalIndex = .none;
     var payload_val_scope: Scope.LocalVal = undefined;
     const then_sub_scope = s: {
         if (while_full.error_token != null) {
@@ -6255,7 +6258,8 @@ fn whileExpr(
                 else
                     .err_union_payload_unsafe;
                 // will add this instruction to then_scope.instructions below
-                payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr);
+                const payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr);
+                opt_payload_inst = payload_inst.toOptional();
                 const ident_token = if (payload_is_ref) payload_token + 1 else payload_token;
                 const ident_bytes = tree.tokenSlice(ident_token);
                 if (mem.eql(u8, "_", ident_bytes))
@@ -6267,12 +6271,12 @@ fn whileExpr(
                     .parent = &then_scope.base,
                     .gen_zir = &then_scope,
                     .name = ident_name,
-                    .inst = indexToRef(payload_inst),
+                    .inst = payload_inst.toRef(),
                     .token_src = payload_token,
                     .id_cat = .capture,
                 };
                 dbg_var_name = ident_name;
-                dbg_var_inst = indexToRef(payload_inst);
+                dbg_var_inst = payload_inst.toRef();
                 break :s &payload_val_scope.base;
             } else {
                 _ = try then_scope.addUnNode(.ensure_err_union_payload_void, cond.inst, node);
@@ -6285,7 +6289,8 @@ fn whileExpr(
             else
                 .optional_payload_unsafe;
             // will add this instruction to then_scope.instructions below
-            payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr);
+            const payload_inst = try then_scope.makeUnNode(tag, cond.inst, while_full.ast.cond_expr);
+            opt_payload_inst = payload_inst.toOptional();
             const ident_name = try astgen.identAsString(ident_token);
             const ident_bytes = tree.tokenSlice(ident_token);
             if (mem.eql(u8, "_", ident_bytes))
@@ -6295,12 +6300,12 @@ fn whileExpr(
                 .parent = &then_scope.base,
                 .gen_zir = &then_scope,
                 .name = ident_name,
-                .inst = indexToRef(payload_inst),
+                .inst = payload_inst.toRef(),
                 .token_src = ident_token,
                 .id_cat = .capture,
             };
             dbg_var_name = ident_name;
-            dbg_var_inst = indexToRef(payload_inst);
+            dbg_var_inst = payload_inst.toRef();
             break :s &payload_val_scope.base;
         } else {
             break :s &then_scope.base;
@@ -6316,8 +6321,8 @@ fn whileExpr(
     _ = try loop_scope.addNode(repeat_tag, node);
 
     try loop_scope.setBlockBody(loop_block);
-    loop_scope.break_block = loop_block;
-    loop_scope.continue_block = continue_block;
+    loop_scope.break_block = loop_block.toOptional();
+    loop_scope.continue_block = continue_block.toOptional();
     if (while_full.label_token) |label_token| {
         loop_scope.label = .{
             .token = label_token,
@@ -6330,7 +6335,9 @@ fn whileExpr(
 
     try then_scope.addDbgBlockBegin();
     const then_node = while_full.ast.then_expr;
-    if (payload_inst != 0) try then_scope.instructions.append(astgen.gpa, payload_inst);
+    if (opt_payload_inst.unwrap()) |payload_inst| {
+        try then_scope.instructions.append(astgen.gpa, payload_inst);
+    }
     if (dbg_var_name) |name| try then_scope.addDbgVar(.dbg_var_val, name, dbg_var_inst);
     try then_scope.instructions.append(astgen.gpa, continue_block);
     // This code could be improved to avoid emitting the continue expr when there
@@ -6386,8 +6393,8 @@ fn whileExpr(
         };
         // Remove the continue block and break block so that `continue` and `break`
         // control flow apply to outer loops; not this one.
-        loop_scope.continue_block = 0;
-        loop_scope.break_block = 0;
+        loop_scope.continue_block = .none;
+        loop_scope.break_block = .none;
         const else_result = try expr(&else_scope, sub_scope, loop_scope.break_result_info, else_node);
         if (is_statement) {
             _ = try addEnsureResult(&else_scope, else_result, else_node);
@@ -6412,9 +6419,9 @@ fn whileExpr(
     try setCondBrPayload(condbr, cond.bool_bit, &then_scope, &else_scope);
 
     const result = if (need_result_rvalue)
-        try rvalue(parent_gz, ri, indexToRef(loop_block), node)
+        try rvalue(parent_gz, ri, loop_block.toRef(), node)
     else
-        indexToRef(loop_block);
+        loop_block.toRef();
 
     if (is_statement) {
         _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
@@ -6577,8 +6584,8 @@ fn forExpr(
     const cond_block = try loop_scope.makeBlockInst(block_tag, node);
     try cond_scope.setBlockBody(cond_block);
 
-    loop_scope.break_block = loop_block;
-    loop_scope.continue_block = cond_block;
+    loop_scope.break_block = loop_block.toOptional();
+    loop_scope.continue_block = cond_block.toOptional();
     if (for_full.label_token) |label_token| {
         loop_scope.label = .{
             .token = label_token,
@@ -6671,8 +6678,8 @@ fn forExpr(
         const sub_scope = &else_scope.base;
         // Remove the continue block and break block so that `continue` and `break`
         // control flow apply to outer loops; not this one.
-        loop_scope.continue_block = 0;
-        loop_scope.break_block = 0;
+        loop_scope.continue_block = .none;
+        loop_scope.break_block = .none;
         const else_result = try expr(&else_scope, sub_scope, loop_scope.break_result_info, else_node);
         if (is_statement) {
             _ = try addEnsureResult(&else_scope, else_result, else_node);
@@ -6696,7 +6703,7 @@ fn forExpr(
     // then_block and else_block unstacked now, can resurrect loop_scope to finally finish it
     {
         loop_scope.instructions_top = loop_scope.instructions.items.len;
-        try loop_scope.instructions.appendSlice(gpa, &.{ Zir.refToIndex(index).?, cond_block });
+        try loop_scope.instructions.appendSlice(gpa, &.{ index.toIndex().?, cond_block });
 
         // Increment the index variable.
         const index_plus_one = try loop_scope.addPlNode(.add_unsafe, node, Zir.Inst.Bin{
@@ -6711,9 +6718,9 @@ fn forExpr(
     }
 
     const result = if (need_result_rvalue)
-        try rvalue(parent_gz, ri, indexToRef(loop_block), node)
+        try rvalue(parent_gz, ri, loop_block.toRef(), node)
     else
-        indexToRef(loop_block);
+        loop_block.toRef();
 
     if (is_statement) {
         _ = try parent_gz.addUnNode(.ensure_result_used, result, node);
@@ -6912,7 +6919,7 @@ fn switchExpr(
 
     // If any prong has an inline tag capture, allocate a shared dummy instruction for it
     const tag_inst = if (any_has_tag_capture) tag_inst: {
-        const inst: Zir.Inst.Index = @intCast(astgen.instructions.len);
+        const inst: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
         try astgen.instructions.append(astgen.gpa, .{
             .tag = .extended,
             .data = .{ .extended = .{
@@ -6967,12 +6974,12 @@ fn switchExpr(
                     .parent = &case_scope.base,
                     .gen_zir = &case_scope,
                     .name = capture_name,
-                    .inst = indexToRef(switch_block),
+                    .inst = switch_block.toRef(),
                     .token_src = payload_token,
                     .id_cat = .capture,
                 };
                 dbg_var_name = capture_name;
-                dbg_var_inst = indexToRef(switch_block);
+                dbg_var_inst = switch_block.toRef();
                 payload_sub_scope = &capture_val_scope.base;
             }
 
@@ -6996,12 +7003,12 @@ fn switchExpr(
                 .parent = payload_sub_scope,
                 .gen_zir = &case_scope,
                 .name = tag_name,
-                .inst = indexToRef(tag_inst),
+                .inst = tag_inst.toRef(),
                 .token_src = tag_token,
                 .id_cat = .@"switch tag capture",
             };
             dbg_var_tag_name = tag_name;
-            dbg_var_tag_inst = indexToRef(tag_inst);
+            dbg_var_tag_inst = tag_inst.toRef();
             break :blk &tag_scope.base;
         };
 
@@ -7136,11 +7143,11 @@ fn switchExpr(
     }
 
     if (any_has_tag_capture) {
-        astgen.extra.appendAssumeCapacity(tag_inst);
+        astgen.extra.appendAssumeCapacity(@intFromEnum(tag_inst));
     }
 
     const zir_datas = astgen.instructions.items(.data);
-    zir_datas[switch_block].pl_node.payload_index = payload_index;
+    zir_datas[@intFromEnum(switch_block)].pl_node.payload_index = payload_index;
 
     for (payloads.items[case_table_start..case_table_end], 0..) |start_index, i| {
         var body_len_index = start_index;
@@ -7163,9 +7170,9 @@ fn switchExpr(
     }
 
     if (need_result_rvalue) {
-        return rvalue(parent_gz, ri, indexToRef(switch_block), switch_node);
+        return rvalue(parent_gz, ri, switch_block.toRef(), switch_node);
     } else {
-        return indexToRef(switch_block);
+        return switch_block.toRef();
     }
 }
 
@@ -7524,13 +7531,13 @@ fn tunnelThroughClosure(
 ) !Zir.Inst.Ref {
     // For trivial values, we don't need a tunnel.
     // Just return the ref.
-    if (num_tunnels == 0 or refToIndex(value) == null) {
+    if (num_tunnels == 0 or value.toIndex() == null) {
         return value;
     }
 
     // Otherwise we need a tunnel.  Check if this namespace
     // already has one for this value.
-    const gop = try ns.?.captures.getOrPut(gpa, refToIndex(value).?);
+    const gop = try ns.?.captures.getOrPut(gpa, value.toIndex().?);
     if (!gop.found_existing) {
         // Make a new capture for this value but don't add it to the declaring_gz yet
         try gz.astgen.instructions.append(gz.astgen.gpa, .{
@@ -7540,7 +7547,7 @@ fn tunnelThroughClosure(
                 .src_tok = ns.?.declaring_gz.?.tokenIndexToRelative(token),
             } },
         });
-        gop.value_ptr.* = @intCast(gz.astgen.instructions.len - 1);
+        gop.value_ptr.* = @enumFromInt(gz.astgen.instructions.len - 1);
     }
 
     // Add an instruction to get the value from the closure into
@@ -8032,7 +8039,7 @@ fn typeOf(
 
         // typeof_scope unstacked now, can add new instructions to gz
         try gz.instructions.append(gpa, typeof_inst);
-        return rvalue(gz, ri, indexToRef(typeof_inst), node);
+        return rvalue(gz, ri, typeof_inst.toRef(), node);
     }
     const payload_size: u32 = std.meta.fields(Zir.Inst.TypeOfPeer).len;
     const payload_index = try reserveExtra(astgen, payload_size + args.len);
@@ -8047,7 +8054,7 @@ fn typeOf(
         const param_ref = try reachableExpr(&typeof_scope, &typeof_scope.base, .{ .rl = .none }, arg, node);
         astgen.extra.items[args_index + i] = @intFromEnum(param_ref);
     }
-    _ = try typeof_scope.addBreak(.break_inline, refToIndex(typeof_inst).?, .void_value);
+    _ = try typeof_scope.addBreak(.break_inline, typeof_inst.toIndex().?, .void_value);
 
     const body = typeof_scope.instructionsSlice();
     const body_len = astgen.countBodyLenAfterFixups(body);
@@ -8401,7 +8408,7 @@ fn builtinCall(
                 .node = gz.nodeIndexToRelative(node),
                 .operand = operand,
             });
-            const new_index: Zir.Inst.Index = @intCast(gz.astgen.instructions.len);
+            const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
             gz.astgen.instructions.appendAssumeCapacity(.{
                 .tag = .extended,
                 .data = .{ .extended = .{
@@ -8411,7 +8418,7 @@ fn builtinCall(
                 } },
             });
             gz.instructions.appendAssumeCapacity(new_index);
-            const result = indexToRef(new_index);
+            const result = new_index.toRef();
             return rvalue(gz, ri, result, node);
         },
         .panic => {
@@ -8993,7 +9000,7 @@ fn cImport(
     // block_scope unstacked now, can add new instructions to gz
     try gz.instructions.append(gpa, block_inst);
 
-    return indexToRef(block_inst);
+    return block_inst.toRef();
 }
 
 fn overflowArithmetic(
@@ -9056,8 +9063,8 @@ fn callExpr(
     }
     assert(node != 0);
 
-    const call_index: Zir.Inst.Index = @intCast(astgen.instructions.len);
-    const call_inst = Zir.indexToRef(call_index);
+    const call_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
+    const call_inst = call_index.toRef();
     try gz.astgen.instructions.append(astgen.gpa, undefined);
     try gz.instructions.append(astgen.gpa, call_index);
 
@@ -9104,7 +9111,7 @@ fn callExpr(
             if (call.ast.params.len != 0) {
                 try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]);
             }
-            gz.astgen.instructions.set(call_index, .{
+            gz.astgen.instructions.set(@intFromEnum(call_index), .{
                 .tag = .call,
                 .data = .{ .pl_node = .{
                     .src_node = gz.nodeIndexToRelative(node),
@@ -9125,7 +9132,7 @@ fn callExpr(
             if (call.ast.params.len != 0) {
                 try astgen.extra.appendSlice(astgen.gpa, astgen.scratch.items[scratch_top..]);
             }
-            gz.astgen.instructions.set(call_index, .{
+            gz.astgen.instructions.set(@intFromEnum(call_index), .{
                 .tag = .field_call,
                 .data = .{ .pl_node = .{
                     .src_node = gz.nodeIndexToRelative(node),
@@ -10062,10 +10069,10 @@ fn rvalueInner(
     allow_coerce_pre_ref: bool,
 ) InnerError!Zir.Inst.Ref {
     const result = r: {
-        if (refToIndex(raw_result)) |result_index| {
+        if (raw_result.toIndex()) |result_index| {
             const zir_tags = gz.astgen.instructions.items(.tag);
-            const data = gz.astgen.instructions.items(.data)[result_index];
-            if (zir_tags[result_index].isAlwaysVoid(data)) {
+            const data = gz.astgen.instructions.items(.data)[@intFromEnum(result_index)];
+            if (zir_tags[@intFromEnum(result_index)].isAlwaysVoid(data)) {
                 break :r Zir.Inst.Ref.void_value;
             }
         }
@@ -10094,16 +10101,16 @@ fn rvalueInner(
             const astgen = gz.astgen;
             const tree = astgen.tree;
             const src_token = tree.firstToken(src_node);
-            const result_index = refToIndex(coerced_result) orelse
+            const result_index = coerced_result.toIndex() orelse
                 return gz.addUnTok(.ref, coerced_result, src_token);
             const zir_tags = gz.astgen.instructions.items(.tag);
-            if (zir_tags[result_index].isParam() or astgen.isInferred(coerced_result))
+            if (zir_tags[@intFromEnum(result_index)].isParam() or astgen.isInferred(coerced_result))
                 return gz.addUnTok(.ref, coerced_result, src_token);
             const gop = try astgen.ref_table.getOrPut(astgen.gpa, result_index);
             if (!gop.found_existing) {
                 gop.value_ptr.* = try gz.makeUnTok(.ref, coerced_result, src_token);
             }
-            return indexToRef(gop.value_ptr.*);
+            return gop.value_ptr.*.toRef();
         },
         .ty => |ty_inst| {
             // Quickly eliminate some common, unnecessary type coercion.
@@ -10849,7 +10856,7 @@ const Scope = struct {
         parent: *Scope,
         index: u32,
         len: u32,
-        remapped_err_code: Zir.Inst.Index = 0,
+        remapped_err_code: Zir.Inst.OptionalIndex = .none,
     };
 
     /// Represents a global scope that has any number of declarations in it.
@@ -10919,8 +10926,8 @@ const GenZir = struct {
     /// if use is strictly nested. This saves prior size of list for unstacking.
     instructions_top: usize,
     label: ?Label = null,
-    break_block: Zir.Inst.Index = 0,
-    continue_block: Zir.Inst.Index = 0,
+    break_block: Zir.Inst.OptionalIndex = .none,
+    continue_block: Zir.Inst.OptionalIndex = .none,
     /// Only valid when setBreakResultInfo is called.
     break_result_info: AstGen.ResultInfo = undefined,
 
@@ -10995,14 +11002,14 @@ const GenZir = struct {
         if (gz.isEmpty()) return false;
         const tags = gz.astgen.instructions.items(.tag);
         const last_inst = gz.instructions.items[gz.instructions.items.len - 1];
-        return tags[last_inst].isNoReturn();
+        return tags[@intFromEnum(last_inst)].isNoReturn();
     }
 
     /// TODO all uses of this should be replaced with uses of `endsWithNoReturn`.
     fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool {
         if (inst_ref == .unreachable_value) return true;
-        if (refToIndex(inst_ref)) |inst_index| {
-            return gz.astgen.instructions.items(.tag)[inst_index].isNoReturn();
+        if (inst_ref.toIndex()) |inst_index| {
+            return gz.astgen.instructions.items(.tag)[@intFromEnum(inst_index)].isNoReturn();
         }
         return false;
     }
@@ -11053,7 +11060,7 @@ const GenZir = struct {
             @typeInfo(Zir.Inst.Block).Struct.fields.len + body_len,
         );
         const zir_datas = astgen.instructions.items(.data);
-        zir_datas[inst].bool_br.payload_index = astgen.addExtraAssumeCapacity(
+        zir_datas[@intFromEnum(inst)].bool_br.payload_index = astgen.addExtraAssumeCapacity(
             Zir.Inst.Block{ .body_len = body_len },
         );
         astgen.appendBodyWithFixups(body);
@@ -11071,7 +11078,7 @@ const GenZir = struct {
             @typeInfo(Zir.Inst.Block).Struct.fields.len + body_len,
         );
         const zir_datas = astgen.instructions.items(.data);
-        zir_datas[inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(
+        zir_datas[@intFromEnum(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity(
             Zir.Inst.Block{ .body_len = body_len },
         );
         astgen.appendBodyWithFixups(body);
@@ -11089,7 +11096,7 @@ const GenZir = struct {
             @typeInfo(Zir.Inst.Try).Struct.fields.len + body_len,
         );
         const zir_datas = astgen.instructions.items(.data);
-        zir_datas[inst].pl_node.payload_index = astgen.addExtraAssumeCapacity(
+        zir_datas[@intFromEnum(inst)].pl_node.payload_index = astgen.addExtraAssumeCapacity(
             Zir.Inst.Try{
                 .operand = operand,
                 .body_len = body_len,
@@ -11139,7 +11146,7 @@ const GenZir = struct {
         const astgen = gz.astgen;
         const gpa = astgen.gpa;
         const ret_ref = if (args.ret_ref == .void_type) .none else args.ret_ref;
-        const new_index: Zir.Inst.Index = @intCast(astgen.instructions.len);
+        const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
 
         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
 
@@ -11235,9 +11242,9 @@ const GenZir = struct {
             if (align_body.len != 0) {
                 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, align_body));
                 astgen.appendBodyWithFixups(align_body);
-                const break_extra = zir_datas[align_body[align_body.len - 1]].@"break".payload_index;
+                const break_extra = zir_datas[@intFromEnum(align_body[align_body.len - 1])].@"break".payload_index;
                 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
-                    new_index;
+                    @intFromEnum(new_index);
             } else if (args.align_ref != .none) {
                 astgen.extra.appendAssumeCapacity(@intFromEnum(args.align_ref));
             }
@@ -11245,9 +11252,9 @@ const GenZir = struct {
                 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, addrspace_body));
                 astgen.appendBodyWithFixups(addrspace_body);
                 const break_extra =
-                    zir_datas[addrspace_body[addrspace_body.len - 1]].@"break".payload_index;
+                    zir_datas[@intFromEnum(addrspace_body[addrspace_body.len - 1])].@"break".payload_index;
                 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
-                    new_index;
+                    @intFromEnum(new_index);
             } else if (args.addrspace_ref != .none) {
                 astgen.extra.appendAssumeCapacity(@intFromEnum(args.addrspace_ref));
             }
@@ -11255,27 +11262,27 @@ const GenZir = struct {
                 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, section_body));
                 astgen.appendBodyWithFixups(section_body);
                 const break_extra =
-                    zir_datas[section_body[section_body.len - 1]].@"break".payload_index;
+                    zir_datas[@intFromEnum(section_body[section_body.len - 1])].@"break".payload_index;
                 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
-                    new_index;
+                    @intFromEnum(new_index);
             } else if (args.section_ref != .none) {
                 astgen.extra.appendAssumeCapacity(@intFromEnum(args.section_ref));
             }
             if (cc_body.len != 0) {
                 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, cc_body));
                 astgen.appendBodyWithFixups(cc_body);
-                const break_extra = zir_datas[cc_body[cc_body.len - 1]].@"break".payload_index;
+                const break_extra = zir_datas[@intFromEnum(cc_body[cc_body.len - 1])].@"break".payload_index;
                 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
-                    new_index;
+                    @intFromEnum(new_index);
             } else if (args.cc_ref != .none) {
                 astgen.extra.appendAssumeCapacity(@intFromEnum(args.cc_ref));
             }
             if (ret_body.len != 0) {
                 astgen.extra.appendAssumeCapacity(countBodyLenAfterFixups(astgen, ret_body));
                 astgen.appendBodyWithFixups(ret_body);
-                const break_extra = zir_datas[ret_body[ret_body.len - 1]].@"break".payload_index;
+                const break_extra = zir_datas[@intFromEnum(ret_body[ret_body.len - 1])].@"break".payload_index;
                 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
-                    new_index;
+                    @intFromEnum(new_index);
             } else if (ret_ref != .none) {
                 astgen.extra.appendAssumeCapacity(@intFromEnum(ret_ref));
             }
@@ -11307,7 +11314,7 @@ const GenZir = struct {
                 } },
             });
             gz.instructions.appendAssumeCapacity(new_index);
-            return indexToRef(new_index);
+            return new_index.toRef();
         } else {
             try astgen.extra.ensureUnusedCapacity(
                 gpa,
@@ -11330,9 +11337,9 @@ const GenZir = struct {
             if (ret_body.len != 0) {
                 astgen.appendBodyWithFixups(ret_body);
 
-                const break_extra = zir_datas[ret_body[ret_body.len - 1]].@"break".payload_index;
+                const break_extra = zir_datas[@intFromEnum(ret_body[ret_body.len - 1])].@"break".payload_index;
                 astgen.extra.items[break_extra + std.meta.fieldIndex(Zir.Inst.Break, "block_inst").?] =
-                    new_index;
+                    @intFromEnum(new_index);
             } else if (ret_ref != .none) {
                 astgen.extra.appendAssumeCapacity(@intFromEnum(ret_ref));
             }
@@ -11358,7 +11365,7 @@ const GenZir = struct {
                 } },
             });
             gz.instructions.appendAssumeCapacity(new_index);
-            return indexToRef(new_index);
+            return new_index.toRef();
         }
     }
 
@@ -11402,7 +11409,7 @@ const GenZir = struct {
             astgen.extra.appendAssumeCapacity(@intFromEnum(args.init));
         }
 
-        const new_index: Zir.Inst.Index = @intCast(astgen.instructions.len);
+        const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
         astgen.instructions.appendAssumeCapacity(.{
             .tag = .extended,
             .data = .{ .extended = .{
@@ -11418,7 +11425,7 @@ const GenZir = struct {
             } },
         });
         gz.instructions.appendAssumeCapacity(new_index);
-        return indexToRef(new_index);
+        return new_index.toRef();
     }
 
     /// Note that this returns a `Zir.Inst.Index` not a ref.
@@ -11433,7 +11440,7 @@ const GenZir = struct {
         try gz.instructions.ensureUnusedCapacity(gpa, 1);
         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
 
-        const new_index: Zir.Inst.Index = @intCast(gz.astgen.instructions.len);
+        const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
         gz.astgen.instructions.appendAssumeCapacity(.{
             .tag = tag,
             .data = .{ .bool_br = .{
@@ -11459,7 +11466,7 @@ const GenZir = struct {
         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
         try astgen.string_bytes.ensureUnusedCapacity(gpa, @sizeOf(std.math.big.Limb) * limbs.len);
 
-        const new_index: Zir.Inst.Index = @intCast(astgen.instructions.len);
+        const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
         astgen.instructions.appendAssumeCapacity(.{
             .tag = .int_big,
             .data = .{ .str = .{
@@ -11469,7 +11476,7 @@ const GenZir = struct {
         });
         gz.instructions.appendAssumeCapacity(new_index);
         astgen.string_bytes.appendSliceAssumeCapacity(mem.sliceAsBytes(limbs));
-        return indexToRef(new_index);
+        return new_index.toRef();
     }
 
     fn addFloat(gz: *GenZir, number: f64) !Zir.Inst.Ref {
@@ -11504,7 +11511,7 @@ const GenZir = struct {
         src_node: Ast.Node.Index,
     ) !Zir.Inst.Index {
         assert(operand != .none);
-        const new_index: Zir.Inst.Index = @intCast(gz.astgen.instructions.len);
+        const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
         try gz.astgen.instructions.append(gz.astgen.gpa, .{
             .tag = tag,
             .data = .{ .un_node = .{
@@ -11527,7 +11534,7 @@ const GenZir = struct {
         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
 
         const payload_index = try gz.astgen.addExtra(extra);
-        const new_index: Zir.Inst.Index = @intCast(gz.astgen.instructions.len);
+        const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
         gz.astgen.instructions.appendAssumeCapacity(.{
             .tag = tag,
             .data = .{ .pl_node = .{
@@ -11536,7 +11543,7 @@ const GenZir = struct {
             } },
         });
         gz.instructions.appendAssumeCapacity(new_index);
-        return indexToRef(new_index);
+        return new_index.toRef();
     }
 
     fn addPlNodePayloadIndex(
@@ -11584,7 +11591,7 @@ const GenZir = struct {
         gz.astgen.appendBodyWithFixups(param_body);
         param_gz.unstack();
 
-        const new_index: Zir.Inst.Index = @intCast(gz.astgen.instructions.len);
+        const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
         gz.astgen.instructions.appendAssumeCapacity(.{
             .tag = tag,
             .data = .{ .pl_tok = .{
@@ -11612,7 +11619,7 @@ const GenZir = struct {
         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
 
         const payload_index = try gz.astgen.addExtra(extra);
-        const new_index: Zir.Inst.Index = @intCast(gz.astgen.instructions.len);
+        const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
         gz.astgen.instructions.appendAssumeCapacity(.{
             .tag = .extended,
             .data = .{ .extended = .{
@@ -11622,7 +11629,7 @@ const GenZir = struct {
             } },
         });
         gz.instructions.appendAssumeCapacity(new_index);
-        return indexToRef(new_index);
+        return new_index.toRef();
     }
 
     fn addExtendedMultiOp(
@@ -11644,7 +11651,7 @@ const GenZir = struct {
         const payload_index = astgen.addExtraAssumeCapacity(Zir.Inst.NodeMultiOp{
             .src_node = gz.nodeIndexToRelative(node),
         });
-        const new_index: Zir.Inst.Index = @intCast(astgen.instructions.len);
+        const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
         astgen.instructions.appendAssumeCapacity(.{
             .tag = .extended,
             .data = .{ .extended = .{
@@ -11655,7 +11662,7 @@ const GenZir = struct {
         });
         gz.instructions.appendAssumeCapacity(new_index);
         astgen.appendRefsAssumeCapacity(operands);
-        return indexToRef(new_index);
+        return new_index.toRef();
     }
 
     fn addExtendedMultiOpPayloadIndex(
@@ -11669,7 +11676,7 @@ const GenZir = struct {
 
         try gz.instructions.ensureUnusedCapacity(gpa, 1);
         try astgen.instructions.ensureUnusedCapacity(gpa, 1);
-        const new_index: Zir.Inst.Index = @intCast(astgen.instructions.len);
+        const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
         astgen.instructions.appendAssumeCapacity(.{
             .tag = .extended,
             .data = .{ .extended = .{
@@ -11679,7 +11686,7 @@ const GenZir = struct {
             } },
         });
         gz.instructions.appendAssumeCapacity(new_index);
-        return indexToRef(new_index);
+        return new_index.toRef();
     }
 
     fn addUnTok(
@@ -11707,7 +11714,7 @@ const GenZir = struct {
         abs_tok_index: Ast.TokenIndex,
     ) !Zir.Inst.Index {
         const astgen = gz.astgen;
-        const new_index: Zir.Inst.Index = @intCast(astgen.instructions.len);
+        const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
         assert(operand != .none);
         try astgen.instructions.append(astgen.gpa, .{
             .tag = tag,
@@ -11771,7 +11778,7 @@ const GenZir = struct {
             .data = .{ .restore_err_ret_index = .{
                 .block = switch (bt) {
                     .ret => .none,
-                    .block => |b| Zir.indexToRef(b),
+                    .block => |b| b.toRef(),
                 },
                 .operand = if (cond == .if_non_error) cond.if_non_error else .none,
             } },
@@ -11837,7 +11844,7 @@ const GenZir = struct {
         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
         try gz.astgen.extra.ensureUnusedCapacity(gpa, @typeInfo(Zir.Inst.Break).Struct.fields.len);
 
-        const new_index: Zir.Inst.Index = @intCast(gz.astgen.instructions.len);
+        const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
         gz.astgen.instructions.appendAssumeCapacity(.{
             .tag = tag,
             .data = .{ .@"break" = .{
@@ -11978,7 +11985,7 @@ const GenZir = struct {
         const is_comptime: u4 = @intFromBool(args.is_comptime);
         const small: u16 = has_type | (has_align << 1) | (is_const << 2) | (is_comptime << 3);
 
-        const new_index: Zir.Inst.Index = @intCast(astgen.instructions.len);
+        const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
         astgen.instructions.appendAssumeCapacity(.{
             .tag = .extended,
             .data = .{ .extended = .{
@@ -11988,7 +11995,7 @@ const GenZir = struct {
             } },
         });
         gz.instructions.appendAssumeCapacity(new_index);
-        return indexToRef(new_index);
+        return new_index.toRef();
     }
 
     fn addAsm(
@@ -12037,7 +12044,7 @@ const GenZir = struct {
             @as(u16, @intCast(args.clobbers.len << 10)) |
             (@as(u16, @intFromBool(args.is_volatile)) << 15);
 
-        const new_index: Zir.Inst.Index = @intCast(astgen.instructions.len);
+        const new_index: Zir.Inst.Index = @enumFromInt(astgen.instructions.len);
         astgen.instructions.appendAssumeCapacity(.{
             .tag = .extended,
             .data = .{ .extended = .{
@@ -12047,14 +12054,14 @@ const GenZir = struct {
             } },
         });
         gz.instructions.appendAssumeCapacity(new_index);
-        return indexToRef(new_index);
+        return new_index.toRef();
     }
 
     /// Note that this returns a `Zir.Inst.Index` not a ref.
     /// Does *not* append the block instruction to the scope.
     /// Leaves the `payload_index` field undefined.
     fn makeBlockInst(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
-        const new_index: Zir.Inst.Index = @intCast(gz.astgen.instructions.len);
+        const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
         const gpa = gz.astgen.gpa;
         try gz.astgen.instructions.append(gpa, .{
             .tag = tag,
@@ -12071,7 +12078,7 @@ const GenZir = struct {
     fn addCondBr(gz: *GenZir, tag: Zir.Inst.Tag, node: Ast.Node.Index) !Zir.Inst.Index {
         const gpa = gz.astgen.gpa;
         try gz.instructions.ensureUnusedCapacity(gpa, 1);
-        const new_index: Zir.Inst.Index = @intCast(gz.astgen.instructions.len);
+        const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
         try gz.astgen.instructions.append(gpa, .{
             .tag = tag,
             .data = .{ .pl_node = .{
@@ -12119,7 +12126,7 @@ const GenZir = struct {
                 astgen.extra.appendAssumeCapacity(@intFromEnum(args.backing_int_ref));
             }
         }
-        astgen.instructions.set(inst, .{
+        astgen.instructions.set(@intFromEnum(inst), .{
             .tag = .extended,
             .data = .{ .extended = .{
                 .opcode = .struct_decl,
@@ -12174,7 +12181,7 @@ const GenZir = struct {
         if (args.decls_len != 0) {
             astgen.extra.appendAssumeCapacity(args.decls_len);
         }
-        astgen.instructions.set(inst, .{
+        astgen.instructions.set(@intFromEnum(inst), .{
             .tag = .extended,
             .data = .{ .extended = .{
                 .opcode = .union_decl,
@@ -12224,7 +12231,7 @@ const GenZir = struct {
         if (args.decls_len != 0) {
             astgen.extra.appendAssumeCapacity(args.decls_len);
         }
-        astgen.instructions.set(inst, .{
+        astgen.instructions.set(@intFromEnum(inst), .{
             .tag = .extended,
             .data = .{ .extended = .{
                 .opcode = .enum_decl,
@@ -12259,7 +12266,7 @@ const GenZir = struct {
         if (args.decls_len != 0) {
             astgen.extra.appendAssumeCapacity(args.decls_len);
         }
-        astgen.instructions.set(inst, .{
+        astgen.instructions.set(@intFromEnum(inst), .{
             .tag = .extended,
             .data = .{ .extended = .{
                 .opcode = .opaque_decl,
@@ -12274,7 +12281,7 @@ const GenZir = struct {
     }
 
     fn add(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Ref {
-        return indexToRef(try gz.addAsIndex(inst));
+        return (try gz.addAsIndex(inst)).toRef();
     }
 
     fn addAsIndex(gz: *GenZir, inst: Zir.Inst) !Zir.Inst.Index {
@@ -12282,7 +12289,7 @@ const GenZir = struct {
         try gz.instructions.ensureUnusedCapacity(gpa, 1);
         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
 
-        const new_index: Zir.Inst.Index = @intCast(gz.astgen.instructions.len);
+        const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
         gz.astgen.instructions.appendAssumeCapacity(inst);
         gz.instructions.appendAssumeCapacity(new_index);
         return new_index;
@@ -12293,7 +12300,7 @@ const GenZir = struct {
         try gz.instructions.ensureUnusedCapacity(gpa, 1);
         try gz.astgen.instructions.ensureUnusedCapacity(gpa, 1);
 
-        const new_index: Zir.Inst.Index = @intCast(gz.astgen.instructions.len);
+        const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
         gz.astgen.instructions.len += 1;
         gz.instructions.appendAssumeCapacity(new_index);
         return new_index;
@@ -12340,12 +12347,12 @@ const GenZir = struct {
         const tags = gz.astgen.instructions.items(.tag);
         const last_inst = gz.instructions.items[gz.instructions.items.len - 1];
         // remove dbg_block_begin immediately followed by dbg_block_end
-        if (tags[last_inst] == .dbg_block_begin) {
+        if (tags[@intFromEnum(last_inst)] == .dbg_block_begin) {
             _ = gz.instructions.pop();
             return;
         }
 
-        const new_index: Zir.Inst.Index = @intCast(gz.astgen.instructions.len);
+        const new_index: Zir.Inst.Index = @enumFromInt(gz.astgen.instructions.len);
         try gz.astgen.instructions.append(gpa, .{ .tag = .dbg_block_end, .data = undefined });
         try gz.instructions.append(gpa, new_index);
     }
@@ -12622,9 +12629,9 @@ fn scanDecls(astgen: *AstGen, namespace: *Scope.Namespace, members: []const Ast.
 }
 
 fn isInferred(astgen: *AstGen, ref: Zir.Inst.Ref) bool {
-    const inst = refToIndex(ref) orelse return false;
+    const inst = ref.toIndex() orelse return false;
     const zir_tags = astgen.instructions.items(.tag);
-    return switch (zir_tags[inst]) {
+    return switch (zir_tags[@intFromEnum(inst)]) {
         .alloc_inferred,
         .alloc_inferred_mut,
         .alloc_inferred_comptime,
@@ -12633,8 +12640,8 @@ fn isInferred(astgen: *AstGen, ref: Zir.Inst.Ref) bool {
 
         .extended => {
             const zir_data = astgen.instructions.items(.data);
-            if (zir_data[inst].extended.opcode != .alloc) return false;
-            const small: Zir.Inst.AllocExtended.Small = @bitCast(zir_data[inst].extended.small);
+            if (zir_data[@intFromEnum(inst)].extended.opcode != .alloc) return false;
+            const small: Zir.Inst.AllocExtended.Small = @bitCast(zir_data[@intFromEnum(inst)].extended.small);
             return !small.has_type;
         },
 
@@ -12663,7 +12670,7 @@ fn appendPossiblyRefdBodyInst(
     list: *std.ArrayListUnmanaged(u32),
     body_inst: Zir.Inst.Index,
 ) void {
-    list.appendAssumeCapacity(body_inst);
+    list.appendAssumeCapacity(@intFromEnum(body_inst));
     const kv = astgen.ref_table.fetchRemove(body_inst) orelse return;
     const ref_inst = kv.value;
     return appendPossiblyRefdBodyInst(astgen, list, ref_inst);
src/Autodoc.zig
@@ -333,7 +333,7 @@ fn generateZirData(self: *Autodoc, output_dir: std.fs.Dir) !void {
         file,
         &root_scope,
         .{},
-        Zir.main_struct_inst,
+        .main_struct_inst,
         false,
         null,
     );
@@ -464,14 +464,14 @@ const Scope = struct {
     /// Another reason is that in some places we use the pointer to uniquely
     /// refer to a decl, as we wait for it to be analyzed. This means that
     /// those pointers must stay stable.
-    pub fn resolveDeclName(self: Scope, string_table_idx: u32, file: *File, inst_index: usize) *DeclStatus {
+    pub fn resolveDeclName(self: Scope, string_table_idx: u32, file: *File, inst: Zir.Inst.OptionalIndex) *DeclStatus {
         var cur: ?*const Scope = &self;
         return while (cur) |s| : (cur = s.parent) {
             break s.map.get(string_table_idx) orelse continue;
         } else {
-            printWithContext(
+            printWithOptionalContext(
                 file,
-                inst_index,
+                inst,
                 "Could not find `{s}`\n\n",
                 .{file.zir.nullTerminatedString(string_table_idx)},
             );
@@ -937,7 +937,7 @@ const AutodocErrors = error{
 ///  This type is used to keep track of dangerous instruction
 ///  numbers that we definitely don't want to recurse into.
 const CallContext = struct {
-    inst: usize,
+    inst: Zir.Inst.Index,
     prev: ?*const CallContext,
 };
 
@@ -954,14 +954,14 @@ fn walkInstruction(
     file: *File,
     parent_scope: *Scope,
     parent_src: SrcLocInfo,
-    inst_index: usize,
+    inst: Zir.Inst.Index,
     need_type: bool, // true if the caller needs us to provide also a typeRef
     call_ctx: ?*const CallContext,
 ) AutodocErrors!DocData.WalkResult {
     const tags = file.zir.instructions.items(.tag);
     const data = file.zir.instructions.items(.data);
 
-    if (self.repurposed_insts.contains(@intCast(inst_index))) {
+    if (self.repurposed_insts.contains(inst)) {
         // TODO: better handling here
         return .{ .expr = .{ .comptimeExpr = 0 } };
     }
@@ -969,18 +969,18 @@ fn walkInstruction(
     // We assume that the topmost ast_node entry corresponds to our decl
     const self_ast_node_index = self.ast_nodes.items.len - 1;
 
-    switch (tags[inst_index]) {
+    switch (tags[@intFromEnum(inst)]) {
         else => {
             printWithContext(
                 file,
-                inst_index,
+                inst,
                 "TODO: implement `{s}` for walkInstruction\n\n",
-                .{@tagName(tags[inst_index])},
+                .{@tagName(tags[@intFromEnum(inst)])},
             );
-            return self.cteTodo(@tagName(tags[inst_index]));
+            return self.cteTodo(@tagName(tags[@intFromEnum(inst)]));
         },
         .import => {
-            const str_tok = data[inst_index].str_tok;
+            const str_tok = data[@intFromEnum(inst)].str_tok;
             var path = str_tok.get(file.zir);
 
             // importFile cannot error out since all files
@@ -1048,7 +1048,7 @@ fn walkInstruction(
                     new_file,
                     &root_scope,
                     .{},
-                    Zir.main_struct_inst,
+                    .main_struct_inst,
                     false,
                     call_ctx,
                 );
@@ -1080,7 +1080,7 @@ fn walkInstruction(
                 new_file.file,
                 &new_scope,
                 .{},
-                Zir.main_struct_inst,
+                .main_struct_inst,
                 need_type,
                 call_ctx,
             );
@@ -1092,7 +1092,7 @@ fn walkInstruction(
             };
         },
         .ret_node => {
-            const un_node = data[inst_index].un_node;
+            const un_node = data[@intFromEnum(inst)].un_node;
             return self.walkRef(
                 file,
                 parent_scope,
@@ -1103,9 +1103,9 @@ fn walkInstruction(
             );
         },
         .ret_load => {
-            const un_node = data[inst_index].un_node;
+            const un_node = data[@intFromEnum(inst)].un_node;
             const res_ptr_ref = un_node.operand;
-            const res_ptr_inst = Zir.refToIndex(res_ptr_ref).?;
+            const res_ptr_inst = @intFromEnum(res_ptr_ref.toIndex().?);
             // TODO: this instruction doesn't let us know trivially if there's
             //       branching involved or not. For now here's the strat:
             //       We search backwarts until `ret_ptr` for `store_node`,
@@ -1113,7 +1113,7 @@ fn walkInstruction(
             //       than one, then it means that there's branching involved.
             //       Maybe.
 
-            var i = inst_index - 1;
+            var i = @intFromEnum(inst) - 1;
             var result_ref: ?Ref = null;
             while (i > res_ptr_inst) : (i -= 1) {
                 if (tags[i] == .store_node) {
@@ -1146,7 +1146,7 @@ fn walkInstruction(
             };
         },
         .closure_get => {
-            const inst_node = data[inst_index].inst_node;
+            const inst_node = data[@intFromEnum(inst)].inst_node;
             return try self.walkInstruction(
                 file,
                 parent_scope,
@@ -1157,7 +1157,7 @@ fn walkInstruction(
             );
         },
         .closure_capture => {
-            const un_tok = data[inst_index].un_tok;
+            const un_tok = data[@intFromEnum(inst)].un_tok;
             return try self.walkRef(
                 file,
                 parent_scope,
@@ -1168,7 +1168,7 @@ fn walkInstruction(
             );
         },
         .str => {
-            const str = data[inst_index].str.get(file.zir);
+            const str = data[@intFromEnum(inst)].str.get(file.zir);
 
             const tRef: ?DocData.Expr = if (!need_type) null else blk: {
                 const arrTypeId = self.types.items.len;
@@ -1204,7 +1204,7 @@ fn walkInstruction(
             };
         },
         .compile_error => {
-            const un_node = data[inst_index].un_node;
+            const un_node = data[@intFromEnum(inst)].un_node;
 
             var operand: DocData.WalkResult = try self.walkRef(
                 file,
@@ -1223,7 +1223,7 @@ fn walkInstruction(
             };
         },
         .enum_literal => {
-            const str_tok = data[inst_index].str_tok;
+            const str_tok = data[@intFromEnum(inst)].str_tok;
             const literal = file.zir.nullTerminatedString(str_tok.start);
             const type_index = self.types.items.len;
             try self.types.append(self.arena, .{
@@ -1236,7 +1236,7 @@ fn walkInstruction(
             };
         },
         .int => {
-            const int = data[inst_index].int;
+            const int = data[@intFromEnum(inst)].int;
             return DocData.WalkResult{
                 .typeRef = .{ .type = @intFromEnum(Ref.comptime_int_type) },
                 .expr = .{ .int = .{ .value = int } },
@@ -1244,7 +1244,7 @@ fn walkInstruction(
         },
         .int_big => {
             // @check
-            const str = data[inst_index].str; //.get(file.zir);
+            const str = data[@intFromEnum(inst)].str; //.get(file.zir);
             const byte_count = str.len * @sizeOf(std.math.big.Limb);
             const limb_bytes = file.zir.string_bytes[str.start..][0..byte_count];
 
@@ -1271,7 +1271,7 @@ fn walkInstruction(
         },
 
         .slice_start => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.SliceStart, pl_node.payload_index);
 
             const slice_index = self.exprs.items.len;
@@ -1311,7 +1311,7 @@ fn walkInstruction(
             };
         },
         .slice_end => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.SliceEnd, pl_node.payload_index);
 
             const slice_index = self.exprs.items.len;
@@ -1361,7 +1361,7 @@ fn walkInstruction(
             };
         },
         .slice_sentinel => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.SliceSentinel, pl_node.payload_index);
 
             const slice_index = self.exprs.items.len;
@@ -1426,7 +1426,7 @@ fn walkInstruction(
             };
         },
         .slice_length => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.SliceLength, pl_node.payload_index);
 
             const slice_index = self.exprs.items.len;
@@ -1498,7 +1498,7 @@ fn walkInstruction(
         },
 
         .load => {
-            const un_node = data[inst_index].un_node;
+            const un_node = data[@intFromEnum(inst)].un_node;
             const operand = try self.walkRef(
                 file,
                 parent_scope,
@@ -1529,7 +1529,7 @@ fn walkInstruction(
             };
         },
         .ref => {
-            const un_tok = data[inst_index].un_tok;
+            const un_tok = data[@intFromEnum(inst)].un_tok;
             const operand = try self.walkRef(
                 file,
                 parent_scope,
@@ -1565,7 +1565,7 @@ fn walkInstruction(
         .array_cat,
         .array_mul,
         => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.Bin, pl_node.payload_index);
 
             const binop_index = self.exprs.items.len;
@@ -1593,7 +1593,7 @@ fn walkInstruction(
             const rhs_index = self.exprs.items.len;
             try self.exprs.append(self.arena, rhs.expr);
             self.exprs.items[binop_index] = .{ .binOp = .{
-                .name = @tagName(tags[inst_index]),
+                .name = @tagName(tags[@intFromEnum(inst)]),
                 .lhs = lhs_index,
                 .rhs = rhs_index,
             } };
@@ -1611,7 +1611,7 @@ fn walkInstruction(
         .cmp_lt,
         .cmp_lte,
         => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.Bin, pl_node.payload_index);
 
             const binop_index = self.exprs.items.len;
@@ -1639,7 +1639,7 @@ fn walkInstruction(
             const rhs_index = self.exprs.items.len;
             try self.exprs.append(self.arena, rhs.expr);
             self.exprs.items[binop_index] = .{ .binOp = .{
-                .name = @tagName(tags[inst_index]),
+                .name = @tagName(tags[@intFromEnum(inst)]),
                 .lhs = lhs_index,
                 .rhs = rhs_index,
             } };
@@ -1684,7 +1684,7 @@ fn walkInstruction(
         .byte_swap,
         .bit_reverse,
         => {
-            const un_node = data[inst_index].un_node;
+            const un_node = data[@intFromEnum(inst)].un_node;
             const bin_index = self.exprs.items.len;
             try self.exprs.append(self.arena, .{ .builtin = .{ .param = 0 } });
             const param = try self.walkRef(
@@ -1701,7 +1701,7 @@ fn walkInstruction(
 
             self.exprs.items[bin_index] = .{
                 .builtin = .{
-                    .name = @tagName(tags[inst_index]),
+                    .name = @tagName(tags[@intFromEnum(inst)]),
                     .param = param_index,
                 },
             };
@@ -1715,7 +1715,7 @@ fn walkInstruction(
         .bool_not,
         .negate_wrap,
         => {
-            const un_node = data[inst_index].un_node;
+            const un_node = data[@intFromEnum(inst)].un_node;
             const un_index = self.exprs.items.len;
             try self.exprs.append(self.arena, .{ .unOp = .{ .param = 0 } });
             const param = try self.walkRef(
@@ -1732,7 +1732,7 @@ fn walkInstruction(
 
             self.exprs.items[un_index] = .{
                 .unOp = .{
-                    .name = @tagName(tags[inst_index]),
+                    .name = @tagName(tags[@intFromEnum(inst)]),
                     .param = param_index,
                 },
             };
@@ -1743,7 +1743,7 @@ fn walkInstruction(
             };
         },
         .bool_br_and, .bool_br_or => {
-            const bool_br = data[inst_index].bool_br;
+            const bool_br = data[@intFromEnum(inst)].bool_br;
 
             const bin_index = self.exprs.items.len;
             try self.exprs.append(self.arena, .{ .binOp = .{ .lhs = 0, .rhs = 0 } });
@@ -1764,14 +1764,14 @@ fn walkInstruction(
                 file,
                 parent_scope,
                 parent_src,
-                file.zir.extra[extra.end..][extra.data.body_len - 1],
+                @enumFromInt(file.zir.extra[extra.end..][extra.data.body_len - 1]),
                 false,
                 call_ctx,
             );
             const rhs_index = self.exprs.items.len;
             try self.exprs.append(self.arena, rhs.expr);
 
-            self.exprs.items[bin_index] = .{ .binOp = .{ .name = @tagName(tags[inst_index]), .lhs = lhs_index, .rhs = rhs_index } };
+            self.exprs.items[bin_index] = .{ .binOp = .{ .name = @tagName(tags[@intFromEnum(inst)]), .lhs = lhs_index, .rhs = rhs_index } };
 
             return DocData.WalkResult{
                 .typeRef = .{ .type = @intFromEnum(Ref.bool_type) },
@@ -1780,7 +1780,7 @@ fn walkInstruction(
         },
         .truncate => {
             // in the ZIR this node is a builtin `bin` but we want send it as a `un` builtin
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.Bin, pl_node.payload_index);
 
             var rhs: DocData.WalkResult = try self.walkRef(
@@ -1807,7 +1807,7 @@ fn walkInstruction(
                 call_ctx,
             );
 
-            self.exprs.items[bin_index] = .{ .builtin = .{ .name = @tagName(tags[inst_index]), .param = rhs_index } };
+            self.exprs.items[bin_index] = .{ .builtin = .{ .name = @tagName(tags[@intFromEnum(inst)]), .param = rhs_index } };
 
             return DocData.WalkResult{
                 .typeRef = lhs.expr,
@@ -1841,7 +1841,7 @@ fn walkInstruction(
         .min,
         .max,
         => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.Bin, pl_node.payload_index);
 
             const binop_index = self.exprs.items.len;
@@ -1868,7 +1868,7 @@ fn walkInstruction(
             try self.exprs.append(self.arena, lhs.expr);
             const rhs_index = self.exprs.items.len;
             try self.exprs.append(self.arena, rhs.expr);
-            self.exprs.items[binop_index] = .{ .builtinBin = .{ .name = @tagName(tags[inst_index]), .lhs = lhs_index, .rhs = rhs_index } };
+            self.exprs.items[binop_index] = .{ .builtinBin = .{ .name = @tagName(tags[@intFromEnum(inst)]), .lhs = lhs_index, .rhs = rhs_index } };
 
             return DocData.WalkResult{
                 .typeRef = .{ .type = @intFromEnum(Ref.type_type) },
@@ -1876,7 +1876,7 @@ fn walkInstruction(
             };
         },
         .mul_add => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.MulAdd, pl_node.payload_index);
 
             var mul1: DocData.WalkResult = try self.walkRef(
@@ -1927,7 +1927,7 @@ fn walkInstruction(
             };
         },
         .union_init => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.UnionInit, pl_node.payload_index);
 
             var union_type: DocData.WalkResult = try self.walkRef(
@@ -1974,7 +1974,7 @@ fn walkInstruction(
             };
         },
         .builtin_call => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.BuiltinCall, pl_node.payload_index);
 
             var modifier: DocData.WalkResult = try self.walkRef(
@@ -2022,7 +2022,7 @@ fn walkInstruction(
             };
         },
         .error_union_type => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.Bin, pl_node.payload_index);
 
             var lhs: DocData.WalkResult = try self.walkRef(
@@ -2054,7 +2054,7 @@ fn walkInstruction(
             };
         },
         .merge_error_sets => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.Bin, pl_node.payload_index);
 
             var lhs: DocData.WalkResult = try self.walkRef(
@@ -2085,7 +2085,7 @@ fn walkInstruction(
             };
         },
         // .elem_type => {
-        //     const un_node = data[inst_index].un_node;
+        //     const un_node = data[@intFromEnum(inst)].un_node;
 
         //     var operand: DocData.WalkResult = try self.walkRef(
         //         file,
@@ -2097,7 +2097,7 @@ fn walkInstruction(
         //     return operand;
         // },
         .ptr_type => {
-            const ptr = data[inst_index].ptr_type;
+            const ptr = data[@intFromEnum(inst)].ptr_type;
             const extra = file.zir.extraData(Zir.Inst.PtrType, ptr.payload_index);
             var extra_index = extra.end;
 
@@ -2208,7 +2208,7 @@ fn walkInstruction(
             };
         },
         .array_type => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
 
             const bin = file.zir.extraData(Zir.Inst.Bin, pl_node.payload_index).data;
             const len = try self.walkRef(
@@ -2242,7 +2242,7 @@ fn walkInstruction(
             };
         },
         .array_type_sentinel => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.ArrayTypeSentinel, pl_node.payload_index);
             const len = try self.walkRef(
                 file,
@@ -2283,7 +2283,7 @@ fn walkInstruction(
             };
         },
         .array_init => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.MultiOp, pl_node.payload_index);
             const operands = file.zir.refSlice(extra.end, extra.data.operands_len);
             const array_data = try self.arena.alloc(usize, operands.len - 1);
@@ -2318,7 +2318,7 @@ fn walkInstruction(
             };
         },
         .array_init_anon => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.MultiOp, pl_node.payload_index);
             const operands = file.zir.refSlice(extra.end, extra.data.operands_len);
             const array_data = try self.arena.alloc(usize, operands.len);
@@ -2343,7 +2343,7 @@ fn walkInstruction(
             };
         },
         .array_init_ref => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.MultiOp, pl_node.payload_index);
             const operands = file.zir.refSlice(extra.end, extra.data.operands_len);
             const array_data = try self.arena.alloc(usize, operands.len - 1);
@@ -2389,7 +2389,7 @@ fn walkInstruction(
             };
         },
         .float => {
-            const float = data[inst_index].float;
+            const float = data[@intFromEnum(inst)].float;
             return DocData.WalkResult{
                 .typeRef = .{ .type = @intFromEnum(Ref.comptime_float_type) },
                 .expr = .{ .float = float },
@@ -2397,7 +2397,7 @@ fn walkInstruction(
         },
         // @check: In frontend I'm handling float128 with `.toFixed(2)`
         .float128 => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.Float128, pl_node.payload_index);
             return DocData.WalkResult{
                 .typeRef = .{ .type = @intFromEnum(Ref.comptime_float_type) },
@@ -2405,7 +2405,7 @@ fn walkInstruction(
             };
         },
         .negate => {
-            const un_node = data[inst_index].un_node;
+            const un_node = data[@intFromEnum(inst)].un_node;
 
             var operand: DocData.WalkResult = try self.walkRef(
                 file,
@@ -2425,7 +2425,7 @@ fn walkInstruction(
                     try self.exprs.append(self.arena, operand.expr);
                     self.exprs.items[un_index] = .{
                         .unOp = .{
-                            .name = @tagName(tags[inst_index]),
+                            .name = @tagName(tags[@intFromEnum(inst)]),
                             .param = param_index,
                         },
                     };
@@ -2438,7 +2438,7 @@ fn walkInstruction(
             return operand;
         },
         .size_of => {
-            const un_node = data[inst_index].un_node;
+            const un_node = data[@intFromEnum(inst)].un_node;
 
             const operand = try self.walkRef(
                 file,
@@ -2457,7 +2457,7 @@ fn walkInstruction(
         },
         .bit_size_of => {
             // not working correctly with `align()`
-            const un_node = data[inst_index].un_node;
+            const un_node = data[@intFromEnum(inst)].un_node;
 
             const operand = try self.walkRef(
                 file,
@@ -2477,7 +2477,7 @@ fn walkInstruction(
         },
         .int_from_enum => {
             // not working correctly with `align()`
-            const un_node = data[inst_index].un_node;
+            const un_node = data[@intFromEnum(inst)].un_node;
             const operand = try self.walkRef(
                 file,
                 parent_scope,
@@ -2492,7 +2492,7 @@ fn walkInstruction(
             try self.exprs.append(self.arena, operand.expr);
             self.exprs.items[builtin_index] = .{
                 .builtin = .{
-                    .name = @tagName(tags[inst_index]),
+                    .name = @tagName(tags[@intFromEnum(inst)]),
                     .param = operand_index,
                 },
             };
@@ -2504,7 +2504,7 @@ fn walkInstruction(
         },
         .switch_block => {
             // WIP
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.SwitchBlock, pl_node.payload_index);
 
             const switch_cond = try self.walkRef(
@@ -2553,7 +2553,7 @@ fn walkInstruction(
         },
 
         .typeof => {
-            const un_node = data[inst_index].un_node;
+            const un_node = data[@intFromEnum(inst)].un_node;
 
             const operand = try self.walkRef(
                 file,
@@ -2572,7 +2572,7 @@ fn walkInstruction(
             };
         },
         .typeof_builtin => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.Block, pl_node.payload_index);
             const body = file.zir.extra[extra.end..][extra.data.body_len - 1];
             var operand: DocData.WalkResult = try self.walkRef(
@@ -2593,11 +2593,11 @@ fn walkInstruction(
             };
         },
         .as_node, .as_shift_operand => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.As, pl_node.payload_index);
 
             // Skip the as_node if the destination type is a call instruction
-            if (Zir.refToIndex(extra.data.dest_type)) |dti| {
+            if (extra.data.dest_type.toIndex()) |dti| {
                 var maybe_cc = call_ctx;
                 while (maybe_cc) |cc| : (maybe_cc = cc.prev) {
                     if (cc.inst == dti) {
@@ -2650,7 +2650,7 @@ fn walkInstruction(
             };
         },
         .optional_type => {
-            const un_node = data[inst_index].un_node;
+            const un_node = data[@intFromEnum(inst)].un_node;
 
             const operand: DocData.WalkResult = try self.walkRef(
                 file,
@@ -2672,14 +2672,14 @@ fn walkInstruction(
             };
         },
         .decl_val, .decl_ref => {
-            const str_tok = data[inst_index].str_tok;
-            const decl_status = parent_scope.resolveDeclName(str_tok.start, file, inst_index);
+            const str_tok = data[@intFromEnum(inst)].str_tok;
+            const decl_status = parent_scope.resolveDeclName(str_tok.start, file, inst.toOptional());
             return DocData.WalkResult{
                 .expr = .{ .declRef = decl_status },
             };
         },
         .field_val, .field_ptr => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.Field, pl_node.payload_index);
 
             var path: std.ArrayListUnmanaged(DocData.Expr) = .{};
@@ -2692,12 +2692,15 @@ fn walkInstruction(
             const lhs_ref = blk: {
                 var lhs_extra = extra;
                 while (true) {
-                    const lhs = Zir.refToIndex(lhs_extra.data.lhs) orelse {
+                    const lhs = @intFromEnum(lhs_extra.data.lhs.toIndex() orelse {
                         break :blk lhs_extra.data.lhs;
-                    };
+                    });
 
                     if (tags[lhs] != .field_val and
-                        tags[lhs] != .field_ptr) break :blk lhs_extra.data.lhs;
+                        tags[lhs] != .field_ptr)
+                    {
+                        break :blk lhs_extra.data.lhs;
+                    }
 
                     lhs_extra = file.zir.extraData(
                         Zir.Inst.Field,
@@ -2721,15 +2724,16 @@ fn walkInstruction(
             // TODO: double check that we really don't need type info here
 
             const wr = blk: {
-                if (Zir.refToIndex(lhs_ref)) |lhs_inst| {
-                    if (tags[lhs_inst] == .call or tags[lhs_inst] == .field_call) {
+                if (lhs_ref.toIndex()) |lhs_inst| switch (tags[@intFromEnum(lhs_inst)]) {
+                    .call, .field_call => {
                         break :blk DocData.WalkResult{
                             .expr = .{
                                 .comptimeExpr = 0,
                             },
                         };
-                    }
-                }
+                    },
+                    else => {},
+                };
 
                 break :blk try self.walkRef(
                     file,
@@ -2758,11 +2762,11 @@ fn walkInstruction(
             // - (2) Paths can sometimes never resolve fully. This means that
             //       any value that depends on that will have to become a
             //       comptimeExpr.
-            try self.tryResolveRefPath(file, inst_index, path.items);
+            try self.tryResolveRefPath(file, inst, path.items);
             return DocData.WalkResult{ .expr = .{ .refPath = path.items } };
         },
         .int_type => {
-            const int_type = data[inst_index].int_type;
+            const int_type = data[@intFromEnum(inst)].int_type;
             const sign = if (int_type.signedness == .unsigned) "u" else "i";
             const bits = int_type.bit_count;
             const name = try std.fmt.allocPrint(self.arena, "{s}{}", .{ sign, bits });
@@ -2781,7 +2785,7 @@ fn walkInstruction(
                 .typeRef = .{ .type = @intFromEnum(Ref.type_type) },
                 .expr = .{ .comptimeExpr = self.comptime_exprs.items.len },
             };
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const block_expr = try self.getBlockSource(file, parent_src, pl_node.src_node);
             try self.comptime_exprs.append(self.arena, .{
                 .code = block_expr,
@@ -2793,12 +2797,12 @@ fn walkInstruction(
                 file,
                 parent_scope,
                 parent_src,
-                getBlockInlineBreak(file.zir, inst_index) orelse {
+                getBlockInlineBreak(file.zir, inst) orelse {
                     const res = DocData.WalkResult{
                         .typeRef = .{ .type = @intFromEnum(Ref.type_type) },
                         .expr = .{ .comptimeExpr = self.comptime_exprs.items.len },
                     };
-                    const pl_node = data[inst_index].pl_node;
+                    const pl_node = data[@intFromEnum(inst)].pl_node;
                     const block_inline_expr = try self.getBlockSource(file, parent_src, pl_node.src_node);
                     try self.comptime_exprs.append(self.arena, .{
                         .code = block_inline_expr,
@@ -2810,7 +2814,7 @@ fn walkInstruction(
             );
         },
         .break_inline => {
-            const @"break" = data[inst_index].@"break";
+            const @"break" = data[@intFromEnum(inst)].@"break";
             return try self.walkRef(
                 file,
                 parent_scope,
@@ -2821,7 +2825,7 @@ fn walkInstruction(
             );
         },
         .struct_init => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.StructInit, pl_node.payload_index);
             const field_vals = try self.arena.alloc(
                 DocData.Expr.FieldVal,
@@ -2835,7 +2839,7 @@ fn walkInstruction(
                 defer idx = init_extra.end;
 
                 const field_name = blk: {
-                    const field_inst_index = init_extra.data.field_type;
+                    const field_inst_index = @intFromEnum(init_extra.data.field_type);
                     if (tags[field_inst_index] != .struct_init_field_type) unreachable;
                     const field_pl_node = data[field_inst_index].pl_node;
                     const field_extra = file.zir.extraData(
@@ -2881,7 +2885,7 @@ fn walkInstruction(
         .struct_init_empty,
         .struct_init_empty_result,
         => {
-            const un_node = data[inst_index].un_node;
+            const un_node = data[@intFromEnum(inst)].un_node;
 
             var operand: DocData.WalkResult = try self.walkRef(
                 file,
@@ -2898,7 +2902,7 @@ fn walkInstruction(
             };
         },
         .struct_init_empty_ref_result => {
-            const un_node = data[inst_index].un_node;
+            const un_node = data[@intFromEnum(inst)].un_node;
 
             var operand: DocData.WalkResult = try self.walkRef(
                 file,
@@ -2918,7 +2922,7 @@ fn walkInstruction(
             };
         },
         .struct_init_anon => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.StructInitAnon, pl_node.payload_index);
 
             const field_vals = try self.arena.alloc(
@@ -2947,7 +2951,7 @@ fn walkInstruction(
             };
         },
         .error_set_decl => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.ErrorSetDecl, pl_node.payload_index);
             const fields = try self.arena.alloc(
                 DocData.Type.Field,
@@ -2986,7 +2990,7 @@ fn walkInstruction(
             // This switch case handles the case where an expression depends
             // on an anytype field. E.g.: `fn foo(bar: anytype) @TypeOf(bar)`.
             // This means that we're looking at a generic expression.
-            const str_tok = data[inst_index].str_tok;
+            const str_tok = data[@intFromEnum(inst)].str_tok;
             const name = str_tok.get(file.zir);
             const cte_slot_index = self.comptime_exprs.items.len;
             try self.comptime_exprs.append(self.arena, .{
@@ -2996,7 +3000,7 @@ fn walkInstruction(
         },
         .param, .param_comptime => {
             // See .param_anytype for more information.
-            const pl_tok = data[inst_index].pl_tok;
+            const pl_tok = data[@intFromEnum(inst)].pl_tok;
             const extra = file.zir.extraData(Zir.Inst.Param, pl_tok.payload_index);
             const name = file.zir.nullTerminatedString(extra.data.name);
 
@@ -3007,7 +3011,7 @@ fn walkInstruction(
             return DocData.WalkResult{ .expr = .{ .comptimeExpr = cte_slot_index } };
         },
         .call => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.Call, pl_node.payload_index);
 
             const callee = try self.walkRef(
@@ -3023,8 +3027,8 @@ fn walkInstruction(
             var args = try self.arena.alloc(DocData.Expr, args_len);
             const body = file.zir.extra[extra.end..];
 
-            try self.repurposed_insts.put(self.arena, @intCast(inst_index), {});
-            defer _ = self.repurposed_insts.remove(@intCast(inst_index));
+            try self.repurposed_insts.put(self.arena, inst, {});
+            defer _ = self.repurposed_insts.remove(inst);
 
             var i: usize = 0;
             while (i < args_len) : (i += 1) {
@@ -3042,7 +3046,7 @@ fn walkInstruction(
                     ref,
                     false,
                     &.{
-                        .inst = inst_index,
+                        .inst = inst,
                         .prev = call_ctx,
                     },
                 );
@@ -3068,7 +3072,7 @@ fn walkInstruction(
                         else => blk: {
                             printWithContext(
                                 file,
-                                inst_index,
+                                inst,
                                 "unexpected callee type in walkInstruction.call: `{s}`\n",
                                 .{@tagName(self.types.items[func_type_idx])},
                             );
@@ -3089,10 +3093,10 @@ fn walkInstruction(
                 file,
                 parent_scope,
                 parent_src,
-                inst_index,
+                inst,
                 self_ast_node_index,
                 type_slot_index,
-                tags[inst_index] == .func_inferred,
+                tags[@intFromEnum(inst)] == .func_inferred,
                 call_ctx,
             );
 
@@ -3106,7 +3110,7 @@ fn walkInstruction(
                 file,
                 parent_scope,
                 parent_src,
-                inst_index,
+                inst,
                 self_ast_node_index,
                 type_slot_index,
                 call_ctx,
@@ -3115,7 +3119,7 @@ fn walkInstruction(
             return result;
         },
         .optional_payload_safe, .optional_payload_unsafe => {
-            const un_node = data[inst_index].un_node;
+            const un_node = data[@intFromEnum(inst)].un_node;
             const operand = try self.walkRef(
                 file,
                 parent_scope,
@@ -3135,7 +3139,7 @@ fn walkInstruction(
                         switch (t) {
                             .Optional => |opt| typeRef = opt.child,
                             else => {
-                                printWithContext(file, inst_index, "Invalid type for optional_payload_*: {}\n", .{t});
+                                printWithContext(file, inst, "Invalid type for optional_payload_*: {}\n", .{t});
                             },
                         }
                     },
@@ -3149,7 +3153,7 @@ fn walkInstruction(
             };
         },
         .elem_val_node => {
-            const pl_node = data[inst_index].pl_node;
+            const pl_node = data[@intFromEnum(inst)].pl_node;
             const extra = file.zir.extraData(Zir.Inst.Bin, pl_node.payload_index);
             const lhs = try self.walkRef(
                 file,
@@ -3181,12 +3185,12 @@ fn walkInstruction(
             };
         },
         .extended => {
-            const extended = data[inst_index].extended;
+            const extended = data[@intFromEnum(inst)].extended;
             switch (extended.opcode) {
                 else => {
                     printWithContext(
                         file,
-                        inst_index,
+                        inst,
                         "TODO: implement `walkInstruction.extended` for {s}",
                         .{@tagName(extended.opcode)},
                     );
@@ -3265,7 +3269,7 @@ fn walkInstruction(
                     extra_index = try self.analyzeAllDecls(
                         file,
                         &scope,
-                        inst_index,
+                        inst,
                         src_info,
                         &decl_indexes,
                         &priv_decl_indexes,
@@ -3285,7 +3289,7 @@ fn walkInstruction(
                         for (paths.items) |resume_info| {
                             try self.tryResolveRefPath(
                                 resume_info.file,
-                                inst_index,
+                                inst,
                                 resume_info.ref_path,
                             );
                         }
@@ -3393,7 +3397,7 @@ fn walkInstruction(
                     extra_index = try self.analyzeAllDecls(
                         file,
                         &scope,
-                        inst_index,
+                        inst,
                         src_info,
                         &decl_indexes,
                         &priv_decl_indexes,
@@ -3452,7 +3456,7 @@ fn walkInstruction(
                         for (paths.items) |resume_info| {
                             try self.tryResolveRefPath(
                                 resume_info.file,
-                                inst_index,
+                                inst,
                                 resume_info.ref_path,
                             );
                         }
@@ -3524,7 +3528,7 @@ fn walkInstruction(
                     extra_index = try self.analyzeAllDecls(
                         file,
                         &scope,
-                        inst_index,
+                        inst,
                         src_info,
                         &decl_indexes,
                         &priv_decl_indexes,
@@ -3604,7 +3608,7 @@ fn walkInstruction(
                         for (paths.items) |resume_info| {
                             try self.tryResolveRefPath(
                                 resume_info.file,
-                                inst_index,
+                                inst,
                                 resume_info.ref_path,
                             );
                         }
@@ -3668,9 +3672,9 @@ fn walkInstruction(
                             backing_int = backing_int_res.expr;
                             extra_index += 1; // backing_int_ref
                         } else {
-                            const backing_int_body = file.zir.extra[extra_index..][0..backing_int_body_len];
+                            const backing_int_body = file.zir.bodySlice(extra_index, backing_int_body_len);
                             const break_inst = backing_int_body[backing_int_body.len - 1];
-                            const operand = data[break_inst].@"break".operand;
+                            const operand = data[@intFromEnum(break_inst)].@"break".operand;
                             const backing_int_res = try self.walkRef(
                                 file,
                                 &scope,
@@ -3695,7 +3699,7 @@ fn walkInstruction(
                     extra_index = try self.analyzeAllDecls(
                         file,
                         &scope,
-                        inst_index,
+                        inst,
                         src_info,
                         &decl_indexes,
                         &priv_decl_indexes,
@@ -3739,7 +3743,7 @@ fn walkInstruction(
                         for (paths.items) |resume_info| {
                             try self.tryResolveRefPath(
                                 resume_info.file,
-                                inst_index,
+                                inst,
                                 resume_info.ref_path,
                             );
                         }
@@ -3883,7 +3887,7 @@ fn walkInstruction(
 
                     const cmpxchg_index = self.exprs.items.len;
                     try self.exprs.append(self.arena, .{ .cmpxchg = .{
-                        .name = @tagName(tags[inst_index]),
+                        .name = @tagName(tags[@intFromEnum(inst)]),
                         .type = type_index,
                         .ptr = ptr_index,
                         .expected_value = expected_value_index,
@@ -3912,14 +3916,14 @@ fn analyzeAllDecls(
     self: *Autodoc,
     file: *File,
     scope: *Scope,
-    parent_inst_index: usize,
+    parent_inst: Zir.Inst.Index,
     parent_src: SrcLocInfo,
     decl_indexes: *std.ArrayListUnmanaged(usize),
     priv_decl_indexes: *std.ArrayListUnmanaged(usize),
     call_ctx: ?*const CallContext,
 ) AutodocErrors!usize {
     const first_decl_indexes_slot = decl_indexes.items.len;
-    const original_it = file.zir.declIterator(@as(u32, @intCast(parent_inst_index)));
+    const original_it = file.zir.declIterator(parent_inst);
 
     // First loop to discover decl names
     {
@@ -4038,7 +4042,7 @@ fn analyzeDecl(
     const decl_name_index = file.zir.extra[extra_index];
 
     extra_index += 1;
-    const value_index = file.zir.extra[extra_index];
+    const value_index: Zir.Inst.Index = @enumFromInt(file.zir.extra[extra_index]);
 
     extra_index += 1;
     const doc_comment_index = file.zir.extra[extra_index];
@@ -4066,7 +4070,7 @@ fn analyzeDecl(
     _ = addrspace_inst;
 
     // This is known to work because decl values are always block_inlines
-    const value_pl_node = data[value_index].pl_node;
+    const value_pl_node = data[@intFromEnum(value_index)].pl_node;
     const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src);
 
     const name: []const u8 = switch (decl_name_index) {
@@ -4124,7 +4128,7 @@ fn analyzeDecl(
         try priv_decl_indexes.append(self.arena, decls_slot_index);
     }
 
-    const decl_status_ptr = scope.resolveDeclName(decl_name_index, file, 0);
+    const decl_status_ptr = scope.resolveDeclName(decl_name_index, file, .none);
     std.debug.assert(decl_status_ptr.* == .Pending);
     decl_status_ptr.* = .{ .Analyzed = decls_slot_index };
 
@@ -4158,11 +4162,11 @@ fn analyzeUsingnamespaceDecl(
     const data = file.zir.instructions.items(.data);
 
     const is_pub = @as(u1, @truncate(d.flags)) != 0;
-    const value_index = file.zir.extra[@intFromEnum(d.sub_index) + 6];
+    const value_index: Zir.Inst.Index = @enumFromInt(file.zir.extra[@intFromEnum(d.sub_index) + 6]);
     const doc_comment_index = file.zir.extra[@intFromEnum(d.sub_index) + 7];
 
     // This is known to work because decl values are always block_inlines
-    const value_pl_node = data[value_index].pl_node;
+    const value_pl_node = data[@intFromEnum(value_index)].pl_node;
     const decl_src = try self.srcLocInfo(file, value_pl_node.src_node, parent_src);
 
     const doc_comment: ?[]const u8 = if (doc_comment_index != 0)
@@ -4244,7 +4248,7 @@ fn analyzeDecltest(
         break :idx idx;
     };
 
-    const decl_status = scope.resolveDeclName(decl_name_index, file, 0);
+    const decl_status = scope.resolveDeclName(decl_name_index, file, .none);
 
     switch (decl_status.*) {
         .Analyzed => |idx| {
@@ -4277,7 +4281,7 @@ fn tryResolveRefPath(
     self: *Autodoc,
     /// File from which the decl path originates.
     file: *File,
-    inst_index: usize, // used only for panicWithContext
+    inst: Zir.Inst.Index, // used only for panicWithContext
     path: []DocData.Expr,
 ) AutodocErrors!void {
     var i: usize = 0;
@@ -4374,7 +4378,7 @@ fn tryResolveRefPath(
         } else {
             panicWithContext(
                 file,
-                inst_index,
+                inst,
                 "exhausted eval quota for `{}`in tryResolveRefPath\n",
                 .{resolved_parent},
             );
@@ -4386,7 +4390,7 @@ fn tryResolveRefPath(
                 //       in the switch above this one!
                 printWithContext(
                     file,
-                    inst_index,
+                    inst,
                     "TODO: handle `{s}`in tryResolveRefPath\nInfo: {}",
                     .{ @tagName(resolved_parent), resolved_parent },
                 );
@@ -4403,7 +4407,7 @@ fn tryResolveRefPath(
                 else => {
                     panicWithContext(
                         file,
-                        inst_index,
+                        inst,
                         "TODO: handle `{s}` in tryResolveDeclPath.type\nInfo: {}",
                         .{ @tagName(self.types.items[t_index]), resolved_parent },
                     );
@@ -4442,7 +4446,7 @@ fn tryResolveRefPath(
                     } else {
                         panicWithContext(
                             file,
-                            inst_index,
+                            inst,
                             "TODO: handle `{s}` in tryResolveDeclPath.type.Array\nInfo: {}",
                             .{ child_string, resolved_parent },
                         );
@@ -4500,7 +4504,7 @@ fn tryResolveRefPath(
                     // if we got here, our search failed
                     printWithContext(
                         file,
-                        inst_index,
+                        inst,
                         "failed to match `{s}` in enum",
                         .{child_string},
                     );
@@ -4557,7 +4561,7 @@ fn tryResolveRefPath(
                     // if we got here, our search failed
                     printWithContext(
                         file,
-                        inst_index,
+                        inst,
                         "failed to match `{s}` in union",
                         .{child_string},
                     );
@@ -4614,7 +4618,7 @@ fn tryResolveRefPath(
                     // if we got here, our search failed
                     // printWithContext(
                     //     file,
-                    //     inst_index,
+                    //     inst,
                     //     "failed to match `{s}` in struct",
                     //     .{child_string},
                     // );
@@ -4659,7 +4663,7 @@ fn tryResolveRefPath(
                     // if we got here, our search failed
                     printWithContext(
                         file,
-                        inst_index,
+                        inst,
                         "failed to match `{s}` in opaque",
                         .{child_string},
                     );
@@ -4679,7 +4683,7 @@ fn tryResolveRefPath(
                 // if we got here, our search failed
                 printWithContext(
                     file,
-                    inst_index,
+                    inst,
                     "failed to match `{s}` in struct",
                     .{child_string},
                 );
@@ -4696,7 +4700,7 @@ fn tryResolveRefPath(
         _ = self.pending_ref_paths.remove(&path[path.len - 1]);
 
         for (waiter_list.items) |resume_info| {
-            try self.tryResolveRefPath(resume_info.file, inst_index, resume_info.ref_path);
+            try self.tryResolveRefPath(resume_info.file, inst, resume_info.ref_path);
         }
         // TODO: this is where we should free waiter_list, but its in the arena
         //       that said, we might want to store it elsewhere and reclaim memory asap
@@ -4805,14 +4809,14 @@ fn analyzeFancyFunction(
     file: *File,
     scope: *Scope,
     parent_src: SrcLocInfo,
-    inst_index: usize,
+    inst: Zir.Inst.Index,
     self_ast_node_index: usize,
     type_slot_index: usize,
     call_ctx: ?*const CallContext,
 ) AutodocErrors!DocData.WalkResult {
     const tags = file.zir.instructions.items(.tag);
     const data = file.zir.instructions.items(.data);
-    const fn_info = file.zir.getFnInfo(@as(u32, @intCast(inst_index)));
+    const fn_info = file.zir.getFnInfo(inst);
 
     try self.ast_nodes.ensureUnusedCapacity(self.arena, fn_info.total_params_len);
     var param_type_refs = try std.ArrayListUnmanaged(DocData.Expr).initCapacity(
@@ -4826,18 +4830,18 @@ fn analyzeFancyFunction(
 
     // TODO: handle scope rules for fn parameters
     for (fn_info.param_body[0..fn_info.total_params_len]) |param_index| {
-        switch (tags[param_index]) {
+        switch (tags[@intFromEnum(param_index)]) {
             else => {
                 panicWithContext(
                     file,
                     param_index,
                     "TODO: handle `{s}` in walkInstruction.func\n",
-                    .{@tagName(tags[param_index])},
+                    .{@tagName(tags[@intFromEnum(param_index)])},
                 );
             },
             .param_anytype, .param_anytype_comptime => {
                 // TODO: where are the doc comments?
-                const str_tok = data[param_index].str_tok;
+                const str_tok = data[@intFromEnum(param_index)].str_tok;
 
                 const name = str_tok.get(file.zir);
 
@@ -4845,7 +4849,7 @@ fn analyzeFancyFunction(
                 self.ast_nodes.appendAssumeCapacity(.{
                     .name = name,
                     .docs = "",
-                    .@"comptime" = tags[param_index] == .param_anytype_comptime,
+                    .@"comptime" = tags[@intFromEnum(param_index)] == .param_anytype_comptime,
                 });
 
                 param_type_refs.appendAssumeCapacity(
@@ -4853,7 +4857,7 @@ fn analyzeFancyFunction(
                 );
             },
             .param, .param_comptime => {
-                const pl_tok = data[param_index].pl_tok;
+                const pl_tok = data[@intFromEnum(param_index)].pl_tok;
                 const extra = file.zir.extraData(Zir.Inst.Param, pl_tok.payload_index);
                 const doc_comment = if (extra.data.doc_comment != 0)
                     file.zir.nullTerminatedString(extra.data.doc_comment)
@@ -4865,7 +4869,7 @@ fn analyzeFancyFunction(
                 try self.ast_nodes.append(self.arena, .{
                     .name = name,
                     .docs = doc_comment,
-                    .@"comptime" = tags[param_index] == .param_comptime,
+                    .@"comptime" = tags[@intFromEnum(param_index)] == .param_comptime,
                 });
 
                 const break_index = file.zir.extra[extra.end..][extra.data.body_len - 1];
@@ -4886,7 +4890,7 @@ fn analyzeFancyFunction(
 
     self.ast_nodes.items[self_ast_node_index].fields = param_ast_indexes.items;
 
-    const pl_node = data[inst_index].pl_node;
+    const pl_node = data[@intFromEnum(inst)].pl_node;
     const extra = file.zir.extraData(Zir.Inst.FuncFancy, pl_node.payload_index);
 
     var extra_index: usize = extra.end;
@@ -4971,7 +4975,7 @@ fn analyzeFancyFunction(
 
     var cc_index: ?usize = null;
     if (extra.data.bits.has_cc_ref and !extra.data.bits.has_cc_body) {
-        const cc_ref = @as(Zir.Inst.Ref, @enumFromInt(file.zir.extra[extra_index]));
+        const cc_ref: Zir.Inst.Ref = @enumFromInt(file.zir.extra[extra_index]);
         const cc_expr = try self.walkRef(
             file,
             scope,
@@ -4988,11 +4992,11 @@ fn analyzeFancyFunction(
     } else if (extra.data.bits.has_cc_body) {
         const cc_body_len = file.zir.extra[extra_index];
         extra_index += 1;
-        const cc_body = file.zir.extra[extra_index..][0..cc_body_len];
+        const cc_body = file.zir.bodySlice(extra_index, cc_body_len);
 
         // We assume the body ends with a break_inline
         const break_index = cc_body[cc_body.len - 1];
-        const break_operand = data[break_index].@"break".operand;
+        const break_operand = data[@intFromEnum(break_index)].@"break".operand;
         const cc_expr = try self.walkRef(
             file,
             scope,
@@ -5029,7 +5033,7 @@ fn analyzeFancyFunction(
         },
         else => blk: {
             const last_instr_index = fn_info.ret_ty_body[fn_info.ret_ty_body.len - 1];
-            const break_operand = data[last_instr_index].@"break".operand;
+            const break_operand = data[@intFromEnum(last_instr_index)].@"break".operand;
             const wr = try self.walkRef(
                 file,
                 scope,
@@ -5096,7 +5100,7 @@ fn analyzeFunction(
     file: *File,
     scope: *Scope,
     parent_src: SrcLocInfo,
-    inst_index: usize,
+    inst: Zir.Inst.Index,
     self_ast_node_index: usize,
     type_slot_index: usize,
     ret_is_inferred_error_set: bool,
@@ -5104,7 +5108,7 @@ fn analyzeFunction(
 ) AutodocErrors!DocData.WalkResult {
     const tags = file.zir.instructions.items(.tag);
     const data = file.zir.instructions.items(.data);
-    const fn_info = file.zir.getFnInfo(@as(u32, @intCast(inst_index)));
+    const fn_info = file.zir.getFnInfo(inst);
 
     try self.ast_nodes.ensureUnusedCapacity(self.arena, fn_info.total_params_len);
     var param_type_refs = try std.ArrayListUnmanaged(DocData.Expr).initCapacity(
@@ -5118,18 +5122,18 @@ fn analyzeFunction(
 
     // TODO: handle scope rules for fn parameters
     for (fn_info.param_body[0..fn_info.total_params_len]) |param_index| {
-        switch (tags[param_index]) {
+        switch (tags[@intFromEnum(param_index)]) {
             else => {
                 panicWithContext(
                     file,
                     param_index,
                     "TODO: handle `{s}` in walkInstruction.func\n",
-                    .{@tagName(tags[param_index])},
+                    .{@tagName(tags[@intFromEnum(param_index)])},
                 );
             },
             .param_anytype, .param_anytype_comptime => {
                 // TODO: where are the doc comments?
-                const str_tok = data[param_index].str_tok;
+                const str_tok = data[@intFromEnum(param_index)].str_tok;
 
                 const name = str_tok.get(file.zir);
 
@@ -5137,7 +5141,7 @@ fn analyzeFunction(
                 self.ast_nodes.appendAssumeCapacity(.{
                     .name = name,
                     .docs = "",
-                    .@"comptime" = tags[param_index] == .param_anytype_comptime,
+                    .@"comptime" = tags[@intFromEnum(param_index)] == .param_anytype_comptime,
                 });
 
                 param_type_refs.appendAssumeCapacity(
@@ -5145,7 +5149,7 @@ fn analyzeFunction(
                 );
             },
             .param, .param_comptime => {
-                const pl_tok = data[param_index].pl_tok;
+                const pl_tok = data[@intFromEnum(param_index)].pl_tok;
                 const extra = file.zir.extraData(Zir.Inst.Param, pl_tok.payload_index);
                 const doc_comment = if (extra.data.doc_comment != 0)
                     file.zir.nullTerminatedString(extra.data.doc_comment)
@@ -5157,7 +5161,7 @@ fn analyzeFunction(
                 try self.ast_nodes.append(self.arena, .{
                     .name = name,
                     .docs = doc_comment,
-                    .@"comptime" = tags[param_index] == .param_comptime,
+                    .@"comptime" = tags[@intFromEnum(param_index)] == .param_comptime,
                 });
 
                 const break_index = file.zir.extra[extra.end..][extra.data.body_len - 1];
@@ -5195,7 +5199,7 @@ fn analyzeFunction(
         },
         else => blk: {
             const last_instr_index = fn_info.ret_ty_body[fn_info.ret_ty_body.len - 1];
-            const break_operand = data[last_instr_index].@"break".operand;
+            const break_operand = data[@intFromEnum(last_instr_index)].@"break".operand;
             const wr = try self.walkRef(
                 file,
                 scope,
@@ -5266,7 +5270,7 @@ fn getGenericReturnType(
     file: *File,
     scope: *Scope,
     parent_src: SrcLocInfo, // function decl line
-    body_main_block: usize,
+    body_main_block: Zir.Inst.Index,
     call_ctx: ?*const CallContext,
 ) !DocData.Expr {
     const tags = file.zir.instructions.items(.tag);
@@ -5274,25 +5278,27 @@ fn getGenericReturnType(
 
     // We expect `body_main_block` to be the first instruction
     // inside the function body, and for it to be a block instruction.
-    const pl_node = data[body_main_block].pl_node;
+    const pl_node = data[@intFromEnum(body_main_block)].pl_node;
     const extra = file.zir.extraData(Zir.Inst.Block, pl_node.payload_index);
-    const maybe_ret_node = file.zir.extra[extra.end..][extra.data.body_len - 4];
-    switch (tags[maybe_ret_node]) {
-        .ret_node, .ret_load => {
-            const wr = try self.walkInstruction(
-                file,
-                scope,
-                parent_src,
-                maybe_ret_node,
-                false,
-                call_ctx,
-            );
-            return wr.expr;
-        },
-        else => {
-            return DocData.Expr{ .comptimeExpr = 0 };
-        },
+    const body = file.zir.bodySlice(extra.end, extra.data.body_len);
+    if (body.len >= 4) {
+        const maybe_ret_inst = body[body.len - 4];
+        switch (tags[@intFromEnum(maybe_ret_inst)]) {
+            .ret_node, .ret_load => {
+                const wr = try self.walkInstruction(
+                    file,
+                    scope,
+                    parent_src,
+                    maybe_ret_inst,
+                    false,
+                    call_ctx,
+                );
+                return wr.expr;
+            },
+            else => {},
+        }
     }
+    return DocData.Expr{ .comptimeExpr = 0 };
 }
 
 fn collectUnionFieldInfo(
@@ -5470,11 +5476,11 @@ fn collectStructFieldInfo(
             }
 
             std.debug.assert(field.type_body_len != 0);
-            const body = file.zir.extra[extra_index..][0..field.type_body_len];
+            const body = file.zir.bodySlice(extra_index, field.type_body_len);
             extra_index += body.len;
 
             const break_inst = body[body.len - 1];
-            const operand = data[break_inst].@"break".operand;
+            const operand = data[@intFromEnum(break_inst)].@"break".operand;
             try self.ast_nodes.append(self.arena, .{
                 .file = self.files.getIndex(file).?,
                 .line = parent_src.line,
@@ -5499,11 +5505,11 @@ fn collectStructFieldInfo(
                 break :def null;
             }
 
-            const body = file.zir.extra[extra_index..][0..field.init_body_len];
+            const body = file.zir.bodySlice(extra_index, field.init_body_len);
             extra_index += body.len;
 
             const break_inst = body[body.len - 1];
-            const operand = data[break_inst].@"break".operand;
+            const operand = data[@intFromEnum(break_inst)].@"break".operand;
             const walk_result = try self.walkRef(
                 file,
                 scope,
@@ -5559,7 +5565,7 @@ fn walkRef(
             .typeRef = .{ .type = @intFromEnum(std.builtin.TypeId.Type) },
             .expr = .{ .type = @intFromEnum(ref) },
         };
-    } else if (Zir.refToIndex(ref)) |zir_index| {
+    } else if (ref.toIndex()) |zir_index| {
         return self.walkInstruction(
             file,
             parent_scope,
@@ -5571,9 +5577,9 @@ fn walkRef(
     } else {
         switch (ref) {
             else => {
-                panicWithContext(
+                panicWithOptionalContext(
                     file,
-                    0,
+                    .none,
                     "TODO: handle {s} in walkRef",
                     .{@tagName(ref)},
                 );
@@ -5664,10 +5670,10 @@ fn walkRef(
     }
 }
 
-fn getBlockInlineBreak(zir: Zir, inst_index: usize) ?Zir.Inst.Ref {
+fn getBlockInlineBreak(zir: Zir, inst: Zir.Inst.Index) ?Zir.Inst.Ref {
     const tags = zir.instructions.items(.tag);
     const data = zir.instructions.items(.data);
-    const pl_node = data[inst_index].pl_node;
+    const pl_node = data[@intFromEnum(inst)].pl_node;
     const extra = zir.extraData(Zir.Inst.Block, pl_node.payload_index);
     const break_index = zir.extra[extra.end..][extra.data.body_len - 1];
     if (tags[break_index] == .condbr_inline) return null;
@@ -5675,12 +5681,36 @@ fn getBlockInlineBreak(zir: Zir, inst_index: usize) ?Zir.Inst.Ref {
     return data[break_index].@"break".operand;
 }
 
-fn printWithContext(file: *File, inst: usize, comptime fmt: []const u8, args: anytype) void {
+fn printWithContext(
+    file: *File,
+    inst: Zir.Inst.Index,
+    comptime fmt: []const u8,
+    args: anytype,
+) void {
+    return printWithOptionalContext(file, inst.toOptional(), fmt, args);
+}
+
+fn printWithOptionalContext(file: *File, inst: Zir.Inst.OptionalIndex, comptime fmt: []const u8, args: anytype) void {
     log.debug("Context [{s}] % {} \n " ++ fmt, .{ file.sub_file_path, inst } ++ args);
 }
 
-fn panicWithContext(file: *File, inst: usize, comptime fmt: []const u8, args: anytype) noreturn {
-    printWithContext(file, inst, fmt, args);
+fn panicWithContext(
+    file: *File,
+    inst: Zir.Inst.Index,
+    comptime fmt: []const u8,
+    args: anytype,
+) noreturn {
+    printWithOptionalContext(file, inst.toOptional(), fmt, args);
+    unreachable;
+}
+
+fn panicWithOptionalContext(
+    file: *File,
+    inst: Zir.Inst.OptionalIndex,
+    comptime fmt: []const u8,
+    args: anytype,
+) noreturn {
+    printWithOptionalContext(file, inst, fmt, args);
     unreachable;
 }
 
src/InternPool.zig
@@ -580,7 +580,7 @@ pub const Key = union(enum) {
         pub fn setZirIndex(s: @This(), ip: *InternPool, new_zir_index: Zir.Inst.Index) void {
             assert(s.layout != .Packed);
             const field_index = std.meta.fieldIndex(Tag.TypeStruct, "zir_index").?;
-            ip.extra.items[s.extra_index + field_index] = new_zir_index;
+            ip.extra.items[s.extra_index + field_index] = @intFromEnum(new_zir_index);
         }
 
         pub fn haveFieldTypes(s: @This(), ip: *const InternPool) bool {
@@ -2481,7 +2481,14 @@ pub const static_keys = [_]Key{
 };
 
 /// How many items in the InternPool are statically known.
-pub const static_len: u32 = static_keys.len;
+/// This is specified with an integer literal and a corresponding comptime
+/// assert below to break an unfortunate and arguably incorrect dependency loop
+/// when compiling.
+pub const static_len = 84;
+comptime {
+    //@compileLog(static_keys.len);
+    assert(static_len == static_keys.len);
+}
 
 pub const Tag = enum(u8) {
     /// An integer type.
@@ -3658,7 +3665,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
             .extra_index = 0,
             .namespace = .none,
             .decl = .none,
-            .zir_index = @as(u32, undefined),
+            .zir_index = undefined,
             .layout = .Auto,
             .field_names = .{ .start = 0, .len = 0 },
             .field_types = .{ .start = 0, .len = 0 },
@@ -3674,7 +3681,7 @@ pub fn indexToKey(ip: *const InternPool, index: Index) Key {
             .extra_index = 0,
             .namespace = @as(Module.Namespace.Index, @enumFromInt(data)).toOptional(),
             .decl = .none,
-            .zir_index = @as(u32, undefined),
+            .zir_index = undefined,
             .layout = .Auto,
             .field_names = .{ .start = 0, .len = 0 },
             .field_types = .{ .start = 0, .len = 0 },
@@ -6403,6 +6410,7 @@ fn addExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 {
             NullTerminatedString,
             OptionalNullTerminatedString,
             Tag.TypePointer.VectorIndex,
+            Zir.Inst.Index,
             => @intFromEnum(@field(extra, field.name)),
 
             u32,
@@ -6477,6 +6485,7 @@ fn extraDataTrail(ip: *const InternPool, comptime T: type, index: usize) struct
             NullTerminatedString,
             OptionalNullTerminatedString,
             Tag.TypePointer.VectorIndex,
+            Zir.Inst.Index,
             => @enumFromInt(int32),
 
             u32,
@@ -8191,7 +8200,7 @@ pub fn funcZirBodyInst(ip: *const InternPool, i: Index) Zir.Inst.Index {
         },
         else => unreachable,
     };
-    return ip.extra.items[extra_index];
+    return @enumFromInt(ip.extra.items[extra_index]);
 }
 
 pub fn iesFuncIndex(ip: *const InternPool, ies_index: Index) Index {
src/Module.zig
@@ -389,7 +389,7 @@ pub const Decl = struct {
     /// Index to ZIR `extra` array to the entry in the parent's decl structure
     /// (the part that says "for every decls_len"). The first item at this index is
     /// the contents hash, followed by line, name, etc.
-    /// For anonymous decls and also the root Decl for a File, this is 0.
+    /// For anonymous decls and also the root Decl for a File, this is `none`.
     zir_decl_index: Zir.OptionalExtraIndex,
 
     /// Represents the "shallow" analysis status. For example, for decls that are functions,
@@ -547,7 +547,7 @@ pub const Decl = struct {
     pub fn zirBlockIndex(decl: *const Decl, mod: *Module) Zir.Inst.Index {
         assert(decl.zir_decl_index != .none);
         const zir = decl.getFileScope(mod).zir;
-        return zir.extra[@intFromEnum(decl.zir_decl_index) + 6];
+        return @enumFromInt(zir.extra[@intFromEnum(decl.zir_decl_index) + 6]);
     }
 
     pub fn zirAlignRef(decl: Decl, mod: *Module) Zir.Inst.Ref {
@@ -1205,9 +1205,8 @@ pub const File = struct {
         if (imports_index == 0) return;
         const extra = file.zir.extraData(Zir.Inst.Imports, imports_index);
 
-        var import_i: u32 = 0;
         var extra_index = extra.end;
-        while (import_i < extra.data.imports_len) : (import_i += 1) {
+        for (0..extra.data.imports_len) |_| {
             const item = file.zir.extraData(Zir.Inst.Imports.Item, extra_index);
             extra_index = item.end;
 
@@ -3206,8 +3205,8 @@ pub fn mapOldZirToNew(
 
     // Main struct inst is always the same
     try match_stack.append(gpa, .{
-        .old_inst = Zir.main_struct_inst,
-        .new_inst = Zir.main_struct_inst,
+        .old_inst = .main_struct_inst,
+        .new_inst = .main_struct_inst,
     });
 
     var old_decls = std.ArrayList(Zir.Inst.Index).init(gpa);
@@ -3622,11 +3621,10 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
     };
     defer sema.deinit();
 
-    const main_struct_inst = Zir.main_struct_inst;
     const struct_ty = sema.getStructType(
         new_decl_index,
         new_namespace_index,
-        main_struct_inst,
+        .main_struct_inst,
     ) catch |err| switch (err) {
         error.OutOfMemory => return error.OutOfMemory,
     };
@@ -3754,11 +3752,12 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
     defer block_scope.instructions.deinit(gpa);
 
     const zir_block_index = decl.zirBlockIndex(mod);
-    const inst_data = zir_datas[zir_block_index].pl_node;
+    const inst_data = zir_datas[@intFromEnum(zir_block_index)].pl_node;
     const extra = zir.extraData(Zir.Inst.Block, inst_data.payload_index);
     const body = zir.extra[extra.end..][0..extra.data.body_len];
-    const result_ref = (try sema.analyzeBodyBreak(&block_scope, body)).?.operand;
-    // We'll do some other bits with the Sema. Clear the type target index just in case they analyze any type.
+    const result_ref = (try sema.analyzeBodyBreak(&block_scope, @ptrCast(body))).?.operand;
+    // We'll do some other bits with the Sema. Clear the type target index just
+    // in case they analyze any type.
     sema.builtin_type_target_index = .none;
     for (comptime_mutable_decls.items) |ct_decl_index| {
         const ct_decl = mod.declPtr(ct_decl_index);
@@ -6471,13 +6470,13 @@ pub fn getParamName(mod: *Module, func_index: InternPool.Index, index: u32) [:0]
     const param_body = file.zir.getParamBody(func.zir_body_inst);
     const param = param_body[index];
 
-    return switch (tags[param]) {
+    return switch (tags[@intFromEnum(param)]) {
         .param, .param_comptime => blk: {
-            const extra = file.zir.extraData(Zir.Inst.Param, data[param].pl_tok.payload_index);
+            const extra = file.zir.extraData(Zir.Inst.Param, data[@intFromEnum(param)].pl_tok.payload_index);
             break :blk file.zir.nullTerminatedString(extra.data.name);
         },
         .param_anytype, .param_anytype_comptime => blk: {
-            const param_data = data[param].str_tok;
+            const param_data = data[@intFromEnum(param)].str_tok;
             break :blk param_data.get(file.zir);
         },
         else => unreachable,
src/print_zir.zig
@@ -32,7 +32,7 @@ pub fn renderAsTextToFile(
     var raw_stream = std.io.bufferedWriter(fs_file.writer());
     const stream = raw_stream.writer();
 
-    const main_struct_inst = Zir.main_struct_inst;
+    const main_struct_inst: Zir.Inst.Index = .main_struct_inst;
     try stream.print("%{d} ", .{main_struct_inst});
     try writer.writeInstToStream(stream, main_struct_inst);
     try stream.writeAll("\n");
@@ -41,10 +41,9 @@ pub fn renderAsTextToFile(
         try stream.writeAll("Imports:\n");
 
         const extra = scope_file.zir.extraData(Zir.Inst.Imports, imports_index);
-        var import_i: u32 = 0;
         var extra_index = extra.end;
 
-        while (import_i < extra.data.imports_len) : (import_i += 1) {
+        for (0..extra.data.imports_len) |_| {
             const item = scope_file.zir.extraData(Zir.Inst.Imports.Item, extra_index);
             extra_index = item.end;
 
@@ -197,8 +196,8 @@ const Writer = struct {
         inst: Zir.Inst.Index,
     ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
         const tags = self.code.instructions.items(.tag);
-        const tag = tags[inst];
-        try stream.print("= {s}(", .{@tagName(tags[inst])});
+        const tag = tags[@intFromEnum(inst)];
+        try stream.print("= {s}(", .{@tagName(tags[@intFromEnum(inst)])});
         switch (tag) {
             .as,
             .store,
@@ -525,7 +524,7 @@ const Writer = struct {
     }
 
     fn writeExtended(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const extended = self.code.instructions.items(.data)[inst].extended;
+        const extended = self.code.instructions.items(.data)[@intFromEnum(inst)].extended;
         try stream.print("{s}(", .{@tagName(extended.opcode)});
         switch (extended.opcode) {
             .this,
@@ -622,7 +621,7 @@ const Writer = struct {
     }
 
     fn writeBin(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].bin;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].bin;
         try self.writeInstRef(stream, inst_data.lhs);
         try stream.writeAll(", ");
         try self.writeInstRef(stream, inst_data.rhs);
@@ -630,7 +629,7 @@ const Writer = struct {
     }
 
     fn writeArrayInitElemType(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].bin;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].bin;
         try self.writeInstRef(stream, inst_data.lhs);
         try stream.print(", {d})", .{@intFromEnum(inst_data.rhs)});
     }
@@ -640,7 +639,7 @@ const Writer = struct {
         stream: anytype,
         inst: Zir.Inst.Index,
     ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
-        const inst_data = self.code.instructions.items(.data)[inst].un_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
         try self.writeInstRef(stream, inst_data.operand);
         try stream.writeAll(") ");
         try self.writeSrc(stream, inst_data.src());
@@ -651,7 +650,7 @@ const Writer = struct {
         stream: anytype,
         inst: Zir.Inst.Index,
     ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
-        const inst_data = self.code.instructions.items(.data)[inst].un_tok;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].un_tok;
         try self.writeInstRef(stream, inst_data.operand);
         try stream.writeAll(") ");
         try self.writeSrc(stream, inst_data.src());
@@ -662,7 +661,7 @@ const Writer = struct {
         stream: anytype,
         inst: Zir.Inst.Index,
     ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.ValidateDestructure, inst_data.payload_index).data;
         try self.writeInstRef(stream, extra.operand);
         try stream.print(", {d}) (destructure=", .{extra.expect_len});
@@ -676,7 +675,7 @@ const Writer = struct {
         stream: anytype,
         inst: Zir.Inst.Index,
     ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.ArrayInit, inst_data.payload_index).data;
         try self.writeInstRef(stream, extra.ty);
         try stream.print(", {d}) ", .{extra.init_count});
@@ -688,7 +687,7 @@ const Writer = struct {
         stream: anytype,
         inst: Zir.Inst.Index,
     ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data;
         try self.writeInstRef(stream, extra.len);
         try stream.writeAll(", ");
@@ -704,7 +703,7 @@ const Writer = struct {
         stream: anytype,
         inst: Zir.Inst.Index,
     ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
-        const inst_data = self.code.instructions.items(.data)[inst].ptr_type;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].ptr_type;
         const str_allowzero = if (inst_data.flags.is_allowzero) "allowzero, " else "";
         const str_const = if (!inst_data.flags.is_mutable) "const, " else "";
         const str_volatile = if (inst_data.flags.is_volatile) "volatile, " else "";
@@ -745,12 +744,12 @@ const Writer = struct {
     }
 
     fn writeInt(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].int;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].int;
         try stream.print("{d})", .{inst_data});
     }
 
     fn writeIntBig(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].str;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].str;
         const byte_count = inst_data.len * @sizeOf(std.math.big.Limb);
         const limb_bytes = self.code.string_bytes[inst_data.start..][0..byte_count];
         // limb_bytes is not aligned properly; we must allocate and copy the bytes
@@ -769,12 +768,12 @@ const Writer = struct {
     }
 
     fn writeFloat(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const number = self.code.instructions.items(.data)[inst].float;
+        const number = self.code.instructions.items(.data)[@intFromEnum(inst)].float;
         try stream.print("{d})", .{number});
     }
 
     fn writeFloat128(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.Float128, inst_data.payload_index).data;
         const src = inst_data.src();
         const number = extra.get();
@@ -788,13 +787,13 @@ const Writer = struct {
         stream: anytype,
         inst: Zir.Inst.Index,
     ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
-        const inst_data = self.code.instructions.items(.data)[inst].str;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].str;
         const str = inst_data.get(self.code);
         try stream.print("\"{}\")", .{std.zig.fmtEscapes(str)});
     }
 
     fn writeSliceStart(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data;
         try self.writeInstRef(stream, extra.lhs);
         try stream.writeAll(", ");
@@ -804,7 +803,7 @@ const Writer = struct {
     }
 
     fn writeSliceEnd(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.SliceEnd, inst_data.payload_index).data;
         try self.writeInstRef(stream, extra.lhs);
         try stream.writeAll(", ");
@@ -816,7 +815,7 @@ const Writer = struct {
     }
 
     fn writeSliceSentinel(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data;
         try self.writeInstRef(stream, extra.lhs);
         try stream.writeAll(", ");
@@ -830,7 +829,7 @@ const Writer = struct {
     }
 
     fn writeSliceLength(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data;
         try self.writeInstRef(stream, extra.lhs);
         try stream.writeAll(", ");
@@ -846,7 +845,7 @@ const Writer = struct {
     }
 
     fn writeUnionInit(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data;
         try self.writeInstRef(stream, extra.union_type);
         try stream.writeAll(", ");
@@ -858,7 +857,7 @@ const Writer = struct {
     }
 
     fn writeShuffle(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data;
         try self.writeInstRef(stream, extra.elem_type);
         try stream.writeAll(", ");
@@ -885,7 +884,7 @@ const Writer = struct {
     }
 
     fn writeMulAdd(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.MulAdd, inst_data.payload_index).data;
         try self.writeInstRef(stream, extra.mulend1);
         try stream.writeAll(", ");
@@ -897,7 +896,7 @@ const Writer = struct {
     }
 
     fn writeBuiltinCall(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data;
 
         try self.writeFlag(stream, "nodiscard ", extra.flags.ensure_result_used);
@@ -913,7 +912,7 @@ const Writer = struct {
     }
 
     fn writeFieldParentPtr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.FieldParentPtr, inst_data.payload_index).data;
         try self.writeInstRef(stream, extra.parent_type);
         try stream.writeAll(", ");
@@ -938,9 +937,9 @@ const Writer = struct {
     }
 
     fn writeParam(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_tok;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok;
         const extra = self.code.extraData(Zir.Inst.Param, inst_data.payload_index);
-        const body = self.code.extra[extra.end..][0..extra.data.body_len];
+        const body = self.code.bodySlice(extra.end, extra.data.body_len);
         try stream.print("\"{}\", ", .{
             std.zig.fmtEscapes(self.code.nullTerminatedString(extra.data.name)),
         });
@@ -956,7 +955,7 @@ const Writer = struct {
     }
 
     fn writePlNodeBin(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
         try self.writeInstRef(stream, extra.lhs);
         try stream.writeAll(", ");
@@ -966,7 +965,7 @@ const Writer = struct {
     }
 
     fn writePlNodeMultiOp(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
         const args = self.code.refSlice(extra.end, extra.data.operands_len);
         try stream.writeAll("{");
@@ -979,13 +978,13 @@ const Writer = struct {
     }
 
     fn writeElemValImm(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].elem_val_imm;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].elem_val_imm;
         try self.writeInstRef(stream, inst_data.operand);
         try stream.print(", {d})", .{inst_data.idx});
     }
 
     fn writeArrayInitElemPtr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data;
 
         try self.writeInstRef(stream, extra.ptr);
@@ -994,7 +993,7 @@ const Writer = struct {
     }
 
     fn writePlNodeExport(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.Export, inst_data.payload_index).data;
         const decl_name = self.code.nullTerminatedString(extra.decl_name);
 
@@ -1006,7 +1005,7 @@ const Writer = struct {
     }
 
     fn writePlNodeExportValue(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.ExportValue, inst_data.payload_index).data;
 
         try self.writeInstRef(stream, extra.operand);
@@ -1017,7 +1016,7 @@ const Writer = struct {
     }
 
     fn writeValidateArrayInitRefTy(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.ArrayInitRefTy, inst_data.payload_index).data;
 
         try self.writeInstRef(stream, extra.ptr_ty);
@@ -1027,7 +1026,7 @@ const Writer = struct {
     }
 
     fn writeStructInit(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.StructInit, inst_data.payload_index);
         var field_i: u32 = 0;
         var extra_index = extra.end;
@@ -1095,7 +1094,7 @@ const Writer = struct {
     }
 
     fn writeAtomicLoad(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data;
 
         try self.writeInstRef(stream, extra.elem_type);
@@ -1108,7 +1107,7 @@ const Writer = struct {
     }
 
     fn writeAtomicStore(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data;
 
         try self.writeInstRef(stream, extra.ptr);
@@ -1121,7 +1120,7 @@ const Writer = struct {
     }
 
     fn writeAtomicRmw(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data;
 
         try self.writeInstRef(stream, extra.ptr);
@@ -1136,7 +1135,7 @@ const Writer = struct {
     }
 
     fn writeStructInitAnon(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index);
         var field_i: u32 = 0;
         var extra_index = extra.end;
@@ -1157,7 +1156,7 @@ const Writer = struct {
     }
 
     fn writeStructInitFieldType(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data;
         try self.writeInstRef(stream, extra.container_type);
         const field_name = self.code.nullTerminatedString(extra.name_start);
@@ -1166,7 +1165,7 @@ const Writer = struct {
     }
 
     fn writeFieldTypeRef(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.FieldTypeRef, inst_data.payload_index).data;
         try self.writeInstRef(stream, extra.container_type);
         try stream.writeAll(", ");
@@ -1193,7 +1192,7 @@ const Writer = struct {
         stream: anytype,
         inst: Zir.Inst.Index,
     ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
-        const inst_data = self.code.instructions.items(.data)[inst].inst_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].inst_node;
         try self.writeInstIndex(stream, inst_data.inst);
         try stream.writeAll(") ");
         try self.writeSrc(stream, inst_data.src());
@@ -1297,7 +1296,7 @@ const Writer = struct {
         inst: Zir.Inst.Index,
         comptime kind: enum { direct, field },
     ) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const ExtraType = switch (kind) {
             .direct => Zir.Inst.Call,
             .field => Zir.Inst.FieldCall,
@@ -1331,7 +1330,7 @@ const Writer = struct {
             const arg_end = self.code.extra[extra.end + i];
             defer arg_start = arg_end;
             const arg_body = body[arg_start..arg_end];
-            try self.writeBracedBody(stream, arg_body);
+            try self.writeBracedBody(stream, @ptrCast(arg_body));
 
             try stream.writeAll(",\n");
         }
@@ -1345,24 +1344,24 @@ const Writer = struct {
     }
 
     fn writeBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         try self.writePlNodeBlockWithoutSrc(stream, inst);
         try self.writeSrc(stream, inst_data.src());
     }
 
     fn writePlNodeBlockWithoutSrc(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.Block, inst_data.payload_index);
-        const body = self.code.extra[extra.end..][0..extra.data.body_len];
+        const body = self.code.bodySlice(extra.end, extra.data.body_len);
         try self.writeBracedBody(stream, body);
         try stream.writeAll(") ");
     }
 
     fn writeCondBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
-        const then_body = self.code.extra[extra.end..][0..extra.data.then_body_len];
-        const else_body = self.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
+        const then_body = self.code.bodySlice(extra.end, extra.data.then_body_len);
+        const else_body = self.code.bodySlice(extra.end + then_body.len, extra.data.else_body_len);
         try self.writeInstRef(stream, extra.data.condition);
         try stream.writeAll(", ");
         try self.writeBracedBody(stream, then_body);
@@ -1373,9 +1372,9 @@ const Writer = struct {
     }
 
     fn writeTry(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.Try, inst_data.payload_index);
-        const body = self.code.extra[extra.end..][0..extra.data.body_len];
+        const body = self.code.bodySlice(extra.end, extra.data.body_len);
         try self.writeInstRef(stream, extra.data.operand);
         try stream.writeAll(", ");
         try self.writeBracedBody(stream, body);
@@ -1421,7 +1420,7 @@ const Writer = struct {
                 extra_index += 1;
                 try self.writeInstRef(stream, backing_int_ref);
             } else {
-                const body = self.code.extra[extra_index..][0..backing_int_body_len];
+                const body = self.code.bodySlice(extra_index, backing_int_body_len);
                 extra_index += backing_int_body_len;
                 self.indent += 2;
                 try self.writeBracedDecl(stream, body);
@@ -1535,7 +1534,7 @@ const Writer = struct {
                 }
 
                 if (field.type_len > 0) {
-                    const body = self.code.extra[extra_index..][0..field.type_len];
+                    const body = self.code.bodySlice(extra_index, field.type_len);
                     extra_index += body.len;
                     self.indent += 2;
                     try self.writeBracedDecl(stream, body);
@@ -1543,7 +1542,7 @@ const Writer = struct {
                 }
 
                 if (field.align_len > 0) {
-                    const body = self.code.extra[extra_index..][0..field.align_len];
+                    const body = self.code.bodySlice(extra_index, field.align_len);
                     extra_index += body.len;
                     self.indent += 2;
                     try stream.writeAll(" align(");
@@ -1553,7 +1552,7 @@ const Writer = struct {
                 }
 
                 if (field.init_len > 0) {
-                    const body = self.code.extra[extra_index..][0..field.init_len];
+                    const body = self.code.bodySlice(extra_index, field.init_len);
                     extra_index += body.len;
                     self.indent += 2;
                     try stream.writeAll(" = ");
@@ -1639,7 +1638,7 @@ const Writer = struct {
         }
         try stream.writeAll(", ");
 
-        const body = self.code.extra[extra_index..][0..body_len];
+        const body = self.code.bodySlice(extra_index, body_len);
         extra_index += body.len;
 
         const prev_parent_decl_node = self.parent_decl_node;
@@ -1742,7 +1741,7 @@ const Writer = struct {
             extra_index += 1;
             const decl_name_index = self.code.extra[extra_index];
             extra_index += 1;
-            const decl_index = self.code.extra[extra_index];
+            const decl_index: Zir.Inst.Index = @enumFromInt(self.code.extra[extra_index]);
             extra_index += 1;
             const doc_comment_index = self.code.extra[extra_index];
             extra_index += 1;
@@ -1764,7 +1763,7 @@ const Writer = struct {
             };
 
             const pub_str = if (is_pub) "pub " else "";
-            const hash_bytes = @as([16]u8, @bitCast(hash_u32s.*));
+            const hash_bytes: [16]u8 = @bitCast(hash_u32s.*);
             if (decl_name_index == 0) {
                 try stream.writeByteNTimes(' ', self.indent);
                 const name = if (is_exported) "usingnamespace" else "comptime";
@@ -1810,12 +1809,12 @@ const Writer = struct {
             }
 
             if (self.recurse_decls) {
-                const tag = self.code.instructions.items(.tag)[decl_index];
+                const tag = self.code.instructions.items(.tag)[@intFromEnum(decl_index)];
                 try stream.print(" line({d}) hash({}): %{d} = {s}(", .{
                     line, std.fmt.fmtSliceHexLower(&hash_bytes), decl_index, @tagName(tag),
                 });
 
-                const decl_block_inst_data = self.code.instructions.items(.data)[decl_index].pl_node;
+                const decl_block_inst_data = self.code.instructions.items(.data)[@intFromEnum(decl_index)].pl_node;
                 const sub_decl_node_off = decl_block_inst_data.src_node;
                 self.parent_decl_node = self.relativeToNodeIndex(sub_decl_node_off);
                 try self.writePlNodeBlockWithoutSrc(stream, decl_index);
@@ -1888,7 +1887,7 @@ const Writer = struct {
             try stream.writeAll(", ");
         }
 
-        const body = self.code.extra[extra_index..][0..body_len];
+        const body = self.code.bodySlice(extra_index, body_len);
         extra_index += body.len;
 
         const prev_parent_decl_node = self.parent_decl_node;
@@ -1988,7 +1987,7 @@ const Writer = struct {
         inst: Zir.Inst.Index,
         name_strategy: Zir.Inst.NameStrategy,
     ) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.ErrorSetDecl, inst_data.payload_index);
 
         try stream.print("{s}, ", .{@tagName(name_strategy)});
@@ -2015,7 +2014,7 @@ const Writer = struct {
     }
 
     fn writeSwitchBlock(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index);
 
         var extra_index: usize = extra.end;
@@ -2029,7 +2028,7 @@ const Writer = struct {
         const tag_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_has_tag_capture) blk: {
             const tag_capture_inst = self.code.extra[extra_index];
             extra_index += 1;
-            break :blk tag_capture_inst;
+            break :blk @enumFromInt(tag_capture_inst);
         } else undefined;
 
         try self.writeInstRef(stream, extra.data.operand);
@@ -2057,7 +2056,7 @@ const Writer = struct {
             };
             const inline_text = if (info.is_inline) "inline " else "";
             extra_index += 1;
-            const body = self.code.extra[extra_index..][0..info.body_len];
+            const body = self.code.bodySlice(extra_index, info.body_len);
             extra_index += body.len;
 
             try stream.writeAll(",\n");
@@ -2074,7 +2073,7 @@ const Writer = struct {
                 extra_index += 1;
                 const info = @as(Zir.Inst.SwitchBlock.ProngInfo, @bitCast(self.code.extra[extra_index]));
                 extra_index += 1;
-                const body = self.code.extra[extra_index..][0..info.body_len];
+                const body = self.code.bodySlice(extra_index, info.body_len);
                 extra_index += info.body_len;
 
                 try stream.writeAll(",\n");
@@ -2131,7 +2130,7 @@ const Writer = struct {
                     try self.writeInstRef(stream, item_last);
                 }
 
-                const body = self.code.extra[extra_index..][0..info.body_len];
+                const body = self.code.bodySlice(extra_index, info.body_len);
                 extra_index += info.body_len;
                 try stream.writeAll(" => ");
                 try self.writeBracedBody(stream, body);
@@ -2145,7 +2144,7 @@ const Writer = struct {
     }
 
     fn writePlNodeField(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
         const name = self.code.nullTerminatedString(extra.field_name_start);
         try self.writeInstRef(stream, extra.lhs);
@@ -2154,7 +2153,7 @@ const Writer = struct {
     }
 
     fn writePlNodeFieldNamed(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
         try self.writeInstRef(stream, extra.lhs);
         try stream.writeAll(", ");
@@ -2164,7 +2163,7 @@ const Writer = struct {
     }
 
     fn writeAs(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
         try self.writeInstRef(stream, extra.dest_type);
         try stream.writeAll(", ");
@@ -2178,7 +2177,7 @@ const Writer = struct {
         stream: anytype,
         inst: Zir.Inst.Index,
     ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
-        const src_node = self.code.instructions.items(.data)[inst].node;
+        const src_node = self.code.instructions.items(.data)[@intFromEnum(inst)].node;
         const src = LazySrcLoc.nodeOffset(src_node);
         try stream.writeAll(") ");
         try self.writeSrc(stream, src);
@@ -2189,14 +2188,14 @@ const Writer = struct {
         stream: anytype,
         inst: Zir.Inst.Index,
     ) (@TypeOf(stream).Error || error{OutOfMemory})!void {
-        const inst_data = self.code.instructions.items(.data)[inst].str_tok;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
         const str = inst_data.get(self.code);
         try stream.print("\"{}\") ", .{std.zig.fmtEscapes(str)});
         try self.writeSrc(stream, inst_data.src());
     }
 
     fn writeStrOp(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].str_op;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].str_op;
         const str = inst_data.getStr(self.code);
         try self.writeInstRef(stream, inst_data.operand);
         try stream.print(", \"{}\")", .{std.zig.fmtEscapes(str)});
@@ -2208,7 +2207,7 @@ const Writer = struct {
         inst: Zir.Inst.Index,
         inferred_error_set: bool,
     ) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const src = inst_data.src();
         const extra = self.code.extraData(Zir.Inst.Func, inst_data.payload_index);
 
@@ -2225,12 +2224,12 @@ const Writer = struct {
                 extra_index += 1;
             },
             else => {
-                ret_ty_body = self.code.extra[extra_index..][0..extra.data.ret_body_len];
+                ret_ty_body = self.code.bodySlice(extra_index, extra.data.ret_body_len);
                 extra_index += ret_ty_body.len;
             },
         }
 
-        const body = self.code.extra[extra_index..][0..extra.data.body_len];
+        const body = self.code.bodySlice(extra_index, extra.data.body_len);
         extra_index += body.len;
 
         var src_locs: Zir.Inst.Func.SrcLocs = undefined;
@@ -2263,7 +2262,7 @@ const Writer = struct {
     }
 
     fn writeFuncFancy(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
         const extra = self.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index);
         const src = inst_data.src();
 
@@ -2289,7 +2288,7 @@ const Writer = struct {
         if (extra.data.bits.has_align_body) {
             const body_len = self.code.extra[extra_index];
             extra_index += 1;
-            align_body = self.code.extra[extra_index..][0..body_len];
+            align_body = self.code.bodySlice(extra_index, body_len);
             extra_index += align_body.len;
         } else if (extra.data.bits.has_align_ref) {
             align_ref = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
@@ -2298,7 +2297,7 @@ const Writer = struct {
         if (extra.data.bits.has_addrspace_body) {
             const body_len = self.code.extra[extra_index];
             extra_index += 1;
-            addrspace_body = self.code.extra[extra_index..][0..body_len];
+            addrspace_body = self.code.bodySlice(extra_index, body_len);
             extra_index += addrspace_body.len;
         } else if (extra.data.bits.has_addrspace_ref) {
             addrspace_ref = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
@@ -2307,7 +2306,7 @@ const Writer = struct {
         if (extra.data.bits.has_section_body) {
             const body_len = self.code.extra[extra_index];
             extra_index += 1;
-            section_body = self.code.extra[extra_index..][0..body_len];
+            section_body = self.code.bodySlice(extra_index, body_len);
             extra_index += section_body.len;
         } else if (extra.data.bits.has_section_ref) {
             section_ref = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
@@ -2316,7 +2315,7 @@ const Writer = struct {
         if (extra.data.bits.has_cc_body) {
             const body_len = self.code.extra[extra_index];
             extra_index += 1;
-            cc_body = self.code.extra[extra_index..][0..body_len];
+            cc_body = self.code.bodySlice(extra_index, body_len);
             extra_index += cc_body.len;
         } else if (extra.data.bits.has_cc_ref) {
             cc_ref = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
@@ -2325,7 +2324,7 @@ const Writer = struct {
         if (extra.data.bits.has_ret_ty_body) {
             const body_len = self.code.extra[extra_index];
             extra_index += 1;
-            ret_ty_body = self.code.extra[extra_index..][0..body_len];
+            ret_ty_body = self.code.bodySlice(extra_index, body_len);
             extra_index += ret_ty_body.len;
         } else if (extra.data.bits.has_ret_ty_ref) {
             ret_ty_ref = @as(Zir.Inst.Ref, @enumFromInt(self.code.extra[extra_index]));
@@ -2338,7 +2337,7 @@ const Writer = struct {
             break :blk x;
         } else 0;
 
-        const body = self.code.extra[extra_index..][0..extra.data.body_len];
+        const body = self.code.bodySlice(extra_index, extra.data.body_len);
         extra_index += body.len;
 
         var src_locs: Zir.Inst.Func.SrcLocs = undefined;
@@ -2423,7 +2422,7 @@ const Writer = struct {
 
     fn writeTypeofPeer(self: *Writer, stream: anytype, extended: Zir.Inst.Extended.InstData) !void {
         const extra = self.code.extraData(Zir.Inst.TypeOfPeer, extended.operand);
-        const body = self.code.extra[extra.data.body_index..][0..extra.data.body_len];
+        const body = self.code.bodySlice(extra.data.body_index, extra.data.body_len);
         try self.writeBracedBody(stream, body);
         try stream.writeAll(",[");
         const args = self.code.refSlice(extra.end, extended.small);
@@ -2435,16 +2434,16 @@ const Writer = struct {
     }
 
     fn writeBoolBr(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].bool_br;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].bool_br;
         const extra = self.code.extraData(Zir.Inst.Block, inst_data.payload_index);
-        const body = self.code.extra[extra.end..][0..extra.data.body_len];
+        const body = self.code.bodySlice(extra.end, extra.data.body_len);
         try self.writeInstRef(stream, inst_data.lhs);
         try stream.writeAll(", ");
         try self.writeBracedBody(stream, body);
     }
 
     fn writeIntType(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const int_type = self.code.instructions.items(.data)[inst].int_type;
+        const int_type = self.code.instructions.items(.data)[@intFromEnum(inst)].int_type;
         const prefix: u8 = switch (int_type.signedness) {
             .signed => 'i',
             .unsigned => 'u',
@@ -2454,14 +2453,14 @@ const Writer = struct {
     }
 
     fn writeSaveErrRetIndex(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].save_err_ret_index;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].save_err_ret_index;
 
         try self.writeInstRef(stream, inst_data.operand);
         try stream.writeAll(")");
     }
 
     fn writeRestoreErrRetIndex(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].restore_err_ret_index;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].restore_err_ret_index;
 
         try self.writeInstRef(stream, inst_data.block);
         try stream.writeAll(", ");
@@ -2470,7 +2469,7 @@ const Writer = struct {
     }
 
     fn writeBreak(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].@"break";
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].@"break";
         const extra = self.code.extraData(Zir.Inst.Break, inst_data.payload_index).data;
 
         try self.writeInstIndex(stream, extra.block_inst);
@@ -2480,7 +2479,7 @@ const Writer = struct {
     }
 
     fn writeArrayInit(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
 
         const extra = self.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
         const args = self.code.refSlice(extra.end, extra.data.operands_len);
@@ -2496,7 +2495,7 @@ const Writer = struct {
     }
 
     fn writeArrayInitAnon(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
 
         const extra = self.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
         const args = self.code.refSlice(extra.end, extra.data.operands_len);
@@ -2511,7 +2510,7 @@ const Writer = struct {
     }
 
     fn writeArrayInitSent(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
 
         const extra = self.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
         const args = self.code.refSlice(extra.end, extra.data.operands_len);
@@ -2531,7 +2530,7 @@ const Writer = struct {
     }
 
     fn writeUnreachable(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].@"unreachable";
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].@"unreachable";
         try stream.writeAll(") ");
         try self.writeSrc(stream, inst_data.src());
     }
@@ -2585,34 +2584,37 @@ const Writer = struct {
     }
 
     fn writeDbgStmt(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].dbg_stmt;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
         try stream.print("{d}, {d})", .{ inst_data.line + 1, inst_data.column + 1 });
     }
 
     fn writeDefer(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].@"defer";
-        const body = self.code.extra[inst_data.index..][0..inst_data.len];
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].@"defer";
+        const body = self.code.bodySlice(inst_data.index, inst_data.len);
         try self.writeBracedBody(stream, body);
         try stream.writeByte(')');
     }
 
     fn writeDeferErrCode(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
-        const inst_data = self.code.instructions.items(.data)[inst].defer_err_code;
+        const inst_data = self.code.instructions.items(.data)[@intFromEnum(inst)].defer_err_code;
         const extra = self.code.extraData(Zir.Inst.DeferErrCode, inst_data.payload_index).data;
 
-        try self.writeInstRef(stream, Zir.indexToRef(extra.remapped_err_code));
+        try self.writeInstRef(stream, extra.remapped_err_code.toRef());
         try stream.writeAll(" = ");
         try self.writeInstRef(stream, inst_data.err_code);
         try stream.writeAll(", ");
-        const body = self.code.extra[extra.index..][0..extra.len];
+        const body = self.code.bodySlice(extra.index, extra.len);
         try self.writeBracedBody(stream, body);
         try stream.writeByte(')');
     }
 
     fn writeInstRef(self: *Writer, stream: anytype, ref: Zir.Inst.Ref) !void {
-        const i = @intFromEnum(ref);
-        if (i < InternPool.static_len) return stream.print("@{}", .{@as(InternPool.Index, @enumFromInt(i))});
-        return self.writeInstIndex(stream, i - InternPool.static_len);
+        if (ref.toIndex()) |i| {
+            return self.writeInstIndex(stream, i);
+        } else {
+            const val: InternPool.Index = @enumFromInt(@intFromEnum(ref));
+            return stream.print("@{}", .{val});
+        }
     }
 
     fn writeInstIndex(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
src/Sema.zig
@@ -226,7 +226,7 @@ pub const InferredErrorSet = struct {
 /// be called safely for any of the instructions passed in.
 pub const InstMap = struct {
     items: []Air.Inst.Ref = &[_]Air.Inst.Ref{},
-    start: Zir.Inst.Index = 0,
+    start: Zir.Inst.Index = @enumFromInt(0),
 
     pub fn deinit(map: InstMap, allocator: mem.Allocator) void {
         allocator.free(map.items);
@@ -234,7 +234,7 @@ pub const InstMap = struct {
 
     pub fn get(map: InstMap, key: Zir.Inst.Index) ?Air.Inst.Ref {
         if (!map.contains(key)) return null;
-        return map.items[key - map.start];
+        return map.items[@intFromEnum(key) - @intFromEnum(map.start)];
     }
 
     pub fn putAssumeCapacity(
@@ -242,7 +242,7 @@ pub const InstMap = struct {
         key: Zir.Inst.Index,
         ref: Air.Inst.Ref,
     ) void {
-        map.items[key - map.start] = ref;
+        map.items[@intFromEnum(key) - @intFromEnum(map.start)] = ref;
     }
 
     pub fn putAssumeCapacityNoClobber(
@@ -263,7 +263,7 @@ pub const InstMap = struct {
         map: *InstMap,
         key: Zir.Inst.Index,
     ) GetOrPutResult {
-        const index = key - map.start;
+        const index = @intFromEnum(key) - @intFromEnum(map.start);
         return GetOrPutResult{
             .value_ptr = &map.items[index],
             .found_existing = map.items[index] != .none,
@@ -272,12 +272,12 @@ pub const InstMap = struct {
 
     pub fn remove(map: InstMap, key: Zir.Inst.Index) bool {
         if (!map.contains(key)) return false;
-        map.items[key - map.start] = .none;
+        map.items[@intFromEnum(key) - @intFromEnum(map.start)] = .none;
         return true;
     }
 
     pub fn contains(map: InstMap, key: Zir.Inst.Index) bool {
-        return map.items[key - map.start] != .none;
+        return map.items[@intFromEnum(key) - @intFromEnum(map.start)] != .none;
     }
 
     pub fn ensureSpaceForInstructions(
@@ -285,13 +285,12 @@ pub const InstMap = struct {
         allocator: mem.Allocator,
         insts: []const Zir.Inst.Index,
     ) !void {
-        const min_max = mem.minMax(Zir.Inst.Index, insts);
-        const start = min_max.min;
-        const end = min_max.max;
-        if (map.start <= start and end < map.items.len + map.start)
+        const start, const end = mem.minMax(u32, @ptrCast(insts));
+        const map_start = @intFromEnum(map.start);
+        if (map_start <= start and end < map.items.len + map_start)
             return;
 
-        const old_start = if (map.items.len == 0) start else map.start;
+        const old_start = if (map.items.len == 0) start else map_start;
         var better_capacity = map.items.len;
         var better_start = old_start;
         while (true) {
@@ -310,7 +309,7 @@ pub const InstMap = struct {
 
         allocator.free(map.items);
         map.items = new_items;
-        map.start = @intCast(better_start);
+        map.start = @enumFromInt(better_start);
     }
 };
 
@@ -350,7 +349,7 @@ pub const Block = struct {
     /// Non zero if a non-inline loop or a runtime conditional have been encountered.
     /// Stores to comptime variables are only allowed when var.runtime_index <= runtime_index.
     runtime_index: Value.RuntimeIndex = .zero,
-    inline_block: Zir.Inst.Index = 0,
+    inline_block: Zir.Inst.OptionalIndex = .none,
 
     comptime_reason: ?*const ComptimeReason = null,
     // TODO is_comptime and comptime_reason should probably be merged together.
@@ -897,7 +896,7 @@ fn analyzeBodyRuntimeBreak(sema: *Sema, block: *Block, body: []const Zir.Inst.In
     _ = sema.analyzeBodyInner(block, body) catch |err| switch (err) {
         error.ComptimeBreak => {
             const zir_datas = sema.code.instructions.items(.data);
-            const break_data = zir_datas[sema.comptime_break_inst].@"break";
+            const break_data = zir_datas[@intFromEnum(sema.comptime_break_inst)].@"break";
             const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
             try sema.addRuntimeBreak(block, .{
                 .block_inst = extra.block_inst,
@@ -938,7 +937,7 @@ pub fn analyzeBodyBreak(
     if (block.instructions.items.len != 0 and
         sema.isNoReturn(Air.indexToRef(block.instructions.items[block.instructions.items.len - 1])))
         return null;
-    const break_data = sema.code.instructions.items(.data)[break_inst].@"break";
+    const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break";
     const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
     return BreakData{
         .block_inst = extra.block_inst,
@@ -998,7 +997,7 @@ fn analyzeBodyInner(
         std.log.scoped(.sema_zir).debug("sema ZIR {s} %{d}", .{
             mod.namespacePtr(mod.declPtr(block.src_decl).src_namespace).file_scope.sub_file_path, inst,
         });
-        const air_inst: Air.Inst.Ref = switch (tags[inst]) {
+        const air_inst: Air.Inst.Ref = switch (tags[@intFromEnum(inst)]) {
             // zig fmt: off
             .alloc                        => try sema.zirAlloc(block, inst),
             .alloc_inferred               => try sema.zirAllocInferred(block, inst, true),
@@ -1220,7 +1219,7 @@ fn analyzeBodyInner(
             // zig fmt: on
 
             .extended => ext: {
-                const extended = datas[inst].extended;
+                const extended = datas[@intFromEnum(inst)].extended;
                 break :ext switch (extended.opcode) {
                     // zig fmt: off
                     .variable           => try sema.zirVarExtended(       block, extended),
@@ -1477,13 +1476,13 @@ fn analyzeBodyInner(
             },
             .check_comptime_control_flow => {
                 if (!block.is_comptime) {
-                    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+                    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
                     const src = inst_data.src();
-                    const inline_block = Zir.refToIndex(inst_data.operand).?;
+                    const inline_block = inst_data.operand.toIndex().?;
 
                     var check_block = block;
                     const target_runtime_index = while (true) {
-                        if (check_block.inline_block == inline_block) {
+                        if (check_block.inline_block == inline_block.toOptional()) {
                             break check_block.runtime_index;
                         }
                         check_block = check_block.parent.?;
@@ -1534,7 +1533,7 @@ fn analyzeBodyInner(
             .repeat => {
                 if (block.is_comptime) {
                     // Send comptime control flow back to the beginning of this block.
-                    const src = LazySrcLoc.nodeOffset(datas[inst].node);
+                    const src = LazySrcLoc.nodeOffset(datas[@intFromEnum(inst)].node);
                     try sema.emitBackwardBranch(block, src);
 
                     // We need to construct new capture scopes for the next loop iteration so it
@@ -1549,7 +1548,7 @@ fn analyzeBodyInner(
             },
             .repeat_inline => {
                 // Send comptime control flow back to the beginning of this block.
-                const src = LazySrcLoc.nodeOffset(datas[inst].node);
+                const src = LazySrcLoc.nodeOffset(datas[@intFromEnum(inst)].node);
                 try sema.emitBackwardBranch(block, src);
 
                 // We need to construct new capture scopes for the next loop iteration so it
@@ -1562,9 +1561,9 @@ fn analyzeBodyInner(
             .loop => blk: {
                 if (!block.is_comptime) break :blk try sema.zirLoop(block, inst);
                 // Same as `block_inline`. TODO https://github.com/ziglang/zig/issues/8220
-                const inst_data = datas[inst].pl_node;
+                const inst_data = datas[@intFromEnum(inst)].pl_node;
                 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
-                const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
+                const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
                 const break_data = (try sema.analyzeBodyBreak(block, inline_body)) orelse
                     break always_noreturn;
                 if (inst == break_data.block_inst) {
@@ -1575,12 +1574,12 @@ fn analyzeBodyInner(
             },
             .block, .block_comptime => blk: {
                 if (!block.is_comptime) {
-                    break :blk try sema.zirBlock(block, inst, tags[inst] == .block_comptime);
+                    break :blk try sema.zirBlock(block, inst, tags[@intFromEnum(inst)] == .block_comptime);
                 }
                 // Same as `block_inline`. TODO https://github.com/ziglang/zig/issues/8220
-                const inst_data = datas[inst].pl_node;
+                const inst_data = datas[@intFromEnum(inst)].pl_node;
                 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
-                const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
+                const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
                 // If this block contains a function prototype, we need to reset the
                 // current list of parameters and restore it later.
                 // Note: this probably needs to be resolved in a more general manner.
@@ -1601,9 +1600,9 @@ fn analyzeBodyInner(
                 // through a runtime conditional branch, we must retroactively emit
                 // a block, so we remember the block index here just in case.
                 const block_index = block.instructions.items.len;
-                const inst_data = datas[inst].pl_node;
+                const inst_data = datas[@intFromEnum(inst)].pl_node;
                 const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
-                const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
+                const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
                 const gpa = sema.gpa;
 
                 const opt_break_data = b: {
@@ -1614,8 +1613,11 @@ fn analyzeBodyInner(
                     // If this block contains a function prototype, we need to reset the
                     // current list of parameters and restore it later.
                     // Note: this probably needs to be resolved in a more general manner.
-                    child_block.inline_block =
-                        if (tags[inline_body[inline_body.len - 1]] == .repeat_inline) inline_body[0] else inst;
+                    const tag_index = @intFromEnum(inline_body[inline_body.len - 1]);
+                    child_block.inline_block = (if (tags[tag_index] == .repeat_inline)
+                        inline_body[0]
+                    else
+                        inst).toOptional();
 
                     var label: Block.Label = .{
                         .zir_block = inst,
@@ -1682,11 +1684,14 @@ fn analyzeBodyInner(
             .condbr => blk: {
                 if (!block.is_comptime) break sema.zirCondbr(block, inst);
                 // Same as condbr_inline. TODO https://github.com/ziglang/zig/issues/8220
-                const inst_data = datas[inst].pl_node;
+                const inst_data = datas[@intFromEnum(inst)].pl_node;
                 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
                 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
-                const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len];
-                const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
+                const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len);
+                const else_body = sema.code.bodySlice(
+                    extra.end + then_body.len,
+                    extra.data.else_body_len,
+                );
                 const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition, .{
                     .needed_comptime_reason = "condition in comptime branch must be comptime-known",
                     .block_comptime_reason = block.comptime_reason,
@@ -1703,11 +1708,14 @@ fn analyzeBodyInner(
                 }
             },
             .condbr_inline => blk: {
-                const inst_data = datas[inst].pl_node;
+                const inst_data = datas[@intFromEnum(inst)].pl_node;
                 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
                 const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
-                const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len];
-                const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
+                const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len);
+                const else_body = sema.code.bodySlice(
+                    extra.end + then_body.len,
+                    extra.data.else_body_len,
+                );
                 const cond = try sema.resolveInstConst(block, cond_src, extra.data.condition, .{
                     .needed_comptime_reason = "condition in comptime branch must be comptime-known",
                     .block_comptime_reason = block.comptime_reason,
@@ -1727,11 +1735,11 @@ fn analyzeBodyInner(
             },
             .@"try" => blk: {
                 if (!block.is_comptime) break :blk try sema.zirTry(block, inst);
-                const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+                const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
                 const src = inst_data.src();
                 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
                 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
-                const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
+                const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
                 const err_union = try sema.resolveInst(extra.data.operand);
                 const err_union_ty = sema.typeOf(err_union);
                 if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) {
@@ -1758,11 +1766,11 @@ fn analyzeBodyInner(
             },
             .try_ptr => blk: {
                 if (!block.is_comptime) break :blk try sema.zirTryPtr(block, inst);
-                const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+                const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
                 const src = inst_data.src();
                 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
                 const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
-                const inline_body = sema.code.extra[extra.end..][0..extra.data.body_len];
+                const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len);
                 const operand = try sema.resolveInst(extra.data.operand);
                 const err_union = try sema.analyzeLoad(block, src, operand, operand_src);
                 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
@@ -1783,8 +1791,8 @@ fn analyzeBodyInner(
                 }
             },
             .@"defer" => blk: {
-                const inst_data = sema.code.instructions.items(.data)[inst].@"defer";
-                const defer_body = sema.code.extra[inst_data.index..][0..inst_data.len];
+                const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"defer";
+                const defer_body = sema.code.bodySlice(inst_data.index, inst_data.len);
                 const break_inst = sema.analyzeBodyInner(block, defer_body) catch |err| switch (err) {
                     error.ComptimeBreak => sema.comptime_break_inst,
                     else => |e| return e,
@@ -1793,9 +1801,9 @@ fn analyzeBodyInner(
                 break :blk .void_value;
             },
             .defer_err_code => blk: {
-                const inst_data = sema.code.instructions.items(.data)[inst].defer_err_code;
+                const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].defer_err_code;
                 const extra = sema.code.extraData(Zir.Inst.DeferErrCode, inst_data.payload_index).data;
-                const defer_body = sema.code.extra[extra.index..][0..extra.len];
+                const defer_body = sema.code.bodySlice(extra.index, extra.len);
                 const err_code = try sema.resolveInst(inst_data.err_code);
                 map.putAssumeCapacity(extra.remapped_err_code, err_code);
                 const break_inst = sema.analyzeBodyInner(block, defer_body) catch |err| switch (err) {
@@ -1846,14 +1854,14 @@ pub fn resolveInstAllowNone(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref {
 
 pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) !Air.Inst.Ref {
     assert(zir_ref != .none);
-    const i = @intFromEnum(zir_ref);
+    if (zir_ref.toIndex()) |i| {
+        const inst = sema.inst_map.get(i).?;
+        if (inst == .generic_poison) return error.GenericPoison;
+        return inst;
+    }
     // First section of indexes correspond to a set number of constant values.
     // We intentionally map the same indexes to the same values between ZIR and AIR.
-    if (i < InternPool.static_len) return @enumFromInt(i);
-    // The last section of indexes refers to the map of ZIR => AIR.
-    const inst = sema.inst_map.get(i - InternPool.static_len).?;
-    if (inst == .generic_poison) return error.GenericPoison;
-    return inst;
+    return @enumFromInt(@intFromEnum(zir_ref));
 }
 
 fn resolveConstBool(
@@ -1968,30 +1976,30 @@ const GenericPoisonReason = union(enum) {
 fn genericPoisonReason(sema: *Sema, ref: Zir.Inst.Ref) GenericPoisonReason {
     var cur = ref;
     while (true) {
-        const inst = Zir.refToIndex(cur) orelse return .unknown;
-        switch (sema.code.instructions.items(.tag)[inst]) {
+        const inst = cur.toIndex() orelse return .unknown;
+        switch (sema.code.instructions.items(.tag)[@intFromEnum(inst)]) {
             .validate_array_init_ref_ty => {
-                const pl_node = sema.code.instructions.items(.data)[inst].pl_node;
+                const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
                 const extra = sema.code.extraData(Zir.Inst.ArrayInitRefTy, pl_node.payload_index).data;
                 cur = extra.ptr_ty;
             },
             .array_init_elem_type => {
-                const bin = sema.code.instructions.items(.data)[inst].bin;
+                const bin = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin;
                 cur = bin.lhs;
             },
             .indexable_ptr_elem_type, .vector_elem_type => {
-                const un_node = sema.code.instructions.items(.data)[inst].un_node;
+                const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
                 cur = un_node.operand;
             },
             .struct_init_field_type => {
-                const pl_node = sema.code.instructions.items(.data)[inst].pl_node;
+                const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
                 const extra = sema.code.extraData(Zir.Inst.FieldType, pl_node.payload_index).data;
                 cur = extra.container_type;
             },
             .elem_type => {
                 // There are two cases here: the pointer type may already have been
                 // generic poison, or it may have been an anyopaque pointer.
-                const un_node = sema.code.instructions.items(.data)[inst].un_node;
+                const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
                 const operand_ref = sema.resolveInst(un_node.operand) catch |err| switch (err) {
                     error.GenericPoison => unreachable, // this is a type, not a value
                 };
@@ -2008,7 +2016,7 @@ fn genericPoisonReason(sema: *Sema, ref: Zir.Inst.Ref) GenericPoisonReason {
                 // A function call can never return generic poison, so we must be
                 // evaluating an `anytype` function parameter.
                 // TODO: better source location - function decl rather than call
-                const pl_node = sema.code.instructions.items(.data)[inst].pl_node;
+                const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
                 return .{ .anytype_param = pl_node.src() };
             },
             else => return .unknown,
@@ -2651,7 +2659,7 @@ pub fn getStructType(
     const mod = sema.mod;
     const gpa = sema.gpa;
     const ip = &mod.intern_pool;
-    const extended = sema.code.instructions.items(.data)[zir_index].extended;
+    const extended = sema.code.instructions.items(.data)[@intFromEnum(zir_index)].extended;
     assert(extended.opcode == .struct_decl);
     const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
 
@@ -2801,7 +2809,7 @@ fn createAnonymousDeclTypeNamed(
             try writer.print("{}(", .{mod.declPtr(block.src_decl).name.fmt(&mod.intern_pool)});
 
             var arg_i: usize = 0;
-            for (fn_info.param_body) |zir_inst| switch (zir_tags[zir_inst]) {
+            for (fn_info.param_body) |zir_inst| switch (zir_tags[@intFromEnum(zir_inst)]) {
                 .param, .param_comptime, .param_anytype, .param_anytype_comptime => {
                     const arg = sema.inst_map.get(zir_inst).?;
                     // If this is being called in a generic function then analyzeCall will
@@ -2827,11 +2835,10 @@ fn createAnonymousDeclTypeNamed(
             return new_decl_index;
         },
         .dbg_var => {
-            const ref = Zir.indexToRef(inst.?);
+            const ref = inst.?.toRef();
             const zir_tags = sema.code.instructions.items(.tag);
             const zir_data = sema.code.instructions.items(.data);
-            var i = inst.?;
-            while (i < zir_tags.len) : (i += 1) switch (zir_tags[i]) {
+            for (@intFromEnum(inst.?)..zir_tags.len) |i| switch (zir_tags[i]) {
                 .dbg_var_ptr, .dbg_var_val => {
                     if (zir_data[i].str_op.operand != ref) continue;
 
@@ -2917,7 +2924,7 @@ fn zirEnumDecl(
 
     extra_index = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl);
 
-    const body = sema.code.extra[extra_index..][0..body_len];
+    const body = sema.code.bodySlice(extra_index, body_len);
     extra_index += body.len;
 
     const bit_bags_count = std.math.divCeil(usize, fields_len, 32) catch unreachable;
@@ -3296,7 +3303,7 @@ fn zirErrorSetDecl(
 
     const mod = sema.mod;
     const gpa = sema.gpa;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const extra = sema.code.extraData(Zir.Inst.ErrorSetDecl, inst_data.payload_index);
 
@@ -3359,7 +3366,7 @@ fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok;
     const operand = try sema.resolveInst(inst_data.operand);
     return sema.analyzeRef(block, inst_data.src(), operand);
 }
@@ -3368,7 +3375,7 @@ fn zirEnsureResultUsed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compile
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const operand = try sema.resolveInst(inst_data.operand);
     const src = inst_data.src();
 
@@ -3411,7 +3418,7 @@ fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const operand = try sema.resolveInst(inst_data.operand);
     const src = inst_data.src();
     const operand_ty = sema.typeOf(operand);
@@ -3434,7 +3441,7 @@ fn zirEnsureErrUnionPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const operand = try sema.resolveInst(inst_data.operand);
     const operand_ty = sema.typeOf(operand);
@@ -3459,7 +3466,7 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const object = try sema.resolveInst(inst_data.operand);
 
@@ -3578,7 +3585,7 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
     const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
     return sema.analyzeComptimeAlloc(block, var_ty, .none);
@@ -3586,7 +3593,7 @@ fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
 
 fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const alloc = try sema.resolveInst(inst_data.operand);
     const alloc_ty = sema.typeOf(alloc);
     const ptr_info = alloc_ty.ptrInfo(mod);
@@ -3883,7 +3890,7 @@ fn zirAllocInferredComptime(
     is_const: bool,
 ) CompileError!Air.Inst.Ref {
     const gpa = sema.gpa;
-    const src_node = sema.code.instructions.items(.data)[inst].node;
+    const src_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].node;
     const src = LazySrcLoc.nodeOffset(src_node);
     sema.src = src;
 
@@ -3902,7 +3909,7 @@ fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
     const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
     if (block.is_comptime) {
@@ -3925,7 +3932,7 @@ fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
     const var_ty = try sema.resolveType(block, ty_src, inst_data.operand);
     if (block.is_comptime) {
@@ -3951,7 +3958,7 @@ fn zirAllocInferred(
     defer tracy.end();
 
     const gpa = sema.gpa;
-    const src_node = sema.code.instructions.items(.data)[inst].node;
+    const src_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].node;
     const src = LazySrcLoc.nodeOffset(src_node);
     sema.src = src;
 
@@ -3986,7 +3993,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
 
     const mod = sema.mod;
     const gpa = sema.gpa;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node };
     const ptr = try sema.resolveInst(inst_data.operand);
@@ -4027,7 +4034,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
             } });
 
             // Remap the ZIR operand to the resolved pointer value
-            sema.inst_map.putAssumeCapacity(Zir.refToIndex(inst_data.operand).?, Air.internedToRef(interned));
+            sema.inst_map.putAssumeCapacity(inst_data.operand.toIndex().?, Air.internedToRef(interned));
         },
         .inferred_alloc => {
             const ia1 = sema.air_instructions.items(.data)[ptr_inst].inferred_alloc;
@@ -4061,7 +4068,7 @@ fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
                 } });
 
                 // Remap the ZIR oeprand to the resolved pointer value
-                sema.inst_map.putAssumeCapacity(Zir.refToIndex(inst_data.operand).?, Air.internedToRef(new_const_ptr));
+                sema.inst_map.putAssumeCapacity(inst_data.operand.toIndex().?, Air.internedToRef(new_const_ptr));
 
                 // Unless the block is comptime, `alloc_inferred` always produces
                 // a runtime constant. The final inferred type needs to be
@@ -4125,7 +4132,7 @@ fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
     const mod = sema.mod;
     const gpa = sema.gpa;
     const ip = &mod.intern_pool;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
     const args = sema.code.refSlice(extra.end, extra.data.operands_len);
     const src = inst_data.src();
@@ -4264,14 +4271,14 @@ fn optEuBasePtrInit(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, src: LazySrcL
 }
 
 fn zirOptEuBasePtrInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const un_node = sema.code.instructions.items(.data)[inst].un_node;
+    const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const ptr = try sema.resolveInst(un_node.operand);
     return sema.optEuBasePtrInit(block, ptr, un_node.src());
 }
 
 fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const pl_node = sema.code.instructions.items(.data)[inst].pl_node;
+    const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = pl_node.src();
     const extra = sema.code.extraData(Zir.Inst.Bin, pl_node.payload_index).data;
     const uncoerced_val = try sema.resolveInst(extra.rhs);
@@ -4322,7 +4329,7 @@ fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
 
 fn zirValidateRefTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
     const mod = sema.mod;
-    const un_tok = sema.code.instructions.items(.data)[inst].un_tok;
+    const un_tok = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok;
     const src = un_tok.src();
     // In case of GenericPoison, we don't actually have a type, so this will be
     // treated as an untyped address-of operator.
@@ -4353,7 +4360,7 @@ fn zirValidateArrayInitRefTy(
     inst: Zir.Inst.Index,
 ) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const pl_node = sema.code.instructions.items(.data)[inst].pl_node;
+    const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = pl_node.src();
     const extra = sema.code.extraData(Zir.Inst.ArrayInitRefTy, pl_node.payload_index).data;
     const maybe_wrapped_ptr_ty = sema.resolveType(block, .unneeded, extra.ptr_ty) catch |err| switch (err) {
@@ -4389,7 +4396,7 @@ fn zirValidateArrayInitTy(
     is_result_ty: bool,
 ) CompileError!void {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const ty_src: LazySrcLoc = .{ .node_offset_init_ty = inst_data.src_node };
     const extra = sema.code.extraData(Zir.Inst.ArrayInit, inst_data.payload_index).data;
@@ -4452,7 +4459,7 @@ fn zirValidateStructInitTy(
     is_result_ty: bool,
 ) CompileError!void {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const ty = sema.resolveType(block, src, inst_data.operand) catch |err| switch (err) {
         // It's okay for the type to be unknown: this will result in an anonymous struct init.
@@ -4477,11 +4484,11 @@ fn zirValidatePtrStructInit(
     defer tracy.end();
 
     const mod = sema.mod;
-    const validate_inst = sema.code.instructions.items(.data)[inst].pl_node;
+    const validate_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const init_src = validate_inst.src();
     const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);
-    const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len];
-    const field_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;
+    const instrs = sema.code.bodySlice(validate_extra.end, validate_extra.data.body_len);
+    const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(instrs[0])].pl_node;
     const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
     const object_ptr = try sema.resolveInst(field_ptr_extra.lhs);
     const agg_ty = sema.typeOf(object_ptr).childType(mod).optEuBaseType(mod);
@@ -4525,7 +4532,7 @@ fn validateUnionInit(
             errdefer msg.destroy(gpa);
 
             for (instrs[1..]) |inst| {
-                const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+                const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
                 const inst_src: LazySrcLoc = .{ .node_offset_initializer = inst_data.src_node };
                 try sema.errNote(block, inst_src, msg, "additional initializer here", .{});
             }
@@ -4543,7 +4550,7 @@ fn validateUnionInit(
     }
 
     const field_ptr = instrs[0];
-    const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node;
+    const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node;
     const field_src: LazySrcLoc = .{ .node_offset_initializer = field_ptr_data.src_node };
     const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
     const field_name = try mod.intern_pool.getOrPutString(gpa, sema.code.nullTerminatedString(field_ptr_extra.field_name_start));
@@ -4662,14 +4669,14 @@ fn validateStructInit(
     defer gpa.free(field_indices);
 
     // Maps field index to field_ptr index of where it was already initialized.
-    const found_fields = try gpa.alloc(Zir.Inst.Index, struct_ty.structFieldCount(mod));
+    const found_fields = try gpa.alloc(Zir.Inst.OptionalIndex, struct_ty.structFieldCount(mod));
     defer gpa.free(found_fields);
-    @memset(found_fields, 0);
+    @memset(found_fields, .none);
 
     var struct_ptr_zir_ref: Zir.Inst.Ref = undefined;
 
     for (instrs, field_indices) |field_ptr, *field_index| {
-        const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node;
+        const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node;
         const field_src: LazySrcLoc = .{ .node_offset_initializer = field_ptr_data.src_node };
         const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data;
         struct_ptr_zir_ref = field_ptr_extra.lhs;
@@ -4681,9 +4688,8 @@ fn validateStructInit(
             try sema.tupleFieldIndex(block, struct_ty, field_name, field_src)
         else
             try sema.structFieldIndex(block, struct_ty, field_name, field_src);
-        if (found_fields[field_index.*] != 0) {
-            const other_field_ptr = found_fields[field_index.*];
-            const other_field_ptr_data = sema.code.instructions.items(.data)[other_field_ptr].pl_node;
+        if (found_fields[field_index.*].unwrap()) |other_field_ptr| {
+            const other_field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(other_field_ptr)].pl_node;
             const other_field_src: LazySrcLoc = .{ .node_offset_initializer = other_field_ptr_data.src_node };
             const msg = msg: {
                 const msg = try sema.errMsg(block, field_src, "duplicate field", .{});
@@ -4693,7 +4699,7 @@ fn validateStructInit(
             };
             return sema.failWithOwnedErrorMsg(block, msg);
         }
-        found_fields[field_index.*] = field_ptr;
+        found_fields[field_index.*] = field_ptr.toOptional();
     }
 
     var root_msg: ?*Module.ErrorMsg = null;
@@ -4709,7 +4715,7 @@ fn validateStructInit(
         // Avoid the cost of the extra machinery for detecting a comptime struct init value.
         for (found_fields, 0..) |field_ptr, i_usize| {
             const i: u32 = @intCast(i_usize);
-            if (field_ptr != 0) continue;
+            if (field_ptr != .none) continue;
 
             const default_val = struct_ty.structFieldDefaultValue(i, mod);
             if (default_val.toIntern() == .unreachable_value) {
@@ -4770,9 +4776,9 @@ fn validateStructInit(
     // ends up being comptime-known.
     const field_values = try sema.arena.alloc(InternPool.Index, struct_ty.structFieldCount(mod));
 
-    field: for (found_fields, 0..) |field_ptr, i_usize| {
+    field: for (found_fields, 0..) |opt_field_ptr, i_usize| {
         const i: u32 = @intCast(i_usize);
-        if (field_ptr != 0) {
+        if (opt_field_ptr.unwrap()) |field_ptr| {
             // Determine whether the value stored to this pointer is comptime-known.
             const field_ty = struct_ty.structFieldType(i, mod);
             if (try sema.typeHasOnePossibleValue(field_ty)) |opv| {
@@ -4831,7 +4837,7 @@ fn validateStructInit(
                 if (try sema.resolveValue(bin_op.rhs)) |val| {
                     field_values[i] = val.toIntern();
                 } else if (require_comptime) {
-                    const field_ptr_data = sema.code.instructions.items(.data)[field_ptr].pl_node;
+                    const field_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(field_ptr)].pl_node;
                     return sema.failWithNeededComptime(block, field_ptr_data.src(), .{
                         .needed_comptime_reason = "initializer of comptime only struct must be comptime-known",
                     });
@@ -4931,7 +4937,7 @@ fn validateStructInit(
 
     // Our task is to insert `store` instructions for all the default field values.
     for (found_fields, 0..) |field_ptr, i| {
-        if (field_ptr != 0) continue;
+        if (field_ptr != .none) continue;
 
         const field_src = init_src; // TODO better source location
         const default_field_ptr = if (struct_ty.isTuple(mod))
@@ -4950,11 +4956,11 @@ fn zirValidatePtrArrayInit(
     inst: Zir.Inst.Index,
 ) CompileError!void {
     const mod = sema.mod;
-    const validate_inst = sema.code.instructions.items(.data)[inst].pl_node;
+    const validate_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const init_src = validate_inst.src();
     const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index);
-    const instrs = sema.code.extra[validate_extra.end..][0..validate_extra.data.body_len];
-    const first_elem_ptr_data = sema.code.instructions.items(.data)[instrs[0]].pl_node;
+    const instrs = sema.code.bodySlice(validate_extra.end, validate_extra.data.body_len);
+    const first_elem_ptr_data = sema.code.instructions.items(.data)[@intFromEnum(instrs[0])].pl_node;
     const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, first_elem_ptr_data.payload_index).data;
     const array_ptr = try sema.resolveInst(elem_ptr_extra.ptr);
     const array_ty = sema.typeOf(array_ptr).childType(mod).optEuBaseType(mod);
@@ -5147,7 +5153,7 @@ fn zirValidatePtrArrayInit(
 
 fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const operand = try sema.resolveInst(inst_data.operand);
     const operand_ty = sema.typeOf(operand);
@@ -5190,7 +5196,7 @@ fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
 
 fn zirValidateDestructure(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.ValidateDestructure, inst_data.payload_index).data;
     const src = inst_data.src();
     const destructure_src = LazySrcLoc.nodeOffset(extra.destructure_node);
@@ -5328,7 +5334,7 @@ fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi
     defer tracy.end();
 
     const src: LazySrcLoc = sema.src;
-    const bin_inst = sema.code.instructions.items(.data)[inst].bin;
+    const bin_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin;
     const ptr = try sema.resolveInst(bin_inst.lhs);
     const operand = try sema.resolveInst(bin_inst.rhs);
     const ptr_inst = Air.refToIndex(ptr).?;
@@ -5387,7 +5393,7 @@ fn storeToInferredAllocComptime(
 }
 
 fn zirSetEvalBranchQuota(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const quota: u32 = @intCast(try sema.resolveInt(block, src, inst_data.operand, Type.u32, .{
         .needed_comptime_reason = "eval branch quota must be comptime-known",
@@ -5399,7 +5405,7 @@ fn zirStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
     const tracy = trace(@src());
     defer tracy.end();
 
-    const bin_inst = sema.code.instructions.items(.data)[inst].bin;
+    const bin_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin;
     const ptr = try sema.resolveInst(bin_inst.lhs);
     const value = try sema.resolveInst(bin_inst.rhs);
     return sema.storePtr(block, sema.src, ptr, value);
@@ -5412,14 +5418,14 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v
     const mod = sema.mod;
     const zir_tags = sema.code.instructions.items(.tag);
     const zir_datas = sema.code.instructions.items(.data);
-    const inst_data = zir_datas[inst].pl_node;
+    const inst_data = zir_datas[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const ptr = try sema.resolveInst(extra.lhs);
     const operand = try sema.resolveInst(extra.rhs);
 
-    const is_ret = if (Zir.refToIndex(extra.lhs)) |ptr_index|
-        zir_tags[ptr_index] == .ret_ptr
+    const is_ret = if (extra.lhs.toIndex()) |ptr_index|
+        zir_tags[@intFromEnum(ptr_index)] == .ret_ptr
     else
         false;
 
@@ -5445,7 +5451,7 @@ fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!v
 }
 
 fn zirStr(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const bytes = sema.code.instructions.items(.data)[inst].str.get(sema.code);
+    const bytes = sema.code.instructions.items(.data)[@intFromEnum(inst)].str.get(sema.code);
     return sema.addStrLitNoAlias(bytes);
 }
 
@@ -5497,7 +5503,7 @@ fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
     const tracy = trace(@src());
     defer tracy.end();
 
-    const int = sema.code.instructions.items(.data)[inst].int;
+    const int = sema.code.instructions.items(.data)[@intFromEnum(inst)].int;
     return sema.mod.intRef(Type.comptime_int, int);
 }
 
@@ -5507,7 +5513,7 @@ fn zirIntBig(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
     defer tracy.end();
 
     const mod = sema.mod;
-    const int = sema.code.instructions.items(.data)[inst].str;
+    const int = sema.code.instructions.items(.data)[@intFromEnum(inst)].str;
     const byte_count = int.len * @sizeOf(std.math.big.Limb);
     const limb_bytes = sema.code.string_bytes[int.start..][0..byte_count];
 
@@ -5525,7 +5531,7 @@ fn zirIntBig(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
 
 fn zirFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     _ = block;
-    const number = sema.code.instructions.items(.data)[inst].float;
+    const number = sema.code.instructions.items(.data)[@intFromEnum(inst)].float;
     return Air.internedToRef((try sema.mod.floatValue(
         Type.comptime_float,
         number,
@@ -5534,7 +5540,7 @@ fn zirFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
 
 fn zirFloat128(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     _ = block;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Float128, inst_data.payload_index).data;
     const number = extra.get();
     return Air.internedToRef((try sema.mod.floatValue(Type.comptime_float, number)).toIntern());
@@ -5544,7 +5550,7 @@ fn zirCompileError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const msg = try sema.resolveConstString(block, operand_src, inst_data.operand, .{
@@ -5594,7 +5600,7 @@ fn zirCompileLog(
 }
 
 fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const msg_inst = try sema.resolveInst(inst_data.operand);
 
@@ -5606,7 +5612,7 @@ fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.I
 }
 
 fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
-    const src_node = sema.code.instructions.items(.data)[inst].node;
+    const src_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].node;
     const src = LazySrcLoc.nodeOffset(src_node);
     sema.src = src;
     _ = try block.addNoOp(.trap);
@@ -5618,10 +5624,10 @@ fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
-    const body = sema.code.extra[extra.end..][0..extra.data.body_len];
+    const body = sema.code.bodySlice(extra.end, extra.data.body_len);
     const gpa = sema.gpa;
 
     // AIR expects a block outside the loop block too.
@@ -5690,10 +5696,10 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
     const mod = sema.mod;
     const comp = mod.comp;
     const gpa = sema.gpa;
-    const pl_node = sema.code.instructions.items(.data)[inst].pl_node;
+    const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = pl_node.src();
     const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
-    const body = sema.code.extra[extra.end..][0..extra.data.body_len];
+    const body = sema.code.bodySlice(extra.end, extra.data.body_len);
 
     // we check this here to avoid undefined symbols
     if (!@import("build_options").have_llvm)
@@ -5768,7 +5774,7 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
 }
 
 fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     return sema.failWithUseOfAsync(parent_block, src);
 }
@@ -5777,10 +5783,10 @@ fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index, force_compt
     const tracy = trace(@src());
     defer tracy.end();
 
-    const pl_node = sema.code.instructions.items(.data)[inst].pl_node;
+    const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = pl_node.src();
     const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
-    const body = sema.code.extra[extra.end..][0..extra.data.body_len];
+    const body = sema.code.bodySlice(extra.end, extra.data.body_len);
     const gpa = sema.gpa;
 
     // Reserve space for a Block instruction so that generated Break instructions can
@@ -5852,7 +5858,7 @@ fn resolveBlockBody(
                 try parent_block.instructions.appendSlice(sema.gpa, child_block.instructions.items);
 
                 const break_inst = sema.comptime_break_inst;
-                const break_data = sema.code.instructions.items(.data)[break_inst].@"break";
+                const break_data = sema.code.instructions.items(.data)[@intFromEnum(break_inst)].@"break";
                 const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data;
                 if (extra.block_inst == body_inst) {
                     return try sema.resolveInst(break_data.operand);
@@ -5991,7 +5997,7 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Export, inst_data.payload_index).data;
     const src = inst_data.src();
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
@@ -6027,7 +6033,7 @@ fn zirExportValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.ExportValue, inst_data.payload_index).data;
     const src = inst_data.src();
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
@@ -6198,7 +6204,7 @@ fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstD
 }
 
 fn zirSetRuntimeSafety(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand, .{
         .needed_comptime_reason = "operand to @setRuntimeSafety must be comptime-known",
@@ -6228,7 +6234,7 @@ fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].@"break";
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"break";
     const extra = sema.code.extraData(Zir.Inst.Break, inst_data.payload_index).data;
     const operand = try sema.resolveInst(inst_data.operand);
     const zir_block = extra.block_inst;
@@ -6264,7 +6270,7 @@ fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!voi
     // instructions.
     if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return;
 
-    const inst_data = sema.code.instructions.items(.data)[inst].dbg_stmt;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].dbg_stmt;
 
     if (block.instructions.items.len != 0) {
         const idx = block.instructions.items[block.instructions.items.len - 1];
@@ -6313,7 +6319,7 @@ fn zirDbgVar(
 ) CompileError!void {
     if (block.is_comptime or sema.mod.comp.bin_file.options.strip) return;
 
-    const str_op = sema.code.instructions.items(.data)[inst].str_op;
+    const str_op = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_op;
     const operand = try sema.resolveInst(str_op.operand);
     const name = str_op.getStr(sema.code);
     try sema.addDbgVar(block, operand, air_tag, name);
@@ -6358,7 +6364,7 @@ fn addDbgVar(
 
 fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
     const src = inst_data.src();
     const decl_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code));
     const decl_index = try sema.lookupIdentifier(block, src, decl_name);
@@ -6368,7 +6374,7 @@ fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
 
 fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
     const src = inst_data.src();
     const decl_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code));
     const decl = try sema.lookupIdentifier(block, src, decl_name);
@@ -6637,7 +6643,7 @@ fn zirCall(
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const callee_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node };
     const call_src = inst_data.src();
     const ExtraType = switch (kind) {
@@ -6685,12 +6691,12 @@ fn zirCall(
         .call_inst = inst,
         .call_node_offset = inst_data.src_node,
         .num_args = args_len,
-        .args_body = sema.code.extra[extra.end..],
+        .args_body = @ptrCast(sema.code.extra[extra.end..]),
         .any_arg_is_error = &input_is_error,
     } };
 
     // AstGen ensures that a call instruction is always preceded by a dbg_stmt instruction.
-    const call_dbg_node = inst - 1;
+    const call_dbg_node: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1);
     const call_inst = try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, args_info, call_dbg_node, .call);
 
     if (sema.owner_func_index == .none or
@@ -6961,11 +6967,11 @@ const CallArgsInfo = union(enum) {
 
                 const arg_body = if (real_arg_idx == 0) blk: {
                     const start = zir_call.num_args;
-                    const end = zir_call.args_body[0];
+                    const end = @intFromEnum(zir_call.args_body[0]);
                     break :blk zir_call.args_body[start..end];
                 } else blk: {
-                    const start = zir_call.args_body[real_arg_idx - 1];
-                    const end = zir_call.args_body[real_arg_idx];
+                    const start = @intFromEnum(zir_call.args_body[real_arg_idx - 1]);
+                    const end = @intFromEnum(zir_call.args_body[real_arg_idx]);
                     break :blk zir_call.args_body[start..end];
                 };
 
@@ -7447,9 +7453,9 @@ fn analyzeCall(
                 try sema.emitDbgInline(block, prev_fn_index, module_fn_index, new_func_resolved_ty, .dbg_inline_begin);
 
                 const zir_tags = sema.code.instructions.items(.tag);
-                for (fn_info.param_body) |param| switch (zir_tags[param]) {
+                for (fn_info.param_body) |param| switch (zir_tags[@intFromEnum(param)]) {
                     .param, .param_comptime => {
-                        const inst_data = sema.code.instructions.items(.data)[param].pl_tok;
+                        const inst_data = sema.code.instructions.items(.data)[@intFromEnum(param)].pl_tok;
                         const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index);
                         const param_name = sema.code.nullTerminatedString(extra.data.name);
                         const inst = sema.inst_map.get(param).?;
@@ -7457,7 +7463,7 @@ fn analyzeCall(
                         try sema.addDbgVar(&child_block, inst, .dbg_var_val, param_name);
                     },
                     .param_anytype, .param_anytype_comptime => {
-                        const inst_data = sema.code.instructions.items(.data)[param].str_tok;
+                        const inst_data = sema.code.instructions.items(.data)[@intFromEnum(param)].str_tok;
                         const param_name = inst_data.get(sema.code);
                         const inst = sema.inst_map.get(param).?;
 
@@ -7640,18 +7646,18 @@ fn analyzeInlineCallArg(
     const mod = ics.sema.mod;
     const ip = &mod.intern_pool;
     const zir_tags = ics.callee().code.instructions.items(.tag);
-    switch (zir_tags[inst]) {
+    switch (zir_tags[@intFromEnum(inst)]) {
         .param_comptime, .param_anytype_comptime => param_block.inlining.?.has_comptime_args = true,
         else => {},
     }
-    switch (zir_tags[inst]) {
+    switch (zir_tags[@intFromEnum(inst)]) {
         .param, .param_comptime => {
             // Evaluate the parameter type expression now that previous ones have
             // been mapped, and coerce the corresponding argument to it.
-            const pl_tok = ics.callee().code.instructions.items(.data)[inst].pl_tok;
+            const pl_tok = ics.callee().code.instructions.items(.data)[@intFromEnum(inst)].pl_tok;
             const param_src = pl_tok.src();
             const extra = ics.callee().code.extraData(Zir.Inst.Param, pl_tok.payload_index);
-            const param_body = ics.callee().code.extra[extra.end..][0..extra.data.body_len];
+            const param_body = ics.callee().code.bodySlice(extra.end, extra.data.body_len);
             const param_ty = param_ty: {
                 const raw_param_ty = func_ty_info.param_types.get(ip)[arg_i.*];
                 if (raw_param_ty != .generic_poison_type) break :param_ty raw_param_ty;
@@ -7670,7 +7676,7 @@ fn analyzeInlineCallArg(
                     .needed_comptime_reason = "argument to parameter with comptime-only type must be comptime-known",
                     .block_comptime_reason = param_block.comptime_reason,
                 });
-            } else if (!is_comptime_call and zir_tags[inst] == .param_comptime) {
+            } else if (!is_comptime_call and zir_tags[@intFromEnum(inst)] == .param_comptime) {
                 _ = try ics.caller().resolveConstValue(arg_block, arg_src, casted_arg, .{
                     .needed_comptime_reason = "parameter is comptime",
                 });
@@ -7736,7 +7742,7 @@ fn analyzeInlineCallArg(
                 should_memoize.* = should_memoize.* and !resolved_arg_val.canMutateComptimeVarState(mod);
                 memoized_arg_values[arg_i.*] = try resolved_arg_val.intern(ics.caller().typeOf(uncasted_arg), mod);
             } else {
-                if (zir_tags[inst] == .param_anytype_comptime) {
+                if (zir_tags[@intFromEnum(inst)] == .param_anytype_comptime) {
                     _ = try ics.caller().resolveConstValue(arg_block, arg_src, uncasted_arg, .{
                         .needed_comptime_reason = "parameter is comptime",
                     });
@@ -7864,7 +7870,7 @@ fn instantiateGenericCall(
     try child_sema.inst_map.ensureSpaceForInstructions(gpa, fn_info.param_body);
 
     for (fn_info.param_body[0..args_info.count()], 0..) |param_inst, arg_index| {
-        const param_tag = fn_zir.instructions.items(.tag)[param_inst];
+        const param_tag = fn_zir.instructions.items(.tag)[@intFromEnum(param_inst)];
 
         const param_ty = switch (generic_owner_ty_info.param_types.get(ip)[arg_index]) {
             else => |ty| ty.toType(), // parameter is not generic, so type is already resolved
@@ -7879,9 +7885,9 @@ fn instantiateGenericCall(
                     .param, .param_comptime => {
                         // We now know every prior parameter, so can resolve this
                         // parameter's type. The child sema has these types.
-                        const param_data = fn_zir.instructions.items(.data)[param_inst].pl_tok;
+                        const param_data = fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].pl_tok;
                         const param_extra = fn_zir.extraData(Zir.Inst.Param, param_data.payload_index);
-                        const param_ty_body = fn_zir.extra[param_extra.end..][0..param_extra.data.body_len];
+                        const param_ty_body = fn_zir.bodySlice(param_extra.end, param_extra.data.body_len);
 
                         // Make sure any nested instructions don't clobber our work.
                         const prev_params = child_block.params;
@@ -7937,8 +7943,8 @@ fn instantiateGenericCall(
                     const msg = try sema.errMsg(block, arg_src, "runtime-known argument passed to comptime parameter", .{});
                     errdefer msg.destroy(sema.gpa);
                     const param_src = switch (param_tag) {
-                        .param_comptime => fn_zir.instructions.items(.data)[param_inst].pl_tok.src(),
-                        .param_anytype_comptime => fn_zir.instructions.items(.data)[param_inst].str_tok.src(),
+                        .param_comptime => fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].pl_tok.src(),
+                        .param_anytype_comptime => fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].str_tok.src(),
                         else => unreachable,
                     };
                     try child_sema.errNote(&child_block, param_src, msg, "declared comptime here", .{});
@@ -7952,8 +7958,8 @@ fn instantiateGenericCall(
                     const msg = try sema.errMsg(block, arg_src, "runtime-known argument passed to parameter of comptime-only type", .{});
                     errdefer msg.destroy(sema.gpa);
                     const param_src = switch (param_tag) {
-                        .param => fn_zir.instructions.items(.data)[param_inst].pl_tok.src(),
-                        .param_anytype => fn_zir.instructions.items(.data)[param_inst].str_tok.src(),
+                        .param => fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].pl_tok.src(),
+                        .param_anytype => fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].str_tok.src(),
                         else => unreachable,
                     };
                     try child_sema.errNote(&child_block, param_src, msg, "declared here", .{});
@@ -7975,9 +7981,9 @@ fn instantiateGenericCall(
                 } },
             }));
             const param_name: Zir.NullTerminatedString = switch (param_tag) {
-                .param_anytype => @enumFromInt(fn_zir.instructions.items(.data)[param_inst].str_tok.start),
+                .param_anytype => @enumFromInt(fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].str_tok.start),
                 .param => name: {
-                    const inst_data = fn_zir.instructions.items(.data)[param_inst].pl_tok;
+                    const inst_data = fn_zir.instructions.items(.data)[@intFromEnum(param_inst)].pl_tok;
                     const extra = fn_zir.extraData(Zir.Inst.Param, inst_data.payload_index);
                     break :name @enumFromInt(extra.data.name);
                 },
@@ -8094,7 +8100,7 @@ fn emitDbgInline(
 
 fn zirIntType(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const int_type = sema.code.instructions.items(.data)[inst].int_type;
+    const int_type = sema.code.instructions.items(.data)[@intFromEnum(inst)].int_type;
     const ty = try mod.intType(int_type.signedness, int_type.bit_count);
     return Air.internedToRef(ty.toIntern());
 }
@@ -8104,7 +8110,7 @@ fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node };
     const child_type = try sema.resolveType(block, operand_src, inst_data.operand);
     if (child_type.zigTypeTag(mod) == .Opaque) {
@@ -8119,7 +8125,7 @@ fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
 
 fn zirArrayInitElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const bin = sema.code.instructions.items(.data)[inst].bin;
+    const bin = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin;
     const maybe_wrapped_indexable_ty = sema.resolveType(block, .unneeded, bin.lhs) catch |err| switch (err) {
         // Since this is a ZIR instruction that returns a type, encountering
         // generic poison should not result in a failed compilation, but the
@@ -8142,7 +8148,7 @@ fn zirArrayInitElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil
 
 fn zirElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const un_node = sema.code.instructions.items(.data)[inst].un_node;
+    const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const maybe_wrapped_ptr_ty = sema.resolveType(block, .unneeded, un_node.operand) catch |err| switch (err) {
         error.GenericPoison => return .generic_poison_type,
         else => |e| return e,
@@ -8160,7 +8166,7 @@ fn zirElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
 
 fn zirIndexablePtrElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const un_node = sema.code.instructions.items(.data)[inst].un_node;
+    const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = un_node.src();
     const ptr_ty = sema.resolveType(block, src, un_node.operand) catch |err| switch (err) {
         error.GenericPoison => return .generic_poison_type,
@@ -8176,7 +8182,7 @@ fn zirIndexablePtrElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Com
 
 fn zirVectorElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const un_node = sema.code.instructions.items(.data)[inst].un_node;
+    const un_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const vec_ty = sema.resolveType(block, .unneeded, un_node.operand) catch |err| switch (err) {
         // Since this is a ZIR instruction that returns a type, encountering
         // generic poison should not result in a failed compilation, but the
@@ -8193,7 +8199,7 @@ fn zirVectorElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
 
 fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const elem_type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
@@ -8213,7 +8219,7 @@ fn zirArrayType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const len_src: LazySrcLoc = .{ .node_offset_array_type_len = inst_data.src_node };
     const elem_src: LazySrcLoc = .{ .node_offset_array_type_elem = inst_data.src_node };
@@ -8234,7 +8240,7 @@ fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compil
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data;
     const len_src: LazySrcLoc = .{ .node_offset_array_type_len = inst_data.src_node };
     const sentinel_src: LazySrcLoc = .{ .node_offset_array_type_sentinel = inst_data.src_node };
@@ -8271,7 +8277,7 @@ fn zirAnyframeType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     if (true) {
         return sema.failWithUseOfAsync(block, inst_data.src());
     }
@@ -8288,7 +8294,7 @@ fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
@@ -8321,7 +8327,7 @@ fn validateErrorUnionPayloadType(sema: *Sema, block: *Block, payload_ty: Type, p
 fn zirErrorValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     _ = block;
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
     const name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code));
     _ = try mod.getErrorValue(name);
     // Create an error set type with only this error value, and return the value.
@@ -8420,7 +8426,7 @@ fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
 
     const mod = sema.mod;
     const ip = &mod.intern_pool;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
@@ -8475,7 +8481,7 @@ fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
     const name = inst_data.get(sema.code);
     return Air.internedToRef((try mod.intern(.{
         .enum_literal = try mod.intern_pool.getOrPutString(sema.gpa, name),
@@ -8484,7 +8490,7 @@ fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
 
 fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const operand = try sema.resolveInst(inst_data.operand);
@@ -8529,7 +8535,7 @@ fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
 
 fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const src = inst_data.src();
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
@@ -8610,7 +8616,7 @@ fn zirOptionalPayloadPtr(
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const optional_ptr = try sema.resolveInst(inst_data.operand);
     const src = inst_data.src();
 
@@ -8695,7 +8701,7 @@ fn zirOptionalPayload(
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const operand = try sema.resolveInst(inst_data.operand);
     const operand_ty = sema.typeOf(operand);
@@ -8747,7 +8753,7 @@ fn zirErrUnionPayload(
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const operand = try sema.resolveInst(inst_data.operand);
     const operand_src = src;
@@ -8799,7 +8805,7 @@ fn zirErrUnionPayloadPtr(
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const operand = try sema.resolveInst(inst_data.operand);
     const src = inst_data.src();
 
@@ -8883,7 +8889,7 @@ fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const operand = try sema.resolveInst(inst_data.operand);
     return sema.analyzeErrUnionCode(block, src, operand);
@@ -8917,7 +8923,7 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const operand = try sema.resolveInst(inst_data.operand);
     const operand_ty = sema.typeOf(operand);
@@ -8949,7 +8955,7 @@ fn zirFunc(
     inferred_error_set: bool,
 ) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index);
     const target = sema.mod.getTarget();
     const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = inst_data.src_node };
@@ -8971,7 +8977,7 @@ fn zirFunc(
             }
         },
         else => blk: {
-            const ret_ty_body = sema.code.extra[extra_index..][0..extra.data.ret_body_len];
+            const ret_ty_body = sema.code.bodySlice(extra_index, extra.data.ret_body_len);
             extra_index += ret_ty_body.len;
 
             const ret_ty_val = try sema.resolveGenericBody(block, ret_ty_src, ret_ty_body, inst, Type.type, .{
@@ -9591,9 +9597,9 @@ fn finishFunc(
             param_body[0..block.params.len],
         ) |is_comptime, name_nts, param_index| {
             if (!is_comptime) {
-                const param_src = switch (tags[param_index]) {
-                    .param => data[param_index].pl_tok.src(),
-                    .param_anytype => data[param_index].str_tok.src(),
+                const param_src = switch (tags[@intFromEnum(param_index)]) {
+                    .param => data[@intFromEnum(param_index)].pl_tok.src(),
+                    .param_anytype => data[@intFromEnum(param_index)].str_tok.src(),
                     else => unreachable,
                 };
                 const name = sema.code.nullTerminatedString2(name_nts);
@@ -9667,11 +9673,11 @@ fn zirParam(
     inst: Zir.Inst.Index,
     comptime_syntax: bool,
 ) CompileError!void {
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_tok;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_tok;
     const src = inst_data.src();
     const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index);
     const param_name: Zir.NullTerminatedString = @enumFromInt(extra.data.name);
-    const body = sema.code.extra[extra.end..][0..extra.data.body_len];
+    const body = sema.code.bodySlice(extra.end, extra.data.body_len);
 
     const param_ty = param_ty: {
         const err = err: {
@@ -9760,7 +9766,7 @@ fn zirParamAnytype(
     inst: Zir.Inst.Index,
     comptime_syntax: bool,
 ) CompileError!void {
-    const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
     const param_name: Zir.NullTerminatedString = @enumFromInt(inst_data.start);
 
     // We are evaluating a generic function without any comptime args provided.
@@ -9777,7 +9783,7 @@ fn zirAs(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst
     const tracy = trace(@src());
     defer tracy.end();
 
-    const bin_inst = sema.code.instructions.items(.data)[inst].bin;
+    const bin_inst = sema.code.instructions.items(.data)[@intFromEnum(inst)].bin;
     return sema.analyzeAs(block, sema.src, bin_inst.lhs, bin_inst.rhs, false);
 }
 
@@ -9785,7 +9791,7 @@ fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
     sema.src = src;
@@ -9796,7 +9802,7 @@ fn zirAsShiftOperand(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data;
     return sema.analyzeAs(block, src, extra.dest_type, extra.operand, true);
@@ -9828,8 +9834,8 @@ fn analyzeAs(
     if (dest_ty_tag == .NoReturn) {
         return sema.fail(block, src, "cannot cast to noreturn", .{});
     }
-    const is_ret = if (Zir.refToIndex(zir_dest_type)) |ptr_index|
-        sema.code.instructions.items(.tag)[ptr_index] == .ret_type
+    const is_ret = if (zir_dest_type.toIndex()) |ptr_index|
+        sema.code.instructions.items(.tag)[@intFromEnum(ptr_index)] == .ret_type
     else
         false;
     return sema.coerceExtra(block, dest_ty, operand, src, .{ .is_ret = is_ret, .no_cast_to_comptime_int = no_cast_to_comptime_int }) catch |err| switch (err) {
@@ -9843,7 +9849,7 @@ fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const operand = try sema.resolveInst(inst_data.operand);
     const operand_ty = sema.typeOf(operand);
@@ -9909,7 +9915,7 @@ fn zirFieldVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
     const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
@@ -9923,7 +9929,7 @@ fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
     const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
@@ -9937,7 +9943,7 @@ fn zirStructInitFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compi
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
     const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data;
@@ -9958,7 +9964,7 @@ fn zirFieldValNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
     const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
@@ -9973,7 +9979,7 @@ fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
     const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data;
@@ -9988,7 +9994,7 @@ fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
@@ -10149,7 +10155,7 @@ fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
@@ -10292,7 +10298,7 @@ fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
@@ -10371,7 +10377,7 @@ fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const array = try sema.resolveInst(extra.lhs);
@@ -10383,7 +10389,7 @@ fn zirElemValNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node };
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
@@ -10397,7 +10403,7 @@ fn zirElemValImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].elem_val_imm;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].elem_val_imm;
     const array = try sema.resolveInst(inst_data.operand);
     const elem_index = try mod.intRef(Type.usize, inst_data.idx);
     return sema.elemVal(block, .unneeded, array, elem_index, .unneeded, false);
@@ -10408,7 +10414,7 @@ fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const array_ptr = try sema.resolveInst(extra.lhs);
@@ -10435,7 +10441,7 @@ fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node };
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
@@ -10449,7 +10455,7 @@ fn zirArrayInitElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) Compile
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data;
     const array_ptr = try sema.resolveInst(extra.ptr);
@@ -10468,7 +10474,7 @@ fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data;
     const array_ptr = try sema.resolveInst(extra.lhs);
@@ -10484,7 +10490,7 @@ fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const extra = sema.code.extraData(Zir.Inst.SliceEnd, inst_data.payload_index).data;
     const array_ptr = try sema.resolveInst(extra.lhs);
@@ -10501,7 +10507,7 @@ fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const sentinel_src: LazySrcLoc = .{ .node_offset_slice_sentinel = inst_data.src_node };
     const extra = sema.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data;
@@ -10520,7 +10526,7 @@ fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const extra = sema.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data;
     const array_ptr = try sema.resolveInst(extra.lhs);
@@ -10581,7 +10587,7 @@ const SwitchProngAnalysis = struct {
         merges: *Block.Merges,
     ) CompileError!Air.Inst.Ref {
         const sema = spa.sema;
-        const src = sema.code.instructions.items(.data)[spa.switch_block_inst].pl_node.src();
+        const src = sema.code.instructions.items(.data)[@intFromEnum(spa.switch_block_inst)].pl_node.src();
 
         if (has_tag_capture) {
             const tag_ref = try spa.analyzeTagCapture(child_block, raw_capture_src, inline_case_capture);
@@ -10684,7 +10690,7 @@ const SwitchProngAnalysis = struct {
         const operand_ty = sema.typeOf(spa.operand);
         if (operand_ty.zigTypeTag(mod) != .Union) {
             const zir_datas = sema.code.instructions.items(.data);
-            const switch_node_offset = zir_datas[spa.switch_block_inst].pl_node.src_node;
+            const switch_node_offset = zir_datas[@intFromEnum(spa.switch_block_inst)].pl_node.src_node;
             const raw_tag_capture_src: Module.SwitchProngSrc = switch (raw_capture_src) {
                 .scalar_capture => |i| .{ .scalar_tag_capture = i },
                 .multi_capture => |i| .{ .multi_tag_capture = i },
@@ -10720,7 +10726,7 @@ const SwitchProngAnalysis = struct {
         const ip = &mod.intern_pool;
 
         const zir_datas = sema.code.instructions.items(.data);
-        const switch_node_offset = zir_datas[spa.switch_block_inst].pl_node.src_node;
+        const switch_node_offset = zir_datas[@intFromEnum(spa.switch_block_inst)].pl_node.src_node;
 
         const operand_ty = sema.typeOf(spa.operand);
         const operand_ptr_ty = if (capture_byref) sema.typeOf(spa.operand_ptr) else undefined;
@@ -11146,7 +11152,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
     const mod = sema.mod;
     const gpa = sema.gpa;
     const ip = &mod.intern_pool;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const src_node_offset = inst_data.src_node;
     const operand_src: LazySrcLoc = .{ .node_offset_switch_operand = src_node_offset };
@@ -11167,7 +11173,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
 
     // AstGen guarantees that the instruction immediately preceding
     // switch_block(_ref) is a dbg_stmt
-    const cond_dbg_node_index = inst - 1;
+    const cond_dbg_node_index: Zir.Inst.Index = @enumFromInt(@intFromEnum(inst) - 1);
 
     var header_extra_index: usize = extra.end;
 
@@ -11179,7 +11185,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
     } else 0;
 
     const tag_capture_inst: Zir.Inst.Index = if (extra.data.bits.any_has_tag_capture) blk: {
-        const tag_capture_inst = sema.code.extra[header_extra_index];
+        const tag_capture_inst: Zir.Inst.Index = @enumFromInt(sema.code.extra[header_extra_index]);
         header_extra_index += 1;
         // SwitchProngAnalysis wants inst_map to have space for the tag capture.
         // Note that the normal capture is referred to via the switch block
@@ -11212,7 +11218,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
             const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[header_extra_index]);
             const extra_body_start = header_extra_index + 1;
             break :blk .{
-                .body = sema.code.extra[extra_body_start..][0..info.body_len],
+                .body = sema.code.bodySlice(extra_body_start, info.body_len),
                 .end = extra_body_start + info.body_len,
                 .capture = info.capture,
                 .is_inline = info.is_inline,
@@ -11482,7 +11488,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
                         // else => |e| return e,
                         // even if all the possible errors were already handled.
                         const tags = sema.code.instructions.items(.tag);
-                        for (special.body) |else_inst| switch (tags[else_inst]) {
+                        for (special.body) |else_inst| switch (tags[@intFromEnum(else_inst)]) {
                             .dbg_block_begin,
                             .dbg_block_end,
                             .dbg_stmt,
@@ -11826,7 +11832,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
                 extra_index += 1;
                 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
                 extra_index += 1;
-                const body = sema.code.extra[extra_index..][0..info.body_len];
+                const body = sema.code.bodySlice(extra_index, info.body_len);
                 extra_index += info.body_len;
 
                 const item = case_vals.items[scalar_i];
@@ -11857,7 +11863,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
                 extra_index += 1;
                 const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
                 extra_index += 1 + items_len;
-                const body = sema.code.extra[extra_index + 2 * ranges_len ..][0..info.body_len];
+                const body = sema.code.bodySlice(extra_index + 2 * ranges_len, info.body_len);
 
                 const items = case_vals.items[case_val_idx..][0..items_len];
                 case_val_idx += items_len;
@@ -11986,7 +11992,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
         extra_index += 1;
         const info: Zir.Inst.SwitchBlock.ProngInfo = @bitCast(sema.code.extra[extra_index]);
         extra_index += 1;
-        const body = sema.code.extra[extra_index..][0..info.body_len];
+        const body = sema.code.bodySlice(extra_index, info.body_len);
         extra_index += info.body_len;
 
         case_block.instructions.shrinkRetainingCapacity(0);
@@ -12053,7 +12059,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
         // Generate all possible cases as scalar prongs.
         if (info.is_inline) {
             const body_start = extra_index + 2 * ranges_len;
-            const body = sema.code.extra[body_start..][0..info.body_len];
+            const body = sema.code.bodySlice(body_start, info.body_len);
             var emit_bb = false;
 
             var range_i: u32 = 0;
@@ -12184,7 +12190,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
             else
                 true;
 
-            const body = sema.code.extra[extra_index..][0..info.body_len];
+            const body = sema.code.bodySlice(extra_index, info.body_len);
             extra_index += info.body_len;
             if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src)) {
                 // nothing to do here
@@ -12268,7 +12274,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
             case_block.instructions.shrinkRetainingCapacity(0);
             case_block.wip_capture_scope = try mod.createCaptureScope(child_block.wip_capture_scope);
 
-            const body = sema.code.extra[extra_index..][0..info.body_len];
+            const body = sema.code.bodySlice(extra_index, info.body_len);
             extra_index += info.body_len;
             if (err_set and try sema.maybeErrorUnwrap(&case_block, body, operand, operand_src)) {
                 // nothing to do here
@@ -12493,8 +12499,10 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index, operand_is_r
         case_block.instructions.shrinkRetainingCapacity(0);
         case_block.wip_capture_scope = try mod.createCaptureScope(child_block.wip_capture_scope);
 
-        if (mod.backendSupportsFeature(.is_named_enum_value) and special.body.len != 0 and block.wantSafety() and
-            operand_ty.zigTypeTag(mod) == .Enum and (!operand_ty.isNonexhaustiveEnum(mod) or union_originally))
+        if (mod.backendSupportsFeature(.is_named_enum_value) and
+            special.body.len != 0 and block.wantSafety() and
+            operand_ty.zigTypeTag(mod) == .Enum and
+            (!operand_ty.isNonexhaustiveEnum(mod) or union_originally))
         {
             try sema.zirDbgStmt(&case_block, cond_dbg_node_index);
             const ok = try case_block.addUnOp(.is_named_enum_value, operand);
@@ -12879,7 +12887,7 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op
 
     const tags = sema.code.instructions.items(.tag);
     for (body) |inst| {
-        switch (tags[inst]) {
+        switch (tags[@intFromEnum(inst)]) {
             .@"unreachable" => if (!block.wantSafety()) return false,
             .save_err_ret_index,
             .dbg_block_begin,
@@ -12895,7 +12903,7 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op
     }
 
     for (body) |inst| {
-        const air_inst = switch (tags[inst]) {
+        const air_inst = switch (tags[@intFromEnum(inst)]) {
             .dbg_block_begin,
             .dbg_block_end,
             => continue,
@@ -12923,7 +12931,7 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op
                 return true;
             },
             .panic => {
-                const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+                const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
                 const msg_inst = try sema.resolveInst(inst_data.operand);
 
                 const panic_fn = try sema.getBuiltin("panic");
@@ -12943,10 +12951,10 @@ fn maybeErrorUnwrap(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, op
 
 fn maybeErrorUnwrapCondbr(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, cond: Zir.Inst.Ref, cond_src: LazySrcLoc) !void {
     const mod = sema.mod;
-    const index = Zir.refToIndex(cond) orelse return;
-    if (sema.code.instructions.items(.tag)[index] != .is_non_err) return;
+    const index = cond.toIndex() orelse return;
+    if (sema.code.instructions.items(.tag)[@intFromEnum(index)] != .is_non_err) return;
 
-    const err_inst_data = sema.code.instructions.items(.data)[index].un_node;
+    const err_inst_data = sema.code.instructions.items(.data)[@intFromEnum(index)].un_node;
     const err_operand = try sema.resolveInst(err_inst_data.operand);
     const operand_ty = sema.typeOf(err_operand);
     if (operand_ty.zigTypeTag(mod) == .ErrorSet) {
@@ -12963,7 +12971,7 @@ fn maybeErrorUnwrapCondbr(sema: *Sema, block: *Block, body: []const Zir.Inst.Ind
 fn maybeErrorUnwrapComptime(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref) !void {
     const tags = sema.code.instructions.items(.tag);
     const inst = for (body) |inst| {
-        switch (tags[inst]) {
+        switch (tags[@intFromEnum(inst)]) {
             .dbg_block_begin,
             .dbg_block_end,
             .dbg_stmt,
@@ -12973,7 +12981,7 @@ fn maybeErrorUnwrapComptime(sema: *Sema, block: *Block, body: []const Zir.Inst.I
             else => return,
         }
     } else return;
-    const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable";
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"unreachable";
     const src = inst_data.src();
 
     if (try sema.resolveDefinedValue(block, src, operand)) |val| {
@@ -12985,7 +12993,7 @@ fn maybeErrorUnwrapComptime(sema: *Sema, block: *Block, body: []const Zir.Inst.I
 
 fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
@@ -13036,7 +13044,7 @@ fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
 
 fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const src = inst_data.src();
     const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
@@ -13064,7 +13072,7 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
     const operand_src = inst_data.src();
     const operand = inst_data.get(sema.code);
 
@@ -13093,7 +13101,7 @@ fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const name = try sema.resolveConstString(block, operand_src, inst_data.operand, .{
         .needed_comptime_reason = "file path name must be comptime-known",
@@ -13120,7 +13128,7 @@ fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
 
 fn zirRetErrValueCode(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
     const name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code));
     _ = try mod.getErrorValue(name);
     const error_set_type = try mod.singleErrorSetType(name);
@@ -13140,7 +13148,7 @@ fn zirShl(
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     sema.src = src;
     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
@@ -13323,7 +13331,7 @@ fn zirShr(
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     sema.src = src;
     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
@@ -13459,7 +13467,7 @@ fn zirBitwise(
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
     sema.src = src;
     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
@@ -13514,7 +13522,7 @@ fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node };
 
@@ -13648,7 +13656,7 @@ fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const lhs = try sema.resolveInst(extra.lhs);
     const rhs = try sema.resolveInst(extra.rhs);
@@ -13977,7 +13985,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const lhs = try sema.resolveInst(extra.lhs);
     const lhs_ty = sema.typeOf(lhs);
@@ -14116,7 +14124,7 @@ fn zirArrayMul(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
 
 fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const lhs_src = src;
     const rhs_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node };
@@ -14148,7 +14156,7 @@ fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
 
 fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const lhs_src = src;
     const rhs_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node };
@@ -14176,7 +14184,7 @@ fn zirArithmetic(
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     sema.src = .{ .node_offset_bin_op = inst_data.src_node };
     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
     const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
@@ -14189,7 +14197,7 @@ fn zirArithmetic(
 
 fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
     sema.src = src;
     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
@@ -14355,7 +14363,7 @@ fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
 
 fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
     sema.src = src;
     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
@@ -14521,7 +14529,7 @@ fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
 
 fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
     sema.src = src;
     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
@@ -14632,7 +14640,7 @@ fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
 
 fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
     sema.src = src;
     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
@@ -14874,7 +14882,7 @@ fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst
 
 fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
     sema.src = src;
     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
@@ -15059,7 +15067,7 @@ fn intRemScalar(sema: *Sema, lhs: Value, rhs: Value, scalar_ty: Type) CompileErr
 
 fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
     sema.src = src;
     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
@@ -15155,7 +15163,7 @@ fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Ins
 
 fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
     sema.src = src;
     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
@@ -16071,7 +16079,7 @@ fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.In
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const ptr_src = src; // TODO better source location
     const ptr = try sema.resolveInst(inst_data.operand);
@@ -16251,7 +16259,7 @@ fn zirCmpEq(
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const src: LazySrcLoc = inst_data.src();
     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
@@ -16367,7 +16375,7 @@ fn zirCmp(
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const src: LazySrcLoc = inst_data.src();
     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
@@ -16512,7 +16520,7 @@ fn runtimeBoolCmp(
 
 fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const ty = try sema.resolveType(block, operand_src, inst_data.operand);
     switch (ty.zigTypeTag(mod)) {
@@ -16555,7 +16563,7 @@ fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
 
 fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand);
     switch (operand_ty.zigTypeTag(mod)) {
@@ -16607,7 +16615,7 @@ fn zirThis(
 fn zirClosureCapture(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
     const mod = sema.mod;
     const gpa = sema.gpa;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok;
     // Closures are not necessarily constant values. For example, the
     // code might do something like this:
     // fn foo(x: anytype) void { const S = struct {field: @TypeOf(x)}; }
@@ -16629,7 +16637,7 @@ fn zirClosureCapture(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileEr
 fn zirClosureGet(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
     //const ip = &mod.intern_pool;
-    const inst_data = sema.code.instructions.items(.data)[inst].inst_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].inst_node;
     var scope: CaptureScope.Index = mod.declPtr(block.src_decl).src_scope;
     assert(scope != .none);
     // Note: The target closure must be in this scope list.
@@ -16818,7 +16826,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     const mod = sema.mod;
     const gpa = sema.gpa;
     const ip = &mod.intern_pool;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const ty = try sema.resolveType(block, src, inst_data.operand);
     const type_info_ty = try sema.getBuiltinType("Type");
@@ -17923,16 +17931,16 @@ fn typeInfoNamespaceDecls(
 fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     _ = block;
     const zir_datas = sema.code.instructions.items(.data);
-    const inst_data = zir_datas[inst].un_node;
+    const inst_data = zir_datas[@intFromEnum(inst)].un_node;
     const operand = try sema.resolveInst(inst_data.operand);
     const operand_ty = sema.typeOf(operand);
     return Air.internedToRef(operand_ty.toIntern());
 }
 
 fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const pl_node = sema.code.instructions.items(.data)[inst].pl_node;
+    const pl_node = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index);
-    const body = sema.code.extra[extra.end..][0..extra.data.body_len];
+    const body = sema.code.bodySlice(extra.end, extra.data.body_len);
 
     var child_block: Block = .{
         .parent = block,
@@ -17956,7 +17964,7 @@ fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
 }
 
 fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const operand = try sema.resolveInst(inst_data.operand);
     const operand_ty = sema.typeOf(operand);
@@ -18010,7 +18018,7 @@ fn zirTypeofPeer(
 
     const extra = sema.code.extraData(Zir.Inst.TypeOfPeer, extended.operand);
     const src = LazySrcLoc.nodeOffset(extra.data.src_node);
-    const body = sema.code.extra[extra.data.body_index..][0..extra.data.body_len];
+    const body = sema.code.bodySlice(extra.data.body_index, extra.data.body_len);
 
     var child_block: Block = .{
         .parent = block,
@@ -18048,7 +18056,7 @@ fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const operand_src: LazySrcLoc = .{ .node_offset_un_op = inst_data.src_node };
     const uncasted_operand = try sema.resolveInst(inst_data.operand);
@@ -18074,11 +18082,11 @@ fn zirBoolBr(
 
     const mod = sema.mod;
     const datas = sema.code.instructions.items(.data);
-    const inst_data = datas[inst].bool_br;
+    const inst_data = datas[@intFromEnum(inst)].bool_br;
     const lhs = try sema.resolveInst(inst_data.lhs);
     const lhs_src = sema.src;
     const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index);
-    const body = sema.code.extra[extra.end..][0..extra.data.body_len];
+    const body = sema.code.bodySlice(extra.end, extra.data.body_len);
     const gpa = sema.gpa;
 
     if (try sema.resolveDefinedValue(parent_block, lhs_src, lhs)) |lhs_val| {
@@ -18193,7 +18201,7 @@ fn zirIsNonNull(
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const operand = try sema.resolveInst(inst_data.operand);
     try sema.checkNullableType(block, src, sema.typeOf(operand));
@@ -18209,7 +18217,7 @@ fn zirIsNonNullPtr(
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const ptr = try sema.resolveInst(inst_data.operand);
     try sema.checkNullableType(block, src, sema.typeOf(ptr).elemType2(mod));
@@ -18234,7 +18242,7 @@ fn zirIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const operand = try sema.resolveInst(inst_data.operand);
     try sema.checkErrorType(block, src, sema.typeOf(operand));
@@ -18246,7 +18254,7 @@ fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const ptr = try sema.resolveInst(inst_data.operand);
     try sema.checkErrorType(block, src, sema.typeOf(ptr).elemType2(mod));
@@ -18258,7 +18266,7 @@ fn zirRetIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const operand = try sema.resolveInst(inst_data.operand);
     return sema.analyzeIsNonErr(block, src, operand);
@@ -18273,12 +18281,12 @@ fn zirCondbr(
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
     const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index);
 
-    const then_body = sema.code.extra[extra.end..][0..extra.data.then_body_len];
-    const else_body = sema.code.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
+    const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len);
+    const else_body = sema.code.bodySlice(extra.end + then_body.len, extra.data.else_body_len);
 
     const uncasted_cond = try sema.resolveInst(extra.data.condition);
     const cond = try sema.coerce(parent_block, Type.bool, uncasted_cond, cond_src);
@@ -18307,10 +18315,10 @@ fn zirCondbr(
     defer gpa.free(true_instructions);
 
     const err_cond = blk: {
-        const index = Zir.refToIndex(extra.data.condition) orelse break :blk null;
-        if (sema.code.instructions.items(.tag)[index] != .is_non_err) break :blk null;
+        const index = extra.data.condition.toIndex() orelse break :blk null;
+        if (sema.code.instructions.items(.tag)[@intFromEnum(index)] != .is_non_err) break :blk null;
 
-        const err_inst_data = sema.code.instructions.items(.data)[index].un_node;
+        const err_inst_data = sema.code.instructions.items(.data)[@intFromEnum(index)].un_node;
         const err_operand = try sema.resolveInst(err_inst_data.operand);
         const operand_ty = sema.typeOf(err_operand);
         assert(operand_ty.zigTypeTag(mod) == .ErrorUnion);
@@ -18341,11 +18349,11 @@ fn zirCondbr(
 }
 
 fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
     const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
-    const body = sema.code.extra[extra.end..][0..extra.data.body_len];
+    const body = sema.code.bodySlice(extra.end, extra.data.body_len);
     const err_union = try sema.resolveInst(extra.data.operand);
     const err_union_ty = sema.typeOf(err_union);
     const mod = sema.mod;
@@ -18387,11 +18395,11 @@ fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!
 }
 
 fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
     const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index);
-    const body = sema.code.extra[extra.end..][0..extra.data.body_len];
+    const body = sema.code.bodySlice(extra.end, extra.data.body_len);
     const operand = try sema.resolveInst(extra.data.operand);
     const err_union = try sema.analyzeLoad(parent_block, src, operand, operand_src);
     const err_union_ty = sema.typeOf(err_union);
@@ -18503,7 +18511,7 @@ fn addRuntimeBreak(sema: *Sema, child_block: *Block, break_data: BreakData) !voi
 }
 
 fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index {
-    const inst_data = sema.code.instructions.items(.data)[inst].@"unreachable";
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].@"unreachable";
     const src = inst_data.src();
 
     if (block.is_comptime) {
@@ -18528,7 +18536,7 @@ fn zirRetErrValue(
     inst: Zir.Inst.Index,
 ) CompileError!Zir.Inst.Index {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].str_tok;
     const err_name = try mod.intern_pool.getOrPutString(sema.gpa, inst_data.get(sema.code));
     _ = try mod.getErrorValue(err_name);
     const src = inst_data.src();
@@ -18550,7 +18558,7 @@ fn zirRetImplicit(
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_tok;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_tok;
     const r_brace_src = inst_data.src();
     if (block.inlining == null and sema.func_is_naked) {
         assert(!block.is_comptime);
@@ -18595,7 +18603,7 @@ fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const operand = try sema.resolveInst(inst_data.operand);
     const src = inst_data.src();
 
@@ -18606,7 +18614,7 @@ fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Zir
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const ret_ptr = try sema.resolveInst(inst_data.operand);
 
@@ -18693,7 +18701,7 @@ fn wantErrorReturnTracing(sema: *Sema, fn_ret_ty: Type) bool {
 
 fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].save_err_ret_index;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].save_err_ret_index;
 
     if (!mod.backendSupportsFeature(.error_return_trace)) return;
     if (!mod.comp.bin_file.options.error_return_tracing) return;
@@ -18712,7 +18720,7 @@ fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
 }
 
 fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void {
-    const inst_data = sema.code.instructions.items(.data)[inst].restore_err_ret_index;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].restore_err_ret_index;
     const src = sema.src; // TODO
 
     // This is only relevant at runtime.
@@ -18728,7 +18736,7 @@ fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index)
     const tracy = trace(@src());
     defer tracy.end();
 
-    const saved_index = if (Zir.refToIndexAllowNone(inst_data.block)) |zir_block| b: {
+    const saved_index = if (inst_data.block.toIndexAllowNone()) |zir_block| b: {
         var block = start_block;
         while (true) {
             if (block.label) |label| {
@@ -18858,7 +18866,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].ptr_type;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].ptr_type;
     const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index);
     const elem_ty_src: LazySrcLoc = .{ .node_offset_ptr_elem = extra.data.src_node };
     const sentinel_src: LazySrcLoc = .{ .node_offset_ptr_sentinel = extra.data.src_node };
@@ -18998,7 +19006,7 @@ fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE
     const tracy = trace(@src());
     defer tracy.end();
 
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const obj_ty = try sema.resolveType(block, src, inst_data.operand);
     const mod = sema.mod;
@@ -19017,7 +19025,7 @@ fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const ty_operand = sema.resolveType(block, src, inst_data.operand) catch |err| switch (err) {
         // Generic poison means this is an untyped anonymous empty struct init
@@ -19092,7 +19100,7 @@ fn arrayInitEmpty(sema: *Sema, block: *Block, src: LazySrcLoc, obj_ty: Type) Com
 }
 
 fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const field_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
     const init_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
@@ -19154,14 +19162,14 @@ fn zirStructInit(
 ) CompileError!Air.Inst.Ref {
     const gpa = sema.gpa;
     const zir_datas = sema.code.instructions.items(.data);
-    const inst_data = zir_datas[inst].pl_node;
+    const inst_data = zir_datas[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.StructInit, inst_data.payload_index);
     const src = inst_data.src();
 
     const mod = sema.mod;
     const ip = &mod.intern_pool;
     const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data;
-    const first_field_type_data = zir_datas[first_item.field_type].pl_node;
+    const first_field_type_data = zir_datas[@intFromEnum(first_item.field_type)].pl_node;
     const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data;
     const result_ty = sema.resolveType(block, src, first_field_type_extra.container_type) catch |err| switch (err) {
         error.GenericPoison => {
@@ -19194,7 +19202,7 @@ fn zirStructInit(
             const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index);
             extra_index = item.end;
 
-            const field_type_data = zir_datas[item.data.field_type].pl_node;
+            const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node;
             const field_src: LazySrcLoc = .{ .node_offset_initializer = field_type_data.src_node };
             const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
             const field_name = try ip.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start));
@@ -19204,7 +19212,7 @@ fn zirStructInit(
                 try sema.structFieldIndex(block, resolved_ty, field_name, field_src);
             if (field_inits[field_index] != .none) {
                 const other_field_type = found_fields[field_index];
-                const other_field_type_data = zir_datas[other_field_type].pl_node;
+                const other_field_type_data = zir_datas[@intFromEnum(other_field_type)].pl_node;
                 const other_field_src: LazySrcLoc = .{ .node_offset_initializer = other_field_type_data.src_node };
                 const msg = msg: {
                     const msg = try sema.errMsg(block, field_src, "duplicate field", .{});
@@ -19239,7 +19247,7 @@ fn zirStructInit(
 
         const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end);
 
-        const field_type_data = zir_datas[item.data.field_type].pl_node;
+        const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node;
         const field_src: LazySrcLoc = .{ .node_offset_initializer = field_type_data.src_node };
         const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data;
         const field_name = try ip.getOrPutString(gpa, sema.code.nullTerminatedString(field_type_extra.name_start));
@@ -19481,7 +19489,7 @@ fn zirStructInitAnon(
     block: *Block,
     inst: Zir.Inst.Index,
 ) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index);
     return sema.structInitAnon(block, src, .anon_init, extra.data, extra.end, false);
@@ -19528,7 +19536,7 @@ fn structInitAnon(
                 .anon_init => sema.code.nullTerminatedString(item.data.field_name),
                 .typed_init => name: {
                     // `item.data.field_type` references a `field_type` instruction
-                    const field_type_data = zir_datas[item.data.field_type].pl_node;
+                    const field_type_data = zir_datas[@intFromEnum(item.data.field_type)].pl_node;
                     const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index);
                     break :name sema.code.nullTerminatedString(field_type_extra.data.name_start);
                 },
@@ -19650,7 +19658,7 @@ fn zirArrayInit(
 ) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
     const gpa = sema.gpa;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
 
     const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
@@ -19813,7 +19821,7 @@ fn zirArrayInitAnon(
     block: *Block,
     inst: Zir.Inst.Index,
 ) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index);
     const operands = sema.code.refSlice(extra.end, extra.data.operands_len);
@@ -19911,7 +19919,7 @@ fn addConstantMaybeRef(sema: *Sema, val: InternPool.Index, is_ref: bool) !Air.In
 }
 
 fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.FieldTypeRef, inst_data.payload_index).data;
     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const field_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
@@ -19925,7 +19933,7 @@ fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
 fn zirStructInitFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
     const ip = &mod.intern_pool;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data;
     const ty_src = inst_data.src();
     const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node };
@@ -20034,7 +20042,7 @@ fn zirFrame(
 
 fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const ty = try sema.resolveType(block, operand_src, inst_data.operand);
     if (ty.isNoReturn(mod)) {
@@ -20049,7 +20057,7 @@ fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
 
 fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const operand = try sema.resolveInst(inst_data.operand);
     const operand_ty = sema.typeOf(operand);
@@ -20098,7 +20106,7 @@ fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
 }
 
 fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const operand = try sema.resolveInst(inst_data.operand);
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
 
@@ -20118,7 +20126,7 @@ fn zirAbs(
     inst: Zir.Inst.Index,
 ) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const operand = try sema.resolveInst(inst_data.operand);
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const operand_ty = sema.typeOf(operand);
@@ -20186,7 +20194,7 @@ fn zirUnaryMath(
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const operand = try sema.resolveInst(inst_data.operand);
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const operand_ty = sema.typeOf(operand);
@@ -20209,7 +20217,7 @@ fn zirUnaryMath(
 }
 
 fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const src = inst_data.src();
     const operand = try sema.resolveInst(inst_data.operand);
@@ -21385,7 +21393,7 @@ fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData)
 
 fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const ty = try sema.resolveType(block, ty_src, inst_data.operand);
 
@@ -21395,20 +21403,20 @@ fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
 }
 
 fn zirFrameType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     return sema.failWithUseOfAsync(block, src);
 }
 
 fn zirFrameSize(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     return sema.failWithUseOfAsync(block, src);
 }
 
 fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
@@ -21490,7 +21498,7 @@ fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
 
 fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
@@ -21532,7 +21540,7 @@ fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErro
 
 fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
 
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
@@ -21807,7 +21815,7 @@ fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstDa
 }
 
 fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
@@ -22237,7 +22245,7 @@ fn zirPtrCastNoDest(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.Inst
 
 fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src = inst_data.src();
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
@@ -22326,7 +22334,7 @@ fn zirBitCount(
     comptime comptimeOp: fn (val: Value, ty: Type, mod: *Module) u64,
 ) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const operand = try sema.resolveInst(inst_data.operand);
@@ -22380,7 +22388,7 @@ fn zirBitCount(
 
 fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const operand = try sema.resolveInst(inst_data.operand);
@@ -22436,7 +22444,7 @@ fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
 }
 
 fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const operand = try sema.resolveInst(inst_data.operand);
@@ -22495,7 +22503,7 @@ fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
 }
 
 fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u64 {
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
     sema.src = src;
     const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
@@ -23182,7 +23190,7 @@ fn zirCmpxchg(
 
 fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const src = inst_data.src();
     const scalar_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
@@ -23203,7 +23211,7 @@ fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
 }
 
 fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const op_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
@@ -23275,7 +23283,7 @@ fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
 
 fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data;
     const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const mask_src: LazySrcLoc = .{ .node_offset_builtin_call_arg3 = inst_data.src_node };
@@ -23552,7 +23560,7 @@ fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) C
 }
 
 fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data;
     // zig fmt: off
     const elem_ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
@@ -23600,7 +23608,7 @@ fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
 
 fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data;
     const src = inst_data.src();
     // zig fmt: off
@@ -23685,7 +23693,7 @@ fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
 }
 
 fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data;
     const src = inst_data.src();
     // zig fmt: off
@@ -23721,7 +23729,7 @@ fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
 }
 
 fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.MulAdd, inst_data.payload_index).data;
     const src = inst_data.src();
 
@@ -23789,7 +23797,7 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const modifier_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const func_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
     const args_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
@@ -23880,7 +23888,7 @@ fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
 }
 
 fn zirFieldParentPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.FieldParentPtr, inst_data.payload_index).data;
     const src = inst_data.src();
     const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
@@ -24002,7 +24010,7 @@ fn zirMinMax(
     inst: Zir.Inst.Index,
     comptime air_tag: Air.Inst.Tag,
 ) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const src = inst_data.src();
     const lhs_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
@@ -24301,7 +24309,7 @@ fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !A
 }
 
 fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void {
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const src = inst_data.src();
     const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
@@ -24521,7 +24529,7 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
     const mod = sema.mod;
     const gpa = sema.gpa;
     const ip = &mod.intern_pool;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const src = inst_data.src();
     const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
@@ -24607,7 +24615,7 @@ fn zirBuiltinAsyncCall(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.I
 }
 
 fn zirResume(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
     return sema.failWithUseOfAsync(block, src);
 }
@@ -24617,7 +24625,7 @@ fn zirAwait(
     block: *Block,
     inst: Zir.Inst.Index,
 ) CompileError!Air.Inst.Ref {
-    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
     const src = inst_data.src();
 
     return sema.failWithUseOfAsync(block, src);
@@ -24702,7 +24710,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     defer tracy.end();
 
     const mod = sema.mod;
-    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = sema.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index);
     const target = mod.getTarget();
 
@@ -24731,7 +24739,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     const @"align": ?Alignment = if (extra.data.bits.has_align_body) blk: {
         const body_len = sema.code.extra[extra_index];
         extra_index += 1;
-        const body = sema.code.extra[extra_index..][0..body_len];
+        const body = sema.code.bodySlice(extra_index, body_len);
         extra_index += body.len;
 
         const val = try sema.resolveGenericBody(block, align_src, body, inst, Type.u29, .{
@@ -24762,7 +24770,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     const @"addrspace": ?std.builtin.AddressSpace = if (extra.data.bits.has_addrspace_body) blk: {
         const body_len = sema.code.extra[extra_index];
         extra_index += 1;
-        const body = sema.code.extra[extra_index..][0..body_len];
+        const body = sema.code.bodySlice(extra_index, body_len);
         extra_index += body.len;
 
         const addrspace_ty = try sema.getBuiltinType("AddressSpace");
@@ -24790,7 +24798,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     const section: Section = if (extra.data.bits.has_section_body) blk: {
         const body_len = sema.code.extra[extra_index];
         extra_index += 1;
-        const body = sema.code.extra[extra_index..][0..body_len];
+        const body = sema.code.bodySlice(extra_index, body_len);
         extra_index += body.len;
 
         const ty = Type.slice_const_u8;
@@ -24818,7 +24826,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     const cc: ?std.builtin.CallingConvention = if (extra.data.bits.has_cc_body) blk: {
         const body_len = sema.code.extra[extra_index];
         extra_index += 1;
-        const body = sema.code.extra[extra_index..][0..body_len];
+        const body = sema.code.bodySlice(extra_index, body_len);
         extra_index += body.len;
 
         const cc_ty = try sema.getBuiltinType("CallingConvention");
@@ -24849,7 +24857,7 @@ fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: {
         const body_len = sema.code.extra[extra_index];
         extra_index += 1;
-        const body = sema.code.extra[extra_index..][0..body_len];
+        const body = sema.code.bodySlice(extra_index, body_len);
         extra_index += body.len;
 
         const val = try sema.resolveGenericBody(block, ret_src, body, inst, Type.type, .{
@@ -34821,7 +34829,7 @@ fn semaBackingIntType(mod: *Module, struct_type: InternPool.Key.StructType) Comp
         break :blk accumulator;
     };
 
-    const extended = zir.instructions.items(.data)[struct_type.zir_index].extended;
+    const extended = zir.instructions.items(.data)[@intFromEnum(struct_type.zir_index)].extended;
     assert(extended.opcode == .struct_decl);
     const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
 
@@ -34840,7 +34848,7 @@ fn semaBackingIntType(mod: *Module, struct_type: InternPool.Key.StructType) Comp
                 const backing_int_ref: Zir.Inst.Ref = @enumFromInt(zir.extra[extra_index]);
                 break :blk try sema.resolveType(&block, backing_int_src, backing_int_ref);
             } else {
-                const body = zir.extra[extra_index..][0..backing_int_body_len];
+                const body = zir.bodySlice(extra_index, backing_int_body_len);
                 const ty_ref = try sema.resolveBody(&block, body, struct_type.zir_index);
                 break :blk try sema.analyzeAsType(&block, backing_int_src, ty_ref);
             }
@@ -35409,7 +35417,7 @@ fn semaStructFields(
     const namespace_index = struct_type.namespace.unwrap() orelse decl.src_namespace;
     const zir = mod.namespacePtr(namespace_index).file_scope.zir;
     const zir_index = struct_type.zir_index;
-    const extended = zir.instructions.items(.data)[zir_index].extended;
+    const extended = zir.instructions.items(.data)[@intFromEnum(zir_index)].extended;
     assert(extended.opcode == .struct_decl);
     const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
     var extra_index: usize = extended.operand;
@@ -35590,7 +35598,7 @@ fn semaStructFields(
                 };
             }
             assert(zir_field.type_body_len != 0);
-            const body = zir.extra[extra_index..][0..zir_field.type_body_len];
+            const body = zir.bodySlice(extra_index, zir_field.type_body_len);
             extra_index += body.len;
             const ty_ref = try sema.resolveBody(&block_scope, body, zir_index);
             break :ty sema.analyzeAsType(&block_scope, .unneeded, ty_ref) catch |err| switch (err) {
@@ -35676,7 +35684,7 @@ fn semaStructFields(
         }
 
         if (zir_field.align_body_len > 0) {
-            const body = zir.extra[extra_index..][0..zir_field.align_body_len];
+            const body = zir.bodySlice(extra_index, zir_field.align_body_len);
             extra_index += body.len;
             const align_ref = try sema.resolveBody(&block_scope, body, zir_index);
             const field_align = sema.analyzeAsAlign(&block_scope, .unneeded, align_ref) catch |err| switch (err) {
@@ -35706,7 +35714,7 @@ fn semaStructFields(
             extra_index += zir_field.type_body_len;
             extra_index += zir_field.align_body_len;
             if (zir_field.init_body_len > 0) {
-                const body = zir.extra[extra_index..][0..zir_field.init_body_len];
+                const body = zir.bodySlice(extra_index, zir_field.init_body_len);
                 extra_index += body.len;
                 const init = try sema.resolveBody(&block_scope, body, zir_index);
                 const coerced = sema.coerce(&block_scope, field_ty, init, .unneeded) catch |err| switch (err) {
@@ -35748,7 +35756,7 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un
     const ip = &mod.intern_pool;
     const decl_index = union_type.decl;
     const zir = mod.namespacePtr(union_type.namespace).file_scope.zir;
-    const extended = zir.instructions.items(.data)[union_type.zir_index].extended;
+    const extended = zir.instructions.items(.data)[@intFromEnum(union_type.zir_index)].extended;
     assert(extended.opcode == .union_decl);
     const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
     var extra_index: usize = extended.operand;
@@ -35785,7 +35793,7 @@ fn semaUnionFields(mod: *Module, arena: Allocator, union_type: InternPool.Key.Un
     while (decls_it.next()) |_| {}
     extra_index = decls_it.extra_index;
 
-    const body = zir.extra[extra_index..][0..body_len];
+    const body = zir.bodySlice(extra_index, body_len);
     extra_index += body.len;
 
     const decl = mod.declPtr(decl_index);
src/Zir.zig
@@ -93,13 +93,18 @@ pub fn extraData(code: Zir, comptime T: type, index: usize) ExtraData(T) {
     inline for (fields) |field| {
         @field(result, field.name) = switch (field.type) {
             u32 => code.extra[i],
-            Inst.Ref => @enumFromInt(code.extra[i]),
+
+            Inst.Ref,
+            Inst.Index,
+            => @enumFromInt(code.extra[i]),
+
             i32,
             Inst.Call.Flags,
             Inst.BuiltinCall.Flags,
             Inst.SwitchBlock.Bits,
             Inst.FuncFancy.Bits,
             => @bitCast(code.extra[i]),
+
             else => @compileError("bad field type"),
         };
         i += 1;
@@ -134,6 +139,10 @@ pub fn refSlice(code: Zir, start: usize, len: usize) []Inst.Ref {
     return @ptrCast(code.extra[start..][0..len]);
 }
 
+pub fn bodySlice(zir: Zir, start: usize, len: usize) []Inst.Index {
+    return @ptrCast(zir.extra[start..][0..len]);
+}
+
 pub fn hasCompileErrors(code: Zir) bool {
     return code.extra[@intFromEnum(ExtraIndex.compile_errors)] != 0;
 }
@@ -145,10 +154,6 @@ pub fn deinit(code: *Zir, gpa: Allocator) void {
     code.* = undefined;
 }
 
-/// ZIR is structured so that the outermost "main" struct of any file
-/// is always at index 0.
-pub const main_struct_inst: Inst.Index = 0;
-
 /// These are untyped instructions generated from an Abstract Syntax Tree.
 /// The data here is immutable because it is possible to have multiple
 /// analyses on the same ZIR happening at the same time.
@@ -2093,7 +2098,34 @@ pub const Inst = struct {
     };
 
     /// The position of a ZIR instruction within the `Zir` instructions array.
-    pub const Index = u32;
+    pub const Index = enum(u32) {
+        /// ZIR is structured so that the outermost "main" struct of any file
+        /// is always at index 0.
+        main_struct_inst = 0,
+        ref_start_index = InternPool.static_len,
+        _,
+
+        pub fn toRef(i: Index) Inst.Ref {
+            return @enumFromInt(@intFromEnum(Index.ref_start_index) + @intFromEnum(i));
+        }
+
+        pub fn toOptional(i: Index) OptionalIndex {
+            return @enumFromInt(@intFromEnum(i));
+        }
+    };
+
+    pub const OptionalIndex = enum(u32) {
+        /// ZIR is structured so that the outermost "main" struct of any file
+        /// is always at index 0.
+        main_struct_inst = 0,
+        ref_start_index = InternPool.static_len,
+        none = std.math.maxInt(u32),
+        _,
+
+        pub fn unwrap(oi: OptionalIndex) ?Index {
+            return if (oi == .none) null else @enumFromInt(@intFromEnum(oi));
+        }
+    };
 
     /// A reference to ZIR instruction, or to an InternPool index, or neither.
     ///
@@ -2196,6 +2228,21 @@ pub const Inst = struct {
         /// value and may instead be used as a sentinel to indicate null.
         none = @intFromEnum(InternPool.Index.none),
         _,
+
+        pub fn toIndex(inst: Ref) ?Index {
+            assert(inst != .none);
+            const ref_int = @intFromEnum(inst);
+            if (ref_int >= @intFromEnum(Index.ref_start_index)) {
+                return @enumFromInt(ref_int - @intFromEnum(Index.ref_start_index));
+            } else {
+                return null;
+            }
+        }
+
+        pub fn toIndexAllowNone(inst: Ref) ?Index {
+            if (inst == .none) return null;
+            return toIndex(inst);
+        }
     };
 
     /// All instructions have an 8-byte payload, which is contained within
@@ -3286,7 +3333,7 @@ pub const Inst = struct {
 
     /// Trailing: for each `imports_len` there is an Item
     pub const Imports = struct {
-        imports_len: Inst.Index,
+        imports_len: u32,
 
         pub const Item = struct {
             /// null terminated string index
@@ -3371,16 +3418,16 @@ pub const DeclIterator = struct {
     }
 };
 
-pub fn declIterator(zir: Zir, decl_inst: u32) DeclIterator {
+pub fn declIterator(zir: Zir, decl_inst: Zir.Inst.Index) DeclIterator {
     const tags = zir.instructions.items(.tag);
     const datas = zir.instructions.items(.data);
-    switch (tags[decl_inst]) {
+    switch (tags[@intFromEnum(decl_inst)]) {
         // Functions are allowed and yield no iterations.
         // There is one case matching this in the extended instruction set below.
         .func, .func_inferred, .func_fancy => return declIteratorInner(zir, 0, 0),
 
         .extended => {
-            const extended = datas[decl_inst].extended;
+            const extended = datas[@intFromEnum(decl_inst)].extended;
             switch (extended.opcode) {
                 .struct_decl => {
                     const small: Inst.StructDecl.Small = @bitCast(extended.small);
@@ -3469,7 +3516,7 @@ pub fn declIteratorInner(zir: Zir, extra_index: usize, decls_len: u32) DeclItera
 /// The iterator would have to allocate memory anyway to iterate. So here we populate
 /// an ArrayList as the result.
 pub fn findDecls(zir: Zir, list: *std.ArrayList(Inst.Index), decl_sub_index: ExtraIndex) !void {
-    const block_inst = zir.extra[@intFromEnum(decl_sub_index) + 6];
+    const block_inst: Zir.Inst.Index = @enumFromInt(zir.extra[@intFromEnum(decl_sub_index) + 6]);
     list.clearRetainingCapacity();
 
     return zir.findDeclsInner(list, block_inst);
@@ -3483,32 +3530,32 @@ fn findDeclsInner(
     const tags = zir.instructions.items(.tag);
     const datas = zir.instructions.items(.data);
 
-    switch (tags[inst]) {
+    switch (tags[@intFromEnum(inst)]) {
         // Functions instructions are interesting and have a body.
         .func,
         .func_inferred,
         => {
             try list.append(inst);
 
-            const inst_data = datas[inst].pl_node;
+            const inst_data = datas[@intFromEnum(inst)].pl_node;
             const extra = zir.extraData(Inst.Func, inst_data.payload_index);
             var extra_index: usize = extra.end;
             switch (extra.data.ret_body_len) {
                 0 => {},
                 1 => extra_index += 1,
                 else => {
-                    const body = zir.extra[extra_index..][0..extra.data.ret_body_len];
+                    const body = zir.bodySlice(extra_index, extra.data.ret_body_len);
                     extra_index += body.len;
                     try zir.findDeclsBody(list, body);
                 },
             }
-            const body = zir.extra[extra_index..][0..extra.data.body_len];
+            const body = zir.bodySlice(extra_index, extra.data.body_len);
             return zir.findDeclsBody(list, body);
         },
         .func_fancy => {
             try list.append(inst);
 
-            const inst_data = datas[inst].pl_node;
+            const inst_data = datas[@intFromEnum(inst)].pl_node;
             const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index);
             var extra_index: usize = extra.end;
             extra_index += @intFromBool(extra.data.bits.has_lib_name);
@@ -3516,7 +3563,7 @@ fn findDeclsInner(
             if (extra.data.bits.has_align_body) {
                 const body_len = zir.extra[extra_index];
                 extra_index += 1;
-                const body = zir.extra[extra_index..][0..body_len];
+                const body = zir.bodySlice(extra_index, body_len);
                 try zir.findDeclsBody(list, body);
                 extra_index += body.len;
             } else if (extra.data.bits.has_align_ref) {
@@ -3526,7 +3573,7 @@ fn findDeclsInner(
             if (extra.data.bits.has_addrspace_body) {
                 const body_len = zir.extra[extra_index];
                 extra_index += 1;
-                const body = zir.extra[extra_index..][0..body_len];
+                const body = zir.bodySlice(extra_index, body_len);
                 try zir.findDeclsBody(list, body);
                 extra_index += body.len;
             } else if (extra.data.bits.has_addrspace_ref) {
@@ -3536,7 +3583,7 @@ fn findDeclsInner(
             if (extra.data.bits.has_section_body) {
                 const body_len = zir.extra[extra_index];
                 extra_index += 1;
-                const body = zir.extra[extra_index..][0..body_len];
+                const body = zir.bodySlice(extra_index, body_len);
                 try zir.findDeclsBody(list, body);
                 extra_index += body.len;
             } else if (extra.data.bits.has_section_ref) {
@@ -3546,7 +3593,7 @@ fn findDeclsInner(
             if (extra.data.bits.has_cc_body) {
                 const body_len = zir.extra[extra_index];
                 extra_index += 1;
-                const body = zir.extra[extra_index..][0..body_len];
+                const body = zir.bodySlice(extra_index, body_len);
                 try zir.findDeclsBody(list, body);
                 extra_index += body.len;
             } else if (extra.data.bits.has_cc_ref) {
@@ -3556,7 +3603,7 @@ fn findDeclsInner(
             if (extra.data.bits.has_ret_ty_body) {
                 const body_len = zir.extra[extra_index];
                 extra_index += 1;
-                const body = zir.extra[extra_index..][0..body_len];
+                const body = zir.bodySlice(extra_index, body_len);
                 try zir.findDeclsBody(list, body);
                 extra_index += body.len;
             } else if (extra.data.bits.has_ret_ty_ref) {
@@ -3565,11 +3612,11 @@ fn findDeclsInner(
 
             extra_index += @intFromBool(extra.data.bits.has_any_noalias);
 
-            const body = zir.extra[extra_index..][0..extra.data.body_len];
+            const body = zir.bodySlice(extra_index, extra.data.body_len);
             return zir.findDeclsBody(list, body);
         },
         .extended => {
-            const extended = datas[inst].extended;
+            const extended = datas[@intFromEnum(inst)].extended;
             switch (extended.opcode) {
 
                 // Decl instructions are interesting but have no body.
@@ -3587,23 +3634,23 @@ fn findDeclsInner(
         // Block instructions, recurse over the bodies.
 
         .block, .block_comptime, .block_inline => {
-            const inst_data = datas[inst].pl_node;
+            const inst_data = datas[@intFromEnum(inst)].pl_node;
             const extra = zir.extraData(Inst.Block, inst_data.payload_index);
-            const body = zir.extra[extra.end..][0..extra.data.body_len];
+            const body = zir.bodySlice(extra.end, extra.data.body_len);
             return zir.findDeclsBody(list, body);
         },
         .condbr, .condbr_inline => {
-            const inst_data = datas[inst].pl_node;
+            const inst_data = datas[@intFromEnum(inst)].pl_node;
             const extra = zir.extraData(Inst.CondBr, inst_data.payload_index);
-            const then_body = zir.extra[extra.end..][0..extra.data.then_body_len];
-            const else_body = zir.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
+            const then_body = zir.bodySlice(extra.end, extra.data.then_body_len);
+            const else_body = zir.bodySlice(extra.end + then_body.len, extra.data.else_body_len);
             try zir.findDeclsBody(list, then_body);
             try zir.findDeclsBody(list, else_body);
         },
         .@"try", .try_ptr => {
-            const inst_data = datas[inst].pl_node;
+            const inst_data = datas[@intFromEnum(inst)].pl_node;
             const extra = zir.extraData(Inst.Try, inst_data.payload_index);
-            const body = zir.extra[extra.end..][0..extra.data.body_len];
+            const body = zir.bodySlice(extra.end, extra.data.body_len);
             try zir.findDeclsBody(list, body);
         },
         .switch_block => return findDeclsSwitch(zir, list, inst),
@@ -3619,7 +3666,7 @@ fn findDeclsSwitch(
     list: *std.ArrayList(Inst.Index),
     inst: Inst.Index,
 ) Allocator.Error!void {
-    const inst_data = zir.instructions.items(.data)[inst].pl_node;
+    const inst_data = zir.instructions.items(.data)[@intFromEnum(inst)].pl_node;
     const extra = zir.extraData(Inst.SwitchBlock, inst_data.payload_index);
 
     var extra_index: usize = extra.end;
@@ -3634,7 +3681,7 @@ fn findDeclsSwitch(
     if (special_prong != .none) {
         const body_len: u31 = @truncate(zir.extra[extra_index]);
         extra_index += 1;
-        const body = zir.extra[extra_index..][0..body_len];
+        const body = zir.bodySlice(extra_index, body_len);
         extra_index += body.len;
 
         try zir.findDeclsBody(list, body);
@@ -3642,20 +3689,18 @@ fn findDeclsSwitch(
 
     {
         const scalar_cases_len = extra.data.bits.scalar_cases_len;
-        var scalar_i: usize = 0;
-        while (scalar_i < scalar_cases_len) : (scalar_i += 1) {
+        for (0..scalar_cases_len) |_| {
             extra_index += 1;
             const body_len: u31 = @truncate(zir.extra[extra_index]);
             extra_index += 1;
-            const body = zir.extra[extra_index..][0..body_len];
+            const body = zir.bodySlice(extra_index, body_len);
             extra_index += body_len;
 
             try zir.findDeclsBody(list, body);
         }
     }
     {
-        var multi_i: usize = 0;
-        while (multi_i < multi_cases_len) : (multi_i += 1) {
+        for (0..multi_cases_len) |_| {
             const items_len = zir.extra[extra_index];
             extra_index += 1;
             const ranges_len = zir.extra[extra_index];
@@ -3672,7 +3717,7 @@ fn findDeclsSwitch(
                 extra_index += 1;
             }
 
-            const body = zir.extra[extra_index..][0..body_len];
+            const body = zir.bodySlice(extra_index, body_len);
             extra_index += body_len;
 
             try zir.findDeclsBody(list, body);
@@ -3699,12 +3744,12 @@ pub const FnInfo = struct {
     total_params_len: u32,
 };
 
-pub fn getParamBody(zir: Zir, fn_inst: Inst.Index) []const u32 {
+pub fn getParamBody(zir: Zir, fn_inst: Inst.Index) []const Zir.Inst.Index {
     const tags = zir.instructions.items(.tag);
     const datas = zir.instructions.items(.data);
-    const inst_data = datas[fn_inst].pl_node;
+    const inst_data = datas[@intFromEnum(fn_inst)].pl_node;
 
-    const param_block_index = switch (tags[fn_inst]) {
+    const param_block_index = switch (tags[@intFromEnum(fn_inst)]) {
         .func, .func_inferred => blk: {
             const extra = zir.extraData(Inst.Func, inst_data.payload_index);
             break :blk extra.data.param_block;
@@ -3716,8 +3761,8 @@ pub fn getParamBody(zir: Zir, fn_inst: Inst.Index) []const u32 {
         else => unreachable,
     };
 
-    const param_block = zir.extraData(Inst.Block, datas[param_block_index].pl_node.payload_index);
-    return zir.extra[param_block.end..][0..param_block.data.body_len];
+    const param_block = zir.extraData(Inst.Block, datas[@intFromEnum(param_block_index)].pl_node.payload_index);
+    return zir.bodySlice(param_block.end, param_block.data.body_len);
 }
 
 pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
@@ -3728,9 +3773,9 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
         body: []const Inst.Index,
         ret_ty_ref: Inst.Ref,
         ret_ty_body: []const Inst.Index,
-    } = switch (tags[fn_inst]) {
+    } = switch (tags[@intFromEnum(fn_inst)]) {
         .func, .func_inferred => blk: {
-            const inst_data = datas[fn_inst].pl_node;
+            const inst_data = datas[@intFromEnum(fn_inst)].pl_node;
             const extra = zir.extraData(Inst.Func, inst_data.payload_index);
 
             var extra_index: usize = extra.end;
@@ -3746,12 +3791,12 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
                     extra_index += 1;
                 },
                 else => {
-                    ret_ty_body = zir.extra[extra_index..][0..extra.data.ret_body_len];
+                    ret_ty_body = zir.bodySlice(extra_index, extra.data.ret_body_len);
                     extra_index += ret_ty_body.len;
                 },
             }
 
-            const body = zir.extra[extra_index..][0..extra.data.body_len];
+            const body = zir.bodySlice(extra_index, extra.data.body_len);
             extra_index += body.len;
 
             break :blk .{
@@ -3762,7 +3807,7 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
             };
         },
         .func_fancy => blk: {
-            const inst_data = datas[fn_inst].pl_node;
+            const inst_data = datas[@intFromEnum(fn_inst)].pl_node;
             const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index);
 
             var extra_index: usize = extra.end;
@@ -3793,7 +3838,7 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
             if (extra.data.bits.has_ret_ty_body) {
                 const body_len = zir.extra[extra_index];
                 extra_index += 1;
-                ret_ty_body = zir.extra[extra_index..][0..body_len];
+                ret_ty_body = zir.bodySlice(extra_index, body_len);
                 extra_index += ret_ty_body.len;
             } else if (extra.data.bits.has_ret_ty_ref) {
                 ret_ty_ref = @enumFromInt(zir.extra[extra_index]);
@@ -3802,7 +3847,7 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
 
             extra_index += @intFromBool(extra.data.bits.has_any_noalias);
 
-            const body = zir.extra[extra_index..][0..extra.data.body_len];
+            const body = zir.bodySlice(extra_index, extra.data.body_len);
             extra_index += body.len;
             break :blk .{
                 .param_block = extra.data.param_block,
@@ -3813,14 +3858,15 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
         },
         else => unreachable,
     };
-    assert(tags[info.param_block] == .block or
-        tags[info.param_block] == .block_comptime or
-        tags[info.param_block] == .block_inline);
-    const param_block = zir.extraData(Inst.Block, datas[info.param_block].pl_node.payload_index);
-    const param_body = zir.extra[param_block.end..][0..param_block.data.body_len];
+    switch (tags[@intFromEnum(info.param_block)]) {
+        .block, .block_comptime, .block_inline => {}, // OK
+        else => unreachable, // assertion failure
+    }
+    const param_block = zir.extraData(Inst.Block, datas[@intFromEnum(info.param_block)].pl_node.payload_index);
+    const param_body = zir.bodySlice(param_block.end, param_block.data.body_len);
     var total_params_len: u32 = 0;
     for (param_body) |inst| {
-        switch (tags[inst]) {
+        switch (tags[@intFromEnum(inst)]) {
             .param, .param_comptime, .param_anytype, .param_anytype_comptime => {
                 total_params_len += 1;
             },
@@ -3836,24 +3882,3 @@ pub fn getFnInfo(zir: Zir, fn_inst: Inst.Index) FnInfo {
         .total_params_len = total_params_len,
     };
 }
-
-pub const ref_start_index: u32 = InternPool.static_len;
-
-pub fn indexToRef(inst: Inst.Index) Inst.Ref {
-    return @enumFromInt(ref_start_index + inst);
-}
-
-pub fn refToIndex(inst: Inst.Ref) ?Inst.Index {
-    assert(inst != .none);
-    const ref_int = @intFromEnum(inst);
-    if (ref_int >= ref_start_index) {
-        return ref_int - ref_start_index;
-    } else {
-        return null;
-    }
-}
-
-pub fn refToIndexAllowNone(inst: Inst.Ref) ?Inst.Index {
-    if (inst == .none) return null;
-    return refToIndex(inst);
-}