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

@@ -986,10 +986,10 @@ pub const Lowering = struct {
// Declare extern stub for all functions (bodies lowered
// lazily). Key the identity map (`fn_decl_fids`, inside
// `declareFunction`) by the STABLE AST field pointer the
// same `&decl.data.fn_decl` stored in `fn_ast_map` and
// `module_fns` not the switch-capture copy `fd`, whose
// address is a per-iteration stack temporary that no later
// decl-identity lookup can reproduce.
// same `&decl.data.fn_decl` stored in `fn_ast_map` and the
// `module_decls` raw facts not the switch-capture copy `fd`,
// whose address is a per-iteration stack temporary that no
// later decl-identity lookup can reproduce.
self.declareFunction(&decl.data.fn_decl, fd.name);
},
.const_decl => |cd| {
@@ -1741,35 +1741,36 @@ pub const Lowering = struct {
/// The first-wins flat/directory merge keeps exactly one author per name in
/// the merged decl list; `scanDecls` declares that WINNER (lowered on demand
/// through the name-keyed `lazyLowerFunction`). fix-0102a retained every
/// dropped same-name author in `module_fns` (path name `*FnDecl`) without
/// touching resolution; this walks that index and gives each shadowed author
/// its own slot: `declareFunction` (identity-mapped to a fresh same-name
/// FuncId) + `lowerFunctionBodyInto` (its body, in its own module's
/// dropped same-name author in the `module_decls` raw facts (path name
/// `RawDeclRef`) without touching resolution; this walks that index, filters
/// each author to its `*FnDecl` (`fnDeclOfRaw`), and gives each shadowed
/// author its own slot: `declareFunction` (identity-mapped to a fresh
/// same-name FuncId) + `lowerFunctionBodyInto` (its body, in its own module's
/// visibility context). Two same-name authors then carry distinct FuncIds and
/// distinct bodies, while `resolveFuncByName` still returns the first (winner)
/// author so existing calls bind first-wins.
///
/// Scoped to DIRECT flat imports of the main file: a `module_fns` entry whose
/// path is the main file or one of its bare `#import` edges. A namespaced
/// (`ns :: #import`) author has no bare-name winner and is excluded both by
/// that flat-edge gate and by the `fn_ast_map` winner lookup below.
/// Scoped to DIRECT flat imports of the main file: a `module_decls` entry
/// whose path is the main file or one of its bare `#import` edges. A
/// namespaced (`ns :: #import`) author has no bare-name winner and is excluded
/// both by that flat-edge gate and by the `fn_ast_map` winner lookup below.
pub fn lowerRetainedSameNameAuthors(self: *Lowering) void {
const module_fns = self.program_index.module_fns orelse return;
const module_decls = self.program_index.module_decls orelse return;
const main_file = self.main_file orelse return;
const flat_graph = self.program_index.flat_import_graph orelse return;
const main_flat_edges = flat_graph.get(main_file);
var path_it = module_fns.iterator();
var path_it = module_decls.iterator();
while (path_it.next()) |path_entry| {
const path = path_entry.key_ptr.*;
const is_eligible = std.mem.eql(u8, path, main_file) or
(main_flat_edges != null and main_flat_edges.?.contains(path));
if (!is_eligible) continue;
var fn_it = path_entry.value_ptr.iterator();
var fn_it = path_entry.value_ptr.names.iterator();
while (fn_it.next()) |fn_entry| {
const name = fn_entry.key_ptr.*;
const fd = fn_entry.value_ptr.*;
const fd = fnDeclOfRaw(fn_entry.value_ptr.*) orelse continue;
// A name with no bare winner is namespaced-only (`ns.fn`) it
// never participated in the flat merge, so it has no shadow to
@@ -1871,18 +1872,18 @@ pub const Lowering = struct {
/// THE plain bare-name call selector (fix-0102c, R5 §C). `resolveBareCallee`'s
/// body verbatim, now over the Phase B author collector
/// (`resolver.collectVisibleAuthors` the ONE graph-walk) instead of a direct
/// `module_fns` + `flat_import_graph` traversal. Routes a bare identifier call
/// `name` from `caller_file` to the right same-name author when flat imports
/// introduce a genuine collision. Every single-author / local / parameter /
/// std / qualified name resolves through the EXISTING path unchanged: the
/// selector returns `.none` whenever the outcome would match first-wins, so
/// nothing on the common path is perturbed.
/// `module_decls` + `flat_import_graph` traversal. Routes a bare identifier
/// call `name` from `caller_file` to the right same-name author when flat
/// imports introduce a genuine collision. Every single-author / local /
/// parameter / std / qualified name resolves through the EXISTING path
/// unchanged: the selector returns `.none` whenever the outcome would match
/// first-wins, so nothing on the common path is perturbed.
///
/// The collector returns RAW authors across ALL decl domains; this selector
/// reproduces `module_fns`' fn-only view by filtering each author through
/// `fnDeclOfRaw` (a `const`-wrapped fn unwraps to its inner fn the exact
/// `*FnDecl` `module_fns` stored; every other domain drops out), preserving
/// resolveBareCallee's negative space byte-for-byte.
/// reproduces a fn-only author view by filtering each author through
/// `fnDeclOfRaw` (a `const`-wrapped fn unwraps to its inner fn; every other
/// domain drops out), preserving resolveBareCallee's negative space
/// byte-for-byte.
///
/// - **own-author wins**: if `caller_file` authors `name` as a fn and the
/// bare-name first-wins winner is a DIFFERENT author, select the caller's
@@ -1910,8 +1911,8 @@ pub const Lowering = struct {
// own-author wins. The collector's `own` spans all domains; a non-fn
// (or a const not bound to a function) means `caller_file` has no fn
// `name` fall through to the flat authors, exactly as the fn-only
// `module_fns` walk did.
// `name` fall through to the flat authors, exactly as a fn-only walk
// would.
if (set.own) |own_author| {
if (fnDeclOfRaw(own_author.raw)) |own| {
if (winner != null and winner.? == own) return .none;
@@ -2378,10 +2379,10 @@ pub const Lowering = struct {
}
/// The `*FnDecl` a raw author wraps, or null when the author is not a
/// function `imports.fnDeclOf` over a `RawDeclRef` so the collector's
/// all-domain authors reproduce `module_fns`' fn-only view (a `const`-wrapped
/// fn unwraps to its inner fn, the same pointer `module_fns` holds; every
/// other domain null).
/// function unwraps a `RawDeclRef` so the collector's all-domain authors
/// yield a fn-only view (a `const`-wrapped fn unwraps to its inner fn; every
/// other domain null). The single place function authors are read out of
/// the `module_decls` raw facts.
fn fnDeclOfRaw(ref: resolver_mod.RawDeclRef) ?*const ast.FnDecl {
return switch (ref) {
.fn_decl => |fd| fd,
@@ -2633,27 +2634,20 @@ pub const Lowering = struct {
const fd = self.program_index.fn_ast_map.get(name) orelse return true;
if (fd.body.data != .foreign_expr) return true;
if (fd.body.data.foreign_expr.library_ref != null) return true;
return self.visibleOverEdges(name, .flat);
return self.visibleOverEdges(name);
},
.user_bare_flat => return self.visibleOverEdges(name, .flat),
.legacy_direct_any => return self.visibleOverEdges(name, .all),
.user_bare_flat => return self.visibleOverEdges(name),
}
}
const VisEdgeSet = enum { flat, all };
/// Resolve the mode's edge set and run the per-file visibility walk. Falls
/// Run the per-file visibility walk over the flat-import edge set. Falls
/// open (visible) when the scoping infrastructure isn't wired (comptime
/// callers, directory imports without main_file, etc.). The caller is
/// responsible for restricting the check to names that ARE known top-level
/// decls; otherwise every local variable would be policed.
fn visibleOverEdges(self: *Lowering, name: []const u8, edges: VisEdgeSet) bool {
fn visibleOverEdges(self: *Lowering, name: []const u8) bool {
const source = self.current_source_file orelse return true;
const graph = switch (edges) {
.flat => self.program_index.flat_import_graph,
.all => self.program_index.import_graph,
};
return nameVisibleOverEdges(self.program_index.module_scopes, graph, source, name);
return nameVisibleOverEdges(self.program_index.module_scopes, self.program_index.flat_import_graph, source, name);
}
/// Check if a C-imported function is visible from the current source file.