selfhost/cmd/wcc/check: populate tinfo.fields + .tupleelems (#57, A.6.3i-phase-1)

Phase 1 of A.6.3i: populate the field chain in tinfofornode's TSTRUCT
and TTUPLE arms so Phase 2/J/K (#58/#59/#60) can retire dotfieldtnode,
dotinnerstructptr, dotchainresolve, and indexbaseesz off their AST-keyed
structinfo walk and onto a tinfo read. Direct analog 26724fe (#50 phase
1, A.6.3f-a) for the head/tail append-list pattern.

TSTRUCT walks n.list's N_TFIELD chain in lockstep with the existing
natural-layout offset accumulator: alloc tfield {name, type_, offset,
tnext}, link head/tail, set r.fields after the loop. Mirrors cstage
cmd/wcc/check.c:468-527. Harec cite: ref/harec/include/types.h:109-115
struct_field and ref/harec/src/type_store.c:314-347 struct_init_from_atype.
Anonymous-embed promotion not populated here (#13 per the cstage cite
at check.ww:1263).

TTUPLE adds a new ttupleelem struct {type_, offset, tnext} on a new
tinfo.tupleelems slot, distinct from .fields per Rob's call: harec
splits struct_field vs type_tuple at types.h:109-115 vs :122-126
because tuples are positional/anonymous and struct members are named,
and the name="" idiom #50 reused for tagged-variants-on-tparam would
conflate two semantic axes. Diverges from cstage cmd/wcc/check.c:329-345
which stores tuple positionals on t->params (Tparam, no offset, consumer
recomputes by walking at cgen.c:5723-5750); storing the offset matches
the A.6 stamp-once-read-many arc Phase 2/J/K consume. Offset is raw-sum
(no per-element padding) matching cstage cgen.c:5723-5750, distinct
from harec's add_padding at type_store.c:561.

Purely additive: r.fields and r.tupleelems have zero readers today.
Phase 2/J/K consume. make test 133/133 (worker port); test-unit 124/124
post comment-only review trim.
This commit is contained in:
2026-05-23 15:13:07 +09:00
parent 8d4a9700a0
commit 6c9a3b369e
4 changed files with 174 additions and 3 deletions

View File

@@ -74,6 +74,19 @@ type tparam = struct {
tnext: *tparam,
};
// #57 A.6.3i-phase-1: tuple positional element. Distinct from tfield
// (named, struct member) per harec's split at ref/harec/include/types.h
// :109-115 (struct_field) vs :122-126 (type_tuple) — tuple positionals
// carry no name (positional only) and a separate next-link. Rule-12
// sea-of-stars mirrors Hare's structural choice; the empty-name idiom
// from #50's TTAGGED-on-tparam would conflate two semantic axes
// (variants can be named; positionals never can).
type ttupleelem = struct {
type_: *tinfo,
offset: u64,
tnext: *ttupleelem,
};
type tinfo = struct {
kind: tykind,
size: u64,
@@ -82,6 +95,11 @@ type tinfo = struct {
alen: u64,
fields: *tfield,
params: *tparam,
tupleelems: *ttupleelem, // #57 A.6.3i-phase-1: TY_TUPLE
// positional chain (harec types.h:122-126
// `struct type_tuple`). Distinct slot from
// .fields so struct-member vs tuple-
// positional stay axis-separated.
ret: *tinfo,
variadic: i32,
nullable: i32, // #61 A.3: TY_TAGGED `(*T | void)` fold collapses to
@@ -148,7 +166,7 @@ type tctx = struct {
// ---- constructors -----------------------------------------------------
export fn newtype(k: tykind) *tinfo = {
let t: *tinfo = alloc(tinfo{kind=k, size=0u64, align=0u64, sub=nil, alen=0u64, fields=nil, params=nil, ret=nil, variadic=0, nullable=0, name="", under=nil, slotsize=0u64})!;
let t: *tinfo = alloc(tinfo{kind=k, size=0u64, align=0u64, sub=nil, alen=0u64, fields=nil, params=nil, tupleelems=nil, ret=nil, variadic=0, nullable=0, name="", under=nil, slotsize=0u64})!;
return t;
};

View File

@@ -6404,6 +6404,19 @@ type tparam = struct {
tnext: *tparam,
};
// #57 A.6.3i-phase-1: tuple positional element. Distinct from tfield
// (named, struct member) per harec's split at ref/harec/include/types.h
// :109-115 (struct_field) vs :122-126 (type_tuple) — tuple positionals
// carry no name (positional only) and a separate next-link. Rule-12
// sea-of-stars mirrors Hare's structural choice; the empty-name idiom
// from #50's TTAGGED-on-tparam would conflate two semantic axes
// (variants can be named; positionals never can).
type ttupleelem = struct {
type_: *tinfo,
offset: u64,
tnext: *ttupleelem,
};
type tinfo = struct {
kind: tykind,
size: u64,
@@ -6412,6 +6425,11 @@ type tinfo = struct {
alen: u64,
fields: *tfield,
params: *tparam,
tupleelems: *ttupleelem, // #57 A.6.3i-phase-1: TY_TUPLE
// positional chain (harec types.h:122-126
// `struct type_tuple`). Distinct slot from
// .fields so struct-member vs tuple-
// positional stay axis-separated.
ret: *tinfo,
variadic: i32,
nullable: i32, // #61 A.3: TY_TAGGED `(*T | void)` fold collapses to
@@ -6478,7 +6496,7 @@ type tctx = struct {
// ---- constructors -----------------------------------------------------
export fn newtype(k: tykind) *tinfo = {
let t: *tinfo = alloc(tinfo{kind=k, size=0u64, align=0u64, sub=nil, alen=0u64, fields=nil, params=nil, ret=nil, variadic=0, nullable=0, name="", under=nil, slotsize=0u64})!;
let t: *tinfo = alloc(tinfo{kind=k, size=0u64, align=0u64, sub=nil, alen=0u64, fields=nil, params=nil, tupleelems=nil, ret=nil, variadic=0, nullable=0, name="", under=nil, slotsize=0u64})!;
return t;
};
@@ -8289,12 +8307,33 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
// composites contribute their own ti.slotsize.
r = newtype(tykind.TY_TUPLE);
tinfocachebind(c.tc, n, r);
// #57 A.6.3i-phase-1: populate r.tupleelems as a ttupleelem
// linked list (head=positional 0) in lock-step with the
// size/align accumulator. Harec analog ref/harec/src/type_
// store.c:532-589 tuple_init_from_atype — {type, offset, next}
// per member onto type->tuple.next chain. Diverges from cstage
// cmd/wcc/check.c:329-345 which stores tuple positionals on
// t->params (Tparam, no offset, consumer recomputes by walking
// at cgen.c:5723-5750); the offset-stored shape lets Phase
// 2/J/K consumers (dotchainresolve, indexbaseesz) read offsets
// directly per the A.6 stamp-once-read-many arc. Direct analog
// 26724fe (#50 phase 1, A.6.3f-a) for the head/tail append
// pattern. Offset matches cstage's raw-sum layout (no per-
// element padding) — rule 10 aligns wwstage tuple layout down
// to cstage, distinct from harec's add_padding(&offset,
// memb.align) at type_store.c:561.
let teh: *ttupleelem = nil;
let tet: *ttupleelem = nil;
let total: u64 = 0u64;
let slottotal: u64 = 0u64;
let maxal: u64 = 1u64;
let p: *node = n.list;
for (p != nil) {
let pt: *tinfo = tinfofornode(c, p.lhs);
let elemoff: u64 = total;
let te: *ttupleelem = alloc(ttupleelem{type_=pt, offset=elemoff, tnext=nil})!;
if (teh == nil) { teh = te; } else { tet.tnext = te; };
tet = te;
if (pt != nil) {
if (pt.align > maxal) { maxal = pt.align; };
total += pt.size;
@@ -8302,6 +8341,7 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
};
p = p.next;
};
r.tupleelems = teh;
r.size = total;
r.align = maxal;
r.slotsize = slottotal;
@@ -8327,6 +8367,18 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
// graduate TY_STRUCT off the AST walker.
r = newtype(tykind.TY_STRUCT);
tinfocachebind(c.tc, n, r);
// #57 A.6.3i-phase-1: populate r.fields as a tfield linked
// list (head=first declared field) in lock-step with the
// natural-layout offset accumulator. Mirrors cstage cmd/wcc/
// check.c:468-527 (Tfield {name, type, offset, next} per
// member onto t->fields). Direct analog 26724fe (#50 phase 1,
// A.6.3f-a) for the head/tail append pattern. Harec cite:
// ref/harec/include/types.h:109-115 struct_field and
// ref/harec/src/type_store.c:314-347 struct_init_from_atype.
// Anonymous-embed promotion not populated here (#13 per
// the cstage cite at check.ww:1263).
let fh: *tfield = nil;
let ft_: *tfield = nil;
let off: u64 = 0u64;
let maxalign: u64 = 1u64;
let soff: u64 = 0u64;
@@ -8339,6 +8391,10 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
if (ft.align > 0u64) {
off = (off + ft.align - 1u64) & ~(ft.align - 1u64);
};
let fldoff: u64 = off;
let tf: *tfield = alloc(tfield{name=f.str, type_=ft, offset=fldoff, tnext=nil})!;
if (fh == nil) { fh = tf; } else { ft_.tnext = tf; };
ft_ = tf;
off += ft.size;
// Slot-padded layout (mirror of cgenutil
// fieldsize + registerstruct align rules).
@@ -8355,6 +8411,7 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
};
f = f.next;
};
r.fields = fh;
if (maxalign > 0u64) {
r.size = (off + maxalign - 1u64) & ~(maxalign - 1u64);
};

View File

@@ -1219,12 +1219,33 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
// composites contribute their own ti.slotsize.
r = newtype(tykind.TY_TUPLE);
tinfocachebind(c.tc, n, r);
// #57 A.6.3i-phase-1: populate r.tupleelems as a ttupleelem
// linked list (head=positional 0) in lock-step with the
// size/align accumulator. Harec analog ref/harec/src/type_
// store.c:532-589 tuple_init_from_atype — {type, offset, next}
// per member onto type->tuple.next chain. Diverges from cstage
// cmd/wcc/check.c:329-345 which stores tuple positionals on
// t->params (Tparam, no offset, consumer recomputes by walking
// at cgen.c:5723-5750); the offset-stored shape lets Phase
// 2/J/K consumers (dotchainresolve, indexbaseesz) read offsets
// directly per the A.6 stamp-once-read-many arc. Direct analog
// 26724fe (#50 phase 1, A.6.3f-a) for the head/tail append
// pattern. Offset matches cstage's raw-sum layout (no per-
// element padding) — rule 10 aligns wwstage tuple layout down
// to cstage, distinct from harec's add_padding(&offset,
// memb.align) at type_store.c:561.
let teh: *ttupleelem = nil;
let tet: *ttupleelem = nil;
let total: u64 = 0u64;
let slottotal: u64 = 0u64;
let maxal: u64 = 1u64;
let p: *node = n.list;
for (p != nil) {
let pt: *tinfo = tinfofornode(c, p.lhs);
let elemoff: u64 = total;
let te: *ttupleelem = alloc(ttupleelem{type_=pt, offset=elemoff, tnext=nil})!;
if (teh == nil) { teh = te; } else { tet.tnext = te; };
tet = te;
if (pt != nil) {
if (pt.align > maxal) { maxal = pt.align; };
total += pt.size;
@@ -1232,6 +1253,7 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
};
p = p.next;
};
r.tupleelems = teh;
r.size = total;
r.align = maxal;
r.slotsize = slottotal;
@@ -1257,6 +1279,18 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
// graduate TY_STRUCT off the AST walker.
r = newtype(tykind.TY_STRUCT);
tinfocachebind(c.tc, n, r);
// #57 A.6.3i-phase-1: populate r.fields as a tfield linked
// list (head=first declared field) in lock-step with the
// natural-layout offset accumulator. Mirrors cstage cmd/wcc/
// check.c:468-527 (Tfield {name, type, offset, next} per
// member onto t->fields). Direct analog 26724fe (#50 phase 1,
// A.6.3f-a) for the head/tail append pattern. Harec cite:
// ref/harec/include/types.h:109-115 struct_field and
// ref/harec/src/type_store.c:314-347 struct_init_from_atype.
// Anonymous-embed promotion not populated here (#13 per
// the cstage cite at check.ww:1263).
let fh: *tfield = nil;
let ft_: *tfield = nil;
let off: u64 = 0u64;
let maxalign: u64 = 1u64;
let soff: u64 = 0u64;
@@ -1269,6 +1303,10 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
if (ft.align > 0u64) {
off = (off + ft.align - 1u64) & ~(ft.align - 1u64);
};
let fldoff: u64 = off;
let tf: *tfield = alloc(tfield{name=f.str, type_=ft, offset=fldoff, tnext=nil})!;
if (fh == nil) { fh = tf; } else { ft_.tnext = tf; };
ft_ = tf;
off += ft.size;
// Slot-padded layout (mirror of cgenutil
// fieldsize + registerstruct align rules).
@@ -1285,6 +1323,7 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
};
f = f.next;
};
r.fields = fh;
if (maxalign > 0u64) {
r.size = (off + maxalign - 1u64) & ~(maxalign - 1u64);
};

View File

@@ -6404,6 +6404,19 @@ type tparam = struct {
tnext: *tparam,
};
// #57 A.6.3i-phase-1: tuple positional element. Distinct from tfield
// (named, struct member) per harec's split at ref/harec/include/types.h
// :109-115 (struct_field) vs :122-126 (type_tuple) — tuple positionals
// carry no name (positional only) and a separate next-link. Rule-12
// sea-of-stars mirrors Hare's structural choice; the empty-name idiom
// from #50's TTAGGED-on-tparam would conflate two semantic axes
// (variants can be named; positionals never can).
type ttupleelem = struct {
type_: *tinfo,
offset: u64,
tnext: *ttupleelem,
};
type tinfo = struct {
kind: tykind,
size: u64,
@@ -6412,6 +6425,11 @@ type tinfo = struct {
alen: u64,
fields: *tfield,
params: *tparam,
tupleelems: *ttupleelem, // #57 A.6.3i-phase-1: TY_TUPLE
// positional chain (harec types.h:122-126
// `struct type_tuple`). Distinct slot from
// .fields so struct-member vs tuple-
// positional stay axis-separated.
ret: *tinfo,
variadic: i32,
nullable: i32, // #61 A.3: TY_TAGGED `(*T | void)` fold collapses to
@@ -6478,7 +6496,7 @@ type tctx = struct {
// ---- constructors -----------------------------------------------------
export fn newtype(k: tykind) *tinfo = {
let t: *tinfo = alloc(tinfo{kind=k, size=0u64, align=0u64, sub=nil, alen=0u64, fields=nil, params=nil, ret=nil, variadic=0, nullable=0, name="", under=nil, slotsize=0u64})!;
let t: *tinfo = alloc(tinfo{kind=k, size=0u64, align=0u64, sub=nil, alen=0u64, fields=nil, params=nil, tupleelems=nil, ret=nil, variadic=0, nullable=0, name="", under=nil, slotsize=0u64})!;
return t;
};
@@ -8289,12 +8307,33 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
// composites contribute their own ti.slotsize.
r = newtype(tykind.TY_TUPLE);
tinfocachebind(c.tc, n, r);
// #57 A.6.3i-phase-1: populate r.tupleelems as a ttupleelem
// linked list (head=positional 0) in lock-step with the
// size/align accumulator. Harec analog ref/harec/src/type_
// store.c:532-589 tuple_init_from_atype — {type, offset, next}
// per member onto type->tuple.next chain. Diverges from cstage
// cmd/wcc/check.c:329-345 which stores tuple positionals on
// t->params (Tparam, no offset, consumer recomputes by walking
// at cgen.c:5723-5750); the offset-stored shape lets Phase
// 2/J/K consumers (dotchainresolve, indexbaseesz) read offsets
// directly per the A.6 stamp-once-read-many arc. Direct analog
// 26724fe (#50 phase 1, A.6.3f-a) for the head/tail append
// pattern. Offset matches cstage's raw-sum layout (no per-
// element padding) — rule 10 aligns wwstage tuple layout down
// to cstage, distinct from harec's add_padding(&offset,
// memb.align) at type_store.c:561.
let teh: *ttupleelem = nil;
let tet: *ttupleelem = nil;
let total: u64 = 0u64;
let slottotal: u64 = 0u64;
let maxal: u64 = 1u64;
let p: *node = n.list;
for (p != nil) {
let pt: *tinfo = tinfofornode(c, p.lhs);
let elemoff: u64 = total;
let te: *ttupleelem = alloc(ttupleelem{type_=pt, offset=elemoff, tnext=nil})!;
if (teh == nil) { teh = te; } else { tet.tnext = te; };
tet = te;
if (pt != nil) {
if (pt.align > maxal) { maxal = pt.align; };
total += pt.size;
@@ -8302,6 +8341,7 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
};
p = p.next;
};
r.tupleelems = teh;
r.size = total;
r.align = maxal;
r.slotsize = slottotal;
@@ -8327,6 +8367,18 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
// graduate TY_STRUCT off the AST walker.
r = newtype(tykind.TY_STRUCT);
tinfocachebind(c.tc, n, r);
// #57 A.6.3i-phase-1: populate r.fields as a tfield linked
// list (head=first declared field) in lock-step with the
// natural-layout offset accumulator. Mirrors cstage cmd/wcc/
// check.c:468-527 (Tfield {name, type, offset, next} per
// member onto t->fields). Direct analog 26724fe (#50 phase 1,
// A.6.3f-a) for the head/tail append pattern. Harec cite:
// ref/harec/include/types.h:109-115 struct_field and
// ref/harec/src/type_store.c:314-347 struct_init_from_atype.
// Anonymous-embed promotion not populated here (#13 per
// the cstage cite at check.ww:1263).
let fh: *tfield = nil;
let ft_: *tfield = nil;
let off: u64 = 0u64;
let maxalign: u64 = 1u64;
let soff: u64 = 0u64;
@@ -8339,6 +8391,10 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
if (ft.align > 0u64) {
off = (off + ft.align - 1u64) & ~(ft.align - 1u64);
};
let fldoff: u64 = off;
let tf: *tfield = alloc(tfield{name=f.str, type_=ft, offset=fldoff, tnext=nil})!;
if (fh == nil) { fh = tf; } else { ft_.tnext = tf; };
ft_ = tf;
off += ft.size;
// Slot-padded layout (mirror of cgenutil
// fieldsize + registerstruct align rules).
@@ -8355,6 +8411,7 @@ fn tinfofornode(c: *checker, n: *node) *tinfo = {
};
f = f.next;
};
r.fields = fh;
if (maxalign > 0u64) {
r.size = (off + maxalign - 1u64) & ~(maxalign - 1u64);
};