fix(lower): key fn_decl_fids by stable AST pointer in scanDecls [0102b F1]

scanDecls declared a bare `.fn_decl` via `declareFunction(&fd, ...)`,
where `fd` is the switch-capture COPY of `decl.data.fn_decl`. Its
address is a per-iteration stack temporary, so the winner author's
`fn_decl_fids` entry was keyed by an address no later decl-identity
lookup can reproduce — `fn_ast_map` and `module_fns` carry the stable
`&decl.data.fn_decl`, so a lookup by that pointer missed the winner's
FuncId. fix-0102c routes calls through exactly these stable pointers,
so the key has to match.

Record the entry under `&decl.data.fn_decl` (the persistent AST node
field) to match `fn_ast_map`/`module_fns`. The other declareFunction
sites already pass stable pointers (const_decl field, module_fns entry,
fn_ast_map entry, struct-method node field, heap-synthesized objc decl);
`lowered_fids` keys by FuncId value, so neither has the temporary-address
mistake.

Strengthen the fix-0102b regression test: assert the identity map
round-trips by the STABLE pointer for BOTH same-name authors — the
winner's `fn_ast_map` pointer resolves to the first-wins FuncId, and the
shadow's `module_fns` pointer resolves to a distinct FuncId. This
assertion fails on the pre-fix code (winner keyed by `&fd` → null) and
passes after. Call resolution unchanged (name path still default).

Gate (this worktree): zig build, zig build test (400/400),
bash tests/run_examples.sh (457 passed) all green.
This commit is contained in:
agra
2026-06-06 13:22:11 +03:00
parent 237f794585
commit bb1ed7294b
2 changed files with 34 additions and 2 deletions

View File

@@ -1400,4 +1400,30 @@ test "lower: shadowed same-name author gets its own FuncId + real body (fix-0102
}
try std.testing.expect(first != null and second != null);
try std.testing.expect(first.? != second.?);
// 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
// `module_fns` carry — not a per-iteration switch-capture temporary. If the
// winner were keyed by `&fd` (the scanDecls bug), this lookup by the stable
// `fn_ast_map` pointer would miss (null). fix-0102c routes calls through
// exactly these pointers, so the round-trip must hold here.
const winner_fd = lowering.program_index.fn_ast_map.get("greet").?;
const winner_fid = lowering.fn_decl_fids.get(winner_fd);
try std.testing.expect(winner_fid != null);
// Round-trips to the first-wins winner FuncId (resolveFuncByName's pick).
try std.testing.expectEqual(lowering.resolveFuncByName("greet").?, winner_fid.?);
// The shadowed author's stable pointer lives in `module_fns`; find the one
// that is NOT the winner and confirm IT round-trips to a DISTINCT FuncId.
var shadow_fd: ?*const ast.FnDecl = null;
var mf_it = module_fns.iterator();
while (mf_it.next()) |path_entry| {
if (path_entry.value_ptr.get("greet")) |fd| {
if (fd != winner_fd) shadow_fd = fd;
}
}
try std.testing.expect(shadow_fd != null);
const shadow_fid = lowering.fn_decl_fids.get(shadow_fd.?);
try std.testing.expect(shadow_fid != null);
try std.testing.expect(shadow_fid.? != winner_fid.?);
}

View File

@@ -791,8 +791,14 @@ pub const Lowering = struct {
self.program_index.fn_ast_map.put(fd.name, &decl.data.fn_decl) catch {};
self.program_index.import_flags.put(fd.name, is_imported) catch {};
}
// Declare extern stub for all functions (bodies lowered lazily)
self.declareFunction(&fd, fd.name);
// 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.
self.declareFunction(&decl.data.fn_decl, fd.name);
},
.const_decl => |cd| {
if (cd.value.data == .fn_decl) {