Commit 667b4f9054

Jacob Young <jacobly0@users.noreply.github.com>
2024-07-10 00:48:37
Zcu: cache fully qualified name on Decl
This avoids needing to mutate the intern pool from backends.
1 parent 95d9292
src/arch/riscv64/CodeGen.zig
@@ -933,7 +933,7 @@ fn formatDecl(
     _: std.fmt.FormatOptions,
     writer: anytype,
 ) @TypeOf(writer).Error!void {
-    try data.mod.declPtr(data.decl_index).renderFullyQualifiedName(data.mod, writer);
+    try writer.print("{}", .{data.mod.declPtr(data.decl_index).fqn.fmt(&data.mod.intern_pool)});
 }
 fn fmtDecl(func: *Func, decl_index: InternPool.DeclIndex) std.fmt.Formatter(formatDecl) {
     return .{ .data = .{
src/arch/wasm/CodeGen.zig
@@ -7284,8 +7284,8 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
     defer arena_allocator.deinit();
     const arena = arena_allocator.allocator();
 
-    const fqn = try mod.declPtr(enum_decl_index).fullyQualifiedName(pt);
-    const func_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{}", .{fqn.fmt(ip)});
+    const decl = mod.declPtr(enum_decl_index);
+    const func_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{}", .{decl.fqn.fmt(ip)});
 
     // check if we already generated code for this.
     if (func.bin_file.findGlobalSymbol(func_name)) |loc| {
src/arch/x86_64/CodeGen.zig
@@ -1077,7 +1077,7 @@ fn formatDecl(
     _: std.fmt.FormatOptions,
     writer: anytype,
 ) @TypeOf(writer).Error!void {
-    try data.zcu.declPtr(data.decl_index).renderFullyQualifiedName(data.zcu, writer);
+    try writer.print("{}", .{data.zcu.declPtr(data.decl_index).fqn.fmt(&data.zcu.intern_pool)});
 }
 fn fmtDecl(self: *Self, decl_index: InternPool.DeclIndex) std.fmt.Formatter(formatDecl) {
     return .{ .data = .{
src/codegen/c.zig
@@ -2194,13 +2194,9 @@ pub const DeclGen = struct {
         }) else {
             // MSVC has a limit of 4095 character token length limit, and fmtIdent can (worst case),
             // expand to 3x the length of its input, but let's cut it off at a much shorter limit.
-            var name: [100]u8 = undefined;
-            var name_stream = std.io.fixedBufferStream(&name);
-            decl.renderFullyQualifiedName(zcu, name_stream.writer()) catch |err| switch (err) {
-                error.NoSpaceLeft => {},
-            };
+            const fqn_slice = decl.fqn.toSlice(ip);
             try writer.print("{}__{d}", .{
-                fmtIdent(name_stream.getWritten()),
+                fmtIdent(fqn_slice[0..@min(fqn_slice.len, 100)]),
                 @intFromEnum(decl_index),
             });
         }
@@ -2587,11 +2583,9 @@ pub fn genTypeDecl(
                 try writer.writeByte(';');
                 const owner_decl = zcu.declPtr(owner_decl_index);
                 const owner_mod = zcu.namespacePtr(owner_decl.src_namespace).fileScope(zcu).mod;
-                if (!owner_mod.strip) {
-                    try writer.writeAll(" /* ");
-                    try owner_decl.renderFullyQualifiedName(zcu, writer);
-                    try writer.writeAll(" */");
-                }
+                if (!owner_mod.strip) try writer.print(" /* {} */", .{
+                    owner_decl.fqn.fmt(&zcu.intern_pool),
+                });
                 try writer.writeByte('\n');
             },
         },
@@ -4563,9 +4557,7 @@ fn airDbgInlineBlock(f: *Function, inst: Air.Inst.Index) !CValue {
     const extra = f.air.extraData(Air.DbgInlineBlock, ty_pl.payload);
     const owner_decl = zcu.funcOwnerDeclPtr(extra.data.func);
     const writer = f.object.writer();
-    try writer.writeAll("/* inline:");
-    try owner_decl.renderFullyQualifiedName(zcu, writer);
-    try writer.writeAll(" */\n");
+    try writer.print("/* inline:{} */\n", .{owner_decl.fqn.fmt(&zcu.intern_pool)});
     return lowerBlock(f, inst, @ptrCast(f.air.extra[extra.end..][0..extra.data.body_len]));
 }
 
src/codegen/llvm.zig
@@ -1744,7 +1744,7 @@ pub const Object = struct {
         if (export_indices.len != 0) {
             return updateExportedGlobal(self, zcu, global_index, export_indices);
         } else {
-            const fqn = try self.builder.strtabString((try decl.fullyQualifiedName(pt)).toSlice(ip));
+            const fqn = try self.builder.strtabString(decl.fqn.toSlice(ip));
             try global_index.rename(fqn, &self.builder);
             global_index.setLinkage(.internal, &self.builder);
             if (comp.config.dll_export_fns)
@@ -2863,10 +2863,7 @@ pub const Object = struct {
         const is_extern = decl.isExtern(zcu);
         const function_index = try o.builder.addFunction(
             try o.lowerType(zig_fn_type),
-            try o.builder.strtabString((if (is_extern)
-                decl.name
-            else
-                try decl.fullyQualifiedName(pt)).toSlice(ip)),
+            try o.builder.strtabString((if (is_extern) decl.name else decl.fqn).toSlice(ip)),
             toLlvmAddressSpace(decl.@"addrspace", target),
         );
         gop.value_ptr.* = function_index.ptrConst(&o.builder).global;
@@ -3077,14 +3074,12 @@ pub const Object = struct {
 
         const pt = o.pt;
         const zcu = pt.zcu;
+        const ip = &zcu.intern_pool;
         const decl = zcu.declPtr(decl_index);
         const is_extern = decl.isExtern(zcu);
 
         const variable_index = try o.builder.addVariable(
-            try o.builder.strtabString((if (is_extern)
-                decl.name
-            else
-                try decl.fullyQualifiedName(pt)).toSlice(&zcu.intern_pool)),
+            try o.builder.strtabString((if (is_extern) decl.name else decl.fqn).toSlice(ip)),
             try o.lowerType(decl.typeOf(zcu)),
             toLlvmGlobalAddressSpace(decl.@"addrspace", zcu.getTarget()),
         );
@@ -3312,7 +3307,7 @@ pub const Object = struct {
                         return int_ty;
                     }
 
-                    const fqn = try mod.declPtr(struct_type.decl.unwrap().?).fullyQualifiedName(pt);
+                    const decl = mod.declPtr(struct_type.decl.unwrap().?);
 
                     var llvm_field_types = std.ArrayListUnmanaged(Builder.Type){};
                     defer llvm_field_types.deinit(o.gpa);
@@ -3377,7 +3372,7 @@ pub const Object = struct {
                         );
                     }
 
-                    const ty = try o.builder.opaqueType(try o.builder.string(fqn.toSlice(ip)));
+                    const ty = try o.builder.opaqueType(try o.builder.string(decl.fqn.toSlice(ip)));
                     try o.type_map.put(o.gpa, t.toIntern(), ty);
 
                     o.builder.namedTypeSetBody(
@@ -3466,7 +3461,7 @@ pub const Object = struct {
                         return enum_tag_ty;
                     }
 
-                    const fqn = try mod.declPtr(union_obj.decl).fullyQualifiedName(pt);
+                    const decl = mod.declPtr(union_obj.decl);
 
                     const aligned_field_ty = Type.fromInterned(union_obj.field_types.get(ip)[layout.most_aligned_field]);
                     const aligned_field_llvm_ty = try o.lowerType(aligned_field_ty);
@@ -3486,7 +3481,7 @@ pub const Object = struct {
                     };
 
                     if (layout.tag_size == 0) {
-                        const ty = try o.builder.opaqueType(try o.builder.string(fqn.toSlice(ip)));
+                        const ty = try o.builder.opaqueType(try o.builder.string(decl.fqn.toSlice(ip)));
                         try o.type_map.put(o.gpa, t.toIntern(), ty);
 
                         o.builder.namedTypeSetBody(
@@ -3514,7 +3509,7 @@ pub const Object = struct {
                         llvm_fields_len += 1;
                     }
 
-                    const ty = try o.builder.opaqueType(try o.builder.string(fqn.toSlice(ip)));
+                    const ty = try o.builder.opaqueType(try o.builder.string(decl.fqn.toSlice(ip)));
                     try o.type_map.put(o.gpa, t.toIntern(), ty);
 
                     o.builder.namedTypeSetBody(
@@ -3527,8 +3522,7 @@ pub const Object = struct {
                     const gop = try o.type_map.getOrPut(o.gpa, t.toIntern());
                     if (!gop.found_existing) {
                         const decl = mod.declPtr(ip.loadOpaqueType(t.toIntern()).decl);
-                        const fqn = try decl.fullyQualifiedName(pt);
-                        gop.value_ptr.* = try o.builder.opaqueType(try o.builder.string(fqn.toSlice(ip)));
+                        gop.value_ptr.* = try o.builder.opaqueType(try o.builder.string(decl.fqn.toSlice(ip)));
                     }
                     return gop.value_ptr.*;
                 },
@@ -4587,11 +4581,11 @@ pub const Object = struct {
 
         const usize_ty = try o.lowerType(Type.usize);
         const ret_ty = try o.lowerType(Type.slice_const_u8_sentinel_0);
-        const fqn = try zcu.declPtr(enum_type.decl).fullyQualifiedName(pt);
+        const decl = zcu.declPtr(enum_type.decl);
         const target = zcu.root_mod.resolved_target.result;
         const function_index = try o.builder.addFunction(
             try o.builder.fnType(ret_ty, &.{try o.lowerType(Type.fromInterned(enum_type.tag_ty))}, .normal),
-            try o.builder.strtabStringFmt("__zig_tag_name_{}", .{fqn.fmt(ip)}),
+            try o.builder.strtabStringFmt("__zig_tag_name_{}", .{decl.fqn.fmt(ip)}),
             toLlvmAddressSpace(.generic, target),
         );
 
@@ -5175,8 +5169,6 @@ pub const FuncGen = struct {
             const line_number = decl.navSrcLine(zcu) + 1;
             self.inlined = self.wip.debug_location;
 
-            const fqn = try decl.fullyQualifiedName(pt);
-
             const fn_ty = try pt.funcType(.{
                 .param_types = &.{},
                 .return_type = .void_type,
@@ -5185,7 +5177,7 @@ pub const FuncGen = struct {
             self.scope = try o.builder.debugSubprogram(
                 self.file,
                 try o.builder.metadataString(decl.name.toSlice(&zcu.intern_pool)),
-                try o.builder.metadataString(fqn.toSlice(&zcu.intern_pool)),
+                try o.builder.metadataString(decl.fqn.toSlice(&zcu.intern_pool)),
                 line_number,
                 line_number + func.lbrace_line,
                 try o.lowerDebugType(fn_ty),
@@ -9702,18 +9694,19 @@ pub const FuncGen = struct {
         const o = self.dg.object;
         const pt = o.pt;
         const zcu = pt.zcu;
-        const enum_type = zcu.intern_pool.loadEnumType(enum_ty.toIntern());
+        const ip = &zcu.intern_pool;
+        const enum_type = ip.loadEnumType(enum_ty.toIntern());
 
         // TODO: detect when the type changes and re-emit this function.
         const gop = try o.named_enum_map.getOrPut(o.gpa, enum_type.decl);
         if (gop.found_existing) return gop.value_ptr.*;
         errdefer assert(o.named_enum_map.remove(enum_type.decl));
 
-        const fqn = try zcu.declPtr(enum_type.decl).fullyQualifiedName(pt);
+        const decl = zcu.declPtr(enum_type.decl);
         const target = zcu.root_mod.resolved_target.result;
         const function_index = try o.builder.addFunction(
             try o.builder.fnType(.i1, &.{try o.lowerType(Type.fromInterned(enum_type.tag_ty))}, .normal),
-            try o.builder.strtabStringFmt("__zig_is_named_enum_value_{}", .{fqn.fmt(&zcu.intern_pool)}),
+            try o.builder.strtabStringFmt("__zig_is_named_enum_value_{}", .{decl.fqn.fmt(ip)}),
             toLlvmAddressSpace(.generic, target),
         );
 
src/codegen/spirv.zig
@@ -3012,12 +3012,11 @@ const DeclGen = struct {
                 // Append the actual code into the functions section.
                 try self.spv.addFunction(spv_decl_index, self.func);
 
-                const fqn = try decl.fullyQualifiedName(self.pt);
-                try self.spv.debugName(result_id, fqn.toSlice(ip));
+                try self.spv.debugName(result_id, decl.fqn.toSlice(ip));
 
                 // Temporarily generate a test kernel declaration if this is a test function.
                 if (self.pt.zcu.test_functions.contains(self.decl_index)) {
-                    try self.generateTestEntryPoint(fqn.toSlice(ip), spv_decl_index);
+                    try self.generateTestEntryPoint(decl.fqn.toSlice(ip), spv_decl_index);
                 }
             },
             .global => {
@@ -3041,8 +3040,7 @@ const DeclGen = struct {
                     .storage_class = final_storage_class,
                 });
 
-                const fqn = try decl.fullyQualifiedName(self.pt);
-                try self.spv.debugName(result_id, fqn.toSlice(ip));
+                try self.spv.debugName(result_id, decl.fqn.toSlice(ip));
                 try self.spv.declareDeclDeps(spv_decl_index, &.{});
             },
             .invocation_global => {
@@ -3086,8 +3084,7 @@ const DeclGen = struct {
                     try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {});
                     try self.spv.addFunction(spv_decl_index, self.func);
 
-                    const fqn = try decl.fullyQualifiedName(self.pt);
-                    try self.spv.debugNameFmt(initializer_id, "initializer of {}", .{fqn.fmt(ip)});
+                    try self.spv.debugNameFmt(initializer_id, "initializer of {}", .{decl.fqn.fmt(ip)});
 
                     try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpExtInst, .{
                         .id_result_type = ptr_ty_id,
src/link/Elf/ZigObject.zig
@@ -907,10 +907,10 @@ fn updateDeclCode(
 ) !void {
     const gpa = elf_file.base.comp.gpa;
     const mod = pt.zcu;
+    const ip = &mod.intern_pool;
     const decl = mod.declPtr(decl_index);
-    const decl_name = try decl.fullyQualifiedName(pt);
 
-    log.debug("updateDeclCode {}{*}", .{ decl_name.fmt(&mod.intern_pool), decl });
+    log.debug("updateDeclCode {}{*}", .{ decl.fqn.fmt(ip), decl });
 
     const required_alignment = decl.getAlignment(pt).max(
         target_util.minFunctionAlignment(mod.getTarget()),
@@ -923,7 +923,7 @@ fn updateDeclCode(
     sym.output_section_index = shdr_index;
     atom_ptr.output_section_index = shdr_index;
 
-    sym.name_offset = try self.strtab.insert(gpa, decl_name.toSlice(&mod.intern_pool));
+    sym.name_offset = try self.strtab.insert(gpa, decl.fqn.toSlice(ip));
     atom_ptr.flags.alive = true;
     atom_ptr.name_offset = sym.name_offset;
     esym.st_name = sym.name_offset;
@@ -940,7 +940,7 @@ fn updateDeclCode(
         const need_realloc = code.len > capacity or !required_alignment.check(@intCast(atom_ptr.value));
         if (need_realloc) {
             try atom_ptr.grow(elf_file);
-            log.debug("growing {} from 0x{x} to 0x{x}", .{ decl_name.fmt(&mod.intern_pool), old_vaddr, atom_ptr.value });
+            log.debug("growing {} from 0x{x} to 0x{x}", .{ decl.fqn.fmt(ip), old_vaddr, atom_ptr.value });
             if (old_vaddr != atom_ptr.value) {
                 sym.value = 0;
                 esym.st_value = 0;
@@ -1007,11 +1007,11 @@ fn updateTlv(
     code: []const u8,
 ) !void {
     const mod = pt.zcu;
+    const ip = &mod.intern_pool;
     const gpa = mod.gpa;
     const decl = mod.declPtr(decl_index);
-    const decl_name = try decl.fullyQualifiedName(pt);
 
-    log.debug("updateTlv {} ({*})", .{ decl_name.fmt(&mod.intern_pool), decl });
+    log.debug("updateTlv {} ({*})", .{ decl.fqn.fmt(ip), decl });
 
     const required_alignment = decl.getAlignment(pt);
 
@@ -1023,7 +1023,7 @@ fn updateTlv(
     sym.output_section_index = shndx;
     atom_ptr.output_section_index = shndx;
 
-    sym.name_offset = try self.strtab.insert(gpa, decl_name.toSlice(&mod.intern_pool));
+    sym.name_offset = try self.strtab.insert(gpa, decl.fqn.toSlice(ip));
     atom_ptr.flags.alive = true;
     atom_ptr.name_offset = sym.name_offset;
     esym.st_value = 0;
@@ -1286,9 +1286,8 @@ pub fn lowerUnnamedConst(
     }
     const unnamed_consts = gop.value_ptr;
     const decl = mod.declPtr(decl_index);
-    const decl_name = try decl.fullyQualifiedName(pt);
     const index = unnamed_consts.items.len;
-    const name = try std.fmt.allocPrint(gpa, "__unnamed_{}_{d}", .{ decl_name.fmt(&mod.intern_pool), index });
+    const name = try std.fmt.allocPrint(gpa, "__unnamed_{}_{d}", .{ decl.fqn.fmt(&mod.intern_pool), index });
     defer gpa.free(name);
     const ty = val.typeOf(mod);
     const sym_index = switch (try self.lowerConst(
@@ -1473,9 +1472,8 @@ pub fn updateDeclLineNumber(
     defer tracy.end();
 
     const decl = pt.zcu.declPtr(decl_index);
-    const decl_name = try decl.fullyQualifiedName(pt);
 
-    log.debug("updateDeclLineNumber {}{*}", .{ decl_name.fmt(&pt.zcu.intern_pool), decl });
+    log.debug("updateDeclLineNumber {}{*}", .{ decl.fqn.fmt(&pt.zcu.intern_pool), decl });
 
     if (self.dwarf) |*dw| {
         try dw.updateDeclLineNumber(pt.zcu, decl_index);
src/link/MachO/ZigObject.zig
@@ -809,10 +809,10 @@ fn updateDeclCode(
 ) !void {
     const gpa = macho_file.base.comp.gpa;
     const mod = pt.zcu;
+    const ip = &mod.intern_pool;
     const decl = mod.declPtr(decl_index);
-    const decl_name = try decl.fullyQualifiedName(pt);
 
-    log.debug("updateDeclCode {}{*}", .{ decl_name.fmt(&mod.intern_pool), decl });
+    log.debug("updateDeclCode {}{*}", .{ decl.fqn.fmt(ip), decl });
 
     const required_alignment = decl.getAlignment(pt);
 
@@ -824,7 +824,7 @@ fn updateDeclCode(
     sym.out_n_sect = sect_index;
     atom.out_n_sect = sect_index;
 
-    sym.name = try self.strtab.insert(gpa, decl_name.toSlice(&mod.intern_pool));
+    sym.name = try self.strtab.insert(gpa, decl.fqn.toSlice(ip));
     atom.flags.alive = true;
     atom.name = sym.name;
     nlist.n_strx = sym.name;
@@ -843,7 +843,7 @@ fn updateDeclCode(
 
         if (need_realloc) {
             try atom.grow(macho_file);
-            log.debug("growing {} from 0x{x} to 0x{x}", .{ decl_name.fmt(&mod.intern_pool), old_vaddr, atom.value });
+            log.debug("growing {} from 0x{x} to 0x{x}", .{ decl.fqn.fmt(ip), old_vaddr, atom.value });
             if (old_vaddr != atom.value) {
                 sym.value = 0;
                 nlist.n_value = 0;
@@ -893,25 +893,22 @@ fn updateTlv(
     sect_index: u8,
     code: []const u8,
 ) !void {
+    const ip = &pt.zcu.intern_pool;
     const decl = pt.zcu.declPtr(decl_index);
-    const decl_name = try decl.fullyQualifiedName(pt);
 
-    log.debug("updateTlv {} ({*})", .{ decl_name.fmt(&pt.zcu.intern_pool), decl });
-
-    const decl_name_slice = decl_name.toSlice(&pt.zcu.intern_pool);
-    const required_alignment = decl.getAlignment(pt);
+    log.debug("updateTlv {} ({*})", .{ decl.fqn.fmt(&pt.zcu.intern_pool), decl });
 
     // 1. Lower TLV initializer
     const init_sym_index = try self.createTlvInitializer(
         macho_file,
-        decl_name_slice,
-        required_alignment,
+        decl.fqn.toSlice(ip),
+        decl.getAlignment(pt),
         sect_index,
         code,
     );
 
     // 2. Create TLV descriptor
-    try self.createTlvDescriptor(macho_file, sym_index, init_sym_index, decl_name_slice);
+    try self.createTlvDescriptor(macho_file, sym_index, init_sym_index, decl.fqn.toSlice(ip));
 }
 
 fn createTlvInitializer(
@@ -1099,9 +1096,8 @@ pub fn lowerUnnamedConst(
     }
     const unnamed_consts = gop.value_ptr;
     const decl = mod.declPtr(decl_index);
-    const decl_name = try decl.fullyQualifiedName(pt);
     const index = unnamed_consts.items.len;
-    const name = try std.fmt.allocPrint(gpa, "__unnamed_{}_{d}", .{ decl_name.fmt(&mod.intern_pool), index });
+    const name = try std.fmt.allocPrint(gpa, "__unnamed_{}_{d}", .{ decl.fqn.fmt(&mod.intern_pool), index });
     defer gpa.free(name);
     const sym_index = switch (try self.lowerConst(
         macho_file,
src/link/Wasm/ZigObject.zig
@@ -346,8 +346,7 @@ fn finishUpdateDecl(
     const atom_index = decl_info.atom;
     const atom = wasm_file.getAtomPtr(atom_index);
     const sym = zig_object.symbol(atom.sym_index);
-    const full_name = try decl.fullyQualifiedName(pt);
-    sym.name = try zig_object.string_table.insert(gpa, full_name.toSlice(ip));
+    sym.name = try zig_object.string_table.insert(gpa, decl.fqn.toSlice(ip));
     try atom.code.appendSlice(gpa, code);
     atom.size = @intCast(code.len);
 
@@ -387,7 +386,7 @@ fn finishUpdateDecl(
             // Will be freed upon freeing of decl or after cleanup of Wasm binary.
             const full_segment_name = try std.mem.concat(gpa, u8, &.{
                 segment_name,
-                full_name.toSlice(ip),
+                decl.fqn.toSlice(ip),
             });
             errdefer gpa.free(full_segment_name);
             sym.tag = .data;
@@ -436,9 +435,8 @@ pub fn getOrCreateAtomForDecl(
         const sym_index = try zig_object.allocateSymbol(gpa);
         gop.value_ptr.* = .{ .atom = try wasm_file.createAtom(sym_index, zig_object.index) };
         const decl = pt.zcu.declPtr(decl_index);
-        const full_name = try decl.fullyQualifiedName(pt);
         const sym = zig_object.symbol(sym_index);
-        sym.name = try zig_object.string_table.insert(gpa, full_name.toSlice(&pt.zcu.intern_pool));
+        sym.name = try zig_object.string_table.insert(gpa, decl.fqn.toSlice(&pt.zcu.intern_pool));
     }
     return gop.value_ptr.atom;
 }
@@ -494,9 +492,8 @@ pub fn lowerUnnamedConst(
     const parent_atom_index = try zig_object.getOrCreateAtomForDecl(wasm_file, pt, decl_index);
     const parent_atom = wasm_file.getAtom(parent_atom_index);
     const local_index = parent_atom.locals.items.len;
-    const fqn = try decl.fullyQualifiedName(pt);
     const name = try std.fmt.allocPrintZ(gpa, "__unnamed_{}_{d}", .{
-        fqn.fmt(&mod.intern_pool), local_index,
+        decl.fqn.fmt(&mod.intern_pool), local_index,
     });
     defer gpa.free(name);
 
@@ -1127,9 +1124,7 @@ pub fn updateDeclLineNumber(
 ) !void {
     if (zig_object.dwarf) |*dw| {
         const decl = pt.zcu.declPtr(decl_index);
-        const decl_name = try decl.fullyQualifiedName(pt);
-
-        log.debug("updateDeclLineNumber {}{*}", .{ decl_name.fmt(&pt.zcu.intern_pool), decl });
+        log.debug("updateDeclLineNumber {}{*}", .{ decl.fqn.fmt(&pt.zcu.intern_pool), decl });
         try dw.updateDeclLineNumber(pt.zcu, decl_index);
     }
 }
src/link/Coff.zig
@@ -1176,9 +1176,10 @@ pub fn lowerUnnamedConst(self: *Coff, pt: Zcu.PerThread, val: Value, decl_index:
         gop.value_ptr.* = .{};
     }
     const unnamed_consts = gop.value_ptr;
-    const decl_name = try decl.fullyQualifiedName(pt);
     const index = unnamed_consts.items.len;
-    const sym_name = try std.fmt.allocPrint(gpa, "__unnamed_{}_{d}", .{ decl_name.fmt(&mod.intern_pool), index });
+    const sym_name = try std.fmt.allocPrint(gpa, "__unnamed_{}_{d}", .{
+        decl.fqn.fmt(&mod.intern_pool), index,
+    });
     defer gpa.free(sym_name);
     const ty = val.typeOf(mod);
     const atom_index = switch (try self.lowerConst(pt, sym_name, val, ty.abiAlignment(pt), self.rdata_section_index.?, decl.navSrcLoc(mod))) {
@@ -1427,9 +1428,7 @@ fn updateDeclCode(self: *Coff, pt: Zcu.PerThread, decl_index: InternPool.DeclInd
     const mod = pt.zcu;
     const decl = mod.declPtr(decl_index);
 
-    const decl_name = try decl.fullyQualifiedName(pt);
-
-    log.debug("updateDeclCode {}{*}", .{ decl_name.fmt(&mod.intern_pool), decl });
+    log.debug("updateDeclCode {}{*}", .{ decl.fqn.fmt(&mod.intern_pool), decl });
     const required_alignment: u32 = @intCast(decl.getAlignment(pt).toByteUnits() orelse 0);
 
     const decl_metadata = self.decls.get(decl_index).?;
@@ -1441,7 +1440,7 @@ fn updateDeclCode(self: *Coff, pt: Zcu.PerThread, decl_index: InternPool.DeclInd
 
     if (atom.size != 0) {
         const sym = atom.getSymbolPtr(self);
-        try self.setSymbolName(sym, decl_name.toSlice(&mod.intern_pool));
+        try self.setSymbolName(sym, decl.fqn.toSlice(&mod.intern_pool));
         sym.section_number = @as(coff.SectionNumber, @enumFromInt(sect_index + 1));
         sym.type = .{ .complex_type = complex_type, .base_type = .NULL };
 
@@ -1449,7 +1448,7 @@ fn updateDeclCode(self: *Coff, pt: Zcu.PerThread, decl_index: InternPool.DeclInd
         const need_realloc = code.len > capacity or !mem.isAlignedGeneric(u64, sym.value, required_alignment);
         if (need_realloc) {
             const vaddr = try self.growAtom(atom_index, code_len, required_alignment);
-            log.debug("growing {} from 0x{x} to 0x{x}", .{ decl_name.fmt(&mod.intern_pool), sym.value, vaddr });
+            log.debug("growing {} from 0x{x} to 0x{x}", .{ decl.fqn.fmt(&mod.intern_pool), sym.value, vaddr });
             log.debug("  (required alignment 0x{x}", .{required_alignment});
 
             if (vaddr != sym.value) {
@@ -1465,13 +1464,13 @@ fn updateDeclCode(self: *Coff, pt: Zcu.PerThread, decl_index: InternPool.DeclInd
         self.getAtomPtr(atom_index).size = code_len;
     } else {
         const sym = atom.getSymbolPtr(self);
-        try self.setSymbolName(sym, decl_name.toSlice(&mod.intern_pool));
+        try self.setSymbolName(sym, decl.fqn.toSlice(&mod.intern_pool));
         sym.section_number = @as(coff.SectionNumber, @enumFromInt(sect_index + 1));
         sym.type = .{ .complex_type = complex_type, .base_type = .NULL };
 
         const vaddr = try self.allocateAtom(atom_index, code_len, required_alignment);
         errdefer self.freeAtom(atom_index);
-        log.debug("allocated atom for {} at 0x{x}", .{ decl_name.fmt(&mod.intern_pool), vaddr });
+        log.debug("allocated atom for {} at 0x{x}", .{ decl.fqn.fmt(&mod.intern_pool), vaddr });
         self.getAtomPtr(atom_index).size = code_len;
         sym.value = vaddr;
 
src/link/Dwarf.zig
@@ -1082,9 +1082,7 @@ pub fn initDeclState(self: *Dwarf, pt: Zcu.PerThread, decl_index: InternPool.Dec
     defer tracy.end();
 
     const decl = pt.zcu.declPtr(decl_index);
-    const decl_linkage_name = try decl.fullyQualifiedName(pt);
-
-    log.debug("initDeclState {}{*}", .{ decl_linkage_name.fmt(&pt.zcu.intern_pool), decl });
+    log.debug("initDeclState {}{*}", .{ decl.fqn.fmt(&pt.zcu.intern_pool), decl });
 
     const gpa = self.allocator;
     var decl_state: DeclState = .{
@@ -1157,7 +1155,7 @@ pub fn initDeclState(self: *Dwarf, pt: Zcu.PerThread, decl_index: InternPool.Dec
 
             // .debug_info subprogram
             const decl_name_slice = decl.name.toSlice(&pt.zcu.intern_pool);
-            const decl_linkage_name_slice = decl_linkage_name.toSlice(&pt.zcu.intern_pool);
+            const decl_linkage_name_slice = decl.fqn.toSlice(&pt.zcu.intern_pool);
             try dbg_info_buffer.ensureUnusedCapacity(1 + ptr_width_bytes + 4 + 4 +
                 (decl_name_slice.len + 1) + (decl_linkage_name_slice.len + 1));
 
src/link/Plan9.zig
@@ -483,11 +483,9 @@ pub fn lowerUnnamedConst(self: *Plan9, pt: Zcu.PerThread, val: Value, decl_index
     }
     const unnamed_consts = gop.value_ptr;
 
-    const decl_name = try decl.fullyQualifiedName(pt);
-
     const index = unnamed_consts.items.len;
     // name is freed when the unnamed const is freed
-    const name = try std.fmt.allocPrint(gpa, "__unnamed_{}_{d}", .{ decl_name.fmt(&mod.intern_pool), index });
+    const name = try std.fmt.allocPrint(gpa, "__unnamed_{}_{d}", .{ decl.fqn.fmt(&mod.intern_pool), index });
 
     const sym_index = try self.allocateSymbolIndex();
     const new_atom_idx = try self.createAtom();
src/Zcu/PerThread.zig
@@ -548,7 +548,7 @@ pub fn ensureDeclAnalyzed(pt: Zcu.PerThread, decl_index: Zcu.Decl.Index) Zcu.Sem
             };
         }
 
-        const decl_prog_node = mod.sema_prog_node.start((try decl.fullyQualifiedName(pt)).toSlice(ip), 0);
+        const decl_prog_node = mod.sema_prog_node.start(decl.fqn.toSlice(ip), 0);
         defer decl_prog_node.end();
 
         break :blk pt.semaDecl(decl_index) catch |err| switch (err) {
@@ -747,10 +747,9 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: Ai
     defer liveness.deinit(gpa);
 
     if (build_options.enable_debug_extensions and comp.verbose_air) {
-        const fqn = try decl.fullyQualifiedName(pt);
-        std.debug.print("# Begin Function AIR: {}:\n", .{fqn.fmt(ip)});
+        std.debug.print("# Begin Function AIR: {}:\n", .{decl.fqn.fmt(ip)});
         @import("../print_air.zig").dump(pt, air, liveness);
-        std.debug.print("# End Function AIR: {}\n\n", .{fqn.fmt(ip)});
+        std.debug.print("# End Function AIR: {}\n\n", .{decl.fqn.fmt(ip)});
     }
 
     if (std.debug.runtime_safety) {
@@ -781,7 +780,7 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: Ai
         };
     }
 
-    const codegen_prog_node = zcu.codegen_prog_node.start((try decl.fullyQualifiedName(pt)).toSlice(ip), 0);
+    const codegen_prog_node = zcu.codegen_prog_node.start(decl.fqn.toSlice(ip), 0);
     defer codegen_prog_node.end();
 
     if (!air.typesFullyResolved(zcu)) {
@@ -996,8 +995,8 @@ fn semaFile(pt: Zcu.PerThread, file_index: Zcu.File.Index) Zcu.SemaError!void {
     zcu.setFileRootDecl(file_index, new_decl_index.toOptional());
     zcu.namespacePtr(new_namespace_index).decl_index = new_decl_index;
 
-    new_decl.name = try file.fullyQualifiedName(pt);
-    new_decl.name_fully_qualified = true;
+    new_decl.fqn = try file.internFullyQualifiedName(pt);
+    new_decl.name = new_decl.fqn;
     new_decl.is_pub = true;
     new_decl.is_exported = false;
     new_decl.alignment = .none;
@@ -1058,10 +1057,8 @@ fn semaDecl(pt: Zcu.PerThread, decl_index: Zcu.Decl.Index) !Zcu.SemaDeclResult {
     }
 
     log.debug("semaDecl '{d}'", .{@intFromEnum(decl_index)});
-    log.debug("decl name '{}'", .{(try decl.fullyQualifiedName(pt)).fmt(ip)});
-    defer blk: {
-        log.debug("finish decl name '{}'", .{(decl.fullyQualifiedName(pt) catch break :blk).fmt(ip)});
-    }
+    log.debug("decl name '{}'", .{decl.fqn.fmt(ip)});
+    defer log.debug("finish decl name '{}'", .{decl.fqn.fmt(ip)});
 
     const old_has_tv = decl.has_tv;
     // The following values are ignored if `!old_has_tv`
@@ -1728,6 +1725,7 @@ const ScanDeclIter = struct {
             const was_exported = decl.is_exported;
             assert(decl.kind == kind); // ZIR tracking should preserve this
             decl.name = decl_name;
+            decl.fqn = try namespace.internFullyQualifiedName(pt, decl_name);
             decl.is_pub = declaration.flags.is_pub;
             decl.is_exported = declaration.flags.is_export;
             break :decl_index .{ was_exported, decl_index };
@@ -1737,6 +1735,7 @@ const ScanDeclIter = struct {
             const new_decl = zcu.declPtr(new_decl_index);
             new_decl.kind = kind;
             new_decl.name = decl_name;
+            new_decl.fqn = try namespace.internFullyQualifiedName(pt, decl_name);
             new_decl.is_pub = declaration.flags.is_pub;
             new_decl.is_exported = declaration.flags.is_export;
             new_decl.zir_decl_index = tracked_inst.toOptional();
@@ -1761,10 +1760,9 @@ const ScanDeclIter = struct {
                 if (!comp.config.is_test) break :a false;
                 if (decl_mod != zcu.main_mod) break :a false;
                 if (is_named_test and comp.test_filters.len > 0) {
-                    const decl_fqn = try namespace.fullyQualifiedName(pt, decl_name);
-                    const decl_fqn_slice = decl_fqn.toSlice(ip);
+                    const decl_fqn = decl.fqn.toSlice(ip);
                     for (comp.test_filters) |test_filter| {
-                        if (std.mem.indexOf(u8, decl_fqn_slice, test_filter)) |_| break;
+                        if (std.mem.indexOf(u8, decl_fqn, test_filter)) |_| break;
                     } else break :a false;
                 }
                 zcu.test_functions.putAssumeCapacity(decl_index, {}); // may clobber on incremental update
@@ -1805,12 +1803,10 @@ pub fn analyzeFnBody(pt: Zcu.PerThread, func_index: InternPool.Index, arena: All
     const decl_index = func.owner_decl;
     const decl = mod.declPtr(decl_index);
 
-    log.debug("func name '{}'", .{(try decl.fullyQualifiedName(pt)).fmt(ip)});
-    defer blk: {
-        log.debug("finish func name '{}'", .{(decl.fullyQualifiedName(pt) catch break :blk).fmt(ip)});
-    }
+    log.debug("func name '{}'", .{decl.fqn.fmt(ip)});
+    defer log.debug("finish func name '{}'", .{decl.fqn.fmt(ip)});
 
-    const decl_prog_node = mod.sema_prog_node.start((try decl.fullyQualifiedName(pt)).toSlice(ip), 0);
+    const decl_prog_node = mod.sema_prog_node.start(decl.fqn.toSlice(ip), 0);
     defer decl_prog_node.end();
 
     mod.intern_pool.removeDependenciesForDepender(gpa, InternPool.AnalUnit.wrap(.{ .func = func_index }));
@@ -2053,6 +2049,7 @@ pub fn allocateNewDecl(pt: Zcu.PerThread, namespace: Zcu.Namespace.Index) !Zcu.D
     const gpa = zcu.gpa;
     const decl_index = try zcu.intern_pool.createDecl(gpa, pt.tid, .{
         .name = undefined,
+        .fqn = undefined,
         .src_namespace = namespace,
         .has_tv = false,
         .owns_tv = false,
@@ -2077,6 +2074,25 @@ pub fn allocateNewDecl(pt: Zcu.PerThread, namespace: Zcu.Namespace.Index) !Zcu.D
     return decl_index;
 }
 
+pub fn initNewAnonDecl(
+    pt: Zcu.PerThread,
+    new_decl_index: Zcu.Decl.Index,
+    val: Value,
+    name: InternPool.NullTerminatedString,
+    fqn: InternPool.OptionalNullTerminatedString,
+) Allocator.Error!void {
+    const new_decl = pt.zcu.declPtr(new_decl_index);
+
+    new_decl.name = name;
+    new_decl.fqn = fqn.unwrap() orelse
+        try pt.zcu.namespacePtr(new_decl.src_namespace).internFullyQualifiedName(pt, name);
+    new_decl.val = val;
+    new_decl.alignment = .none;
+    new_decl.@"linksection" = .none;
+    new_decl.has_tv = true;
+    new_decl.analysis = .complete;
+}
+
 fn lockAndClearFileCompileError(pt: Zcu.PerThread, file: *Zcu.File) void {
     switch (file.status) {
         .success_zir, .retryable_failure => {},
@@ -2260,7 +2276,7 @@ pub fn populateTestFunctions(
 
         for (test_fn_vals, zcu.test_functions.keys()) |*test_fn_val, test_decl_index| {
             const test_decl = zcu.declPtr(test_decl_index);
-            const test_decl_name = try test_decl.fullyQualifiedName(pt);
+            const test_decl_name = test_decl.fqn;
             const test_decl_name_len = test_decl_name.length(ip);
             const test_name_anon_decl: InternPool.Key.Ptr.BaseAddr.AnonDecl = n: {
                 const test_name_ty = try pt.arrayType(.{
@@ -2366,7 +2382,7 @@ pub fn linkerUpdateDecl(pt: Zcu.PerThread, decl_index: Zcu.Decl.Index) !void {
 
     const decl = zcu.declPtr(decl_index);
 
-    const codegen_prog_node = zcu.codegen_prog_node.start((try decl.fullyQualifiedName(pt)).toSlice(&zcu.intern_pool), 0);
+    const codegen_prog_node = zcu.codegen_prog_node.start(decl.fqn.toSlice(&zcu.intern_pool), 0);
     defer codegen_prog_node.end();
 
     if (comp.bin_file) |lf| {
src/InternPool.zig
@@ -7955,6 +7955,7 @@ fn finishFuncInstance(
     const fn_owner_decl = ip.declPtr(ip.funcDeclOwner(generic_owner));
     const decl_index = try ip.createDecl(gpa, tid, .{
         .name = undefined,
+        .fqn = undefined,
         .src_namespace = fn_owner_decl.src_namespace,
         .has_tv = true,
         .owns_tv = true,
src/print_value.zig
@@ -299,8 +299,8 @@ fn printPtrDerivation(derivation: Value.PointerDeriveStep, writer: anytype, leve
             int.ptr_ty.fmt(pt),
             int.addr,
         }),
-        .decl_ptr => |decl| {
-            try zcu.declPtr(decl).renderFullyQualifiedName(zcu, writer);
+        .decl_ptr => |decl_index| {
+            try writer.print("{}", .{zcu.declPtr(decl_index).fqn.fmt(ip)});
         },
         .anon_decl_ptr => |anon| {
             const ty = Value.fromInterned(anon.val).typeOf(zcu);
src/Sema.zig
@@ -2878,7 +2878,7 @@ fn createAnonymousDeclTypeNamed(
     switch (name_strategy) {
         .anon => {}, // handled after switch
         .parent => {
-            try zcu.initNewAnonDecl(new_decl_index, val, block.type_name_ctx);
+            try pt.initNewAnonDecl(new_decl_index, val, block.type_name_ctx, .none);
             return new_decl_index;
         },
         .func => func_strat: {
@@ -2923,7 +2923,7 @@ fn createAnonymousDeclTypeNamed(
 
             try writer.writeByte(')');
             const name = try ip.getOrPutString(gpa, pt.tid, buf.items, .no_embedded_nulls);
-            try zcu.initNewAnonDecl(new_decl_index, val, name);
+            try pt.initNewAnonDecl(new_decl_index, val, name, .none);
             return new_decl_index;
         },
         .dbg_var => {
@@ -2937,7 +2937,7 @@ fn createAnonymousDeclTypeNamed(
                     const name = try ip.getOrPutStringFmt(gpa, pt.tid, "{}.{s}", .{
                         block.type_name_ctx.fmt(ip), zir_data[i].str_op.getStr(sema.code),
                     }, .no_embedded_nulls);
-                    try zcu.initNewAnonDecl(new_decl_index, val, name);
+                    try pt.initNewAnonDecl(new_decl_index, val, name, .none);
                     return new_decl_index;
                 },
                 else => {},
@@ -2958,7 +2958,7 @@ fn createAnonymousDeclTypeNamed(
     const name = ip.getOrPutStringFmt(gpa, pt.tid, "{}__{s}_{d}", .{
         block.type_name_ctx.fmt(ip), anon_prefix, @intFromEnum(new_decl_index),
     }, .no_embedded_nulls) catch unreachable;
-    try zcu.initNewAnonDecl(new_decl_index, val, name);
+    try pt.initNewAnonDecl(new_decl_index, val, name, .none);
     return new_decl_index;
 }
 
@@ -5527,13 +5527,12 @@ fn failWithBadStructFieldAccess(
     const zcu = pt.zcu;
     const ip = &zcu.intern_pool;
     const decl = zcu.declPtr(struct_type.decl.unwrap().?);
-    const fqn = try decl.fullyQualifiedName(pt);
 
     const msg = msg: {
         const msg = try sema.errMsg(
             field_src,
             "no field named '{}' in struct '{}'",
-            .{ field_name.fmt(ip), fqn.fmt(ip) },
+            .{ field_name.fmt(ip), decl.fqn.fmt(ip) },
         );
         errdefer msg.destroy(sema.gpa);
         try sema.errNote(struct_ty.srcLoc(zcu), msg, "struct declared here", .{});
@@ -5554,15 +5553,13 @@ fn failWithBadUnionFieldAccess(
     const zcu = pt.zcu;
     const ip = &zcu.intern_pool;
     const gpa = sema.gpa;
-
     const decl = zcu.declPtr(union_obj.decl);
-    const fqn = try decl.fullyQualifiedName(pt);
 
     const msg = msg: {
         const msg = try sema.errMsg(
             field_src,
             "no field named '{}' in union '{}'",
-            .{ field_name.fmt(ip), fqn.fmt(ip) },
+            .{ field_name.fmt(ip), decl.fqn.fmt(ip) },
         );
         errdefer msg.destroy(gpa);
         try sema.errNote(union_ty.srcLoc(zcu), msg, "union declared here", .{});
@@ -9733,6 +9730,9 @@ fn funcCommon(
             .generic_owner = sema.generic_owner,
             .comptime_args = sema.comptime_args,
         });
+        const func_decl = mod.declPtr(ip.indexToKey(func_index).func.owner_decl);
+        func_decl.fqn =
+            try ip.namespacePtr(func_decl.src_namespace).internFullyQualifiedName(pt, func_decl.name);
         return finishFunc(
             sema,
             block,
@@ -26500,7 +26500,7 @@ fn zirBuiltinExtern(
     const new_decl_index = try pt.allocateNewDecl(sema.owner_decl.src_namespace);
     errdefer pt.destroyDecl(new_decl_index);
     const new_decl = mod.declPtr(new_decl_index);
-    try mod.initNewAnonDecl(
+    try pt.initNewAnonDecl(
         new_decl_index,
         Value.fromInterned(
             if (Type.fromInterned(ptr_info.child).zigTypeTag(mod) == .Fn)
@@ -26522,6 +26522,7 @@ fn zirBuiltinExtern(
                 } }),
         ),
         options.name,
+        .none,
     );
     new_decl.owns_tv = true;
     // Note that this will queue the anon decl for codegen, so that the backend can
@@ -36735,24 +36736,23 @@ fn generateUnionTagTypeNumbered(
 
     const new_decl_index = try pt.allocateNewDecl(block.namespace);
     errdefer pt.destroyDecl(new_decl_index);
-    const fqn = try union_owner_decl.fullyQualifiedName(pt);
     const name = try ip.getOrPutStringFmt(
         gpa,
         pt.tid,
         "@typeInfo({}).Union.tag_type.?",
-        .{fqn.fmt(ip)},
+        .{union_owner_decl.fqn.fmt(ip)},
         .no_embedded_nulls,
     );
-    try mod.initNewAnonDecl(
+    try pt.initNewAnonDecl(
         new_decl_index,
         Value.@"unreachable",
         name,
+        name.toOptional(),
     );
     errdefer pt.abortAnonDecl(new_decl_index);
 
     const new_decl = mod.declPtr(new_decl_index);
     new_decl.owns_tv = true;
-    new_decl.name_fully_qualified = true;
 
     const enum_ty = try ip.getGeneratedTagEnumType(gpa, pt.tid, .{
         .decl = new_decl_index,
@@ -36784,22 +36784,21 @@ fn generateUnionTagTypeSimple(
     const gpa = sema.gpa;
 
     const new_decl_index = new_decl_index: {
-        const fqn = try union_owner_decl.fullyQualifiedName(pt);
         const new_decl_index = try pt.allocateNewDecl(block.namespace);
         errdefer pt.destroyDecl(new_decl_index);
         const name = try ip.getOrPutStringFmt(
             gpa,
             pt.tid,
             "@typeInfo({}).Union.tag_type.?",
-            .{fqn.fmt(ip)},
+            .{union_owner_decl.fqn.fmt(ip)},
             .no_embedded_nulls,
         );
-        try mod.initNewAnonDecl(
+        try pt.initNewAnonDecl(
             new_decl_index,
             Value.@"unreachable",
             name,
+            name.toOptional(),
         );
-        mod.declPtr(new_decl_index).name_fully_qualified = true;
         break :new_decl_index new_decl_index;
     };
     errdefer pt.abortAnonDecl(new_decl_index);
src/Type.zig
@@ -268,10 +268,10 @@ pub fn print(ty: Type, writer: anytype, pt: Zcu.PerThread) @TypeOf(writer).Error
             return;
         },
         .inferred_error_set_type => |func_index| {
-            try writer.writeAll("@typeInfo(@typeInfo(@TypeOf(");
             const owner_decl = mod.funcOwnerDeclPtr(func_index);
-            try owner_decl.renderFullyQualifiedName(mod, writer);
-            try writer.writeAll(")).Fn.return_type.?).ErrorUnion.error_set");
+            try writer.print("@typeInfo(@typeInfo(@TypeOf({})).Fn.return_type.?).ErrorUnion.error_set", .{
+                owner_decl.fqn.fmt(ip),
+            });
         },
         .error_set_type => |error_set_type| {
             const names = error_set_type.names;
@@ -334,7 +334,7 @@ pub fn print(ty: Type, writer: anytype, pt: Zcu.PerThread) @TypeOf(writer).Error
             const struct_type = ip.loadStructType(ty.toIntern());
             if (struct_type.decl.unwrap()) |decl_index| {
                 const decl = mod.declPtr(decl_index);
-                try decl.renderFullyQualifiedName(mod, writer);
+                try writer.print("{}", .{decl.fqn.fmt(ip)});
             } else if (ip.loadStructType(ty.toIntern()).namespace.unwrap()) |namespace_index| {
                 const namespace = mod.namespacePtr(namespace_index);
                 try namespace.renderFullyQualifiedName(mod, .empty, writer);
@@ -367,15 +367,15 @@ pub fn print(ty: Type, writer: anytype, pt: Zcu.PerThread) @TypeOf(writer).Error
 
         .union_type => {
             const decl = mod.declPtr(ip.loadUnionType(ty.toIntern()).decl);
-            try decl.renderFullyQualifiedName(mod, writer);
+            try writer.print("{}", .{decl.fqn.fmt(ip)});
         },
         .opaque_type => {
             const decl = mod.declPtr(ip.loadOpaqueType(ty.toIntern()).decl);
-            try decl.renderFullyQualifiedName(mod, writer);
+            try writer.print("{}", .{decl.fqn.fmt(ip)});
         },
         .enum_type => {
             const decl = mod.declPtr(ip.loadEnumType(ty.toIntern()).decl);
-            try decl.renderFullyQualifiedName(mod, writer);
+            try writer.print("{}", .{decl.fqn.fmt(ip)});
         },
         .func_type => |fn_info| {
             if (fn_info.is_noinline) {
src/Zcu.zig
@@ -326,7 +326,10 @@ pub const Reference = struct {
 };
 
 pub const Decl = struct {
+    /// Equal to `fqn` if already fully qualified.
     name: InternPool.NullTerminatedString,
+    /// Fully qualified name.
+    fqn: InternPool.NullTerminatedString,
     /// The most recent Value of the Decl after a successful semantic analysis.
     /// Populated when `has_tv`.
     val: Value,
@@ -384,8 +387,6 @@ pub const Decl = struct {
     is_pub: bool,
     /// Whether the corresponding AST decl has a `export` keyword.
     is_exported: bool,
-    /// If true `name` is already fully qualified.
-    name_fully_qualified: bool = false,
     /// What kind of a declaration is this.
     kind: Kind,
 
@@ -408,25 +409,6 @@ pub const Decl = struct {
         return extra.data.getBodies(@intCast(extra.end), zir);
     }
 
-    pub fn renderFullyQualifiedName(decl: Decl, zcu: *Zcu, writer: anytype) !void {
-        if (decl.name_fully_qualified) {
-            try writer.print("{}", .{decl.name.fmt(&zcu.intern_pool)});
-        } else {
-            try zcu.namespacePtr(decl.src_namespace).renderFullyQualifiedName(zcu, decl.name, writer);
-        }
-    }
-
-    pub fn renderFullyQualifiedDebugName(decl: Decl, zcu: *Zcu, writer: anytype) !void {
-        return zcu.namespacePtr(decl.src_namespace).renderFullyQualifiedDebugName(zcu, decl.name, writer);
-    }
-
-    pub fn fullyQualifiedName(decl: Decl, pt: Zcu.PerThread) !InternPool.NullTerminatedString {
-        return if (decl.name_fully_qualified)
-            decl.name
-        else
-            pt.zcu.namespacePtr(decl.src_namespace).fullyQualifiedName(pt, decl.name);
-    }
-
     pub fn typeOf(decl: Decl, zcu: *const Zcu) Type {
         assert(decl.has_tv);
         return decl.val.typeOf(zcu);
@@ -686,7 +668,7 @@ pub const Namespace = struct {
         if (name != .empty) try writer.print("{c}{}", .{ sep, name.fmt(&zcu.intern_pool) });
     }
 
-    pub fn fullyQualifiedName(
+    pub fn internFullyQualifiedName(
         ns: Namespace,
         pt: Zcu.PerThread,
         name: InternPool.NullTerminatedString,
@@ -882,7 +864,7 @@ pub const File = struct {
         };
     }
 
-    pub fn fullyQualifiedName(file: File, pt: Zcu.PerThread) !InternPool.NullTerminatedString {
+    pub fn internFullyQualifiedName(file: File, pt: Zcu.PerThread) !InternPool.NullTerminatedString {
         const gpa = pt.zcu.gpa;
         const ip = &pt.zcu.intern_pool;
         const strings = ip.getLocal(pt.tid).getMutableStrings(gpa);
@@ -3313,22 +3295,6 @@ pub fn errorSetBits(mod: *Module) u16 {
     return std.math.log2_int_ceil(ErrorInt, mod.error_limit + 1); // +1 for no error
 }
 
-pub fn initNewAnonDecl(
-    mod: *Module,
-    new_decl_index: Decl.Index,
-    val: Value,
-    name: InternPool.NullTerminatedString,
-) Allocator.Error!void {
-    const new_decl = mod.declPtr(new_decl_index);
-
-    new_decl.name = name;
-    new_decl.val = val;
-    new_decl.alignment = .none;
-    new_decl.@"linksection" = .none;
-    new_decl.has_tv = true;
-    new_decl.analysis = .complete;
-}
-
 pub fn errNote(
     mod: *Module,
     src_loc: LazySrcLoc,