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:
@@ -1177,11 +1177,15 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
// Enum member access: `EnumName.MEMBER` or `pkg.EnumName.MEMBER`
|
||||
// → inline the pre-computed constant. With driver-side
|
||||
// concatenation, both forms key off the leaf type name.
|
||||
// → inline the pre-computed constant. `pkg.Enum.MEMBER` keeps
|
||||
// `pkg` so enumlookupmod can prefer the explicit module on a
|
||||
// leaf collision; bare `Enum.MEMBER` falls back to c.curmod via
|
||||
// enumlookup's same-module-first walk.
|
||||
if (lhs != nil) {
|
||||
let etname: str;
|
||||
let etmod: str;
|
||||
etname.ptr = nil; etname.len = 0;
|
||||
etmod.ptr = nil; etmod.len = 0;
|
||||
if (lhs.kind == nkind.N_IDENT) {
|
||||
etname = lhs.str;
|
||||
};
|
||||
@@ -1189,11 +1193,12 @@ fn cgdot(c: *cgen, n: *node) void = {
|
||||
if (lhs.lhs != nil) {
|
||||
if (lhs.lhs.kind == nkind.N_IDENT) {
|
||||
etname = lhs.str;
|
||||
etmod = lhs.lhs.str;
|
||||
};
|
||||
};
|
||||
};
|
||||
if (etname.len > 0) {
|
||||
let en: *enumtype = enumlookup(c, etname);
|
||||
let en: *enumtype = enumlookupmod(c, etname, etmod);
|
||||
if (en != nil) {
|
||||
let v: u64;
|
||||
if (enummemberval(en, fld, &v)) {
|
||||
|
||||
Reference in New Issue
Block a user