cgen: build the full slice header in the unwrap success shuffle (#6 Mech B)

The N_TRYUNW/N_TRYPROP success shuffle materialized {ptr,len,cap} into {AX,BX,CX} only when the success variant was a str; a slice success got only MOVQ DX,AX (ptr), leaving every slice-unwrap consumer (call-arg push, let-receive store, ident-source) reading junk .len/.cap — silent on BOTH stages (byte-id blind, cstage not the oracle). Widen the success gate to type_isstr||type_isslice (cstage) / typeisstr||typeisslice (wwstage) at all four shuffle sites; str and slice share the identical 24B header shuffle. Stays str||slice-specific — a struct success variant uses a different {AX,DX,CX} ABI (task #12). Value-asserting pin (len!=cap, poison-decoy) reddens under each stage's independent revert.

Follows #6 Mech A (Fix-R); order forced (C1 first or the slice call-arg push reddens byte-id).
This commit is contained in:
2026-06-27 21:10:17 +09:00
parent 3fe4a2cd3b
commit 6e480df180
3 changed files with 86 additions and 4 deletions

View File

@@ -11070,7 +11070,13 @@ cgexpr(Cg *c, Node *n, Local *locals)
for (Tparam *p = u->params; p; p = p->next, i++)
if (i == s_tag) { succ_t = p->type; break; }
}
int success_is_str = type_isstr(succ_t);
/* #6 (Mech B): a slice success variant shares str's 24B
* {ptr,len,cap} header in the tagged ABI {DX,CX,R8}; the same
* shuffle serves both. A struct/aggregate success uses a
* different {AX,DX,CX} eightbyte ABI and stays on the bare
* MOVQ DX,AX fallthrough (separate latent, task #12) — keep
* this gate str||slice-specific. */
int success_is_str = type_isstr(succ_t) || type_isslice(succ_t);
char *cont = mklabel(c, "tryprop_ok");
ins2(c, A_CMPQ, aimm(s_tag), areg(D_AX));
ins1(c, A_JE, abranch(cont));
@@ -11206,7 +11212,13 @@ cgexpr(Cg *c, Node *n, Local *locals)
for (Tparam *p = u->params; p; p = p->next, i++)
if (i == s_tag) { succ_t = p->type; break; }
}
int success_is_str = type_isstr(succ_t);
/* #6 (Mech B): a slice success variant shares str's 24B
* {ptr,len,cap} header in the tagged ABI {DX,CX,R8}; the same
* shuffle serves both. A struct/aggregate success uses a
* different {AX,DX,CX} eightbyte ABI and stays on the bare
* MOVQ DX,AX fallthrough (separate latent, task #12) — keep
* this gate str||slice-specific. */
int success_is_str = type_isstr(succ_t) || type_isslice(succ_t);
char *cont = mklabel(c, "tryunw_ok");
ins2(c, A_CMPQ, aimm(s_tag), areg(D_AX));
ins1(c, A_JE, abranch(cont));