w6c+w6c_ww: struct-lit store into indexed/deref/field place fills via resolver (#20)
A struct-LITERAL rhs aimed at an N_INDEX element (a[i] = pt{...},
(*ts)[i].caps[k] = capture{...}), an N_UN deref place (*p = pt{...}),
or an indexed-base FIELD place (a[i].f = pt{...}, reviewer-20 sibling)
fell to a scalar store tail in BOTH stages: cgexpr on a struct
literal emits nothing (AX=0) and one MOVQ zeroed the place's first
word — every field silently dropped, a str-leading element's
content.ptr nulled (downstream SEGFAULT). Byte-identically wrong, so
every byte-id gate was blind; runtime pins added.
Fix: divert struct-lit-rhs INDEX/UN-STAR/DOT-over-INDEX places past
the legacy arms and widen the F6 assign-resolver gate
(N_DOT -> N_DOT|N_INDEX|N_UN); the existing C1.25 aggregate arm
materialises the literal into a fresh per-use @placescr slot and
word-copies to the cgplaceaddr-resolved address. No new path;
@placescr alloc site stays single per stage. Rider (task #32): an
array-LITERAL rhs at assignment — unwired for EVERY place kind, same
silent zero-word tail — now dies loud at one choke-point until the
fill lands; build-fail rows pin it.
Gates regex fold-5a (run_thread groupstart capture store,
regex.ha:643-651). Residual adjacent gaps (deref ident-rhs truncation,
>24B ident reassign cs!=ww, struct compound acceptance, value-global
DATAW, tuple-lit deref truncation, CALL-rhs RAX-only store) probed
pre-existing and filed as tasks #31 A-G / #32.
This commit is contained in:
@@ -4267,6 +4267,41 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
cgexpr(c, n->rhs, locals);
|
||||
break;
|
||||
}
|
||||
/* #20 (task): struct-lit rhs into an INDEXED struct element —
|
||||
* `a[i] = pt{...}`, `(*ts)[i].caps[k] = capture{...}` — a
|
||||
* DEREF place (`*p = pt{...}`) or an indexed-base FIELD
|
||||
* place (`a[i].f = pt{...}`, same class) skips the legacy
|
||||
* arms and routes to the resolver aggregate arm below (the
|
||||
* single @placescr funnel). The legacy arms' rhs handling
|
||||
* (#270-1b ident/dot/deref gate; deref scalar store; the
|
||||
* a[i].f fldstoreop tail) let the lit fall to a scalar
|
||||
* tail: cgexpr(N_STRUCTLIT) emits nothing (AX=0) and one
|
||||
* MOVQ zeroed the place's first word — every field
|
||||
* silently dropped, a leading str header trashed. */
|
||||
int place_slit = 0;
|
||||
if (n->lhs
|
||||
&& (n->lhs->kind == N_INDEX
|
||||
|| (n->lhs->kind == N_UN && n->lhs->op == TK_STAR)
|
||||
|| (n->lhs->kind == N_DOT && n->lhs->lhs
|
||||
&& n->lhs->lhs->kind == N_INDEX))
|
||||
&& n->op == TK_ASSIGN
|
||||
&& n->rhs && n->rhs->kind == N_STRUCTLIT) {
|
||||
Type *iet = type_chase_named(n->lhs->type);
|
||||
if (iet && iet->kind == TY_STRUCT)
|
||||
place_slit = 1;
|
||||
}
|
||||
/* Task #32: an array-LITERAL rhs at assignment is unwired
|
||||
* for EVERY place kind (ident reassign, index, deref, dot)
|
||||
* — only decl-init fills. Pre-#32 the same scalar tail
|
||||
* zeroed one word silently; die loud until the fill lands.
|
||||
* Slice-typed places are already loud in the checker. */
|
||||
if (n->op == TK_ASSIGN && n->lhs
|
||||
&& n->rhs && n->rhs->kind == N_ARRLIT) {
|
||||
Type *alt = type_chase_named(n->lhs->type);
|
||||
if (alt && alt->kind == TY_ARRAY)
|
||||
fatal("array-literal store at assignment "
|
||||
"unwired (task #32)");
|
||||
}
|
||||
/* p.x = v or p.x += v where p.x is a struct field
|
||||
* (direct or via *struct). For compound ops we read-modify-
|
||||
* write the field; for plain `=` we just write. The base
|
||||
@@ -4645,7 +4680,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* fall through and silently drop the store. Placed before the
|
||||
* `!= N_IDENT` branch so both shapes share one path. */
|
||||
if (n->lhs && n->lhs->kind == N_DOT && n->lhs->lhs
|
||||
&& n->lhs->lhs->kind == N_INDEX) {
|
||||
&& n->lhs->lhs->kind == N_INDEX && !place_slit) {
|
||||
Node *idxbase = n->lhs->lhs->lhs;
|
||||
Node *idx = n->lhs->lhs->rhs;
|
||||
if (idxbase && idxbase->kind == N_IDENT && idx) {
|
||||
@@ -5482,7 +5517,7 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* ptr local) or a more complex expression like s.ptr where
|
||||
* s: *[]u8. We compute the base address, scale the index by
|
||||
* elem size, and store with the right size. */
|
||||
if (n->lhs->kind == N_INDEX && n->lhs->lhs) {
|
||||
if (n->lhs->kind == N_INDEX && n->lhs->lhs && !place_slit) {
|
||||
Node *base = n->lhs->lhs;
|
||||
Type *bt = base->type;
|
||||
Type *u = (bt && bt->kind == TY_NAMED) ? bt->under : bt;
|
||||
@@ -5585,8 +5620,10 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* address, then word-copy esz bytes: the WRITE-twin of the
|
||||
* #268 let-init copy loop. Source shapes mirror that loop
|
||||
* (ident local/global, N_DOT field via cg_dotchain_addr,
|
||||
* `*p` deref); a by-value call result is the deferred #271,
|
||||
* so N_CALL/literal sources fall through unchanged. */
|
||||
* `*p` deref); struct-lit sources divert at the place_slit
|
||||
* gate above (#20), array-lit dies loud (task #32), and a
|
||||
* by-value call result still falls to the scalar tail —
|
||||
* RAX-only store, task #31-G. */
|
||||
if ((is_arr || is_sl || is_ptr) && n->op == TK_ASSIGN
|
||||
&& esubu && (esubu->kind == TY_STRUCT
|
||||
|| esubu->kind == TY_ARRAY
|
||||
@@ -5943,9 +5980,12 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
}
|
||||
/* Deref-target assignment `*p = v;`. The size of the store is
|
||||
* determined by the type *p points at; the pointer expression
|
||||
* is evaluated after the value so we don't need to spill BX. */
|
||||
* is evaluated after the value so we don't need to spill BX.
|
||||
* Retained gap: an aggregate >8B rhs (ident, tuple-lit, call)
|
||||
* truncates to one word here — task #31 A/E/G; struct-lit
|
||||
* diverts at the place_slit gate, array-lit dies loud (#32). */
|
||||
if (n->lhs && n->lhs->kind == N_UN && n->lhs->op == TK_STAR
|
||||
&& n->op == TK_ASSIGN) {
|
||||
&& n->op == TK_ASSIGN && !place_slit) {
|
||||
Type *pt = n->lhs->lhs ? n->lhs->lhs->type : NULL;
|
||||
Type *pu = (pt && pt->kind == TY_NAMED) ? pt->under : pt;
|
||||
Type *vt = (pu && pu->kind == TY_PTR) ? pu->sub : NULL;
|
||||
@@ -6218,8 +6258,15 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* routes through cgplaceaddr; the load/store emission stays
|
||||
* here. Any N_DOT shape the resolver can't address dies
|
||||
* LOUD below: the pre-C1 dispatch tail silently emitted
|
||||
* NOTHING (rhs unevaluated) for every such shape. */
|
||||
if (n->lhs && n->lhs->kind == N_DOT) {
|
||||
* NOTHING (rhs unevaluated) for every such shape.
|
||||
* #20 (task): N_INDEX and N_UN(STAR) lvalues enroll too —
|
||||
* only the struct-lit-rhs diversion above reaches here
|
||||
* (every other indexed/deref shape broke out of its legacy
|
||||
* arm), and the C1.25 aggregate branch fills via
|
||||
* @placescr. */
|
||||
if (n->lhs && (n->lhs->kind == N_DOT
|
||||
|| n->lhs->kind == N_INDEX
|
||||
|| (n->lhs->kind == N_UN && n->lhs->op == TK_STAR))) {
|
||||
Type *ft = n->lhs->type;
|
||||
Type *fu = type_chase_named(ft);
|
||||
int fsz = (int)(ft ? ft->size : 8);
|
||||
|
||||
Reference in New Issue
Block a user