selfhost+cstage+test: graduate enumlookup same-module-first + N_DOT enumlookupmod (#4a)
Class A silent miscompile, latent until two modules export the same enum leaf name. Wwstage's enumlookup (selfhost/cmd/wcc/cgen.ww) walked c.enums head-first by ename; cgdot handed it the bare leaf from N_DOT.lhs.str for both `Color.MEMBER` (lhs N_IDENT) and `pkg.Color.MEMBER` (lhs N_DOT) shapes, silently dropping the explicit qualifier on the second. Cstage's enum-member fold (cmd/wcc/check.c cexpr N_DOT) was carrying the same head-pick on the lhs-ident lookup — pre-fix the mismatch surfaced as a "not assignable to <same-leaf>" checker error rather than a silent wrong-constant because resolve_typename for the fn return spec already used scope_lookup_prefer correctly, so the rhs's wrong- module-Color clashed with the return type's right-module-Color. No in-tree corpus currently declares two same-leaf enums, so 995_self_rebuild stayed green and the latent miscompile only surfaces once a stdlib port introduces the collision (same shape as #27 surfacing when lib/strings dragged utf8's invalid alias into the chain alongside strconv's invalid). Fourth leaf of the trio leaf-name lookup graduation (after #27 aliaslookup, #28 fnparamslookupmod, #31 fnretlookupmod): wwstage enumlookup grows a same-module-first walk before the head-walk fallback, mirroring aliaslookup's two-pass shape (cgen.ww:75). The N_DOT consumer surface — `pkg.Enum.MEMBER`, already used in-corpus by os.flag.RDONLY, temp.mode.RDWR, os.whence.SET etc. — routes through a new enumlookupmod variant with the explicit N_DOT.lhs.lhs.str as the mod qualifier (mirror of fnret/ fnparamslookupmod). Cstage's check.c cexpr N_DOT lhs lookup graduates from scope_lookup to scope_lookup_prefer to align symmetrically (rule 10: both stages pick same-module-first on the bare-leaf shape). 733_enum_modshadow pins both surfaces with 3 rows: row 1 bare-leaf in module M must fold against M's own Color even with another module's same-leaf Color at the head of c.enums; row 2 same-module `mod.Color.MEMBER` from inside that mod pins the API surface; row 3 cross-module `othermod.Color.MEMBER` from a third module with no local Color sentinel-flips the cgdot etmod tracking + enumlookupmod path independently of row 1's same-module-first fallback. Asserts the matching \$N, immediate inside the right TEXT sym + bad_imm NOT-presence anti-check on both stages plus byte-id between stages per row.
This commit is contained in:
@@ -737,9 +737,14 @@ cexpr(Checker *c, Node *n)
|
||||
/* module-qualified: lhs is an N_IDENT bound as SK_USE.
|
||||
* Resolve to the symbol with the same leaf name. With
|
||||
* driver-side concatenation, all symbols live in flat
|
||||
* scope, so we lookup `n->str` directly. */
|
||||
* scope, so we lookup `n->str` directly. Same-module-
|
||||
* first via _prefer keeps a bare-leaf enum `Color.M`
|
||||
* inside module M from collapsing onto another module's
|
||||
* Color sitting at the head of the flat scope chain —
|
||||
* symmetric with wwstage's enumlookup graduation. */
|
||||
if (n->lhs && n->lhs->kind == N_IDENT) {
|
||||
Sym *ms = scope_lookup(c->cur, n->lhs->str);
|
||||
Sym *ms = scope_lookup_prefer(c->cur, c->cur_mod,
|
||||
n->lhs->str);
|
||||
if (ms && (ms->kind == SK_USE || ms->use_alias)) {
|
||||
/* Module-qualified ref. `use_alias` covers
|
||||
* the self-import case where the module's
|
||||
|
||||
Reference in New Issue
Block a user