wcc/ww: promote module-name SK_USE to value kind (use_alias), aligned to cstage
A primary-package top-level decl whose leaf also names a bundled module (`type sym` vs `import sym`; the lib/test->fnmatch->ascii -T floor's `ascii` module vs a `@test fn ascii`) collided in the flat scope: #23's installtop dup-check false-fired "duplicate fn/type" where cstage coexists. cstage keys the module name out of the value namespace by PROMOTING the same-leaf SK_USE in place to the value kind with use_alias=1 (cmd/wcc/check.c:2831/2848/2871/2907/2928/2951), so one correctly-kinded sym serves bare refs (call/structlit/var) while `name.member` still resolves the module via the `kind==SK_USE || use_alias` N_DOT guards (check.c:87/1337). This REVERSES wwstage's documented two-sym coexistence design (lib/ww/sym.ww scopelookupuselocal): keeping the SK_USE as a separate coexisting sym ripples into every bare-reference resolver (~25 scopelookup sites), and a missed site is a byte-id-consistent-but-wrong cat-A risk the gate cannot prove away — the same failure mode retired with the name-keyed variant-match cluster. The promote model is correct by construction: one sym of the right kind, identical to cstage. sym gains a use_alias field; the three N_DOT/N_CALL module-qualified guards honor use_alias. cstage installs every SK_USE in a dedicated first pass, so its value-arm promote is order-INDEPENDENT; wwstage installs in source order, so BOTH directions of the collision are promoted to reach cstage's identical single-sym end state: - use-before-value (`import aa` then `fn aa`): installtop promotes the pre-installed SK_USE to the value kind (use_alias=1). - value-before-use (`fn aa` then `import aa`): installdecl's N_USE arm promotes the pre-installed value sym in place (set use_alias=1, no coexisting SK_USE), mirroring cstage's self-import N_USE arm (check.c:2823-2834 `if (prev) prev->use_alias = 1`). Both orders compile + are cs/ww byte-identical AND byte-identical to each other. Cannot split: promote without the guards leaves `name.member` red on the promoted sym; the guards without promote are inert (no use_alias is ever set) — no bisect-clean intermediate. (#30) Pins (910/997): modfn_coexist_ok (use-before-value) AND modfn_coexist_vbu_ok (value-before-use) both accept on cstage + are cs/ww byte-id + run to exit 6 (bare fn and qualified module both resolve); the dup_fn row still rejects both stages (regression). fnmatch byte-id holds. The order-dependence is exactly what regresses silently, so both orders are pinned.
This commit is contained in:
@@ -25,6 +25,12 @@ type sym = struct {
|
||||
decl: *node,
|
||||
exported: i32,
|
||||
is_const: i32, // const-bound (assignment rejected)
|
||||
use_alias: i32, // #30: this value/type decl ALSO names an imported
|
||||
// module (the fnmatch.fnmatch / random.random shape).
|
||||
// Set when installtop promotes a same-leaf SK_USE in
|
||||
// place; the N_DOT guards treat such a sym as a module
|
||||
// for `name.member`. Mirror cstage Sym.use_alias
|
||||
// (cmd/wcc/check.c:2831-2951 promote + 87/1337 guards).
|
||||
mod: str, // importing module's bareword for symbols
|
||||
// from a `use`-imported module; "" for primary
|
||||
// (root) compilation unit symbols. Used by
|
||||
@@ -145,14 +151,20 @@ export fn scopelookuptype(s: *scope, name: str) *sym = {
|
||||
// so this returns nil and the dot stays field access. Only a genuine
|
||||
// same-scope coexistence (top-level use + top-level type/fn) re-resolves.
|
||||
//
|
||||
// This is the coexistence-equivalent of cstage's Sym.use_alias bit
|
||||
// (cmd/wcc/check.c: set at the SK_USE→SK_X promotion sites, consulted by
|
||||
// the `kind == SK_USE || use_alias` dot guards in resolve_typename and
|
||||
// cexpr's N_DOT arm). Wwstage installs the SK_USE and the same-leaf
|
||||
// type/fn as SEPARATE coexisting entries (see selfhost/cmd/wcc/check.ww
|
||||
// installdecl), so no flag is needed — the SK_USE is never overwritten,
|
||||
// only out-preferred. Cite: project memory module_type_name_collision
|
||||
// (cstage fix 2026-05-13).
|
||||
// #30 (design reversal): this serves the DISTINCT-mod two-sym case only —
|
||||
// a `fn fnmatch` (mod="fnmatch") coexisting with `import fnmatch`'s SK_USE
|
||||
// (mod=""), where scopelookupprefer lands on the value and the dot re-
|
||||
// resolves to the SK_USE here. The SAME-mod collision (a primary-package
|
||||
// decl whose leaf also names a bundled module — `type sym` vs `import sym`,
|
||||
// `@test fn ascii` vs the fnmatch->ascii bundle floor) is NO LONGER left to
|
||||
// two coexisting syms: that ripples into every bare-ref resolver (a missed
|
||||
// site is a byte-id-consistent-but-wrong cat-A risk the gate can't prove
|
||||
// away). Instead installtop now PROMOTES the SK_USE in place to the value
|
||||
// kind with use_alias=1 (selfhost/cmd/wcc/check.ww installtop), mirroring
|
||||
// cstage's Sym.use_alias promote exactly (cmd/wcc/check.c:2831-2951); the
|
||||
// N_DOT guards honor `skind == SK_USE || use_alias` directly. ONE
|
||||
// correctly-kinded sym → all resolvers correct by construction. Cite:
|
||||
// task #30; project memory module_type_name_collision (cstage 2026-05-13).
|
||||
export fn scopelookupuselocal(s: *scope, name: str) *sym = {
|
||||
if (s == nil) { return nil; };
|
||||
let h: u64 = hashstr(name);
|
||||
@@ -270,7 +282,7 @@ export fn scopedefineinmodule(s: *scope, name: str, mod: str, k: skind, t: *tinf
|
||||
};
|
||||
b = b.hashnext;
|
||||
};
|
||||
let sy: *sym = alloc(sym{name=name, skind=k, type_=t, decl=decl, exported=0, is_const=0, mod=mod, snext=nil, hashnext=s.buckets[bi], scope=s})!;
|
||||
let sy: *sym = alloc(sym{name=name, skind=k, type_=t, decl=decl, exported=0, is_const=0, use_alias=0, mod=mod, snext=nil, hashnext=s.buckets[bi], scope=s})!;
|
||||
s.buckets[bi] = sy;
|
||||
if (s.first == nil) { s.first = sy; } else { s.last.snext = sy; };
|
||||
s.last = sy;
|
||||
|
||||
Reference in New Issue
Block a user