fix(stdlib/S2.1c): preseed top-level UFCS aliases [additive]

This commit is contained in:
agra
2026-06-09 14:50:51 +03:00
parent 59681e0a09
commit 3b1415a287
2 changed files with 70 additions and 12 deletions

View File

@@ -353,6 +353,18 @@ fn findDecl(root: *const ast.Node, name: []const u8, comptime kind: std.meta.Tag
return null;
}
fn firstBareCallCallee(func: *const ast.Node, name: []const u8) ?*const ast.Node {
if (func.data != .fn_decl) return null;
const body = func.data.fn_decl.body;
if (body.data != .block) return null;
for (body.data.block.stmts) |stmt| {
if (stmt.data != .call) continue;
const callee = stmt.data.call.callee;
if (callee.data == .identifier and std.mem.eql(u8, callee.data.identifier.name, name)) return callee;
}
return null;
}
fn expectTypeRefOwnTag(
rp: *const resolver.ResolvedProgram,
node: *const ast.Node,
@@ -711,8 +723,9 @@ test "resolver: resolve — namespace-qualified + generic-struct/type-fn/protoco
// into foreign_class_refs — NOT type_refs. (2) A `Type.CONST` field access whose
// base resolves to a struct carrying that const member (`Phys.GRAVITY`) fills
// struct_const_refs, keyed by the field_access node. (3) A UFCS alias
// (`plus :: ufcs adder`) is keyed by its decl node AND its rewrite call site
// (`plus(1, 2)`) is keyed by the callee, both resolving to the target's author.
// (`plus :: ufcs adder`) is keyed by its decl node AND its rewrite call sites
// (including one before the alias decl) are keyed by the callee, both resolving
// to the target's author.
// This fixture exercises ALL TEN domains at once, proving the full-population
// acceptance: every ResolvedProgram side table is non-empty and node-keyed.
test "resolver: resolve — S2.1c foreign-class/struct-const/UFCS + all ten domains populated" {
@@ -738,6 +751,7 @@ test "resolver: resolve — S2.1c foreign-class/struct-const/UFCS + all ten doma
\\Cmp :: protocol(T: Type) { get :: () -> T; }
\\LIMIT :: 5;
\\adder :: (a: s64, b: s64) -> s64 { a + b }
\\call_alias_before :: () -> s64 { plus(3, 4) }
\\plus :: ufcs adder;
\\use_phys :: (p: Phys) -> s64 { p.mass }
\\use_obj :: (o: Obj) -> s64 { 0 }
@@ -746,7 +760,7 @@ test "resolver: resolve — S2.1c foreign-class/struct-const/UFCS + all ten doma
\\use_cmp :: (c: Cmp(s64)) -> s64 { 0 }
\\read_const :: () -> s64 { Phys.GRAVITY }
\\read_ns :: () -> s64 { g.helper_fn() }
\\call_alias :: () -> s64 { plus(1, 2) }
\\call_alias_after :: () -> s64 { plus(1, 2) }
\\main :: () -> s32 {
\\ n := helper();
\\ m := LIMIT;
@@ -824,20 +838,29 @@ test "resolver: resolve — S2.1c foreign-class/struct-const/UFCS + all ten doma
try std.testing.expect(plus_ref.authors.own != null);
try std.testing.expectEqual(fn_tag, std.meta.activeTag(plus_ref.authors.own.?.raw));
// (3b) UFCS rewrite site: the `plus(1, 2)` callee identifier is keyed in
// ufcs_refs (NOT callable_refs) and resolves to the SAME target author.
var saw_site = false;
// (3b) UFCS rewrite sites: both the forward `plus(3, 4)` site and the later
// `plus(1, 2)` site are keyed in ufcs_refs (NOT callable_refs) and
// resolve to the SAME target author.
const before_alias = findFn(prog.root, "call_alias_before") orelse return error.MissingBeforeAliasFn;
const before_alias_callee = firstBareCallCallee(before_alias, "plus") orelse return error.MissingBeforeAliasCallee;
const before_ref = rp.ufcs_refs.get(before_alias_callee) orelse return error.BeforeAliasUfcsNotKeyed;
try std.testing.expect(before_ref == .authors);
try std.testing.expect(before_ref.authors.own != null);
try std.testing.expectEqual(fn_tag, std.meta.activeTag(before_ref.authors.own.?.raw));
try std.testing.expect(rp.callable_refs.get(before_alias_callee) == null);
var saw_after_site = false;
var uit = rp.ufcs_refs.iterator();
while (uit.next()) |e| {
const k = e.key_ptr.*;
if (k.data == .identifier and std.mem.eql(u8, k.data.identifier.name, "plus")) {
if (k != before_alias_callee and k.data == .identifier and std.mem.eql(u8, k.data.identifier.name, "plus")) {
try std.testing.expect(e.value_ptr.* == .authors);
try std.testing.expect(e.value_ptr.authors.own != null);
try std.testing.expectEqual(fn_tag, std.meta.activeTag(e.value_ptr.authors.own.?.raw));
saw_site = true;
saw_after_site = true;
}
}
try std.testing.expect(saw_site);
try std.testing.expect(saw_after_site);
// A UFCS-rewritten callee is NOT also recorded as an ordinary callable head.
var cit = rp.callable_refs.iterator();