Commit 8f849684f4

mlugg <mlugg@mlugg.co.uk>
2024-12-05 19:03:09
std.zig.Zir: improve instruction tracking
The main change here is to partition tracked instructions found within a declaration. It's very unlikely that, for instance, a `struct { ... }` type declaration was intentionally turned into a reification or an anonymous initialization, so it makes sense to track things in a few different arrays. In particular, this fixes an issue where a `func` instruction could wrongly be mapped to something else if the types of function parameters changed. This would cause huge problems further down the pipeline; we expect that if a `declaration` is tracked, and it previously contained a `func`/`func_inferred`/`func_fancy`, then this instruction is either tracked to another `func`/`func_inferred`/`func_fancy` instruction, or is lost. Also, this commit takes the opportunity to rename the functions actually doing this logic. `Zir.findDecls` was a name that might have made sense at some point, but nowadays, it's definitely not finding declarations, and it's not *exclusively* finding type declarations. Instead, the point is to find instructions which we want to track; hence the new name, `Zir.findTrackable`. Lastly, a nice side effect of partitioning the output of `findTrackable` is that `Zir.declIterator` no longer needs to accept input instructions which aren't type declarations (e.g. `reify`, `func`).
1 parent 7f3211a
Changed files (2)
lib
std
src
lib/std/zig/Zir.zig
@@ -3615,145 +3615,155 @@ pub const DeclIterator = struct {
 };
 
 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[@intFromEnum(decl_inst)]) {
-        // Functions are allowed and yield no iterations.
-        // This is because they are returned by `findDecls`.
-        .func, .func_inferred, .func_fancy => return .{
-            .extra_index = undefined,
-            .decls_remaining = 0,
-            .zir = zir,
-        },
-
-        .extended => {
-            const extended = datas[@intFromEnum(decl_inst)].extended;
-            switch (extended.opcode) {
-                // Reifications are allowed and yield no iterations.
-                // This is because they are returned by `findDecls`.
-                .reify => return .{
-                    .extra_index = undefined,
-                    .decls_remaining = 0,
-                    .zir = zir,
-                },
-                .struct_decl => {
-                    const small: Inst.StructDecl.Small = @bitCast(extended.small);
-                    var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.StructDecl).@"struct".fields.len);
-                    const captures_len = if (small.has_captures_len) captures_len: {
-                        const captures_len = zir.extra[extra_index];
-                        extra_index += 1;
-                        break :captures_len captures_len;
-                    } else 0;
-                    extra_index += @intFromBool(small.has_fields_len);
-                    const decls_len = if (small.has_decls_len) decls_len: {
-                        const decls_len = zir.extra[extra_index];
-                        extra_index += 1;
-                        break :decls_len decls_len;
-                    } else 0;
-
-                    extra_index += captures_len;
-
-                    if (small.has_backing_int) {
-                        const backing_int_body_len = zir.extra[extra_index];
-                        extra_index += 1; // backing_int_body_len
-                        if (backing_int_body_len == 0) {
-                            extra_index += 1; // backing_int_ref
-                        } else {
-                            extra_index += backing_int_body_len; // backing_int_body_inst
-                        }
-                    }
+    const inst = zir.instructions.get(@intFromEnum(decl_inst));
+    assert(inst.tag == .extended);
+    const extended = inst.data.extended;
+    switch (extended.opcode) {
+        .struct_decl => {
+            const small: Inst.StructDecl.Small = @bitCast(extended.small);
+            var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.StructDecl).@"struct".fields.len);
+            const captures_len = if (small.has_captures_len) captures_len: {
+                const captures_len = zir.extra[extra_index];
+                extra_index += 1;
+                break :captures_len captures_len;
+            } else 0;
+            extra_index += @intFromBool(small.has_fields_len);
+            const decls_len = if (small.has_decls_len) decls_len: {
+                const decls_len = zir.extra[extra_index];
+                extra_index += 1;
+                break :decls_len decls_len;
+            } else 0;
+
+            extra_index += captures_len;
+
+            if (small.has_backing_int) {
+                const backing_int_body_len = zir.extra[extra_index];
+                extra_index += 1; // backing_int_body_len
+                if (backing_int_body_len == 0) {
+                    extra_index += 1; // backing_int_ref
+                } else {
+                    extra_index += backing_int_body_len; // backing_int_body_inst
+                }
+            }
 
-                    return .{
-                        .extra_index = extra_index,
-                        .decls_remaining = decls_len,
-                        .zir = zir,
-                    };
-                },
-                .enum_decl => {
-                    const small: Inst.EnumDecl.Small = @bitCast(extended.small);
-                    var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.EnumDecl).@"struct".fields.len);
-                    extra_index += @intFromBool(small.has_tag_type);
-                    const captures_len = if (small.has_captures_len) captures_len: {
-                        const captures_len = zir.extra[extra_index];
-                        extra_index += 1;
-                        break :captures_len captures_len;
-                    } else 0;
-                    extra_index += @intFromBool(small.has_body_len);
-                    extra_index += @intFromBool(small.has_fields_len);
-                    const decls_len = if (small.has_decls_len) decls_len: {
-                        const decls_len = zir.extra[extra_index];
-                        extra_index += 1;
-                        break :decls_len decls_len;
-                    } else 0;
+            return .{
+                .extra_index = extra_index,
+                .decls_remaining = decls_len,
+                .zir = zir,
+            };
+        },
+        .enum_decl => {
+            const small: Inst.EnumDecl.Small = @bitCast(extended.small);
+            var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.EnumDecl).@"struct".fields.len);
+            extra_index += @intFromBool(small.has_tag_type);
+            const captures_len = if (small.has_captures_len) captures_len: {
+                const captures_len = zir.extra[extra_index];
+                extra_index += 1;
+                break :captures_len captures_len;
+            } else 0;
+            extra_index += @intFromBool(small.has_body_len);
+            extra_index += @intFromBool(small.has_fields_len);
+            const decls_len = if (small.has_decls_len) decls_len: {
+                const decls_len = zir.extra[extra_index];
+                extra_index += 1;
+                break :decls_len decls_len;
+            } else 0;
 
-                    extra_index += captures_len;
+            extra_index += captures_len;
 
-                    return .{
-                        .extra_index = extra_index,
-                        .decls_remaining = decls_len,
-                        .zir = zir,
-                    };
-                },
-                .union_decl => {
-                    const small: Inst.UnionDecl.Small = @bitCast(extended.small);
-                    var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.UnionDecl).@"struct".fields.len);
-                    extra_index += @intFromBool(small.has_tag_type);
-                    const captures_len = if (small.has_captures_len) captures_len: {
-                        const captures_len = zir.extra[extra_index];
-                        extra_index += 1;
-                        break :captures_len captures_len;
-                    } else 0;
-                    extra_index += @intFromBool(small.has_body_len);
-                    extra_index += @intFromBool(small.has_fields_len);
-                    const decls_len = if (small.has_decls_len) decls_len: {
-                        const decls_len = zir.extra[extra_index];
-                        extra_index += 1;
-                        break :decls_len decls_len;
-                    } else 0;
+            return .{
+                .extra_index = extra_index,
+                .decls_remaining = decls_len,
+                .zir = zir,
+            };
+        },
+        .union_decl => {
+            const small: Inst.UnionDecl.Small = @bitCast(extended.small);
+            var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.UnionDecl).@"struct".fields.len);
+            extra_index += @intFromBool(small.has_tag_type);
+            const captures_len = if (small.has_captures_len) captures_len: {
+                const captures_len = zir.extra[extra_index];
+                extra_index += 1;
+                break :captures_len captures_len;
+            } else 0;
+            extra_index += @intFromBool(small.has_body_len);
+            extra_index += @intFromBool(small.has_fields_len);
+            const decls_len = if (small.has_decls_len) decls_len: {
+                const decls_len = zir.extra[extra_index];
+                extra_index += 1;
+                break :decls_len decls_len;
+            } else 0;
 
-                    extra_index += captures_len;
+            extra_index += captures_len;
 
-                    return .{
-                        .extra_index = extra_index,
-                        .decls_remaining = decls_len,
-                        .zir = zir,
-                    };
-                },
-                .opaque_decl => {
-                    const small: Inst.OpaqueDecl.Small = @bitCast(extended.small);
-                    var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.OpaqueDecl).@"struct".fields.len);
-                    const decls_len = if (small.has_decls_len) decls_len: {
-                        const decls_len = zir.extra[extra_index];
-                        extra_index += 1;
-                        break :decls_len decls_len;
-                    } else 0;
-                    const captures_len = if (small.has_captures_len) captures_len: {
-                        const captures_len = zir.extra[extra_index];
-                        extra_index += 1;
-                        break :captures_len captures_len;
-                    } else 0;
+            return .{
+                .extra_index = extra_index,
+                .decls_remaining = decls_len,
+                .zir = zir,
+            };
+        },
+        .opaque_decl => {
+            const small: Inst.OpaqueDecl.Small = @bitCast(extended.small);
+            var extra_index: u32 = @intCast(extended.operand + @typeInfo(Inst.OpaqueDecl).@"struct".fields.len);
+            const decls_len = if (small.has_decls_len) decls_len: {
+                const decls_len = zir.extra[extra_index];
+                extra_index += 1;
+                break :decls_len decls_len;
+            } else 0;
+            const captures_len = if (small.has_captures_len) captures_len: {
+                const captures_len = zir.extra[extra_index];
+                extra_index += 1;
+                break :captures_len captures_len;
+            } else 0;
 
-                    extra_index += captures_len;
+            extra_index += captures_len;
 
-                    return .{
-                        .extra_index = extra_index,
-                        .decls_remaining = decls_len,
-                        .zir = zir,
-                    };
-                },
-                else => unreachable,
-            }
+            return .{
+                .extra_index = extra_index,
+                .decls_remaining = decls_len,
+                .zir = zir,
+            };
         },
         else => unreachable,
     }
 }
 
-/// Find all type declarations, recursively, within a `declaration` instruction. Does not recurse through
-/// said type declarations' declarations; to find all declarations, call this function on the declarations
-/// of the discovered types recursively.
-/// The iterator would have to allocate memory anyway to iterate, so an `ArrayList` is populated as the result.
-pub fn findDecls(zir: Zir, gpa: Allocator, list: *std.ArrayListUnmanaged(Inst.Index), decl_inst: Zir.Inst.Index) !void {
-    list.clearRetainingCapacity();
+/// `DeclContents` contains all "interesting" instructions found within a declaration by `findTrackable`.
+/// These instructions are partitioned into a few different sets, since this makes ZIR instruction mapping
+/// more effective.
+pub const DeclContents = struct {
+    /// This is a simple optional because ZIR guarantees that a `func`/`func_inferred`/`func_fancy` instruction
+    /// can only occur once per `declaration`.
+    func_decl: ?Inst.Index,
+    explicit_types: std.ArrayListUnmanaged(Inst.Index),
+    other: std.ArrayListUnmanaged(Inst.Index),
+
+    pub const init: DeclContents = .{
+        .func_decl = null,
+        .explicit_types = .empty,
+        .other = .empty,
+    };
+
+    pub fn clear(contents: *DeclContents) void {
+        contents.func_decl = null;
+        contents.explicit_types.clearRetainingCapacity();
+        contents.other.clearRetainingCapacity();
+    }
+
+    pub fn deinit(contents: *DeclContents, gpa: Allocator) void {
+        contents.explicit_types.deinit(gpa);
+        contents.other.deinit(gpa);
+    }
+};
+
+/// Find all tracked ZIR instructions, recursively, within a `declaration` instruction. Does not recurse through
+/// nested declarations; to find all declarations, call this function recursively on the type declarations discovered
+/// in `contents.explicit_types`.
+///
+/// This populates an `ArrayListUnmanaged` because an iterator would need to allocate memory anyway.
+pub fn findTrackable(zir: Zir, gpa: Allocator, contents: *DeclContents, decl_inst: Zir.Inst.Index) !void {
+    contents.clear();
+
     const declaration, const extra_end = zir.getDeclaration(decl_inst);
     const bodies = declaration.getBodies(extra_end, zir);
 
@@ -3762,27 +3772,27 @@ pub fn findDecls(zir: Zir, gpa: Allocator, list: *std.ArrayListUnmanaged(Inst.In
     var found_defers: std.AutoHashMapUnmanaged(u32, void) = .empty;
     defer found_defers.deinit(gpa);
 
-    try zir.findDeclsBody(gpa, list, &found_defers, bodies.value_body);
-    if (bodies.align_body) |b| try zir.findDeclsBody(gpa, list, &found_defers, b);
-    if (bodies.linksection_body) |b| try zir.findDeclsBody(gpa, list, &found_defers, b);
-    if (bodies.addrspace_body) |b| try zir.findDeclsBody(gpa, list, &found_defers, b);
+    try zir.findTrackableBody(gpa, contents, &found_defers, bodies.value_body);
+    if (bodies.align_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
+    if (bodies.linksection_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
+    if (bodies.addrspace_body) |b| try zir.findTrackableBody(gpa, contents, &found_defers, b);
 }
 
-/// Like `findDecls`, but only considers the `main_struct_inst` instruction. This may return more than
+/// Like `findTrackable`, but only considers the `main_struct_inst` instruction. This may return more than
 /// just that instruction because it will also traverse fields.
-pub fn findDeclsRoot(zir: Zir, gpa: Allocator, list: *std.ArrayListUnmanaged(Inst.Index)) !void {
-    list.clearRetainingCapacity();
+pub fn findTrackableRoot(zir: Zir, gpa: Allocator, contents: *DeclContents) !void {
+    contents.clear();
 
     var found_defers: std.AutoHashMapUnmanaged(u32, void) = .empty;
     defer found_defers.deinit(gpa);
 
-    try zir.findDeclsInner(gpa, list, &found_defers, .main_struct_inst);
+    try zir.findTrackableInner(gpa, contents, &found_defers, .main_struct_inst);
 }
 
-fn findDeclsInner(
+fn findTrackableInner(
     zir: Zir,
     gpa: Allocator,
-    list: *std.ArrayListUnmanaged(Inst.Index),
+    contents: *DeclContents,
     defers: *std.AutoHashMapUnmanaged(u32, void),
     inst: Inst.Index,
 ) Allocator.Error!void {
@@ -4026,7 +4036,7 @@ fn findDeclsInner(
         .struct_init,
         .struct_init_ref,
         .struct_init_anon,
-        => return list.append(gpa, inst),
+        => return contents.other.append(gpa, inst),
 
         .extended => {
             const extended = datas[@intFromEnum(inst)].extended;
@@ -4093,15 +4103,15 @@ fn findDeclsInner(
                 .typeof_peer => {
                     const extra = zir.extraData(Zir.Inst.TypeOfPeer, extended.operand);
                     const body = zir.bodySlice(extra.data.body_index, extra.data.body_len);
-                    try zir.findDeclsBody(gpa, list, defers, body);
+                    try zir.findTrackableBody(gpa, contents, defers, body);
                 },
 
                 // Reifications and opaque declarations need tracking, but have no body.
-                .reify, .opaque_decl => return list.append(gpa, inst),
+                .reify, .opaque_decl => return contents.other.append(gpa, inst),
 
                 // Struct declarations need tracking and have bodies.
                 .struct_decl => {
-                    try list.append(gpa, inst);
+                    try contents.explicit_types.append(gpa, inst);
 
                     const small: Zir.Inst.StructDecl.Small = @bitCast(extended.small);
                     const extra = zir.extraData(Zir.Inst.StructDecl, extended.operand);
@@ -4130,7 +4140,7 @@ fn findDeclsInner(
                         } else {
                             const body = zir.bodySlice(extra_index, backing_int_body_len);
                             extra_index += backing_int_body_len;
-                            try zir.findDeclsBody(gpa, list, defers, body);
+                            try zir.findTrackableBody(gpa, contents, defers, body);
                         }
                     }
                     extra_index += decls_len;
@@ -4186,12 +4196,12 @@ fn findDeclsInner(
 
                     // Now, `fields_extra_index` points to `bodies`. Let's treat this as one big body.
                     const merged_bodies = zir.bodySlice(fields_extra_index, total_bodies_len);
-                    try zir.findDeclsBody(gpa, list, defers, merged_bodies);
+                    try zir.findTrackableBody(gpa, contents, defers, merged_bodies);
                 },
 
                 // Union declarations need tracking and have a body.
                 .union_decl => {
-                    try list.append(gpa, inst);
+                    try contents.explicit_types.append(gpa, inst);
 
                     const small: Zir.Inst.UnionDecl.Small = @bitCast(extended.small);
                     const extra = zir.extraData(Zir.Inst.UnionDecl, extended.operand);
@@ -4216,12 +4226,12 @@ fn findDeclsInner(
                     extra_index += captures_len;
                     extra_index += decls_len;
                     const body = zir.bodySlice(extra_index, body_len);
-                    try zir.findDeclsBody(gpa, list, defers, body);
+                    try zir.findTrackableBody(gpa, contents, defers, body);
                 },
 
                 // Enum declarations need tracking and have a body.
                 .enum_decl => {
-                    try list.append(gpa, inst);
+                    try contents.explicit_types.append(gpa, inst);
 
                     const small: Zir.Inst.EnumDecl.Small = @bitCast(extended.small);
                     const extra = zir.extraData(Zir.Inst.EnumDecl, extended.operand);
@@ -4246,7 +4256,7 @@ fn findDeclsInner(
                     extra_index += captures_len;
                     extra_index += decls_len;
                     const body = zir.bodySlice(extra_index, body_len);
-                    try zir.findDeclsBody(gpa, list, defers, body);
+                    try zir.findTrackableBody(gpa, contents, defers, body);
                 },
             }
         },
@@ -4255,7 +4265,8 @@ fn findDeclsInner(
         .func,
         .func_inferred,
         => {
-            try list.append(gpa, inst);
+            assert(contents.func_decl == null);
+            contents.func_decl = inst;
 
             const inst_data = datas[@intFromEnum(inst)].pl_node;
             const extra = zir.extraData(Inst.Func, inst_data.payload_index);
@@ -4266,14 +4277,15 @@ fn findDeclsInner(
                 else => {
                     const body = zir.bodySlice(extra_index, extra.data.ret_body_len);
                     extra_index += body.len;
-                    try zir.findDeclsBody(gpa, list, defers, body);
+                    try zir.findTrackableBody(gpa, contents, defers, body);
                 },
             }
             const body = zir.bodySlice(extra_index, extra.data.body_len);
-            return zir.findDeclsBody(gpa, list, defers, body);
+            return zir.findTrackableBody(gpa, contents, defers, body);
         },
         .func_fancy => {
-            try list.append(gpa, inst);
+            assert(contents.func_decl == null);
+            contents.func_decl = inst;
 
             const inst_data = datas[@intFromEnum(inst)].pl_node;
             const extra = zir.extraData(Inst.FuncFancy, inst_data.payload_index);
@@ -4284,7 +4296,7 @@ fn findDeclsInner(
                 const body_len = zir.extra[extra_index];
                 extra_index += 1;
                 const body = zir.bodySlice(extra_index, body_len);
-                try zir.findDeclsBody(gpa, list, defers, body);
+                try zir.findTrackableBody(gpa, contents, defers, body);
                 extra_index += body.len;
             } else if (extra.data.bits.has_align_ref) {
                 extra_index += 1;
@@ -4294,7 +4306,7 @@ fn findDeclsInner(
                 const body_len = zir.extra[extra_index];
                 extra_index += 1;
                 const body = zir.bodySlice(extra_index, body_len);
-                try zir.findDeclsBody(gpa, list, defers, body);
+                try zir.findTrackableBody(gpa, contents, defers, body);
                 extra_index += body.len;
             } else if (extra.data.bits.has_addrspace_ref) {
                 extra_index += 1;
@@ -4304,7 +4316,7 @@ fn findDeclsInner(
                 const body_len = zir.extra[extra_index];
                 extra_index += 1;
                 const body = zir.bodySlice(extra_index, body_len);
-                try zir.findDeclsBody(gpa, list, defers, body);
+                try zir.findTrackableBody(gpa, contents, defers, body);
                 extra_index += body.len;
             } else if (extra.data.bits.has_section_ref) {
                 extra_index += 1;
@@ -4314,7 +4326,7 @@ fn findDeclsInner(
                 const body_len = zir.extra[extra_index];
                 extra_index += 1;
                 const body = zir.bodySlice(extra_index, body_len);
-                try zir.findDeclsBody(gpa, list, defers, body);
+                try zir.findTrackableBody(gpa, contents, defers, body);
                 extra_index += body.len;
             } else if (extra.data.bits.has_cc_ref) {
                 extra_index += 1;
@@ -4324,7 +4336,7 @@ fn findDeclsInner(
                 const body_len = zir.extra[extra_index];
                 extra_index += 1;
                 const body = zir.bodySlice(extra_index, body_len);
-                try zir.findDeclsBody(gpa, list, defers, body);
+                try zir.findTrackableBody(gpa, contents, defers, body);
                 extra_index += body.len;
             } else if (extra.data.bits.has_ret_ty_ref) {
                 extra_index += 1;
@@ -4333,7 +4345,7 @@ fn findDeclsInner(
             extra_index += @intFromBool(extra.data.bits.has_any_noalias);
 
             const body = zir.bodySlice(extra_index, extra.data.body_len);
-            return zir.findDeclsBody(gpa, list, defers, body);
+            return zir.findTrackableBody(gpa, contents, defers, body);
         },
 
         // Block instructions, recurse over the bodies.
@@ -4348,24 +4360,24 @@ fn findDeclsInner(
             const inst_data = datas[@intFromEnum(inst)].pl_node;
             const extra = zir.extraData(Inst.Block, inst_data.payload_index);
             const body = zir.bodySlice(extra.end, extra.data.body_len);
-            return zir.findDeclsBody(gpa, list, defers, body);
+            return zir.findTrackableBody(gpa, contents, defers, body);
         },
         .condbr, .condbr_inline => {
             const inst_data = datas[@intFromEnum(inst)].pl_node;
             const extra = zir.extraData(Inst.CondBr, inst_data.payload_index);
             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(gpa, list, defers, then_body);
-            try zir.findDeclsBody(gpa, list, defers, else_body);
+            try zir.findTrackableBody(gpa, contents, defers, then_body);
+            try zir.findTrackableBody(gpa, contents, defers, else_body);
         },
         .@"try", .try_ptr => {
             const inst_data = datas[@intFromEnum(inst)].pl_node;
             const extra = zir.extraData(Inst.Try, inst_data.payload_index);
             const body = zir.bodySlice(extra.end, extra.data.body_len);
-            try zir.findDeclsBody(gpa, list, defers, body);
+            try zir.findTrackableBody(gpa, contents, defers, body);
         },
-        .switch_block, .switch_block_ref => return zir.findDeclsSwitch(gpa, list, defers, inst, .normal),
-        .switch_block_err_union => return zir.findDeclsSwitch(gpa, list, defers, inst, .err_union),
+        .switch_block, .switch_block_ref => return zir.findTrackableSwitch(gpa, contents, defers, inst, .normal),
+        .switch_block_err_union => return zir.findTrackableSwitch(gpa, contents, defers, inst, .err_union),
 
         .suspend_block => @panic("TODO iterate suspend block"),
 
@@ -4373,7 +4385,7 @@ fn findDeclsInner(
             const inst_data = datas[@intFromEnum(inst)].pl_tok;
             const extra = zir.extraData(Inst.Param, inst_data.payload_index);
             const body = zir.bodySlice(extra.end, extra.data.body_len);
-            try zir.findDeclsBody(gpa, list, defers, body);
+            try zir.findTrackableBody(gpa, contents, defers, body);
         },
 
         inline .call, .field_call => |tag| {
@@ -4389,7 +4401,7 @@ fn findDeclsInner(
                 const first_arg_start_off = args_len;
                 const final_arg_end_off = zir.extra[extra.end + args_len - 1];
                 const args_body = zir.bodySlice(extra.end + first_arg_start_off, final_arg_end_off - first_arg_start_off);
-                try zir.findDeclsBody(gpa, list, defers, args_body);
+                try zir.findTrackableBody(gpa, contents, defers, args_body);
             }
         },
         .@"defer" => {
@@ -4397,7 +4409,7 @@ fn findDeclsInner(
             const gop = try defers.getOrPut(gpa, inst_data.index);
             if (!gop.found_existing) {
                 const body = zir.bodySlice(inst_data.index, inst_data.len);
-                try zir.findDeclsBody(gpa, list, defers, body);
+                try zir.findTrackableBody(gpa, contents, defers, body);
             }
         },
         .defer_err_code => {
@@ -4406,16 +4418,16 @@ fn findDeclsInner(
             const gop = try defers.getOrPut(gpa, extra.index);
             if (!gop.found_existing) {
                 const body = zir.bodySlice(extra.index, extra.len);
-                try zir.findDeclsBody(gpa, list, defers, body);
+                try zir.findTrackableBody(gpa, contents, defers, body);
             }
         },
     }
 }
 
-fn findDeclsSwitch(
+fn findTrackableSwitch(
     zir: Zir,
     gpa: Allocator,
-    list: *std.ArrayListUnmanaged(Inst.Index),
+    contents: *DeclContents,
     defers: *std.AutoHashMapUnmanaged(u32, void),
     inst: Inst.Index,
     /// Distinguishes between `switch_block[_ref]` and `switch_block_err_union`.
@@ -4451,7 +4463,7 @@ fn findDeclsSwitch(
             const body = zir.bodySlice(extra_index, prong_info.body_len);
             extra_index += body.len;
 
-            try zir.findDeclsBody(gpa, list, defers, body);
+            try zir.findTrackableBody(gpa, contents, defers, body);
 
             break :has_special extra.data.bits.has_else;
         },
@@ -4463,7 +4475,7 @@ fn findDeclsSwitch(
         const body = zir.bodySlice(extra_index, prong_info.body_len);
         extra_index += body.len;
 
-        try zir.findDeclsBody(gpa, list, defers, body);
+        try zir.findTrackableBody(gpa, contents, defers, body);
     }
 
     {
@@ -4475,7 +4487,7 @@ fn findDeclsSwitch(
             const body = zir.bodySlice(extra_index, prong_info.body_len);
             extra_index += body.len;
 
-            try zir.findDeclsBody(gpa, list, defers, body);
+            try zir.findTrackableBody(gpa, contents, defers, body);
         }
     }
     {
@@ -4492,20 +4504,20 @@ fn findDeclsSwitch(
             const body = zir.bodySlice(extra_index, prong_info.body_len);
             extra_index += body.len;
 
-            try zir.findDeclsBody(gpa, list, defers, body);
+            try zir.findTrackableBody(gpa, contents, defers, body);
         }
     }
 }
 
-fn findDeclsBody(
+fn findTrackableBody(
     zir: Zir,
     gpa: Allocator,
-    list: *std.ArrayListUnmanaged(Inst.Index),
+    contents: *DeclContents,
     defers: *std.AutoHashMapUnmanaged(u32, void),
     body: []const Inst.Index,
 ) Allocator.Error!void {
     for (body) |member| {
-        try zir.findDeclsInner(gpa, list, defers, member);
+        try zir.findTrackableInner(gpa, contents, defers, member);
     }
 }
 
src/Zcu.zig
@@ -2593,26 +2593,44 @@ pub fn mapOldZirToNew(
     defer match_stack.deinit(gpa);
 
     // Used as temporary buffers for namespace declaration instructions
-    var old_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .empty;
-    defer old_decls.deinit(gpa);
-    var new_decls: std.ArrayListUnmanaged(Zir.Inst.Index) = .empty;
-    defer new_decls.deinit(gpa);
+    var old_contents: Zir.DeclContents = .init;
+    defer old_contents.deinit(gpa);
+    var new_contents: Zir.DeclContents = .init;
+    defer new_contents.deinit(gpa);
 
     // Map the main struct inst (and anything in its fields)
     {
-        try old_zir.findDeclsRoot(gpa, &old_decls);
-        try new_zir.findDeclsRoot(gpa, &new_decls);
+        try old_zir.findTrackableRoot(gpa, &old_contents);
+        try new_zir.findTrackableRoot(gpa, &new_contents);
 
-        assert(old_decls.items[0] == .main_struct_inst);
-        assert(new_decls.items[0] == .main_struct_inst);
+        assert(old_contents.explicit_types.items[0] == .main_struct_inst);
+        assert(new_contents.explicit_types.items[0] == .main_struct_inst);
 
-        // We don't have any smart way of matching up these type declarations, so we always
-        // correlate them based on source order.
-        const n = @min(old_decls.items.len, new_decls.items.len);
-        try match_stack.ensureUnusedCapacity(gpa, n);
-        for (old_decls.items[0..n], new_decls.items[0..n]) |old_inst, new_inst| {
+        assert(old_contents.func_decl == null);
+        assert(new_contents.func_decl == null);
+
+        // We don't have any smart way of matching up these instructions, so we correlate them based on source order
+        // in their respective arrays.
+
+        const num_explicit_types = @min(old_contents.explicit_types.items.len, new_contents.explicit_types.items.len);
+        try match_stack.ensureUnusedCapacity(gpa, @intCast(num_explicit_types));
+        for (
+            old_contents.explicit_types.items[0..num_explicit_types],
+            new_contents.explicit_types.items[0..num_explicit_types],
+        ) |old_inst, new_inst| {
+            // Here we use `match_stack`, so that we will recursively consider declarations on these types.
             match_stack.appendAssumeCapacity(.{ .old_inst = old_inst, .new_inst = new_inst });
         }
+
+        const num_other = @min(old_contents.other.items.len, new_contents.other.items.len);
+        try inst_map.ensureUnusedCapacity(gpa, @intCast(num_other));
+        for (
+            old_contents.other.items[0..num_other],
+            new_contents.other.items[0..num_other],
+        ) |old_inst, new_inst| {
+            // These instructions don't have declarations, so we just modify `inst_map` directly.
+            inst_map.putAssumeCapacity(old_inst, new_inst);
+        }
     }
 
     while (match_stack.popOrNull()) |match_item| {
@@ -2700,17 +2718,39 @@ pub fn mapOldZirToNew(
             // Match the `declaration` instruction
             try inst_map.put(gpa, old_decl_inst, new_decl_inst);
 
-            // Find container type declarations within this declaration
-            try old_zir.findDecls(gpa, &old_decls, old_decl_inst);
-            try new_zir.findDecls(gpa, &new_decls, new_decl_inst);
+            // Find trackable instructions within this declaration
+            try old_zir.findTrackable(gpa, &old_contents, old_decl_inst);
+            try new_zir.findTrackable(gpa, &new_contents, new_decl_inst);
+
+            // We don't have any smart way of matching up these instructions, so we correlate them based on source order
+            // in their respective arrays.
 
-            // We don't have any smart way of matching up these type declarations, so we always
-            // correlate them based on source order.
-            const n = @min(old_decls.items.len, new_decls.items.len);
-            try match_stack.ensureUnusedCapacity(gpa, n);
-            for (old_decls.items[0..n], new_decls.items[0..n]) |old_inst, new_inst| {
+            const num_explicit_types = @min(old_contents.explicit_types.items.len, new_contents.explicit_types.items.len);
+            try match_stack.ensureUnusedCapacity(gpa, @intCast(num_explicit_types));
+            for (
+                old_contents.explicit_types.items[0..num_explicit_types],
+                new_contents.explicit_types.items[0..num_explicit_types],
+            ) |old_inst, new_inst| {
+                // Here we use `match_stack`, so that we will recursively consider declarations on these types.
                 match_stack.appendAssumeCapacity(.{ .old_inst = old_inst, .new_inst = new_inst });
             }
+
+            const num_other = @min(old_contents.other.items.len, new_contents.other.items.len);
+            try inst_map.ensureUnusedCapacity(gpa, @intCast(num_other));
+            for (
+                old_contents.other.items[0..num_other],
+                new_contents.other.items[0..num_other],
+            ) |old_inst, new_inst| {
+                // These instructions don't have declarations, so we just modify `inst_map` directly.
+                inst_map.putAssumeCapacity(old_inst, new_inst);
+            }
+
+            if (old_contents.func_decl) |old_func_inst| {
+                if (new_contents.func_decl) |new_func_inst| {
+                    // There are no declarations on a function either, so again, we just directly add it to `inst_map`.
+                    try inst_map.put(gpa, old_func_inst, new_func_inst);
+                }
+            }
         }
     }
 }