ww: hare-feature batch (_, const, [_]T, ..., size/offset, assert, for-else)

This commit is contained in:
2026-05-11 19:38:12 +09:00
parent 579cc39f9b
commit 6219a47c6f
25 changed files with 1496 additions and 1049 deletions

View File

@@ -33,7 +33,7 @@ fn scanlocals(c: *cgen, n: *node) i32 = {
// of the caller. Same-name re-declarations share the first
// slot (see scanseenmark / localadd).
if (!scanseenmark(c, n.str)) {
let sz: i32 = slotsize(c, n.lhs);
let sz: i32 = letslotsize(c, n);
if (sz < 8) { sz = 8; };
if ((sz & 7) != 0) { sz = (sz + 7) & ~7; };
total += sz;

View File

@@ -736,6 +736,19 @@ fn cgcall(c: *cgen, n: *node) void = {
fn cgassign(c: *cgen, n: *node) void = {
let lhs: *node = n.lhs;
// Discard lvalue `_ = expr;` — evaluate rhs for side effects,
// write nothing. Detected by lhs being an N_IDENT with empty str
// (planted by parseprimary on the TK_UNDER token).
if (lhs != nil) {
if (lhs.kind == N_IDENT) {
if (lhs.str.len == 0) {
if (n.op == TK_ASSIGN) {
cgexpr(c, n.rhs);
return;
};
};
};
};
// `*p = v` — deref-assign. Element width comes from the
// pointer's declared type. Mirrors C cgen: eval rhs (AX,
// and BX if str), push, eval pointer, pop value, store.

View File

@@ -126,7 +126,7 @@ fn cgexprstmt(c: *cgen, n: *node) void = {
fn cglet(c: *cgen, n: *node) void = {
let nm: str = n.str;
let sz: i32 = slotsize(c, n.lhs);
let sz: i32 = letslotsize(c, n);
let off: i32 = localadd(c, nm, sz, n.lhs);
if (n.rhs != nil) {
let rhs: *node = n.rhs;
@@ -187,10 +187,84 @@ fn cglet(c: *cgen, n: *node) void = {
c.lastwasreturn = 0;
return;
};
// Array literal init: `let xs: [N]T = [a, b, c];` (or [_]T).
// Walk elements in declaration order, store each at off + i*esz
// using the right width for the element type. Trailing `...`
// after the last value (an N_FIELD with str=="...") fills the
// remaining slots up to the declared length with that value.
if (rhs.kind == N_ARRLIT) {
let elemn: *node = n.lhs.lhs;
let esz: i32 = 8;
if (elemn != nil) {
if (elemn.kind == N_TNAME) {
let ps: i32 = primsize(elemn.str);
if (ps > 0) { esz = ps; };
};
};
let mop: str = "MOVQ";
if (esz == 1) { mop = "MOVB"; }
else { if (esz == 4) { mop = "MOVL"; }; };
let idx: i32 = 0;
let repeat: bool = false;
let e: *node = rhs.list;
for (e != nil) {
if (e.kind == N_FIELD) {
if (streq(e.str, "...")) {
repeat = true;
e = nil;
} else {
cgexpr(c, e);
emitline("\t");
emitline(mop);
emitline("\tAX, ");
emitoff((off + idx * esz): i64);
emitline("(BP)\n");
idx += 1;
e = e.next;
};
} else {
cgexpr(c, e);
emitline("\t");
emitline(mop);
emitline("\tAX, ");
emitoff((off + idx * esz): i64);
emitline("(BP)\n");
idx += 1;
e = e.next;
};
};
// AX still holds the last stored value; fill remaining
// slots up to the declared length with it.
if (repeat) {
let total: i32 = idx;
if (n.lhs != nil) {
if (n.lhs.kind == N_TARRAY) {
if (n.lhs.rhs != nil) {
if (n.lhs.rhs.kind == N_INTLIT) {
total = n.lhs.rhs.uval: i32;
};
};
};
};
for (idx < total) {
emitline("\t");
emitline(mop);
emitline("\tAX, ");
emitoff((off + idx * esz): i64);
emitline("(BP)\n");
idx += 1;
};
};
c.lastwasreturn = 0;
return;
};
// Struct literal init: `let p: point = point{x=..., y=...};`.
// For each field in the lit, evaluate its value and store at
// the field's offset within the slot. Field-name → offset
// from the struct registry.
// from the struct registry. When the literal carries
// op == TK_ELLIPSIS (autofill marker from the parser), the
// entire slot is zero-filled first so unmentioned fields
// read as 0.
if (rhs.kind == N_STRUCTLIT) {
let trefn: *node = rhs.lhs;
let sname: str;
@@ -201,6 +275,29 @@ fn cglet(c: *cgen, n: *node) void = {
};
let si: *structinfo = structlookup(c, sname);
if (si != nil) {
if (rhs.op == TK_ELLIPSIS) {
let total: i32 = si.totsize;
emitline("\tXORQ\tAX, AX\n");
let zi: i32 = 0;
for (zi + 8 <= total) {
emitline("\tMOVQ\tAX, ");
emitoff((off + zi): i64);
emitline("(BP)\n");
zi += 8;
};
for (zi + 4 <= total) {
emitline("\tMOVL\tAX, ");
emitoff((off + zi): i64);
emitline("(BP)\n");
zi += 4;
};
for (zi < total) {
emitline("\tMOVB\tAX, ");
emitoff((off + zi): i64);
emitline("(BP)\n");
zi += 1;
};
};
let fieldnode: *node = rhs.list;
for (fieldnode != nil) {
if (fieldnode.kind == N_FIELD) {
@@ -292,6 +389,11 @@ fn cgfor(c: *cgen, n: *node) void = {
// label when there's no post-expression.
let topl: str = mklabel(c, "loop");
let endl: str = mklabel(c, "endloop");
// `else` runs at natural cond-false exit; break skips it. When
// present, branch the cond-fail edge to a separate natural_exit
// label so the else body sits between it and the break target.
let naturall: str = endl;
if (n.els != nil) { naturall = mklabel(c, "elseloop"); };
if (n.lhs != nil) { cgstmt(c, n.lhs); };
@@ -299,7 +401,7 @@ fn cgfor(c: *cgen, n: *node) void = {
if (n.cond != nil) {
cgexpr(c, n.cond);
emitline("\tCMPQ\t$0, AX\n");
emitline("\tJE\t"); emitline(endl); emitline("\n");
emitline("\tJE\t"); emitline(naturall); emitline("\n");
};
c.loopendbuf[c.looptop] = endl;
@@ -312,6 +414,10 @@ fn cgfor(c: *cgen, n: *node) void = {
if (n.rhs != nil) { cgexpr(c, n.rhs); };
emitline("\tJMP\t"); emitline(topl); emitline("\n");
if (n.els != nil) {
emitlabel(naturall);
cgstmt(c, n.els);
};
emitlabel(endl);
c.lastwasreturn = 0;
return;

View File

@@ -619,6 +619,52 @@ fn primsize(name: str) i32 = {
return 0;
};
// letslotsize — slot size for a `let` binding. Like slotsize, but
// detects `[_]T = arrlit;` (the type-AST has rhs == nil as the
// length-inferred sentinel) and computes count × element-size from
// the initialiser. Used by both scanlocals (prologue sizing) and
// cglet (slot alloc) so they agree on the frame layout.
export fn letslotsize(c: *cgen, n: *node) i32 = {
// `[_]T = arrlit;` — inferred-length array. slotsize would
// return elem_size * 1 (treating missing length as 1); intercept
// and compute the real count first.
if (n.lhs != nil) {
if (n.lhs.kind == N_TARRAY) {
if (n.lhs.rhs == nil) {
if (n.rhs != nil) {
if (n.rhs.kind == N_ARRLIT) {
let elemn: *node = n.lhs.lhs;
let esz: i32 = 8;
if (elemn != nil) {
if (elemn.kind == N_TNAME) {
let ps: i32 = primsize(elemn.str);
if (ps > 0) { esz = ps; };
};
};
let cnt: i32 = 0;
let e: *node = n.rhs.list;
for (e != nil) {
let adv: bool = true;
if (e.kind == N_FIELD) {
if (streq(e.str, "...")) {
e = nil;
adv = false;
};
};
if (adv) {
cnt += 1;
e = e.next;
};
};
return esz * cnt;
};
};
};
};
};
return slotsize(c, n.lhs);
};
fn slotsize(c: *cgen, typn: *node) i32 = {
if (typn == nil) { return 8; };
let k: i32 = typn.kind;