w6c+w6c_ww: tuple by-value ARG send — every cursor-filling producer rides #163 (C-t2, #32)

node_tuplearg was N_CALL-scoped and its comment claimed non-call forms
"loud-stop" — they did NOT: a tuple ident/literal/unwrap arg fell to
the scalar single-PUSHQ default, skewing every later arg register so
the callee read garbage word 2 (byte-id both stages, the gate-blind
both-wrong class; packed shapes SIGSEGV'd pre-C-t0). The receive side
(cgfn #163 walk) was already correct.

cgexpr already fills the return-ABI cursor for every supported
producer (#241: ident via slot-to-cursor, literal via lit-to-cursor,
unwrap via payload shift; call via the return ABI) — the send now
admits exactly those into the existing @tupargscr restage + per-class
drain (node_tuplearg widened; wwstage gains nodetuplearg, mirroring it
over the local tnode / inferletcalltype; rettupleof stays N_CALL-scoped
for the destructure receives). Any OTHER tuple-typed source shape
loud-stops at the push site — the false comment's claim, now true
(rule 7). Literal tuple elements are stamped expr types, so the
restage/drain wide test goes type_isstr/type_isslice (TY_UNTYPED_STR-
aware) with the ty_str->size header stride; the wwstage twin walks a
literal's VALUE exprs the way cgtuplelittocursor classifies them.

Ken review demands folded in: (1) a NESTED composite element
(tuple/struct/array/tagged inside the tuple) occupies more than the
one GP word the restage walk counts — the checker accepted it and it
ran WRONG (inner words skewed, wwstage SIGSEGV); both stages' restage
walks now loud-stop the element kind (wiring is the filed follow-up,
task #65). (2) the variadic interaction probed: a tuple arg ahead of
a variadic tail rides the restage correctly (positive row);
variadic-of-tuples stays bounded-loud via the tuple-in-slice read
surface.

941 grows the t2 matrix: packed/16B params with branched callees,
mixed arg orders both ways, literal arg, (f64,i64) param, unwrap arg,
ken's >6-GP-pressure stress (4 leading scalars + tuple + a 7th
stack-class word), variadic-after-tuple, plus rule-7 reject rows
(chain-source arg, nested-element arg, variadic-of-tuples, over-cap
ident arg) and the fold-4 charset substrate pin ([](u32,u32) append
stays LOUD). At the C-t1 parent 18/73 checks fail: every runtime arg
row except the (f64,i64) anchor on BOTH stages (byte-identically — the
gate-blind both-wrong class) and the chain/nested args silently
accepted.
This commit is contained in:
2026-06-04 18:43:32 +09:00
parent 12af54f9f8
commit 6426fac6f2
7 changed files with 635 additions and 101 deletions

View File

@@ -169,6 +169,37 @@ fn rettupleof(c: *cgen, rhs: *node) *node = {
return rtyp;
};
// nodetuplearg — the tuple node of a call ARG whose cgexpr fills the
// return-ABI cursor (#163/#32, C-t2): an N_CALL or `?`/`!` unwrap (the
// declared return / success variant via inferletcalltype), an N_IDENT
// local (declared tnode, alias-peeled), or an N_TUPLE literal — returned
// AS-IS, kind-discriminated at the walks (its elements are VALUE exprs,
// classified the way cgtuplelittocursor classifies them, not type
// nodes). Mirror of cstage node_tuplearg; the cgcall push site
// loud-stops any other tuple-typed source shape (rule 7). rettupleof
// stays N_CALL-scoped for the destructure/reassign receive sites.
fn nodetuplearg(c: *cgen, a: *node) *node = {
if (a == nil) { return nil; };
if (a.kind == nkind.N_TUPLE) { return a; };
if (a.kind == nkind.N_IDENT) {
let lc: *local = localfindnode(c, a.str);
if (lc == nil) { return nil; };
let tn: *node = lc.tnode;
for (tn != nil && tn.kind == nkind.N_TNAME) {
tn = aliaslookup(c, tn.str);
};
if (tn != nil) {
if (tn.kind == nkind.N_TTUPLE) { return tn; };
};
return nil;
};
let t: *node = inferletcalltype(c, a);
if (t != nil) {
if (t.kind == nkind.N_TTUPLE) { return t; };
};
return nil;
};
// tupstore — store the tuple element at register-cursor `cur` into the
// BP-relative slot at `off`. A slice/str stores its 3-word {ptr,len,cap}
// header (ref/hare/rt/ensure.ha:4-8) at off/+8/+16 from consecutive