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

@@ -712,6 +712,29 @@ cexpr(Checker *c, Node *n)
Type *u = (base->kind == TY_NAMED) ? base->under : base;
if (u && u->kind == TY_PTR) u = u->sub;
if (u && u->kind == TY_NAMED) u = u->under;
/* Enum member access via a qualified base, e.g. `os.whence.CUR`.
* The inner N_DOT resolved through SK_USE → the SK_TYPE sym's
* named type. Fold the outer access to the member literal. */
if (u && u->kind == TY_ENUM) {
for (Tfield *f = u->fields; f; f = f->next) {
if (f->name && n->str &&
strcmp(f->name, n->str) == 0) {
n->kind = N_INTLIT;
n->uval = f->offset;
n->str = aprintf(c->a, "%llu",
(unsigned long long)f->offset);
n->strlen = strlen(n->str);
n->lhs = NULL;
n->rhs = NULL;
n->tsuffix = NULL;
return n->type = base;
}
}
return n->type = err(c, n->pos,
"no enum member '%s' in %s",
n->str ? n->str : "?",
type_name(c->a, base));
}
/* built-in pseudo-fields on slice/str/array: .len, .cap, .ptr */
if (u && (u->kind == TY_SLICE || u->kind == TY_ARRAY ||
u->kind == TY_STR)) {