Files
ww/lib/ww/sym.ww
Hojun-Cho 922877309b 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.
2026-05-12 05:04:33 +09:00

117 lines
2.6 KiB
Plaintext

// lib/ww/sym.ww — port of cmd/wcc/sym.c.
//
// Per-scope hashtable, chained to the parent. Lookup walks up.
// Plan 9 / Hare flavoured. Duplicate definitions in the same scope
// return nil; the caller flags the error.
use mem;
use typ;
use ast;
// Symbol kinds — must stay numerically aligned with cmd/wcc/ww.h Skind.
type skind = enum i32 {
SK_NONE = 0,
SK_VAR = 1,
SK_PARAM = 2,
SK_DEF = 3,
SK_TYPE = 4,
SK_FN = 5,
SK_USE = 6,
SK_FIELD = 7,
};
type sym = struct {
name: str,
skind: skind,
type_: *tinfo,
decl: *node,
exported: i32,
is_const: i32, // const-bound (assignment rejected)
snext: *sym, // iteration order
hashnext: *sym, // hash bucket chain
scope: *scope,
};
def NBUCKETS: i32 = 16;
type scope = struct {
parent: *scope,
first: *sym,
last: *sym,
buckets: **sym, // length = NBUCKETS
nbuckets: i32,
a: *arena,
};
// FNV-1a 64 — same hash the C side uses, so bucket distribution is
// identical when both walk a scope in declaration order.
fn hashstr(s: str) u64 = {
let h: u64 = 14695981039346656037u64;
let i: i32 = 0;
for (i < s.len) {
let c: u8 = s[i];
h = h ^ (c: u64);
h = h * 1099511628211u64;
i += 1;
};
return h;
};
export fn newscope(a: *arena, parent: *scope) *scope = {
let s: *scope = amalloc(a, 64u64): *scope;
s.parent = parent;
s.a = a;
s.nbuckets = NBUCKETS;
s.buckets = amalloc(a, (NBUCKETS: u64) * 8u64): **sym;
return s;
};
export fn streq(a: str, b: str) bool = {
if (a.len != b.len) { return false; };
let i: i32 = 0;
for (i < a.len) {
if (a[i] != b[i]) { return false; };
i += 1;
};
return true;
};
export fn scopelookuplocal(s: *scope, name: str) *sym = {
if (s == nil) { return nil; };
let h: u64 = hashstr(name);
let bi: i32 = (h % (s.nbuckets: u64)): i32;
let b: *sym = s.buckets[bi];
for (b != nil) {
let bn: str = b.name;
if (streq(bn, name)) { return b; };
b = b.hashnext;
};
return nil;
};
export fn scopelookup(s: *scope, name: str) *sym = {
for (s != nil) {
let r: *sym = scopelookuplocal(s, name);
if (r != nil) { return r; };
s = s.parent;
};
return nil;
};
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;
sy.skind = k;
sy.type_ = t;
sy.decl = decl;
sy.scope = s;
let h: u64 = hashstr(name);
let bi: i32 = (h % (s.nbuckets: u64)): i32;
sy.hashnext = s.buckets[bi];
s.buckets[bi] = sy;
if (s.first == nil) { s.first = sy; } else { s.last.snext = sy; };
s.last = sy;
return sy;
};