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:
@@ -312,8 +312,24 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node, memphase: bool) i32 = {
|
||||
// then push slot words high → low). Scalar / str go via
|
||||
// the direct push fast path below — keeps wwstage's asm
|
||||
// byte-identical to cstage for selfhost source.
|
||||
// #66: a tuple-typed source has no direct-push shape — the
|
||||
// scalar fast arm 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). Mirrors
|
||||
// cstage cg_widen_tagged_push src_is_tuple.
|
||||
let argtup: *tinfo = arg.type_: *tinfo;
|
||||
for (argtup != nil && argtup.kind == tykind.TY_NAMED) {
|
||||
argtup = argtup.under;
|
||||
};
|
||||
let argistuple: bool = false;
|
||||
if (argtup != nil) {
|
||||
if (argtup.kind == tykind.TY_TUPLE) {
|
||||
argistuple = true;
|
||||
};
|
||||
};
|
||||
let pname: str = rhsstructpayload(c, arg);
|
||||
if (pname.len > 0) {
|
||||
if (pname.len > 0 || argistuple) {
|
||||
let ptype: *node = param.lhs;
|
||||
let scroff: i32 = tagscradd(c, widensz);
|
||||
emitline("\tXORQ\tAX, AX\n");
|
||||
@@ -3305,7 +3321,46 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
|
||||
// (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
|
||||
// cstage cg_widen_tagged_store's TY_TUPLE arm.
|
||||
if (src != nil) { if (src.kind == nkind.N_TUPLE) {
|
||||
//
|
||||
// #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; src.type_ 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.
|
||||
let tupsrc: *node = nil;
|
||||
let tupcast: bool = false;
|
||||
if (src != nil) {
|
||||
if (src.kind == nkind.N_TUPLE) { tupsrc = src; };
|
||||
if (src.kind == nkind.N_CAST) {
|
||||
if (src.lhs != nil) {
|
||||
if (src.lhs.kind == nkind.N_TUPLE) {
|
||||
tupsrc = src.lhs;
|
||||
tupcast = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
// #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. Mirrors
|
||||
// cstage cg_widen_tagged_store.
|
||||
if (tupsrc == nil) {
|
||||
let stu72: *tinfo = src.type_: *tinfo;
|
||||
for (stu72 != nil && stu72.kind == tykind.TY_NAMED) {
|
||||
stu72 = stu72.under;
|
||||
};
|
||||
if (stu72 != nil) {
|
||||
if (stu72.kind == tykind.TY_TUPLE) {
|
||||
let m72: str = "cgwidentaggedstore: tuple-typed source shape unwired (only the bare/cast tuple literal carries a full payload; see #72)\n";
|
||||
os.write(2, m72.ptr, m72.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
if (tupsrc != nil) {
|
||||
let stu: *tinfo = src.type_: *tinfo;
|
||||
for (stu != nil && stu.kind == tykind.TY_NAMED) { stu = stu.under; };
|
||||
if (stu != nil) { if (stu.kind == tykind.TY_TUPLE) {
|
||||
@@ -3319,9 +3374,11 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
|
||||
// cstage can't type: a bool literal (N_TRUE/N_FALSE) or a
|
||||
// suffix-less numeric literal (untyped_int/untyped_float).
|
||||
// LIFT BOTH stage guards together when #241 fixes cstage
|
||||
// literal typing -> symmetric accept.
|
||||
let bl: *node = src.list;
|
||||
for (bl != nil) {
|
||||
// literal typing -> symmetric accept. BARE form only (#66):
|
||||
// the cast form resolves its tag from the cast's type on
|
||||
// BOTH stages, so bare elements are fine there.
|
||||
let bl: *node = tupsrc.list;
|
||||
for (bl != nil && !tupcast) {
|
||||
let bare: bool = false;
|
||||
if (bl.kind == nkind.N_TRUE) { bare = true; };
|
||||
if (bl.kind == nkind.N_FALSE) { bare = true; };
|
||||
@@ -3361,7 +3418,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
|
||||
// SysV eightbyte tuple classification is a deferred
|
||||
// follow-up. Symmetric with cstage cg_widen_tagged_store.
|
||||
let ttotal: i32 = 0;
|
||||
let ce: *node = src.list;
|
||||
let ce: *node = tupsrc.list;
|
||||
for (ce != nil) {
|
||||
if (nodeisstr(c, ce) || nodeisslice(c, ce)) {
|
||||
ttotal += 24;
|
||||
@@ -3382,7 +3439,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
|
||||
tzk += 8;
|
||||
};
|
||||
let tfoff: i32 = 0;
|
||||
let te: *node = src.list;
|
||||
let te: *node = tupsrc.list;
|
||||
for (te != nil) {
|
||||
let isflt: bool = isfloattype(c, te);
|
||||
let wide: bool = nodeisstr(c, te) || nodeisslice(c, te);
|
||||
@@ -3426,7 +3483,7 @@ fn cgwidentaggedstorebp(c: *cgen, dst: *tinfo, src: *node, slot_off: i32, slot_s
|
||||
emitline("(BP)\n");
|
||||
return;
|
||||
}; };
|
||||
}; };
|
||||
};
|
||||
// Struct payload (literal or ident).
|
||||
let sname: str = rhsstructpayload(c, src);
|
||||
if (sname.len > 0) {
|
||||
|
||||
Reference in New Issue
Block a user