wcc/check: inferred-let tuple literal carries its slot-layout size — 0-size local smashed saved BP/RIP (#44)

The N_TUPLE expr arm built its TY_TUPLE with size 0 (only the
annotated N_TTUPLE resolve_type route computed the layout), and
type_default passes TY_TUPLE through, so an inferred
`let t = (4: size, 2: size)` planted a 0-size local at offset 0 —
the element stores landed on the saved BP/RIP and main segfaulted
on RET (cstage; the arg shape instead fell to the global-symbol
path and link-failed). wwstage (exprtype N_TUPLE -> tinfofornode)
was runtime-correct throughout — cstage aligns UP to it; all
fixed shapes are now byte-id. Slot rule mirrors the N_TTUPLE twin
and cgen tuple_eslot, with untyped elements sized at their
type_default (element types stay untyped for the consumer-side
assignability contract).

7 table rows in 941 pin the class (cast/bare/mixed/float elems,
destructure-from-local, call-arg, nested); each fails at master
e8977a4 cstage (segfault or link-fail + byte-id NO).
This commit is contained in:
2026-06-05 09:08:01 +09:00
parent e8977a413d
commit 24e02b259c
2 changed files with 99 additions and 1 deletions

View File

@@ -2040,17 +2040,39 @@ cexpr(Checker *c, Node *n)
}
case N_TUPLE: {
/* keep untyped element types; assignability is checked
* element-wise at the consumer (return / mlet / massign). */
* element-wise at the consumer (return / mlet / massign).
* Size/align must still be planted HERE: an inferred
* `let t = (a, b)` takes this type verbatim as the local's
* type (type_default passes TY_TUPLE through) and cgen's
* N_LET sizes the frame slot from ->size — a 0-size tuple
* planted the local at offset 0, over the saved BP/RIP
* (task #44 segfault; wwstage is the correct reference:
* check.ww exprtype N_TUPLE routes through tinfofornode).
* Slot rule mirrors the N_TTUPLE twin (resolve_type) and
* cgen tuple_eslot; untyped elements are sized at their
* default (tuple_eslot's UNTYPED_STR precedent). */
Type *t = newtype(c->a, TY_TUPLE);
Tparam *head = NULL, *tail = NULL;
u64 sz = 0, al = 1;
for (Node *e = n->list; e; e = e->next) {
Tparam *tp = amalloc(c->a, sizeof *tp);
tp->type = cexpr(c, e);
Type *ed = type_default(tp->type);
if (ed && ed->align > al) al = ed->align;
Type *eu = (ed && ed->kind == TY_NAMED)
? ed->under : ed;
if (eu && (eu->kind == TY_STR || eu->kind == TY_SLICE
|| eu->kind == TY_TAGGED))
sz += (eu->size + 7) & ~(u64)7;
else if (eu == NULL || eu->kind != TY_VOID)
sz += 8; /* sizelint-ok: the slot IS the 8B eightbyte */
if (head == NULL) head = tp;
else tail->next = tp;
tail = tp;
}
t->params = head;
t->size = sz;
t->align = al;
return n->type = t;
}
default: