wcc/cgen: #64+#68 tuple-literal cursor-fill decl-blind — massign + call-arg widen (both-stage)
A tuple LITERAL with a declared-tagged element reached the cursor-fill
helper (cg_tuple_lit_to_cursor) through the generic cgexpr(N_TUPLE) arm
with no declared type, so the element was stored stamped-keyed at its
constructed scalar width rather than widened into the declared tagged box.
Both consumers ran silent and wrong on both stages (#263 gate-blind:
cs==ww byte-identical, both wrong — runtime is the only net).
#64 massign: N_MASSIGN derives a declared tuple type from the lvalue
binding types and threads it into cg_tuple_lit_to_cursor + the receive
loop (mirror of the #57 N_LET wire); a `_` target falls back to the rhs
literal element type for cursor stride.
#68 call-arg: the send is made param-aware (fill over the PARAM tuple) and
the restage guard graduates a declared-tagged element to a real widen
(reusing cg_widen_tagged_store); nested tuple/struct/array elements and
tagged elements with no param decl stay rule-7 loud. The matching
pop/drain is made param-aware too so push count == pop count: a
param-aware send pushes the box's N words, so the drain must pop N or the
SysV arg sequence skews. This is a push/pop balance requirement of the
send change, not a separate latent under-drain (the standalone trailing-
arg drain is already correct at HEAD).
Closed by construction: the only remaining cg_tuple_lit_to_cursor caller
passing NULL/nil is the generic cgexpr(N_TUPLE) arm, provably non-widening
(constructed type == governing type). The four widening consumers — LET,
RETURN, MASSIGN, call-arg — are all decl-wired. Whole-tuple single-ident
reassign from a tuple literal is rule-7 loud (task #49), not a silent
widening consumer, so the residual NULL arm stays non-widening.
Pin: 945_tuple_lit_declblind_run — massign / call-arg / `_`-control /
call-arg-drain / nested-tuple-ERR rows, each base-fail at abd97e6 and
post-pass with cs==ww byte-id.
This commit is contained in:
107
cmd/w6c/cgen.c
107
cmd/w6c/cgen.c
@@ -250,6 +250,23 @@ node_tuplearg(Node *n)
|
||||
return (u && u->kind == TY_TUPLE) ? u : NULL;
|
||||
}
|
||||
|
||||
/* node_tuplearg_decl — #68: like node_tuplearg, but an N_TUPLE LITERAL arg
|
||||
* with a tuple PARAM type reports the DECLARED param tuple (element widths
|
||||
* keyed on the param, so a declared-tagged element's box words drive the
|
||||
* send/drain). The literal's own type is element-constructed (a concrete
|
||||
* rvalue under a tagged slot counts ONE word, not the box) — see the cgcall
|
||||
* paramtup send. Every other source falls back to node_tuplearg. */
|
||||
static Type *
|
||||
node_tuplearg_decl(Node *n, Type *param)
|
||||
{
|
||||
if (n && n->kind == N_TUPLE && param) {
|
||||
Type *pu = type_chase_named(param);
|
||||
if (pu && pu->kind == TY_TUPLE)
|
||||
return pu;
|
||||
}
|
||||
return node_tuplearg(n);
|
||||
}
|
||||
|
||||
/* #83: positional tuple register-return ABI. Tuple elements ride
|
||||
* consecutive eightbytes over tuple_rseq[]; a slice/str rides its 3-word
|
||||
* {ptr,len,cap} header (ref/hare/rt/ensure.ha:4-8, ty_str->size SSoT), a
|
||||
@@ -8842,10 +8859,12 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
int widen_sz[64] = {0};
|
||||
Type *widen_param[64] = {0};
|
||||
int memarg[64] = {0};
|
||||
Type *argparam[64] = {0}; /* #68: declared param type per arg */
|
||||
{
|
||||
Tparam *p = callee_params;
|
||||
for (int i = 0; i < argcount; i++) {
|
||||
if (p == NULL) break;
|
||||
argparam[i] = p->type;
|
||||
Type *at = args[i] ? args[i]->type : NULL;
|
||||
int psz = tagged_arg_size(p->type);
|
||||
if (psz > 0) {
|
||||
@@ -9207,8 +9226,26 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
args[i], widen_sz[i]);
|
||||
continue;
|
||||
}
|
||||
cgexpr(c, args[i], locals);
|
||||
Type *tuparg_push = node_tuplearg(args[i]);
|
||||
/* #68: a tuple-LITERAL arg derives its DECLARED tuple
|
||||
* type from the callee PARAM (the #57 decl wire,
|
||||
* extended to call-arg send), so a declared-tagged
|
||||
* element's concrete rvalue widens into the box cursor;
|
||||
* the restage + push then key on the param tuple type,
|
||||
* not the literal's element-constructed type. */
|
||||
Type *paramtup = NULL;
|
||||
if (args[i] && args[i]->kind == N_TUPLE) {
|
||||
Type *pcu = argparam[i]
|
||||
? type_chase_named(argparam[i]) : NULL;
|
||||
if (pcu && pcu->kind == TY_TUPLE)
|
||||
paramtup = pcu;
|
||||
}
|
||||
if (paramtup)
|
||||
cg_tuple_lit_to_cursor(c, &locals, args[i],
|
||||
paramtup);
|
||||
else
|
||||
cgexpr(c, args[i], locals);
|
||||
Type *tuparg_push = paramtup
|
||||
? paramtup : node_tuplearg(args[i]);
|
||||
/* #32 (C-t2, rule 7): a tuple-typed arg from a source
|
||||
* shape whose cgexpr does NOT fill the return cursor
|
||||
* (chain reads, match exprs, ...) must die loud here —
|
||||
@@ -9307,11 +9344,22 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* cannot carry it; pre-guard it ran
|
||||
* WRONG (inner words skewed). Loud
|
||||
* until a consumer motivates wiring. */
|
||||
/* #68: a declared-tagged element graduates
|
||||
* to a real widen — the decl-aware send
|
||||
* (cg_tuple_lit_to_cursor over the param
|
||||
* tuple type) already left the box words in
|
||||
* the cursor, so the count + tuple_store
|
||||
* below carry them. Only the param-decl
|
||||
* literal path is wired; nested tuple/
|
||||
* struct/array stay rule-7 loud, as does a
|
||||
* tagged element with no param decl (the
|
||||
* cursor was filled stamped-keyed). */
|
||||
Type *cu = type_chase_named(p->type);
|
||||
if (cu && (cu->kind == TY_TUPLE
|
||||
|| cu->kind == TY_STRUCT
|
||||
|| cu->kind == TY_ARRAY
|
||||
|| cu->kind == TY_TAGGED))
|
||||
|| (cu->kind == TY_TAGGED
|
||||
&& !paramtup)))
|
||||
fatal("#32: tuple arg element "
|
||||
"kind unsupported (nested "
|
||||
"tuple/struct/array/tagged; "
|
||||
@@ -9549,7 +9597,8 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
else
|
||||
stackslots++;
|
||||
}
|
||||
} else if ((tu = node_tuplearg(args[i])) != NULL) {
|
||||
} else if ((tu = node_tuplearg_decl(args[i],
|
||||
argparam[i])) != NULL) {
|
||||
/* #163: drain the tuple's staged words (pushed
|
||||
* slot+0 first) into the SysV arg cursor by SysV
|
||||
* class — a float MOVSD/MOVSS off (SP) into the
|
||||
@@ -9557,7 +9606,14 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* next INTEGER arg reg (DI/SI/..); a slice/str its
|
||||
* 3-word {ptr,len,cap}. Reg overflow loud-stops
|
||||
* (rule 7): the partial-spill stitch is out of
|
||||
* scope (twin of #164's cap). */
|
||||
* scope (twin of #164's cap).
|
||||
*
|
||||
* #68: a tuple-LITERAL arg drains over the PARAM
|
||||
* tuple element widths (node_tuplearg_decl), so a
|
||||
* declared-tagged element's box words pop into the
|
||||
* arg cursor together with the i64 that follows —
|
||||
* the literal's element-constructed types undercount
|
||||
* a wide box. Mirrors the param-aware send. */
|
||||
int ef32;
|
||||
for (Tparam *p = tu->params; p; p = p->next) {
|
||||
if (fld_isfloat(p->type, &ef32)) {
|
||||
@@ -14084,15 +14140,42 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
* SAME producer source the SEND walks. */
|
||||
int sret_recv = (n->rhs && n->rhs->kind == N_CALL)
|
||||
? cg_sret_retsize(n->rhs->type) : 0;
|
||||
/* #64 (filed, rule 7): an N_TUPLE literal rhs rides this
|
||||
* decl-less cgexpr route, so a declared-TAGGED element's
|
||||
* concrete rvalue still fills the cursor stamped-keyed
|
||||
* (silent skew) — the #57 decl wire stops at return/let. */
|
||||
cgexpr(c, n->rhs, *locals);
|
||||
Type *rt = n->rhs ? n->rhs->type : NULL;
|
||||
Type *ru = type_chase_named(rt);
|
||||
Tparam *tp0 = (ru && ru->kind == TY_TUPLE) ? ru->params : NULL;
|
||||
int mf32;
|
||||
/* #64: a tuple-LITERAL rhs carries a DECLARED tuple type (built
|
||||
* from the lvalue binding types) into the cursor fill, so a
|
||||
* declared-tagged element's concrete rvalue widens into the box
|
||||
* instead of riding the decl-less stamped-keyed route — the #57
|
||||
* decl wire extended past N_LET/N_RETURN to destructure-reassign.
|
||||
* A `_` lvalue is never type-stamped (the checker skips it) —
|
||||
* fall back to the rhs literal element type for its cursor
|
||||
* stride so the next element stays aligned (harec `_` advance;
|
||||
* pinned by the R3 control row). */
|
||||
int litrhs = (n->rhs && n->rhs->kind == N_TUPLE);
|
||||
Tparam *declp = NULL;
|
||||
if (litrhs) {
|
||||
Tparam **dpp = &declp;
|
||||
Tparam *tpw = tp0;
|
||||
for (Node *l = n->list; l; l = l->next) {
|
||||
Tparam *dpn = amalloc(c->a, sizeof *dpn);
|
||||
dpn->name = NULL;
|
||||
dpn->type = l->type ? l->type
|
||||
: (tpw ? tpw->type : NULL);
|
||||
dpn->next = NULL;
|
||||
dpn->variadic = 0;
|
||||
*dpp = dpn;
|
||||
dpp = &dpn->next;
|
||||
if (tpw) tpw = tpw->next;
|
||||
}
|
||||
Type *decltt = newtype(c->a, TY_TUPLE);
|
||||
decltt->params = declp;
|
||||
cg_tuple_lit_to_cursor(c, locals, n->rhs, decltt);
|
||||
} else {
|
||||
cgexpr(c, n->rhs, *locals);
|
||||
}
|
||||
Tparam *recvp = litrhs ? declp : tp0;
|
||||
if (sret_recv > 0) {
|
||||
int scr = cg_sretscr_off;
|
||||
int foff = 0;
|
||||
@@ -14137,7 +14220,7 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
int gpcap = TUPLE_GPCAP;
|
||||
int ssecap = TUPLE_SSECAP;
|
||||
int gptotal = 0, ssetotal = 0;
|
||||
for (Tparam *tp = tp0; tp; tp = tp->next) {
|
||||
for (Tparam *tp = recvp; tp; tp = tp->next) {
|
||||
if (fld_isfloat(tp->type, &mf32))
|
||||
ssetotal++;
|
||||
else
|
||||
@@ -14152,7 +14235,7 @@ cgstmt(Cg *c, Node *n, Local **locals, int *frame)
|
||||
"capacity (%d eightbytes: X0,X1); see return-ABI #10",
|
||||
ssecap);
|
||||
int gpcur = 0, ssecur = 0;
|
||||
Tparam *tp = tp0;
|
||||
Tparam *tp = recvp;
|
||||
for (Node *l = n->list; l; l = l->next) {
|
||||
Type *et = tp ? tp->type : NULL;
|
||||
int isflt = fld_isfloat(et, &mf32);
|
||||
|
||||
Reference in New Issue
Block a user