cstage+selfhost+test: fix (*p).f silent drop in N_DOT lhs (read+write)
Pre-existing landmine surfaced by #5 (whole-STRUCT N_ASSIGN). Both stages' N_DOT dispatch gated on `lhs->lhs->kind == N_IDENT`; the parser produces N_UN(STAR, IDENT(p)) for `(*p).f`, so both sides fell off: - Write side (cgassign N_DOT base): emitted nothing, store dropped. - Read side (case N_DOT pointer-auto-deref): cgexpr derefed the pointer as a scalar, AX = first qword of struct, field offset dropped. Fix: retarget base / dot_lhs to the inner IDENT when shape is N_UN(STAR, IDENT). The existing via_ptr branch fires identically to `p.f`. v1 scope is bare-IDENT inner only; `(*expr).f` (non-IDENT pointer expression) is tracked separately as task #19. 702 covers 7 rows: write_i64/i32/str, read_i64/i32/str_len, roundtrip
This commit is contained in:
@@ -1834,10 +1834,22 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
}
|
||||
/* 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. */
|
||||
* write the field; for plain `=` we just write. The base
|
||||
* accepts two parser shapes: a bare IDENT (auto-deref when
|
||||
* the IDENT's type is *T, value-struct otherwise) and the
|
||||
* explicit-deref form `(*p).f = ...` where the parser emits
|
||||
* N_UN(STAR, IDENT(p)). For (*p).f, retarget base to the
|
||||
* inner IDENT so the via_ptr branch fires identically to
|
||||
* `p.f = v`. v1 scope: bare-IDENT inner only; (*expr).f
|
||||
* (non-IDENT inner) falls through to the existing drop
|
||||
* behaviour pending follow-up task. */
|
||||
if (n->lhs && n->lhs->kind == N_DOT && n->lhs->lhs &&
|
||||
n->lhs->lhs->kind == N_IDENT) {
|
||||
(n->lhs->lhs->kind == N_IDENT ||
|
||||
(n->lhs->lhs->kind == N_UN && n->lhs->lhs->op == TK_STAR
|
||||
&& n->lhs->lhs->lhs
|
||||
&& n->lhs->lhs->lhs->kind == N_IDENT))) {
|
||||
Node *base = n->lhs->lhs;
|
||||
if (base->kind == N_UN) base = base->lhs;
|
||||
Type *bt = base->type;
|
||||
Type *u = (bt && bt->kind == TY_NAMED) ? bt->under : bt;
|
||||
int via_ptr = 0;
|
||||
@@ -3480,6 +3492,11 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* Tagged-union field: delegate to the
|
||||
* shared widening writer. Handles
|
||||
* str, scalar, struct literal/ident
|
||||
* payload, and tagged-subset
|
||||
* forwarding (with tag remap). */
|
||||
Type *fu = (ft
|
||||
&& ft->kind == TY_NAMED)
|
||||
? ft->under : ft;
|
||||
@@ -4808,7 +4825,19 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
/* slice/str pseudo-fields: .ptr (offset 0), .len (8), .cap (16).
|
||||
* Arrays don't carry a header; .len uses the static size and
|
||||
* .ptr is the address of the first element. */
|
||||
Type *bt = n->lhs ? n->lhs->type : NULL;
|
||||
/* `(*p).f` read retarget: parser produces n->lhs = N_UN(STAR,
|
||||
* IDENT(p)) with type T (post-deref struct). Pull the inner
|
||||
* IDENT in as dot_lhs so bt resolves to *T and the pointer-
|
||||
* auto-deref branch below fires (mirror of the N_ASSIGN
|
||||
* N_DOT lhs retarget). v1 scope: N_IDENT inner only;
|
||||
* (*expr).f follow-up task pending. Branches that gate on
|
||||
* `n->lhs->kind == N_DOT/N_INDEX/...` keep checking the raw
|
||||
* n->lhs since (*p) isn't either of those shapes. */
|
||||
Node *dot_lhs = n->lhs;
|
||||
if (dot_lhs && dot_lhs->kind == N_UN && dot_lhs->op == TK_STAR
|
||||
&& dot_lhs->lhs && dot_lhs->lhs->kind == N_IDENT)
|
||||
dot_lhs = dot_lhs->lhs;
|
||||
Type *bt = dot_lhs ? dot_lhs->type : NULL;
|
||||
Type *u = (bt && bt->kind == TY_NAMED) ? bt->under : bt;
|
||||
/* Module-qualified value reference: `mod.name`. The checker
|
||||
* leaves SK_USE idents untyped (NULL/ty_err); detect that and
|
||||
@@ -5224,14 +5253,17 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
break;
|
||||
}
|
||||
/* pointer-to-slice/str field: deref and read pseudo-field.
|
||||
* Used by helpers like rt_appendu8(s: *[]u8, v: u8). */
|
||||
* Used by helpers like rt_appendu8(s: *[]u8, v: u8). dot_lhs
|
||||
* gates the N_IDENT check so `(*p).len` (parser N_UN(STAR,
|
||||
* IDENT)) emits the same load as `p.len` after the case-top
|
||||
* retarget. */
|
||||
if (u && u->kind == TY_PTR && u->sub) {
|
||||
Type *inner = u->sub;
|
||||
if (inner->kind == TY_NAMED) inner = inner->under;
|
||||
if (inner && (inner->kind == TY_SLICE || inner->kind == TY_STR)
|
||||
&& (lenfld || capfld || ptrfld)
|
||||
&& n->lhs->kind == N_IDENT) {
|
||||
int off = localfind(locals, n->lhs->str);
|
||||
&& dot_lhs && dot_lhs->kind == N_IDENT) {
|
||||
int off = localfind(locals, dot_lhs->str);
|
||||
ins2(c, A_MOVQ, amem(D_BP, off), areg(D_BX));
|
||||
int delta = ptrfld ? 0 : (lenfld ? 8 : 16);
|
||||
ins2(c, A_MOVQ, amem(D_BX, delta), areg(D_AX));
|
||||
@@ -5239,13 +5271,16 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
}
|
||||
}
|
||||
/* pointer-to-struct field: deref and load. Common pattern:
|
||||
* fn move(p: *point) ... { p.x += dx; ... } */
|
||||
* fn move(p: *point) ... { p.x += dx; ... }
|
||||
* dot_lhs gates this branch so both `p.f` (n->lhs is IDENT)
|
||||
* and `(*p).f` (n->lhs is N_UN(STAR, IDENT), retargeted to
|
||||
* inner IDENT at case-top) emit the same load sequence. */
|
||||
if (u && u->kind == TY_PTR && u->sub) {
|
||||
Type *inner = u->sub;
|
||||
if (inner->kind == TY_NAMED) inner = inner->under;
|
||||
if (inner && inner->kind == TY_STRUCT
|
||||
&& n->lhs->kind == N_IDENT) {
|
||||
int off = localfind(locals, n->lhs->str);
|
||||
&& dot_lhs && dot_lhs->kind == N_IDENT) {
|
||||
int off = localfind(locals, dot_lhs->str);
|
||||
ins2(c, A_MOVQ, amem(D_BP, off), areg(D_BX));
|
||||
for (Tfield *f = inner->fields; f; f = f->next) {
|
||||
if (strcmp(f->name, n->str) != 0) continue;
|
||||
|
||||
Reference in New Issue
Block a user