feat(stdlib/S2.1b): namespace-qualified + 3 head domains on the owning pass [additive]
On the S2.1a exhaustive traversal, populate four more ResolvedProgram side tables, still RAW / PARALLEL / UNCONSUMED: - namespace-qualified references: an `alias.member` field_access whose base alias is a NamespaceEdges[ambient_source] target resolves via collectNamespaceAuthors into namespace_refs, keyed by the access node. - the three HEAD domains at parameterized_type_expr heads, binned by the resolved author's decl kind: a struct with type params -> generic_struct_heads, a fn/const-wrapped fn with type params -> type_fn_heads, a protocol -> protocol_heads. RAW: the whole author set is recorded with no winner picked; a name authored as >1 head kind lands a distinct entry in every matching table. Lowering still reads the old selectors and resolved_program has no consumer, so generated output is byte-identical. ResolvedRef stays RAW (selection is S2.2); generics stay symbolic. S2.1c (foreign-class / struct-const / UFCS) owns the remaining three tables. Extends the population proof: a resolver unit test asserting all four tables are non-empty + node-keyed with the expected RAW authors. Gate (all exit 0): zig build; zig build test (All 427 mod + exe + LSP sweep 574); tests/run_examples.sh (540 passed, byte-identical); tests/resolver-target (18 xfail, 0 leaked); m3te ios-sim via the main sx binary.
This commit is contained in:
@@ -288,6 +288,7 @@ test "resolver: visibility edge-walk — own + flat visible; namespaced-only onl
|
||||
const Resolved = struct {
|
||||
root: *ast.Node,
|
||||
decls: imports.ModuleDecls,
|
||||
ns_edges: imports.NamespaceEdges,
|
||||
flat_import_graph: Graph,
|
||||
import_graph: Graph,
|
||||
};
|
||||
@@ -329,6 +330,7 @@ fn buildResolved(alloc: std.mem.Allocator, io: std.Io, absdir: []const u8, main_
|
||||
return .{
|
||||
.root = root,
|
||||
.decls = facts.decls,
|
||||
.ns_edges = facts.ns_edges,
|
||||
.flat_import_graph = flat_import_graph,
|
||||
.import_graph = import_graph,
|
||||
};
|
||||
@@ -473,7 +475,10 @@ test "resolver: resolve — bare-name domains populated, keyed by node, symbolic
|
||||
try std.testing.expect(pack_ref == .pack);
|
||||
try std.testing.expectEqual(@as(?u32, 0), pack_ref.pack.index);
|
||||
|
||||
// (5) The seven domains S2.1b/c own stay EMPTY — S2.1a is parallel/unconsumed.
|
||||
// (5) This fixture exercises NONE of the S2.1b/c domains (no namespaced import,
|
||||
// no parameterized heads, no foreign/const/UFCS sites) — and the index has
|
||||
// no `namespace_edges` wired — so all seven stay EMPTY. The dedicated S2.1b
|
||||
// test below proves the four it owns populate when exercised.
|
||||
try std.testing.expectEqual(@as(u32, 0), rp.namespace_refs.count());
|
||||
try std.testing.expectEqual(@as(u32, 0), rp.generic_struct_heads.count());
|
||||
try std.testing.expectEqual(@as(u32, 0), rp.type_fn_heads.count());
|
||||
@@ -535,3 +540,117 @@ test "resolver: resolve — generic constraints and named error sets are type re
|
||||
try std.testing.expect(err_ty.data == .error_type_expr);
|
||||
try expectTypeRefOwnTag(&rp, err_ty, error_tag);
|
||||
}
|
||||
|
||||
// ── the owning resolution pass — S2.1b namespace-qualified + head domains ──
|
||||
|
||||
// S2.1b populates four more domains on the SAME traversal, still RAW /
|
||||
// PARALLEL / UNCONSUMED. (1) A namespace-qualified `g.helper_fn` resolves via
|
||||
// collectNamespaceAuthors into namespace_refs, keyed by its field_access node.
|
||||
// (2..4) Parameterized heads are binned by the resolved author's decl kind: a
|
||||
// generic struct (`Box(s64)`) → generic_struct_heads, a type-function
|
||||
// (`Make(s64)`) → type_fn_heads, a parameterized protocol used as a value type
|
||||
// (`Cmp(s64)`) → protocol_heads — each keyed by its parameterized_type_expr node.
|
||||
test "resolver: resolve — namespace-qualified + generic-struct/type-fn/protocol heads" {
|
||||
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
|
||||
defer arena.deinit();
|
||||
const alloc = arena.allocator();
|
||||
const io = testIo();
|
||||
|
||||
var tmp = std.testing.tmpDir(.{});
|
||||
defer tmp.cleanup();
|
||||
|
||||
try tmp.dir.writeFile(io, .{ .sub_path = "lib.sx", .data =
|
||||
\\helper_fn :: () -> s64 { 7 }
|
||||
\\
|
||||
});
|
||||
try tmp.dir.writeFile(io, .{ .sub_path = "main.sx", .data =
|
||||
\\g :: #import "lib.sx";
|
||||
\\Box :: struct($T: Type) { value: T }
|
||||
\\Make :: ($T: Type) -> Type { return [3]T; }
|
||||
\\Cmp :: protocol(T: Type) { get :: () -> T; }
|
||||
\\use_box :: (b: Box(s64)) -> s64 { return 0; }
|
||||
\\use_make :: (m: Make(s64)) -> s64 { return 0; }
|
||||
\\use_cmp :: (c: Cmp(s64)) -> s64 { return 0; }
|
||||
\\read_ns :: () -> s64 { return g.helper_fn(); }
|
||||
\\main :: () -> s32 { return 0; }
|
||||
\\
|
||||
});
|
||||
|
||||
var dirbuf: [4096]u8 = undefined;
|
||||
const absdir = dirbuf[0..try tmp.dir.realPath(io, &dirbuf)];
|
||||
const main_path = try std.fmt.allocPrint(alloc, "{s}/main.sx", .{absdir});
|
||||
|
||||
var prog = try buildResolved(alloc, io, absdir, main_path);
|
||||
|
||||
var idx = ProgramIndex.init(alloc);
|
||||
defer idx.deinit();
|
||||
idx.module_decls = &prog.decls;
|
||||
idx.namespace_edges = &prog.ns_edges;
|
||||
idx.flat_import_graph = &prog.flat_import_graph;
|
||||
idx.import_graph = &prog.import_graph;
|
||||
|
||||
var rp = resolver.resolve(prog.root, &idx, main_path, alloc);
|
||||
defer rp.deinit();
|
||||
|
||||
// (0) All four S2.1b tables are NON-EMPTY.
|
||||
try std.testing.expect(rp.namespace_refs.count() > 0);
|
||||
try std.testing.expect(rp.generic_struct_heads.count() > 0);
|
||||
try std.testing.expect(rp.type_fn_heads.count() > 0);
|
||||
try std.testing.expect(rp.protocol_heads.count() > 0);
|
||||
|
||||
const struct_tag = std.meta.Tag(resolver.RawDeclRef).struct_decl;
|
||||
const fn_tag = std.meta.Tag(resolver.RawDeclRef).fn_decl;
|
||||
const protocol_tag = std.meta.Tag(resolver.RawDeclRef).protocol_decl;
|
||||
|
||||
// (1) Namespace-qualified: keyed by a `field_access` node naming `helper_fn`,
|
||||
// resolved RAW to lib.sx's fn author (own of the namespace target).
|
||||
var saw_ns = false;
|
||||
var nit = rp.namespace_refs.iterator();
|
||||
while (nit.next()) |e| {
|
||||
const k = e.key_ptr.*;
|
||||
try std.testing.expect(k.data == .field_access); // namespace refs key the access node
|
||||
if (std.mem.eql(u8, k.data.field_access.field, "helper_fn")) {
|
||||
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_ns = true;
|
||||
}
|
||||
}
|
||||
try std.testing.expect(saw_ns);
|
||||
|
||||
// (2) Generic-struct head: the `Box(s64)` param type-expr is keyed in
|
||||
// generic_struct_heads and resolves RAW to the own generic struct.
|
||||
const use_box = findFn(prog.root, "use_box") orelse return error.MissingFn;
|
||||
const box_head = use_box.data.fn_decl.params[0].type_expr;
|
||||
try std.testing.expect(box_head.data == .parameterized_type_expr);
|
||||
const box_ref = rp.generic_struct_heads.get(box_head) orelse return error.BoxHeadNotKeyed;
|
||||
try std.testing.expect(box_ref == .authors);
|
||||
try std.testing.expect(box_ref.authors.own != null);
|
||||
try std.testing.expectEqual(struct_tag, std.meta.activeTag(box_ref.authors.own.?.raw));
|
||||
// and it is NOT mis-binned into the other head tables.
|
||||
try std.testing.expect(rp.type_fn_heads.get(box_head) == null);
|
||||
try std.testing.expect(rp.protocol_heads.get(box_head) == null);
|
||||
|
||||
// (3) Type-function head: `Make(s64)` keyed in type_fn_heads, RAW fn author.
|
||||
const use_make = findFn(prog.root, "use_make") orelse return error.MissingFn;
|
||||
const make_head = use_make.data.fn_decl.params[0].type_expr;
|
||||
try std.testing.expect(make_head.data == .parameterized_type_expr);
|
||||
const make_ref = rp.type_fn_heads.get(make_head) orelse return error.MakeHeadNotKeyed;
|
||||
try std.testing.expect(make_ref == .authors);
|
||||
try std.testing.expect(make_ref.authors.own != null);
|
||||
try std.testing.expectEqual(fn_tag, std.meta.activeTag(make_ref.authors.own.?.raw));
|
||||
|
||||
// (4) Protocol head: the `Cmp(s64)` value-type param keyed in protocol_heads.
|
||||
const use_cmp = findFn(prog.root, "use_cmp") orelse return error.MissingFn;
|
||||
const cmp_head = use_cmp.data.fn_decl.params[0].type_expr;
|
||||
try std.testing.expect(cmp_head.data == .parameterized_type_expr);
|
||||
const cmp_ref = rp.protocol_heads.get(cmp_head) orelse return error.CmpHeadNotKeyed;
|
||||
try std.testing.expect(cmp_ref == .authors);
|
||||
try std.testing.expect(cmp_ref.authors.own != null);
|
||||
try std.testing.expectEqual(protocol_tag, std.meta.activeTag(cmp_ref.authors.own.?.raw));
|
||||
|
||||
// (5) The three S2.1c domains remain UNTOUCHED by S2.1b.
|
||||
try std.testing.expectEqual(@as(u32, 0), rp.foreign_class_refs.count());
|
||||
try std.testing.expectEqual(@as(u32, 0), rp.struct_const_refs.count());
|
||||
try std.testing.expectEqual(@as(u32, 0), rp.ufcs_refs.count());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user