refactor(stdlib/S1.2): delete module_fns; dissolve legacy_direct_any [additive]

Delete module_fns as a separate function-author fact source. Its authors
already live in the module_decls raw facts, so lowerRetainedSameNameAuthors now
reads function authors straight out of module_decls (filtered to *FnDecl via
fnDeclOfRaw) — the same path → name → RawDeclRef store, fn-filtered. Remove
imports.ModuleFns / FnIndex / indexModuleFns / buildModuleFns / fnDeclOf, the
Compilation.module_fns field + its build + wiring, and ProgramIndex.module_fns.

Remove VisibilityMode.legacy_direct_any (the quarantined own-scope-plus-full-
import_graph mode): no production caller passed it, so the collectVisibleAuthors
and isVisible switch arms that handled it are dead and go too, collapsing
VisEdgeSet to the single flat-import walk. No semantic fallback is introduced;
import_graph stays the transitive-visibility source for findVisibleImpls.

Additive: the old maps stay active and lowering still consumes them — no
lowering consumer is cut over to the DeclTable (that is S3), and no resolution
behavior changes. Tests that drove the removed symbols are rerouted through
module_decls / the flat-edge walk.

Gate over the baseline-green corpus: zig build, zig build test (424/424),
bash tests/run_examples.sh (540 passed) — all exit 0; single-author output
byte-identical; multi-author 0722–0740 stdout/exit unchanged.
This commit is contained in:
agra
2026-06-09 11:36:04 +03:00
parent 8058be2538
commit cd7510067f
8 changed files with 98 additions and 180 deletions

View File

@@ -70,11 +70,11 @@ fn hasErr(diags: *const errors.DiagnosticList, needle: []const u8) bool {
}
// Two flat-imported modules each author `greet`; a third is namespaced. The
// step retains BOTH `greet` authors under their own paths in `module_fns` and
// records the namespaced import in `import_graph` but NOT in `flat_import_graph`
// — WITHOUT touching the merged scope: `mod.decls` stays byte-for-byte
// first-wins (one `greet`, a.sx's), exactly as on `wt-fix-0102-base`.
test "imports: module_fns retains same-name cross-module fns; flat_import_graph excludes namespaced edge" {
// raw facts retain BOTH `greet` authors under their own paths in `module_decls`
// (function authors flow through it) and record the namespaced import in
// `import_graph` but NOT in `flat_import_graph` — WITHOUT touching the merged
// scope: `mod.decls` stays byte-for-byte first-wins (one `greet`, a.sx's).
test "imports: module_decls retains same-name cross-module fns; flat_import_graph excludes namespaced edge" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();
const alloc = arena.allocator();
@@ -131,8 +131,7 @@ test "imports: module_fns retains same-name cross-module fns; flat_import_graph
.{},
);
var module_fns = imports.ModuleFns.init(alloc);
try imports.buildModuleFns(alloc, main_path, mod, &cache, &module_fns);
var facts = try imports.buildImportFacts(alloc, main_path, mod, &cache);
// The MERGED scope the first-wins resolver consumes is unchanged: mergeFlat
// still drops the second `greet`, so `mod.decls` carries exactly ONE — and
@@ -147,12 +146,18 @@ test "imports: module_fns retains same-name cross-module fns; flat_import_graph
}
try std.testing.expectEqual(@as(usize, 1), greet_count);
// module_fns retains BOTH authors of `greet`, keyed by their own paths —
// module_decls retains BOTH authors of `greet`, keyed by their own paths —
// the dropped author is recorded here (side index), not in the merged scope.
const a_fns = module_fns.get(a_path) orelse return error.MissingAFns;
const b_fns = module_fns.get(b_path) orelse return error.MissingBFns;
const a_greet = a_fns.get("greet") orelse return error.MissingAGreet;
const b_greet = b_fns.get("greet") orelse return error.MissingBGreet;
const a_idx = facts.decls.get(a_path) orelse return error.MissingAIndex;
const b_idx = facts.decls.get(b_path) orelse return error.MissingBIndex;
const a_greet = switch (a_idx.names.get("greet") orelse return error.MissingAGreet) {
.fn_decl => |fd| fd,
else => return error.AGreetNotFn,
};
const b_greet = switch (b_idx.names.get("greet") orelse return error.MissingBGreet) {
.fn_decl => |fd| fd,
else => return error.BGreetNotFn,
};
// Distinct authoring decls — not the same node deduped down to one.
try std.testing.expect(a_greet != b_greet);
// First-wins: the surviving merged-scope `greet` is a.sx's author.
@@ -175,9 +180,9 @@ test "imports: module_fns retains same-name cross-module fns; flat_import_graph
// Mixed collision: a.sx authors `Widget` as a STRUCT (non-fn), b.sx authors it
// as a FUNCTION. fix-0102a must NOT let the function-author retention shift the
// merged scope — first-wins keeps a.sx's struct and drops b.sx's function,
// exactly as on `wt-fix-0102-base`. (The fn author may still be indexed in
// module_fns; resolution is what must be untouched.)
// merged scope — first-wins keeps a.sx's struct and drops b.sx's function.
// (The fn author may still be indexed in `module_decls`; resolution is what
// must be untouched.)
test "imports: mixed non-fn/fn same-name collision stays first-wins in merged scope" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();