diff --git a/cmd/wcc/check.c b/cmd/wcc/check.c index 990b625e..0003d499 100644 --- a/cmd/wcc/check.c +++ b/cmd/wcc/check.c @@ -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)) { diff --git a/selfhost/cmd/w6c/main.combined.ww b/selfhost/cmd/w6c/main.combined.ww index 76a0c799..362a536f 100644 --- a/selfhost/cmd/w6c/main.combined.ww +++ b/selfhost/cmd/w6c/main.combined.ww @@ -6307,11 +6307,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)) { @@ -8476,9 +8489,22 @@ fn collectenums(c: *cgen, file: *node) void = { }; fn enumlookup(c: *cgen, name: str) *enumtype = { + // Strip any `pkg.` prefix and key off the leaf — driver-side + // concatenation flattens the namespace, so `os.whence` and + // `whence` refer to the same registered enum. + let leaf: str = name; + let i: i32 = name.len - 1; + for (i >= 0) { + if (name[i] == 46u8) { // '.' + leaf.ptr = name.ptr + (i + 1): u64; + leaf.len = name.len - (i + 1); + break; + }; + i -= 1; + }; let e: *enumtype = c.enums; for (e != nil) { - if (streq(e.ename, name)) { return e; }; + if (streq(e.ename, leaf)) { return e; }; e = e.etnext; }; return nil; diff --git a/selfhost/cmd/wcc/cgen.ww b/selfhost/cmd/wcc/cgen.ww index 90956118..881cde9c 100644 --- a/selfhost/cmd/wcc/cgen.ww +++ b/selfhost/cmd/wcc/cgen.ww @@ -186,9 +186,22 @@ fn collectenums(c: *cgen, file: *node) void = { }; fn enumlookup(c: *cgen, name: str) *enumtype = { + // Strip any `pkg.` prefix and key off the leaf — driver-side + // concatenation flattens the namespace, so `os.whence` and + // `whence` refer to the same registered enum. + let leaf: str = name; + let i: i32 = name.len - 1; + for (i >= 0) { + if (name[i] == 46u8) { // '.' + leaf.ptr = name.ptr + (i + 1): u64; + leaf.len = name.len - (i + 1); + break; + }; + i -= 1; + }; let e: *enumtype = c.enums; for (e != nil) { - if (streq(e.ename, name)) { return e; }; + if (streq(e.ename, leaf)) { return e; }; e = e.etnext; }; return nil; diff --git a/selfhost/cmd/wcc/cgenexpr.ww b/selfhost/cmd/wcc/cgenexpr.ww index b715752c..7562673e 100644 --- a/selfhost/cmd/wcc/cgenexpr.ww +++ b/selfhost/cmd/wcc/cgenexpr.ww @@ -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)) { diff --git a/selfhost/cmd/wwdump/main.combined.ww b/selfhost/cmd/wwdump/main.combined.ww index 2e7c9e5a..e4450fdb 100644 --- a/selfhost/cmd/wwdump/main.combined.ww +++ b/selfhost/cmd/wwdump/main.combined.ww @@ -6307,11 +6307,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)) { @@ -8476,9 +8489,22 @@ fn collectenums(c: *cgen, file: *node) void = { }; fn enumlookup(c: *cgen, name: str) *enumtype = { + // Strip any `pkg.` prefix and key off the leaf — driver-side + // concatenation flattens the namespace, so `os.whence` and + // `whence` refer to the same registered enum. + let leaf: str = name; + let i: i32 = name.len - 1; + for (i >= 0) { + if (name[i] == 46u8) { // '.' + leaf.ptr = name.ptr + (i + 1): u64; + leaf.len = name.len - (i + 1); + break; + }; + i -= 1; + }; let e: *enumtype = c.enums; for (e != nil) { - if (streq(e.ename, name)) { return e; }; + if (streq(e.ename, leaf)) { return e; }; e = e.etnext; }; return nil; diff --git a/test/wcc/700_e2e.c b/test/wcc/700_e2e.c index 5b95d47c..89ede8ac 100644 --- a/test/wcc/700_e2e.c +++ b/test/wcc/700_e2e.c @@ -1100,6 +1100,13 @@ static const struct row rows[] = { " let m: mode = mode.R | mode.W;\n" " return m as i32;\n" "};", 3 }, + /* enum: pkg-qualified access — `os.whence.CUR` resolves through + * SK_USE and folds to the member literal. */ + { "// MODULE: os\n" + "type whence = enum { SET, CUR, END };\n" + "// MODULE: main\n" + "use os;\n" + "fn main() i32 = { return os.whence.CUR as i32; };", 1 }, { NULL, 0 } };