w6c+w6c_ww: cast-wrapped tuple literal widens its whole payload into a tagged slot (#66)
The #242 tuple arm of the widen choke-point (cg_widen_tagged_store /
cgwidentaggedstorebp) gated on a BARE N_TUPLE source. The cast-to-
CONCRETE-VARIANT wrapper ((a, b): range_alias) — the only spelling
real code uses (ref/hare/regex/regex.ha:213) — is not a widen-cast
(its destination is the variant, not the union), so the peel left it
intact and it fell to the SCALAR arm: cursor word 0 stored, payload
slot 1+ silently zero-filled. Both stages, byte-id, gate-blind.
Fix at the choke-point: peel N_CAST(lhs=N_TUPLE) where the NAMED-
peeled cast type is TY_TUPLE and iterate the inner element list; the
variant tag keeps resolving from the CAST's type (exact named match),
so the #241 untyped-element loud-stop stays scoped to the bare form
on both stages.
Closure by construction needed two more arms (reviewer proof-grep):
cg_widen_tagged_push's direct-push fast path classified a tuple-typed
ARG source as scalar — pushed word 0 only AND coerced an unresolved
tag to 0 — so f(((a,b): rng)) bypassed the fixed arm entirely (and
the bare typed (a,b) arg dropped slot 1 the same way). Tuple-typed
sources now route through the scratch store. The remaining non-
literal tuple sources (ident / call result / match binding) have no
word-copy arm in the store and fell to its scalar arm — loud-stop
(rule 7) until #72 wires them. Every tagged-payload materialisation
now funnels through cg_widen_tagged_store, which handles or rejects
every tuple shape: let/assign/return/append (cgen.c:7511) directly,
arg push via the scratch route.
test/936: cast-tuple matrix (let / append local+index-place+deref-
place+ptr-field-place / ident+float+str elements / 3-member layout-neutrality /
direct-arg) + bare-form no-regress (return + arg) + bare-literal and
tuple-ident reject pins, per-row cs==ww byte-id; verified failing
24/40 at parent 8578ad0.
Unblocks regex fold-4 (charset_range_item construction).
This commit is contained in:
@@ -2456,8 +2456,32 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
|
||||
* (AX=tag, DX=word0, CX=word1, R8=word2) and the cgmlet receive
|
||||
* cursor. NOT the packed-by-size t.N field layout (#238). Mirror of
|
||||
* the struct-literal field-flow below, but 8B-slotted, not field-
|
||||
* offset. */
|
||||
if (su && su->kind == TY_TUPLE && src->kind == N_TUPLE) {
|
||||
* offset.
|
||||
*
|
||||
* #66: the cast-wrapped tuple literal `((a, b): range_alias)` is
|
||||
* the spelling real code uses (regex.ha:213) — the cast targets the
|
||||
* CONCRETE variant, so the widen-cast peel above leaves it intact
|
||||
* and pre-#66 it fell to the scalar arm, silently dropping payload
|
||||
* slot 1+. Peel to the inner tuple here; st stays the CAST's type,
|
||||
* which resolves the variant tag by exact named match, so the #241
|
||||
* untyped-element un-matchability does not arise for this form. */
|
||||
Node *tupsrc = NULL;
|
||||
if (su && su->kind == TY_TUPLE) {
|
||||
if (src->kind == N_TUPLE)
|
||||
tupsrc = src;
|
||||
else if (src->kind == N_CAST && src->lhs
|
||||
&& src->lhs->kind == N_TUPLE)
|
||||
tupsrc = src->lhs;
|
||||
/* #72: any OTHER tuple-typed source (ident, call result,
|
||||
* match binding) would fall to the scalar arm below and
|
||||
* silently drop payload slot 1+ — loud-stop (rule 7) until
|
||||
* the word-copy / cursor-receive arms are wired. */
|
||||
if (tupsrc == NULL)
|
||||
fatal("cg_widen_tagged_store: tuple-typed source "
|
||||
"shape unwired (only the bare/cast tuple literal "
|
||||
"carries a full payload; see #72)");
|
||||
}
|
||||
if (tupsrc != NULL) {
|
||||
int tag = cg_tag_for_variant(du, st);
|
||||
/* #242: a tuple built from UNTYPED/literal elements (`(true,7)`)
|
||||
* leaves the src tuple type un-matchable by type_eq, so the
|
||||
@@ -2478,7 +2502,7 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
|
||||
* payload the slotted write assumes. Loud-stop (rule 7); the
|
||||
* SysV eightbyte tuple classification is a deferred follow-up. */
|
||||
int total = 0;
|
||||
for (Node *e = src->list; e; e = e->next)
|
||||
for (Node *e = tupsrc->list; e; e = e->next)
|
||||
total += (node_isstr(e) || node_isslice(e)) ? 24 : 8;
|
||||
if (8 + total > sz)
|
||||
fatal("cg_widen_tagged_store: tuple-in-union payload needs "
|
||||
@@ -2489,7 +2513,7 @@ cg_widen_tagged_store(Cg *c, Local **locals_p, Type *dst, Node *src,
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BP, write_off + k));
|
||||
int foff = 0;
|
||||
for (Node *e = src->list; e; e = e->next) {
|
||||
for (Node *e = tupsrc->list; e; e = e->next) {
|
||||
int e_isf32 = 0;
|
||||
int isflt = fld_isfloat(e->type, &e_isf32);
|
||||
int wide = node_isstr(e) || node_isslice(e);
|
||||
@@ -2702,11 +2726,17 @@ cg_widen_tagged_push(Cg *c, Local **locals_p, Type *dst, Node *src, int sz)
|
||||
Type *su = (st && st->kind == TY_NAMED) ? st->under : st;
|
||||
int src_is_struct = su && su->kind == TY_STRUCT;
|
||||
int src_is_tagged = su && su->kind == TY_TAGGED;
|
||||
/* #66: a tuple-typed source has no direct-push shape — the scalar
|
||||
* fast arm below would push word 0 only (payload slot 1+ dropped)
|
||||
* and coerce an unresolved tag to 0. Route through the scratch
|
||||
* store, whose #242/#66 tuple arm handles the literal/cast forms
|
||||
* and loud-stops the rest (#72). */
|
||||
int src_is_tuple = su && su->kind == TY_TUPLE;
|
||||
/* #38b: a MEMORY-class (>48B) dst slot always routes through the
|
||||
* scratch path — the str/slice fast arms push exactly 4 words,
|
||||
* short of the slot's msz/8 the mem pre-pass accounts for. */
|
||||
int dst_is_mem = tagged_memarg_size(dst) > 0;
|
||||
if (!src_is_struct && !src_is_tagged && !dst_is_mem) {
|
||||
if (!src_is_struct && !src_is_tagged && !src_is_tuple && !dst_is_mem) {
|
||||
/* Direct-push fast path: str / slice / scalar / pointer. */
|
||||
cgexpr(c, src, *locals_p);
|
||||
int tag = cg_tag_for_variant(du, st);
|
||||
|
||||
Reference in New Issue
Block a user