lib/ww + wcc + w6c + wwdump: strip *arena cascade (γ-6)

amalloc has 0 callers post-γ-2; the *arena threaded through
newnode/newscope/newtype/prim/typesinit/type{ptr,slice,array,chan,
named}/lexinit/parserinit/joindotted/checkinit/arenau64tos/cgeninit
and the scope.a / tctx.a / lex.a / parser.a / checker.a / cgen.a
fields are vestigial.

Drop `import mem;` from 15 files, remove six struct fields, strip
*arena from 14 signatures, update ~120 call sites across lib/ww +
wcc + w6c + wwdump. selfhost/test/sym_link.ww fixture drops the
newarena/freearena probe; still exits 42 on scopedefine/scopelookup.
Both main.combined.ww auto-regenerated.

Comments retidied: typ.ww "once per arena" → "once per program";
parse.ww drops "arena-build" qualifier on joindotted; sym.ww drops
mem-sibling-imports rationale.

Verified 132/132 incl. 994_w6c_ww + 995_self_rebuild byte-identity
(the primary symmetric-stages gate).
This commit is contained in:
2026-05-21 13:01:57 +09:00
parent f83e65b82a
commit 353dffb5e8
19 changed files with 543 additions and 824 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,6 @@ package main;
import os;
import rt;
import mem;
import strings;
import tok;
import lex;
@@ -135,7 +134,6 @@ export fn main(argc: i32, argv: **u8) i32 = {
os.close(ofd);
};
let ar: *arena = newarena();
let nlen: u64 = cstrlen(src);
let view: str;
view.ptr = src;
@@ -143,10 +141,10 @@ export fn main(argc: i32, argv: **u8) i32 = {
let fname: str = strings.dup(view);
let l: lex;
lexinit(&l, ar, fname, buf, blen);
lexinit(&l, fname, buf, blen);
let ps: parser;
parserinit(&ps, ar, &l);
parserinit(&ps, &l);
let f: *node = parsefile(&ps);
// Gate cgen on parse-stage errors. Mirrors cmd/w6c/main.c's
// `if (l.errs || p.errs) return 1;` — broken AST otherwise reaches
@@ -160,14 +158,14 @@ export fn main(argc: i32, argv: **u8) i32 = {
// enum↔int reinterpret, #53 N_BLOCK scoping, #55 nominal-first variant
// compare, #56 bare-leaf same-module preference.
let tc: tctx;
typesinit(&tc, ar);
typesinit(&tc);
let ck: checker;
checkinit(&ck, ar, &tc);
checkinit(&ck, &tc);
checkfile(&ck, f);
if (ck.errs > 0) { return 1; };
let cg: cgen;
cgeninit(&cg, ar);
cgeninit(&cg);
cgfile(&cg, f);
return 0;
};

View File

@@ -25,7 +25,6 @@
package wcc;
import os;
import mem;
import ast;
import tok;
import typ;
@@ -68,9 +67,9 @@ fn collectaliases(c: *cgen, file: *node) void = {
// same-module / any-match passes in aliaslookup then let a local
// `type nomem = !void;` shadow this fallback within its module.
let empty: str;
let tnvoid: *node = newnode(c.a, nkind.N_TNAME, empty, 0, 0);
let tnvoid: *node = newnode(nkind.N_TNAME, empty, 0, 0);
tnvoid.str = "void";
let bang: *node = newnode(c.a, nkind.N_TBANG, empty, 0, 0);
let bang: *node = newnode(nkind.N_TBANG, empty, 0, 0);
bang.lhs = tnvoid;
let nomemal: *aliasent = alloc(aliasent{aname="nomem", amod=empty, target=bang, aanext=nil})!;
c.aliases = nomemal;
@@ -435,7 +434,6 @@ def LOOP_MAX: i32 = 16;
def DEFER_MAX: i32 = 16;
type cgen = struct {
a: *arena,
locals: *local,
// atlocals — persistent registry of `@`-prefix scratch slots
// for the current fn. cgblock save/restores c.locals to scope
@@ -522,8 +520,7 @@ type letvar = struct {
lvnext: *letvar,
};
fn cgeninit(c: *cgen, a: *arena) void = {
c.a = a;
fn cgeninit(c: *cgen) void = {
c.locals = nil;
c.atlocals = nil;
c.frame = 0;

View File

@@ -13,7 +13,6 @@
package wcc;
import os;
import mem;
import ast;
import tok;
import typ;
@@ -339,7 +338,7 @@ fn cgfnparams(c: *cgen, params: *node) void = {
};
fn cgfn(c: *cgen, fn_: *node) void = {
cgeninit(c, c.a);
cgeninit(c);
c.fnname = fn_.str;
c.curmod = fn_.nmod;
c.fnret = fn_.lhs;

View File

@@ -15,7 +15,6 @@
package wcc;
import os;
import mem;
import ast;
import tok;
import typ;
@@ -3172,7 +3171,7 @@ fn cgcall(c: *cgen, n: *node) void = {
emitline("\tMOVQ\tAX, ");
emitoff((soff + 16): i64);
emitline("(BP)\n");
let sn: *node = newnode(c.a, nkind.N_IDENT,
let sn: *node = newnode(nkind.N_IDENT,
"", 0, 0);
sn.str = sname;
if (prevarg == nil) { n.list = sn; }

View File

@@ -11,7 +11,6 @@
package wcc;
import os;
import mem;
import ast;
import tok;
import typ;

View File

@@ -15,7 +15,6 @@
package wcc;
import os;
import mem;
import ast;
import tok;
import typ;
@@ -30,7 +29,7 @@ import strconv;
// (caller side) both advertise their effective type as []ELEM —
// every isslicetype / nodeisslice check then succeeds naturally.
fn slicewrap(c: *cgen, elem: *node) *node = {
let s: *node = newnode(c.a, nkind.N_TSLICE, "", 0, 0);
let s: *node = newnode(nkind.N_TSLICE, "", 0, 0);
s.lhs = elem;
return s;
};

View File

@@ -20,11 +20,9 @@
package wcc;
import os;
import mem;
import tok;
type checker = struct {
a: *arena,
tc: *tctx,
top: *scope,
cur: *scope,
@@ -72,11 +70,11 @@ fn seedprimitives(c: *checker) void = {
// separate alias chain — see collectaliases in cgen.ww for the
// companion seed.
let empty: str;
let tnvoid: *node = newnode(c.a, nkind.N_TNAME, empty, 0, 0);
let tnvoid: *node = newnode(nkind.N_TNAME, empty, 0, 0);
tnvoid.str = "void";
let bang: *node = newnode(c.a, nkind.N_TBANG, empty, 0, 0);
let bang: *node = newnode(nkind.N_TBANG, empty, 0, 0);
bang.lhs = tnvoid;
let nomemdecl: *node = newnode(c.a, nkind.N_TYPEDECL, empty, 0, 0);
let nomemdecl: *node = newnode(nkind.N_TYPEDECL, empty, 0, 0);
nomemdecl.str = "nomem";
nomemdecl.lhs = bang;
scopedefine(c.top, "nomem", skind.SK_TYPE, nil, nomemdecl);
@@ -350,7 +348,7 @@ fn resolvewalk(c: *checker, n: *node) void = {
if (k == nkind.N_MCASE) {
if (n.lhs != nil) { resolvewalk(c, n.lhs); };
let outer: *scope = c.cur;
c.cur = newscope(c.a, outer);
c.cur = newscope(outer);
let nm: str = n.str;
if (nm.len > 0) {
checkmoduleshadow(c, nm, "binding");
@@ -372,7 +370,7 @@ fn resolvewalk(c: *checker, n: *node) void = {
// Mirrors cstage cstmt N_BLOCK at cmd/wcc/check.c:1559-1566.
if (k == nkind.N_BLOCK) {
let outer: *scope = c.cur;
c.cur = newscope(c.a, outer);
c.cur = newscope(outer);
let m: *node = n.list;
for (m != nil) {
resolvewalk(c, m);
@@ -651,7 +649,7 @@ fn scruttype(c: *checker, e: *node) *node = {
// exprtype to return primitive type nodes for literal
// expressions. The arena keeps them around as long as the checker.
fn mktname(c: *checker, nm: str) *node = {
let n: *node = newnode(c.a, nkind.N_TNAME, "", 0, 0);
let n: *node = newnode(nkind.N_TNAME, "", 0, 0);
n.str = nm;
return n;
};
@@ -838,7 +836,7 @@ fn astoffset(c: *checker, dot: *node) i64 = {
// wwstage cgen only reads `uval` for N_INTLIT codegen so `str` is
// just for the AST printer, but set it for parity with the parser's
// own literal-emit shape.
fn arenau64tos(a: *arena, v: u64) str = {
fn arenau64tos(v: u64) str = {
let buf: []u8 = alloc([], 24u64)!;
let i: i32 = 23;
buf[i] = 0u8;
@@ -862,7 +860,7 @@ fn arenau64tos(a: *arena, v: u64) str = {
fn foldtointlit(c: *checker, n: *node, v: i64) void = {
n.kind = nkind.N_INTLIT;
n.uval = v: u64;
n.str = arenau64tos(c.a, v: u64);
n.str = arenau64tos(v: u64);
n.lhs = nil;
n.list = nil;
let empty: str;
@@ -1014,11 +1012,11 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
// site that needs iserror discrimination.
r = tinfofornode(c, n.lhs);
} else { if (k == nkind.N_TPTR) {
r = typeptr(c.a, tinfofornode(c, n.lhs));
r = typeptr(tinfofornode(c, n.lhs));
} else { if (k == nkind.N_TSLICE) {
r = typeslice(c.a, tinfofornode(c, n.lhs));
r = typeslice(tinfofornode(c, n.lhs));
} else { if (k == nkind.N_TCHAN) {
r = typechan(c.a, tinfofornode(c, n.lhs));
r = typechan(tinfofornode(c, n.lhs));
} else { if (k == nkind.N_TARRAY) {
// Cstage cmd/wcc/check.c:314-326: length must be an integer
// literal (`[_]T` keeps alen=0 as the inferred-length sentinel
@@ -1034,14 +1032,14 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
if (n.rhs.kind == nkind.N_INTLIT) { elen = n.rhs.uval; };
};
let sub: *tinfo = tinfofornode(c, n.lhs);
r = typearray(c.a, sub, elen);
r = typearray(sub, elen);
} else { if (k == nkind.N_TFN) {
// Cstage cmd/wcc/check.c:437-466: function types are 8B / 8B
// (call-target pointer shape). Pre-bind before recursing into
// the return type so a recursive `type F = fn() F` self-ref
// doesn't spin (cycle-break mirror of the TSTRUCT/TTAGGED
// pattern below).
r = newtype(c.a, tykind.TY_FN);
r = newtype(tykind.TY_FN);
r.size = 8u64;
r.align = 8u64;
r.slotsize = 8u64;
@@ -1052,7 +1050,7 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
// (default i32 = 4B/4B). Cgen's slotsize-TENUM fallback pads
// to 8B per its stack-slot contract; tinfo.size carries the
// raw storage width so size(EnumT) folds to the correct value.
r = newtype(c.a, tykind.TY_ENUM);
r = newtype(tykind.TY_ENUM);
let storage: *tinfo = nil;
if (n.lhs != nil) { storage = tinfofornode(c, n.lhs); };
if (storage == nil) { storage = c.tc.tyi32; };
@@ -1071,7 +1069,7 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
// slotsize TTUPLE — narrow scalars pad to 8 (cgen spills each
// tuple element into its own register / stack-slot eightbyte),
// composites contribute their own ti.slotsize.
r = newtype(c.a, tykind.TY_TUPLE);
r = newtype(tykind.TY_TUPLE);
tinfocachebind(c.tc, n, r);
let total: u64 = 0u64;
let slottotal: u64 = 0u64;
@@ -1109,7 +1107,7 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
// struct; size-derived alignment; final round to 8). That
// slot total lands in ti.slotsize so the cgen fast-path can
// graduate TY_STRUCT off the AST walker.
r = newtype(c.a, tykind.TY_STRUCT);
r = newtype(tykind.TY_STRUCT);
tinfocachebind(c.tc, n, r);
let off: u64 = 0u64;
let maxalign: u64 = 1u64;
@@ -1151,7 +1149,7 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
// Cstage cmd/wcc/check.c:347-435: 8B tag + max(variant)
// rounded up to 8. Pre-bind for cycle protection (recursive
// sum-type shapes through NAMED variants).
r = newtype(c.a, tykind.TY_TAGGED);
r = newtype(tykind.TY_TAGGED);
tinfocachebind(c.tc, n, r);
// #61 A.3 nullable fold: `(*T | void)` collapses to a single
// 8B pointer slot, null is the void variant. Mirrors
@@ -1310,11 +1308,11 @@ fn exprtype(c: *checker, e: *node) *node = {
if (e.list.list == nil) {
if (e.list.next != nil) {
if (e.list.next.next == nil) {
let sl: *node = newnode(c.a, nkind.N_TSLICE, "", 0, 0);
let sl: *node = newnode(nkind.N_TSLICE, "", 0, 0);
sl.lhs = mktname(c, "u8");
let nome: *node = mktname(c, "nomem");
sl.next = nome;
let tt: *node = newnode(c.a, nkind.N_TTAGGED, "", 0, 0);
let tt: *node = newnode(nkind.N_TTAGGED, "", 0, 0);
tt.list = sl;
return tt;
};
@@ -1324,11 +1322,11 @@ fn exprtype(c: *checker, e: *node) *node = {
// Value form: `alloc(value)`.
if (e.list.next == nil) {
let argt: *node = exprtype(c, e.list);
let ptr: *node = newnode(c.a, nkind.N_TPTR, "", 0, 0);
let ptr: *node = newnode(nkind.N_TPTR, "", 0, 0);
ptr.lhs = argt;
let nome: *node = mktname(c, "nomem");
ptr.next = nome;
let tt: *node = newnode(c.a, nkind.N_TTAGGED, "", 0, 0);
let tt: *node = newnode(nkind.N_TTAGGED, "", 0, 0);
tt.list = ptr;
return tt;
};
@@ -1845,14 +1843,14 @@ fn checkletassign(c: *checker, n: *node) void = {
};
};
if (!shadowed) {
let sl: *node = newnode(c.a, nkind.N_TSLICE, "", 0, 0);
let sl: *node = newnode(nkind.N_TSLICE, "", 0, 0);
sl.lhs = n.lhs.lhs;
if (wrapped) {
src = sl;
} else {
let nome: *node = mktname(c, "nomem");
sl.next = nome;
let tt: *node = newnode(c.a, nkind.N_TTAGGED, "", 0, 0);
let tt: *node = newnode(nkind.N_TTAGGED, "", 0, 0);
tt.list = sl;
src = tt;
};
@@ -2043,7 +2041,7 @@ fn installparams(c: *checker, params: *node) void = {
// per-statement scopes.
fn resolvefnbody(c: *checker, fnnode: *node) void = {
let outer: *scope = c.cur;
c.cur = newscope(c.a, c.cur);
c.cur = newscope(c.cur);
installparams(c, fnnode.list);
// #61 audit §1.8 — A.2: walk each param's declared type-expr so
// tinfofornode stamps n.type_ on it. installparams binds the name
@@ -2065,10 +2063,9 @@ fn resolvefnbody(c: *checker, fnnode: *node) void = {
c.cur = outer;
};
export fn checkinit(c: *checker, a: *arena, tc: *tctx) void = {
c.a = a;
export fn checkinit(c: *checker, tc: *tctx) void = {
c.tc = tc;
c.top = newscope(a, nil);
c.top = newscope(nil);
c.cur = c.top;
c.nresolved = 0;
c.nunresolved = 0;

File diff suppressed because it is too large Load Diff

View File

@@ -12,7 +12,6 @@
package main;
import os;
import mem;
import tok;
import lex;
import ast;
@@ -99,7 +98,6 @@ export fn main(argc: i32, argv: **u8) i32 = {
};
};
let a: *arena = newarena();
let buf: []u8 = alloc([], sz: u64)!;
buf.len = sz: i32;
let rr: (i64 | os.oserror) = os.readall(fd, buf.ptr, sz: u64);
@@ -118,7 +116,7 @@ export fn main(argc: i32, argv: **u8) i32 = {
};
let l: lex;
lexinit(&l, a, argstr(path), buf.ptr, sz: u64);
lexinit(&l, argstr(path), buf.ptr, sz: u64);
if (mode == 116) { // '-t'
for (true) {
@@ -130,17 +128,17 @@ export fn main(argc: i32, argv: **u8) i32 = {
};
} else { if (mode == 97) { // '-a'
let ps: parser;
parserinit(&ps, a, &l);
parserinit(&ps, &l);
let f: *node = parsefile(&ps);
astprint(1i32, f);
} else { if (mode == 114) { // '-r' — name resolve report
let ps: parser;
parserinit(&ps, a, &l);
parserinit(&ps, &l);
let f: *node = parsefile(&ps);
let tc: tctx;
typesinit(&tc, a);
typesinit(&tc);
let ck: checker;
checkinit(&ck, a, &tc);
checkinit(&ck, &tc);
// Quiet by default; flip to 1 when debugging missing names.
ck.verbose = 0;
checkfile(&ck, f);
@@ -158,7 +156,7 @@ export fn main(argc: i32, argv: **u8) i32 = {
if (ck.nunresolved > 0) { return 1; };
} else { if (mode == 99) { // '-c' — codegen / emit asm
let ps: parser;
parserinit(&ps, a, &l);
parserinit(&ps, &l);
let f: *node = parsefile(&ps);
// #50: mirror w6c — run check before cgen so AST mutations
// from #42 (size/align/offset fold) and audit §1.8 (node.type_
@@ -166,13 +164,13 @@ export fn main(argc: i32, argv: **u8) i32 = {
// (the byte-identity probe for 994) would diverge from w6c_ww
// on any program that uses the size/align/offset typed builtins.
let tc: tctx;
typesinit(&tc, a);
typesinit(&tc);
let ck: checker;
checkinit(&ck, a, &tc);
checkinit(&ck, &tc);
checkfile(&ck, f);
if (ck.errs > 0) { return 1; };
let cg: cgen;
cgeninit(&cg, a);
cgeninit(&cg);
cgfile(&cg, f);
};};};};

View File

@@ -1,20 +1,16 @@
// selfhost/test/sym_link.ww — link-and-run probe for the ww-cgen
// against the sym/typ/ast/mem dep stack. Exercises arena (mem),
// hashtable scope (sym), and pulls in typ/ast as type carriers.
// against the sym/typ/ast dep stack. Exercises hashtable scope
// (sym), and pulls in typ/ast as type carriers.
// Returns 42 on success; smaller values name the probe that broke.
package test;
import mem;
import typ;
import ast;
import sym;
export fn main() i32 = {
let a: *arena = newarena();
if (a == nil) { return 1; };
let s: *scope = newscope(a, nil);
let s: *scope = newscope(nil);
if (s == nil) { return 2; };
let n1: str = "foo";
@@ -42,6 +38,5 @@ export fn main() i32 = {
let l3: *sym = scopelookup(s, n3);
if (l3 != nil) { return 10; };
freearena(a);
return 42;
};