wwstage: port struct embedding; graduate #59.13

The last frontend-gap pin: wwstage had no Hare struct embedding
(struct { hash.hash, ... }), rejecting lib/crypto/sha256 at parse.

- parse.ww: the three member forms (named / anonymous struct / bare
  dotted-ident embed), consume-then-branch since this parser has no
  peek; embeds carry f.str == "" and the type in f.lhs.
- check.ww N_TSTRUCT flatten: promote the inner struct's flattened
  fields at base+src.offset (check.c:961-990); the embed is one
  nested-struct unit in the slot ladder; the resolved inner AST is
  planted on the TFIELD rhs for cgen.
- check.ww walkers: astoffset / exprtype N_DOT / #251 struct-lit
  field lookups descend embeds through shared helpers; the collision
  and non-struct-embed rejects live in validatestructfields (the
  once-per-decl diagnostic site).
- cgenutil.ww registerstruct: regfieldrun walks the AST against the
  flattened tfield cursor, descending embeds via the planted inner
  AST so promoted fieldinfo entries keep the inner field's own name
  and type node.
- wwi printers unchanged (both stages already emit nameless fields).

sha256_test compiles byte-identically end to end and its 6 tests
pass; 989_lib_byteid is now 44 id / 0 divergent / 0 wwreject.
Fixtures r5913_* (promoted rw, offset shift, anonymous embed,
two-level embed + promoted fn-ptr callee, three rejects); corpus pin
1485/2970.
This commit is contained in:
2026-08-08 02:37:50 +09:00
parent 6553d60e91
commit 5abb1e6069
13 changed files with 472 additions and 95 deletions

View File

@@ -2990,6 +2990,53 @@ fn fieldsize(c: *cgen, tnode: *syntax.node) i32 = {
// non-TFIELD identically), so they advance in exact step. si.totsize keeps
// the slot-padded total (stack-slot allocator's number) from ti.slotsize,
// already 8-rounded at check.ww:2259-2261.
// regfieldwalk — cursor state threaded through regfieldrun's embed-
// descending AST/tfield lock-step walk.
type regfieldwalk = struct {
tf: *syntax.tfield,
head: *fieldinfo,
tail: *fieldinfo,
};
// regfieldrun — walk an AST TFIELD list against the flattened
// tinfo.fields cursor. A named field consumes one tfield; an embed
// (#59.13, f.str == "") descends into the resolved inner struct AST
// the checker planted on f.rhs (check.ww N_TSTRUCT flatten) — its
// fields were promoted into THIS struct's tinfo run in the same
// order, so the one cursor stays in exact step and each promoted
// fieldinfo carries the inner field's own name and type node.
fn regfieldrun(c: *cgen, flist: *syntax.node, w: *regfieldwalk) void = {
let f: *syntax.node = flist;
for (f != nil) {
if (f.kind == syntax.nkind.N_TFIELD) {
if (f.str.len == 0) {
if (f.rhs == nil) {
let msg: str = "#59.13: registerstruct: embed field has no checker-planted inner struct AST\n";
os.write(2, msg.ptr, msg.len: u64);
os.exit(1);
};
regfieldrun(c, f.rhs.list, w);
} else {
if (w.tf == nil) {
let msg: str = "#44/#55: registerstruct: AST/tfield walk desync (more TFIELDs than tinfo.fields)\n";
os.write(2, msg.ptr, msg.len: u64);
os.exit(1);
};
let fi: *fieldinfo = alloc(fieldinfo{
fname = f.str,
foff = w.tf.offset: i32,
fsz = w.tf.type_.size: i32,
tnode = f.lhs,
})!;
if (w.head == nil) { w.head = fi; w.tail = fi; }
else { w.tail.finext = fi; w.tail = fi; };
w.tf = w.tf.tnext;
};
};
f = f.next;
};
};
fn registerstruct(c: *cgen, name: str, srcmod: str, tstruct: *syntax.node) void = {
let si: *structinfo = alloc(structinfo{
sname = name,
@@ -3006,30 +3053,12 @@ fn registerstruct(c: *cgen, name: str, srcmod: str, tstruct: *syntax.node) void
os.write(2, msg.ptr, msg.len: u64);
os.exit(1);
};
let head: *fieldinfo = nil;
let tail: *fieldinfo = nil;
let f: *syntax.node = tstruct.list;
let tf: *syntax.tfield = ti.fields;
for (f != nil) {
if (f.kind == syntax.nkind.N_TFIELD) {
if (tf == nil) {
let msg: str = "#44/#55: registerstruct: AST/tfield walk desync (more TFIELDs than tinfo.fields)\n";
os.write(2, msg.ptr, msg.len: u64);
os.exit(1);
};
let fi: *fieldinfo = alloc(fieldinfo{
fname = f.str,
foff = tf.offset: i32,
fsz = tf.type_.size: i32,
tnode = f.lhs,
})!;
if (head == nil) { head = fi; tail = fi; }
else { tail.finext = fi; tail = fi; };
tf = tf.tnext;
};
f = f.next;
};
si.fields = head;
let w: regfieldwalk;
w.tf = ti.fields;
w.head = nil;
w.tail = nil;
regfieldrun(c, tstruct.list, &w);
si.fields = w.head;
si.totsize = ti.slotsize: i32;
si.sinext = c.structs;
c.structs = si;