ww+wcc: graduate selfhost TK_* defs to tkind enum

`type tkind = enum i32 { TK_NONE = 0, TK_EOF = 1, ... TK_LAST = 86 }`
replaces the 87-line `def TK_*: i32 = N` cluster in lib/ww/lex/tok.ww.
Numeric values explicit so 990_selfhost's byte-diff against the C-side
`Tkind` enum still passes.

All ~270 reference sites in lib/ww and selfhost/cmd/{wcc,wwdump}
sed-renamed `TK_X` → `tkind.TK_X`. Struct fields (`tok.kind`,
`parser.curkind`) intentionally kept as `i32` — making them `tkind`
shifted some byte-positions in the cgen output and broke 990/993/995
byte-identity probes without an obvious win.

To make the rename non-cascading on every signature, type_assignable
and unify_arith in cmd/wcc/check+type relax to allow enum ↔ int
mixing when storage matches (a `tkind` value flows into an `i32`
slot and vice versa, no explicit cast). This deviates from Hare's
strict enum semantics; doc'd as an explicit pragmatic relaxation
for the compiler's internal enum-shaped kinds. External user code
can still get the type-safety benefit if they declare their
parameters with the enum type.

combined.ww files regenerated by ww build.
This commit is contained in:
2026-05-12 04:50:36 +09:00
parent fc49da44d8
commit 408ea2a322
14 changed files with 1806 additions and 1771 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -111,31 +111,31 @@ fn enumevalmember(prev: *enummember, e: *node, out: *u64) bool = {
if (!enumevalmember(prev, e.lhs, &a)) { return false; };
if (!enumevalmember(prev, e.rhs, &b)) { return false; };
let op: i32 = e.op;
if (op == TK_PLUS) { *out = a + b; return true; };
if (op == TK_MINUS) { *out = a - b; return true; };
if (op == TK_STAR) { *out = a * b; return true; };
if (op == TK_SLASH) {
if (op == tkind.TK_PLUS) { *out = a + b; return true; };
if (op == tkind.TK_MINUS) { *out = a - b; return true; };
if (op == tkind.TK_STAR) { *out = a * b; return true; };
if (op == tkind.TK_SLASH) {
if (b == 0u64) { return false; };
*out = a / b; return true;
};
if (op == TK_PERCENT) {
if (op == tkind.TK_PERCENT) {
if (b == 0u64) { return false; };
*out = a % b; return true;
};
if (op == TK_AMP) { *out = a & b; return true; };
if (op == TK_PIPE) { *out = a | b; return true; };
if (op == TK_CARET) { *out = a ^ b; return true; };
if (op == TK_LSHIFT) { *out = a << b; return true; };
if (op == TK_RSHIFT) { *out = a >> b; return true; };
if (op == tkind.TK_AMP) { *out = a & b; return true; };
if (op == tkind.TK_PIPE) { *out = a | b; return true; };
if (op == tkind.TK_CARET) { *out = a ^ b; return true; };
if (op == tkind.TK_LSHIFT) { *out = a << b; return true; };
if (op == tkind.TK_RSHIFT) { *out = a >> b; return true; };
return false;
};
if (k == N_UN) {
let v: u64;
if (!enumevalmember(prev, e.lhs, &v)) { return false; };
let op: i32 = e.op;
if (op == TK_MINUS) { *out = (-(v: i64)): u64; return true; };
if (op == TK_TILDE) { *out = ~v; return true; };
if (op == TK_PLUS) { *out = v; return true; };
if (op == tkind.TK_MINUS) { *out = (-(v: i64)): u64; return true; };
if (op == tkind.TK_TILDE) { *out = ~v; return true; };
if (op == tkind.TK_PLUS) { *out = v; return true; };
return false;
};
return false;

View File

@@ -935,10 +935,10 @@ fn cgun(c: *cgen, n: *node) void = {
// address / deref. The wasted load before AMP keeps our asm
// byte-identical to the C version.
cgexpr(c, n.lhs);
if (n.op == TK_MINUS) { emitline("\tNEGQ\tAX\n"); return; };
if (n.op == TK_TILDE) { emitline("\tNOTQ\tAX\n"); return; };
if (n.op == TK_STAR) { emitline("\tMOVQ\t(AX), AX\n"); return; };
if (n.op == TK_AMP) {
if (n.op == tkind.TK_MINUS) { emitline("\tNEGQ\tAX\n"); return; };
if (n.op == tkind.TK_TILDE) { emitline("\tNOTQ\tAX\n"); return; };
if (n.op == tkind.TK_STAR) { emitline("\tMOVQ\t(AX), AX\n"); return; };
if (n.op == tkind.TK_AMP) {
let opnd: *node = n.lhs;
if (opnd != nil) {
if (opnd.kind == N_IDENT) {
@@ -954,7 +954,7 @@ fn cgun(c: *cgen, n: *node) void = {
};
return;
};
if (n.op == TK_NOT) {
if (n.op == tkind.TK_NOT) {
let t: str = mklabel(c, "tt");
let e: str = mklabel(c, "te");
emitline("\tCMPQ\t$0, AX\n");
@@ -977,48 +977,48 @@ fn cgbin(c: *cgen, n: *node) void = {
emitline("\tPUSHQ\tAX\n");
cgexpr(c, n.lhs);
emitline("\tPOPQ\tBX\n");
if (n.op == TK_PLUS) { emitline("\tADDQ\tBX, AX\n"); return; };
if (n.op == TK_MINUS) { emitline("\tSUBQ\tBX, AX\n"); return; };
if (n.op == TK_STAR) { emitline("\tIMULQ\tBX, AX\n"); return; };
if (n.op == TK_SLASH) {
if (n.op == tkind.TK_PLUS) { emitline("\tADDQ\tBX, AX\n"); return; };
if (n.op == tkind.TK_MINUS) { emitline("\tSUBQ\tBX, AX\n"); return; };
if (n.op == tkind.TK_STAR) { emitline("\tIMULQ\tBX, AX\n"); return; };
if (n.op == tkind.TK_SLASH) {
emitline("\tMOVQ\t$0, DX\n");
if (unsignd) { emitline("\tDIVQ\tBX\n"); }
else { emitline("\tIDIVQ\tBX\n"); };
return;
};
if (n.op == TK_PERCENT) {
if (n.op == tkind.TK_PERCENT) {
emitline("\tMOVQ\t$0, DX\n");
if (unsignd) { emitline("\tDIVQ\tBX\n"); }
else { emitline("\tIDIVQ\tBX\n"); };
emitline("\tMOVQ\tDX, AX\n");
return;
};
if (n.op == TK_AMP) { emitline("\tANDQ\tBX, AX\n"); return; };
if (n.op == TK_PIPE) { emitline("\tORQ\tBX, AX\n"); return; };
if (n.op == TK_CARET) { emitline("\tXORQ\tBX, AX\n"); return; };
if (n.op == TK_LSHIFT) {
if (n.op == tkind.TK_AMP) { emitline("\tANDQ\tBX, AX\n"); return; };
if (n.op == tkind.TK_PIPE) { emitline("\tORQ\tBX, AX\n"); return; };
if (n.op == tkind.TK_CARET) { emitline("\tXORQ\tBX, AX\n"); return; };
if (n.op == tkind.TK_LSHIFT) {
emitline("\tMOVQ\tBX, CX\n");
emitline("\tSHLQ\tCX, AX\n");
return;
};
if (n.op == TK_RSHIFT) {
if (n.op == tkind.TK_RSHIFT) {
emitline("\tMOVQ\tBX, CX\n");
emitline("\tSHRQ\tCX, AX\n");
return;
};
if (n.op == TK_AND) { emitline("\tANDQ\tBX, AX\n"); return; };
if (n.op == TK_OR) { emitline("\tORQ\tBX, AX\n"); return; };
if (n.op == tkind.TK_AND) { emitline("\tANDQ\tBX, AX\n"); return; };
if (n.op == tkind.TK_OR) { emitline("\tORQ\tBX, AX\n"); return; };
// Comparison: emit CMPQ, jump on signed/unsigned variant,
// materialise 0/1 in AX. Same shape as the C cgen.
let iscmp: bool = false;
let jcc: str = "";
if (n.op == TK_EQ) { iscmp = true; jcc = "JE"; };
if (n.op == TK_NEQ) { iscmp = true; jcc = "JNE"; };
if (n.op == TK_LT) { iscmp = true; if (unsignd) { jcc = "JB"; } else { jcc = "JL"; }; };
if (n.op == TK_LE) { iscmp = true; if (unsignd) { jcc = "JBE"; } else { jcc = "JLE"; }; };
if (n.op == TK_GT) { iscmp = true; if (unsignd) { jcc = "JA"; } else { jcc = "JG"; }; };
if (n.op == TK_GE) { iscmp = true; if (unsignd) { jcc = "JAE"; } else { jcc = "JGE"; }; };
if (n.op == tkind.TK_EQ) { iscmp = true; jcc = "JE"; };
if (n.op == tkind.TK_NEQ) { iscmp = true; jcc = "JNE"; };
if (n.op == tkind.TK_LT) { iscmp = true; if (unsignd) { jcc = "JB"; } else { jcc = "JL"; }; };
if (n.op == tkind.TK_LE) { iscmp = true; if (unsignd) { jcc = "JBE"; } else { jcc = "JLE"; }; };
if (n.op == tkind.TK_GT) { iscmp = true; if (unsignd) { jcc = "JA"; } else { jcc = "JG"; }; };
if (n.op == tkind.TK_GE) { iscmp = true; if (unsignd) { jcc = "JAE"; } else { jcc = "JGE"; }; };
if (iscmp) {
let t: str = mklabel(c, "ct");
let e: str = mklabel(c, "ce");
@@ -1142,11 +1142,11 @@ fn cgassign(c: *cgen, n: *node) void = {
let lhs: *node = n.lhs;
// Discard lvalue `_ = expr;` — evaluate rhs for side effects,
// write nothing. Detected by lhs being an N_IDENT with empty str
// (planted by parseprimary on the TK_UNDER token).
// (planted by parseprimary on the tkind.TK_UNDER token).
if (lhs != nil) {
if (lhs.kind == N_IDENT) {
if (lhs.str.len == 0) {
if (n.op == TK_ASSIGN) {
if (n.op == tkind.TK_ASSIGN) {
cgexpr(c, n.rhs);
return;
};
@@ -1160,8 +1160,8 @@ fn cgassign(c: *cgen, n: *node) void = {
// `*bool` / `*u8` / `*i32` we narrow via the local's tnode.
if (lhs != nil) {
if (lhs.kind == N_UN) {
if (lhs.op == TK_STAR) {
if (n.op == TK_ASSIGN) {
if (lhs.op == tkind.TK_STAR) {
if (n.op == tkind.TK_ASSIGN) {
let inner: *node = lhs.lhs;
let elemstr: bool = false;
let storeop: str = "MOVQ";
@@ -1219,7 +1219,7 @@ fn cgassign(c: *cgen, n: *node) void = {
// from base.tnode picks MOVB vs MOVQ.
if (lhs != nil) {
if (lhs.kind == N_INDEX) {
if (n.op == TK_ASSIGN) {
if (n.op == tkind.TK_ASSIGN) {
let base: *node = lhs.lhs;
let idx: *node = lhs.rhs;
let esz: i32 = 8;
@@ -1308,7 +1308,7 @@ fn cgassign(c: *cgen, n: *node) void = {
for (fi != nil) {
let fn_: str = fi.fname;
if (streq(fn_, fld)) {
if (n.op != TK_ASSIGN) {
if (n.op != tkind.TK_ASSIGN) {
// compound: load current value
emitline("\tMOVQ\t");
emitoff(lc.off: i64);
@@ -1322,13 +1322,13 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tPUSHQ\tBX\n");
};
cgexpr(c, n.rhs);
if (n.op != TK_ASSIGN) {
if (n.op != tkind.TK_ASSIGN) {
emitline("\tPOPQ\tBX\n");
// PLUSEQ is commutative; MINUSEQ
// needs lhs - rhs (BX is old lhs,
// AX is rhs).
if (n.op == TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); };
if (n.op == TK_MINUSEQ) {
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); };
if (n.op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
};
@@ -1337,7 +1337,7 @@ fn cgassign(c: *cgen, n: *node) void = {
// (AX=ptr, BX=len). Use CX as the
// address scratch so we don't clobber
// the len half before storing it.
if (n.op == TK_ASSIGN) {
if (n.op == tkind.TK_ASSIGN) {
if (isstrtype(c, fi.tnode)) {
emitline("\tMOVQ\t");
emitoff(lc.off: i64);
@@ -1405,7 +1405,7 @@ fn cgassign(c: *cgen, n: *node) void = {
};
if (innerkind == N_TSLICE) { innerstr = true; };
if (innerstr) {
if (n.op != TK_ASSIGN) {
if (n.op != tkind.TK_ASSIGN) {
// Compound on `(*str|*slice).field`: load
// current → push → eval rhs → combine → store.
emitline("\tMOVQ\t");
@@ -1419,8 +1419,8 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tPOPQ\tBX\n");
// PLUSEQ is commutative; MINUSEQ
// needs lhs - rhs.
if (n.op == TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); };
if (n.op == TK_MINUSEQ) {
if (n.op == tkind.TK_PLUSEQ) { emitline("\tADDQ\tBX, AX\n"); };
if (n.op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, BX\n");
emitline("\tMOVQ\tBX, AX\n");
};
@@ -1475,7 +1475,7 @@ fn cgassign(c: *cgen, n: *node) void = {
let fi: *fieldinfo = si.fields;
for (fi != nil) {
if (streq(fi.fname, fld)) {
if (n.op == TK_ASSIGN) {
if (n.op == tkind.TK_ASSIGN) {
if (isstrtype(c, fi.tnode)) {
// str rhs: AX=ptr, BX=len.
// Stash both, then load
@@ -1532,7 +1532,7 @@ fn cgassign(c: *cgen, n: *node) void = {
let lcn: *local = localfindnode(c, nm);
if (lcn != nil) { lcstr = isstrtype(c, lcn.tnode); };
cgexpr(c, n.rhs);
if (n.op == TK_ASSIGN) {
if (n.op == tkind.TK_ASSIGN) {
emitline("\tMOVQ\tAX, ");
emitoff(off: i64);
emitline("(BP)\n");
@@ -1543,13 +1543,13 @@ fn cgassign(c: *cgen, n: *node) void = {
};
return;
};
if (n.op == TK_PLUSEQ) {
if (n.op == tkind.TK_PLUSEQ) {
emitline("\tADDQ\tAX, ");
emitoff(off: i64);
emitline("(BP)\n");
return;
};
if (n.op == TK_MINUSEQ) {
if (n.op == tkind.TK_MINUSEQ) {
emitline("\tSUBQ\tAX, ");
emitoff(off: i64);
emitline("(BP)\n");
@@ -1559,15 +1559,15 @@ fn cgassign(c: *cgen, n: *node) void = {
emitline("\tMOVQ\t");
emitoff(off: i64);
emitline("(BP), BX\n");
if (n.op == TK_STAREQ) { emitline("\tIMULQ\tAX, BX\n"); };
if (n.op == TK_AMPEQ) { emitline("\tANDQ\tAX, BX\n"); };
if (n.op == TK_PIPEEQ) { emitline("\tORQ\tAX, BX\n"); };
if (n.op == TK_CARETEQ) { emitline("\tXORQ\tAX, BX\n"); };
if (n.op == TK_LSHIFTEQ) {
if (n.op == tkind.TK_STAREQ) { emitline("\tIMULQ\tAX, BX\n"); };
if (n.op == tkind.TK_AMPEQ) { emitline("\tANDQ\tAX, BX\n"); };
if (n.op == tkind.TK_PIPEEQ) { emitline("\tORQ\tAX, BX\n"); };
if (n.op == tkind.TK_CARETEQ) { emitline("\tXORQ\tAX, BX\n"); };
if (n.op == tkind.TK_LSHIFTEQ) {
emitline("\tMOVQ\tAX, CX\n");
emitline("\tSHLQ\tCX, BX\n");
};
if (n.op == TK_RSHIFTEQ) {
if (n.op == tkind.TK_RSHIFTEQ) {
emitline("\tMOVQ\tAX, CX\n");
emitline("\tSHRQ\tCX, BX\n");
};

View File

@@ -403,7 +403,7 @@ fn cglet(c: *cgen, n: *node) void = {
// For each field in the lit, evaluate its value and store at
// the field's offset within the slot. Field-name → offset
// from the struct registry. When the literal carries
// op == TK_ELLIPSIS (autofill marker from the parser), the
// op == tkind.TK_ELLIPSIS (autofill marker from the parser), the
// entire slot is zero-filled first so unmentioned fields
// read as 0.
if (rhs.kind == N_STRUCTLIT) {
@@ -416,7 +416,7 @@ fn cglet(c: *cgen, n: *node) void = {
};
let si: *structinfo = structlookup(c, sname);
if (si != nil) {
if (rhs.op == TK_ELLIPSIS) {
if (rhs.op == tkind.TK_ELLIPSIS) {
let total: i32 = si.totsize;
emitline("\tXORQ\tAX, AX\n");
let zi: i32 = 0;

File diff suppressed because it is too large Load Diff

View File

@@ -122,8 +122,8 @@ export fn main(argc: i32, argv: **u8) i32 = {
let t: tok;
lexnext(&l, &t);
tokprint(1i32, &t);
if (t.kind == TK_EOF) { break; };
if (t.kind == TK_ERR) { break; };
if (t.kind == tkind.TK_EOF) { break; };
if (t.kind == tkind.TK_ERR) { break; };
};
} else { if (mode == 97) { // '-a'
let ps: parser;