Commit 275652f620

Andrew Kelley <andrew@ziglang.org>
2023-05-09 01:52:59
stage2: move opaque types to InternPool
1 parent e94a81c
src/arch/wasm/CodeGen.zig
@@ -764,8 +764,9 @@ pub fn deinit(func: *CodeGen) void {
 
 /// Sets `err_msg` on `CodeGen` and returns `error.CodegenFail` which is caught in link/Wasm.zig
 fn fail(func: *CodeGen, comptime fmt: []const u8, args: anytype) InnerError {
+    const mod = func.bin_file.base.options.module.?;
     const src = LazySrcLoc.nodeOffset(0);
-    const src_loc = src.toSrcLoc(func.decl);
+    const src_loc = src.toSrcLoc(func.decl, mod);
     func.err_msg = try Module.ErrorMsg.create(func.gpa, src_loc, fmt, args);
     return error.CodegenFail;
 }
@@ -6799,7 +6800,7 @@ fn airTagName(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
 
 fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
     const mod = func.bin_file.base.options.module.?;
-    const enum_decl_index = enum_ty.getOwnerDecl();
+    const enum_decl_index = enum_ty.getOwnerDecl(mod);
 
     var arena_allocator = std.heap.ArenaAllocator.init(func.gpa);
     defer arena_allocator.deinit();
src/arch/wasm/Emit.zig
@@ -254,7 +254,7 @@ fn fail(emit: *Emit, comptime format: []const u8, args: anytype) InnerError {
     @setCold(true);
     std.debug.assert(emit.error_msg == null);
     const mod = emit.bin_file.base.options.module.?;
-    emit.error_msg = try Module.ErrorMsg.create(emit.bin_file.base.allocator, mod.declPtr(emit.decl_index).srcLoc(), format, args);
+    emit.error_msg = try Module.ErrorMsg.create(emit.bin_file.base.allocator, mod.declPtr(emit.decl_index).srcLoc(mod), format, args);
     return error.EmitFail;
 }
 
src/arch/x86_64/CodeGen.zig
@@ -112,10 +112,10 @@ const Owner = union(enum) {
     mod_fn: *const Module.Fn,
     lazy_sym: link.File.LazySymbol,
 
-    fn getDecl(owner: Owner) Module.Decl.Index {
+    fn getDecl(owner: Owner, mod: *Module) Module.Decl.Index {
         return switch (owner) {
             .mod_fn => |mod_fn| mod_fn.owner_decl,
-            .lazy_sym => |lazy_sym| lazy_sym.ty.getOwnerDecl(),
+            .lazy_sym => |lazy_sym| lazy_sym.ty.getOwnerDecl(mod),
         };
     }
 
@@ -7926,6 +7926,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
 }
 
 fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void {
+    const mod = self.bin_file.options.module.?;
     switch (self.debug_output) {
         .dwarf => |dw| {
             const loc: link.File.Dwarf.DeclState.DbgInfoLoc = switch (mcv) {
@@ -7944,7 +7945,7 @@ fn genArgDbgInfo(self: Self, ty: Type, name: [:0]const u8, mcv: MCValue) !void {
             // TODO: this might need adjusting like the linkers do.
             // Instead of flattening the owner and passing Decl.Index here we may
             // want to special case LazySymbol in DWARF linker too.
-            try dw.genArgDbgInfo(name, ty, self.owner.getDecl(), loc);
+            try dw.genArgDbgInfo(name, ty, self.owner.getDecl(mod), loc);
         },
         .plan9 => {},
         .none => {},
@@ -7958,6 +7959,7 @@ fn genVarDbgInfo(
     mcv: MCValue,
     name: [:0]const u8,
 ) !void {
+    const mod = self.bin_file.options.module.?;
     const is_ptr = switch (tag) {
         .dbg_var_ptr => true,
         .dbg_var_val => false,
@@ -7988,7 +7990,7 @@ fn genVarDbgInfo(
             // TODO: this might need adjusting like the linkers do.
             // Instead of flattening the owner and passing Decl.Index here we may
             // want to special case LazySymbol in DWARF linker too.
-            try dw.genVarDbgInfo(name, ty, self.owner.getDecl(), is_ptr, loc);
+            try dw.genVarDbgInfo(name, ty, self.owner.getDecl(mod), is_ptr, loc);
         },
         .plan9 => {},
         .none => {},
@@ -10936,7 +10938,7 @@ fn airTagName(self: *Self, inst: Air.Inst.Index) !void {
     try self.genLazySymbolRef(
         .call,
         .rax,
-        link.File.LazySymbol.initDecl(.code, enum_ty.getOwnerDecl(), mod),
+        link.File.LazySymbol.initDecl(.code, enum_ty.getOwnerDecl(mod), mod),
     );
 
     return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none });
@@ -11651,7 +11653,8 @@ fn limitImmediateType(self: *Self, operand: Air.Inst.Ref, comptime T: type) !MCV
 }
 
 fn genTypedValue(self: *Self, arg_tv: TypedValue) InnerError!MCValue {
-    return switch (try codegen.genTypedValue(self.bin_file, self.src_loc, arg_tv, self.owner.getDecl())) {
+    const mod = self.bin_file.options.module.?;
+    return switch (try codegen.genTypedValue(self.bin_file, self.src_loc, arg_tv, self.owner.getDecl(mod))) {
         .mcv => |mcv| switch (mcv) {
             .none => .none,
             .undef => .undef,
src/codegen/c/type.zig
@@ -1538,7 +1538,7 @@ pub const CType = extern union {
                         .forward, .forward_parameter => {
                             self.storage = .{ .fwd = .{
                                 .base = .{ .tag = if (is_struct) .fwd_struct else .fwd_union },
-                                .data = ty.getOwnerDecl(),
+                                .data = ty.getOwnerDecl(mod),
                             } };
                             self.value = .{ .cty = initPayload(&self.storage.fwd) };
                         },
@@ -1985,7 +1985,7 @@ pub const CType = extern union {
                             const unnamed_pl = try arena.create(Payload.Unnamed);
                             unnamed_pl.* = .{ .base = .{ .tag = t }, .data = .{
                                 .fields = fields_pl,
-                                .owner_decl = ty.getOwnerDecl(),
+                                .owner_decl = ty.getOwnerDecl(mod),
                                 .id = if (ty.unionTagTypeSafety()) |_| 0 else unreachable,
                             } };
                             return initPayload(unnamed_pl);
@@ -2124,7 +2124,7 @@ pub const CType = extern union {
                             .forward, .forward_parameter, .complete, .parameter, .global => unreachable,
                             .payload => if (ty.unionTagTypeSafety()) |_| {
                                 const data = cty.cast(Payload.Unnamed).?.data;
-                                return ty.getOwnerDecl() == data.owner_decl and data.id == 0;
+                                return ty.getOwnerDecl(mod) == data.owner_decl and data.id == 0;
                             } else unreachable,
                         },
 
@@ -2242,7 +2242,7 @@ pub const CType = extern union {
                         => switch (self.kind) {
                             .forward, .forward_parameter, .complete, .parameter, .global => unreachable,
                             .payload => if (ty.unionTagTypeSafety()) |_| {
-                                autoHash(hasher, ty.getOwnerDecl());
+                                autoHash(hasher, ty.getOwnerDecl(mod));
                                 autoHash(hasher, @as(u32, 0));
                             } else unreachable,
                         },
src/codegen/spirv/Module.zig
@@ -390,8 +390,8 @@ pub fn addFunction(self: *Module, decl_index: Decl.Index, func: Fn) !void {
 /// Fetch the result-id of an OpString instruction that encodes the path of the source
 /// file of the decl. This function may also emit an OpSource with source-level information regarding
 /// the decl.
-pub fn resolveSourceFileName(self: *Module, decl: *ZigDecl) !IdRef {
-    const path = decl.getFileScope().sub_file_path;
+pub fn resolveSourceFileName(self: *Module, zig_module: *ZigModule, zig_decl: *ZigDecl) !IdRef {
+    const path = zig_decl.getFileScope(zig_module).sub_file_path;
     const result = try self.source_file_names.getOrPut(self.gpa, path);
     if (!result.found_existing) {
         const file_result_id = self.allocId();
src/codegen/c.zig
@@ -524,8 +524,9 @@ pub const DeclGen = struct {
 
     fn fail(dg: *DeclGen, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } {
         @setCold(true);
+        const mod = dg.module;
         const src = LazySrcLoc.nodeOffset(0);
-        const src_loc = src.toSrcLoc(dg.decl.?);
+        const src_loc = src.toSrcLoc(dg.decl.?, mod);
         dg.error_msg = try Module.ErrorMsg.create(dg.gpa, src_loc, format, args);
         return error.AnalysisFail;
     }
@@ -6484,6 +6485,7 @@ fn airGetUnionTag(f: *Function, inst: Air.Inst.Index) !CValue {
 }
 
 fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {
+    const mod = f.object.dg.module;
     const un_op = f.air.instructions.items(.data)[inst].un_op;
 
     const inst_ty = f.typeOfIndex(inst);
@@ -6495,7 +6497,7 @@ fn airTagName(f: *Function, inst: Air.Inst.Index) !CValue {
     const local = try f.allocLocal(inst, inst_ty);
     try f.writeCValue(writer, local, .Other);
     try writer.print(" = {s}(", .{
-        try f.getLazyFnName(.{ .tag_name = enum_ty.getOwnerDecl() }, .{ .tag_name = enum_ty }),
+        try f.getLazyFnName(.{ .tag_name = enum_ty.getOwnerDecl(mod) }, .{ .tag_name = enum_ty }),
     });
     try f.writeCValue(writer, operand, .Other);
     try writer.writeAll(");\n");
src/codegen/llvm.zig
@@ -1177,7 +1177,7 @@ pub const Object = struct {
         var di_scope: ?*llvm.DIScope = null;
 
         if (dg.object.di_builder) |dib| {
-            di_file = try dg.object.getDIFile(gpa, decl.src_namespace.file_scope);
+            di_file = try dg.object.getDIFile(gpa, mod.namespacePtr(decl.src_namespace).file_scope);
 
             const line_number = decl.src_line + 1;
             const is_internal_linkage = decl.val.tag() != .extern_fn and
@@ -1505,7 +1505,7 @@ pub const Object = struct {
                 return di_type;
             },
             .Enum => {
-                const owner_decl_index = ty.getOwnerDecl();
+                const owner_decl_index = ty.getOwnerDecl(mod);
                 const owner_decl = o.module.declPtr(owner_decl_index);
 
                 if (!ty.hasRuntimeBitsIgnoreComptime(mod)) {
@@ -1558,7 +1558,7 @@ pub const Object = struct {
                     @panic("TODO implement bigint debug enumerators to llvm int for 32-bit compiler builds");
                 }
 
-                const di_file = try o.getDIFile(gpa, owner_decl.src_namespace.file_scope);
+                const di_file = try o.getDIFile(gpa, mod.namespacePtr(owner_decl.src_namespace).file_scope);
                 const di_scope = try o.namespaceToDebugScope(owner_decl.src_namespace);
 
                 const name = try ty.nameAlloc(gpa, o.module);
@@ -1737,13 +1737,13 @@ pub const Object = struct {
                 }
                 const name = try ty.nameAlloc(gpa, o.module);
                 defer gpa.free(name);
-                const owner_decl_index = ty.getOwnerDecl();
+                const owner_decl_index = ty.getOwnerDecl(mod);
                 const owner_decl = o.module.declPtr(owner_decl_index);
                 const opaque_di_ty = dib.createForwardDeclType(
                     DW.TAG.structure_type,
                     name,
                     try o.namespaceToDebugScope(owner_decl.src_namespace),
-                    try o.getDIFile(gpa, owner_decl.src_namespace.file_scope),
+                    try o.getDIFile(gpa, mod.namespacePtr(owner_decl.src_namespace).file_scope),
                     owner_decl.src_node + 1,
                 );
                 // The recursive call to `lowerDebugType` va `namespaceToDebugScope`
@@ -2085,7 +2085,7 @@ pub const Object = struct {
                         // into. Therefore we can satisfy this by making an empty namespace,
                         // rather than changing the frontend to unnecessarily resolve the
                         // struct field types.
-                        const owner_decl_index = ty.getOwnerDecl();
+                        const owner_decl_index = ty.getOwnerDecl(mod);
                         const struct_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index);
                         dib.replaceTemporary(fwd_decl, struct_di_ty);
                         // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType`
@@ -2096,7 +2096,7 @@ pub const Object = struct {
                 }
 
                 if (!ty.hasRuntimeBitsIgnoreComptime(mod)) {
-                    const owner_decl_index = ty.getOwnerDecl();
+                    const owner_decl_index = ty.getOwnerDecl(mod);
                     const struct_di_ty = try o.makeEmptyNamespaceDIType(owner_decl_index);
                     dib.replaceTemporary(fwd_decl, struct_di_ty);
                     // The recursive call to `lowerDebugType` via `makeEmptyNamespaceDIType`
@@ -2162,7 +2162,7 @@ pub const Object = struct {
             },
             .Union => {
                 const compile_unit_scope = o.di_compile_unit.?.toScope();
-                const owner_decl_index = ty.getOwnerDecl();
+                const owner_decl_index = ty.getOwnerDecl(mod);
 
                 const name = try ty.nameAlloc(gpa, o.module);
                 defer gpa.free(name);
@@ -2395,8 +2395,10 @@ pub const Object = struct {
         }
     }
 
-    fn namespaceToDebugScope(o: *Object, namespace: *const Module.Namespace) !*llvm.DIScope {
-        if (namespace.parent == null) {
+    fn namespaceToDebugScope(o: *Object, namespace_index: Module.Namespace.Index) !*llvm.DIScope {
+        const mod = o.module;
+        const namespace = mod.namespacePtr(namespace_index);
+        if (namespace.parent == .none) {
             const di_file = try o.getDIFile(o.gpa, namespace.file_scope);
             return di_file.toScope();
         }
@@ -2408,12 +2410,13 @@ pub const Object = struct {
     /// Assertion `!isa<DIType>(Scope) && "shouldn't make a namespace scope for a type"'
     /// when targeting CodeView (Windows).
     fn makeEmptyNamespaceDIType(o: *Object, decl_index: Module.Decl.Index) !*llvm.DIType {
-        const decl = o.module.declPtr(decl_index);
+        const mod = o.module;
+        const decl = mod.declPtr(decl_index);
         const fields: [0]*llvm.DIType = .{};
         return o.di_builder.?.createStructType(
             try o.namespaceToDebugScope(decl.src_namespace),
             decl.name, // TODO use fully qualified name
-            try o.getDIFile(o.gpa, decl.src_namespace.file_scope),
+            try o.getDIFile(o.gpa, mod.namespacePtr(decl.src_namespace).file_scope),
             decl.src_line + 1,
             0, // size in bits
             0, // align in bits
@@ -2434,14 +2437,14 @@ pub const Object = struct {
         const std_file = (mod.importPkg(std_pkg) catch unreachable).file;
 
         const builtin_str: []const u8 = "builtin";
-        const std_namespace = mod.declPtr(std_file.root_decl.unwrap().?).src_namespace;
+        const std_namespace = mod.namespacePtr(mod.declPtr(std_file.root_decl.unwrap().?).src_namespace);
         const builtin_decl = std_namespace.decls
             .getKeyAdapted(builtin_str, Module.DeclAdapter{ .mod = mod }).?;
 
         const stack_trace_str: []const u8 = "StackTrace";
         // buffer is only used for int_type, `builtin` is a struct.
         const builtin_ty = mod.declPtr(builtin_decl).val.toType();
-        const builtin_namespace = builtin_ty.getNamespace().?;
+        const builtin_namespace = builtin_ty.getNamespace(mod).?;
         const stack_trace_decl_index = builtin_namespace.decls
             .getKeyAdapted(stack_trace_str, Module.DeclAdapter{ .mod = mod }).?;
         const stack_trace_decl = mod.declPtr(stack_trace_decl_index);
@@ -2464,7 +2467,8 @@ pub const DeclGen = struct {
     fn todo(self: *DeclGen, comptime format: []const u8, args: anytype) Error {
         @setCold(true);
         assert(self.err_msg == null);
-        const src_loc = LazySrcLoc.nodeOffset(0).toSrcLoc(self.decl);
+        const mod = self.module;
+        const src_loc = LazySrcLoc.nodeOffset(0).toSrcLoc(self.decl, mod);
         self.err_msg = try Module.ErrorMsg.create(self.gpa, src_loc, "TODO (LLVM): " ++ format, args);
         return error.CodegenFail;
     }
@@ -2536,7 +2540,7 @@ pub const DeclGen = struct {
             }
 
             if (dg.object.di_builder) |dib| {
-                const di_file = try dg.object.getDIFile(dg.gpa, decl.src_namespace.file_scope);
+                const di_file = try dg.object.getDIFile(dg.gpa, mod.namespacePtr(decl.src_namespace).file_scope);
 
                 const line_number = decl.src_line + 1;
                 const is_internal_linkage = !dg.module.decl_exports.contains(decl_index);
@@ -2837,15 +2841,11 @@ pub const DeclGen = struct {
             .Opaque => {
                 if (t.ip_index == .anyopaque_type) return dg.context.intType(8);
 
-                const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .mod = dg.module });
+                const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .mod = mod });
                 if (gop.found_existing) return gop.value_ptr.*;
 
-                // The Type memory is ephemeral; since we want to store a longer-lived
-                // reference, we need to copy it here.
-                gop.key_ptr.* = try t.copy(dg.object.type_map_arena.allocator());
-
-                const opaque_obj = t.castTag(.@"opaque").?.data;
-                const name = try opaque_obj.getFullyQualifiedName(dg.module);
+                const opaque_type = mod.intern_pool.indexToKey(t.ip_index).opaque_type;
+                const name = try mod.opaqueFullyQualifiedName(opaque_type);
                 defer gpa.free(name);
 
                 const llvm_struct_ty = dg.context.structCreateNamed(name);
@@ -2931,7 +2931,7 @@ pub const DeclGen = struct {
             },
             .ErrorSet => return dg.context.intType(16),
             .Struct => {
-                const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .mod = dg.module });
+                const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .mod = mod });
                 if (gop.found_existing) return gop.value_ptr.*;
 
                 // The Type memory is ephemeral; since we want to store a longer-lived
@@ -2999,7 +2999,7 @@ pub const DeclGen = struct {
                     return int_llvm_ty;
                 }
 
-                const name = try struct_obj.getFullyQualifiedName(dg.module);
+                const name = try struct_obj.getFullyQualifiedName(mod);
                 defer gpa.free(name);
 
                 const llvm_struct_ty = dg.context.structCreateNamed(name);
@@ -3057,7 +3057,7 @@ pub const DeclGen = struct {
                 return llvm_struct_ty;
             },
             .Union => {
-                const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .mod = dg.module });
+                const gop = try dg.object.type_map.getOrPutContext(gpa, t, .{ .mod = mod });
                 if (gop.found_existing) return gop.value_ptr.*;
 
                 // The Type memory is ephemeral; since we want to store a longer-lived
@@ -3080,7 +3080,7 @@ pub const DeclGen = struct {
                     return enum_tag_llvm_ty;
                 }
 
-                const name = try union_obj.getFullyQualifiedName(dg.module);
+                const name = try union_obj.getFullyQualifiedName(mod);
                 defer gpa.free(name);
 
                 const llvm_union_ty = dg.context.structCreateNamed(name);
@@ -6131,7 +6131,7 @@ pub const FuncGen = struct {
         const func = self.air.values[ty_pl.payload].castTag(.function).?.data;
         const decl_index = func.owner_decl;
         const decl = mod.declPtr(decl_index);
-        const di_file = try self.dg.object.getDIFile(self.gpa, decl.src_namespace.file_scope);
+        const di_file = try self.dg.object.getDIFile(self.gpa, mod.namespacePtr(decl.src_namespace).file_scope);
         self.di_file = di_file;
         const line_number = decl.src_line + 1;
         const cur_debug_location = self.builder.getCurrentDebugLocation2();
@@ -6193,7 +6193,7 @@ pub const FuncGen = struct {
         const func = self.air.values[ty_pl.payload].castTag(.function).?.data;
         const mod = self.dg.module;
         const decl = mod.declPtr(func.owner_decl);
-        const di_file = try self.dg.object.getDIFile(self.gpa, decl.src_namespace.file_scope);
+        const di_file = try self.dg.object.getDIFile(self.gpa, mod.namespacePtr(decl.src_namespace).file_scope);
         self.di_file = di_file;
         const old = self.dbg_inlined.pop();
         self.di_scope = old.scope;
@@ -8853,7 +8853,8 @@ pub const FuncGen = struct {
     }
 
     fn getIsNamedEnumValueFunction(self: *FuncGen, enum_ty: Type) !*llvm.Value {
-        const enum_decl = enum_ty.getOwnerDecl();
+        const mod = self.dg.module;
+        const enum_decl = enum_ty.getOwnerDecl(mod);
 
         // TODO: detect when the type changes and re-emit this function.
         const gop = try self.dg.object.named_enum_map.getOrPut(self.dg.gpa, enum_decl);
@@ -8864,7 +8865,6 @@ pub const FuncGen = struct {
         defer arena_allocator.deinit();
         const arena = arena_allocator.allocator();
 
-        const mod = self.dg.module;
         const fqn = try mod.declPtr(enum_decl).getFullyQualifiedName(mod);
         defer self.gpa.free(fqn);
         const llvm_fn_name = try std.fmt.allocPrintZ(arena, "__zig_is_named_enum_value_{s}", .{fqn});
@@ -8931,7 +8931,8 @@ pub const FuncGen = struct {
     }
 
     fn getEnumTagNameFunction(self: *FuncGen, enum_ty: Type) !*llvm.Value {
-        const enum_decl = enum_ty.getOwnerDecl();
+        const mod = self.dg.module;
+        const enum_decl = enum_ty.getOwnerDecl(mod);
 
         // TODO: detect when the type changes and re-emit this function.
         const gop = try self.dg.object.decl_map.getOrPut(self.dg.gpa, enum_decl);
@@ -8942,7 +8943,6 @@ pub const FuncGen = struct {
         defer arena_allocator.deinit();
         const arena = arena_allocator.allocator();
 
-        const mod = self.dg.module;
         const fqn = try mod.declPtr(enum_decl).getFullyQualifiedName(mod);
         defer self.gpa.free(fqn);
         const llvm_fn_name = try std.fmt.allocPrintZ(arena, "__zig_tag_name_{s}", .{fqn});
src/codegen/spirv.zig
@@ -218,8 +218,9 @@ pub const DeclGen = struct {
 
     pub fn fail(self: *DeclGen, comptime format: []const u8, args: anytype) Error {
         @setCold(true);
+        const mod = self.module;
         const src = LazySrcLoc.nodeOffset(0);
-        const src_loc = src.toSrcLoc(self.module.declPtr(self.decl_index));
+        const src_loc = src.toSrcLoc(self.module.declPtr(self.decl_index), mod);
         assert(self.error_msg == null);
         self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, format, args);
         return error.CodegenFail;
@@ -2775,7 +2776,10 @@ pub const DeclGen = struct {
 
     fn airDbgStmt(self: *DeclGen, inst: Air.Inst.Index) !void {
         const dbg_stmt = self.air.instructions.items(.data)[inst].dbg_stmt;
-        const src_fname_id = try self.spv.resolveSourceFileName(self.module.declPtr(self.decl_index));
+        const src_fname_id = try self.spv.resolveSourceFileName(
+            self.module,
+            self.module.declPtr(self.decl_index),
+        );
         try self.func.body.emit(self.spv.gpa, .OpLine, .{
             .file = src_fname_id,
             .line = dbg_stmt.line,
@@ -3192,6 +3196,7 @@ pub const DeclGen = struct {
     }
 
     fn airAssembly(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
+        const mod = self.module;
         const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
         const extra = self.air.extraData(Air.Asm, ty_pl.payload);
 
@@ -3274,7 +3279,7 @@ pub const DeclGen = struct {
                 assert(as.errors.items.len != 0);
                 assert(self.error_msg == null);
                 const loc = LazySrcLoc.nodeOffset(0);
-                const src_loc = loc.toSrcLoc(self.module.declPtr(self.decl_index));
+                const src_loc = loc.toSrcLoc(self.module.declPtr(self.decl_index), mod);
                 self.error_msg = try Module.ErrorMsg.create(self.module.gpa, src_loc, "failed to assemble SPIR-V inline assembly", .{});
                 const notes = try self.module.gpa.alloc(Module.ErrorMsg, as.errors.items.len);
 
src/link/Coff.zig
@@ -1032,20 +1032,20 @@ fn freeAtom(self: *Coff, atom_index: Atom.Index) void {
     self.getAtomPtr(atom_index).sym_index = 0;
 }
 
-pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
+pub fn updateFunc(self: *Coff, mod: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
     if (build_options.skip_non_native and builtin.object_format != .coff) {
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
     if (build_options.have_llvm) {
         if (self.llvm_object) |llvm_object| {
-            return llvm_object.updateFunc(module, func, air, liveness);
+            return llvm_object.updateFunc(mod, func, air, liveness);
         }
     }
     const tracy = trace(@src());
     defer tracy.end();
 
     const decl_index = func.owner_decl;
-    const decl = module.declPtr(decl_index);
+    const decl = mod.declPtr(decl_index);
 
     const atom_index = try self.getOrCreateAtomForDecl(decl_index);
     self.freeUnnamedConsts(decl_index);
@@ -1056,7 +1056,7 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
 
     const res = try codegen.generateFunction(
         &self.base,
-        decl.srcLoc(),
+        decl.srcLoc(mod),
         func,
         air,
         liveness,
@@ -1067,7 +1067,7 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
         .ok => code_buffer.items,
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl_index, em);
+            try mod.failed_decls.put(mod.gpa, decl_index, em);
             return;
         },
     };
@@ -1076,7 +1076,7 @@ pub fn updateFunc(self: *Coff, module: *Module, func: *Module.Fn, air: Air, live
 
     // Since we updated the vaddr and the size, each corresponding export
     // symbol also needs to be updated.
-    return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
+    return self.updateDeclExports(mod, decl_index, mod.getDeclExports(decl_index));
 }
 
 pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.Index) !u32 {
@@ -1110,7 +1110,7 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In
         sym.section_number = @intToEnum(coff.SectionNumber, self.rdata_section_index.? + 1);
     }
 
-    const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), tv, &code_buffer, .none, .{
+    const res = try codegen.generateSymbol(&self.base, decl.srcLoc(mod), tv, &code_buffer, .none, .{
         .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,
     });
     var code = switch (res) {
@@ -1141,19 +1141,19 @@ pub fn lowerUnnamedConst(self: *Coff, tv: TypedValue, decl_index: Module.Decl.In
 
 pub fn updateDecl(
     self: *Coff,
-    module: *Module,
+    mod: *Module,
     decl_index: Module.Decl.Index,
 ) link.File.UpdateDeclError!void {
     if (build_options.skip_non_native and builtin.object_format != .coff) {
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
     if (build_options.have_llvm) {
-        if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl_index);
+        if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(mod, decl_index);
     }
     const tracy = trace(@src());
     defer tracy.end();
 
-    const decl = module.declPtr(decl_index);
+    const decl = mod.declPtr(decl_index);
 
     if (decl.val.tag() == .extern_fn) {
         return; // TODO Should we do more when front-end analyzed extern decl?
@@ -1173,7 +1173,7 @@ pub fn updateDecl(
     defer code_buffer.deinit();
 
     const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
-    const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
+    const res = try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{
         .ty = decl.ty,
         .val = decl_val,
     }, &code_buffer, .none, .{
@@ -1183,7 +1183,7 @@ pub fn updateDecl(
         .ok => code_buffer.items,
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl_index, em);
+            try mod.failed_decls.put(mod.gpa, decl_index, em);
             return;
         },
     };
@@ -1192,7 +1192,7 @@ pub fn updateDecl(
 
     // Since we updated the vaddr and the size, each corresponding export
     // symbol also needs to be updated.
-    return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
+    return self.updateDeclExports(mod, decl_index, mod.getDeclExports(decl_index));
 }
 
 fn updateLazySymbolAtom(
@@ -1217,8 +1217,8 @@ fn updateLazySymbolAtom(
     const atom = self.getAtomPtr(atom_index);
     const local_sym_index = atom.getSymbolIndex().?;
 
-    const src = if (sym.ty.getOwnerDeclOrNull()) |owner_decl|
-        mod.declPtr(owner_decl).srcLoc()
+    const src = if (sym.ty.getOwnerDeclOrNull(mod)) |owner_decl|
+        mod.declPtr(owner_decl).srcLoc(mod)
     else
         Module.SrcLoc{
             .file_scope = undefined,
@@ -1262,7 +1262,8 @@ fn updateLazySymbolAtom(
 }
 
 pub fn getOrCreateAtomForLazySymbol(self: *Coff, sym: link.File.LazySymbol) !Atom.Index {
-    const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl());
+    const mod = self.base.options.module.?;
+    const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl(mod));
     errdefer _ = if (!gop.found_existing) self.lazy_syms.pop();
     if (!gop.found_existing) gop.value_ptr.* = .{};
     const metadata: struct { atom: *Atom.Index, state: *LazySymbolMetadata.State } = switch (sym.kind) {
@@ -1277,7 +1278,7 @@ pub fn getOrCreateAtomForLazySymbol(self: *Coff, sym: link.File.LazySymbol) !Ato
     metadata.state.* = .pending_flush;
     const atom = metadata.atom.*;
     // anyerror needs to be deferred until flushModule
-    if (sym.getDecl() != .none) try self.updateLazySymbolAtom(sym, atom, switch (sym.kind) {
+    if (sym.getDecl(mod) != .none) try self.updateLazySymbolAtom(sym, atom, switch (sym.kind) {
         .code => self.text_section_index.?,
         .const_data => self.rdata_section_index.?,
     });
@@ -1411,7 +1412,7 @@ pub fn freeDecl(self: *Coff, decl_index: Module.Decl.Index) void {
 
 pub fn updateDeclExports(
     self: *Coff,
-    module: *Module,
+    mod: *Module,
     decl_index: Module.Decl.Index,
     exports: []const *Module.Export,
 ) link.File.UpdateDeclExportsError!void {
@@ -1423,7 +1424,7 @@ pub fn updateDeclExports(
         // Even in the case of LLVM, we need to notice certain exported symbols in order to
         // detect the default subsystem.
         for (exports) |exp| {
-            const exported_decl = module.declPtr(exp.exported_decl);
+            const exported_decl = mod.declPtr(exp.exported_decl);
             if (exported_decl.getFunction() == null) continue;
             const winapi_cc = switch (self.base.options.target.cpu.arch) {
                 .x86 => std.builtin.CallingConvention.Stdcall,
@@ -1433,23 +1434,23 @@ pub fn updateDeclExports(
             if (decl_cc == .C and mem.eql(u8, exp.options.name, "main") and
                 self.base.options.link_libc)
             {
-                module.stage1_flags.have_c_main = true;
+                mod.stage1_flags.have_c_main = true;
             } else if (decl_cc == winapi_cc and self.base.options.target.os.tag == .windows) {
                 if (mem.eql(u8, exp.options.name, "WinMain")) {
-                    module.stage1_flags.have_winmain = true;
+                    mod.stage1_flags.have_winmain = true;
                 } else if (mem.eql(u8, exp.options.name, "wWinMain")) {
-                    module.stage1_flags.have_wwinmain = true;
+                    mod.stage1_flags.have_wwinmain = true;
                 } else if (mem.eql(u8, exp.options.name, "WinMainCRTStartup")) {
-                    module.stage1_flags.have_winmain_crt_startup = true;
+                    mod.stage1_flags.have_winmain_crt_startup = true;
                 } else if (mem.eql(u8, exp.options.name, "wWinMainCRTStartup")) {
-                    module.stage1_flags.have_wwinmain_crt_startup = true;
+                    mod.stage1_flags.have_wwinmain_crt_startup = true;
                 } else if (mem.eql(u8, exp.options.name, "DllMainCRTStartup")) {
-                    module.stage1_flags.have_dllmain_crt_startup = true;
+                    mod.stage1_flags.have_dllmain_crt_startup = true;
                 }
             }
         }
 
-        if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl_index, exports);
+        if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(mod, decl_index, exports);
     }
 
     const tracy = trace(@src());
@@ -1457,7 +1458,7 @@ pub fn updateDeclExports(
 
     const gpa = self.base.allocator;
 
-    const decl = module.declPtr(decl_index);
+    const decl = mod.declPtr(decl_index);
     const atom_index = try self.getOrCreateAtomForDecl(decl_index);
     const atom = self.getAtom(atom_index);
     const decl_sym = atom.getSymbol(self);
@@ -1468,12 +1469,12 @@ pub fn updateDeclExports(
 
         if (exp.options.section) |section_name| {
             if (!mem.eql(u8, section_name, ".text")) {
-                try module.failed_exports.putNoClobber(
-                    module.gpa,
+                try mod.failed_exports.putNoClobber(
+                    mod.gpa,
                     exp,
                     try Module.ErrorMsg.create(
                         gpa,
-                        decl.srcLoc(),
+                        decl.srcLoc(mod),
                         "Unimplemented: ExportOptions.section",
                         .{},
                     ),
@@ -1483,12 +1484,12 @@ pub fn updateDeclExports(
         }
 
         if (exp.options.linkage == .LinkOnce) {
-            try module.failed_exports.putNoClobber(
-                module.gpa,
+            try mod.failed_exports.putNoClobber(
+                mod.gpa,
                 exp,
                 try Module.ErrorMsg.create(
                     gpa,
-                    decl.srcLoc(),
+                    decl.srcLoc(mod),
                     "Unimplemented: GlobalLinkage.LinkOnce",
                     .{},
                 ),
src/link/Dwarf.zig
@@ -2597,7 +2597,7 @@ pub fn flushModule(self: *Dwarf, module: *Module) !void {
 
 fn addDIFile(self: *Dwarf, mod: *Module, decl_index: Module.Decl.Index) !u28 {
     const decl = mod.declPtr(decl_index);
-    const file_scope = decl.getFileScope();
+    const file_scope = decl.getFileScope(mod);
     const gop = try self.di_files.getOrPut(self.allocator, file_scope);
     if (!gop.found_existing) {
         switch (self.bin_file.tag) {
src/link/Elf.zig
@@ -2414,7 +2414,8 @@ pub fn freeDecl(self: *Elf, decl_index: Module.Decl.Index) void {
 }
 
 pub fn getOrCreateAtomForLazySymbol(self: *Elf, sym: File.LazySymbol) !Atom.Index {
-    const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl());
+    const mod = self.base.options.module.?;
+    const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl(mod));
     errdefer _ = if (!gop.found_existing) self.lazy_syms.pop();
     if (!gop.found_existing) gop.value_ptr.* = .{};
     const metadata: struct { atom: *Atom.Index, state: *LazySymbolMetadata.State } = switch (sym.kind) {
@@ -2429,7 +2430,7 @@ pub fn getOrCreateAtomForLazySymbol(self: *Elf, sym: File.LazySymbol) !Atom.Inde
     metadata.state.* = .pending_flush;
     const atom = metadata.atom.*;
     // anyerror needs to be deferred until flushModule
-    if (sym.getDecl() != .none) try self.updateLazySymbolAtom(sym, atom, switch (sym.kind) {
+    if (sym.getDecl(mod) != .none) try self.updateLazySymbolAtom(sym, atom, switch (sym.kind) {
         .code => self.text_section_index.?,
         .const_data => self.rodata_section_index.?,
     });
@@ -2573,19 +2574,19 @@ fn updateDeclCode(self: *Elf, decl_index: Module.Decl.Index, code: []const u8, s
     return local_sym;
 }
 
-pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
+pub fn updateFunc(self: *Elf, mod: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
     if (build_options.skip_non_native and builtin.object_format != .elf) {
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
     if (build_options.have_llvm) {
-        if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(module, func, air, liveness);
+        if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(mod, func, air, liveness);
     }
 
     const tracy = trace(@src());
     defer tracy.end();
 
     const decl_index = func.owner_decl;
-    const decl = module.declPtr(decl_index);
+    const decl = mod.declPtr(decl_index);
 
     const atom_index = try self.getOrCreateAtomForDecl(decl_index);
     self.freeUnnamedConsts(decl_index);
@@ -2594,28 +2595,28 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
     defer code_buffer.deinit();
 
-    var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(module, decl_index) else null;
+    var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(mod, decl_index) else null;
     defer if (decl_state) |*ds| ds.deinit();
 
     const res = if (decl_state) |*ds|
-        try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{
+        try codegen.generateFunction(&self.base, decl.srcLoc(mod), func, air, liveness, &code_buffer, .{
             .dwarf = ds,
         })
     else
-        try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
+        try codegen.generateFunction(&self.base, decl.srcLoc(mod), func, air, liveness, &code_buffer, .none);
 
     const code = switch (res) {
         .ok => code_buffer.items,
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl_index, em);
+            try mod.failed_decls.put(mod.gpa, decl_index, em);
             return;
         },
     };
     const local_sym = try self.updateDeclCode(decl_index, code, elf.STT_FUNC);
     if (decl_state) |*ds| {
         try self.dwarf.?.commitDeclState(
-            module,
+            mod,
             decl_index,
             local_sym.st_value,
             local_sym.st_size,
@@ -2625,25 +2626,25 @@ pub fn updateFunc(self: *Elf, module: *Module, func: *Module.Fn, air: Air, liven
 
     // Since we updated the vaddr and the size, each corresponding export
     // symbol also needs to be updated.
-    return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
+    return self.updateDeclExports(mod, decl_index, mod.getDeclExports(decl_index));
 }
 
 pub fn updateDecl(
     self: *Elf,
-    module: *Module,
+    mod: *Module,
     decl_index: Module.Decl.Index,
 ) File.UpdateDeclError!void {
     if (build_options.skip_non_native and builtin.object_format != .elf) {
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
     if (build_options.have_llvm) {
-        if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl_index);
+        if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(mod, decl_index);
     }
 
     const tracy = trace(@src());
     defer tracy.end();
 
-    const decl = module.declPtr(decl_index);
+    const decl = mod.declPtr(decl_index);
 
     if (decl.val.tag() == .extern_fn) {
         return; // TODO Should we do more when front-end analyzed extern decl?
@@ -2662,13 +2663,13 @@ pub fn updateDecl(
     var code_buffer = std.ArrayList(u8).init(self.base.allocator);
     defer code_buffer.deinit();
 
-    var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(module, decl_index) else null;
+    var decl_state: ?Dwarf.DeclState = if (self.dwarf) |*dw| try dw.initDeclState(mod, decl_index) else null;
     defer if (decl_state) |*ds| ds.deinit();
 
     // TODO implement .debug_info for global variables
     const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
     const res = if (decl_state) |*ds|
-        try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
+        try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{
             .ty = decl.ty,
             .val = decl_val,
         }, &code_buffer, .{
@@ -2677,7 +2678,7 @@ pub fn updateDecl(
             .parent_atom_index = atom.getSymbolIndex().?,
         })
     else
-        try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
+        try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{
             .ty = decl.ty,
             .val = decl_val,
         }, &code_buffer, .none, .{
@@ -2688,7 +2689,7 @@ pub fn updateDecl(
         .ok => code_buffer.items,
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl_index, em);
+            try mod.failed_decls.put(mod.gpa, decl_index, em);
             return;
         },
     };
@@ -2696,7 +2697,7 @@ pub fn updateDecl(
     const local_sym = try self.updateDeclCode(decl_index, code, elf.STT_OBJECT);
     if (decl_state) |*ds| {
         try self.dwarf.?.commitDeclState(
-            module,
+            mod,
             decl_index,
             local_sym.st_value,
             local_sym.st_size,
@@ -2706,7 +2707,7 @@ pub fn updateDecl(
 
     // Since we updated the vaddr and the size, each corresponding export
     // symbol also needs to be updated.
-    return self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
+    return self.updateDeclExports(mod, decl_index, mod.getDeclExports(decl_index));
 }
 
 fn updateLazySymbolAtom(
@@ -2735,8 +2736,8 @@ fn updateLazySymbolAtom(
     const atom = self.getAtom(atom_index);
     const local_sym_index = atom.getSymbolIndex().?;
 
-    const src = if (sym.ty.getOwnerDeclOrNull()) |owner_decl|
-        mod.declPtr(owner_decl).srcLoc()
+    const src = if (sym.ty.getOwnerDeclOrNull(mod)) |owner_decl|
+        mod.declPtr(owner_decl).srcLoc(mod)
     else
         Module.SrcLoc{
             .file_scope = undefined,
@@ -2812,7 +2813,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
 
     const atom_index = try self.createAtom();
 
-    const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .{
+    const res = try codegen.generateSymbol(&self.base, decl.srcLoc(mod), typed_value, &code_buffer, .{
         .none = {},
     }, .{
         .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,
@@ -2853,7 +2854,7 @@ pub fn lowerUnnamedConst(self: *Elf, typed_value: TypedValue, decl_index: Module
 
 pub fn updateDeclExports(
     self: *Elf,
-    module: *Module,
+    mod: *Module,
     decl_index: Module.Decl.Index,
     exports: []const *Module.Export,
 ) File.UpdateDeclExportsError!void {
@@ -2861,7 +2862,7 @@ pub fn updateDeclExports(
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
     if (build_options.have_llvm) {
-        if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(module, decl_index, exports);
+        if (self.llvm_object) |llvm_object| return llvm_object.updateDeclExports(mod, decl_index, exports);
     }
 
     const tracy = trace(@src());
@@ -2869,7 +2870,7 @@ pub fn updateDeclExports(
 
     const gpa = self.base.allocator;
 
-    const decl = module.declPtr(decl_index);
+    const decl = mod.declPtr(decl_index);
     const atom_index = try self.getOrCreateAtomForDecl(decl_index);
     const atom = self.getAtom(atom_index);
     const decl_sym = atom.getSymbol(self);
@@ -2881,10 +2882,10 @@ pub fn updateDeclExports(
     for (exports) |exp| {
         if (exp.options.section) |section_name| {
             if (!mem.eql(u8, section_name, ".text")) {
-                try module.failed_exports.ensureUnusedCapacity(module.gpa, 1);
-                module.failed_exports.putAssumeCapacityNoClobber(
+                try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1);
+                mod.failed_exports.putAssumeCapacityNoClobber(
                     exp,
-                    try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: ExportOptions.section", .{}),
+                    try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(mod), "Unimplemented: ExportOptions.section", .{}),
                 );
                 continue;
             }
@@ -2900,10 +2901,10 @@ pub fn updateDeclExports(
             },
             .Weak => elf.STB_WEAK,
             .LinkOnce => {
-                try module.failed_exports.ensureUnusedCapacity(module.gpa, 1);
-                module.failed_exports.putAssumeCapacityNoClobber(
+                try mod.failed_exports.ensureUnusedCapacity(mod.gpa, 1);
+                mod.failed_exports.putAssumeCapacityNoClobber(
                     exp,
-                    try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(), "Unimplemented: GlobalLinkage.LinkOnce", .{}),
+                    try Module.ErrorMsg.create(self.base.allocator, decl.srcLoc(mod), "Unimplemented: GlobalLinkage.LinkOnce", .{}),
                 );
                 continue;
             },
src/link/MachO.zig
@@ -1847,18 +1847,18 @@ fn addStubEntry(self: *MachO, target: SymbolWithLoc) !void {
     self.markRelocsDirtyByTarget(target);
 }
 
-pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
+pub fn updateFunc(self: *MachO, mod: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
     if (build_options.skip_non_native and builtin.object_format != .macho) {
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
     if (build_options.have_llvm) {
-        if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(module, func, air, liveness);
+        if (self.llvm_object) |llvm_object| return llvm_object.updateFunc(mod, func, air, liveness);
     }
     const tracy = trace(@src());
     defer tracy.end();
 
     const decl_index = func.owner_decl;
-    const decl = module.declPtr(decl_index);
+    const decl = mod.declPtr(decl_index);
 
     const atom_index = try self.getOrCreateAtomForDecl(decl_index);
     self.freeUnnamedConsts(decl_index);
@@ -1868,23 +1868,23 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
     defer code_buffer.deinit();
 
     var decl_state = if (self.d_sym) |*d_sym|
-        try d_sym.dwarf.initDeclState(module, decl_index)
+        try d_sym.dwarf.initDeclState(mod, decl_index)
     else
         null;
     defer if (decl_state) |*ds| ds.deinit();
 
     const res = if (decl_state) |*ds|
-        try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .{
+        try codegen.generateFunction(&self.base, decl.srcLoc(mod), func, air, liveness, &code_buffer, .{
             .dwarf = ds,
         })
     else
-        try codegen.generateFunction(&self.base, decl.srcLoc(), func, air, liveness, &code_buffer, .none);
+        try codegen.generateFunction(&self.base, decl.srcLoc(mod), func, air, liveness, &code_buffer, .none);
 
     var code = switch (res) {
         .ok => code_buffer.items,
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl_index, em);
+            try mod.failed_decls.put(mod.gpa, decl_index, em);
             return;
         },
     };
@@ -1893,7 +1893,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
 
     if (decl_state) |*ds| {
         try self.d_sym.?.dwarf.commitDeclState(
-            module,
+            mod,
             decl_index,
             addr,
             self.getAtom(atom_index).size,
@@ -1903,7 +1903,7 @@ pub fn updateFunc(self: *MachO, module: *Module, func: *Module.Fn, air: Air, liv
 
     // Since we updated the vaddr and the size, each corresponding export symbol also
     // needs to be updated.
-    try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
+    try self.updateDeclExports(mod, decl_index, mod.getDeclExports(decl_index));
 }
 
 pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Module.Decl.Index) !u32 {
@@ -1912,15 +1912,15 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
     var code_buffer = std.ArrayList(u8).init(gpa);
     defer code_buffer.deinit();
 
-    const module = self.base.options.module.?;
+    const mod = self.base.options.module.?;
     const gop = try self.unnamed_const_atoms.getOrPut(gpa, decl_index);
     if (!gop.found_existing) {
         gop.value_ptr.* = .{};
     }
     const unnamed_consts = gop.value_ptr;
 
-    const decl = module.declPtr(decl_index);
-    const decl_name = try decl.getFullyQualifiedName(module);
+    const decl = mod.declPtr(decl_index);
+    const decl_name = try decl.getFullyQualifiedName(mod);
     defer gpa.free(decl_name);
 
     const name_str_index = blk: {
@@ -1935,20 +1935,19 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
 
     const atom_index = try self.createAtom();
 
-    const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), typed_value, &code_buffer, .none, .{
+    const res = try codegen.generateSymbol(&self.base, decl.srcLoc(mod), typed_value, &code_buffer, .none, .{
         .parent_atom_index = self.getAtom(atom_index).getSymbolIndex().?,
     });
     var code = switch (res) {
         .ok => code_buffer.items,
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl_index, em);
+            try mod.failed_decls.put(mod.gpa, decl_index, em);
             log.err("{s}", .{em.msg});
             return error.CodegenFail;
         },
     };
 
-    const mod = self.base.options.module.?;
     const required_alignment = typed_value.ty.abiAlignment(mod);
     const atom = self.getAtomPtr(atom_index);
     atom.size = code.len;
@@ -1972,17 +1971,17 @@ pub fn lowerUnnamedConst(self: *MachO, typed_value: TypedValue, decl_index: Modu
     return atom.getSymbolIndex().?;
 }
 
-pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index) !void {
+pub fn updateDecl(self: *MachO, mod: *Module, decl_index: Module.Decl.Index) !void {
     if (build_options.skip_non_native and builtin.object_format != .macho) {
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
     if (build_options.have_llvm) {
-        if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(module, decl_index);
+        if (self.llvm_object) |llvm_object| return llvm_object.updateDecl(mod, decl_index);
     }
     const tracy = trace(@src());
     defer tracy.end();
 
-    const decl = module.declPtr(decl_index);
+    const decl = mod.declPtr(decl_index);
 
     if (decl.val.tag() == .extern_fn) {
         return; // TODO Should we do more when front-end analyzed extern decl?
@@ -1998,7 +1997,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
         payload.data.is_threadlocal and !self.base.options.single_threaded
     else
         false;
-    if (is_threadlocal) return self.updateThreadlocalVariable(module, decl_index);
+    if (is_threadlocal) return self.updateThreadlocalVariable(mod, decl_index);
 
     const atom_index = try self.getOrCreateAtomForDecl(decl_index);
     const sym_index = self.getAtom(atom_index).getSymbolIndex().?;
@@ -2008,14 +2007,14 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
     defer code_buffer.deinit();
 
     var decl_state: ?Dwarf.DeclState = if (self.d_sym) |*d_sym|
-        try d_sym.dwarf.initDeclState(module, decl_index)
+        try d_sym.dwarf.initDeclState(mod, decl_index)
     else
         null;
     defer if (decl_state) |*ds| ds.deinit();
 
     const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
     const res = if (decl_state) |*ds|
-        try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
+        try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{
             .ty = decl.ty,
             .val = decl_val,
         }, &code_buffer, .{
@@ -2024,7 +2023,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
             .parent_atom_index = sym_index,
         })
     else
-        try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
+        try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{
             .ty = decl.ty,
             .val = decl_val,
         }, &code_buffer, .none, .{
@@ -2035,7 +2034,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
         .ok => code_buffer.items,
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl_index, em);
+            try mod.failed_decls.put(mod.gpa, decl_index, em);
             return;
         },
     };
@@ -2043,7 +2042,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
 
     if (decl_state) |*ds| {
         try self.d_sym.?.dwarf.commitDeclState(
-            module,
+            mod,
             decl_index,
             addr,
             self.getAtom(atom_index).size,
@@ -2053,7 +2052,7 @@ pub fn updateDecl(self: *MachO, module: *Module, decl_index: Module.Decl.Index)
 
     // Since we updated the vaddr and the size, each corresponding export symbol also
     // needs to be updated.
-    try self.updateDeclExports(module, decl_index, module.getDeclExports(decl_index));
+    try self.updateDeclExports(mod, decl_index, mod.getDeclExports(decl_index));
 }
 
 fn updateLazySymbolAtom(
@@ -2082,8 +2081,8 @@ fn updateLazySymbolAtom(
     const atom = self.getAtomPtr(atom_index);
     const local_sym_index = atom.getSymbolIndex().?;
 
-    const src = if (sym.ty.getOwnerDeclOrNull()) |owner_decl|
-        mod.declPtr(owner_decl).srcLoc()
+    const src = if (sym.ty.getOwnerDeclOrNull(mod)) |owner_decl|
+        mod.declPtr(owner_decl).srcLoc(mod)
     else
         Module.SrcLoc{
             .file_scope = undefined,
@@ -2127,7 +2126,8 @@ fn updateLazySymbolAtom(
 }
 
 pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol) !Atom.Index {
-    const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl());
+    const mod = self.base.options.module.?;
+    const gop = try self.lazy_syms.getOrPut(self.base.allocator, sym.getDecl(mod));
     errdefer _ = if (!gop.found_existing) self.lazy_syms.pop();
     if (!gop.found_existing) gop.value_ptr.* = .{};
     const metadata: struct { atom: *Atom.Index, state: *LazySymbolMetadata.State } = switch (sym.kind) {
@@ -2145,7 +2145,7 @@ pub fn getOrCreateAtomForLazySymbol(self: *MachO, sym: File.LazySymbol) !Atom.In
     metadata.state.* = .pending_flush;
     const atom = metadata.atom.*;
     // anyerror needs to be deferred until flushModule
-    if (sym.getDecl() != .none) try self.updateLazySymbolAtom(sym, atom, switch (sym.kind) {
+    if (sym.getDecl(mod) != .none) try self.updateLazySymbolAtom(sym, atom, switch (sym.kind) {
         .code => self.text_section_index.?,
         .const_data => self.data_const_section_index.?,
     });
@@ -2179,7 +2179,7 @@ fn updateThreadlocalVariable(self: *MachO, module: *Module, decl_index: Module.D
     const decl_metadata = self.decls.get(decl_index).?;
     const decl_val = decl.val.castTag(.variable).?.data.init;
     const res = if (decl_state) |*ds|
-        try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
+        try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{
             .ty = decl.ty,
             .val = decl_val,
         }, &code_buffer, .{
@@ -2188,7 +2188,7 @@ fn updateThreadlocalVariable(self: *MachO, module: *Module, decl_index: Module.D
             .parent_atom_index = init_sym_index,
         })
     else
-        try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
+        try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{
             .ty = decl.ty,
             .val = decl_val,
         }, &code_buffer, .none, .{
@@ -2379,7 +2379,7 @@ pub fn updateDeclLineNumber(self: *MachO, module: *Module, decl_index: Module.De
 
 pub fn updateDeclExports(
     self: *MachO,
-    module: *Module,
+    mod: *Module,
     decl_index: Module.Decl.Index,
     exports: []const *Module.Export,
 ) File.UpdateDeclExportsError!void {
@@ -2388,7 +2388,7 @@ pub fn updateDeclExports(
     }
     if (build_options.have_llvm) {
         if (self.llvm_object) |llvm_object|
-            return llvm_object.updateDeclExports(module, decl_index, exports);
+            return llvm_object.updateDeclExports(mod, decl_index, exports);
     }
 
     const tracy = trace(@src());
@@ -2396,7 +2396,7 @@ pub fn updateDeclExports(
 
     const gpa = self.base.allocator;
 
-    const decl = module.declPtr(decl_index);
+    const decl = mod.declPtr(decl_index);
     const atom_index = try self.getOrCreateAtomForDecl(decl_index);
     const atom = self.getAtom(atom_index);
     const decl_sym = atom.getSymbol(self);
@@ -2410,12 +2410,12 @@ pub fn updateDeclExports(
 
         if (exp.options.section) |section_name| {
             if (!mem.eql(u8, section_name, "__text")) {
-                try module.failed_exports.putNoClobber(
-                    module.gpa,
+                try mod.failed_exports.putNoClobber(
+                    mod.gpa,
                     exp,
                     try Module.ErrorMsg.create(
                         gpa,
-                        decl.srcLoc(),
+                        decl.srcLoc(mod),
                         "Unimplemented: ExportOptions.section",
                         .{},
                     ),
@@ -2425,12 +2425,12 @@ pub fn updateDeclExports(
         }
 
         if (exp.options.linkage == .LinkOnce) {
-            try module.failed_exports.putNoClobber(
-                module.gpa,
+            try mod.failed_exports.putNoClobber(
+                mod.gpa,
                 exp,
                 try Module.ErrorMsg.create(
                     gpa,
-                    decl.srcLoc(),
+                    decl.srcLoc(mod),
                     "Unimplemented: GlobalLinkage.LinkOnce",
                     .{},
                 ),
@@ -2474,9 +2474,9 @@ pub fn updateDeclExports(
                 // TODO: this needs rethinking
                 const global = self.getGlobal(exp_name).?;
                 if (sym_loc.sym_index != global.sym_index and global.file != null) {
-                    _ = try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(
+                    _ = try mod.failed_exports.put(mod.gpa, exp, try Module.ErrorMsg.create(
                         gpa,
-                        decl.srcLoc(),
+                        decl.srcLoc(mod),
                         \\LinkError: symbol '{s}' defined multiple times
                     ,
                         .{exp_name},
src/link/Plan9.zig
@@ -213,14 +213,14 @@ fn putFn(self: *Plan9, decl_index: Module.Decl.Index, out: FnDeclOutput) !void {
     const gpa = self.base.allocator;
     const mod = self.base.options.module.?;
     const decl = mod.declPtr(decl_index);
-    const fn_map_res = try self.fn_decl_table.getOrPut(gpa, decl.getFileScope());
+    const fn_map_res = try self.fn_decl_table.getOrPut(gpa, decl.getFileScope(mod));
     if (fn_map_res.found_existing) {
         if (try fn_map_res.value_ptr.functions.fetchPut(gpa, decl_index, out)) |old_entry| {
             gpa.free(old_entry.value.code);
             gpa.free(old_entry.value.lineinfo);
         }
     } else {
-        const file = decl.getFileScope();
+        const file = decl.getFileScope(mod);
         const arena = self.path_arena.allocator();
         // each file gets a symbol
         fn_map_res.value_ptr.* = .{
@@ -276,13 +276,13 @@ fn addPathComponents(self: *Plan9, path: []const u8, a: *std.ArrayList(u8)) !voi
     }
 }
 
-pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
+pub fn updateFunc(self: *Plan9, mod: *Module, func: *Module.Fn, air: Air, liveness: Liveness) !void {
     if (build_options.skip_non_native and builtin.object_format != .plan9) {
         @panic("Attempted to compile for object format that was disabled by build configuration");
     }
 
     const decl_index = func.owner_decl;
-    const decl = module.declPtr(decl_index);
+    const decl = mod.declPtr(decl_index);
     self.freeUnnamedConsts(decl_index);
 
     _ = try self.seeDecl(decl_index);
@@ -298,7 +298,7 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
 
     const res = try codegen.generateFunction(
         &self.base,
-        decl.srcLoc(),
+        decl.srcLoc(mod),
         func,
         air,
         liveness,
@@ -316,7 +316,7 @@ pub fn updateFunc(self: *Plan9, module: *Module, func: *Module.Fn, air: Air, liv
         .ok => try code_buffer.toOwnedSlice(),
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl_index, em);
+            try mod.failed_decls.put(mod.gpa, decl_index, em);
             return;
         },
     };
@@ -366,7 +366,7 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I
     };
     self.syms.items[info.sym_index.?] = sym;
 
-    const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), tv, &code_buffer, .{
+    const res = try codegen.generateSymbol(&self.base, decl.srcLoc(mod), tv, &code_buffer, .{
         .none = {},
     }, .{
         .parent_atom_index = @enumToInt(decl_index),
@@ -388,8 +388,8 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I
     return @intCast(u32, info.got_index.?);
 }
 
-pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index) !void {
-    const decl = module.declPtr(decl_index);
+pub fn updateDecl(self: *Plan9, mod: *Module, decl_index: Module.Decl.Index) !void {
+    const decl = mod.declPtr(decl_index);
 
     if (decl.val.tag() == .extern_fn) {
         return; // TODO Should we do more when front-end analyzed extern decl?
@@ -409,7 +409,7 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index)
     defer code_buffer.deinit();
     const decl_val = if (decl.val.castTag(.variable)) |payload| payload.data.init else decl.val;
     // TODO we need the symbol index for symbol in the table of locals for the containing atom
-    const res = try codegen.generateSymbol(&self.base, decl.srcLoc(), .{
+    const res = try codegen.generateSymbol(&self.base, decl.srcLoc(mod), .{
         .ty = decl.ty,
         .val = decl_val,
     }, &code_buffer, .{ .none = {} }, .{
@@ -419,7 +419,7 @@ pub fn updateDecl(self: *Plan9, module: *Module, decl_index: Module.Decl.Index)
         .ok => code_buffer.items,
         .fail => |em| {
             decl.analysis = .codegen_failure;
-            try module.failed_decls.put(module.gpa, decl_index, em);
+            try mod.failed_decls.put(mod.gpa, decl_index, em);
             return;
         },
     };
@@ -707,7 +707,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
                 const code = blk: {
                     const is_fn = source_decl.ty.zigTypeTag(mod) == .Fn;
                     if (is_fn) {
-                        const table = self.fn_decl_table.get(source_decl.getFileScope()).?.functions;
+                        const table = self.fn_decl_table.get(source_decl.getFileScope(mod)).?.functions;
                         const output = table.get(source_decl_index).?;
                         break :blk output.code;
                     } else {
@@ -729,7 +729,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
 }
 fn addDeclExports(
     self: *Plan9,
-    module: *Module,
+    mod: *Module,
     decl_index: Module.Decl.Index,
     exports: []const *Module.Export,
 ) !void {
@@ -740,9 +740,9 @@ fn addDeclExports(
         // plan9 does not support custom sections
         if (exp.options.section) |section_name| {
             if (!mem.eql(u8, section_name, ".text") or !mem.eql(u8, section_name, ".data")) {
-                try module.failed_exports.put(module.gpa, exp, try Module.ErrorMsg.create(
+                try mod.failed_exports.put(mod.gpa, exp, try Module.ErrorMsg.create(
                     self.base.allocator,
-                    module.declPtr(decl_index).srcLoc(),
+                    mod.declPtr(decl_index).srcLoc(mod),
                     "plan9 does not support extra sections",
                     .{},
                 ));
@@ -773,7 +773,7 @@ pub fn freeDecl(self: *Plan9, decl_index: Module.Decl.Index) void {
     const decl = mod.declPtr(decl_index);
     const is_fn = (decl.val.tag() == .function);
     if (is_fn) {
-        var symidx_and_submap = self.fn_decl_table.get(decl.getFileScope()).?;
+        var symidx_and_submap = self.fn_decl_table.get(decl.getFileScope(mod)).?;
         var submap = symidx_and_submap.functions;
         if (submap.fetchSwapRemove(decl_index)) |removed_entry| {
             self.base.allocator.free(removed_entry.value.code);
src/link/Wasm.zig
@@ -1348,7 +1348,7 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes
     defer code_writer.deinit();
     // const result = try codegen.generateFunction(
     //     &wasm.base,
-    //     decl.srcLoc(),
+    //     decl.srcLoc(mod),
     //     func,
     //     air,
     //     liveness,
@@ -1357,7 +1357,7 @@ pub fn updateFunc(wasm: *Wasm, mod: *Module, func: *Module.Fn, air: Air, livenes
     // );
     const result = try codegen.generateFunction(
         &wasm.base,
-        decl.srcLoc(),
+        decl.srcLoc(mod),
         func,
         air,
         liveness,
@@ -1425,7 +1425,7 @@ pub fn updateDecl(wasm: *Wasm, mod: *Module, decl_index: Module.Decl.Index) !voi
 
     const res = try codegen.generateSymbol(
         &wasm.base,
-        decl.srcLoc(),
+        decl.srcLoc(mod),
         .{ .ty = decl.ty, .val = val },
         &code_writer,
         .none,
@@ -1554,7 +1554,7 @@ pub fn lowerUnnamedConst(wasm: *Wasm, tv: TypedValue, decl_index: Module.Decl.In
 
         const result = try codegen.generateSymbol(
             &wasm.base,
-            decl.srcLoc(),
+            decl.srcLoc(mod),
             tv,
             &value_bytes,
             .none,
@@ -1693,7 +1693,7 @@ pub fn updateDeclExports(
         if (exp.options.section) |section| {
             try mod.failed_exports.putNoClobber(mod.gpa, exp, try Module.ErrorMsg.create(
                 mod.gpa,
-                decl.srcLoc(),
+                decl.srcLoc(mod),
                 "Unimplemented: ExportOptions.section '{s}'",
                 .{section},
             ));
@@ -1712,7 +1712,7 @@ pub fn updateDeclExports(
             if (!exp_is_weak and !existing_sym.isWeak()) {
                 try mod.failed_exports.put(mod.gpa, exp, try Module.ErrorMsg.create(
                     mod.gpa,
-                    decl.srcLoc(),
+                    decl.srcLoc(mod),
                     \\LinkError: symbol '{s}' defined multiple times
                     \\  first definition in '{s}'
                     \\  next definition in '{s}'
@@ -1745,7 +1745,7 @@ pub fn updateDeclExports(
             .LinkOnce => {
                 try mod.failed_exports.putNoClobber(mod.gpa, exp, try Module.ErrorMsg.create(
                     mod.gpa,
-                    decl.srcLoc(),
+                    decl.srcLoc(mod),
                     "Unimplemented: LinkOnce",
                     .{},
                 ));
src/Compilation.zig
@@ -2048,7 +2048,7 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void
             assert(decl.deletion_flag);
             assert(decl.dependants.count() == 0);
             const is_anon = if (decl.zir_decl_index == 0) blk: {
-                break :blk decl.src_namespace.anon_decls.swapRemove(decl_index);
+                break :blk module.namespacePtr(decl.src_namespace).anon_decls.swapRemove(decl_index);
             } else false;
 
             try module.clearDecl(decl_index, null);
@@ -2530,8 +2530,7 @@ pub fn totalErrorCount(self: *Compilation) u32 {
         // the previous parse success, including compile errors, but we cannot
         // emit them until the file succeeds parsing.
         for (module.failed_decls.keys()) |key| {
-            const decl = module.declPtr(key);
-            if (decl.getFileScope().okToReportErrors()) {
+            if (module.declFileScope(key).okToReportErrors()) {
                 total += 1;
                 if (module.cimport_errors.get(key)) |errors| {
                     total += errors.len;
@@ -2540,8 +2539,7 @@ pub fn totalErrorCount(self: *Compilation) u32 {
         }
         if (module.emit_h) |emit_h| {
             for (emit_h.failed_decls.keys()) |key| {
-                const decl = module.declPtr(key);
-                if (decl.getFileScope().okToReportErrors()) {
+                if (module.declFileScope(key).okToReportErrors()) {
                     total += 1;
                 }
             }
@@ -2644,10 +2642,10 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle {
         {
             var it = module.failed_decls.iterator();
             while (it.next()) |entry| {
-                const decl = module.declPtr(entry.key_ptr.*);
+                const decl_index = entry.key_ptr.*;
                 // Skip errors for Decls within files that had a parse failure.
                 // We'll try again once parsing succeeds.
-                if (decl.getFileScope().okToReportErrors()) {
+                if (module.declFileScope(decl_index).okToReportErrors()) {
                     try addModuleErrorMsg(&bundle, entry.value_ptr.*.*);
                     if (module.cimport_errors.get(entry.key_ptr.*)) |cimport_errors| for (cimport_errors) |c_error| {
                         try bundle.addRootErrorMessage(.{
@@ -2669,10 +2667,10 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle {
         if (module.emit_h) |emit_h| {
             var it = emit_h.failed_decls.iterator();
             while (it.next()) |entry| {
-                const decl = module.declPtr(entry.key_ptr.*);
+                const decl_index = entry.key_ptr.*;
                 // Skip errors for Decls within files that had a parse failure.
                 // We'll try again once parsing succeeds.
-                if (decl.getFileScope().okToReportErrors()) {
+                if (module.declFileScope(decl_index).okToReportErrors()) {
                     try addModuleErrorMsg(&bundle, entry.value_ptr.*.*);
                 }
             }
@@ -2710,7 +2708,7 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle {
             const values = module.compile_log_decls.values();
             // First one will be the error; subsequent ones will be notes.
             const err_decl = module.declPtr(keys[0]);
-            const src_loc = err_decl.nodeOffsetSrcLoc(values[0]);
+            const src_loc = err_decl.nodeOffsetSrcLoc(values[0], module);
             const err_msg = Module.ErrorMsg{
                 .src_loc = src_loc,
                 .msg = "found compile log statement",
@@ -2721,7 +2719,7 @@ pub fn getAllErrorsAlloc(self: *Compilation) !ErrorBundle {
             for (keys[1..], 0..) |key, i| {
                 const note_decl = module.declPtr(key);
                 err_msg.notes[i] = .{
-                    .src_loc = note_decl.nodeOffsetSrcLoc(values[i + 1]),
+                    .src_loc = note_decl.nodeOffsetSrcLoc(values[i + 1], module),
                     .msg = "also here",
                 };
             }
@@ -3235,7 +3233,7 @@ fn processOneJob(comp: *Compilation, job: Job, prog_node: *std.Progress.Node) !v
                 try module.failed_decls.ensureUnusedCapacity(gpa, 1);
                 module.failed_decls.putAssumeCapacityNoClobber(decl_index, try Module.ErrorMsg.create(
                     gpa,
-                    decl.srcLoc(),
+                    decl.srcLoc(module),
                     "unable to update line number: {s}",
                     .{@errorName(err)},
                 ));
@@ -3848,7 +3846,7 @@ fn reportRetryableEmbedFileError(
     const mod = comp.bin_file.options.module.?;
     const gpa = mod.gpa;
 
-    const src_loc: Module.SrcLoc = mod.declPtr(embed_file.owner_decl).srcLoc();
+    const src_loc: Module.SrcLoc = mod.declPtr(embed_file.owner_decl).srcLoc(mod);
 
     const err_msg = if (embed_file.pkg.root_src_directory.path) |dir_path|
         try Module.ErrorMsg.create(
src/crash_report.zig
@@ -99,7 +99,7 @@ fn dumpStatusReport() !void {
         allocator,
         anal.body,
         anal.body_index,
-        block.namespace.file_scope,
+        mod.namespacePtr(block.namespace).file_scope,
         block_src_decl.src_node,
         6, // indent
         stderr,
@@ -108,7 +108,7 @@ fn dumpStatusReport() !void {
         else => |e| return e,
     };
     try stderr.writeAll("    For full context, use the command\n      zig ast-check -t ");
-    try writeFilePath(block.namespace.file_scope, stderr);
+    try writeFilePath(mod.namespacePtr(block.namespace).file_scope, stderr);
     try stderr.writeAll("\n\n");
 
     var parent = anal.parent;
@@ -121,7 +121,7 @@ fn dumpStatusReport() !void {
         print_zir.renderSingleInstruction(
             allocator,
             curr.body[curr.body_index],
-            curr.block.namespace.file_scope,
+            mod.namespacePtr(curr.block.namespace).file_scope,
             curr_block_src_decl.src_node,
             6, // indent
             stderr,
@@ -148,7 +148,7 @@ fn writeFilePath(file: *Module.File, stream: anytype) !void {
 }
 
 fn writeFullyQualifiedDeclWithFile(mod: *Module, decl: *Decl, stream: anytype) !void {
-    try writeFilePath(decl.getFileScope(), stream);
+    try writeFilePath(decl.getFileScope(mod), stream);
     try stream.writeAll(": ");
     try decl.renderFullyQualifiedDebugName(mod, stream);
 }
src/InternPool.zig
@@ -17,7 +17,8 @@ const BigIntMutable = std.math.big.int.Mutable;
 const Limb = std.math.big.Limb;
 
 const InternPool = @This();
-const DeclIndex = enum(u32) { _ };
+const DeclIndex = @import("Module.zig").Decl.Index;
+const NamespaceIndex = @import("Module.zig").Namespace.Index;
 
 const KeyAdapter = struct {
     intern_pool: *const InternPool,
@@ -48,7 +49,7 @@ pub const Key = union(enum) {
     extern_func: struct {
         ty: Index,
         /// The Decl that corresponds to the function itself.
-        owner_decl: DeclIndex,
+        decl: DeclIndex,
         /// Library name if specified.
         /// For example `extern "c" fn write(...) usize` would have 'c' as library name.
         /// Index into the string table bytes.
@@ -62,6 +63,7 @@ pub const Key = union(enum) {
         tag: BigIntConst,
     },
     struct_type: StructType,
+    opaque_type: OpaqueType,
 
     union_type: struct {
         fields_len: u32,
@@ -116,6 +118,13 @@ pub const Key = union(enum) {
         // TODO move Module.Struct data to InternPool
     };
 
+    pub const OpaqueType = struct {
+        /// The Decl that corresponds to the opaque itself.
+        decl: DeclIndex,
+        /// Represents the declarations inside this opaque.
+        namespace: NamespaceIndex,
+    };
+
     pub const Int = struct {
         ty: Index,
         storage: Storage,
@@ -221,6 +230,7 @@ pub const Key = union(enum) {
                 _ = union_type;
                 @panic("TODO");
             },
+            .opaque_type => |opaque_type| std.hash.autoHash(hasher, opaque_type.decl),
         }
     }
 
@@ -338,6 +348,11 @@ pub const Key = union(enum) {
                 _ = b_info;
                 @panic("TODO");
             },
+
+            .opaque_type => |a_info| {
+                const b_info = b.opaque_type;
+                return a_info.decl == b_info.decl;
+            },
         }
     }
 
@@ -352,6 +367,7 @@ pub const Key = union(enum) {
             .simple_type,
             .struct_type,
             .union_type,
+            .opaque_type,
             => return .type_type,
 
             inline .ptr,
@@ -770,10 +786,13 @@ pub const Tag = enum(u8) {
     /// are auto-numbered, and there are no declarations.
     /// data is payload index to `EnumSimple`.
     type_enum_simple,
-
     /// A type that can be represented with only an enum tag.
     /// data is SimpleType enum value.
     simple_type,
+    /// An opaque type.
+    /// data is index of Key.OpaqueType in extra.
+    type_opaque,
+
     /// A value that can be represented with only an enum tag.
     /// data is SimpleValue enum value.
     simple_value,
@@ -986,7 +1005,7 @@ pub const ErrorUnion = struct {
 /// 0. field name: null-terminated string index for each fields_len; declaration order
 pub const EnumSimple = struct {
     /// The Decl that corresponds to the enum itself.
-    owner_decl: DeclIndex,
+    decl: DeclIndex,
     /// An integer type which is used for the numerical value of the enum. This
     /// is inferred by Zig to be the smallest power of two unsigned int that
     /// fits the number of fields. It is stored here to avoid unnecessary
@@ -1146,6 +1165,9 @@ pub fn indexToKey(ip: InternPool, index: Index) Key {
 
         .type_error_union => @panic("TODO"),
         .type_enum_simple => @panic("TODO"),
+
+        .type_opaque => .{ .opaque_type = ip.extraData(Key.OpaqueType, data) },
+
         .simple_internal => switch (@intToEnum(SimpleInternal, data)) {
             .type_empty_struct => .{ .struct_type = .{
                 .fields_len = 0,
@@ -1335,6 +1357,29 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
                 .data = @enumToInt(simple_value),
             });
         },
+
+        .struct_type => |struct_type| {
+            if (struct_type.fields_len != 0) {
+                @panic("TODO"); // handle structs other than empty_struct
+            }
+            ip.items.appendAssumeCapacity(.{
+                .tag = .simple_internal,
+                .data = @enumToInt(SimpleInternal.type_empty_struct),
+            });
+        },
+
+        .union_type => |union_type| {
+            _ = union_type;
+            @panic("TODO");
+        },
+
+        .opaque_type => |opaque_type| {
+            ip.items.appendAssumeCapacity(.{
+                .tag = .type_opaque,
+                .data = try ip.addExtra(gpa, opaque_type),
+            });
+        },
+
         .extern_func => @panic("TODO"),
 
         .ptr => |ptr| switch (ptr.addr) {
@@ -1504,21 +1549,6 @@ pub fn get(ip: *InternPool, gpa: Allocator, key: Key) Allocator.Error!Index {
             const tag: Tag = if (enum_tag.tag.positive) .enum_tag_positive else .enum_tag_negative;
             try addInt(ip, gpa, enum_tag.ty, tag, enum_tag.tag.limbs);
         },
-
-        .struct_type => |struct_type| {
-            if (struct_type.fields_len != 0) {
-                @panic("TODO"); // handle structs other than empty_struct
-            }
-            ip.items.appendAssumeCapacity(.{
-                .tag = .simple_internal,
-                .data = @enumToInt(SimpleInternal.type_empty_struct),
-            });
-        },
-
-        .union_type => |union_type| {
-            _ = union_type;
-            @panic("TODO");
-        },
     }
     return @intToEnum(Index, ip.items.len - 1);
 }
@@ -1548,6 +1578,8 @@ fn addExtraAssumeCapacity(ip: *InternPool, extra: anytype) u32 {
         ip.extra.appendAssumeCapacity(switch (field.type) {
             u32 => @field(extra, field.name),
             Index => @enumToInt(@field(extra, field.name)),
+            DeclIndex => @enumToInt(@field(extra, field.name)),
+            NamespaceIndex => @enumToInt(@field(extra, field.name)),
             i32 => @bitCast(u32, @field(extra, field.name)),
             Pointer.Flags => @bitCast(u32, @field(extra, field.name)),
             Pointer.PackedOffset => @bitCast(u32, @field(extra, field.name)),
@@ -1603,6 +1635,8 @@ fn extraData(ip: InternPool, comptime T: type, index: usize) T {
         @field(result, field.name) = switch (field.type) {
             u32 => int32,
             Index => @intToEnum(Index, int32),
+            DeclIndex => @intToEnum(DeclIndex, int32),
+            NamespaceIndex => @intToEnum(NamespaceIndex, int32),
             i32 => @bitCast(i32, int32),
             Pointer.Flags => @bitCast(Pointer.Flags, int32),
             Pointer.PackedOffset => @bitCast(Pointer.PackedOffset, int32),
@@ -1824,6 +1858,7 @@ fn dumpFallible(ip: InternPool, arena: Allocator) anyerror!void {
             .type_optional => 0,
             .type_error_union => @sizeOf(ErrorUnion),
             .type_enum_simple => @sizeOf(EnumSimple),
+            .type_opaque => @sizeOf(Key.OpaqueType),
             .simple_type => 0,
             .simple_value => 0,
             .simple_internal => 0,
src/link.zig
@@ -1129,8 +1129,8 @@ pub const File = struct {
                 Type.anyerror };
         }
 
-        pub fn getDecl(self: LazySymbol) Module.Decl.OptionalIndex {
-            return Module.Decl.OptionalIndex.init(self.ty.getOwnerDeclOrNull());
+        pub fn getDecl(self: LazySymbol, mod: *Module) Module.Decl.OptionalIndex {
+            return Module.Decl.OptionalIndex.init(self.ty.getOwnerDeclOrNull(mod));
         }
     };
 
src/Module.zig
@@ -185,6 +185,11 @@ allocated_decls: std.SegmentedList(Decl, 0) = .{},
 /// When a Decl object is freed from `allocated_decls`, it is pushed into this stack.
 decls_free_list: ArrayListUnmanaged(Decl.Index) = .{},
 
+/// Same pattern as with `allocated_decls`.
+allocated_namespaces: std.SegmentedList(Namespace, 0) = .{},
+/// Same pattern as with `decls_free_list`.
+namespaces_free_list: ArrayListUnmanaged(Namespace.Index) = .{},
+
 global_assembly: std.AutoHashMapUnmanaged(Decl.Index, []u8) = .{},
 
 reference_table: std.AutoHashMapUnmanaged(Decl.Index, struct {
@@ -363,7 +368,7 @@ pub const Export = struct {
     pub fn getSrcLoc(exp: Export, mod: *Module) SrcLoc {
         const src_decl = mod.declPtr(exp.src_decl);
         return .{
-            .file_scope = src_decl.getFileScope(),
+            .file_scope = src_decl.getFileScope(mod),
             .parent_decl_node = src_decl.src_node,
             .lazy = exp.src,
         };
@@ -494,7 +499,7 @@ pub const Decl = struct {
     /// Reference to externally owned memory.
     /// In the case of the Decl corresponding to a file, this is
     /// the namespace of the struct, since there is no parent.
-    src_namespace: *Namespace,
+    src_namespace: Namespace.Index,
 
     /// The scope which lexically contains this decl.  A decl must depend
     /// on its lexical parent, in order to ensure that this pointer is valid.
@@ -691,8 +696,8 @@ pub const Decl = struct {
 
     /// This name is relative to the containing namespace of the decl.
     /// The memory is owned by the containing File ZIR.
-    pub fn getName(decl: Decl) ?[:0]const u8 {
-        const zir = decl.getFileScope().zir;
+    pub fn getName(decl: Decl, mod: *Module) ?[:0]const u8 {
+        const zir = decl.getFileScope(mod).zir;
         return decl.getNameZir(zir);
     }
 
@@ -703,8 +708,8 @@ pub const Decl = struct {
         return zir.nullTerminatedString(name_index);
     }
 
-    pub fn contentsHash(decl: Decl) std.zig.SrcHash {
-        const zir = decl.getFileScope().zir;
+    pub fn contentsHash(decl: Decl, mod: *Module) std.zig.SrcHash {
+        const zir = decl.getFileScope(mod).zir;
         return decl.contentsHashZir(zir);
     }
 
@@ -715,31 +720,31 @@ pub const Decl = struct {
         return contents_hash;
     }
 
-    pub fn zirBlockIndex(decl: *const Decl) Zir.Inst.Index {
+    pub fn zirBlockIndex(decl: *const Decl, mod: *Module) Zir.Inst.Index {
         assert(decl.zir_decl_index != 0);
-        const zir = decl.getFileScope().zir;
+        const zir = decl.getFileScope(mod).zir;
         return zir.extra[decl.zir_decl_index + 6];
     }
 
-    pub fn zirAlignRef(decl: Decl) Zir.Inst.Ref {
+    pub fn zirAlignRef(decl: Decl, mod: *Module) Zir.Inst.Ref {
         if (!decl.has_align) return .none;
         assert(decl.zir_decl_index != 0);
-        const zir = decl.getFileScope().zir;
+        const zir = decl.getFileScope(mod).zir;
         return @intToEnum(Zir.Inst.Ref, zir.extra[decl.zir_decl_index + 8]);
     }
 
-    pub fn zirLinksectionRef(decl: Decl) Zir.Inst.Ref {
+    pub fn zirLinksectionRef(decl: Decl, mod: *Module) Zir.Inst.Ref {
         if (!decl.has_linksection_or_addrspace) return .none;
         assert(decl.zir_decl_index != 0);
-        const zir = decl.getFileScope().zir;
+        const zir = decl.getFileScope(mod).zir;
         const extra_index = decl.zir_decl_index + 8 + @boolToInt(decl.has_align);
         return @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
     }
 
-    pub fn zirAddrspaceRef(decl: Decl) Zir.Inst.Ref {
+    pub fn zirAddrspaceRef(decl: Decl, mod: *Module) Zir.Inst.Ref {
         if (!decl.has_linksection_or_addrspace) return .none;
         assert(decl.zir_decl_index != 0);
-        const zir = decl.getFileScope().zir;
+        const zir = decl.getFileScope(mod).zir;
         const extra_index = decl.zir_decl_index + 8 + @boolToInt(decl.has_align) + 1;
         return @intToEnum(Zir.Inst.Ref, zir.extra[extra_index]);
     }
@@ -764,25 +769,25 @@ pub const Decl = struct {
         return LazySrcLoc.nodeOffset(decl.nodeIndexToRelative(node_index));
     }
 
-    pub fn srcLoc(decl: Decl) SrcLoc {
-        return decl.nodeOffsetSrcLoc(0);
+    pub fn srcLoc(decl: Decl, mod: *Module) SrcLoc {
+        return decl.nodeOffsetSrcLoc(0, mod);
     }
 
-    pub fn nodeOffsetSrcLoc(decl: Decl, node_offset: i32) SrcLoc {
+    pub fn nodeOffsetSrcLoc(decl: Decl, node_offset: i32, mod: *Module) SrcLoc {
         return .{
-            .file_scope = decl.getFileScope(),
+            .file_scope = decl.getFileScope(mod),
             .parent_decl_node = decl.src_node,
             .lazy = LazySrcLoc.nodeOffset(node_offset),
         };
     }
 
-    pub fn srcToken(decl: Decl) Ast.TokenIndex {
-        const tree = &decl.getFileScope().tree;
+    pub fn srcToken(decl: Decl, mod: *Module) Ast.TokenIndex {
+        const tree = &decl.getFileScope(mod).tree;
         return tree.firstToken(decl.src_node);
     }
 
-    pub fn srcByteOffset(decl: Decl) u32 {
-        const tree = &decl.getFileScope().tree;
+    pub fn srcByteOffset(decl: Decl, mod: *Module) u32 {
+        const tree = &decl.getFileScope(mod).tree;
         return tree.tokens.items(.start)[decl.srcToken()];
     }
 
@@ -791,12 +796,12 @@ pub const Decl = struct {
         if (decl.name_fully_qualified) {
             return writer.writeAll(unqualified_name);
         }
-        return decl.src_namespace.renderFullyQualifiedName(mod, unqualified_name, writer);
+        return mod.namespacePtr(decl.src_namespace).renderFullyQualifiedName(mod, unqualified_name, writer);
     }
 
     pub fn renderFullyQualifiedDebugName(decl: Decl, mod: *Module, writer: anytype) !void {
         const unqualified_name = mem.sliceTo(decl.name, 0);
-        return decl.src_namespace.renderFullyQualifiedDebugName(mod, unqualified_name, writer);
+        return mod.namespacePtr(decl.src_namespace).renderFullyQualifiedDebugName(mod, unqualified_name, writer);
     }
 
     pub fn getFullyQualifiedName(decl: Decl, mod: *Module) ![:0]u8 {
@@ -877,32 +882,39 @@ pub const Decl = struct {
     /// Gets the namespace that this Decl creates by being a struct, union,
     /// enum, or opaque.
     /// Only returns it if the Decl is the owner.
-    pub fn getInnerNamespace(decl: *Decl) ?*Namespace {
-        if (!decl.owns_tv) return null;
-        const ty = (decl.val.castTag(.ty) orelse return null).data;
-        switch (ty.tag()) {
-            .@"struct" => {
-                const struct_obj = ty.castTag(.@"struct").?.data;
-                return &struct_obj.namespace;
-            },
-            .enum_full, .enum_nonexhaustive => {
-                const enum_obj = ty.cast(Type.Payload.EnumFull).?.data;
-                return &enum_obj.namespace;
-            },
-            .empty_struct => {
-                return ty.castTag(.empty_struct).?.data;
-            },
-            .@"opaque" => {
-                const opaque_obj = ty.cast(Type.Payload.Opaque).?.data;
-                return &opaque_obj.namespace;
-            },
-            .@"union", .union_safety_tagged, .union_tagged => {
-                const union_obj = ty.cast(Type.Payload.Union).?.data;
-                return &union_obj.namespace;
-            },
+    pub fn getInnerNamespaceIndex(decl: *Decl, mod: *Module) Namespace.OptionalIndex {
+        if (!decl.owns_tv) return .none;
+        if (decl.val.ip_index == .none) {
+            const ty = (decl.val.castTag(.ty) orelse return .none).data;
+            switch (ty.tag()) {
+                .@"struct" => {
+                    const struct_obj = ty.castTag(.@"struct").?.data;
+                    return struct_obj.namespace.toOptional();
+                },
+                .enum_full, .enum_nonexhaustive => {
+                    const enum_obj = ty.cast(Type.Payload.EnumFull).?.data;
+                    return enum_obj.namespace.toOptional();
+                },
+                .empty_struct => {
+                    @panic("TODO");
+                },
+                .@"union", .union_safety_tagged, .union_tagged => {
+                    const union_obj = ty.cast(Type.Payload.Union).?.data;
+                    return union_obj.namespace.toOptional();
+                },
 
-            else => return null,
+                else => return .none,
+            }
         }
+        return switch (mod.intern_pool.indexToKey(decl.val.ip_index)) {
+            .opaque_type => |opaque_type| opaque_type.namespace.toOptional(),
+            else => .none,
+        };
+    }
+
+    /// Same as `getInnerNamespaceIndex` but additionally obtains the pointer.
+    pub fn getInnerNamespace(decl: *Decl, mod: *Module) ?*Namespace {
+        return if (getInnerNamespaceIndex(decl, mod).unwrap()) |i| mod.namespacePtr(i) else null;
     }
 
     pub fn dump(decl: *Decl) void {
@@ -920,8 +932,8 @@ pub const Decl = struct {
         std.debug.print("\n", .{});
     }
 
-    pub fn getFileScope(decl: Decl) *File {
-        return decl.src_namespace.file_scope;
+    pub fn getFileScope(decl: Decl, mod: *Module) *File {
+        return mod.namespacePtr(decl.src_namespace).file_scope;
     }
 
     pub fn removeDependant(decl: *Decl, other: Decl.Index) void {
@@ -974,7 +986,7 @@ pub const ErrorSet = struct {
     pub fn srcLoc(self: ErrorSet, mod: *Module) SrcLoc {
         const owner_decl = mod.declPtr(self.owner_decl);
         return .{
-            .file_scope = owner_decl.getFileScope(),
+            .file_scope = owner_decl.getFileScope(mod),
             .parent_decl_node = owner_decl.src_node,
             .lazy = LazySrcLoc.nodeOffset(0),
         };
@@ -1000,7 +1012,7 @@ pub const Struct = struct {
     /// Set of field names in declaration order.
     fields: Fields,
     /// Represents the declarations inside this struct.
-    namespace: Namespace,
+    namespace: Namespace.Index,
     /// The Decl that corresponds to the struct itself.
     owner_decl: Decl.Index,
     /// Index of the struct_decl ZIR instruction.
@@ -1101,7 +1113,7 @@ pub const Struct = struct {
     pub fn srcLoc(s: Struct, mod: *Module) SrcLoc {
         const owner_decl = mod.declPtr(s.owner_decl);
         return .{
-            .file_scope = owner_decl.getFileScope(),
+            .file_scope = owner_decl.getFileScope(mod),
             .parent_decl_node = owner_decl.src_node,
             .lazy = LazySrcLoc.nodeOffset(0),
         };
@@ -1110,7 +1122,7 @@ pub const Struct = struct {
     pub fn fieldSrcLoc(s: Struct, mod: *Module, query: FieldSrcQuery) SrcLoc {
         @setCold(true);
         const owner_decl = mod.declPtr(s.owner_decl);
-        const file = owner_decl.getFileScope();
+        const file = owner_decl.getFileScope(mod);
         const tree = file.getTree(mod.gpa) catch |err| {
             // In this case we emit a warning + a less precise source location.
             log.warn("unable to load {s}: {s}", .{
@@ -1224,7 +1236,7 @@ pub const EnumSimple = struct {
     pub fn srcLoc(self: EnumSimple, mod: *Module) SrcLoc {
         const owner_decl = mod.declPtr(self.owner_decl);
         return .{
-            .file_scope = owner_decl.getFileScope(),
+            .file_scope = owner_decl.getFileScope(mod),
             .parent_decl_node = owner_decl.src_node,
             .lazy = LazySrcLoc.nodeOffset(0),
         };
@@ -1253,7 +1265,7 @@ pub const EnumNumbered = struct {
     pub fn srcLoc(self: EnumNumbered, mod: *Module) SrcLoc {
         const owner_decl = mod.declPtr(self.owner_decl);
         return .{
-            .file_scope = owner_decl.getFileScope(),
+            .file_scope = owner_decl.getFileScope(mod),
             .parent_decl_node = owner_decl.src_node,
             .lazy = LazySrcLoc.nodeOffset(0),
         };
@@ -1275,7 +1287,7 @@ pub const EnumFull = struct {
     /// If this hash map is empty, it means the enum tags are auto-numbered.
     values: ValueMap,
     /// Represents the declarations inside this enum.
-    namespace: Namespace,
+    namespace: Namespace.Index,
     /// true if zig inferred this tag type, false if user specified it
     tag_ty_inferred: bool,
 
@@ -1285,7 +1297,7 @@ pub const EnumFull = struct {
     pub fn srcLoc(self: EnumFull, mod: *Module) SrcLoc {
         const owner_decl = mod.declPtr(self.owner_decl);
         return .{
-            .file_scope = owner_decl.getFileScope(),
+            .file_scope = owner_decl.getFileScope(mod),
             .parent_decl_node = owner_decl.src_node,
             .lazy = LazySrcLoc.nodeOffset(0),
         };
@@ -1294,7 +1306,7 @@ pub const EnumFull = struct {
     pub fn fieldSrcLoc(e: EnumFull, mod: *Module, query: FieldSrcQuery) SrcLoc {
         @setCold(true);
         const owner_decl = mod.declPtr(e.owner_decl);
-        const file = owner_decl.getFileScope();
+        const file = owner_decl.getFileScope(mod);
         const tree = file.getTree(mod.gpa) catch |err| {
             // In this case we emit a warning + a less precise source location.
             log.warn("unable to load {s}: {s}", .{
@@ -1323,7 +1335,7 @@ pub const Union = struct {
     /// Set of field names in declaration order.
     fields: Fields,
     /// Represents the declarations inside this union.
-    namespace: Namespace,
+    namespace: Namespace.Index,
     /// The Decl that corresponds to the union itself.
     owner_decl: Decl.Index,
     /// Index of the union_decl ZIR instruction.
@@ -1371,7 +1383,7 @@ pub const Union = struct {
     pub fn srcLoc(self: Union, mod: *Module) SrcLoc {
         const owner_decl = mod.declPtr(self.owner_decl);
         return .{
-            .file_scope = owner_decl.getFileScope(),
+            .file_scope = owner_decl.getFileScope(mod),
             .parent_decl_node = owner_decl.src_node,
             .lazy = LazySrcLoc.nodeOffset(0),
         };
@@ -1380,7 +1392,7 @@ pub const Union = struct {
     pub fn fieldSrcLoc(u: Union, mod: *Module, query: FieldSrcQuery) SrcLoc {
         @setCold(true);
         const owner_decl = mod.declPtr(u.owner_decl);
-        const file = owner_decl.getFileScope();
+        const file = owner_decl.getFileScope(mod);
         const tree = file.getTree(mod.gpa) catch |err| {
             // In this case we emit a warning + a less precise source location.
             log.warn("unable to load {s}: {s}", .{
@@ -1563,26 +1575,6 @@ pub const Union = struct {
     }
 };
 
-pub const Opaque = struct {
-    /// The Decl that corresponds to the opaque itself.
-    owner_decl: Decl.Index,
-    /// Represents the declarations inside this opaque.
-    namespace: Namespace,
-
-    pub fn srcLoc(self: Opaque, mod: *Module) SrcLoc {
-        const owner_decl = mod.declPtr(self.owner_decl);
-        return .{
-            .file_scope = owner_decl.getFileScope(),
-            .parent_decl_node = owner_decl.src_node,
-            .lazy = LazySrcLoc.nodeOffset(0),
-        };
-    }
-
-    pub fn getFullyQualifiedName(s: *Opaque, mod: *Module) ![:0]u8 {
-        return mod.declPtr(s.owner_decl).getFullyQualifiedName(mod);
-    }
-};
-
 /// Some extern function struct memory is owned by the Decl's TypedValue.Managed
 /// arena allocator.
 pub const ExternFn = struct {
@@ -1759,7 +1751,7 @@ pub const Fn = struct {
     }
 
     pub fn isAnytypeParam(func: Fn, mod: *Module, index: u32) bool {
-        const file = mod.declPtr(func.owner_decl).getFileScope();
+        const file = mod.declPtr(func.owner_decl).getFileScope(mod);
 
         const tags = file.zir.instructions.items(.tag);
 
@@ -1774,7 +1766,7 @@ pub const Fn = struct {
     }
 
     pub fn getParamName(func: Fn, mod: *Module, index: u32) [:0]const u8 {
-        const file = mod.declPtr(func.owner_decl).getFileScope();
+        const file = mod.declPtr(func.owner_decl).getFileScope(mod);
 
         const tags = file.zir.instructions.items(.tag);
         const data = file.zir.instructions.items(.data);
@@ -1797,7 +1789,7 @@ pub const Fn = struct {
 
     pub fn hasInferredErrorSet(func: Fn, mod: *Module) bool {
         const owner_decl = mod.declPtr(func.owner_decl);
-        const zir = owner_decl.getFileScope().zir;
+        const zir = owner_decl.getFileScope(mod).zir;
         const zir_tags = zir.instructions.items(.tag);
         switch (zir_tags[func.zir_body_inst]) {
             .func => return false,
@@ -1851,7 +1843,7 @@ pub const DeclAdapter = struct {
 
 /// The container that structs, enums, unions, and opaques have.
 pub const Namespace = struct {
-    parent: ?*Namespace,
+    parent: OptionalIndex,
     file_scope: *File,
     /// Will be a struct, enum, union, or opaque.
     ty: Type,
@@ -1869,6 +1861,28 @@ pub const Namespace = struct {
     /// Value is whether the usingnamespace decl is marked `pub`.
     usingnamespace_set: std.AutoHashMapUnmanaged(Decl.Index, bool) = .{},
 
+    pub const Index = enum(u32) {
+        _,
+
+        pub fn toOptional(i: Index) OptionalIndex {
+            return @intToEnum(OptionalIndex, @enumToInt(i));
+        }
+    };
+
+    pub const OptionalIndex = enum(u32) {
+        none = std.math.maxInt(u32),
+        _,
+
+        pub fn init(oi: ?Index) OptionalIndex {
+            return @intToEnum(OptionalIndex, @enumToInt(oi orelse return .none));
+        }
+
+        pub fn unwrap(oi: OptionalIndex) ?Index {
+            if (oi == .none) return null;
+            return @intToEnum(Index, @enumToInt(oi));
+        }
+    };
+
     const DeclContext = struct {
         module: *Module,
 
@@ -1955,10 +1969,10 @@ pub const Namespace = struct {
         name: []const u8,
         writer: anytype,
     ) @TypeOf(writer).Error!void {
-        if (ns.parent) |parent| {
-            const decl_index = ns.getDeclIndex();
+        if (ns.parent.unwrap()) |parent| {
+            const decl_index = ns.getDeclIndex(mod);
             const decl = mod.declPtr(decl_index);
-            try parent.renderFullyQualifiedName(mod, mem.sliceTo(decl.name, 0), writer);
+            try mod.namespacePtr(parent).renderFullyQualifiedName(mod, mem.sliceTo(decl.name, 0), writer);
         } else {
             try ns.file_scope.renderFullyQualifiedName(writer);
         }
@@ -1976,10 +1990,10 @@ pub const Namespace = struct {
         writer: anytype,
     ) @TypeOf(writer).Error!void {
         var separator_char: u8 = '.';
-        if (ns.parent) |parent| {
-            const decl_index = ns.getDeclIndex();
+        if (ns.parent.unwrap()) |parent| {
+            const decl_index = ns.getDeclIndex(mod);
             const decl = mod.declPtr(decl_index);
-            try parent.renderFullyQualifiedDebugName(mod, mem.sliceTo(decl.name, 0), writer);
+            try mod.namespacePtr(parent).renderFullyQualifiedDebugName(mod, mem.sliceTo(decl.name, 0), writer);
         } else {
             try ns.file_scope.renderFullyQualifiedDebugName(writer);
             separator_char = ':';
@@ -1990,8 +2004,8 @@ pub const Namespace = struct {
         }
     }
 
-    pub fn getDeclIndex(ns: Namespace) Decl.Index {
-        return ns.ty.getOwnerDecl();
+    pub fn getDeclIndex(ns: Namespace, mod: *Module) Decl.Index {
+        return ns.ty.getOwnerDecl(mod);
     }
 };
 
@@ -3320,7 +3334,7 @@ pub const LazySrcLoc = union(enum) {
     }
 
     /// Upgrade to a `SrcLoc` based on the `Decl` provided.
-    pub fn toSrcLoc(lazy: LazySrcLoc, decl: *Decl) SrcLoc {
+    pub fn toSrcLoc(lazy: LazySrcLoc, decl: *Decl, mod: *Module) SrcLoc {
         return switch (lazy) {
             .unneeded,
             .entire_file,
@@ -3328,7 +3342,7 @@ pub const LazySrcLoc = union(enum) {
             .token_abs,
             .node_abs,
             => .{
-                .file_scope = decl.getFileScope(),
+                .file_scope = decl.getFileScope(mod),
                 .parent_decl_node = 0,
                 .lazy = lazy,
             },
@@ -3394,7 +3408,7 @@ pub const LazySrcLoc = union(enum) {
             .for_input,
             .for_capture_from_input,
             => .{
-                .file_scope = decl.getFileScope(),
+                .file_scope = decl.getFileScope(mod),
                 .parent_decl_node = decl.src_node,
                 .lazy = lazy,
             },
@@ -3555,6 +3569,9 @@ pub fn deinit(mod: *Module) void {
     mod.global_assembly.deinit(gpa);
     mod.reference_table.deinit(gpa);
 
+    mod.namespaces_free_list.deinit(gpa);
+    mod.allocated_namespaces.deinit(gpa);
+
     mod.string_literal_table.deinit(gpa);
     mod.string_literal_bytes.deinit(gpa);
 
@@ -3575,8 +3592,9 @@ pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {
             gpa.free(kv.value);
         }
         if (decl.has_tv) {
-            if (decl.getInnerNamespace()) |namespace| {
-                namespace.destroyDecls(mod);
+            if (decl.getInnerNamespaceIndex(mod).unwrap()) |i| {
+                mod.namespacePtr(i).destroyDecls(mod);
+                mod.destroyNamespace(i);
             }
         }
         decl.clearValues(mod);
@@ -3596,16 +3614,21 @@ pub fn destroyDecl(mod: *Module, decl_index: Decl.Index) void {
     }
 }
 
-pub fn declPtr(mod: *Module, decl_index: Decl.Index) *Decl {
-    return mod.allocated_decls.at(@enumToInt(decl_index));
+pub fn declPtr(mod: *Module, index: Decl.Index) *Decl {
+    return mod.allocated_decls.at(@enumToInt(index));
+}
+
+pub fn namespacePtr(mod: *Module, index: Namespace.Index) *Namespace {
+    return mod.allocated_namespaces.at(@enumToInt(index));
 }
 
 /// Returns true if and only if the Decl is the top level struct associated with a File.
 pub fn declIsRoot(mod: *Module, decl_index: Decl.Index) bool {
     const decl = mod.declPtr(decl_index);
-    if (decl.src_namespace.parent != null)
+    const namespace = mod.namespacePtr(decl.src_namespace);
+    if (namespace.parent != .none)
         return false;
-    return decl_index == decl.src_namespace.getDeclIndex();
+    return decl_index == namespace.getDeclIndex(mod);
 }
 
 fn freeExportList(gpa: Allocator, export_list: *ArrayListUnmanaged(*Export)) void {
@@ -4076,7 +4099,7 @@ fn updateZirRefs(mod: *Module, file: *File, old_zir: Zir) !void {
             };
         }
 
-        if (decl.getInnerNamespace()) |namespace| {
+        if (decl.getInnerNamespace(mod)) |namespace| {
             for (namespace.decls.keys()) |sub_decl| {
                 try decl_stack.append(gpa, sub_decl);
             }
@@ -4306,7 +4329,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl_index: Decl.Index) SemaError!void {
                 try mod.failed_decls.ensureUnusedCapacity(mod.gpa, 1);
                 mod.failed_decls.putAssumeCapacityNoClobber(decl_index, try ErrorMsg.create(
                     mod.gpa,
-                    decl.srcLoc(),
+                    decl.srcLoc(mod),
                     "unable to analyze: {s}",
                     .{@errorName(e)},
                 ));
@@ -4437,7 +4460,7 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {
                             decl_index,
                             try Module.ErrorMsg.create(
                                 gpa,
-                                decl.srcLoc(),
+                                decl.srcLoc(mod),
                                 "invalid liveness: {s}",
                                 .{@errorName(err)},
                             ),
@@ -4460,7 +4483,7 @@ pub fn ensureFuncBodyAnalyzed(mod: *Module, func: *Fn) SemaError!void {
                     try mod.failed_decls.ensureUnusedCapacity(gpa, 1);
                     mod.failed_decls.putAssumeCapacityNoClobber(decl_index, try Module.ErrorMsg.create(
                         gpa,
-                        decl.srcLoc(),
+                        decl.srcLoc(mod),
                         "unable to codegen: {s}",
                         .{@errorName(err)},
                     ));
@@ -4586,13 +4609,13 @@ pub fn semaFile(mod: *Module, file: *File) SemaError!void {
         .status = .none,
         .known_non_opv = undefined,
         .is_tuple = undefined, // set below
-        .namespace = .{
-            .parent = null,
+        .namespace = try mod.createNamespace(.{
+            .parent = .none,
             .ty = struct_ty,
             .file_scope = file,
-        },
+        }),
     };
-    const new_decl_index = try mod.allocateNewDecl(&struct_obj.namespace, 0, null);
+    const new_decl_index = try mod.allocateNewDecl(struct_obj.namespace, 0, null);
     const new_decl = mod.declPtr(new_decl_index);
     file.root_decl = new_decl_index.toOptional();
     struct_obj.owner_decl = new_decl_index;
@@ -4688,12 +4711,12 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
 
     const decl = mod.declPtr(decl_index);
 
-    if (decl.getFileScope().status != .success_zir) {
+    if (decl.getFileScope(mod).status != .success_zir) {
         return error.AnalysisFail;
     }
 
     const gpa = mod.gpa;
-    const zir = decl.getFileScope().zir;
+    const zir = decl.getFileScope(mod).zir;
     const zir_datas = zir.instructions.items(.data);
 
     decl.analysis = .in_progress;
@@ -4767,7 +4790,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
         block_scope.params.deinit(gpa);
     }
 
-    const zir_block_index = decl.zirBlockIndex();
+    const zir_block_index = decl.zirBlockIndex(mod);
     const inst_data = zir_datas[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];
@@ -4792,7 +4815,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
             });
         }
         const ty = try decl_tv.val.toType().copy(decl_arena_allocator);
-        if (ty.getNamespace() == null) {
+        if (ty.getNamespace(mod) == null) {
             return sema.fail(&block_scope, ty_src, "type {} has no namespace", .{ty.fmt(mod)});
         }
 
@@ -4895,12 +4918,12 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
     decl.ty = try decl_tv.ty.copy(decl_arena_allocator);
     decl.val = try decl_tv.val.copy(decl_arena_allocator);
     decl.@"align" = blk: {
-        const align_ref = decl.zirAlignRef();
+        const align_ref = decl.zirAlignRef(mod);
         if (align_ref == .none) break :blk 0;
         break :blk try sema.resolveAlign(&block_scope, align_src, align_ref);
     };
     decl.@"linksection" = blk: {
-        const linksection_ref = decl.zirLinksectionRef();
+        const linksection_ref = decl.zirLinksectionRef(mod);
         if (linksection_ref == .none) break :blk null;
         const bytes = try sema.resolveConstString(&block_scope, section_src, linksection_ref, "linksection must be comptime-known");
         if (mem.indexOfScalar(u8, bytes, 0) != null) {
@@ -4921,7 +4944,7 @@ fn semaDecl(mod: *Module, decl_index: Decl.Index) !bool {
         };
 
         const target = sema.mod.getTarget();
-        break :blk switch (decl.zirAddrspaceRef()) {
+        break :blk switch (decl.zirAddrspaceRef(mod)) {
             .none => switch (addrspace_ctx) {
                 .function => target_util.defaultAddressSpace(target, .function),
                 .variable => target_util.defaultAddressSpace(target, .global_mutable),
@@ -5273,7 +5296,7 @@ pub fn detectEmbedFileUpdate(mod: *Module, embed_file: *EmbedFile) !void {
 
 pub fn scanNamespace(
     mod: *Module,
-    namespace: *Namespace,
+    namespace_index: Namespace.Index,
     extra_start: usize,
     decls_len: u32,
     parent_decl: *Decl,
@@ -5282,6 +5305,7 @@ pub fn scanNamespace(
     defer tracy.end();
 
     const gpa = mod.gpa;
+    const namespace = mod.namespacePtr(namespace_index);
     const zir = namespace.file_scope.zir;
 
     try mod.comp.work_queue.ensureUnusedCapacity(decls_len);
@@ -5294,7 +5318,7 @@ pub fn scanNamespace(
     var decl_i: u32 = 0;
     var scan_decl_iter: ScanDeclIter = .{
         .module = mod,
-        .namespace = namespace,
+        .namespace_index = namespace_index,
         .parent_decl = parent_decl,
     };
     while (decl_i < decls_len) : (decl_i += 1) {
@@ -5317,7 +5341,7 @@ pub fn scanNamespace(
 
 const ScanDeclIter = struct {
     module: *Module,
-    namespace: *Namespace,
+    namespace_index: Namespace.Index,
     parent_decl: *Decl,
     usingnamespace_index: usize = 0,
     comptime_index: usize = 0,
@@ -5329,7 +5353,8 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
     defer tracy.end();
 
     const mod = iter.module;
-    const namespace = iter.namespace;
+    const namespace_index = iter.namespace_index;
+    const namespace = mod.namespacePtr(namespace_index);
     const gpa = mod.gpa;
     const zir = namespace.file_scope.zir;
 
@@ -5404,7 +5429,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
     );
     const comp = mod.comp;
     if (!gop.found_existing) {
-        const new_decl_index = try mod.allocateNewDecl(namespace, decl_node, iter.parent_decl.src_scope);
+        const new_decl_index = try mod.allocateNewDecl(namespace_index, decl_node, iter.parent_decl.src_scope);
         const new_decl = mod.declPtr(new_decl_index);
         new_decl.kind = kind;
         new_decl.name = decl_name;
@@ -5456,7 +5481,7 @@ fn scanDecl(iter: *ScanDeclIter, decl_sub_index: usize, flags: u4) Allocator.Err
     const decl = mod.declPtr(decl_index);
     if (kind == .@"test") {
         const src_loc = SrcLoc{
-            .file_scope = decl.getFileScope(),
+            .file_scope = decl.getFileScope(mod),
             .parent_decl_node = decl.src_node,
             .lazy = .{ .token_offset = 1 },
         };
@@ -5564,7 +5589,7 @@ pub fn clearDecl(
         if (decl.ty.isFnOrHasRuntimeBits(mod)) {
             mod.comp.bin_file.freeDecl(decl_index);
         }
-        if (decl.getInnerNamespace()) |namespace| {
+        if (decl.getInnerNamespace(mod)) |namespace| {
             try namespace.deleteAllDecls(mod, outdated_decls);
         }
     }
@@ -5584,7 +5609,7 @@ pub fn deleteUnusedDecl(mod: *Module, decl_index: Decl.Index) void {
     log.debug("deleteUnusedDecl {d} ({s})", .{ decl_index, decl.name });
 
     assert(!mod.declIsRoot(decl_index));
-    assert(decl.src_namespace.anon_decls.swapRemove(decl_index));
+    assert(mod.namespacePtr(decl.src_namespace).anon_decls.swapRemove(decl_index));
 
     const dependants = decl.dependants.keys();
     for (dependants) |dep| {
@@ -5612,7 +5637,7 @@ pub fn abortAnonDecl(mod: *Module, decl_index: Decl.Index) void {
     log.debug("abortAnonDecl {*} ({s})", .{ decl, decl.name });
 
     assert(!mod.declIsRoot(decl_index));
-    assert(decl.src_namespace.anon_decls.swapRemove(decl_index));
+    assert(mod.namespacePtr(decl.src_namespace).anon_decls.swapRemove(decl_index));
 
     // An aborted decl must not have dependants -- they must have
     // been aborted first and removed from this list.
@@ -5689,7 +5714,7 @@ pub fn analyzeFnBody(mod: *Module, func: *Fn, arena: Allocator) SemaError!Air {
         .gpa = gpa,
         .arena = arena,
         .perm_arena = decl_arena_allocator,
-        .code = decl.getFileScope().zir,
+        .code = decl.getFileScope(mod).zir,
         .owner_decl = decl,
         .owner_decl_index = decl_index,
         .func = func,
@@ -5920,9 +5945,34 @@ fn markOutdatedDecl(mod: *Module, decl_index: Decl.Index) !void {
     decl.analysis = .outdated;
 }
 
+pub const CreateNamespaceOptions = struct {
+    parent: Namespace.OptionalIndex,
+    file_scope: *File,
+    ty: Type,
+};
+
+pub fn createNamespace(mod: *Module, options: CreateNamespaceOptions) !Namespace.Index {
+    if (mod.namespaces_free_list.popOrNull()) |index| return index;
+    const ptr = try mod.allocated_namespaces.addOne(mod.gpa);
+    ptr.* = .{
+        .parent = options.parent,
+        .file_scope = options.file_scope,
+        .ty = options.ty,
+    };
+    return @intToEnum(Namespace.Index, mod.allocated_namespaces.len - 1);
+}
+
+pub fn destroyNamespace(mod: *Module, index: Namespace.Index) void {
+    mod.namespacePtr(index).* = undefined;
+    mod.namespaces_free_list.append(mod.gpa, index) catch {
+        // In order to keep `destroyNamespace` a non-fallible function, we ignore memory
+        // allocation failures here, instead leaking the Namespace until garbage collection.
+    };
+}
+
 pub fn allocateNewDecl(
     mod: *Module,
-    namespace: *Namespace,
+    namespace: Namespace.Index,
     src_node: Ast.Node.Index,
     src_scope: ?*CaptureScope,
 ) !Decl.Index {
@@ -6004,7 +6054,7 @@ pub fn createAnonymousDecl(mod: *Module, block: *Sema.Block, typed_value: TypedV
 pub fn createAnonymousDeclFromDecl(
     mod: *Module,
     src_decl: *Decl,
-    namespace: *Namespace,
+    namespace: Namespace.Index,
     src_scope: ?*CaptureScope,
     tv: TypedValue,
 ) !Decl.Index {
@@ -6022,7 +6072,7 @@ pub fn initNewAnonDecl(
     mod: *Module,
     new_decl_index: Decl.Index,
     src_line: u32,
-    namespace: *Namespace,
+    namespace: Namespace.Index,
     typed_value: TypedValue,
     name: [:0]u8,
 ) !void {
@@ -6040,7 +6090,7 @@ pub fn initNewAnonDecl(
     new_decl.analysis = .complete;
     new_decl.generation = mod.generation;
 
-    try namespace.anon_decls.putNoClobber(mod.gpa, new_decl_index, {});
+    try mod.namespacePtr(namespace).anon_decls.putNoClobber(mod.gpa, new_decl_index, {});
 
     // The Decl starts off with alive=false and the codegen backend will set alive=true
     // if the Decl is referenced by an instruction or another constant. Otherwise,
@@ -6110,16 +6160,17 @@ pub const SwitchProngSrc = union(enum) {
     /// the LazySrcLoc in order to emit a compile error.
     pub fn resolve(
         prong_src: SwitchProngSrc,
-        gpa: Allocator,
+        mod: *Module,
         decl: *Decl,
         switch_node_offset: i32,
         range_expand: RangeExpand,
     ) LazySrcLoc {
         @setCold(true);
-        const tree = decl.getFileScope().getTree(gpa) catch |err| {
+        const gpa = mod.gpa;
+        const tree = decl.getFileScope(mod).getTree(gpa) catch |err| {
             // In this case we emit a warning + a less precise source location.
             log.warn("unable to load {s}: {s}", .{
-                decl.getFileScope().sub_file_path, @errorName(err),
+                decl.getFileScope(mod).sub_file_path, @errorName(err),
             });
             return LazySrcLoc.nodeOffset(0);
         };
@@ -6203,11 +6254,12 @@ pub const PeerTypeCandidateSrc = union(enum) {
 
     pub fn resolve(
         self: PeerTypeCandidateSrc,
-        gpa: Allocator,
+        mod: *Module,
         decl: *Decl,
         candidate_i: usize,
     ) ?LazySrcLoc {
         @setCold(true);
+        const gpa = mod.gpa;
 
         switch (self) {
             .none => {
@@ -6229,10 +6281,10 @@ pub const PeerTypeCandidateSrc = union(enum) {
                     else => {},
                 }
 
-                const tree = decl.getFileScope().getTree(gpa) catch |err| {
+                const tree = decl.getFileScope(mod).getTree(gpa) catch |err| {
                     // In this case we emit a warning + a less precise source location.
                     log.warn("unable to load {s}: {s}", .{
-                        decl.getFileScope().sub_file_path, @errorName(err),
+                        decl.getFileScope(mod).sub_file_path, @errorName(err),
                     });
                     return LazySrcLoc.nodeOffset(0);
                 };
@@ -6291,15 +6343,16 @@ fn queryFieldSrc(
 
 pub fn paramSrc(
     func_node_offset: i32,
-    gpa: Allocator,
+    mod: *Module,
     decl: *Decl,
     param_i: usize,
 ) LazySrcLoc {
     @setCold(true);
-    const tree = decl.getFileScope().getTree(gpa) catch |err| {
+    const gpa = mod.gpa;
+    const tree = decl.getFileScope(mod).getTree(gpa) catch |err| {
         // In this case we emit a warning + a less precise source location.
         log.warn("unable to load {s}: {s}", .{
-            decl.getFileScope().sub_file_path, @errorName(err),
+            decl.getFileScope(mod).sub_file_path, @errorName(err),
         });
         return LazySrcLoc.nodeOffset(0);
     };
@@ -6321,19 +6374,20 @@ pub fn paramSrc(
 }
 
 pub fn argSrc(
+    mod: *Module,
     call_node_offset: i32,
-    gpa: Allocator,
     decl: *Decl,
     start_arg_i: usize,
     bound_arg_src: ?LazySrcLoc,
 ) LazySrcLoc {
+    @setCold(true);
+    const gpa = mod.gpa;
     if (start_arg_i == 0 and bound_arg_src != null) return bound_arg_src.?;
     const arg_i = start_arg_i - @boolToInt(bound_arg_src != null);
-    @setCold(true);
-    const tree = decl.getFileScope().getTree(gpa) catch |err| {
+    const tree = decl.getFileScope(mod).getTree(gpa) catch |err| {
         // In this case we emit a warning + a less precise source location.
         log.warn("unable to load {s}: {s}", .{
-            decl.getFileScope().sub_file_path, @errorName(err),
+            decl.getFileScope(mod).sub_file_path, @errorName(err),
         });
         return LazySrcLoc.nodeOffset(0);
     };
@@ -6347,7 +6401,7 @@ pub fn argSrc(
             const node_datas = tree.nodes.items(.data);
             const call_args_node = tree.extra_data[node_datas[node].rhs - 1];
             const call_args_offset = decl.nodeIndexToRelative(call_args_node);
-            return initSrc(call_args_offset, gpa, decl, arg_i);
+            return mod.initSrc(call_args_offset, decl, arg_i);
         },
         else => unreachable,
     };
@@ -6355,16 +6409,17 @@ pub fn argSrc(
 }
 
 pub fn initSrc(
+    mod: *Module,
     init_node_offset: i32,
-    gpa: Allocator,
     decl: *Decl,
     init_index: usize,
 ) LazySrcLoc {
     @setCold(true);
-    const tree = decl.getFileScope().getTree(gpa) catch |err| {
+    const gpa = mod.gpa;
+    const tree = decl.getFileScope(mod).getTree(gpa) catch |err| {
         // In this case we emit a warning + a less precise source location.
         log.warn("unable to load {s}: {s}", .{
-            decl.getFileScope().sub_file_path, @errorName(err),
+            decl.getFileScope(mod).sub_file_path, @errorName(err),
         });
         return LazySrcLoc.nodeOffset(0);
     };
@@ -6400,12 +6455,13 @@ pub fn initSrc(
     }
 }
 
-pub fn optionsSrc(gpa: Allocator, decl: *Decl, base_src: LazySrcLoc, wanted: []const u8) LazySrcLoc {
+pub fn optionsSrc(mod: *Module, decl: *Decl, base_src: LazySrcLoc, wanted: []const u8) LazySrcLoc {
     @setCold(true);
-    const tree = decl.getFileScope().getTree(gpa) catch |err| {
+    const gpa = mod.gpa;
+    const tree = decl.getFileScope(mod).getTree(gpa) catch |err| {
         // In this case we emit a warning + a less precise source location.
         log.warn("unable to load {s}: {s}", .{
-            decl.getFileScope().sub_file_path, @errorName(err),
+            decl.getFileScope(mod).sub_file_path, @errorName(err),
         });
         return LazySrcLoc.nodeOffset(0);
     };
@@ -6471,7 +6527,10 @@ pub fn processOutdatedAndDeletedDecls(mod: *Module) !void {
 
             // Remove from the namespace it resides in, preserving declaration order.
             assert(decl.zir_decl_index != 0);
-            _ = decl.src_namespace.decls.orderedRemoveAdapted(@as([]const u8, mem.sliceTo(decl.name, 0)), DeclAdapter{ .mod = mod });
+            _ = mod.namespacePtr(decl.src_namespace).decls.orderedRemoveAdapted(
+                @as([]const u8, mem.sliceTo(decl.name, 0)),
+                DeclAdapter{ .mod = mod },
+            );
 
             try mod.clearDecl(decl_index, &outdated_decls);
             mod.destroyDecl(decl_index);
@@ -6541,8 +6600,11 @@ pub fn populateTestFunctions(
     const builtin_pkg = mod.main_pkg.table.get("builtin").?;
     const builtin_file = (mod.importPkg(builtin_pkg) catch unreachable).file;
     const root_decl = mod.declPtr(builtin_file.root_decl.unwrap().?);
-    const builtin_namespace = root_decl.src_namespace;
-    const decl_index = builtin_namespace.decls.getKeyAdapted(@as([]const u8, "test_functions"), DeclAdapter{ .mod = mod }).?;
+    const builtin_namespace = mod.namespacePtr(root_decl.src_namespace);
+    const decl_index = builtin_namespace.decls.getKeyAdapted(
+        @as([]const u8, "test_functions"),
+        DeclAdapter{ .mod = mod },
+    ).?;
     {
         // We have to call `ensureDeclAnalyzed` here in case `builtin.test_functions`
         // was not referenced by start code.
@@ -6673,7 +6735,7 @@ pub fn linkerUpdateDecl(mod: *Module, decl_index: Decl.Index) !void {
             try mod.failed_decls.ensureUnusedCapacity(gpa, 1);
             mod.failed_decls.putAssumeCapacityNoClobber(decl_index, try ErrorMsg.create(
                 gpa,
-                decl.srcLoc(),
+                decl.srcLoc(mod),
                 "unable to codegen: {s}",
                 .{@errorName(err)},
             ));
@@ -7138,3 +7200,24 @@ pub fn atomicPtrAlignment(
 
     return 0;
 }
+
+pub fn opaqueSrcLoc(mod: *Module, opaque_type: InternPool.Key.OpaqueType) SrcLoc {
+    const owner_decl = mod.declPtr(opaque_type.decl);
+    return .{
+        .file_scope = owner_decl.getFileScope(mod),
+        .parent_decl_node = owner_decl.src_node,
+        .lazy = LazySrcLoc.nodeOffset(0),
+    };
+}
+
+pub fn opaqueFullyQualifiedName(mod: *Module, opaque_type: InternPool.Key.OpaqueType) ![:0]u8 {
+    return mod.declPtr(opaque_type.decl).getFullyQualifiedName(mod);
+}
+
+pub fn declFileScope(mod: *Module, decl_index: Decl.Index) *File {
+    return mod.declPtr(decl_index).getFileScope(mod);
+}
+
+pub fn namespaceDeclIndex(mod: *Module, namespace_index: Namespace.Index) Decl.Index {
+    return mod.namespacePtr(namespace_index).getDeclIndex(mod);
+}
src/Sema.zig
@@ -227,7 +227,7 @@ pub const Block = struct {
     sema: *Sema,
     /// The namespace to use for lookups from this source block
     /// When analyzing fields, this is different from src_decl.src_namespace.
-    namespace: *Namespace,
+    namespace: Namespace.Index,
     /// The AIR instructions generated for this block.
     instructions: std.ArrayListUnmanaged(Air.Inst.Index),
     // `param` instructions are collected here to be used by the `func` instruction.
@@ -286,6 +286,7 @@ pub const Block = struct {
 
         fn explain(cr: ComptimeReason, sema: *Sema, msg: ?*Module.ErrorMsg) !void {
             const parent = msg orelse return;
+            const mod = sema.mod;
             const prefix = "expression is evaluated at comptime because ";
             switch (cr) {
                 .c_import => |ci| {
@@ -293,12 +294,12 @@ pub const Block = struct {
                 },
                 .comptime_ret_ty => |rt| {
                     const src_loc = if (try sema.funcDeclSrc(rt.func)) |fn_decl| blk: {
-                        var src_loc = fn_decl.srcLoc();
+                        var src_loc = fn_decl.srcLoc(mod);
                         src_loc.lazy = .{ .node_offset_fn_type_ret_ty = 0 };
                         break :blk src_loc;
                     } else blk: {
                         const src_decl = sema.mod.declPtr(rt.block.src_decl);
-                        break :blk rt.func_src.toSrcLoc(src_decl);
+                        break :blk rt.func_src.toSrcLoc(src_decl, mod);
                     };
                     if (rt.return_ty.isGenericPoison()) {
                         return sema.mod.errNoteNonLazy(src_loc, parent, prefix ++ "the generic function was instantiated with a comptime-only return type", .{});
@@ -399,8 +400,8 @@ pub const Block = struct {
         };
     }
 
-    pub fn getFileScope(block: *Block) *Module.File {
-        return block.namespace.file_scope;
+    pub fn getFileScope(block: *Block, mod: *Module) *Module.File {
+        return mod.namespacePtr(block.namespace).file_scope;
     }
 
     fn addTy(
@@ -876,6 +877,7 @@ fn analyzeBodyInner(
         wip_captures.deinit();
     };
 
+    const mod = sema.mod;
     const map = &sema.inst_map;
     const tags = sema.code.instructions.items(.tag);
     const datas = sema.code.instructions.items(.data);
@@ -896,7 +898,7 @@ fn analyzeBodyInner(
         crash_info.setBodyIndex(i);
         const inst = body[i];
         std.log.scoped(.sema_zir).debug("sema ZIR {s} %{d}", .{
-            sema.mod.declPtr(block.src_decl).src_namespace.file_scope.sub_file_path, inst,
+            mod.namespacePtr(mod.declPtr(block.src_decl).src_namespace).file_scope.sub_file_path, inst,
         });
         const air_inst: Air.Inst.Ref = switch (tags[inst]) {
             // zig fmt: off
@@ -1574,7 +1576,6 @@ fn analyzeBodyInner(
             },
             .condbr => blk: {
                 if (!block.is_comptime) break sema.zirCondbr(block, inst);
-                const mod = sema.mod;
                 // Same as condbr_inline. TODO https://github.com/ziglang/zig/issues/8220
                 const inst_data = datas[inst].pl_node;
                 const cond_src: LazySrcLoc = .{ .node_offset_if_cond = inst_data.src_node };
@@ -1597,7 +1598,6 @@ fn analyzeBodyInner(
                 }
             },
             .condbr_inline => blk: {
-                const mod = sema.mod;
                 const inst_data = datas[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);
@@ -1622,7 +1622,6 @@ fn analyzeBodyInner(
             },
             .@"try" => blk: {
                 if (!block.is_comptime) break :blk try sema.zirTry(block, inst);
-                const mod = sema.mod;
                 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
                 const src = inst_data.src();
                 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
@@ -1632,7 +1631,7 @@ fn analyzeBodyInner(
                 const err_union_ty = sema.typeOf(err_union);
                 if (err_union_ty.zigTypeTag(mod) != .ErrorUnion) {
                     return sema.fail(block, operand_src, "expected error union type, found '{}'", .{
-                        err_union_ty.fmt(sema.mod),
+                        err_union_ty.fmt(mod),
                     });
                 }
                 const is_non_err = try sema.analyzeIsNonErrComptimeOnly(block, operand_src, err_union);
@@ -1654,7 +1653,6 @@ fn analyzeBodyInner(
             },
             .try_ptr => blk: {
                 if (!block.is_comptime) break :blk try sema.zirTryPtr(block, inst);
-                const mod = sema.mod;
                 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
                 const src = inst_data.src();
                 const operand_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
@@ -1713,7 +1711,7 @@ fn analyzeBodyInner(
     const noreturn_inst = block.instructions.popOrNull();
     while (dbg_block_begins > 0) {
         dbg_block_begins -= 1;
-        if (block.is_comptime or sema.mod.comp.bin_file.options.strip) continue;
+        if (block.is_comptime or mod.comp.bin_file.options.strip) continue;
 
         _ = try block.addInst(.{
             .tag = .dbg_block_end,
@@ -2172,7 +2170,7 @@ fn errNote(
 ) error{OutOfMemory}!void {
     const mod = sema.mod;
     const src_decl = mod.declPtr(block.src_decl);
-    return mod.errNoteNonLazy(src.toSrcLoc(src_decl), parent, format, args);
+    return mod.errNoteNonLazy(src.toSrcLoc(src_decl, mod), parent, format, args);
 }
 
 fn addFieldErrNote(
@@ -2185,19 +2183,19 @@ fn addFieldErrNote(
 ) !void {
     @setCold(true);
     const mod = sema.mod;
-    const decl_index = container_ty.getOwnerDecl();
+    const decl_index = container_ty.getOwnerDecl(mod);
     const decl = mod.declPtr(decl_index);
 
     const field_src = blk: {
-        const tree = decl.getFileScope().getTree(sema.gpa) catch |err| {
+        const tree = decl.getFileScope(mod).getTree(sema.gpa) catch |err| {
             log.err("unable to load AST to report compile error: {s}", .{@errorName(err)});
-            break :blk decl.srcLoc();
+            break :blk decl.srcLoc(mod);
         };
 
         const container_node = decl.relativeToNodeIndex(0);
         const node_tags = tree.nodes.items(.tag);
         var buf: [2]std.zig.Ast.Node.Index = undefined;
-        const container_decl = tree.fullContainerDecl(&buf, container_node) orelse break :blk decl.srcLoc();
+        const container_decl = tree.fullContainerDecl(&buf, container_node) orelse break :blk decl.srcLoc(mod);
 
         var it_index: usize = 0;
         for (container_decl.ast.members) |member_node| {
@@ -2207,7 +2205,7 @@ fn addFieldErrNote(
                 .container_field,
                 => {
                     if (it_index == field_index) {
-                        break :blk decl.nodeOffsetSrcLoc(decl.nodeIndexToRelative(member_node));
+                        break :blk decl.nodeOffsetSrcLoc(decl.nodeIndexToRelative(member_node), mod);
                     }
                     it_index += 1;
                 },
@@ -2228,7 +2226,7 @@ fn errMsg(
 ) error{OutOfMemory}!*Module.ErrorMsg {
     const mod = sema.mod;
     const src_decl = mod.declPtr(block.src_decl);
-    return Module.ErrorMsg.create(sema.gpa, src.toSrcLoc(src_decl), format, args);
+    return Module.ErrorMsg.create(sema.gpa, src.toSrcLoc(src_decl, mod), format, args);
 }
 
 pub fn fail(
@@ -2287,7 +2285,7 @@ fn failWithOwnedErrorMsg(sema: *Sema, err_msg: *Module.ErrorMsg) CompileError {
             if (gop.found_existing) break;
             if (cur_reference_trace < max_references) {
                 const decl = sema.mod.declPtr(ref.referencer);
-                try reference_stack.append(.{ .decl = decl.name, .src_loc = ref.src.toSrcLoc(decl) });
+                try reference_stack.append(.{ .decl = decl.name, .src_loc = ref.src.toSrcLoc(decl, mod) });
             }
             referenced_by = ref.referencer;
         }
@@ -2664,7 +2662,7 @@ pub fn analyzeStructDecl(
         }
     }
 
-    _ = try sema.mod.scanNamespace(&struct_obj.namespace, extra_index, decls_len, new_decl);
+    _ = try sema.mod.scanNamespace(struct_obj.namespace, extra_index, decls_len, new_decl);
 }
 
 fn zirStructDecl(
@@ -2702,15 +2700,12 @@ fn zirStructDecl(
         .status = .none,
         .known_non_opv = undefined,
         .is_tuple = small.is_tuple,
-        .namespace = .{
-            .parent = block.namespace,
+        .namespace = try mod.createNamespace(.{
+            .parent = block.namespace.toOptional(),
             .ty = struct_ty,
-            .file_scope = block.getFileScope(),
-        },
+            .file_scope = block.getFileScope(mod),
+        }),
     };
-    std.log.scoped(.module).debug("create struct {*} owned by {*} ({s})", .{
-        &struct_obj.namespace, new_decl, new_decl.name,
-    });
     try sema.analyzeStructDecl(new_decl, inst, struct_obj);
     try new_decl.finalizeNewArena(&new_decl_arena);
     return sema.analyzeDeclVal(block, src, new_decl_index);
@@ -2887,15 +2882,12 @@ fn zirEnumDecl(
         .tag_ty_inferred = true,
         .fields = .{},
         .values = .{},
-        .namespace = .{
-            .parent = block.namespace,
+        .namespace = try mod.createNamespace(.{
+            .parent = block.namespace.toOptional(),
             .ty = enum_ty,
-            .file_scope = block.getFileScope(),
-        },
+            .file_scope = block.getFileScope(mod),
+        }),
     };
-    std.log.scoped(.module).debug("create enum {*} owned by {*} ({s})", .{
-        &enum_obj.namespace, new_decl, new_decl.name,
-    });
 
     try new_decl.finalizeNewArena(&new_decl_arena);
     const decl_val = try sema.analyzeDeclVal(block, src, new_decl_index);
@@ -2905,7 +2897,7 @@ fn zirEnumDecl(
     const decl_arena_allocator = new_decl.value_arena.?.acquire(gpa, &decl_arena);
     defer new_decl.value_arena.?.release(&decl_arena);
 
-    extra_index = try mod.scanNamespace(&enum_obj.namespace, extra_index, decls_len, new_decl);
+    extra_index = try mod.scanNamespace(enum_obj.namespace, extra_index, decls_len, new_decl);
 
     const body = sema.code.extra[extra_index..][0..body_len];
     extra_index += body.len;
@@ -2944,7 +2936,7 @@ fn zirEnumDecl(
             .parent = null,
             .sema = sema,
             .src_decl = new_decl_index,
-            .namespace = &enum_obj.namespace,
+            .namespace = enum_obj.namespace,
             .wip_capture_scope = wip_captures.scope,
             .instructions = .{},
             .inlining = null,
@@ -3164,17 +3156,14 @@ fn zirUnionDecl(
         .zir_index = inst,
         .layout = small.layout,
         .status = .none,
-        .namespace = .{
-            .parent = block.namespace,
+        .namespace = try mod.createNamespace(.{
+            .parent = block.namespace.toOptional(),
             .ty = union_ty,
-            .file_scope = block.getFileScope(),
-        },
+            .file_scope = block.getFileScope(mod),
+        }),
     };
-    std.log.scoped(.module).debug("create union {*} owned by {*} ({s})", .{
-        &union_obj.namespace, new_decl, new_decl.name,
-    });
 
-    _ = try mod.scanNamespace(&union_obj.namespace, extra_index, decls_len, new_decl);
+    _ = try mod.scanNamespace(union_obj.namespace, extra_index, decls_len, new_decl);
 
     try new_decl.finalizeNewArena(&new_decl_arena);
     return sema.analyzeDeclVal(block, src, new_decl_index);
@@ -3208,37 +3197,37 @@ fn zirOpaqueDecl(
 
     var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
     errdefer new_decl_arena.deinit();
-    const new_decl_arena_allocator = new_decl_arena.allocator();
 
-    const opaque_obj = try new_decl_arena_allocator.create(Module.Opaque);
-    const opaque_ty_payload = try new_decl_arena_allocator.create(Type.Payload.Opaque);
-    opaque_ty_payload.* = .{
-        .base = .{ .tag = .@"opaque" },
-        .data = opaque_obj,
-    };
-    const opaque_ty = Type.initPayload(&opaque_ty_payload.base);
-    const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty);
+    // Because these three things each reference each other, `undefined`
+    // placeholders are used in two places before being set after the opaque
+    // type gains an InternPool index.
+
     const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
         .ty = Type.type,
-        .val = opaque_val,
+        .val = undefined,
     }, small.name_strategy, "opaque", inst);
     const new_decl = mod.declPtr(new_decl_index);
     new_decl.owns_tv = true;
     errdefer mod.abortAnonDecl(new_decl_index);
 
-    opaque_obj.* = .{
-        .owner_decl = new_decl_index,
-        .namespace = .{
-            .parent = block.namespace,
-            .ty = opaque_ty,
-            .file_scope = block.getFileScope(),
-        },
-    };
-    std.log.scoped(.module).debug("create opaque {*} owned by {*} ({s})", .{
-        &opaque_obj.namespace, new_decl, new_decl.name,
+    const new_namespace_index = try mod.createNamespace(.{
+        .parent = block.namespace.toOptional(),
+        .ty = undefined,
+        .file_scope = block.getFileScope(mod),
     });
+    const new_namespace = mod.namespacePtr(new_namespace_index);
+    errdefer @panic("TODO error handling");
+
+    const opaque_ty = try mod.intern_pool.get(gpa, .{ .opaque_type = .{
+        .decl = new_decl_index,
+        .namespace = new_namespace_index,
+    } });
+    errdefer @panic("TODO error handling");
+
+    new_decl.val = opaque_ty.toValue();
+    new_namespace.ty = opaque_ty.toType();
 
-    extra_index = try mod.scanNamespace(&opaque_obj.namespace, extra_index, decls_len, new_decl);
+    extra_index = try mod.scanNamespace(new_namespace_index, extra_index, decls_len, new_decl);
 
     try new_decl.finalizeNewArena(&new_decl_arena);
     return sema.analyzeDeclVal(block, src, new_decl_index);
@@ -4848,7 +4837,7 @@ fn zirValidateDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
             errdefer msg.destroy(sema.gpa);
 
             const src_decl = sema.mod.declPtr(block.src_decl);
-            try sema.explainWhyTypeIsComptime(msg, src.toSrcLoc(src_decl), elem_ty);
+            try sema.explainWhyTypeIsComptime(msg, src.toSrcLoc(src_decl, mod), elem_ty);
             break :msg msg;
         };
         return sema.failWithOwnedErrorMsg(msg);
@@ -4870,7 +4859,7 @@ fn failWithBadMemberAccess(
         .Enum => "enum",
         else => unreachable,
     };
-    if (agg_ty.getOwnerDeclOrNull()) |some| if (sema.mod.declIsRoot(some)) {
+    if (agg_ty.getOwnerDeclOrNull(mod)) |some| if (sema.mod.declIsRoot(some)) {
         return sema.fail(block, field_src, "root struct of file '{}' has no member named '{s}'", .{
             agg_ty.fmt(sema.mod), field_name,
         });
@@ -5632,7 +5621,7 @@ fn analyzeBlockBody(
             try sema.errNote(child_block, runtime_src, msg, "runtime control flow here", .{});
 
             const child_src_decl = mod.declPtr(child_block.src_decl);
-            try sema.explainWhyTypeIsComptime(msg, type_src.toSrcLoc(child_src_decl), resolved_ty);
+            try sema.explainWhyTypeIsComptime(msg, type_src.toSrcLoc(child_src_decl, mod), resolved_ty);
 
             break :msg msg;
         };
@@ -5703,6 +5692,7 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
     const tracy = trace(@src());
     defer tracy.end();
 
+    const mod = sema.mod;
     const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
     const extra = sema.code.extraData(Zir.Inst.Export, inst_data.payload_index).data;
     const src = inst_data.src();
@@ -5711,7 +5701,7 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
     const decl_name = sema.code.nullTerminatedString(extra.decl_name);
     const decl_index = if (extra.namespace != .none) index_blk: {
         const container_ty = try sema.resolveType(block, operand_src, extra.namespace);
-        const container_namespace = container_ty.getNamespace().?;
+        const container_namespace = container_ty.getNamespaceIndex(mod).unwrap().?;
 
         const maybe_index = try sema.lookupInNamespace(block, operand_src, container_namespace, decl_name, false);
         break :index_blk maybe_index orelse
@@ -5725,8 +5715,8 @@ fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
         else => |e| return e,
     };
     {
-        try sema.mod.ensureDeclAnalyzed(decl_index);
-        const exported_decl = sema.mod.declPtr(decl_index);
+        try mod.ensureDeclAnalyzed(decl_index);
+        const exported_decl = mod.declPtr(decl_index);
         if (exported_decl.val.castTag(.function)) |some| {
             return sema.analyzeExport(block, src, options, some.data.owner_decl);
         }
@@ -5789,7 +5779,7 @@ pub fn analyzeExport(
             errdefer msg.destroy(sema.gpa);
 
             const src_decl = sema.mod.declPtr(block.src_decl);
-            try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl), exported_decl.ty, .other);
+            try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), exported_decl.ty, .other);
 
             try sema.addDeclaredHereNote(msg, exported_decl.ty);
             break :msg msg;
@@ -6075,12 +6065,13 @@ fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
 }
 
 fn lookupIdentifier(sema: *Sema, block: *Block, src: LazySrcLoc, name: []const u8) !Decl.Index {
+    const mod = sema.mod;
     var namespace = block.namespace;
     while (true) {
         if (try sema.lookupInNamespace(block, src, namespace, name, false)) |decl_index| {
             return decl_index;
         }
-        namespace = namespace.parent orelse break;
+        namespace = mod.namespacePtr(namespace).parent.unwrap() orelse break;
     }
     unreachable; // AstGen detects use of undeclared identifier errors.
 }
@@ -6091,13 +6082,14 @@ fn lookupInNamespace(
     sema: *Sema,
     block: *Block,
     src: LazySrcLoc,
-    namespace: *Namespace,
+    namespace_index: Namespace.Index,
     ident_name: []const u8,
     observe_usingnamespace: bool,
 ) CompileError!?Decl.Index {
     const mod = sema.mod;
 
-    const namespace_decl_index = namespace.getDeclIndex();
+    const namespace = mod.namespacePtr(namespace_index);
+    const namespace_decl_index = namespace.getDeclIndex(mod);
     const namespace_decl = sema.mod.declPtr(namespace_decl_index);
     if (namespace_decl.analysis == .file_failure) {
         try mod.declareDeclDependency(sema.owner_decl_index, namespace_decl_index);
@@ -6105,7 +6097,7 @@ fn lookupInNamespace(
     }
 
     if (observe_usingnamespace and namespace.usingnamespace_set.count() != 0) {
-        const src_file = block.namespace.file_scope;
+        const src_file = mod.namespacePtr(block.namespace).file_scope;
 
         const gpa = sema.gpa;
         var checked_namespaces: std.AutoArrayHashMapUnmanaged(*Namespace, bool) = .{};
@@ -6124,7 +6116,7 @@ fn lookupInNamespace(
                 // Skip decls which are not marked pub, which are in a different
                 // file than the `a.b`/`@hasDecl` syntax.
                 const decl = mod.declPtr(decl_index);
-                if (decl.is_pub or (src_file == decl.getFileScope() and checked_namespaces.values()[check_i])) {
+                if (decl.is_pub or (src_file == decl.getFileScope(mod) and checked_namespaces.values()[check_i])) {
                     try candidates.append(gpa, decl_index);
                 }
             }
@@ -6135,15 +6127,15 @@ fn lookupInNamespace(
                 if (sub_usingnamespace_decl_index == sema.owner_decl_index) continue;
                 const sub_usingnamespace_decl = mod.declPtr(sub_usingnamespace_decl_index);
                 const sub_is_pub = entry.value_ptr.*;
-                if (!sub_is_pub and src_file != sub_usingnamespace_decl.getFileScope()) {
+                if (!sub_is_pub and src_file != sub_usingnamespace_decl.getFileScope(mod)) {
                     // Skip usingnamespace decls which are not marked pub, which are in
                     // a different file than the `a.b`/`@hasDecl` syntax.
                     continue;
                 }
                 try sema.ensureDeclAnalyzed(sub_usingnamespace_decl_index);
                 const ns_ty = sub_usingnamespace_decl.val.castTag(.ty).?.data;
-                const sub_ns = ns_ty.getNamespace().?;
-                try checked_namespaces.put(gpa, sub_ns, src_file == sub_usingnamespace_decl.getFileScope());
+                const sub_ns = ns_ty.getNamespace(mod).?;
+                try checked_namespaces.put(gpa, sub_ns, src_file == sub_usingnamespace_decl.getFileScope(mod));
             }
         }
 
@@ -6171,7 +6163,7 @@ fn lookupInNamespace(
                     errdefer msg.destroy(gpa);
                     for (candidates.items) |candidate_index| {
                         const candidate = mod.declPtr(candidate_index);
-                        const src_loc = candidate.srcLoc();
+                        const src_loc = candidate.srcLoc(mod);
                         try mod.errNoteNonLazy(src_loc, msg, "declared here", .{});
                     }
                     break :msg msg;
@@ -6532,7 +6524,7 @@ fn checkCallArgumentCount(
         );
         errdefer msg.destroy(sema.gpa);
 
-        if (maybe_decl) |fn_decl| try sema.mod.errNoteNonLazy(fn_decl.srcLoc(), msg, "function declared here", .{});
+        if (maybe_decl) |fn_decl| try sema.mod.errNoteNonLazy(fn_decl.srcLoc(mod), msg, "function declared here", .{});
         break :msg msg;
     };
     return sema.failWithOwnedErrorMsg(msg);
@@ -6669,7 +6661,7 @@ fn analyzeCall(
             );
             errdefer msg.destroy(sema.gpa);
 
-            if (maybe_decl) |fn_decl| try sema.mod.errNoteNonLazy(fn_decl.srcLoc(), msg, "function declared here", .{});
+            if (maybe_decl) |fn_decl| try sema.mod.errNoteNonLazy(fn_decl.srcLoc(mod), msg, "function declared here", .{});
             break :msg msg;
         };
         return sema.failWithOwnedErrorMsg(msg);
@@ -6811,7 +6803,7 @@ fn analyzeCall(
         // than create a child one.
         const parent_zir = sema.code;
         const fn_owner_decl = mod.declPtr(module_fn.owner_decl);
-        sema.code = fn_owner_decl.getFileScope().zir;
+        sema.code = fn_owner_decl.getFileScope(mod).zir;
         defer sema.code = parent_zir;
 
         try mod.declareDeclDependencyType(sema.owner_decl_index, module_fn.owner_decl, .function_body);
@@ -6911,7 +6903,7 @@ fn analyzeCall(
                     try sema.analyzeInlineCallArg(
                         block,
                         &child_block,
-                        Module.argSrc(call_src.node_offset.x, sema.gpa, decl, arg_i, bound_arg_src),
+                        mod.argSrc(call_src.node_offset.x, decl, arg_i, bound_arg_src),
                         inst,
                         new_fn_info,
                         &arg_i,
@@ -7098,7 +7090,7 @@ fn analyzeCall(
                         const decl = sema.mod.declPtr(block.src_decl);
                         _ = try sema.analyzeCallArg(
                             block,
-                            Module.argSrc(call_src.node_offset.x, sema.gpa, decl, i, bound_arg_src),
+                            mod.argSrc(call_src.node_offset.x, decl, i, bound_arg_src),
                             param_ty,
                             uncasted_arg,
                             opts,
@@ -7114,7 +7106,7 @@ fn analyzeCall(
                         _ = try sema.coerceVarArgParam(
                             block,
                             uncasted_arg,
-                            Module.argSrc(call_src.node_offset.x, sema.gpa, decl, i, bound_arg_src),
+                            mod.argSrc(call_src.node_offset.x, decl, i, bound_arg_src),
                         );
                         unreachable;
                     },
@@ -7406,7 +7398,8 @@ fn instantiateGenericCall(
     // can match against `uncasted_args` rather than doing the work below to create a
     // generic Scope only to junk it if it matches an existing instantiation.
     const fn_owner_decl = mod.declPtr(module_fn.owner_decl);
-    const namespace = fn_owner_decl.src_namespace;
+    const namespace_index = fn_owner_decl.src_namespace;
+    const namespace = mod.namespacePtr(namespace_index);
     const fn_zir = namespace.file_scope.zir;
     const fn_info = fn_zir.getFnInfo(module_fn.zir_body_inst);
     const zir_tags = fn_zir.instructions.items(.tag);
@@ -7456,7 +7449,7 @@ fn instantiateGenericCall(
                 const arg_val = sema.analyzeGenericCallArgVal(block, .unneeded, uncasted_args[i]) catch |err| switch (err) {
                     error.NeededSourceLocation => {
                         const decl = sema.mod.declPtr(block.src_decl);
-                        const arg_src = Module.argSrc(call_src.node_offset.x, sema.gpa, decl, i, bound_arg_src);
+                        const arg_src = mod.argSrc(call_src.node_offset.x, decl, i, bound_arg_src);
                         _ = try sema.analyzeGenericCallArgVal(block, arg_src, uncasted_args[i]);
                         unreachable;
                     },
@@ -7519,9 +7512,9 @@ fn instantiateGenericCall(
         try namespace.anon_decls.ensureUnusedCapacity(gpa, 1);
 
         // Create a Decl for the new function.
-        const src_decl_index = namespace.getDeclIndex();
+        const src_decl_index = namespace.getDeclIndex(mod);
         const src_decl = mod.declPtr(src_decl_index);
-        const new_decl_index = try mod.allocateNewDecl(namespace, fn_owner_decl.src_node, src_decl.src_scope);
+        const new_decl_index = try mod.allocateNewDecl(namespace_index, fn_owner_decl.src_node, src_decl.src_scope);
         const new_decl = mod.declPtr(new_decl_index);
         // TODO better names for generic function instantiations
         const decl_name = try std.fmt.allocPrintZ(gpa, "{s}__anon_{d}", .{
@@ -7559,7 +7552,7 @@ fn instantiateGenericCall(
             uncasted_args,
             module_fn,
             new_module_func,
-            namespace,
+            namespace_index,
             func_ty_info,
             call_src,
             bound_arg_src,
@@ -7631,7 +7624,7 @@ fn instantiateGenericCall(
                     const decl = sema.mod.declPtr(block.src_decl);
                     _ = try sema.analyzeGenericCallArg(
                         block,
-                        Module.argSrc(call_src.node_offset.x, sema.gpa, decl, total_i, bound_arg_src),
+                        mod.argSrc(call_src.node_offset.x, decl, total_i, bound_arg_src),
                         uncasted_args[total_i],
                         comptime_args[total_i],
                         runtime_args,
@@ -7692,7 +7685,7 @@ fn resolveGenericInstantiationType(
     uncasted_args: []const Air.Inst.Ref,
     module_fn: *Module.Fn,
     new_module_func: *Module.Fn,
-    namespace: *Namespace,
+    namespace: Namespace.Index,
     func_ty_info: Type.Payload.Function.Data,
     call_src: LazySrcLoc,
     bound_arg_src: ?LazySrcLoc,
@@ -7779,7 +7772,7 @@ fn resolveGenericInstantiationType(
                 const arg_val = sema.resolveConstValue(block, .unneeded, arg, "") catch |err| switch (err) {
                     error.NeededSourceLocation => {
                         const decl = sema.mod.declPtr(block.src_decl);
-                        const arg_src = Module.argSrc(call_src.node_offset.x, sema.gpa, decl, arg_i, bound_arg_src);
+                        const arg_src = mod.argSrc(call_src.node_offset.x, decl, arg_i, bound_arg_src);
                         _ = try sema.resolveConstValue(block, arg_src, arg, "argument to parameter with comptime-only type must be comptime-known");
                         unreachable;
                     },
@@ -8987,7 +8980,7 @@ fn funcCommon(
                     const decl = sema.mod.declPtr(block.src_decl);
                     try sema.analyzeParameter(
                         block,
-                        Module.paramSrc(src_node_offset, sema.gpa, decl, i),
+                        Module.paramSrc(src_node_offset, mod, decl, i),
                         param,
                         comptime_params,
                         i,
@@ -9050,7 +9043,7 @@ fn funcCommon(
                 errdefer msg.destroy(sema.gpa);
 
                 const src_decl = sema.mod.declPtr(block.src_decl);
-                try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src.toSrcLoc(src_decl), return_type, .ret_ty);
+                try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src.toSrcLoc(src_decl, mod), return_type, .ret_ty);
 
                 try sema.addDeclaredHereNote(msg, return_type);
                 break :msg msg;
@@ -9070,7 +9063,7 @@ fn funcCommon(
                 "function with comptime-only return type '{}' requires all parameters to be comptime",
                 .{return_type.fmt(sema.mod)},
             );
-            try sema.explainWhyTypeIsComptime(msg, ret_ty_src.toSrcLoc(sema.owner_decl), return_type);
+            try sema.explainWhyTypeIsComptime(msg, ret_ty_src.toSrcLoc(sema.owner_decl, mod), return_type);
 
             const tags = sema.code.instructions.items(.tag);
             const data = sema.code.instructions.items(.data);
@@ -9278,7 +9271,7 @@ fn analyzeParameter(
             errdefer msg.destroy(sema.gpa);
 
             const src_decl = mod.declPtr(block.src_decl);
-            try sema.explainWhyTypeIsNotExtern(msg, param_src.toSrcLoc(src_decl), param.ty, .param_ty);
+            try sema.explainWhyTypeIsNotExtern(msg, param_src.toSrcLoc(src_decl, mod), param.ty, .param_ty);
 
             try sema.addDeclaredHereNote(msg, param.ty);
             break :msg msg;
@@ -9293,7 +9286,7 @@ fn analyzeParameter(
             errdefer msg.destroy(sema.gpa);
 
             const src_decl = mod.declPtr(block.src_decl);
-            try sema.explainWhyTypeIsComptime(msg, param_src.toSrcLoc(src_decl), param.ty);
+            try sema.explainWhyTypeIsComptime(msg, param_src.toSrcLoc(src_decl, mod), param.ty);
 
             try sema.addDeclaredHereNote(msg, param.ty);
             break :msg msg;
@@ -10233,15 +10226,15 @@ fn zirSwitchCapture(
                 if (!field.ty.eql(first_field.ty, sema.mod)) {
                     const msg = msg: {
                         const raw_capture_src = Module.SwitchProngSrc{ .multi_capture = capture_info.prong_index };
-                        const capture_src = raw_capture_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), switch_info.src_node, .first);
+                        const capture_src = raw_capture_src.resolve(mod, sema.mod.declPtr(block.src_decl), switch_info.src_node, .first);
 
                         const msg = try sema.errMsg(block, capture_src, "capture group with incompatible types", .{});
                         errdefer msg.destroy(sema.gpa);
 
                         const raw_first_item_src = Module.SwitchProngSrc{ .multi = .{ .prong = capture_info.prong_index, .item = 0 } };
-                        const first_item_src = raw_first_item_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), switch_info.src_node, .first);
+                        const first_item_src = raw_first_item_src.resolve(mod, sema.mod.declPtr(block.src_decl), switch_info.src_node, .first);
                         const raw_item_src = Module.SwitchProngSrc{ .multi = .{ .prong = capture_info.prong_index, .item = 1 + @intCast(u32, i) } };
-                        const item_src = raw_item_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), switch_info.src_node, .first);
+                        const item_src = raw_item_src.resolve(mod, sema.mod.declPtr(block.src_decl), switch_info.src_node, .first);
                         try sema.errNote(block, first_item_src, msg, "type '{}' here", .{first_field.ty.fmt(sema.mod)});
                         try sema.errNote(block, item_src, msg, "type '{}' here", .{field.ty.fmt(sema.mod)});
                         break :msg msg;
@@ -11265,7 +11258,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                         error.NeededSourceLocation => {
                             const case_src = Module.SwitchProngSrc{ .range = .{ .prong = multi_i, .item = range_i } };
                             const decl = mod.declPtr(case_block.src_decl);
-                            try sema.emitBackwardBranch(block, case_src.resolve(sema.gpa, decl, src_node_offset, .none));
+                            try sema.emitBackwardBranch(block, case_src.resolve(mod, decl, src_node_offset, .none));
                             unreachable;
                         },
                         else => return err,
@@ -11301,7 +11294,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
                     error.NeededSourceLocation => {
                         const case_src = Module.SwitchProngSrc{ .multi = .{ .prong = multi_i, .item = @intCast(u32, item_i) } };
                         const decl = mod.declPtr(case_block.src_decl);
-                        try sema.emitBackwardBranch(block, case_src.resolve(sema.gpa, decl, src_node_offset, .none));
+                        try sema.emitBackwardBranch(block, case_src.resolve(mod, decl, src_node_offset, .none));
                         unreachable;
                     },
                     else => return err,
@@ -11724,6 +11717,7 @@ fn resolveSwitchItemVal(
     switch_prong_src: Module.SwitchProngSrc,
     range_expand: Module.SwitchProngSrc.RangeExpand,
 ) CompileError!TypedValue {
+    const mod = sema.mod;
     const item = try sema.resolveInst(item_ref);
     const item_ty = sema.typeOf(item);
     // Constructing a LazySrcLoc is costly because we only have the switch AST node.
@@ -11734,7 +11728,7 @@ fn resolveSwitchItemVal(
         return TypedValue{ .ty = item_ty, .val = val };
     } else |err| switch (err) {
         error.NeededSourceLocation => {
-            const src = switch_prong_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), switch_node_offset, range_expand);
+            const src = switch_prong_src.resolve(mod, sema.mod.declPtr(block.src_decl), switch_node_offset, range_expand);
             _ = try sema.resolveConstValue(block, src, item, "switch prong values must be comptime-known");
             unreachable;
         },
@@ -11752,10 +11746,11 @@ fn validateSwitchRange(
     src_node_offset: i32,
     switch_prong_src: Module.SwitchProngSrc,
 ) CompileError!void {
+    const mod = sema.mod;
     const first_val = (try sema.resolveSwitchItemVal(block, first_ref, src_node_offset, switch_prong_src, .first)).val;
     const last_val = (try sema.resolveSwitchItemVal(block, last_ref, src_node_offset, switch_prong_src, .last)).val;
-    if (first_val.compareScalar(.gt, last_val, operand_ty, sema.mod)) {
-        const src = switch_prong_src.resolve(sema.gpa, sema.mod.declPtr(block.src_decl), src_node_offset, .first);
+    if (first_val.compareScalar(.gt, last_val, operand_ty, mod)) {
+        const src = switch_prong_src.resolve(mod, mod.declPtr(block.src_decl), src_node_offset, .first);
         return sema.fail(block, src, "range start value is greater than the end value", .{});
     }
     const maybe_prev_src = try range_set.add(first_val, last_val, operand_ty, switch_prong_src);
@@ -11821,10 +11816,10 @@ fn validateSwitchDupe(
     src_node_offset: i32,
 ) CompileError!void {
     const prev_prong_src = maybe_prev_src orelse return;
-    const gpa = sema.gpa;
+    const mod = sema.mod;
     const block_src_decl = sema.mod.declPtr(block.src_decl);
-    const src = switch_prong_src.resolve(gpa, block_src_decl, src_node_offset, .none);
-    const prev_src = prev_prong_src.resolve(gpa, block_src_decl, src_node_offset, .none);
+    const src = switch_prong_src.resolve(mod, block_src_decl, src_node_offset, .none);
+    const prev_src = prev_prong_src.resolve(mod, block_src_decl, src_node_offset, .none);
     const msg = msg: {
         const msg = try sema.errMsg(
             block,
@@ -11863,7 +11858,7 @@ fn validateSwitchItemBool(
     }
     if (true_count.* + false_count.* > 2) {
         const block_src_decl = sema.mod.declPtr(block.src_decl);
-        const src = switch_prong_src.resolve(sema.gpa, block_src_decl, src_node_offset, .none);
+        const src = switch_prong_src.resolve(mod, block_src_decl, src_node_offset, .none);
         return sema.fail(block, src, "duplicate switch value", .{});
     }
 }
@@ -12068,6 +12063,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 extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
     const src = inst_data.src();
@@ -12078,10 +12074,11 @@ fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
 
     try sema.checkNamespaceType(block, lhs_src, container_type);
 
-    const namespace = container_type.getNamespace() orelse return Air.Inst.Ref.bool_false;
+    const namespace = container_type.getNamespaceIndex(mod).unwrap() orelse
+        return Air.Inst.Ref.bool_false;
     if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| {
-        const decl = sema.mod.declPtr(decl_index);
-        if (decl.is_pub or decl.getFileScope() == block.getFileScope()) {
+        const decl = mod.declPtr(decl_index);
+        if (decl.is_pub or decl.getFileScope(mod) == block.getFileScope(mod)) {
             return Air.Inst.Ref.bool_true;
         }
     }
@@ -12097,12 +12094,12 @@ fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.
     const operand_src = inst_data.src();
     const operand = inst_data.get(sema.code);
 
-    const result = mod.importFile(block.getFileScope(), operand) catch |err| switch (err) {
+    const result = mod.importFile(block.getFileScope(mod), operand) catch |err| switch (err) {
         error.ImportOutsidePkgPath => {
             return sema.fail(block, operand_src, "import of file outside package path: '{s}'", .{operand});
         },
         error.PackageNotFound => {
-            const name = try block.getFileScope().pkg.getName(sema.gpa, mod.*);
+            const name = try block.getFileScope(mod).pkg.getName(sema.gpa, mod.*);
             defer sema.gpa.free(name);
             return sema.fail(block, operand_src, "no package named '{s}' available within package '{s}'", .{ operand, name });
         },
@@ -12128,7 +12125,7 @@ fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
     const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
     const name = try sema.resolveConstString(block, operand_src, inst_data.operand, "file path name must be comptime-known");
 
-    const embed_file = mod.embedFile(block.getFileScope(), name) catch |err| switch (err) {
+    const embed_file = mod.embedFile(block.getFileScope(mod), name) catch |err| switch (err) {
         error.ImportOutsidePkgPath => {
             return sema.fail(block, operand_src, "embed of file outside package path: '{s}'", .{name});
         },
@@ -15666,7 +15663,8 @@ fn zirThis(
     block: *Block,
     extended: Zir.Inst.Extended.InstData,
 ) CompileError!Air.Inst.Ref {
-    const this_decl_index = block.namespace.getDeclIndex();
+    const mod = sema.mod;
+    const this_decl_index = mod.namespaceDeclIndex(block.namespace);
     const src = LazySrcLoc.nodeOffset(@bitCast(i32, extended.operand));
     return sema.analyzeDeclVal(block, src, this_decl_index);
 }
@@ -15698,9 +15696,10 @@ fn zirClosureGet(
     block: *Block,
     inst: Zir.Inst.Index,
 ) CompileError!Air.Inst.Ref {
+    const mod = sema.mod;
     // TODO CLOSURE: Test this with inline functions
     const inst_data = sema.code.instructions.items(.data)[inst].inst_node;
-    var scope: *CaptureScope = sema.mod.declPtr(block.src_decl).src_scope.?;
+    var scope: *CaptureScope = mod.declPtr(block.src_decl).src_scope.?;
     // Note: The target closure must be in this scope list.
     // If it's not here, the zir is invalid, or the list is broken.
     const tv = while (true) {
@@ -15725,8 +15724,8 @@ fn zirClosureGet(
     if (tv.val.ip_index == .unreachable_value and !block.is_typeof and sema.func == null) {
         const msg = msg: {
             const name = name: {
-                const file = sema.owner_decl.getFileScope();
-                const tree = file.getTree(sema.mod.gpa) catch |err| {
+                const file = sema.owner_decl.getFileScope(mod);
+                const tree = file.getTree(mod.gpa) catch |err| {
                     // In this case we emit a warning + a less precise source location.
                     log.warn("unable to load {s}: {s}", .{
                         file.sub_file_path, @errorName(err),
@@ -15753,8 +15752,8 @@ fn zirClosureGet(
     if (tv.val.ip_index == .unreachable_value and !block.is_typeof and !block.is_comptime and sema.func != null) {
         const msg = msg: {
             const name = name: {
-                const file = sema.owner_decl.getFileScope();
-                const tree = file.getTree(sema.mod.gpa) catch |err| {
+                const file = sema.owner_decl.getFileScope(mod);
+                const tree = file.getTree(mod.gpa) catch |err| {
                     // In this case we emit a warning + a less precise source location.
                     log.warn("unable to load {s}: {s}", .{
                         file.sub_file_path, @errorName(err),
@@ -15825,7 +15824,7 @@ fn zirBuiltinSrc(
     const extra = sema.code.extraData(Zir.Inst.Src, extended.operand).data;
     const src = LazySrcLoc.nodeOffset(extra.node);
     const func = sema.func orelse return sema.fail(block, src, "@src outside function", .{});
-    const fn_owner_decl = sema.mod.declPtr(func.owner_decl);
+    const fn_owner_decl = mod.declPtr(func.owner_decl);
 
     const func_name_val = blk: {
         var anon_decl = try block.startAnonDecl();
@@ -15844,7 +15843,7 @@ fn zirBuiltinSrc(
         var anon_decl = try block.startAnonDecl();
         defer anon_decl.deinit();
         // The compiler must not call realpath anywhere.
-        const name = try fn_owner_decl.getFileScope().fullPathZ(anon_decl.arena());
+        const name = try fn_owner_decl.getFileScope(mod).fullPathZ(anon_decl.arena());
         const new_decl = try anon_decl.finish(
             try Type.array(anon_decl.arena(), name.len, try mod.intValue(Type.u8, 0), Type.u8, mod),
             try Value.Tag.bytes.create(anon_decl.arena(), name[0 .. name.len + 1]),
@@ -15980,22 +15979,22 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 const fn_info_decl_index = (try sema.namespaceLookup(
                     block,
                     src,
-                    type_info_ty.getNamespace().?,
+                    type_info_ty.getNamespaceIndex(mod).unwrap().?,
                     "Fn",
                 )).?;
-                try sema.mod.declareDeclDependency(sema.owner_decl_index, fn_info_decl_index);
+                try mod.declareDeclDependency(sema.owner_decl_index, fn_info_decl_index);
                 try sema.ensureDeclAnalyzed(fn_info_decl_index);
-                const fn_info_decl = sema.mod.declPtr(fn_info_decl_index);
+                const fn_info_decl = mod.declPtr(fn_info_decl_index);
                 const fn_ty = fn_info_decl.val.toType();
                 const param_info_decl_index = (try sema.namespaceLookup(
                     block,
                     src,
-                    fn_ty.getNamespace().?,
+                    fn_ty.getNamespaceIndex(mod).unwrap().?,
                     "Param",
                 )).?;
-                try sema.mod.declareDeclDependency(sema.owner_decl_index, param_info_decl_index);
+                try mod.declareDeclDependency(sema.owner_decl_index, param_info_decl_index);
                 try sema.ensureDeclAnalyzed(param_info_decl_index);
-                const param_info_decl = sema.mod.declPtr(param_info_decl_index);
+                const param_info_decl = mod.declPtr(param_info_decl_index);
                 const param_ty = param_info_decl.val.toType();
                 const new_decl = try params_anon_decl.finish(
                     try Type.Tag.array.create(params_anon_decl.arena(), .{
@@ -16169,12 +16168,12 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 const set_field_ty_decl_index = (try sema.namespaceLookup(
                     block,
                     src,
-                    type_info_ty.getNamespace().?,
+                    type_info_ty.getNamespaceIndex(mod).unwrap().?,
                     "Error",
                 )).?;
-                try sema.mod.declareDeclDependency(sema.owner_decl_index, set_field_ty_decl_index);
+                try mod.declareDeclDependency(sema.owner_decl_index, set_field_ty_decl_index);
                 try sema.ensureDeclAnalyzed(set_field_ty_decl_index);
-                const set_field_ty_decl = sema.mod.declPtr(set_field_ty_decl_index);
+                const set_field_ty_decl = mod.declPtr(set_field_ty_decl_index);
                 break :t try set_field_ty_decl.val.toType().copy(fields_anon_decl.arena());
             };
 
@@ -16277,12 +16276,12 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 const enum_field_ty_decl_index = (try sema.namespaceLookup(
                     block,
                     src,
-                    type_info_ty.getNamespace().?,
+                    type_info_ty.getNamespaceIndex(mod).unwrap().?,
                     "EnumField",
                 )).?;
-                try sema.mod.declareDeclDependency(sema.owner_decl_index, enum_field_ty_decl_index);
+                try mod.declareDeclDependency(sema.owner_decl_index, enum_field_ty_decl_index);
                 try sema.ensureDeclAnalyzed(enum_field_ty_decl_index);
-                const enum_field_ty_decl = sema.mod.declPtr(enum_field_ty_decl_index);
+                const enum_field_ty_decl = mod.declPtr(enum_field_ty_decl_index);
                 break :t try enum_field_ty_decl.val.toType().copy(fields_anon_decl.arena());
             };
 
@@ -16336,7 +16335,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 break :v try Value.Tag.decl_ref.create(sema.arena, new_decl);
             };
 
-            const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ty.getNamespace());
+            const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, ty.getNamespace(mod));
 
             const field_values = try sema.arena.create([4]Value);
             field_values.* = .{
@@ -16368,12 +16367,12 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 const union_field_ty_decl_index = (try sema.namespaceLookup(
                     block,
                     src,
-                    type_info_ty.getNamespace().?,
+                    type_info_ty.getNamespaceIndex(mod).unwrap().?,
                     "UnionField",
                 )).?;
-                try sema.mod.declareDeclDependency(sema.owner_decl_index, union_field_ty_decl_index);
+                try mod.declareDeclDependency(sema.owner_decl_index, union_field_ty_decl_index);
                 try sema.ensureDeclAnalyzed(union_field_ty_decl_index);
-                const union_field_ty_decl = sema.mod.declPtr(union_field_ty_decl_index);
+                const union_field_ty_decl = mod.declPtr(union_field_ty_decl_index);
                 break :t try union_field_ty_decl.val.toType().copy(fields_anon_decl.arena());
             };
 
@@ -16434,7 +16433,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 });
             };
 
-            const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, union_ty.getNamespace());
+            const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, union_ty.getNamespace(mod));
 
             const enum_tag_ty_val = if (union_ty.unionTagType()) |tag_ty| v: {
                 const ty_val = try Value.Tag.ty.create(sema.arena, tag_ty);
@@ -16475,12 +16474,12 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 const struct_field_ty_decl_index = (try sema.namespaceLookup(
                     block,
                     src,
-                    type_info_ty.getNamespace().?,
+                    type_info_ty.getNamespaceIndex(mod).unwrap().?,
                     "StructField",
                 )).?;
-                try sema.mod.declareDeclDependency(sema.owner_decl_index, struct_field_ty_decl_index);
+                try mod.declareDeclDependency(sema.owner_decl_index, struct_field_ty_decl_index);
                 try sema.ensureDeclAnalyzed(struct_field_ty_decl_index);
-                const struct_field_ty_decl = sema.mod.declPtr(struct_field_ty_decl_index);
+                const struct_field_ty_decl = mod.declPtr(struct_field_ty_decl_index);
                 break :t try struct_field_ty_decl.val.toType().copy(fields_anon_decl.arena());
             };
             const struct_ty = try sema.resolveTypeFields(ty);
@@ -16597,7 +16596,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
                 });
             };
 
-            const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, struct_ty.getNamespace());
+            const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, struct_ty.getNamespace(mod));
 
             const backing_integer_val = blk: {
                 if (layout == .Packed) {
@@ -16640,7 +16639,7 @@ fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
             // TODO: look into memoizing this result.
 
             const opaque_ty = try sema.resolveTypeFields(ty);
-            const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, opaque_ty.getNamespace());
+            const decls_val = try sema.typeInfoDecls(block, src, type_info_ty, opaque_ty.getNamespace(mod));
 
             const field_values = try sema.arena.create([1]Value);
             field_values.* = .{
@@ -16676,7 +16675,7 @@ fn typeInfoDecls(
         const declaration_ty_decl_index = (try sema.namespaceLookup(
             block,
             src,
-            type_info_ty.getNamespace().?,
+            type_info_ty.getNamespaceIndex(mod).unwrap().?,
             "Declaration",
         )).?;
         try mod.declareDeclDependency(sema.owner_decl_index, declaration_ty_decl_index);
@@ -16730,7 +16729,7 @@ fn typeInfoNamespaceDecls(
         if (decl.kind == .@"usingnamespace") {
             if (decl.analysis == .in_progress) continue;
             try mod.ensureDeclAnalyzed(decl_index);
-            const new_ns = decl.val.toType().getNamespace().?;
+            const new_ns = decl.val.toType().getNamespace(mod).?;
             try sema.typeInfoNamespaceDecls(block, decls_anon_decl, new_ns, decl_vals, seen_namespaces);
             continue;
         }
@@ -17750,7 +17749,7 @@ fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
                 errdefer msg.destroy(sema.gpa);
 
                 const src_decl = sema.mod.declPtr(block.src_decl);
-                try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src.toSrcLoc(src_decl), elem_ty, .other);
+                try sema.explainWhyTypeIsNotExtern(msg, elem_ty_src.toSrcLoc(src_decl, mod), elem_ty, .other);
 
                 try sema.addDeclaredHereNote(msg, elem_ty);
                 break :msg msg;
@@ -18006,6 +18005,7 @@ fn finishStructInit(
     struct_ty: Type,
     is_ref: bool,
 ) CompileError!Air.Inst.Ref {
+    const mod = sema.mod;
     const gpa = sema.gpa;
 
     var root_msg: ?*Module.ErrorMsg = null;
@@ -18118,8 +18118,8 @@ fn finishStructInit(
 
     sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) {
         error.NeededSourceLocation => {
-            const decl = sema.mod.declPtr(block.src_decl);
-            const field_src = Module.initSrc(dest_src.node_offset.x, sema.gpa, decl, runtime_index);
+            const decl = mod.declPtr(block.src_decl);
+            const field_src = mod.initSrc(dest_src.node_offset.x, decl, runtime_index);
             try sema.requireRuntimeBlock(block, dest_src, field_src);
             unreachable;
         },
@@ -18158,11 +18158,11 @@ fn zirStructInitAnon(
             if (gop.found_existing) {
                 const msg = msg: {
                     const decl = sema.mod.declPtr(block.src_decl);
-                    const field_src = Module.initSrc(src.node_offset.x, sema.gpa, decl, i);
+                    const field_src = mod.initSrc(src.node_offset.x, decl, i);
                     const msg = try sema.errMsg(block, field_src, "duplicate field", .{});
                     errdefer msg.destroy(sema.gpa);
 
-                    const prev_source = Module.initSrc(src.node_offset.x, sema.gpa, decl, gop.value_ptr.*);
+                    const prev_source = mod.initSrc(src.node_offset.x, decl, gop.value_ptr.*);
                     try sema.errNote(block, prev_source, msg, "other field here", .{});
                     break :msg msg;
                 };
@@ -18175,7 +18175,7 @@ fn zirStructInitAnon(
             if (types[i].zigTypeTag(mod) == .Opaque) {
                 const msg = msg: {
                     const decl = sema.mod.declPtr(block.src_decl);
-                    const field_src = Module.initSrc(src.node_offset.x, sema.gpa, decl, i);
+                    const field_src = mod.initSrc(src.node_offset.x, decl, i);
                     const msg = try sema.errMsg(block, field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{});
                     errdefer msg.destroy(sema.gpa);
 
@@ -18208,7 +18208,7 @@ fn zirStructInitAnon(
     sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) {
         error.NeededSourceLocation => {
             const decl = sema.mod.declPtr(block.src_decl);
-            const field_src = Module.initSrc(src.node_offset.x, sema.gpa, decl, runtime_index);
+            const field_src = mod.initSrc(src.node_offset.x, decl, runtime_index);
             try sema.requireRuntimeBlock(block, src, field_src);
             unreachable;
         },
@@ -18283,7 +18283,7 @@ fn zirArrayInit(
         resolved_args[i] = sema.coerce(block, elem_ty, resolved_arg, .unneeded) catch |err| switch (err) {
             error.NeededSourceLocation => {
                 const decl = sema.mod.declPtr(block.src_decl);
-                const elem_src = Module.initSrc(src.node_offset.x, sema.gpa, decl, i);
+                const elem_src = mod.initSrc(src.node_offset.x, decl, i);
                 _ = try sema.coerce(block, elem_ty, resolved_arg, elem_src);
                 unreachable;
             },
@@ -18315,7 +18315,7 @@ fn zirArrayInit(
     sema.requireRuntimeBlock(block, .unneeded, null) catch |err| switch (err) {
         error.NeededSourceLocation => {
             const decl = sema.mod.declPtr(block.src_decl);
-            const elem_src = Module.initSrc(src.node_offset.x, sema.gpa, decl, runtime_index);
+            const elem_src = mod.initSrc(src.node_offset.x, decl, runtime_index);
             try sema.requireRuntimeBlock(block, src, elem_src);
             unreachable;
         },
@@ -18724,7 +18724,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
             enum_ty.fmt(mod),
         });
     }
-    const enum_decl_index = enum_ty.getOwnerDecl();
+    const enum_decl_index = enum_ty.getOwnerDecl(mod);
     const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src);
     if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| {
         const field_index = enum_ty.enumTagFieldIndex(val, mod) orelse {
@@ -18734,7 +18734,7 @@ fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air
                     val.fmtValue(enum_ty, sema.mod), enum_decl.name,
                 });
                 errdefer msg.destroy(sema.gpa);
-                try mod.errNoteNonLazy(enum_decl.srcLoc(), msg, "declared here", .{});
+                try mod.errNoteNonLazy(enum_decl.srcLoc(mod), msg, "declared here", .{});
                 break :msg msg;
             };
             return sema.failWithOwnedErrorMsg(msg);
@@ -18760,6 +18760,7 @@ fn zirReify(
     inst: Zir.Inst.Index,
 ) CompileError!Air.Inst.Ref {
     const mod = sema.mod;
+    const gpa = sema.gpa;
     const name_strategy = @intToEnum(Zir.Inst.NameStrategy, extended.small);
     const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data;
     const src = LazySrcLoc.nodeOffset(extra.node);
@@ -18887,10 +18888,10 @@ fn zirReify(
                 if (!try sema.validateExternType(elem_ty, .other)) {
                     const msg = msg: {
                         const msg = try sema.errMsg(block, src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(mod)});
-                        errdefer msg.destroy(sema.gpa);
+                        errdefer msg.destroy(gpa);
 
                         const src_decl = mod.declPtr(block.src_decl);
-                        try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl), elem_ty, .other);
+                        try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), elem_ty, .other);
 
                         try sema.addDeclaredHereNote(msg, elem_ty);
                         break :msg msg;
@@ -19043,7 +19044,6 @@ fn zirReify(
                 return sema.fail(block, src, "reified enums must have no decls", .{});
             }
 
-            const gpa = sema.gpa;
             var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
             errdefer new_decl_arena.deinit();
             const new_decl_arena_allocator = new_decl_arena.allocator();
@@ -19076,11 +19076,11 @@ fn zirReify(
                 .tag_ty_inferred = false,
                 .fields = .{},
                 .values = .{},
-                .namespace = .{
-                    .parent = block.namespace,
+                .namespace = try mod.createNamespace(.{
+                    .parent = block.namespace.toOptional(),
                     .ty = enum_ty,
-                    .file_scope = block.getFileScope(),
-                },
+                    .file_scope = block.getFileScope(mod),
+                }),
             };
 
             // Enum tag type
@@ -19164,34 +19164,37 @@ fn zirReify(
                 return sema.fail(block, src, "reified opaque must have no decls", .{});
             }
 
-            var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
+            var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
             errdefer new_decl_arena.deinit();
-            const new_decl_arena_allocator = new_decl_arena.allocator();
 
-            const opaque_obj = try new_decl_arena_allocator.create(Module.Opaque);
-            const opaque_ty_payload = try new_decl_arena_allocator.create(Type.Payload.Opaque);
-            opaque_ty_payload.* = .{
-                .base = .{ .tag = .@"opaque" },
-                .data = opaque_obj,
-            };
-            const opaque_ty = Type.initPayload(&opaque_ty_payload.base);
-            const opaque_val = try Value.Tag.ty.create(new_decl_arena_allocator, opaque_ty);
+            // Because these three things each reference each other,
+            // `undefined` placeholders are used in two places before being set
+            // after the opaque type gains an InternPool index.
+
             const new_decl_index = try sema.createAnonymousDeclTypeNamed(block, src, .{
                 .ty = Type.type,
-                .val = opaque_val,
+                .val = undefined,
             }, name_strategy, "opaque", inst);
             const new_decl = mod.declPtr(new_decl_index);
             new_decl.owns_tv = true;
             errdefer mod.abortAnonDecl(new_decl_index);
 
-            opaque_obj.* = .{
-                .owner_decl = new_decl_index,
-                .namespace = .{
-                    .parent = block.namespace,
-                    .ty = opaque_ty,
-                    .file_scope = block.getFileScope(),
-                },
-            };
+            const new_namespace_index = try mod.createNamespace(.{
+                .parent = block.namespace.toOptional(),
+                .ty = undefined,
+                .file_scope = block.getFileScope(mod),
+            });
+            const new_namespace = mod.namespacePtr(new_namespace_index);
+            errdefer @panic("TODO error handling");
+
+            const opaque_ty = try mod.intern_pool.get(gpa, .{ .opaque_type = .{
+                .decl = new_decl_index,
+                .namespace = new_namespace_index,
+            } });
+            errdefer @panic("TODO error handling");
+
+            new_decl.val = opaque_ty.toValue();
+            new_namespace.ty = opaque_ty.toType();
 
             try new_decl.finalizeNewArena(&new_decl_arena);
             return sema.analyzeDeclVal(block, src, new_decl_index);
@@ -19214,7 +19217,7 @@ fn zirReify(
             }
             const layout = layout_val.toEnum(std.builtin.Type.ContainerLayout);
 
-            var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa);
+            var new_decl_arena = std.heap.ArenaAllocator.init(gpa);
             errdefer new_decl_arena.deinit();
             const new_decl_arena_allocator = new_decl_arena.allocator();
 
@@ -19248,11 +19251,11 @@ fn zirReify(
                 .zir_index = inst,
                 .layout = layout,
                 .status = .have_field_types,
-                .namespace = .{
-                    .parent = block.namespace,
+                .namespace = try mod.createNamespace(.{
+                    .parent = block.namespace.toOptional(),
                     .ty = union_ty,
-                    .file_scope = block.getFileScope(),
-                },
+                    .file_scope = block.getFileScope(mod),
+                }),
             };
 
             // Tag type
@@ -19301,7 +19304,7 @@ fn zirReify(
                     if (!enum_has_field) {
                         const msg = msg: {
                             const msg = try sema.errMsg(block, src, "no field named '{s}' in enum '{}'", .{ field_name, union_obj.tag_ty.fmt(mod) });
-                            errdefer msg.destroy(sema.gpa);
+                            errdefer msg.destroy(gpa);
                             try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
                             break :msg msg;
                         };
@@ -19324,7 +19327,7 @@ fn zirReify(
                 if (field_ty.zigTypeTag(mod) == .Opaque) {
                     const msg = msg: {
                         const msg = try sema.errMsg(block, src, "opaque types have unknown size and therefore cannot be directly embedded in unions", .{});
-                        errdefer msg.destroy(sema.gpa);
+                        errdefer msg.destroy(gpa);
 
                         try sema.addDeclaredHereNote(msg, field_ty);
                         break :msg msg;
@@ -19334,10 +19337,10 @@ fn zirReify(
                 if (union_obj.layout == .Extern and !try sema.validateExternType(field_ty, .union_field)) {
                     const msg = msg: {
                         const msg = try sema.errMsg(block, src, "extern unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
-                        errdefer msg.destroy(sema.gpa);
+                        errdefer msg.destroy(gpa);
 
                         const src_decl = mod.declPtr(block.src_decl);
-                        try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl), field_ty, .union_field);
+                        try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .union_field);
 
                         try sema.addDeclaredHereNote(msg, field_ty);
                         break :msg msg;
@@ -19346,10 +19349,10 @@ fn zirReify(
                 } else if (union_obj.layout == .Packed and !(validatePackedType(field_ty, mod))) {
                     const msg = msg: {
                         const msg = try sema.errMsg(block, src, "packed unions cannot contain fields of type '{}'", .{field_ty.fmt(mod)});
-                        errdefer msg.destroy(sema.gpa);
+                        errdefer msg.destroy(gpa);
 
                         const src_decl = mod.declPtr(block.src_decl);
-                        try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl), field_ty);
+                        try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty);
 
                         try sema.addDeclaredHereNote(msg, field_ty);
                         break :msg msg;
@@ -19362,7 +19365,7 @@ fn zirReify(
                 if (names.count() > 0) {
                     const msg = msg: {
                         const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{});
-                        errdefer msg.destroy(sema.gpa);
+                        errdefer msg.destroy(gpa);
 
                         const enum_ty = union_obj.tag_ty;
                         for (names.keys()) |field_name| {
@@ -19513,11 +19516,11 @@ fn reifyStruct(
         .status = .have_field_types,
         .known_non_opv = false,
         .is_tuple = is_tuple,
-        .namespace = .{
-            .parent = block.namespace,
+        .namespace = try mod.createNamespace(.{
+            .parent = block.namespace.toOptional(),
             .ty = struct_ty,
-            .file_scope = block.getFileScope(),
-        },
+            .file_scope = block.getFileScope(mod),
+        }),
     };
 
     // Fields
@@ -19629,7 +19632,7 @@ fn reifyStruct(
                 errdefer msg.destroy(sema.gpa);
 
                 const src_decl = sema.mod.declPtr(block.src_decl);
-                try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl), field_ty, .struct_field);
+                try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), field_ty, .struct_field);
 
                 try sema.addDeclaredHereNote(msg, field_ty);
                 break :msg msg;
@@ -19641,7 +19644,7 @@ fn reifyStruct(
                 errdefer msg.destroy(sema.gpa);
 
                 const src_decl = sema.mod.declPtr(block.src_decl);
-                try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl), field_ty);
+                try sema.explainWhyTypeIsNotPacked(msg, src.toSrcLoc(src_decl, mod), field_ty);
 
                 try sema.addDeclaredHereNote(msg, field_ty);
                 break :msg msg;
@@ -19741,6 +19744,7 @@ fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.In
 }
 
 fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref {
+    const mod = sema.mod;
     const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data;
     const src = LazySrcLoc.nodeOffset(extra.node);
     const va_list_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = extra.node };
@@ -19755,7 +19759,7 @@ fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) C
             errdefer msg.destroy(sema.gpa);
 
             const src_decl = sema.mod.declPtr(block.src_decl);
-            try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl), arg_ty, .param_ty);
+            try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl, mod), arg_ty, .param_ty);
 
             try sema.addDeclaredHereNote(msg, arg_ty);
             break :msg msg;
@@ -21006,7 +21010,8 @@ fn checkVectorizableBinaryOperands(
 
 fn maybeOptionsSrc(sema: *Sema, block: *Block, base_src: LazySrcLoc, wanted: []const u8) LazySrcLoc {
     if (base_src == .unneeded) return .unneeded;
-    return Module.optionsSrc(sema.gpa, sema.mod.declPtr(block.src_decl), base_src, wanted);
+    const mod = sema.mod;
+    return mod.optionsSrc(sema.mod.declPtr(block.src_decl), base_src, wanted);
 }
 
 fn resolveExportOptions(
@@ -23067,7 +23072,7 @@ fn zirBuiltinExtern(
             const msg = try sema.errMsg(block, ty_src, "extern symbol cannot have type '{}'", .{ty.fmt(mod)});
             errdefer msg.destroy(sema.gpa);
             const src_decl = sema.mod.declPtr(block.src_decl);
-            try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl), ty, .other);
+            try sema.explainWhyTypeIsNotExtern(msg, ty_src.toSrcLoc(src_decl, mod), ty, .other);
             break :msg msg;
         };
         return sema.failWithOwnedErrorMsg(msg);
@@ -23087,9 +23092,9 @@ fn zirBuiltinExtern(
 
     // TODO check duplicate extern
 
-    const new_decl_index = try sema.mod.allocateNewDecl(sema.owner_decl.src_namespace, sema.owner_decl.src_node, null);
-    errdefer sema.mod.destroyDecl(new_decl_index);
-    const new_decl = sema.mod.declPtr(new_decl_index);
+    const new_decl_index = try mod.allocateNewDecl(sema.owner_decl.src_namespace, sema.owner_decl.src_node, null);
+    errdefer mod.destroyDecl(new_decl_index);
+    const new_decl = mod.declPtr(new_decl_index);
     new_decl.name = try sema.gpa.dupeZ(u8, options.name);
 
     {
@@ -23117,12 +23122,12 @@ fn zirBuiltinExtern(
         new_decl.@"linksection" = null;
         new_decl.has_tv = true;
         new_decl.analysis = .complete;
-        new_decl.generation = sema.mod.generation;
+        new_decl.generation = mod.generation;
 
         try new_decl.finalizeNewArena(&new_decl_arena);
     }
 
-    try sema.mod.declareDeclDependency(sema.owner_decl_index, new_decl_index);
+    try mod.declareDeclDependency(sema.owner_decl_index, new_decl_index);
     try sema.ensureDeclAnalyzed(new_decl_index);
 
     const ref = try Value.Tag.decl_ref.create(sema.arena, new_decl_index);
@@ -23209,7 +23214,7 @@ fn validateVarType(
             const msg = try sema.errMsg(block, src, "extern variable cannot have type '{}'", .{var_ty.fmt(mod)});
             errdefer msg.destroy(sema.gpa);
             const src_decl = mod.declPtr(block.src_decl);
-            try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl), var_ty, .other);
+            try sema.explainWhyTypeIsNotExtern(msg, src.toSrcLoc(src_decl, mod), var_ty, .other);
             break :msg msg;
         };
         return sema.failWithOwnedErrorMsg(msg);
@@ -23222,7 +23227,7 @@ fn validateVarType(
         errdefer msg.destroy(sema.gpa);
 
         const src_decl = mod.declPtr(block.src_decl);
-        try sema.explainWhyTypeIsComptime(msg, src.toSrcLoc(src_decl), var_ty);
+        try sema.explainWhyTypeIsComptime(msg, src.toSrcLoc(src_decl, mod), var_ty);
         if (var_ty.zigTypeTag(mod) == .ComptimeInt or var_ty.zigTypeTag(mod) == .ComptimeFloat) {
             try sema.errNote(block, src, msg, "to modify this variable at runtime, it must be given an explicit fixed-size number type", .{});
         }
@@ -23939,11 +23944,12 @@ fn safetyPanic(
     block: *Block,
     panic_id: PanicId,
 ) CompileError!void {
+    const mod = sema.mod;
     const panic_messages_ty = try sema.getBuiltinType("panic_messages");
     const msg_decl_index = (try sema.namespaceLookup(
         block,
         sema.src,
-        panic_messages_ty.getNamespace().?,
+        panic_messages_ty.getNamespaceIndex(mod).unwrap().?,
         @tagName(panic_id),
     )).?;
 
@@ -24006,7 +24012,7 @@ fn fieldVal(
                 );
             } else if (mem.eql(u8, field_name, "ptr") and is_pointer_to) {
                 const ptr_info = object_ty.ptrInfo(mod);
-                const result_ty = try Type.ptr(sema.arena, sema.mod, .{
+                const result_ty = try Type.ptr(sema.arena, mod, .{
                     .pointee_type = ptr_info.pointee_type.childType(mod),
                     .sentinel = ptr_info.sentinel,
                     .@"align" = ptr_info.@"align",
@@ -24025,7 +24031,7 @@ fn fieldVal(
                     block,
                     field_name_src,
                     "no member named '{s}' in '{}'",
-                    .{ field_name, object_ty.fmt(sema.mod) },
+                    .{ field_name, object_ty.fmt(mod) },
                 );
             }
         },
@@ -24049,7 +24055,7 @@ fn fieldVal(
                         block,
                         field_name_src,
                         "no member named '{s}' in '{}'",
-                        .{ field_name, object_ty.fmt(sema.mod) },
+                        .{ field_name, object_ty.fmt(mod) },
                     );
                 }
             }
@@ -24071,14 +24077,14 @@ fn fieldVal(
                         }
                         const msg = msg: {
                             const msg = try sema.errMsg(block, src, "no error named '{s}' in '{}'", .{
-                                field_name, child_type.fmt(sema.mod),
+                                field_name, child_type.fmt(mod),
                             });
                             errdefer msg.destroy(sema.gpa);
                             try sema.addDeclaredHereNote(msg, child_type);
                             break :msg msg;
                         };
                         return sema.failWithOwnedErrorMsg(msg);
-                    } else (try sema.mod.getErrorValue(field_name)).key;
+                    } else (try mod.getErrorValue(field_name)).key;
 
                     return sema.addConstant(
                         if (!child_type.isAnyError())
@@ -24089,7 +24095,7 @@ fn fieldVal(
                     );
                 },
                 .Union => {
-                    if (child_type.getNamespace()) |namespace| {
+                    if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
                         if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
                             return inst;
                         }
@@ -24107,7 +24113,7 @@ fn fieldVal(
                     return sema.failWithBadMemberAccess(block, union_ty, field_name_src, field_name);
                 },
                 .Enum => {
-                    if (child_type.getNamespace()) |namespace| {
+                    if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
                         if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
                             return inst;
                         }
@@ -24119,7 +24125,7 @@ fn fieldVal(
                     return sema.addConstant(try child_type.copy(arena), enum_val);
                 },
                 .Struct, .Opaque => {
-                    if (child_type.getNamespace()) |namespace| {
+                    if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
                         if (try sema.namespaceLookupVal(block, src, namespace, field_name)) |inst| {
                             return inst;
                         }
@@ -24128,7 +24134,7 @@ fn fieldVal(
                 },
                 else => {
                     const msg = msg: {
-                        const msg = try sema.errMsg(block, src, "type '{}' has no members", .{child_type.fmt(sema.mod)});
+                        const msg = try sema.errMsg(block, src, "type '{}' has no members", .{child_type.fmt(mod)});
                         errdefer msg.destroy(sema.gpa);
                         if (child_type.isSlice(mod)) try sema.errNote(block, src, msg, "slice values have 'len' and 'ptr' members", .{});
                         if (child_type.zigTypeTag(mod) == .Array) try sema.errNote(block, src, msg, "array values have 'len' member", .{});
@@ -24174,7 +24180,7 @@ fn fieldPtr(
     const object_ptr_ty = sema.typeOf(object_ptr);
     const object_ty = switch (object_ptr_ty.zigTypeTag(mod)) {
         .Pointer => object_ptr_ty.childType(mod),
-        else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty.fmt(sema.mod)}),
+        else => return sema.fail(block, object_ptr_src, "expected pointer, found '{}'", .{object_ptr_ty.fmt(mod)}),
     };
 
     // Zig allows dereferencing a single pointer during field lookup. Note that
@@ -24202,7 +24208,7 @@ fn fieldPtr(
                     block,
                     field_name_src,
                     "no member named '{s}' in '{}'",
-                    .{ field_name, object_ty.fmt(sema.mod) },
+                    .{ field_name, object_ty.fmt(mod) },
                 );
             }
         },
@@ -24218,7 +24224,7 @@ fn fieldPtr(
                 const buf = try sema.arena.create(Type.SlicePtrFieldTypeBuffer);
                 const slice_ptr_ty = inner_ty.slicePtrFieldType(buf, mod);
 
-                const result_ty = try Type.ptr(sema.arena, sema.mod, .{
+                const result_ty = try Type.ptr(sema.arena, mod, .{
                     .pointee_type = slice_ptr_ty,
                     .mutable = attr_ptr_ty.ptrIsMutable(mod),
                     .@"volatile" = attr_ptr_ty.isVolatilePtr(mod),
@@ -24239,7 +24245,7 @@ fn fieldPtr(
 
                 return block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr);
             } else if (mem.eql(u8, field_name, "len")) {
-                const result_ty = try Type.ptr(sema.arena, sema.mod, .{
+                const result_ty = try Type.ptr(sema.arena, mod, .{
                     .pointee_type = Type.usize,
                     .mutable = attr_ptr_ty.ptrIsMutable(mod),
                     .@"volatile" = attr_ptr_ty.isVolatilePtr(mod),
@@ -24264,7 +24270,7 @@ fn fieldPtr(
                     block,
                     field_name_src,
                     "no member named '{s}' in '{}'",
-                    .{ field_name, object_ty.fmt(sema.mod) },
+                    .{ field_name, object_ty.fmt(mod) },
                 );
             }
         },
@@ -24287,9 +24293,9 @@ fn fieldPtr(
                             break :blk entry.key_ptr.*;
                         }
                         return sema.fail(block, src, "no error named '{s}' in '{}'", .{
-                            field_name, child_type.fmt(sema.mod),
+                            field_name, child_type.fmt(mod),
                         });
-                    } else (try sema.mod.getErrorValue(field_name)).key;
+                    } else (try mod.getErrorValue(field_name)).key;
 
                     var anon_decl = try block.startAnonDecl();
                     defer anon_decl.deinit();
@@ -24303,7 +24309,7 @@ fn fieldPtr(
                     ));
                 },
                 .Union => {
-                    if (child_type.getNamespace()) |namespace| {
+                    if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
                         if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
                             return inst;
                         }
@@ -24324,7 +24330,7 @@ fn fieldPtr(
                     return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
                 },
                 .Enum => {
-                    if (child_type.getNamespace()) |namespace| {
+                    if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
                         if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
                             return inst;
                         }
@@ -24342,14 +24348,14 @@ fn fieldPtr(
                     ));
                 },
                 .Struct, .Opaque => {
-                    if (child_type.getNamespace()) |namespace| {
+                    if (child_type.getNamespaceIndex(mod).unwrap()) |namespace| {
                         if (try sema.namespaceLookupRef(block, src, namespace, field_name)) |inst| {
                             return inst;
                         }
                     }
                     return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name);
                 },
-                else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(sema.mod)}),
+                else => return sema.fail(block, src, "type '{}' has no members", .{child_type.fmt(mod)}),
             }
         },
         .Struct => {
@@ -24398,7 +24404,7 @@ fn fieldCallBind(
     const inner_ty = if (raw_ptr_ty.zigTypeTag(mod) == .Pointer and (raw_ptr_ty.ptrSize(mod) == .One or raw_ptr_ty.ptrSize(mod) == .C))
         raw_ptr_ty.childType(mod)
     else
-        return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty.fmt(sema.mod)});
+        return sema.fail(block, raw_ptr_src, "expected single pointer, found '{}'", .{raw_ptr_ty.fmt(mod)});
 
     // Optionally dereference a second pointer to get the concrete type.
     const is_double_ptr = inner_ty.zigTypeTag(mod) == .Pointer and inner_ty.ptrSize(mod) == .One;
@@ -24458,7 +24464,7 @@ fn fieldCallBind(
     // If we get here, we need to look for a decl in the struct type instead.
     const found_decl = switch (concrete_ty.zigTypeTag(mod)) {
         .Struct, .Opaque, .Union, .Enum => found_decl: {
-            if (concrete_ty.getNamespace()) |namespace| {
+            if (concrete_ty.getNamespaceIndex(mod).unwrap()) |namespace| {
                 if (try sema.namespaceLookup(block, src, namespace, field_name)) |decl_idx| {
                     try sema.addReferencedBy(block, src, decl_idx);
                     const decl_val = try sema.analyzeDeclVal(block, src, decl_idx);
@@ -24472,7 +24478,7 @@ fn fieldCallBind(
                                 first_param_type.zigTypeTag(mod) == .Pointer and
                                 (first_param_type.ptrSize(mod) == .One or
                                 first_param_type.ptrSize(mod) == .C) and
-                                first_param_type.childType(mod).eql(concrete_ty, sema.mod)))
+                                first_param_type.childType(mod).eql(concrete_ty, mod)))
                         {
                         // zig fmt: on
                             // Note that if the param type is generic poison, we know that it must
@@ -24484,7 +24490,7 @@ fn fieldCallBind(
                                 .func_inst = decl_val,
                                 .arg0_inst = object_ptr,
                             } };
-                        } else if (first_param_type.eql(concrete_ty, sema.mod)) {
+                        } else if (first_param_type.eql(concrete_ty, mod)) {
                             const deref = try sema.analyzeLoad(block, src, object_ptr, src);
                             return .{ .method = .{
                                 .func_inst = decl_val,
@@ -24492,7 +24498,7 @@ fn fieldCallBind(
                             } };
                         } else if (first_param_type.zigTypeTag(mod) == .Optional) {
                             const child = first_param_type.optionalChild(mod);
-                            if (child.eql(concrete_ty, sema.mod)) {
+                            if (child.eql(concrete_ty, mod)) {
                                 const deref = try sema.analyzeLoad(block, src, object_ptr, src);
                                 return .{ .method = .{
                                     .func_inst = decl_val,
@@ -24500,7 +24506,7 @@ fn fieldCallBind(
                                 } };
                             } else if (child.zigTypeTag(mod) == .Pointer and
                                 child.ptrSize(mod) == .One and
-                                child.childType(mod).eql(concrete_ty, sema.mod))
+                                child.childType(mod).eql(concrete_ty, mod))
                             {
                                 return .{ .method = .{
                                     .func_inst = decl_val,
@@ -24508,7 +24514,7 @@ fn fieldCallBind(
                                 } };
                             }
                         } else if (first_param_type.zigTypeTag(mod) == .ErrorUnion and
-                            first_param_type.errorUnionPayload().eql(concrete_ty, sema.mod))
+                            first_param_type.errorUnionPayload().eql(concrete_ty, mod))
                         {
                             const deref = try sema.analyzeLoad(block, src, object_ptr, src);
                             return .{ .method = .{
@@ -24526,12 +24532,12 @@ fn fieldCallBind(
     };
 
     const msg = msg: {
-        const msg = try sema.errMsg(block, src, "no field or member function named '{s}' in '{}'", .{ field_name, concrete_ty.fmt(sema.mod) });
+        const msg = try sema.errMsg(block, src, "no field or member function named '{s}' in '{}'", .{ field_name, concrete_ty.fmt(mod) });
         errdefer msg.destroy(sema.gpa);
         try sema.addDeclaredHereNote(msg, concrete_ty);
         if (found_decl) |decl_idx| {
-            const decl = sema.mod.declPtr(decl_idx);
-            try sema.mod.errNoteNonLazy(decl.srcLoc(), msg, "'{s}' is not a member function", .{field_name});
+            const decl = mod.declPtr(decl_idx);
+            try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "'{s}' is not a member function", .{field_name});
         }
         break :msg msg;
     };
@@ -24549,7 +24555,7 @@ fn finishFieldCallBind(
 ) CompileError!ResolvedFieldCallee {
     const mod = sema.mod;
     const arena = sema.arena;
-    const ptr_field_ty = try Type.ptr(arena, sema.mod, .{
+    const ptr_field_ty = try Type.ptr(arena, mod, .{
         .pointee_type = field_ty,
         .mutable = ptr_ty.ptrIsMutable(mod),
         .@"addrspace" = ptr_ty.ptrAddressSpace(mod),
@@ -24583,19 +24589,20 @@ fn namespaceLookup(
     sema: *Sema,
     block: *Block,
     src: LazySrcLoc,
-    namespace: *Namespace,
+    namespace: Namespace.Index,
     decl_name: []const u8,
 ) CompileError!?Decl.Index {
+    const mod = sema.mod;
     const gpa = sema.gpa;
     if (try sema.lookupInNamespace(block, src, namespace, decl_name, true)) |decl_index| {
-        const decl = sema.mod.declPtr(decl_index);
-        if (!decl.is_pub and decl.getFileScope() != block.getFileScope()) {
+        const decl = mod.declPtr(decl_index);
+        if (!decl.is_pub and decl.getFileScope(mod) != block.getFileScope(mod)) {
             const msg = msg: {
                 const msg = try sema.errMsg(block, src, "'{s}' is not marked 'pub'", .{
                     decl_name,
                 });
                 errdefer msg.destroy(gpa);
-                try sema.mod.errNoteNonLazy(decl.srcLoc(), msg, "declared here", .{});
+                try mod.errNoteNonLazy(decl.srcLoc(mod), msg, "declared here", .{});
                 break :msg msg;
             };
             return sema.failWithOwnedErrorMsg(msg);
@@ -24609,7 +24616,7 @@ fn namespaceLookupRef(
     sema: *Sema,
     block: *Block,
     src: LazySrcLoc,
-    namespace: *Namespace,
+    namespace: Namespace.Index,
     decl_name: []const u8,
 ) CompileError!?Air.Inst.Ref {
     const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null;
@@ -24621,7 +24628,7 @@ fn namespaceLookupVal(
     sema: *Sema,
     block: *Block,
     src: LazySrcLoc,
-    namespace: *Namespace,
+    namespace: Namespace.Index,
     decl_name: []const u8,
 ) CompileError!?Air.Inst.Ref {
     const decl = (try sema.namespaceLookup(block, src, namespace, decl_name)) orelse return null;
@@ -24692,7 +24699,7 @@ fn structFieldPtrByIndex(
         .@"addrspace" = struct_ptr_ty_info.@"addrspace",
     };
 
-    const target = sema.mod.getTarget();
+    const target = mod.getTarget();
 
     if (struct_obj.layout == .Packed) {
         comptime assert(Type.packed_struct_layout_version == 2);
@@ -24746,7 +24753,7 @@ fn structFieldPtrByIndex(
         ptr_ty_data.@"align" = field.abi_align;
     }
 
-    const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, ptr_ty_data);
+    const ptr_field_ty = try Type.ptr(sema.arena, mod, ptr_ty_data);
 
     if (field.is_comptime) {
         const val = try Value.Tag.comptime_field_ptr.create(sema.arena, .{
@@ -24848,16 +24855,17 @@ fn tupleFieldIndex(
     field_name: []const u8,
     field_name_src: LazySrcLoc,
 ) CompileError!u32 {
+    const mod = sema.mod;
     assert(!std.mem.eql(u8, field_name, "len"));
     if (std.fmt.parseUnsigned(u32, field_name, 10)) |field_index| {
         if (field_index < tuple_ty.structFieldCount()) return field_index;
         return sema.fail(block, field_name_src, "index '{s}' out of bounds of tuple '{}'", .{
-            field_name, tuple_ty.fmt(sema.mod),
+            field_name, tuple_ty.fmt(mod),
         });
     } else |_| {}
 
     return sema.fail(block, field_name_src, "no field named '{s}' in tuple '{}'", .{
-        field_name, tuple_ty.fmt(sema.mod),
+        field_name, tuple_ty.fmt(mod),
     });
 }
 
@@ -24913,7 +24921,7 @@ fn unionFieldPtr(
     const union_obj = union_ty.cast(Type.Payload.Union).?.data;
     const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src);
     const field = union_obj.fields.values()[field_index];
-    const ptr_field_ty = try Type.ptr(arena, sema.mod, .{
+    const ptr_field_ty = try Type.ptr(arena, mod, .{
         .pointee_type = field.ty,
         .mutable = union_ptr_ty.ptrIsMutable(mod),
         .@"volatile" = union_ptr_ty.isVolatilePtr(mod),
@@ -24947,7 +24955,7 @@ fn unionFieldPtr(
                     .data = enum_field_index,
                 };
                 const field_tag = Value.initPayload(&field_tag_buf.base);
-                const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, sema.mod);
+                const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, mod);
                 if (!tag_matches) {
                     const msg = msg: {
                         const active_index = tag_and_val.tag.castTag(.enum_field_index).?.data;
@@ -25017,7 +25025,7 @@ fn unionFieldVal(
             .data = enum_field_index,
         };
         const field_tag = Value.initPayload(&field_tag_buf.base);
-        const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, sema.mod);
+        const tag_matches = tag_and_val.tag.eql(field_tag, union_obj.tag_ty, mod);
         switch (union_obj.layout) {
             .Auto => {
                 if (tag_matches) {
@@ -25038,7 +25046,7 @@ fn unionFieldVal(
                 if (tag_matches) {
                     return sema.addConstant(field.ty, tag_and_val.val);
                 } else {
-                    const old_ty = union_ty.unionFieldType(tag_and_val.tag, sema.mod);
+                    const old_ty = union_ty.unionFieldType(tag_and_val.tag, mod);
                     if (try sema.bitCastVal(block, src, tag_and_val.val, old_ty, field.ty, 0)) |new_val| {
                         return sema.addConstant(field.ty, new_val);
                     }
@@ -25079,7 +25087,7 @@ fn elemPtr(
 
     const indexable_ty = switch (indexable_ptr_ty.zigTypeTag(mod)) {
         .Pointer => indexable_ptr_ty.childType(mod),
-        else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty.fmt(sema.mod)}),
+        else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{}'", .{indexable_ptr_ty.fmt(mod)}),
     };
     try checkIndexable(sema, block, src, indexable_ty);
 
@@ -25124,7 +25132,7 @@ fn elemPtrOneLayerOnly(
                 const ptr_val = maybe_ptr_val orelse break :rs indexable_src;
                 const index_val = maybe_index_val orelse break :rs elem_index_src;
                 const index = @intCast(usize, index_val.toUnsignedInt(mod));
-                const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, sema.mod);
+                const elem_ptr = try ptr_val.elemPtr(indexable_ty, sema.arena, index, mod);
                 const result_ty = try sema.elemPtrType(indexable_ty, index);
                 return sema.addConstant(result_ty, elem_ptr);
             };
@@ -25170,7 +25178,7 @@ fn elemVal(
                     const indexable_val = maybe_indexable_val orelse break :rs indexable_src;
                     const index_val = maybe_index_val orelse break :rs elem_index_src;
                     const index = @intCast(usize, index_val.toUnsignedInt(mod));
-                    const elem_ptr_val = try indexable_val.elemPtr(indexable_ty, sema.arena, index, sema.mod);
+                    const elem_ptr_val = try indexable_val.elemPtr(indexable_ty, sema.arena, index, mod);
                     if (try sema.pointerDeref(block, indexable_src, elem_ptr_val, indexable_ty)) |elem_val| {
                         return sema.addConstant(indexable_ty.elemType2(mod), elem_val);
                     }
@@ -25209,6 +25217,7 @@ fn validateRuntimeElemAccess(
     parent_ty: Type,
     parent_src: LazySrcLoc,
 ) CompileError!void {
+    const mod = sema.mod;
     const valid_rt = try sema.validateRunTimeType(elem_ty, false);
     if (!valid_rt) {
         const msg = msg: {
@@ -25216,12 +25225,12 @@ fn validateRuntimeElemAccess(
                 block,
                 elem_index_src,
                 "values of type '{}' must be comptime-known, but index value is runtime-known",
-                .{parent_ty.fmt(sema.mod)},
+                .{parent_ty.fmt(mod)},
             );
             errdefer msg.destroy(sema.gpa);
 
-            const src_decl = sema.mod.declPtr(block.src_decl);
-            try sema.explainWhyTypeIsComptime(msg, parent_src.toSrcLoc(src_decl), parent_ty);
+            const src_decl = mod.declPtr(block.src_decl);
+            try sema.explainWhyTypeIsComptime(msg, parent_src.toSrcLoc(src_decl, mod), parent_ty);
 
             break :msg msg;
         };
@@ -25255,7 +25264,7 @@ fn tupleFieldPtr(
     }
 
     const field_ty = tuple_ty.structFieldType(field_index);
-    const ptr_field_ty = try Type.ptr(sema.arena, sema.mod, .{
+    const ptr_field_ty = try Type.ptr(sema.arena, mod, .{
         .pointee_type = field_ty,
         .mutable = tuple_ptr_ty.ptrIsMutable(mod),
         .@"volatile" = tuple_ptr_ty.isVolatilePtr(mod),
@@ -25431,7 +25440,7 @@ fn elemPtrArray(
             return sema.addConstUndef(elem_ptr_ty);
         }
         if (offset) |index| {
-            const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, index, sema.mod);
+            const elem_ptr = try array_ptr_val.elemPtr(array_ptr_ty, sema.arena, index, mod);
             return sema.addConstant(elem_ptr_ty, elem_ptr);
         }
     }
@@ -25476,7 +25485,7 @@ fn elemValSlice(
 
     if (maybe_slice_val) |slice_val| {
         runtime_src = elem_index_src;
-        const slice_len = slice_val.sliceLen(sema.mod);
+        const slice_len = slice_val.sliceLen(mod);
         const slice_len_s = slice_len + @boolToInt(slice_sent);
         if (slice_len_s == 0) {
             return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{});
@@ -25487,7 +25496,7 @@ fn elemValSlice(
                 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
                 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
             }
-            const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index, sema.mod);
+            const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index, mod);
             if (try sema.pointerDeref(block, slice_src, elem_ptr_val, slice_ty)) |elem_val| {
                 return sema.addConstant(elem_ty, elem_val);
             }
@@ -25500,7 +25509,7 @@ fn elemValSlice(
     try sema.requireRuntimeBlock(block, src, runtime_src);
     if (oob_safety and block.wantSafety()) {
         const len_inst = if (maybe_slice_val) |slice_val|
-            try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod))
+            try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(mod))
         else
             try block.addTyOp(.slice_len, Type.usize, slice);
         const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
@@ -25537,7 +25546,7 @@ fn elemPtrSlice(
         if (slice_val.isUndef()) {
             return sema.addConstUndef(elem_ptr_ty);
         }
-        const slice_len = slice_val.sliceLen(sema.mod);
+        const slice_len = slice_val.sliceLen(mod);
         const slice_len_s = slice_len + @boolToInt(slice_sent);
         if (slice_len_s == 0) {
             return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{});
@@ -25547,7 +25556,7 @@ fn elemPtrSlice(
                 const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else "";
                 return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label });
             }
-            const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index, sema.mod);
+            const elem_ptr_val = try slice_val.elemPtr(slice_ty, sema.arena, index, mod);
             return sema.addConstant(elem_ptr_ty, elem_ptr_val);
         }
     }
@@ -25560,7 +25569,7 @@ fn elemPtrSlice(
         const len_inst = len: {
             if (maybe_undef_slice_val) |slice_val|
                 if (!slice_val.isUndef())
-                    break :len try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(sema.mod));
+                    break :len try sema.addIntUnsigned(Type.usize, slice_val.sliceLen(mod));
             break :len try block.addTyOp(.slice_len, Type.usize, slice);
         };
         const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt;
@@ -25602,16 +25611,17 @@ const CoerceOpts = struct {
 
         fn get(info: @This(), sema: *Sema) !?Module.SrcLoc {
             if (info.func_inst == .none) return null;
+            const mod = sema.mod;
             const fn_decl = (try sema.funcDeclSrc(info.func_inst)) orelse return null;
-            const param_src = Module.paramSrc(0, sema.gpa, fn_decl, info.param_i);
+            const param_src = Module.paramSrc(0, mod, fn_decl, info.param_i);
             if (param_src == .node_offset_param) {
                 return Module.SrcLoc{
-                    .file_scope = fn_decl.getFileScope(),
+                    .file_scope = fn_decl.getFileScope(mod),
                     .parent_decl_node = fn_decl.src_node,
                     .lazy = LazySrcLoc.nodeOffset(param_src.node_offset_param),
                 };
             }
-            return param_src.toSrcLoc(fn_decl);
+            return param_src.toSrcLoc(fn_decl, mod);
         }
     } = .{},
 };
@@ -25625,13 +25635,13 @@ fn coerceExtra(
     opts: CoerceOpts,
 ) CoersionError!Air.Inst.Ref {
     if (dest_ty_unresolved.isGenericPoison()) return inst;
+    const mod = sema.mod;
     const dest_ty_src = inst_src; // TODO better source location
     const dest_ty = try sema.resolveTypeFields(dest_ty_unresolved);
     const inst_ty = try sema.resolveTypeFields(sema.typeOf(inst));
-    const mod = sema.mod;
-    const target = sema.mod.getTarget();
+    const target = mod.getTarget();
     // If the types are the same, we can return the operand.
-    if (dest_ty.eql(inst_ty, sema.mod))
+    if (dest_ty.eql(inst_ty, mod))
         return inst;
 
     const arena = sema.arena;
@@ -26254,7 +26264,7 @@ fn coerceExtra(
 
             const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
             const src_decl = sema.mod.declPtr(sema.func.?.owner_decl);
-            try sema.mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl), msg, "'noreturn' declared here", .{});
+            try sema.mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "'noreturn' declared here", .{});
             break :msg msg;
         };
         return sema.failWithOwnedErrorMsg(msg);
@@ -26287,9 +26297,9 @@ fn coerceExtra(
             const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = 0 };
             const src_decl = sema.mod.declPtr(sema.func.?.owner_decl);
             if (inst_ty.isError(mod) and !dest_ty.isError(mod)) {
-                try sema.mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl), msg, "function cannot return an error", .{});
+                try sema.mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "function cannot return an error", .{});
             } else {
-                try sema.mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl), msg, "function return type declared here", .{});
+                try sema.mod.errNoteNonLazy(ret_ty_src.toSrcLoc(src_decl, mod), msg, "function return type declared here", .{});
             }
         }
 
@@ -27246,7 +27256,7 @@ fn coerceVarArgParam(
             errdefer msg.destroy(sema.gpa);
 
             const src_decl = sema.mod.declPtr(block.src_decl);
-            try sema.explainWhyTypeIsNotExtern(msg, inst_src.toSrcLoc(src_decl), coerced_ty, .param_ty);
+            try sema.explainWhyTypeIsNotExtern(msg, inst_src.toSrcLoc(src_decl, mod), coerced_ty, .param_ty);
 
             try sema.addDeclaredHereNote(msg, coerced_ty);
             break :msg msg;
@@ -29186,13 +29196,14 @@ fn addReferencedBy(
 }
 
 fn ensureDeclAnalyzed(sema: *Sema, decl_index: Decl.Index) CompileError!void {
-    const decl = sema.mod.declPtr(decl_index);
+    const mod = sema.mod;
+    const decl = mod.declPtr(decl_index);
     if (decl.analysis == .in_progress) {
-        const msg = try Module.ErrorMsg.create(sema.gpa, decl.srcLoc(), "dependency loop detected", .{});
+        const msg = try Module.ErrorMsg.create(sema.gpa, decl.srcLoc(mod), "dependency loop detected", .{});
         return sema.failWithOwnedErrorMsg(msg);
     }
 
-    sema.mod.ensureDeclAnalyzed(decl_index) catch |err| {
+    mod.ensureDeclAnalyzed(decl_index) catch |err| {
         if (sema.owner_func) |owner_func| {
             owner_func.state = .dependency_failure;
         } else {
@@ -31015,12 +31026,12 @@ fn resolvePeerTypes(
         // At this point, we hit a compile error. We need to recover
         // the source locations.
         const chosen_src = candidate_srcs.resolve(
-            sema.gpa,
+            mod,
             mod.declPtr(block.src_decl),
             chosen_i,
         );
         const candidate_src = candidate_srcs.resolve(
-            sema.gpa,
+            mod,
             mod.declPtr(block.src_decl),
             candidate_i + 1,
         );
@@ -31315,7 +31326,7 @@ fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!voi
     const decl_arena_allocator = decl.value_arena.?.acquire(gpa, &decl_arena);
     defer decl.value_arena.?.release(&decl_arena);
 
-    const zir = struct_obj.namespace.file_scope.zir;
+    const zir = mod.namespacePtr(struct_obj.namespace).file_scope.zir;
     const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended;
     assert(extended.opcode == .struct_decl);
     const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);
@@ -31353,7 +31364,7 @@ fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!voi
             .parent = null,
             .sema = &sema,
             .src_decl = decl_index,
-            .namespace = &struct_obj.namespace,
+            .namespace = struct_obj.namespace,
             .wip_capture_scope = wip_captures.scope,
             .instructions = .{},
             .inlining = null,
@@ -31399,7 +31410,7 @@ fn semaBackingIntType(mod: *Module, struct_obj: *Module.Struct) CompileError!voi
                 .parent = null,
                 .sema = &sema,
                 .src_decl = decl_index,
-                .namespace = &struct_obj.namespace,
+                .namespace = struct_obj.namespace,
                 .wip_capture_scope = undefined,
                 .instructions = .{},
                 .inlining = null,
@@ -31522,7 +31533,6 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
             .error_set_single,
             .error_set_inferred,
             .error_set_merged,
-            .@"opaque",
             .enum_simple,
             => false,
 
@@ -31678,6 +31688,7 @@ pub fn resolveTypeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
             },
             .struct_type => @panic("TODO"),
             .union_type => @panic("TODO"),
+            .opaque_type => false,
 
             // values, not types
             .simple_value => unreachable,
@@ -31991,6 +32002,8 @@ fn resolveInferredErrorSet(
         return sema.fail(block, src, "unable to resolve inferred error set", .{});
     }
 
+    const mod = sema.mod;
+
     // In order to ensure that all dependencies are properly added to the set, we
     // need to ensure the function body is analyzed of the inferred error set.
     // However, in the case of comptime/inline function calls with inferred error sets,
@@ -32011,7 +32024,7 @@ fn resolveInferredErrorSet(
                 const msg = try sema.errMsg(block, src, "unable to resolve inferred error set of generic function", .{});
                 errdefer msg.destroy(sema.gpa);
 
-                try sema.mod.errNoteNonLazy(ies_func_owner_decl.srcLoc(), msg, "generic function declared here", .{});
+                try sema.mod.errNoteNonLazy(ies_func_owner_decl.srcLoc(mod), msg, "generic function declared here", .{});
                 break :msg msg;
             };
             return sema.failWithOwnedErrorMsg(msg);
@@ -32049,7 +32062,7 @@ fn resolveInferredErrorSetTy(
 fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void {
     const gpa = mod.gpa;
     const decl_index = struct_obj.owner_decl;
-    const zir = struct_obj.namespace.file_scope.zir;
+    const zir = mod.namespacePtr(struct_obj.namespace).file_scope.zir;
     const extended = zir.instructions.items(.data)[struct_obj.zir_index].extended;
     assert(extended.opcode == .struct_decl);
     const small = @bitCast(Zir.Inst.StructDecl.Small, extended.small);
@@ -32123,7 +32136,7 @@ fn semaStructFields(mod: *Module, struct_obj: *Module.Struct) CompileError!void
         .parent = null,
         .sema = &sema,
         .src_decl = decl_index,
-        .namespace = &struct_obj.namespace,
+        .namespace = struct_obj.namespace,
         .wip_capture_scope = wip_captures.scope,
         .instructions = .{},
         .inlining = null,
@@ -32393,7 +32406,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
 
     const gpa = mod.gpa;
     const decl_index = union_obj.owner_decl;
-    const zir = union_obj.namespace.file_scope.zir;
+    const zir = mod.namespacePtr(union_obj.namespace).file_scope.zir;
     const extended = zir.instructions.items(.data)[union_obj.zir_index].extended;
     assert(extended.opcode == .union_decl);
     const small = @bitCast(Zir.Inst.UnionDecl.Small, extended.small);
@@ -32463,7 +32476,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
         .parent = null,
         .sema = &sema,
         .src_decl = decl_index,
-        .namespace = &union_obj.namespace,
+        .namespace = union_obj.namespace,
         .wip_capture_scope = wip_captures.scope,
         .instructions = .{},
         .inlining = null,
@@ -32665,7 +32678,7 @@ fn semaUnionFields(mod: *Module, union_obj: *Module.Union) CompileError!void {
 
                 const prev_field_index = union_obj.fields.getIndex(field_name).?;
                 const prev_field_src = union_obj.fieldSrcLoc(sema.mod, .{ .index = prev_field_index }).lazy;
-                try sema.mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl), msg, "other field here", .{});
+                try sema.mod.errNoteNonLazy(prev_field_src.toSrcLoc(decl, mod), msg, "other field here", .{});
                 try sema.errNote(&block_scope, src, msg, "union declared here", .{});
                 break :msg msg;
             };
@@ -32929,7 +32942,7 @@ fn getBuiltin(sema: *Sema, name: []const u8) CompileError!Air.Inst.Ref {
     const opt_ty_decl = (try sema.namespaceLookup(
         &block,
         src,
-        builtin_ty.getNamespace().?,
+        builtin_ty.getNamespaceIndex(mod).unwrap().?,
         name,
     )) orelse std.debug.panic("lib/std/builtin.zig is corrupt and missing '{s}'", .{name});
     return sema.analyzeDeclVal(&block, src, opt_ty_decl);
@@ -32984,7 +32997,6 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
             .function,
             .array_sentinel,
             .error_set_inferred,
-            .@"opaque",
             .anyframe_T,
             .pointer,
             => return null,
@@ -33123,7 +33135,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
             .inferred_alloc_mut => unreachable,
         },
 
-        else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+        else => return switch (mod.intern_pool.indexToKey(ty.ip_index)) {
             .int_type => |int_type| {
                 if (int_type.bits == 0) {
                     return try mod.intValue(ty, 0);
@@ -33131,7 +33143,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
                     return null;
                 }
             },
-            .ptr_type => return null,
+            .ptr_type => null,
             .array_type => |array_type| {
                 if (array_type.len == 0)
                     return Value.initTag(.empty_array);
@@ -33152,7 +33164,7 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
                     return null;
                 }
             },
-            .error_union_type => return null,
+            .error_union_type => null,
             .simple_type => |t| switch (t) {
                 .f16,
                 .f32,
@@ -33190,18 +33202,19 @@ pub fn typeHasOnePossibleValue(sema: *Sema, ty: Type) CompileError!?Value {
                 .export_options,
                 .extern_options,
                 .type_info,
-                => return null,
+                => null,
 
-                .void => return Value.void,
-                .noreturn => return Value.@"unreachable",
-                .null => return Value.null,
-                .undefined => return Value.undef,
+                .void => Value.void,
+                .noreturn => Value.@"unreachable",
+                .null => Value.null,
+                .undefined => Value.undef,
 
                 .generic_poison => return error.GenericPoison,
                 .var_args_param => unreachable,
             },
             .struct_type => @panic("TODO"),
             .union_type => @panic("TODO"),
+            .opaque_type => null,
 
             // values, not types
             .simple_value => unreachable,
@@ -33606,7 +33619,6 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
             .error_set_single,
             .error_set_inferred,
             .error_set_merged,
-            .@"opaque",
             .enum_simple,
             => false,
 
@@ -33772,6 +33784,7 @@ pub fn typeRequiresComptime(sema: *Sema, ty: Type) CompileError!bool {
             },
             .struct_type => @panic("TODO"),
             .union_type => @panic("TODO"),
+            .opaque_type => false,
 
             // values, not types
             .simple_value => unreachable,
src/type.zig
@@ -42,8 +42,6 @@ pub const Type = struct {
                 .error_set_merged,
                 => return .ErrorSet,
 
-                .@"opaque" => return .Opaque,
-
                 .function => return .Fn,
 
                 .array,
@@ -87,6 +85,7 @@ pub const Type = struct {
                 .error_union_type => return .ErrorUnion,
                 .struct_type => return .Struct,
                 .union_type => return .Union,
+                .opaque_type => return .Opaque,
                 .simple_type => |s| switch (s) {
                     .f16,
                     .f32,
@@ -361,12 +360,6 @@ pub const Type = struct {
                 return true;
             },
 
-            .@"opaque" => {
-                const opaque_obj_a = a.castTag(.@"opaque").?.data;
-                const opaque_obj_b = (b.castTag(.@"opaque") orelse return false).data;
-                return opaque_obj_a == opaque_obj_b;
-            },
-
             .function => {
                 if (b.zigTypeTag(mod) != .Fn) return false;
 
@@ -649,12 +642,6 @@ pub const Type = struct {
                 std.hash.autoHash(hasher, ies);
             },
 
-            .@"opaque" => {
-                std.hash.autoHash(hasher, std.builtin.TypeId.Opaque);
-                const opaque_obj = ty.castTag(.@"opaque").?.data;
-                std.hash.autoHash(hasher, opaque_obj);
-            },
-
             .function => {
                 std.hash.autoHash(hasher, std.builtin.TypeId.Fn);
 
@@ -974,7 +961,6 @@ pub const Type = struct {
             .enum_simple => return self.copyPayloadShallow(allocator, Payload.EnumSimple),
             .enum_numbered => return self.copyPayloadShallow(allocator, Payload.EnumNumbered),
             .enum_full, .enum_nonexhaustive => return self.copyPayloadShallow(allocator, Payload.EnumFull),
-            .@"opaque" => return self.copyPayloadShallow(allocator, Payload.Opaque),
         }
     }
 
@@ -1079,12 +1065,6 @@ pub const Type = struct {
                         @tagName(t), enum_numbered.owner_decl,
                     });
                 },
-                .@"opaque" => {
-                    const opaque_obj = ty.castTag(.@"opaque").?.data;
-                    return writer.print("({s} decl={d})", .{
-                        @tagName(t), opaque_obj.owner_decl,
-                    });
-                },
 
                 .function => {
                     const payload = ty.castTag(.function).?.data;
@@ -1303,11 +1283,6 @@ pub const Type = struct {
                     const decl = mod.declPtr(enum_numbered.owner_decl);
                     try decl.renderFullyQualifiedName(mod, writer);
                 },
-                .@"opaque" => {
-                    const opaque_obj = ty.cast(Payload.Opaque).?.data;
-                    const decl = mod.declPtr(opaque_obj.owner_decl);
-                    try decl.renderFullyQualifiedName(mod, writer);
-                },
 
                 .error_set_inferred => {
                     const func = ty.castTag(.error_set_inferred).?.data.func;
@@ -1575,6 +1550,10 @@ pub const Type = struct {
                 .simple_type => |s| return writer.writeAll(@tagName(s)),
                 .struct_type => @panic("TODO"),
                 .union_type => @panic("TODO"),
+                .opaque_type => |opaque_type| {
+                    const decl = mod.declPtr(opaque_type.decl);
+                    try decl.renderFullyQualifiedName(mod, writer);
+                },
 
                 // values, not types
                 .simple_value => unreachable,
@@ -1622,7 +1601,6 @@ pub const Type = struct {
             .none => switch (ty.tag()) {
                 .error_set_inferred,
 
-                .@"opaque",
                 .error_set_single,
                 .error_union,
                 .error_set,
@@ -1759,8 +1737,8 @@ pub const Type = struct {
                 .inferred_alloc_const => unreachable,
                 .inferred_alloc_mut => unreachable,
             },
-            else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
-                .int_type => |int_type| return int_type.bits != 0,
+            else => return switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+                .int_type => |int_type| int_type.bits != 0,
                 .ptr_type => |ptr_type| {
                     // Pointers to zero-bit types still have a runtime address; however, pointers
                     // to comptime-only types do not, with the exception of function pointers.
@@ -1797,7 +1775,7 @@ pub const Type = struct {
                     }
                 },
                 .error_union_type => @panic("TODO"),
-                .simple_type => |t| return switch (t) {
+                .simple_type => |t| switch (t) {
                     .f16,
                     .f32,
                     .f64,
@@ -1848,6 +1826,7 @@ pub const Type = struct {
                 },
                 .struct_type => @panic("TODO"),
                 .union_type => @panic("TODO"),
+                .opaque_type => true,
 
                 // values, not types
                 .simple_value => unreachable,
@@ -1876,7 +1855,6 @@ pub const Type = struct {
                 .error_set_single,
                 .error_set_inferred,
                 .error_set_merged,
-                .@"opaque",
                 // These are function bodies, not function pointers.
                 .function,
                 .enum_simple,
@@ -1960,6 +1938,7 @@ pub const Type = struct {
                 },
                 .struct_type => @panic("TODO"),
                 .union_type => @panic("TODO"),
+                .opaque_type => false,
 
                 // values, not types
                 .simple_value => unreachable,
@@ -2144,8 +2123,6 @@ pub const Type = struct {
         switch (ty.ip_index) {
             .empty_struct_type => return AbiAlignmentAdvanced{ .scalar = 0 },
             .none => switch (ty.tag()) {
-                .@"opaque" => return AbiAlignmentAdvanced{ .scalar = 1 },
-
                 // represents machine code; not a pointer
                 .function => {
                     const alignment = ty.castTag(.function).?.data.alignment;
@@ -2362,6 +2339,7 @@ pub const Type = struct {
                 },
                 .struct_type => @panic("TODO"),
                 .union_type => @panic("TODO"),
+                .opaque_type => return AbiAlignmentAdvanced{ .scalar = 1 },
 
                 // values, not types
                 .simple_value => unreachable,
@@ -2536,7 +2514,6 @@ pub const Type = struct {
 
             .none => switch (ty.tag()) {
                 .function => unreachable, // represents machine code; not a pointer
-                .@"opaque" => unreachable, // no size available
                 .inferred_alloc_const => unreachable,
                 .inferred_alloc_mut => unreachable,
 
@@ -2777,6 +2754,7 @@ pub const Type = struct {
                 },
                 .struct_type => @panic("TODO"),
                 .union_type => @panic("TODO"),
+                .opaque_type => unreachable, // no size available
 
                 // values, not types
                 .simple_value => unreachable,
@@ -2948,6 +2926,7 @@ pub const Type = struct {
             },
             .struct_type => @panic("TODO"),
             .union_type => @panic("TODO"),
+            .opaque_type => unreachable,
 
             // values, not types
             .simple_value => unreachable,
@@ -2965,7 +2944,6 @@ pub const Type = struct {
             .empty_struct => unreachable,
             .inferred_alloc_const => unreachable,
             .inferred_alloc_mut => unreachable,
-            .@"opaque" => unreachable,
 
             .@"struct" => {
                 const struct_obj = ty.castTag(.@"struct").?.data;
@@ -3806,6 +3784,7 @@ pub const Type = struct {
                 .simple_type => unreachable, // handled via Index enum tag above
                 .struct_type => @panic("TODO"),
                 .union_type => unreachable,
+                .opaque_type => unreachable,
 
                 // values, not types
                 .simple_value => unreachable,
@@ -4004,7 +3983,6 @@ pub const Type = struct {
                 .function,
                 .array_sentinel,
                 .error_set_inferred,
-                .@"opaque",
                 .anyframe_T,
                 .pointer,
                 => return null,
@@ -4182,6 +4160,7 @@ pub const Type = struct {
                 },
                 .struct_type => @panic("TODO"),
                 .union_type => @panic("TODO"),
+                .opaque_type => return null,
 
                 // values, not types
                 .simple_value => unreachable,
@@ -4208,7 +4187,6 @@ pub const Type = struct {
                 .error_set_single,
                 .error_set_inferred,
                 .error_set_merged,
-                .@"opaque",
                 .enum_simple,
                 => false,
 
@@ -4350,6 +4328,7 @@ pub const Type = struct {
                 },
                 .struct_type => @panic("TODO"),
                 .union_type => @panic("TODO"),
+                .opaque_type => false,
 
                 // values, not types
                 .simple_value => unreachable,
@@ -4399,21 +4378,31 @@ pub const Type = struct {
     }
 
     /// Returns null if the type has no namespace.
-    pub fn getNamespace(self: Type) ?*Module.Namespace {
-        return switch (self.tag()) {
-            .@"struct" => &self.castTag(.@"struct").?.data.namespace,
-            .enum_full => &self.castTag(.enum_full).?.data.namespace,
-            .enum_nonexhaustive => &self.castTag(.enum_nonexhaustive).?.data.namespace,
-            .empty_struct => self.castTag(.empty_struct).?.data,
-            .@"opaque" => &self.castTag(.@"opaque").?.data.namespace,
-            .@"union" => &self.castTag(.@"union").?.data.namespace,
-            .union_safety_tagged => &self.castTag(.union_safety_tagged).?.data.namespace,
-            .union_tagged => &self.castTag(.union_tagged).?.data.namespace,
+    pub fn getNamespaceIndex(ty: Type, mod: *Module) Module.Namespace.OptionalIndex {
+        return switch (ty.ip_index) {
+            .none => switch (ty.tag()) {
+                .@"struct" => ty.castTag(.@"struct").?.data.namespace.toOptional(),
+                .enum_full => ty.castTag(.enum_full).?.data.namespace.toOptional(),
+                .enum_nonexhaustive => ty.castTag(.enum_nonexhaustive).?.data.namespace.toOptional(),
+                .empty_struct => @panic("TODO"),
+                .@"union" => ty.castTag(.@"union").?.data.namespace.toOptional(),
+                .union_safety_tagged => ty.castTag(.union_safety_tagged).?.data.namespace.toOptional(),
+                .union_tagged => ty.castTag(.union_tagged).?.data.namespace.toOptional(),
 
-            else => null,
+                else => .none,
+            },
+            else => switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+                .opaque_type => |opaque_type| opaque_type.namespace.toOptional(),
+                else => .none,
+            },
         };
     }
 
+    /// Returns null if the type has no namespace.
+    pub fn getNamespace(ty: Type, mod: *Module) ?*Module.Namespace {
+        return if (getNamespaceIndex(ty, mod).unwrap()) |i| mod.namespacePtr(i) else null;
+    }
+
     // Works for vectors and vectors of integers.
     pub fn minInt(ty: Type, arena: Allocator, mod: *Module) !Value {
         const scalar = try minIntScalar(ty.scalarType(mod), mod);
@@ -4911,78 +4900,81 @@ pub const Type = struct {
     }
 
     pub fn declSrcLocOrNull(ty: Type, mod: *Module) ?Module.SrcLoc {
-        if (ty.ip_index != .none) switch (mod.intern_pool.indexToKey(ty.ip_index)) {
-            .struct_type => @panic("TODO"),
-            .union_type => @panic("TODO"),
-            else => return null,
-        };
-        switch (ty.tag()) {
-            .enum_full, .enum_nonexhaustive => {
-                const enum_full = ty.cast(Payload.EnumFull).?.data;
-                return enum_full.srcLoc(mod);
-            },
-            .enum_numbered => {
-                const enum_numbered = ty.castTag(.enum_numbered).?.data;
-                return enum_numbered.srcLoc(mod);
-            },
-            .enum_simple => {
-                const enum_simple = ty.castTag(.enum_simple).?.data;
-                return enum_simple.srcLoc(mod);
-            },
-            .@"struct" => {
-                const struct_obj = ty.castTag(.@"struct").?.data;
-                return struct_obj.srcLoc(mod);
-            },
-            .error_set => {
-                const error_set = ty.castTag(.error_set).?.data;
-                return error_set.srcLoc(mod);
-            },
-            .@"union", .union_safety_tagged, .union_tagged => {
-                const union_obj = ty.cast(Payload.Union).?.data;
-                return union_obj.srcLoc(mod);
+        switch (ty.ip_index) {
+            .none => switch (ty.tag()) {
+                .enum_full, .enum_nonexhaustive => {
+                    const enum_full = ty.cast(Payload.EnumFull).?.data;
+                    return enum_full.srcLoc(mod);
+                },
+                .enum_numbered => {
+                    const enum_numbered = ty.castTag(.enum_numbered).?.data;
+                    return enum_numbered.srcLoc(mod);
+                },
+                .enum_simple => {
+                    const enum_simple = ty.castTag(.enum_simple).?.data;
+                    return enum_simple.srcLoc(mod);
+                },
+                .@"struct" => {
+                    const struct_obj = ty.castTag(.@"struct").?.data;
+                    return struct_obj.srcLoc(mod);
+                },
+                .error_set => {
+                    const error_set = ty.castTag(.error_set).?.data;
+                    return error_set.srcLoc(mod);
+                },
+                .@"union", .union_safety_tagged, .union_tagged => {
+                    const union_obj = ty.cast(Payload.Union).?.data;
+                    return union_obj.srcLoc(mod);
+                },
+
+                else => return null,
             },
-            .@"opaque" => {
-                const opaque_obj = ty.cast(Payload.Opaque).?.data;
-                return opaque_obj.srcLoc(mod);
+            else => return switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+                .struct_type => @panic("TODO"),
+                .union_type => @panic("TODO"),
+                .opaque_type => |opaque_type| mod.opaqueSrcLoc(opaque_type),
+                else => null,
             },
-
-            else => return null,
         }
     }
 
-    pub fn getOwnerDecl(ty: Type) Module.Decl.Index {
-        return ty.getOwnerDeclOrNull() orelse unreachable;
+    pub fn getOwnerDecl(ty: Type, mod: *Module) Module.Decl.Index {
+        return ty.getOwnerDeclOrNull(mod) orelse unreachable;
     }
 
-    pub fn getOwnerDeclOrNull(ty: Type) ?Module.Decl.Index {
-        switch (ty.tag()) {
-            .enum_full, .enum_nonexhaustive => {
-                const enum_full = ty.cast(Payload.EnumFull).?.data;
-                return enum_full.owner_decl;
-            },
-            .enum_numbered => return ty.castTag(.enum_numbered).?.data.owner_decl,
-            .enum_simple => {
-                const enum_simple = ty.castTag(.enum_simple).?.data;
-                return enum_simple.owner_decl;
-            },
-            .@"struct" => {
-                const struct_obj = ty.castTag(.@"struct").?.data;
-                return struct_obj.owner_decl;
-            },
-            .error_set => {
-                const error_set = ty.castTag(.error_set).?.data;
-                return error_set.owner_decl;
-            },
-            .@"union", .union_safety_tagged, .union_tagged => {
-                const union_obj = ty.cast(Payload.Union).?.data;
-                return union_obj.owner_decl;
+    pub fn getOwnerDeclOrNull(ty: Type, mod: *Module) ?Module.Decl.Index {
+        switch (ty.ip_index) {
+            .none => switch (ty.tag()) {
+                .enum_full, .enum_nonexhaustive => {
+                    const enum_full = ty.cast(Payload.EnumFull).?.data;
+                    return enum_full.owner_decl;
+                },
+                .enum_numbered => return ty.castTag(.enum_numbered).?.data.owner_decl,
+                .enum_simple => {
+                    const enum_simple = ty.castTag(.enum_simple).?.data;
+                    return enum_simple.owner_decl;
+                },
+                .@"struct" => {
+                    const struct_obj = ty.castTag(.@"struct").?.data;
+                    return struct_obj.owner_decl;
+                },
+                .error_set => {
+                    const error_set = ty.castTag(.error_set).?.data;
+                    return error_set.owner_decl;
+                },
+                .@"union", .union_safety_tagged, .union_tagged => {
+                    const union_obj = ty.cast(Payload.Union).?.data;
+                    return union_obj.owner_decl;
+                },
+
+                else => return null,
             },
-            .@"opaque" => {
-                const opaque_obj = ty.cast(Payload.Opaque).?.data;
-                return opaque_obj.owner_decl;
+            else => return switch (mod.intern_pool.indexToKey(ty.ip_index)) {
+                .struct_type => @panic("TODO"),
+                .union_type => @panic("TODO"),
+                .opaque_type => |opaque_type| opaque_type.decl,
+                else => null,
             },
-
-            else => return null,
         }
     }
 
@@ -5022,7 +5014,6 @@ pub const Type = struct {
         error_set_inferred,
         error_set_merged,
         empty_struct,
-        @"opaque",
         @"struct",
         @"union",
         union_safety_tagged,
@@ -5055,7 +5046,6 @@ pub const Type = struct {
                 .function => Payload.Function,
                 .error_union => Payload.ErrorUnion,
                 .error_set_single => Payload.Name,
-                .@"opaque" => Payload.Opaque,
                 .@"struct" => Payload.Struct,
                 .@"union", .union_safety_tagged, .union_tagged => Payload.Union,
                 .enum_full, .enum_nonexhaustive => Payload.EnumFull,
@@ -5336,11 +5326,6 @@ pub const Type = struct {
             data: *Module.Namespace,
         };
 
-        pub const Opaque = struct {
-            base: Payload = .{ .tag = .@"opaque" },
-            data: *Module.Opaque,
-        };
-
         pub const Struct = struct {
             base: Payload = .{ .tag = .@"struct" },
             data: *Module.Struct,