parse/cgen: tuple-destructure assign + extra type-inference holes
Closes the gaps that kept ww_ww from rebuilding the wwstage byte- for-byte. The pre-existing parser silently produced a broken AST on `a, b = fn();` (a no-op exprstmt + a single assign that lost the second tuple slot); the cgen leaned on a handful of cases that the surface type-walker didn't yet cover, so 6a/6l/ww built through wwstage drifted by a handful of bytes per file. Parser: - Multi-assign in parsestmt mirrors cmd/wwc/parse.c:1015-1031. If parseexpr is followed by `,`, switch into N_MASSIGN: collect the chained lvalues with parsebin(parseunary, 1) so they don't eat the trailing `=`, then absorb `= rhs;` and emit the node. Cgen: - N_MASSIGN handler stores AX into l0's slot, pops DX into l1's slot. Same shape as cmd/6c/cgen.c:2424-2440. Lvalues beyond two are dropped (C drops them too). - N_INDEX added to node_isunsigned: `p[i]` where p is *u8/[]u8/ [N]u8 now flags unsigned, so `p[i] >= 48u8` emits JAE instead of JGE. Bit 6a's parsenum. - node_isstr's N_DOT branch now handles chained dots via dot_inner_struct_ptr, so `p.to.asym` (Adr.asym is str) flags as str and push_args_rev pushes both halves. - index_base_esz returns 1 for str-typed struct fields. Was defaulting to 8, so `node.s[i]` scaled by 8 and used MOVQ instead of MOVZBQ. Bit ww/visit_seen. - N_LET with no initializer zero-inits the slot when the underlying type is an 8-byte primitive (pointer, fn-ptr, i64/u64, ...) — matches cmd/6c/cgen.c:2181. Structs, strings, slices, etc. are left for per-field writes, even when their slot rounds up to 8 in scan_locals. New helper type_is_8byte_primitive walks the type AST to make the same call C's checker would. Tests stay 17/17; new self-rebuild test (995) lands in the next commit and gates on these fixes.
This commit is contained in:
@@ -744,9 +744,31 @@ fn parsestmt(p: *parser) *node = {
|
||||
expect_tok(p, TK_SEMI, "expected ';' after continue");
|
||||
return newnode(p.a, N_CONTINUE, pf, pl, pc);
|
||||
};
|
||||
// expression statement
|
||||
// expression statement, or tuple-destructure multi-assign:
|
||||
// a, b = expr;
|
||||
// Mirrors cmd/wwc/parse.c:1015-1031. We parse the first lvalue
|
||||
// with parseexpr (matches the C side); subsequent lvalues go
|
||||
// through parsebin(parseunary, 1) so the `=` stays for us to
|
||||
// consume — parseexpr would absorb it.
|
||||
let e: *node = parseexpr(p);
|
||||
if (p.cur_kind == TK_COMMA) {
|
||||
let m: *node = newnode(p.a, N_MASSIGN, pf, pl, pc);
|
||||
let head: *node = e;
|
||||
let tail: *node = e;
|
||||
for (p.cur_kind == TK_COMMA) {
|
||||
advance(p);
|
||||
let lv: *node = parsebin(p, parseunary(p), 1);
|
||||
tail.next = lv;
|
||||
tail = lv;
|
||||
};
|
||||
expect_tok(p, TK_ASSIGN, "expected '=' after multi-assign lvalues");
|
||||
m.rhs = parseexpr(p);
|
||||
m.list = head;
|
||||
expect_tok(p, TK_SEMI, "expected ';' after multi-assign");
|
||||
return m;
|
||||
};
|
||||
let n: *node = newnode(p.a, N_EXPRSTMT, pf, pl, pc);
|
||||
n.lhs = parseexpr(p);
|
||||
n.lhs = e;
|
||||
expect_tok(p, TK_SEMI, "expected ';' after expression statement");
|
||||
return n;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user