wcc/check: #9 reject explicit [N]=[init] over-fill incl [0] (both stages)

An explicit [N]T = [init] with more initializers than N silently
mis-compiled for N==0: the over-fill length-mismatch check was suppressed
when alen==0, because alen==0 doubles as the [_] infer-sentinel after
resolve_type collapses the two. So def/let [0]int=[1,2] silently resized
(cstage exit 2) or OOB-read/segfaulted (wwstage) instead of the loud
length-mismatch that [N]=[init>N] gets everywhere else.

The AST keeps the distinction the Type loses: [_] leaves the N_TARRAY
length-child NULL, an explicit [N] carries N_INTLIT. cstage adds an
is_infer_arr() helper, drops the alen>0 exemption at the over-fill check,
and gates the 4 infer-resize/no-init sites on is_infer_arr so an explicit
[0] flows to the over-fill -> loud. wwstage flips the one shared count
gate (checkarrlitfits) from declen>0 to arrtn.rhs!=nil, which also
dissolves a wwstage local-resize/module-OOB inconsistency.

[_] inference, [0]=[] empty, and [_]-no-init louding all preserved.
Under-long (count<N) stays out of scope (#10). byte-id 990-997 8/8.
test/wcc/820 table-driven; its one empty-[0] global row carves out
byte-id (pre-existing spurious-DATAW divergence, task #15).
This commit is contained in:
2026-06-08 19:54:29 +09:00
parent 1aaa0a3670
commit 29a2ab2a72
6 changed files with 386 additions and 14 deletions

View File

@@ -14499,7 +14499,14 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
// Under-long (count < N, no `...`) stays accepted as before; Hare
// rejects it — task #10.
let declen: u64 = arrayelen(c, arrtn.rhs);
if (declen > 0u64) {
// #9: fire the over-fill whenever the length is EXPLICITLY declared
// (arrtn.rhs present) — incl `[0]`. `[_]` leaves arrtn.rhs nil UNTIL
// inferarraylen stamps it with the real count (runs first), so a
// resolved `[_]` arrives here with cnt == declen (no over-fill). An
// explicit `[0]=[1,2]` keeps arrtn.rhs=N_INTLIT(0) → declen 0, cnt 2 →
// loud. `[0]=[]` → cnt 0, no error. Replaces the `declen > 0` guard,
// the wwstage twin of cstage's dropped `alen > 0`.
if (arrtn.rhs != nil) {
let cnt: u64 = 0u64;
let ce: *node = rhs.list;
for (ce != nil) {

View File

@@ -4218,7 +4218,14 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
// Under-long (count < N, no `...`) stays accepted as before; Hare
// rejects it — task #10.
let declen: u64 = arrayelen(c, arrtn.rhs);
if (declen > 0u64) {
// #9: fire the over-fill whenever the length is EXPLICITLY declared
// (arrtn.rhs present) — incl `[0]`. `[_]` leaves arrtn.rhs nil UNTIL
// inferarraylen stamps it with the real count (runs first), so a
// resolved `[_]` arrives here with cnt == declen (no over-fill). An
// explicit `[0]=[1,2]` keeps arrtn.rhs=N_INTLIT(0) → declen 0, cnt 2 →
// loud. `[0]=[]` → cnt 0, no error. Replaces the `declen > 0` guard,
// the wwstage twin of cstage's dropped `alen > 0`.
if (arrtn.rhs != nil) {
let cnt: u64 = 0u64;
let ce: *node = rhs.list;
for (ce != nil) {

View File

@@ -14499,7 +14499,14 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
// Under-long (count < N, no `...`) stays accepted as before; Hare
// rejects it — task #10.
let declen: u64 = arrayelen(c, arrtn.rhs);
if (declen > 0u64) {
// #9: fire the over-fill whenever the length is EXPLICITLY declared
// (arrtn.rhs present) — incl `[0]`. `[_]` leaves arrtn.rhs nil UNTIL
// inferarraylen stamps it with the real count (runs first), so a
// resolved `[_]` arrives here with cnt == declen (no over-fill). An
// explicit `[0]=[1,2]` keeps arrtn.rhs=N_INTLIT(0) → declen 0, cnt 2 →
// loud. `[0]=[]` → cnt 0, no error. Replaces the `declen > 0` guard,
// the wwstage twin of cstage's dropped `alen > 0`.
if (arrtn.rhs != nil) {
let cnt: u64 = 0u64;
let ce: *node = rhs.list;
for (ce != nil) {