cstage+selfhost+test: fold N_UN over signed int literal in let DATA emit (#19)

emit_lets / emitletdataw's scalar-8B and array arms only matched bare
N_INTLIT / N_RUNELIT / N_TRUE / N_FALSE / N_NIL on the let rhs. `let
x: i8 = -1i8;` arrives as N_UN(TK_MINUS, N_INTLIT(1)) — none of those
— so cstage's scalar arm hit `else continue;` and dropped the DATAW
row entirely; the array arm bailed at the first non-foldable element
and the skip-array-with-non-NIL-rhs fall-through dropped the whole
row. Wwstage's mirror arms silently emitted zero bytes for negative
literals in both shapes.

Severity split — cstage symptom is no DATA emit, the linker fails
loudly at build time. Wwstage symptom is silent-zero element
substitution for negative array values: compiles, runs, returns
wrong answers. Corpus-coverage-blind on the wwstage side, only
surfaces when a consumer reads the wrong value. Single N_UN-fold
helper application retires both symptoms across both stages. Fourth
corpus-coverage-blind unmask this session (catalog: i64 div/mod CQO
#16, IDENT-local /= no-op, #21 call-arg DX drop, now #19 wwstage
silent-zero).

Route all four sites through fold_int_literal / foldintliteral, the
same helper #24 used on the def-emit side (which already covered
N_UN over the leaf set). The wwstage array arm also picks up an
N_CAST peel and drops a dead non-`...` N_FIELD branch (the parser
never emits non-`...` N_FIELD inside an N_ARRLIT — only as the `...`
repeat marker). Same-path sibling cleanup; rule-11 justified.

Tests:
  - 719_signed_data_emit asserts DATAW <sym>(SB),"<bytes>" lines are
    present in both stages' .s for the {i8, i16, i32, i64} × {scalar,
    1D array} matrix, plus cmp -s byte-id between stages per row.
    Corpus-coverage-blind sentinel per rob's STATUS-3 note.
  - 923_signed_data_emit_run runtime-pins the same matrix plus a
    TK_TILDE row through cstage and wwstage drivers.

995_self_rebuild stays green (ww2==ww3==ww4 byte-id).
This commit is contained in:
2026-05-17 07:25:49 +09:00
parent a8561c03a0
commit 9bd1d0d734
7 changed files with 543 additions and 87 deletions

View File

@@ -7111,12 +7111,14 @@ emit_lets(Cg *c, FILE *out, Node *file)
Node *r = d->rhs;
while (r != NULL && r->kind == N_CAST) r = r->lhs;
if (r == NULL) continue;
if (r->kind == N_INTLIT) v = r->uval;
else if (r->kind == N_RUNELIT) v = r->uval;
else if (r->kind == N_TRUE) v = 1;
else if (r->kind == N_FALSE) v = 0;
else if (r->kind == N_NIL) v = 0;
else continue;
/* Same helper as emit_defs (#24): widens
* the gate to cover N_UN(TK_MINUS/TILDE/PLUS,
* leaf) so `let x: i8 = -1i8;` and friends
* encode as sign-extended two's-complement
* bytes. emit_data_row writes 8 LE bytes
* so narrow signed types just naturally
* round-trip via the sign-extended u64. */
if (!fold_int_literal(r, &v)) continue;
}
emit_data_row(out, "DATAW", mod_mangle(c, d->str), v);
continue;
@@ -7170,15 +7172,12 @@ emit_lets(Cg *c, FILE *out, Node *file)
Node *ev = e;
while (ev && ev->kind == N_CAST) ev = ev->lhs;
if (ev == NULL) { ok = 0; break; }
if (ev->kind == N_INTLIT || ev->kind == N_RUNELIT) {
last = ev->uval;
} else if (ev->kind == N_TRUE) {
last = 1;
} else if (ev->kind == N_FALSE) {
last = 0;
} else if (ev->kind == N_NIL) {
last = 0;
} else {
/* Same helper as the scalar arm above —
* fold_int_literal covers N_UN over a leaf
* so signed-negative array elements (`-1i8`)
* encode as their two's-complement bytes
* once truncated to esz below. */
if (!fold_int_literal(ev, &last)) {
ok = 0;
break;
}