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:
@@ -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:
|
||||
|
||||
@@ -1849,6 +1849,82 @@ static const struct row rows[] = {
|
||||
" return 0;\n"
|
||||
"};\n", 0,
|
||||
K_BUILDERR, "tagged cast source shape unwired" },
|
||||
|
||||
/* ---- task #44: INFERRED-let tuple literal — cstage's N_TUPLE
|
||||
* expr type carried size 0 (only the annotated N_TTUPLE route
|
||||
* computed the slot layout), so cgen's N_LET planted the local
|
||||
* at offset 0: the element stores landed on the saved BP/RIP
|
||||
* and main SEGFAULTED on RET. wwstage (exprtype N_TUPLE →
|
||||
* tinfofornode) was runtime-correct — the rare cstage-is-the-bug
|
||||
* polarity (#263 corollary). Every row below segfaulted (or, for
|
||||
* the arg row, link-failed on the off==0 global fallback) at
|
||||
* master e8977a4 cstage; all are byte-id post-fix. ---- */
|
||||
{ "t44_inferred_let_cast",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let t = (4: size, 2: size);\n"
|
||||
" if (t.0 != 4) { return 1; };\n"
|
||||
" if (t.1 != 2) { return 2; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
/* bare untyped-int elements; the decl alone (no .N read) already
|
||||
* smashed the frame at master — the noread shape rides the same
|
||||
* row via the early stores. */
|
||||
{ "t44_inferred_let_bare",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let t = (4, 2);\n"
|
||||
" if (t.0 != 4) { return 1; };\n"
|
||||
" if (t.1 != 2) { return 2; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
{ "t44_inferred_let_mixed3",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let t = (4: size, 2, 36: size);\n"
|
||||
" if (t.0 + t.2 != 40) { return 1; };\n"
|
||||
" if (t.1 != 2) { return 2; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
{ "t44_inferred_let_float",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let t = (1.5, 2.5);\n"
|
||||
" if (t.0 + t.1 != 4.0) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
/* destructure FROM the inferred local — the mlet source ident
|
||||
* resolved to the 0-size slot at offset 0 (same root). */
|
||||
{ "t44_inferred_destructure",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let t = (40: size, 2: size);\n"
|
||||
" let (a, b) = t;\n"
|
||||
" if (a + b != 42) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
/* inferred local as a tuple CALL ARG — at master the off==0 local
|
||||
* fell to the global-symbol path: `w6l: undefined reference to
|
||||
* 't'` (link-fail, not segfault — same root, different death). */
|
||||
{ "t44_inferred_arg",
|
||||
"package main;\n"
|
||||
"fn add(t: (size, size)) size = {\n"
|
||||
" return t.0 + t.1;\n"
|
||||
"};\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let t = (40: size, 2: size);\n"
|
||||
" if (add(t) != 42) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
/* nested tuple element: slot rule gives an inner tuple one 8B
|
||||
* eightbyte (tuple_eslot parity) — pins the outer layout. */
|
||||
{ "t44_inferred_nested",
|
||||
"package main;\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let t = ((4, 2), 36);\n"
|
||||
" if (t.1 != 36) { return 1; };\n"
|
||||
" return 0;\n"
|
||||
"};\n", 0, K_RUN, NULL },
|
||||
};
|
||||
|
||||
/* build+run via a driver (ww / ww_ww); returns 0 pass, nonzero fail. */
|
||||
|
||||
Reference in New Issue
Block a user