ww+wcc: Hare-strict enum types — back out the int↔enum relaxation
Cascades the four enum kinds through every signature and local that
holds one of their values, then removes the type_assignable /
unify_arith relaxation that previously let bare i32 mix with the
named enum types.
Signature updates:
- kwlookup() now returns `tkind` (not i32); tokname() takes `tkind`
- accepttok / expecttok / bprec / isassignop take `tkind`
- parsearglist's closekind is `tkind`
- newtype / prim take `tykind`; scopedefine takes `skind`
- newnode / nkname take `nkind`
Struct fields:
- tok.kind is `tkind`; parser.curkind is `tkind`
- node.kind is `nkind`; node.op is `tkind`
- tinfo.kind is `tykind`; sym.skind is `skind`
Locals holding kinds across lex/parse/check/cgen are now typed with
their enum, including sentinel patterns like `let lkind: nkind =
nkind.N_NONE; if (...) lkind = tn.kind;`.
The selfhost cgen had a load-width bug exposed by this: fieldsize()
fell back to 8 bytes for any TNAME that wasn't a struct or primitive.
For a tkind-typed field that gave `MOVQ (BX), AX` instead of `MOVL`,
diverging from the C cgen on tok.kind / parser.curkind / etc. Two
fixes:
- fieldsize now consults the enum registry and returns the storage
type's size (4 for `enum i32`)
- collectenums runs before collectstructs in cgfile so the registry
is populated when registerstruct asks for field sizes
All 22 tests stay green; 990/993/995 byte-identity probes pass with
the strict typing in place.
This commit is contained in:
@@ -103,11 +103,11 @@ type nkind = enum i32 {
|
||||
// ---- Node -------------------------------------------------------------
|
||||
|
||||
type node = struct {
|
||||
kind: i32,
|
||||
kind: nkind,
|
||||
file: str,
|
||||
line: i32,
|
||||
col: i32,
|
||||
op: i32, // for nkind.N_BIN / nkind.N_UN / nkind.N_ASSIGN
|
||||
op: tkind, // for nkind.N_BIN / nkind.N_UN / nkind.N_ASSIGN
|
||||
str: str,
|
||||
uval: u64,
|
||||
fval: f64,
|
||||
@@ -125,7 +125,7 @@ type node = struct {
|
||||
module: str, // originating module from `// MODULE: foo`; "" if none
|
||||
};
|
||||
|
||||
export fn newnode(a: *arena, k: i32, file: str, line: i32, col: i32) *node = {
|
||||
export fn newnode(a: *arena, k: nkind, file: str, line: i32, col: i32) *node = {
|
||||
let n: *node = amalloc(a, 208u64): *node; // ≥ struct size
|
||||
n.kind = k;
|
||||
n.file = file;
|
||||
@@ -136,7 +136,7 @@ export fn newnode(a: *arena, k: i32, file: str, line: i32, col: i32) *node = {
|
||||
|
||||
// ---- printer ----------------------------------------------------------
|
||||
|
||||
fn nkname(k: i32) str = {
|
||||
fn nkname(k: nkind) str = {
|
||||
if (k == nkind.N_NONE) { return "none"; };
|
||||
if (k == nkind.N_INTLIT) { return "int"; };
|
||||
if (k == nkind.N_FLOATLIT) { return "float"; };
|
||||
|
||||
@@ -460,7 +460,7 @@ fn lexident(l: *lex, start: *pos, out: *tok) void = {
|
||||
return;
|
||||
};
|
||||
};
|
||||
let k: i32 = kwlookup(p, n: i32);
|
||||
let k: tkind = kwlookup(p, n: i32);
|
||||
if (k != tkind.TK_NONE) {
|
||||
out.kind = k;
|
||||
} else {
|
||||
@@ -553,7 +553,7 @@ fn lexrune(l: *lex, start: *pos, out: *tok) void = {
|
||||
out.uval = ch: u64;
|
||||
};
|
||||
|
||||
fn emitsimple(start: *pos, k: i32, out: *tok) void = {
|
||||
fn emitsimple(start: *pos, k: tkind, out: *tok) void = {
|
||||
out.kind = k;
|
||||
out.file = start.file;
|
||||
out.line = start.line;
|
||||
|
||||
@@ -134,8 +134,7 @@ type pos = struct {
|
||||
};
|
||||
|
||||
type tok = struct {
|
||||
kind: i32, // holds a `tkind` value; bare i32 so the cgen's
|
||||
// fixed 8B/struct-field layout matches the C side
|
||||
kind: tkind,
|
||||
file: str, // path of the source the token came from
|
||||
line: i32,
|
||||
col: i32,
|
||||
@@ -160,7 +159,7 @@ fn streqn(a: *u8, b: str, n: i32) bool = {
|
||||
// kwlookup — returns the matching TK_* keyword kind for a byte run,
|
||||
// or tkind.TK_NONE if it's an ordinary identifier. Linear search over a
|
||||
// small alphabetised list, matching cmd/wcc/tok.c.
|
||||
export fn kwlookup(p: *u8, n: i32) i32 = {
|
||||
export fn kwlookup(p: *u8, n: i32) tkind = {
|
||||
if (streqn(p, "as", n)) { return tkind.TK_AS; };
|
||||
if (streqn(p, "break", n)) { return tkind.TK_BREAK; };
|
||||
if (streqn(p, "case", n)) { return tkind.TK_CASE; };
|
||||
@@ -198,7 +197,7 @@ export fn kwlookup(p: *u8, n: i32) i32 = {
|
||||
// Returns the canonical printable spelling for a token kind. Matches
|
||||
// the C tokname()'s output exactly so wwdump output diffs cleanly.
|
||||
|
||||
export fn tokname(k: i32) str = {
|
||||
export fn tokname(k: tkind) str = {
|
||||
if (k == tkind.TK_NONE) { return "<none>"; };
|
||||
if (k == tkind.TK_EOF) { return "EOF"; };
|
||||
if (k == tkind.TK_ERR) { return "ERR"; };
|
||||
|
||||
@@ -199,7 +199,7 @@ fn parseprimary(p: *parser) *node = {
|
||||
return newnode(p.a, nkind.N_NONE, pf, pl, pc);
|
||||
};
|
||||
|
||||
fn parsearglist(p: *parser, closekind: i32, headout: **node) void = {
|
||||
fn parsearglist(p: *parser, closekind: tkind, headout: **node) void = {
|
||||
*headout = nil;
|
||||
if (p.curkind == closekind) { return; };
|
||||
let head: *node = nil;
|
||||
@@ -355,7 +355,7 @@ fn parseunary(p: *parser) *node = {
|
||||
let pf: str = p.curfile;
|
||||
let pl: i32 = p.curline;
|
||||
let pc: i32 = p.curcol;
|
||||
let k: i32 = p.curkind;
|
||||
let k: tkind = p.curkind;
|
||||
if (k == tkind.TK_MINUS) {
|
||||
advance(p);
|
||||
let n: *node = newnode(p.a, nkind.N_UN, pf, pl, pc);
|
||||
@@ -398,7 +398,7 @@ fn parseunary(p: *parser) *node = {
|
||||
fn parsebin(p: *parser, lhs: *node, minp: i32) *node = {
|
||||
let cur: *node = lhs;
|
||||
for (true) {
|
||||
let op: i32 = p.curkind;
|
||||
let op: tkind = p.curkind;
|
||||
let pr: i32 = bprec(op);
|
||||
if (pr == 0) { return cur; };
|
||||
if (pr < minp) { return cur; };
|
||||
@@ -425,7 +425,7 @@ fn parseexpr(p: *parser) *node = {
|
||||
let pf: str = p.curfile;
|
||||
let pl: i32 = p.curline;
|
||||
let pc: i32 = p.curcol;
|
||||
let op: i32 = p.curkind;
|
||||
let op: tkind = p.curkind;
|
||||
advance(p);
|
||||
let n: *node = newnode(p.a, nkind.N_ASSIGN, pf, pl, pc);
|
||||
n.op = op;
|
||||
|
||||
@@ -24,7 +24,7 @@ type parser = struct {
|
||||
// nocast: while inside `[...]` we treat ':' as the slice
|
||||
// separator, not the cast operator. Mirrors parse.c's flag.
|
||||
nocast: i32,
|
||||
curkind: i32, // holds a `tkind` value (relaxation lets it mix)
|
||||
curkind: tkind,
|
||||
curfile: str,
|
||||
curline: i32,
|
||||
curcol: i32,
|
||||
@@ -53,7 +53,7 @@ export fn parserinit(p: *parser, a: *arena, l: *lex) void = {
|
||||
|
||||
fn advance(p: *parser) void = { refill(p); };
|
||||
|
||||
fn accepttok(p: *parser, k: i32) bool = {
|
||||
fn accepttok(p: *parser, k: tkind) bool = {
|
||||
if (p.curkind == k) { advance(p); return true; };
|
||||
return false;
|
||||
};
|
||||
@@ -66,7 +66,7 @@ fn errmsg(p: *parser, msg: str) void = {
|
||||
p.errs += 1;
|
||||
};
|
||||
|
||||
fn expecttok(p: *parser, k: i32, what: str) bool = {
|
||||
fn expecttok(p: *parser, k: tkind, what: str) bool = {
|
||||
if (p.curkind == k) { advance(p); return true; };
|
||||
errmsg(p, what);
|
||||
return false;
|
||||
@@ -313,7 +313,7 @@ fn parsetype(p: *parser) *node = {
|
||||
// and the ?/! try operators are not yet wired — they'll arrive as the
|
||||
// AST diff fixture grows to need them.
|
||||
|
||||
fn bprec(k: i32) i32 = {
|
||||
fn bprec(k: tkind) i32 = {
|
||||
if (k == tkind.TK_OR) { return 1; };
|
||||
if (k == tkind.TK_AND) { return 2; };
|
||||
if (k == tkind.TK_EQ) { return 3; };
|
||||
@@ -335,7 +335,7 @@ fn bprec(k: i32) i32 = {
|
||||
return 0;
|
||||
};
|
||||
|
||||
fn isassignop(k: i32) bool = {
|
||||
fn isassignop(k: tkind) bool = {
|
||||
if (k == tkind.TK_ASSIGN) { return true; };
|
||||
if (k == tkind.TK_PLUSEQ) { return true; };
|
||||
if (k == tkind.TK_MINUSEQ) { return true; };
|
||||
|
||||
@@ -22,7 +22,7 @@ type skind = enum i32 {
|
||||
|
||||
type sym = struct {
|
||||
name: str,
|
||||
skind: i32,
|
||||
skind: skind,
|
||||
type_: *tinfo,
|
||||
decl: *node,
|
||||
exported: i32,
|
||||
@@ -98,7 +98,7 @@ export fn scopelookup(s: *scope, name: str) *sym = {
|
||||
return nil;
|
||||
};
|
||||
|
||||
export fn scopedefine(s: *scope, name: str, k: i32, t: *tinfo, decl: *node) *sym = {
|
||||
export fn scopedefine(s: *scope, name: str, k: skind, t: *tinfo, decl: *node) *sym = {
|
||||
if (scopelookuplocal(s, name) != nil) { return nil; };
|
||||
let sy: *sym = amalloc(s.a, 80u64): *sym;
|
||||
sy.name = name;
|
||||
|
||||
@@ -74,7 +74,7 @@ type tparam = struct {
|
||||
};
|
||||
|
||||
type tinfo = struct {
|
||||
kind: i32,
|
||||
kind: tykind,
|
||||
size: u64,
|
||||
align: u64,
|
||||
sub: *tinfo, // ptr/slice/array/chan element
|
||||
@@ -120,13 +120,13 @@ type tctx = struct {
|
||||
|
||||
// ---- constructors -----------------------------------------------------
|
||||
|
||||
export fn newtype(a: *arena, k: i32) *tinfo = {
|
||||
export fn newtype(a: *arena, k: tykind) *tinfo = {
|
||||
let t: *tinfo = amalloc(a, 96u64): *tinfo;
|
||||
t.kind = k;
|
||||
return t;
|
||||
};
|
||||
|
||||
fn prim(a: *arena, k: i32, nm: str, sz: u64, al: u64) *tinfo = {
|
||||
fn prim(a: *arena, k: tykind, nm: str, sz: u64, al: u64) *tinfo = {
|
||||
let t: *tinfo = newtype(a, k);
|
||||
t.name = nm;
|
||||
t.size = sz;
|
||||
@@ -216,7 +216,7 @@ export fn typenamed(a: *arena, name: str, under: *tinfo) *tinfo = {
|
||||
|
||||
export fn typeisint(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: i32 = t.kind;
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_I8) { return true; };
|
||||
if (k == tykind.TY_I16) { return true; };
|
||||
if (k == tykind.TY_I32) { return true; };
|
||||
@@ -238,7 +238,7 @@ export fn typeisint(t: *tinfo) bool = {
|
||||
|
||||
export fn typeisfloat(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: i32 = t.kind;
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_F32) { return true; };
|
||||
if (k == tykind.TY_F64) { return true; };
|
||||
if (k == tykind.TY_UNTYPED_FLOAT) { return true; };
|
||||
@@ -253,7 +253,7 @@ export fn typeisnum(t: *tinfo) bool = {
|
||||
|
||||
export fn typeisunsigned(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: i32 = t.kind;
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_U8) { return true; };
|
||||
if (k == tykind.TY_U16) { return true; };
|
||||
if (k == tykind.TY_U32) { return true; };
|
||||
@@ -266,7 +266,7 @@ export fn typeisunsigned(t: *tinfo) bool = {
|
||||
|
||||
export fn typeisuntyped(t: *tinfo) bool = {
|
||||
if (t == nil) { return false; };
|
||||
let k: i32 = t.kind;
|
||||
let k: tykind = t.kind;
|
||||
if (k == tykind.TY_UNTYPED_INT) { return true; };
|
||||
if (k == tykind.TY_UNTYPED_FLOAT) { return true; };
|
||||
if (k == tykind.TY_UNTYPED_STR) { return true; };
|
||||
@@ -282,7 +282,7 @@ export fn typeeq(a: *tinfo, b: *tinfo) bool = {
|
||||
if (a == nil) { return false; };
|
||||
if (b == nil) { return false; };
|
||||
if (a.kind != b.kind) { return false; };
|
||||
let k: i32 = a.kind;
|
||||
let k: tykind = a.kind;
|
||||
if (k == tykind.TY_PTR) { return typeeq(a.sub, b.sub); };
|
||||
if (k == tykind.TY_SLICE) { return typeeq(a.sub, b.sub); };
|
||||
if (k == tykind.TY_CHAN) { return typeeq(a.sub, b.sub); };
|
||||
|
||||
Reference in New Issue
Block a user