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:
10
src/core.zig
10
src/core.zig
@@ -29,9 +29,6 @@ pub const Compilation = struct {
|
|||||||
/// Flat-only subset of `import_graph` (bare `#import` edges, no namespaced
|
/// Flat-only subset of `import_graph` (bare `#import` edges, no namespaced
|
||||||
/// `ns :: #import`). Borrowed by `ProgramIndex.flat_import_graph`.
|
/// `ns :: #import`). Borrowed by `ProgramIndex.flat_import_graph`.
|
||||||
flat_import_graph: std.StringHashMap(std.StringHashMap(void)),
|
flat_import_graph: std.StringHashMap(std.StringHashMap(void)),
|
||||||
/// Per-module authored-function index (`path → name → *const FnDecl`).
|
|
||||||
/// Borrowed by `ProgramIndex.module_fns`.
|
|
||||||
module_fns: imports.ModuleFns,
|
|
||||||
/// Per-module scalar raw-decl index (`path → name → RawDeclRef`), built by
|
/// Per-module scalar raw-decl index (`path → name → RawDeclRef`), built by
|
||||||
/// `imports.buildImportFacts`. Borrowed by `ProgramIndex.module_decls`.
|
/// `imports.buildImportFacts`. Borrowed by `ProgramIndex.module_decls`.
|
||||||
module_decls: imports.ModuleDecls,
|
module_decls: imports.ModuleDecls,
|
||||||
@@ -69,7 +66,6 @@ pub const Compilation = struct {
|
|||||||
.module_scopes = std.StringHashMap(std.StringHashMap(void)).init(allocator),
|
.module_scopes = std.StringHashMap(std.StringHashMap(void)).init(allocator),
|
||||||
.import_graph = std.StringHashMap(std.StringHashMap(void)).init(allocator),
|
.import_graph = std.StringHashMap(std.StringHashMap(void)).init(allocator),
|
||||||
.flat_import_graph = std.StringHashMap(std.StringHashMap(void)).init(allocator),
|
.flat_import_graph = std.StringHashMap(std.StringHashMap(void)).init(allocator),
|
||||||
.module_fns = imports.ModuleFns.init(allocator),
|
|
||||||
.module_decls = imports.ModuleDecls.init(allocator),
|
.module_decls = imports.ModuleDecls.init(allocator),
|
||||||
.namespace_edges = imports.NamespaceEdges.init(allocator),
|
.namespace_edges = imports.NamespaceEdges.init(allocator),
|
||||||
.decl_table = imports.DeclTable.init(allocator),
|
.decl_table = imports.DeclTable.init(allocator),
|
||||||
@@ -133,11 +129,6 @@ pub const Compilation = struct {
|
|||||||
self.module_scopes.put(entry.key_ptr.*, entry.value_ptr.scope) catch {};
|
self.module_scopes.put(entry.key_ptr.*, entry.value_ptr.scope) catch {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Per-module authored-function index, built from the SAME modules as
|
|
||||||
// `module_scopes` (main + every cache entry). Keyed by path; same-name
|
|
||||||
// cross-module authors stay distinct under their own paths.
|
|
||||||
imports.buildModuleFns(self.allocator, self.file_path, mod, &cache, &self.module_fns) catch {};
|
|
||||||
|
|
||||||
// Raw import facts (the unified-resolver store): scalar per-module
|
// Raw import facts (the unified-resolver store): scalar per-module
|
||||||
// raw-decl index + namespace edges, built from the SAME modules. Nothing
|
// raw-decl index + namespace edges, built from the SAME modules. Nothing
|
||||||
// consumes these yet — they are borrowed by `ProgramIndex` for later
|
// consumes these yet — they are borrowed by `ProgramIndex` for later
|
||||||
@@ -320,7 +311,6 @@ pub const Compilation = struct {
|
|||||||
lowering.program_index.module_scopes = &self.module_scopes;
|
lowering.program_index.module_scopes = &self.module_scopes;
|
||||||
lowering.program_index.import_graph = &self.import_graph;
|
lowering.program_index.import_graph = &self.import_graph;
|
||||||
lowering.program_index.flat_import_graph = &self.flat_import_graph;
|
lowering.program_index.flat_import_graph = &self.flat_import_graph;
|
||||||
lowering.program_index.module_fns = &self.module_fns;
|
|
||||||
lowering.program_index.module_decls = &self.module_decls;
|
lowering.program_index.module_decls = &self.module_decls;
|
||||||
lowering.program_index.namespace_edges = &self.namespace_edges;
|
lowering.program_index.namespace_edges = &self.namespace_edges;
|
||||||
lowering.program_index.decl_table = &self.decl_table;
|
lowering.program_index.decl_table = &self.decl_table;
|
||||||
|
|||||||
@@ -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
|
// 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
|
// raw facts retain BOTH `greet` authors under their own paths in `module_decls`
|
||||||
// records the namespaced import in `import_graph` but NOT in `flat_import_graph`
|
// (function authors flow through it) and record the namespaced import in
|
||||||
// — WITHOUT touching the merged scope: `mod.decls` stays byte-for-byte
|
// `import_graph` but NOT in `flat_import_graph` — WITHOUT touching the merged
|
||||||
// first-wins (one `greet`, a.sx's), exactly as on `wt-fix-0102-base`.
|
// scope: `mod.decls` stays byte-for-byte first-wins (one `greet`, a.sx's).
|
||||||
test "imports: module_fns retains same-name cross-module fns; flat_import_graph excludes namespaced edge" {
|
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);
|
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
const alloc = arena.allocator();
|
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);
|
var facts = try imports.buildImportFacts(alloc, main_path, mod, &cache);
|
||||||
try imports.buildModuleFns(alloc, main_path, mod, &cache, &module_fns);
|
|
||||||
|
|
||||||
// The MERGED scope the first-wins resolver consumes is unchanged: mergeFlat
|
// The MERGED scope the first-wins resolver consumes is unchanged: mergeFlat
|
||||||
// still drops the second `greet`, so `mod.decls` carries exactly ONE — and
|
// 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);
|
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.
|
// 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 a_idx = facts.decls.get(a_path) orelse return error.MissingAIndex;
|
||||||
const b_fns = module_fns.get(b_path) orelse return error.MissingBFns;
|
const b_idx = facts.decls.get(b_path) orelse return error.MissingBIndex;
|
||||||
const a_greet = a_fns.get("greet") orelse return error.MissingAGreet;
|
const a_greet = switch (a_idx.names.get("greet") orelse return error.MissingAGreet) {
|
||||||
const b_greet = b_fns.get("greet") orelse return error.MissingBGreet;
|
.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.
|
// Distinct authoring decls — not the same node deduped down to one.
|
||||||
try std.testing.expect(a_greet != b_greet);
|
try std.testing.expect(a_greet != b_greet);
|
||||||
// First-wins: the surviving merged-scope `greet` is a.sx's author.
|
// 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
|
// 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
|
// 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,
|
// 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
|
// (The fn author may still be indexed in `module_decls`; resolution is what
|
||||||
// module_fns; resolution is what must be untouched.)
|
// must be untouched.)
|
||||||
test "imports: mixed non-fn/fn same-name collision stays first-wins in merged scope" {
|
test "imports: mixed non-fn/fn same-name collision stays first-wins in merged scope" {
|
||||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
|
|||||||
@@ -5,18 +5,6 @@ const errors = @import("errors.zig");
|
|||||||
const c_import = @import("c_import.zig");
|
const c_import = @import("c_import.zig");
|
||||||
const Node = ast.Node;
|
const Node = ast.Node;
|
||||||
|
|
||||||
/// The `*const ast.FnDecl` a function-authoring decl carries, or null when the
|
|
||||||
/// decl is not a function — either a bare `fn_decl` (`f :: (…) -> T { … }`) or a
|
|
||||||
/// `const_decl` whose value is a function. Drives the per-module `module_fns`
|
|
||||||
/// identity index (fix-0102a).
|
|
||||||
fn fnDeclOf(decl: *const Node) ?*const ast.FnDecl {
|
|
||||||
return switch (decl.data) {
|
|
||||||
.fn_decl => &decl.data.fn_decl,
|
|
||||||
.const_decl => |cd| if (cd.value.data == .fn_decl) &cd.value.data.fn_decl else null,
|
|
||||||
else => null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Comptime evaluation context for the inline-if hoisting pass below.
|
/// Comptime evaluation context for the inline-if hoisting pass below.
|
||||||
/// Mirrors the values `injectComptimeConstants` will later push into the
|
/// Mirrors the values `injectComptimeConstants` will later push into the
|
||||||
/// lowering's `comptime_constants` map (OS / ARCH / POINTER_SIZE), but
|
/// lowering's `comptime_constants` map (OS / ARCH / POINTER_SIZE), but
|
||||||
@@ -451,50 +439,15 @@ pub const ResolvedModule = struct {
|
|||||||
/// Module cache: maps resolved file paths to their ResolvedModules.
|
/// Module cache: maps resolved file paths to their ResolvedModules.
|
||||||
pub const ModuleCache = std.StringHashMap(ResolvedModule);
|
pub const ModuleCache = std.StringHashMap(ResolvedModule);
|
||||||
|
|
||||||
/// Per-module function identity index: function NAME → the `*const FnDecl` that
|
// ── Raw import facts (the unified-resolver store) ──
|
||||||
/// module AUTHORS. Mirrors a single module's slice of `module_scopes`.
|
|
||||||
pub const FnIndex = std.StringHashMap(*const ast.FnDecl);
|
|
||||||
|
|
||||||
/// `path → name → *const FnDecl`, mirroring `module_scopes`. One entry per
|
|
||||||
/// resolved module keyed by its path (a directory's combined module keyed by
|
|
||||||
/// `dir_path`); each entry indexes only what that module AUTHORS. Two modules
|
|
||||||
/// each authoring `f` are retained under their own paths — the identity index
|
|
||||||
/// fix-0102c's bare-name disambiguation consults to bind a flat call to the
|
|
||||||
/// right author.
|
|
||||||
pub const ModuleFns = std.StringHashMap(FnIndex);
|
|
||||||
|
|
||||||
/// Index a single module's authored functions (`own_decls`) into `out[path]`.
|
|
||||||
/// First-wins WITHIN a module mirrors the scan pass; cross-module same-name
|
|
||||||
/// authors live under their own `path` keys.
|
|
||||||
fn indexModuleFns(allocator: std.mem.Allocator, out: *ModuleFns, path: []const u8, own_decls: []const *Node) !void {
|
|
||||||
const gop = try out.getOrPut(path);
|
|
||||||
if (!gop.found_existing) gop.value_ptr.* = FnIndex.init(allocator);
|
|
||||||
for (own_decls) |decl| {
|
|
||||||
const fd = fnDeclOf(decl) orelse continue;
|
|
||||||
const name = decl.data.declName() orelse continue;
|
|
||||||
if (gop.value_ptr.contains(name)) continue;
|
|
||||||
try gop.value_ptr.put(name, fd);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Build the per-module function index from a resolved program: the main module
|
|
||||||
/// (keyed by `main_path`) plus every cached module (keyed by its own path).
|
|
||||||
/// Mirrors how `core.zig` fills `module_scopes` from `mod.scope` + the cache.
|
|
||||||
pub fn buildModuleFns(allocator: std.mem.Allocator, main_path: []const u8, main_mod: ResolvedModule, cache: *const ModuleCache, out: *ModuleFns) !void {
|
|
||||||
try indexModuleFns(allocator, out, main_path, main_mod.own_decls);
|
|
||||||
var it = cache.iterator();
|
|
||||||
while (it.next()) |entry| {
|
|
||||||
try indexModuleFns(allocator, out, entry.key_ptr.*, entry.value_ptr.own_decls);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Raw import facts (the unified-resolver store; nothing consumes it yet) ──
|
|
||||||
//
|
//
|
||||||
// `buildImportFacts` produces two source-keyed views over the resolved program,
|
// `buildImportFacts` produces two source-keyed views over the resolved program,
|
||||||
// callable WITHOUT IR lowering (the LSP reuses it later): a scalar per-module
|
// callable WITHOUT IR lowering (the LSP reuses it later): a scalar per-module
|
||||||
// raw-decl index (`name → RawDeclRef`) and the namespace import edges
|
// raw-decl index (`name → RawDeclRef`) and the namespace import edges
|
||||||
// (`importer → alias → NamespaceTarget`). Both are built from each module's
|
// (`importer → alias → NamespaceTarget`). Both are built from each module's
|
||||||
// `own_decls` — exactly the same modules `buildModuleFns` walks.
|
// `own_decls` (the main module plus every cache entry). Function authors are
|
||||||
|
// read out of `name → RawDeclRef` directly (`fnDeclOfRaw`), so there is no
|
||||||
|
// separate function-only index.
|
||||||
|
|
||||||
/// A named top-level declaration the resolver may select, kept as the raw AST
|
/// A named top-level declaration the resolver may select, kept as the raw AST
|
||||||
/// node pointer (NOT pre-classified — a `const_decl` whose value is a function
|
/// node pointer (NOT pre-classified — a `const_decl` whose value is a function
|
||||||
@@ -566,9 +519,9 @@ pub fn rawDeclRefOf(decl: *const Node) ?RawDeclRef {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Index one module's authored decls (`own_decls`) into `decls[path]` and record
|
/// Index one module's authored decls (`own_decls`) into `decls[path]` and record
|
||||||
/// any namespace aliases into `ns_edges[path]`. First-wins WITHIN a module
|
/// any namespace aliases into `ns_edges[path]`. First-wins WITHIN a module;
|
||||||
/// mirrors `indexModuleFns`; `own_decls` is already name-deduped by `addOwnDecl`,
|
/// `own_decls` is already name-deduped by `addOwnDecl`, so the first-wins guard
|
||||||
/// so the first-wins guard never actually fires here.
|
/// never actually fires here.
|
||||||
fn indexModuleDecls(
|
fn indexModuleDecls(
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
decls: *ModuleDecls,
|
decls: *ModuleDecls,
|
||||||
@@ -601,8 +554,8 @@ fn indexModuleDecls(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Build the raw import facts from a resolved program: the main module (keyed by
|
/// Build the raw import facts from a resolved program: the main module (keyed by
|
||||||
/// `main_path`) plus every cached module (keyed by its own path). The same module
|
/// `main_path`) plus every cached module (keyed by its own path). No IR lowering
|
||||||
/// set `buildModuleFns` walks. No IR lowering required.
|
/// required.
|
||||||
pub fn buildImportFacts(
|
pub fn buildImportFacts(
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
main_path: []const u8,
|
main_path: []const u8,
|
||||||
|
|||||||
@@ -1291,7 +1291,7 @@ fn countRealBodies(module: *ir_mod.Module, name: []const u8) usize {
|
|||||||
|
|
||||||
// fix-0102b: two flat-imported modules each author `greet`. The first-wins merge
|
// fix-0102b: two flat-imported modules each author `greet`. The first-wins merge
|
||||||
// keeps a.sx's author in the merged decl list (the WINNER) and drops b.sx's,
|
// keeps a.sx's author in the merged decl list (the WINNER) and drops b.sx's,
|
||||||
// which `module_fns` still retains (0102a). `main` itself can't bare-call `greet`
|
// which the `module_decls` raw facts still retain (0102a). `main` itself can't bare-call `greet`
|
||||||
// — under fix-0102c two flat authors make that ambiguous — so it calls a.sx's
|
// — under fix-0102c two flat authors make that ambiguous — so it calls a.sx's
|
||||||
// `use_greet` wrapper, whose own-author call to `greet` binds a.sx's winner.
|
// `use_greet` wrapper, whose own-author call to `greet` binds a.sx's winner.
|
||||||
// BEFORE the identity-addressable pass, only the winner has a real body — the
|
// BEFORE the identity-addressable pass, only the winner has a real body — the
|
||||||
@@ -1358,11 +1358,9 @@ test "lower: shadowed same-name author gets its own FuncId + real body (fix-0102
|
|||||||
while (cache_it.next()) |entry| {
|
while (cache_it.next()) |entry| {
|
||||||
try module_scopes.put(entry.key_ptr.*, entry.value_ptr.scope);
|
try module_scopes.put(entry.key_ptr.*, entry.value_ptr.scope);
|
||||||
}
|
}
|
||||||
var module_fns = imports.ModuleFns.init(alloc);
|
// Phase A raw facts: both `selectPlainCallableAuthor` (Phase C) and
|
||||||
try imports.buildModuleFns(alloc, main_path, mod, &cache, &module_fns);
|
// `lowerRetainedSameNameAuthors` read function authors out of `module_decls`.
|
||||||
|
// Wired exactly as `core.zig` does.
|
||||||
// Phase A raw facts: `selectPlainCallableAuthor` (Phase C) collects authors
|
|
||||||
// over `module_decls`, not `module_fns`. Wired exactly as `core.zig` does.
|
|
||||||
var facts = try imports.buildImportFacts(alloc, main_path, mod, &cache);
|
var facts = try imports.buildImportFacts(alloc, main_path, mod, &cache);
|
||||||
|
|
||||||
const resolved_root = try alloc.create(Node);
|
const resolved_root = try alloc.create(Node);
|
||||||
@@ -1378,7 +1376,6 @@ test "lower: shadowed same-name author gets its own FuncId + real body (fix-0102
|
|||||||
lowering.program_index.module_scopes = &module_scopes;
|
lowering.program_index.module_scopes = &module_scopes;
|
||||||
lowering.program_index.import_graph = &import_graph;
|
lowering.program_index.import_graph = &import_graph;
|
||||||
lowering.program_index.flat_import_graph = &flat_import_graph;
|
lowering.program_index.flat_import_graph = &flat_import_graph;
|
||||||
lowering.program_index.module_fns = &module_fns;
|
|
||||||
lowering.program_index.module_decls = &facts.decls;
|
lowering.program_index.module_decls = &facts.decls;
|
||||||
|
|
||||||
lowering.lowerRoot(resolved_root);
|
lowering.lowerRoot(resolved_root);
|
||||||
@@ -1409,23 +1406,23 @@ test "lower: shadowed same-name author gets its own FuncId + real body (fix-0102
|
|||||||
|
|
||||||
// F1 (attempt-2): the identity map must be keyed by the STABLE AST field
|
// F1 (attempt-2): the identity map must be keyed by the STABLE AST field
|
||||||
// pointer for BOTH same-name authors — the exact pointers `fn_ast_map` and
|
// pointer for BOTH same-name authors — the exact pointers `fn_ast_map` and
|
||||||
// `module_fns` carry — not a per-iteration switch-capture temporary. If the
|
// the `module_decls` raw facts carry — not a per-iteration switch-capture
|
||||||
// winner were keyed by `&fd` (the scanDecls bug), this lookup by the stable
|
// temporary. If the winner were keyed by `&fd` (the scanDecls bug), this
|
||||||
// `fn_ast_map` pointer would miss (null). fix-0102c routes calls through
|
// lookup by the stable `fn_ast_map` pointer would miss (null). fix-0102c
|
||||||
// exactly these pointers, so the round-trip must hold here.
|
// routes calls through exactly these pointers, so the round-trip must hold.
|
||||||
const winner_fd = lowering.program_index.fn_ast_map.get("greet").?;
|
const winner_fd = lowering.program_index.fn_ast_map.get("greet").?;
|
||||||
const winner_fid = lowering.fn_decl_fids.get(winner_fd);
|
const winner_fid = lowering.fn_decl_fids.get(winner_fd);
|
||||||
try std.testing.expect(winner_fid != null);
|
try std.testing.expect(winner_fid != null);
|
||||||
// Round-trips to the first-wins winner FuncId (resolveFuncByName's pick).
|
// Round-trips to the first-wins winner FuncId (resolveFuncByName's pick).
|
||||||
try std.testing.expectEqual(lowering.resolveFuncByName("greet").?, winner_fid.?);
|
try std.testing.expectEqual(lowering.resolveFuncByName("greet").?, winner_fid.?);
|
||||||
|
|
||||||
// The shadowed author's stable pointer lives in `module_fns`; find the one
|
// The shadowed author's stable pointer lives in `module_decls`; find the one
|
||||||
// that is NOT the winner and confirm IT round-trips to a DISTINCT FuncId.
|
// that is NOT the winner and confirm IT round-trips to a DISTINCT FuncId.
|
||||||
var shadow_fd: ?*const ast.FnDecl = null;
|
var shadow_fd: ?*const ast.FnDecl = null;
|
||||||
var mf_it = module_fns.iterator();
|
var md_it = facts.decls.iterator();
|
||||||
while (mf_it.next()) |path_entry| {
|
while (md_it.next()) |path_entry| {
|
||||||
if (path_entry.value_ptr.get("greet")) |fd| {
|
if (path_entry.value_ptr.names.get("greet")) |ref| {
|
||||||
if (fd != winner_fd) shadow_fd = fd;
|
if (ref == .fn_decl and ref.fn_decl != winner_fd) shadow_fd = ref.fn_decl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try std.testing.expect(shadow_fd != null);
|
try std.testing.expect(shadow_fd != null);
|
||||||
@@ -1521,8 +1518,6 @@ test "lower: scan populates source-keyed caches per declaring source (E0)" {
|
|||||||
while (cache_it.next()) |entry| {
|
while (cache_it.next()) |entry| {
|
||||||
try module_scopes.put(entry.key_ptr.*, entry.value_ptr.scope);
|
try module_scopes.put(entry.key_ptr.*, entry.value_ptr.scope);
|
||||||
}
|
}
|
||||||
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);
|
var facts = try imports.buildImportFacts(alloc, main_path, mod, &cache);
|
||||||
|
|
||||||
const resolved_root = try alloc.create(Node);
|
const resolved_root = try alloc.create(Node);
|
||||||
@@ -1538,7 +1533,6 @@ test "lower: scan populates source-keyed caches per declaring source (E0)" {
|
|||||||
lowering.program_index.module_scopes = &module_scopes;
|
lowering.program_index.module_scopes = &module_scopes;
|
||||||
lowering.program_index.import_graph = &import_graph;
|
lowering.program_index.import_graph = &import_graph;
|
||||||
lowering.program_index.flat_import_graph = &flat_import_graph;
|
lowering.program_index.flat_import_graph = &flat_import_graph;
|
||||||
lowering.program_index.module_fns = &module_fns;
|
|
||||||
lowering.program_index.module_decls = &facts.decls;
|
lowering.program_index.module_decls = &facts.decls;
|
||||||
|
|
||||||
lowering.lowerRoot(resolved_root);
|
lowering.lowerRoot(resolved_root);
|
||||||
|
|||||||
@@ -986,10 +986,10 @@ pub const Lowering = struct {
|
|||||||
// Declare extern stub for all functions (bodies lowered
|
// Declare extern stub for all functions (bodies lowered
|
||||||
// lazily). Key the identity map (`fn_decl_fids`, inside
|
// lazily). Key the identity map (`fn_decl_fids`, inside
|
||||||
// `declareFunction`) by the STABLE AST field pointer — the
|
// `declareFunction`) by the STABLE AST field pointer — the
|
||||||
// same `&decl.data.fn_decl` stored in `fn_ast_map` and
|
// same `&decl.data.fn_decl` stored in `fn_ast_map` and the
|
||||||
// `module_fns` — not the switch-capture copy `fd`, whose
|
// `module_decls` raw facts — not the switch-capture copy `fd`,
|
||||||
// address is a per-iteration stack temporary that no later
|
// whose address is a per-iteration stack temporary that no
|
||||||
// decl-identity lookup can reproduce.
|
// later decl-identity lookup can reproduce.
|
||||||
self.declareFunction(&decl.data.fn_decl, fd.name);
|
self.declareFunction(&decl.data.fn_decl, fd.name);
|
||||||
},
|
},
|
||||||
.const_decl => |cd| {
|
.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 first-wins flat/directory merge keeps exactly one author per name in
|
||||||
/// the merged decl list; `scanDecls` declares that WINNER (lowered on demand
|
/// the merged decl list; `scanDecls` declares that WINNER (lowered on demand
|
||||||
/// through the name-keyed `lazyLowerFunction`). fix-0102a retained every
|
/// through the name-keyed `lazyLowerFunction`). fix-0102a retained every
|
||||||
/// dropped same-name author in `module_fns` (path → name → `*FnDecl`) without
|
/// dropped same-name author in the `module_decls` raw facts (path → name →
|
||||||
/// touching resolution; this walks that index and gives each shadowed author
|
/// `RawDeclRef`) without touching resolution; this walks that index, filters
|
||||||
/// its own slot: `declareFunction` (identity-mapped to a fresh same-name
|
/// each author to its `*FnDecl` (`fnDeclOfRaw`), and gives each shadowed
|
||||||
/// FuncId) + `lowerFunctionBodyInto` (its body, in its own module's
|
/// 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
|
/// visibility context). Two same-name authors then carry distinct FuncIds and
|
||||||
/// distinct bodies, while `resolveFuncByName` still returns the first (winner)
|
/// distinct bodies, while `resolveFuncByName` still returns the first (winner)
|
||||||
/// author so existing calls bind first-wins.
|
/// author so existing calls bind first-wins.
|
||||||
///
|
///
|
||||||
/// Scoped to DIRECT flat imports of the main file: a `module_fns` entry whose
|
/// Scoped to DIRECT flat imports of the main file: a `module_decls` entry
|
||||||
/// path is the main file or one of its bare `#import` edges. A namespaced
|
/// whose path is the main file or one of its bare `#import` edges. A
|
||||||
/// (`ns :: #import`) author has no bare-name winner and is excluded both by
|
/// namespaced (`ns :: #import`) author has no bare-name winner and is excluded
|
||||||
/// that flat-edge gate and by the `fn_ast_map` winner lookup below.
|
/// both by that flat-edge gate and by the `fn_ast_map` winner lookup below.
|
||||||
pub fn lowerRetainedSameNameAuthors(self: *Lowering) void {
|
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 main_file = self.main_file orelse return;
|
||||||
const flat_graph = self.program_index.flat_import_graph orelse return;
|
const flat_graph = self.program_index.flat_import_graph orelse return;
|
||||||
const main_flat_edges = flat_graph.get(main_file);
|
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| {
|
while (path_it.next()) |path_entry| {
|
||||||
const path = path_entry.key_ptr.*;
|
const path = path_entry.key_ptr.*;
|
||||||
const is_eligible = std.mem.eql(u8, path, main_file) or
|
const is_eligible = std.mem.eql(u8, path, main_file) or
|
||||||
(main_flat_edges != null and main_flat_edges.?.contains(path));
|
(main_flat_edges != null and main_flat_edges.?.contains(path));
|
||||||
if (!is_eligible) continue;
|
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| {
|
while (fn_it.next()) |fn_entry| {
|
||||||
const name = fn_entry.key_ptr.*;
|
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
|
// A name with no bare winner is namespaced-only (`ns.fn`) — it
|
||||||
// never participated in the flat merge, so it has no shadow to
|
// 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
|
/// THE plain bare-name call selector (fix-0102c, R5 §C). `resolveBareCallee`'s
|
||||||
/// body verbatim, now over the Phase B author collector
|
/// body verbatim, now over the Phase B author collector
|
||||||
/// (`resolver.collectVisibleAuthors` — the ONE graph-walk) instead of a direct
|
/// (`resolver.collectVisibleAuthors` — the ONE graph-walk) instead of a direct
|
||||||
/// `module_fns` + `flat_import_graph` traversal. Routes a bare identifier call
|
/// `module_decls` + `flat_import_graph` traversal. Routes a bare identifier
|
||||||
/// `name` from `caller_file` to the right same-name author when flat imports
|
/// call `name` from `caller_file` to the right same-name author when flat
|
||||||
/// introduce a genuine collision. Every single-author / local / parameter /
|
/// imports introduce a genuine collision. Every single-author / local /
|
||||||
/// std / qualified name resolves through the EXISTING path unchanged: the
|
/// parameter / std / qualified name resolves through the EXISTING path
|
||||||
/// selector returns `.none` whenever the outcome would match first-wins, so
|
/// unchanged: the selector returns `.none` whenever the outcome would match
|
||||||
/// nothing on the common path is perturbed.
|
/// first-wins, so nothing on the common path is perturbed.
|
||||||
///
|
///
|
||||||
/// The collector returns RAW authors across ALL decl domains; this selector
|
/// The collector returns RAW authors across ALL decl domains; this selector
|
||||||
/// reproduces `module_fns`' fn-only view by filtering each author through
|
/// reproduces a fn-only author view by filtering each author through
|
||||||
/// `fnDeclOfRaw` (a `const`-wrapped fn unwraps to its inner fn — the exact
|
/// `fnDeclOfRaw` (a `const`-wrapped fn unwraps to its inner fn; every other
|
||||||
/// `*FnDecl` `module_fns` stored; every other domain drops out), preserving
|
/// domain drops out), preserving resolveBareCallee's negative space
|
||||||
/// resolveBareCallee's negative space byte-for-byte.
|
/// byte-for-byte.
|
||||||
///
|
///
|
||||||
/// - **own-author wins**: if `caller_file` authors `name` as a fn and the
|
/// - **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
|
/// 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
|
// 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
|
// (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
|
// `name` — fall through to the flat authors, exactly as a fn-only walk
|
||||||
// `module_fns` walk did.
|
// would.
|
||||||
if (set.own) |own_author| {
|
if (set.own) |own_author| {
|
||||||
if (fnDeclOfRaw(own_author.raw)) |own| {
|
if (fnDeclOfRaw(own_author.raw)) |own| {
|
||||||
if (winner != null and winner.? == own) return .none;
|
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
|
/// The `*FnDecl` a raw author wraps, or null when the author is not a
|
||||||
/// function — `imports.fnDeclOf` over a `RawDeclRef` so the collector's
|
/// function — unwraps a `RawDeclRef` so the collector's all-domain authors
|
||||||
/// all-domain authors reproduce `module_fns`' fn-only view (a `const`-wrapped
|
/// yield a fn-only view (a `const`-wrapped fn unwraps to its inner fn; every
|
||||||
/// fn unwraps to its inner fn, the same pointer `module_fns` holds; every
|
/// other domain → null). The single place function authors are read out of
|
||||||
/// other domain → null).
|
/// the `module_decls` raw facts.
|
||||||
fn fnDeclOfRaw(ref: resolver_mod.RawDeclRef) ?*const ast.FnDecl {
|
fn fnDeclOfRaw(ref: resolver_mod.RawDeclRef) ?*const ast.FnDecl {
|
||||||
return switch (ref) {
|
return switch (ref) {
|
||||||
.fn_decl => |fd| fd,
|
.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;
|
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) return true;
|
||||||
if (fd.body.data.foreign_expr.library_ref != null) 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),
|
.user_bare_flat => return self.visibleOverEdges(name),
|
||||||
.legacy_direct_any => return self.visibleOverEdges(name, .all),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const VisEdgeSet = enum { flat, all };
|
/// Run the per-file visibility walk over the flat-import edge set. Falls
|
||||||
|
|
||||||
/// Resolve the mode's edge set and run the per-file visibility walk. Falls
|
|
||||||
/// open (visible) when the scoping infrastructure isn't wired (comptime
|
/// open (visible) when the scoping infrastructure isn't wired (comptime
|
||||||
/// callers, directory imports without main_file, etc.). The caller is
|
/// callers, directory imports without main_file, etc.). The caller is
|
||||||
/// responsible for restricting the check to names that ARE known top-level
|
/// responsible for restricting the check to names that ARE known top-level
|
||||||
/// decls; otherwise every local variable would be policed.
|
/// 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 source = self.current_source_file orelse return true;
|
||||||
const graph = switch (edges) {
|
return nameVisibleOverEdges(self.program_index.module_scopes, self.program_index.flat_import_graph, source, name);
|
||||||
.flat => self.program_index.flat_import_graph,
|
|
||||||
.all => self.program_index.import_graph,
|
|
||||||
};
|
|
||||||
return nameVisibleOverEdges(self.program_index.module_scopes, graph, source, name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if a C-imported function is visible from the current source file.
|
/// Check if a C-imported function is visible from the current source file.
|
||||||
|
|||||||
@@ -588,9 +588,9 @@ pub const GlobalInfo = struct { id: inst.GlobalId, ty: TypeId };
|
|||||||
/// `*ProgramIndex` instead of `*Lowering`.
|
/// `*ProgramIndex` instead of `*Lowering`.
|
||||||
///
|
///
|
||||||
/// OWNS the declaration maps below. BORROWS `module_scopes` / `import_graph` /
|
/// OWNS the declaration maps below. BORROWS `module_scopes` / `import_graph` /
|
||||||
/// `flat_import_graph` / `module_fns` (pointers into maps owned by the
|
/// `flat_import_graph` / `module_decls` / `namespace_edges` / `decl_table`
|
||||||
/// compilation driver, `core.zig`) — those are read-only views and are never
|
/// (pointers into maps owned by the compilation driver, `core.zig`) — those are
|
||||||
/// freed here.
|
/// read-only views and are never freed here.
|
||||||
///
|
///
|
||||||
/// Per-map allocators are preserved exactly as they were on `Lowering`:
|
/// Per-map allocators are preserved exactly as they were on `Lowering`:
|
||||||
/// `import_flags` / `fn_ast_map` / `global_names` use the lowering allocator
|
/// `import_flags` / `fn_ast_map` / `global_names` use the lowering allocator
|
||||||
@@ -615,11 +615,6 @@ pub const ProgramIndex = struct {
|
|||||||
/// `ns :: #import`). fix-0102c's bare-name disambiguation walks this to
|
/// `ns :: #import`). fix-0102c's bare-name disambiguation walks this to
|
||||||
/// decide which same-name authors a flat importer can reach. Borrowed view.
|
/// decide which same-name authors a flat importer can reach. Borrowed view.
|
||||||
flat_import_graph: ?*std.StringHashMap(std.StringHashMap(void)) = null,
|
flat_import_graph: ?*std.StringHashMap(std.StringHashMap(void)) = null,
|
||||||
/// Module path → (function name → authoring `*const FnDecl`), mirroring
|
|
||||||
/// `module_scopes`. Retains every same-name author under its own path so
|
|
||||||
/// fix-0102c can resolve a flat call to the right module's function.
|
|
||||||
/// Borrowed view.
|
|
||||||
module_fns: ?*imports.ModuleFns = null,
|
|
||||||
/// Per-module scalar raw-decl index (`path → name → RawDeclRef`), built by
|
/// Per-module scalar raw-decl index (`path → name → RawDeclRef`), built by
|
||||||
/// `imports.buildImportFacts`. The unified resolver's raw-fact store.
|
/// `imports.buildImportFacts`. The unified resolver's raw-fact store.
|
||||||
/// Borrowed view.
|
/// Borrowed view.
|
||||||
@@ -696,7 +691,7 @@ pub const ProgramIndex = struct {
|
|||||||
|
|
||||||
pub fn deinit(self: *ProgramIndex) void {
|
pub fn deinit(self: *ProgramIndex) void {
|
||||||
// Owned maps only — module_scopes / import_graph / flat_import_graph /
|
// Owned maps only — module_scopes / import_graph / flat_import_graph /
|
||||||
// module_fns / module_decls / namespace_edges / decl_table are borrowed.
|
// module_decls / namespace_edges / decl_table are borrowed.
|
||||||
self.import_flags.deinit();
|
self.import_flags.deinit();
|
||||||
self.fn_ast_map.deinit();
|
self.fn_ast_map.deinit();
|
||||||
self.qualified_fn_source.deinit();
|
self.qualified_fn_source.deinit();
|
||||||
|
|||||||
@@ -4,8 +4,7 @@
|
|||||||
// resolveImports → buildImportFacts, the exact path core.zig drives) plus one
|
// resolveImports → buildImportFacts, the exact path core.zig drives) plus one
|
||||||
// synthetic diamond fixture for pointer-identity dedup. The visibility-adapter
|
// synthetic diamond fixture for pointer-identity dedup. The visibility-adapter
|
||||||
// tests pin the nameVisibleOverEdges edge-walk that isNameVisible /
|
// tests pin the nameVisibleOverEdges edge-walk that isNameVisible /
|
||||||
// isCImportVisible run on top of — including the user_bare_flat vs the
|
// isCImportVisible run on top of — the flat-edge set vs the full import_graph.
|
||||||
// over-permissive legacy_direct_any distinction.
|
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const ast = @import("../ast.zig");
|
const ast = @import("../ast.zig");
|
||||||
@@ -82,8 +81,8 @@ fn tag(ref: resolver.RawDeclRef) std.meta.Tag(resolver.RawDeclRef) {
|
|||||||
// ── collectVisibleAuthors ────────────────────────────────────────────────
|
// ── collectVisibleAuthors ────────────────────────────────────────────────
|
||||||
|
|
||||||
// own author present; two distinct flat authors both returned RAW; and the
|
// own author present; two distinct flat authors both returned RAW; and the
|
||||||
// user_bare_flat edge set EXCLUDES a namespaced-only import that the quarantined
|
// user_bare_flat edge set EXCLUDES a namespaced-only import (reachable only over
|
||||||
// legacy_direct_any set still reaches.
|
// a non-flat edge).
|
||||||
test "resolver: collectVisibleAuthors — own author, two distinct flat authors, namespaced edge excluded" {
|
test "resolver: collectVisibleAuthors — own author, two distinct flat authors, namespaced edge excluded" {
|
||||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
@@ -129,16 +128,10 @@ test "resolver: collectVisibleAuthors — own author, two distinct flat authors,
|
|||||||
try std.testing.expect(dup_set.flat[0].raw.fn_decl != dup_set.flat[1].raw.fn_decl);
|
try std.testing.expect(dup_set.flat[0].raw.fn_decl != dup_set.flat[1].raw.fn_decl);
|
||||||
|
|
||||||
// `secret` is authored only in p.sx, imported NAMESPACED (`g :: #import`).
|
// `secret` is authored only in p.sx, imported NAMESPACED (`g :: #import`).
|
||||||
// user_bare_flat must NOT see it (p.sx is not a flat edge)...
|
// user_bare_flat must NOT see it (p.sx is not a flat edge).
|
||||||
const flat_secret = r.collectVisibleAuthors("secret", main_path, .user_bare_flat);
|
const flat_secret = r.collectVisibleAuthors("secret", main_path, .user_bare_flat);
|
||||||
try std.testing.expect(flat_secret.own == null);
|
try std.testing.expect(flat_secret.own == null);
|
||||||
try std.testing.expectEqual(@as(usize, 0), flat_secret.flat.len);
|
try std.testing.expectEqual(@as(usize, 0), flat_secret.flat.len);
|
||||||
|
|
||||||
// ...but the quarantined legacy_direct_any set (import_graph) still reaches
|
|
||||||
// it — the exact over-permissiveness user_bare_flat tightens away.
|
|
||||||
const any_secret = r.collectVisibleAuthors("secret", main_path, .legacy_direct_any);
|
|
||||||
try std.testing.expect(any_secret.own == null);
|
|
||||||
try std.testing.expectEqual(@as(usize, 1), any_secret.flat.len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Diamond: the SAME author node is reachable over two flat edges. It must
|
// Diamond: the SAME author node is reachable over two flat edges. It must
|
||||||
@@ -234,10 +227,10 @@ test "resolver: collectNamespaceAuthors — returns target members, walks no gra
|
|||||||
|
|
||||||
// ── visibility predicate (the isNameVisible / isCImportVisible core) ──────
|
// ── visibility predicate (the isNameVisible / isCImportVisible core) ──────
|
||||||
|
|
||||||
// nameVisibleOverEdges is what isVisible(.user_bare_flat) (=> .flat graph) and
|
// nameVisibleOverEdges is the edge-walk isVisible(.user_bare_flat) runs on (the
|
||||||
// the quarantined legacy_direct_any (=> import_graph) run on. They agree on own
|
// flat graph). Walked over the flat set vs the full import_graph, the two agree
|
||||||
// + flat names and differ ONLY on a namespaced-only name — the byte-identical
|
// on own + flat names and differ ONLY on a namespaced-only name — the flat set
|
||||||
// behavior the adapters preserve vs the over-permissive set they avoid.
|
// the bare-name predicate uses, contrasted with the over-permissive full set.
|
||||||
test "resolver: visibility edge-walk — own + flat visible; namespaced-only only under import_graph" {
|
test "resolver: visibility edge-walk — own + flat visible; namespaced-only only under import_graph" {
|
||||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
@@ -275,7 +268,7 @@ test "resolver: visibility edge-walk — own + flat visible; namespaced-only onl
|
|||||||
try std.testing.expect(lower.nameVisibleOverEdges(&scopes, &all, "main", "dup"));
|
try std.testing.expect(lower.nameVisibleOverEdges(&scopes, &all, "main", "dup"));
|
||||||
|
|
||||||
// Namespaced-only name: NOT visible under the flat set (user_bare_flat),
|
// Namespaced-only name: NOT visible under the flat set (user_bare_flat),
|
||||||
// but visible under the import_graph set (legacy_direct_any).
|
// but visible under the full import_graph set.
|
||||||
try std.testing.expect(!lower.nameVisibleOverEdges(&scopes, &flat, "main", "secret"));
|
try std.testing.expect(!lower.nameVisibleOverEdges(&scopes, &flat, "main", "secret"));
|
||||||
try std.testing.expect(lower.nameVisibleOverEdges(&scopes, &all, "main", "secret"));
|
try std.testing.expect(lower.nameVisibleOverEdges(&scopes, &all, "main", "secret"));
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
//! A read-only facade over the borrowed Phase A import facts on a
|
//! A read-only facade over the borrowed Phase A import facts on a
|
||||||
//! `*ProgramIndex` (`module_decls` / `namespace_edges`) and the existing
|
//! `*ProgramIndex` (`module_decls` / `namespace_edges`) and the existing
|
||||||
//! `import_graph` / `flat_import_graph` views. It OWNS nothing import-derived;
|
//! `import_graph` / `flat_import_graph` views. It OWNS nothing import-derived;
|
||||||
//! those maps live in `imports.zig`/`core.zig` and are borrowed here, exactly
|
//! those maps live in `imports.zig`/`core.zig` and are borrowed here.
|
||||||
//! like `module_fns`.
|
|
||||||
//!
|
//!
|
||||||
//! Two collectors sit on top of these facts (R5 §1 #1):
|
//! Two collectors sit on top of these facts (R5 §1 #1):
|
||||||
//! - `collectVisibleAuthors` — own author ∪ the flat-import edge walk. THE one
|
//! - `collectVisibleAuthors` — own author ∪ the flat-import edge walk. THE one
|
||||||
@@ -69,10 +68,6 @@ pub const VisibilityMode = enum {
|
|||||||
/// Registration / lazy lowering: falls open (visible), emits no user
|
/// Registration / lazy lowering: falls open (visible), emits no user
|
||||||
/// diagnostic, performs no graph walk.
|
/// diagnostic, performs no graph walk.
|
||||||
lowering_internal,
|
lowering_internal,
|
||||||
/// own scope ∪ `import_graph` (flat AND namespaced edges) — an
|
|
||||||
/// over-permissive set. QUARANTINE: reserved for sites PROVEN to be internal
|
|
||||||
/// scans, never a user-facing lookup. Deleted in Phase K.
|
|
||||||
legacy_direct_any,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Read-only facade over the borrowed import facts. `alloc` backs the
|
/// Read-only facade over the borrowed import facts. `alloc` backs the
|
||||||
@@ -91,10 +86,10 @@ pub const Resolver = struct {
|
|||||||
/// this. `from` is the querying module's source path.
|
/// this. `from` is the querying module's source path.
|
||||||
///
|
///
|
||||||
/// Edge set by mode: `flat_import_graph` for `user_bare_flat`/
|
/// Edge set by mode: `flat_import_graph` for `user_bare_flat`/
|
||||||
/// `c_import_bare`; `import_graph` for the quarantined `legacy_direct_any`.
|
/// `c_import_bare`. `impl_transitive` (a transitive closure owned by
|
||||||
/// `impl_transitive` (a transitive closure owned by `findVisibleImpls`) and
|
/// `findVisibleImpls`) and `lowering_internal` (no graph walk) are not
|
||||||
/// `lowering_internal` (no graph walk) are not single-hop author walks —
|
/// single-hop author walks — reaching them here is a wiring bug, so we trip
|
||||||
/// reaching them here is a wiring bug, so we trip loudly.
|
/// loudly.
|
||||||
pub fn collectVisibleAuthors(
|
pub fn collectVisibleAuthors(
|
||||||
self: *Resolver,
|
self: *Resolver,
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
@@ -111,7 +106,6 @@ pub const Resolver = struct {
|
|||||||
|
|
||||||
const graph = (switch (vis) {
|
const graph = (switch (vis) {
|
||||||
.user_bare_flat, .c_import_bare => self.index.flat_import_graph,
|
.user_bare_flat, .c_import_bare => self.index.flat_import_graph,
|
||||||
.legacy_direct_any => self.index.import_graph,
|
|
||||||
// findVisibleImpls owns transitive visibility; lowering_internal
|
// findVisibleImpls owns transitive visibility; lowering_internal
|
||||||
// performs no graph walk. Neither selects a single-hop edge set.
|
// performs no graph walk. Neither selects a single-hop edge set.
|
||||||
.impl_transitive, .lowering_internal => @panic(
|
.impl_transitive, .lowering_internal => @panic(
|
||||||
|
|||||||
Reference in New Issue
Block a user