feat(resolver): route plain bare-call author through Phase B collector via SelectedFunc [stdlib C]
Phase C of the unified resolver (R5 §C, §#3). Re-base the plain bare-name
call author onto the Phase B collector behind one shared SelectedFunc, so
every call-path consumer reads ONE author and they can no longer disagree
(fix-0102 F2). Behavior-preserving: 0722-0735 byte-identical, run_examples
stays at 475.
- SelectedFunc {decl, source, materialized?} replaces ResolvedAuthor in
BareCallee.func; CallPlan.Target gains a `selected` arm (calls.zig).
- selectPlainCallableAuthor: resolveBareCallee's body verbatim over
resolver.collectVisibleAuthors (.user_bare_flat) — the ONE graph-walk.
fnDeclOfRaw mirrors imports.fnDeclOf so the collector's all-domain authors
reproduce module_fns' fn-only view; every byte of the negative space is
preserved (own==winner → .none; non-plain-free → .none; filter-before-count;
≥2 distinct → .ambiguous). No eager materialization.
- selectedFuncId materializes the FuncId on demand (shadow-only), caching into
materialized — null until a site needs it (0102d: a shadow taken as a value
never lowers the winner).
- Six consumers route through the one selector: lowerCall variadic packing,
free-fn UFCS, fn-value, closure(fn), resolveCallParamTypes, and
expandCallDefaults (decl-only, no materialization). plan() produces the
SelectedFunc as `.selected`. Generic/comptime/foreign/builtin stay legacy.
- lower.test.zig: wire module_decls; selectPlainCallableAuthor verdicts
(own-winner → .none; ≥2 flat → .ambiguous; own-shadow → decl+source, fid
round-trips, materialized null).
Gate: zig build + zig build test (412 ok) + run_examples (475, byte-identical)
+ m3te ios-sim build exit 0.
This commit is contained in:
@@ -1361,6 +1361,10 @@ test "lower: shadowed same-name author gets its own FuncId + real body (fix-0102
|
||||
var module_fns = imports.ModuleFns.init(alloc);
|
||||
try imports.buildModuleFns(alloc, main_path, mod, &cache, &module_fns);
|
||||
|
||||
// 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);
|
||||
|
||||
const resolved_root = try alloc.create(Node);
|
||||
resolved_root.* = .{ .span = root.span, .data = .{ .root = .{ .decls = mod.decls } } };
|
||||
|
||||
@@ -1375,6 +1379,7 @@ test "lower: shadowed same-name author gets its own FuncId + real body (fix-0102
|
||||
lowering.program_index.import_graph = &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.lowerRoot(resolved_root);
|
||||
try std.testing.expect(!diagnostics.hasErrors());
|
||||
@@ -1428,19 +1433,27 @@ test "lower: shadowed same-name author gets its own FuncId + real body (fix-0102
|
||||
try std.testing.expect(shadow_fid != null);
|
||||
try std.testing.expect(shadow_fid.? != winner_fid.?);
|
||||
|
||||
// fix-0102c: THE bare-name resolver routes per caller file. `main` flat-
|
||||
// imports two `greet` authors and is its own author of neither → a bare
|
||||
// `greet()` from `main` is ambiguous. a.sx authors the WINNER, so its bare
|
||||
// `greet` resolves through the existing path (`.none`). b.sx authors the
|
||||
// SHADOW, so own-author-wins binds b.sx's distinct FuncId — not first-wins.
|
||||
// fix-0102c / Phase C: THE bare-name selector routes per caller file over the
|
||||
// Phase A author collector. `main` flat-imports two `greet` authors and is its
|
||||
// own author of neither → a bare `greet()` from `main` is ambiguous. a.sx
|
||||
// authors the WINNER, so its bare `greet` resolves through the existing path
|
||||
// (`.none`). b.sx authors the SHADOW, so own-author-wins selects b.sx's
|
||||
// author — its `*FnDecl` + source, NOT first-wins. The selector does NOT
|
||||
// eagerly materialize: it returns the decl, and the FuncId still round-trips
|
||||
// to the shadow slot via the identity map (`fn_decl_fids`).
|
||||
const a_path = try std.fmt.allocPrint(alloc, "{s}/a.sx", .{absdir});
|
||||
const b_path = try std.fmt.allocPrint(alloc, "{s}/b.sx", .{absdir});
|
||||
try std.testing.expect(lowering.resolveBareCallee("greet", main_path) == .ambiguous);
|
||||
try std.testing.expect(lowering.resolveBareCallee("greet", a_path) == .none);
|
||||
switch (lowering.resolveBareCallee("greet", b_path)) {
|
||||
.func => |resolved| try std.testing.expectEqual(shadow_fid.?, resolved.fid),
|
||||
try std.testing.expect(lowering.selectPlainCallableAuthor("greet", main_path) == .ambiguous);
|
||||
try std.testing.expect(lowering.selectPlainCallableAuthor("greet", a_path) == .none);
|
||||
switch (lowering.selectPlainCallableAuthor("greet", b_path)) {
|
||||
.func => |sf| {
|
||||
try std.testing.expectEqual(shadow_fd.?, sf.decl);
|
||||
try std.testing.expectEqualStrings(b_path, sf.source);
|
||||
try std.testing.expect(sf.materialized == null);
|
||||
try std.testing.expectEqual(shadow_fid.?, lowering.fn_decl_fids.get(sf.decl).?);
|
||||
},
|
||||
else => return error.TestUnexpectedResult,
|
||||
}
|
||||
// A name no module authors (and no flat import provides) never routes.
|
||||
try std.testing.expect(lowering.resolveBareCallee("nonexistent", b_path) == .none);
|
||||
try std.testing.expect(lowering.selectPlainCallableAuthor("nonexistent", b_path) == .none);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user