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:
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 }
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user