fix(KB-9): move Lowering/ProgramIndex maps off page_allocator defaults

16 Lowering map fields and 8 ProgramIndex map fields were declared with
`= ....init(std.heap.page_allocator)` field defaults that init() never
replaced — every instance really allocated page-at-a-time outside the
compilation allocator, invisible to leak checking and never reclaimed.

All 24 now init explicitly with the compilation allocator (module.alloc
/ the init alloc param), which is arena-backed in both the driver
(main's arena) and the test suites (per-test arenas), so backing is
reclaimed at teardown. ProgramIndex's struct doc no longer claims the
page_allocator defaults.

Six lower.test.zig tests that constructed Module with bare
std.testing.allocator leaked once the checker could finally see these
maps; they now use the same per-test ArenaAllocator idiom as the rest
of the file and the facade test suites.

Gate: zig build OK; zig build test 426/426 (6/6 steps, leak-clean);
run_examples 541/0; zero expected/ snapshot churn.
This commit is contained in:
agra
2026-06-10 16:19:52 +03:00
parent 82500931ce
commit 5b304a29c1
3 changed files with 69 additions and 33 deletions

View File

@@ -288,21 +288,21 @@ pub const Lowering = struct {
/// visible ONLY within the source that declares it: an imported template's field
/// resolution (run in the template's source context, E3 attempt-4) must NOT bind a
/// name the CALLER declared block-local (E3 attempt-5).
local_type_names: std.StringHashMap(std.StringHashMap(void)) = std.StringHashMap(std.StringHashMap(void)).init(std.heap.page_allocator),
struct_defaults_map: std.StringHashMap([]const ?*const Node) = std.StringHashMap([]const ?*const Node).init(std.heap.page_allocator), // struct name → field defaults
struct_instance_bindings: std.StringHashMap(std.StringHashMap(TypeId)) = std.StringHashMap(std.StringHashMap(TypeId)).init(std.heap.page_allocator), // mangled struct name → type param bindings
struct_instance_template: std.StringHashMap([]const u8) = std.StringHashMap([]const u8).init(std.heap.page_allocator), // mangled struct name → template name
struct_instance_author: std.StringHashMap(*const ast.StructDecl) = std.StringHashMap(*const ast.StructDecl).init(std.heap.page_allocator), // mangled struct name → authoring StructDecl (CP-2: body-author ≡ layout-author)
local_type_names: std.StringHashMap(std.StringHashMap(void)),
struct_defaults_map: std.StringHashMap([]const ?*const Node), // struct name → field defaults
struct_instance_bindings: std.StringHashMap(std.StringHashMap(TypeId)), // mangled struct name → type param bindings
struct_instance_template: std.StringHashMap([]const u8), // mangled struct name → template name
struct_instance_author: std.StringHashMap(*const ast.StructDecl), // mangled struct name → authoring StructDecl (CP-2: body-author ≡ layout-author)
comptime_value_bindings: ?std.StringHashMap(i64) = null, // comptime value bindings ($N → integer value)
protocol_thunk_map: std.StringHashMap([]const FuncId) = std.StringHashMap([]const FuncId).init(std.heap.page_allocator), // "Proto\x00Type" → thunk FuncIds
protocol_vtable_type_map: std.StringHashMap(TypeId) = std.StringHashMap(TypeId).init(std.heap.page_allocator), // protocol name → vtable struct TypeId
protocol_vtable_global_map: std.StringHashMap(inst_mod.GlobalId) = std.StringHashMap(inst_mod.GlobalId).init(std.heap.page_allocator), // "Proto\x00Type" → vtable GlobalId
param_impl_map: std.StringHashMap(std.ArrayList(ParamImplEntry)) = std.StringHashMap(std.ArrayList(ParamImplEntry)).init(std.heap.page_allocator), // "Proto\x00<arg_mangled>\x00<src_mangled>" → impl entries (parameterised protocols only; list lets Phase 4/5 detect cross-module overlap)
protocol_thunk_map: std.StringHashMap([]const FuncId), // "Proto\x00Type" → thunk FuncIds
protocol_vtable_type_map: std.StringHashMap(TypeId), // protocol name → vtable struct TypeId
protocol_vtable_global_map: std.StringHashMap(inst_mod.GlobalId), // "Proto\x00Type" → vtable GlobalId
param_impl_map: std.StringHashMap(std.ArrayList(ParamImplEntry)), // "Proto\x00<arg_mangled>\x00<src_mangled>" → impl entries (parameterised protocols only; list lets Phase 4/5 detect cross-module overlap)
/// Pack-variadic impl entries — separate map keyed by `"Proto\x00<arg_mangled>"`
/// (NO source suffix) so a single impl `Closure(..$args) -> $R` can be
/// matched against many concrete source shapes. Concrete impls in
/// `param_impl_map` win when both match (specificity rule).
param_impl_pack_map: std.StringHashMap(std.ArrayList(PackParamImplEntry)) = std.StringHashMap(std.ArrayList(PackParamImplEntry)).init(std.heap.page_allocator),
param_impl_pack_map: std.StringHashMap(std.ArrayList(PackParamImplEntry)),
/// Active pack bindings during monomorphisation. Mirrors `type_bindings`
/// but for variadic pack names: `args → [T1, T2, ...]`. Read by
/// `resolveTypeWithBindings` on closure_type_expr to substitute
@@ -346,26 +346,26 @@ pub const Lowering = struct {
/// `xs[i].<m>` is rejected unless `<m>` is one of the protocol's methods.
/// Null / absent for the comptime `..$args` pack (no constraint).
pack_constraint: ?std.StringHashMap([]const u8) = null,
struct_const_map: std.StringHashMap(StructConstInfo) = std.StringHashMap(StructConstInfo).init(std.heap.page_allocator), // "Struct.CONST" → value info
foreign_name_map: std.StringHashMap([]const u8) = std.StringHashMap([]const u8).init(std.heap.page_allocator), // sx name → C name for #foreign renames
struct_const_map: std.StringHashMap(StructConstInfo), // "Struct.CONST" → value info
foreign_name_map: std.StringHashMap([]const u8), // sx name → C name for #foreign renames
target_config: ?@import("../target.zig").TargetConfig = null, // compilation target (for inline if)
comptime_constants: std.StringHashMap(ComptimeValue) = std.StringHashMap(ComptimeValue).init(std.heap.page_allocator), // compile-time known constants (e.g. OS, ARCH)
comptime_constants: std.StringHashMap(ComptimeValue), // compile-time known constants (e.g. OS, ARCH)
diagnostics: ?*errors.DiagnosticList = null, // error reporting with source locations
xx_reentrancy: std.AutoHashMap(u64, void) = std.AutoHashMap(u64, void).init(std.heap.page_allocator), // (src_ty, dst_ty) pairs currently being resolved through user-space Into; prevents infinite monomorphisation when a convert body re-enters the same xx
xx_reentrancy: std.AutoHashMap(u64, void), // (src_ty, dst_ty) pairs currently being resolved through user-space Into; prevents infinite monomorphisation when a convert body re-enters the same xx
/// Whole-program-converged inferred error sets (ERR E1.4b): top-level
/// bare-`!` function name → its sorted escape-tag ids (literal raises +
/// pure-failable `try` edges, fix-pointed across the call graph). The
/// shared `!` placeholder TypeId stays empty; this side map holds the real
/// per-function sets (sidesteps the name-only error-set interning). Read by
/// `lowerTry`'s named-caller widening and the empty-inferred warning.
inferred_error_sets: std.StringHashMap([]const u32) = std.StringHashMap([]const u32).init(std.heap.page_allocator),
inferred_error_sets: std.StringHashMap([]const u32),
/// Whole-program-converged inferred error sets keyed by closure/function
/// VALUE-signature shape (ERR E5.1 sub-feature 2): every occurrence of
/// `Closure(<sig>) -> (T, !)` with a structurally identical value-signature
/// shares one node; each bare-`!` closure literal of that shape unions its
/// escape tags in. Read by `checkEscapeWidening` when a `try` operand is a
/// closure/fn-type SLOT call (no static fn name). Key = `closureShapeKey`.
shape_inferred_sets: std.StringHashMap([]const u32) = std.StringHashMap([]const u32).init(std.heap.page_allocator),
shape_inferred_sets: std.StringHashMap([]const u32),
pub const ComptimeValue = union(enum) {
int_val: i64,
@@ -496,6 +496,22 @@ pub const Lowering = struct {
.fn_decl_fids = std.AutoHashMap(*const ast.FnDecl, FuncId).init(module.alloc),
.lowered_fids = std.AutoHashMap(FuncId, void).init(module.alloc),
.nominal_name_authors = std.AutoHashMap(types.StringId, []const u8).init(module.alloc),
.local_type_names = std.StringHashMap(std.StringHashMap(void)).init(module.alloc),
.struct_defaults_map = std.StringHashMap([]const ?*const Node).init(module.alloc),
.struct_instance_bindings = std.StringHashMap(std.StringHashMap(TypeId)).init(module.alloc),
.struct_instance_template = std.StringHashMap([]const u8).init(module.alloc),
.struct_instance_author = std.StringHashMap(*const ast.StructDecl).init(module.alloc),
.protocol_thunk_map = std.StringHashMap([]const FuncId).init(module.alloc),
.protocol_vtable_type_map = std.StringHashMap(TypeId).init(module.alloc),
.protocol_vtable_global_map = std.StringHashMap(inst_mod.GlobalId).init(module.alloc),
.param_impl_map = std.StringHashMap(std.ArrayList(ParamImplEntry)).init(module.alloc),
.param_impl_pack_map = std.StringHashMap(std.ArrayList(PackParamImplEntry)).init(module.alloc),
.struct_const_map = std.StringHashMap(StructConstInfo).init(module.alloc),
.foreign_name_map = std.StringHashMap([]const u8).init(module.alloc),
.comptime_constants = std.StringHashMap(ComptimeValue).init(module.alloc),
.xx_reentrancy = std.AutoHashMap(u64, void).init(module.alloc),
.inferred_error_sets = std.StringHashMap([]const u32).init(module.alloc),
.shape_inferred_sets = std.StringHashMap([]const u32).init(module.alloc),
.program_index = ProgramIndex.init(module.alloc),
};
}