wcc+selfhost: pkg-qualified enum access (os.whence.CUR)

Driver-side concatenation flattens module names, but enum member
lookup keyed off the exact lhs ident — so `whence.CUR` worked while
`os.whence.CUR` fell through to w6l with `undefined main.whence`.

C side: fold TY_ENUM members in the post-cexpr cascade too, not
just the early SK_TYPE shortcut. The recursive cexpr lands the
inner N_DOT(os, whence) on the named enum type; the outer access
then folds normally.

Selfhost: cgdot now treats `N_IDENT.MEMBER` and `N_DOT.MEMBER` the
same way, keying off the leaf name. enumlookup strips a trailing
`.`-prefix from the lookup key.

Adds e2e test 700: `use os; os.whence.CUR as i32 == 1`.
This commit is contained in:
2026-05-12 04:30:20 +09:00
parent f597ce67f6
commit 5149d10618
6 changed files with 120 additions and 12 deletions

View File

@@ -615,11 +615,24 @@ fn cgmatch(c: *cgen, n: *node) void = {
fn cgdot(c: *cgen, n: *node) void = {
let lhs: *node = n.lhs;
let fld: str = n.str;
// Enum member access: `EnumName.MEMBER` → inline the constant.
// Pre-computed at collectenums time; no value-expr fold here.
// 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.
if (lhs != nil) {
let etname: str;
etname.ptr = nil; etname.len = 0;
if (lhs.kind == N_IDENT) {
let en: *enumtype = enumlookup(c, lhs.str);
etname = lhs.str;
};
if (lhs.kind == N_DOT) {
if (lhs.lhs != nil) {
if (lhs.lhs.kind == N_IDENT) {
etname = lhs.str;
};
};
};
if (etname.len > 0) {
let en: *enumtype = enumlookup(c, etname);
if (en != nil) {
let v: u64;
if (enummemberval(en, fld, &v)) {