wcc/ww: reject duplicate top-level decls, aligned to cstage
installdecl routes all four kinds (fn/type/def/let) through installtop, which turns scopedefineinmodule's nil return into cstage's exact "duplicate <kind> <name>" reject, keyed (name,mod) so cross-package same-leaf decls coexist. Builtin redecls are dropped, not dup-errored: cstage never scopes builtins (lookup_builtin first, check.c:69), so a user redecl is dead there — wwstage mirrors via scopesamekeysym + no-source-decl test. -T synth __wwtests installs direct, mirroring check.c:3079. Closes the silent dup-fn hole (user fn run vs lib/test run built a broken test binary with no diagnostic). Per-kind reject rows + cross-package/builtin accept byte-id rows + -T collision parity row in 910/997. (#23-team, category-A addendum closed)
This commit is contained in:
@@ -276,3 +276,27 @@ export fn scopedefineinmodule(s: *scope, name: str, mod: str, k: skind, t: *tinf
|
||||
s.last = sy;
|
||||
return sy;
|
||||
};
|
||||
|
||||
// scopesamekeysym — the entry scopedefineinmodule(name, mod) treats as a
|
||||
// duplicate (same name, same mod-key), or nil if the key is free. Lets a
|
||||
// caller that got a nil from scopedefineinmodule learn WHAT it collided
|
||||
// with (e.g. a pre-seeded builtin vs a genuine user redeclaration). The
|
||||
// match logic mirrors scopedefineinmodule's reject branch exactly.
|
||||
export fn scopesamekeysym(s: *scope, name: str, mod: str) *sym = {
|
||||
let h: u64 = hashstr(name);
|
||||
let bi: i32 = (h % (s.nbuckets: u64)): i32;
|
||||
let b: *sym = s.buckets[bi];
|
||||
for (b != nil) {
|
||||
if (streq(b.name, name)) {
|
||||
if (b.mod.len == 0) {
|
||||
if (mod.len == 0) { return b; };
|
||||
} else {
|
||||
if (mod.len > 0) {
|
||||
if (streq(b.mod, mod)) { return b; };
|
||||
};
|
||||
};
|
||||
};
|
||||
b = b.hashnext;
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user