fix(imports): diagnose namespace-alias dup + propagate buildImportFacts errors [stdlib A attempt-2]
Two defects from the Phase A attempt-1 review.
F1 — duplicate-name diagnostic missed NAMESPACE ALIASES (silent first-win).
`addNamespace` unconditionally put the alias into scope/own_decls, so a
same-module collision between an authored decl and a `dup :: #import "…"`
alias compiled clean in the fn-then-alias order (the scalar
ModuleRawDeclIndex silently first-won). Now `addNamespace` returns a bool
and refuses a same-module duplicate (mirroring addOwnDecl); the call site
surfaces it via the new `reportDuplicateName` (the import_decl node has no
declName, so the alias name is passed explicitly). The C-import namespace
site gets the same guard. Both orders now emit "duplicate top-level
declaration 'X'" and exit nonzero (alias-then-fn was already caught by
addOwnDecl seeing the alias in scope).
F2 — buildImportFacts errors were swallowed by `else |_| {}` in core.zig
(REJECTED-PATTERN catch-all leaving the borrowed store silently empty).
`resolveImports` returns !void, so the call is now a plain `try` and a
build failure propagates instead of producing a stale/empty store.
Tests: extend the dup-name regression with fn-vs-namespace-alias
collisions in both orders. No resolution behavior change (no lower.zig
edits; run_examples 471 byte-identical); m3te ios-sim builds via the
worktree binary.
This commit is contained in:
@@ -449,3 +449,57 @@ test "buildImportFacts: same-module duplicate top-level name is diagnosed" {
|
||||
const m_idx = facts.decls.get(main_path) orelse return error.MissingMainIndex;
|
||||
try expectTag(m_idx.names.get("foo") orelse return error.MissingFoo, .fn_decl);
|
||||
}
|
||||
|
||||
// F1: the duplicate-name invariant must also cover NAMESPACE ALIASES. A
|
||||
// `dup :: #import "…"` alias colliding with a same-module authored name is a
|
||||
// duplicate in EITHER order — `addNamespace` (alias second) and `addOwnDecl`
|
||||
// (alias first) each refuse the second author and the site diagnoses it. Before
|
||||
// the fix the fn-then-alias order compiled clean (silent first-win in the scalar
|
||||
// index). Surviving author is whichever came FIRST.
|
||||
test "buildImportFacts: fn-then-namespace-alias same-module collision is diagnosed" {
|
||||
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 :: () -> s64 { 9 }\n" });
|
||||
try tmp.dir.writeFile(io, .{ .sub_path = "main.sx", .data = "dup :: () -> s64 { 1 }\ndup :: #import \"lib.sx\";\nmain :: () -> s32 { 0 }\n" });
|
||||
|
||||
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 facts = try buildFacts(alloc, io, absdir, main_path);
|
||||
|
||||
try std.testing.expect(hasErr(&facts.diags, "duplicate top-level declaration 'dup'"));
|
||||
// The fn came first, so it survives in the scalar index; the alias dropped.
|
||||
const m_idx = facts.decls.get(main_path) orelse return error.MissingMainIndex;
|
||||
try expectTag(m_idx.names.get("dup") orelse return error.MissingDup, .fn_decl);
|
||||
}
|
||||
|
||||
test "buildImportFacts: namespace-alias-then-fn same-module collision is diagnosed" {
|
||||
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 :: () -> s64 { 9 }\n" });
|
||||
try tmp.dir.writeFile(io, .{ .sub_path = "main.sx", .data = "dup :: #import \"lib.sx\";\ndup :: () -> s64 { 1 }\nmain :: () -> s32 { 0 }\n" });
|
||||
|
||||
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 facts = try buildFacts(alloc, io, absdir, main_path);
|
||||
|
||||
try std.testing.expect(hasErr(&facts.diags, "duplicate top-level declaration 'dup'"));
|
||||
// The alias came first, so the namespace_decl survives; the fn dropped.
|
||||
const m_idx = facts.decls.get(main_path) orelse return error.MissingMainIndex;
|
||||
try expectTag(m_idx.names.get("dup") orelse return error.MissingDup, .namespace_decl);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user