wcc_ww/check: W3 #105 nested-arrlit gate chases the alias elem type

checkarrlitfits' nested recursion keyed on the raw elemtn kind; a
named-alias element type ([2]row, row=[2]int) arrives as N_TNAME, so
the inner overlong literal skipped the count+range checks and the
module static-DATA route emitted silently TRUNCATED data (ken's
m7c_global: DATAW 1,2,4,5 — exit-masked once the #60 read fix removed
the segv; cstage loud-rejects every spelling via its typed-literal
assignability net). #105: the W1 fill gate never runs on this route,
severity raised post-#60.

Fix: chase elemtn through resolvealias (transitive) at the recursion
gate — alias spellings of any depth take the same checks as the
direct shape at all four contexts funneling through the choke point
(module let / local let / def / struct-field). A direct N_TARRAY
passes through resolvealias unchanged, so accepted shapes are
byte-identical base→tip (m7c_global_ok + exact-fit alias
field/def/2lvl probed ASM-ID vs a base scratch build). The m7/m7b
local overlong rows stay loud, now via the earlier count-naming
checker diagnostic instead of the cgen #270-1c fatal. The
out-of-range narrow inner element louds "array element out of
range" exactly as the direct spelling already did on wwstage.

808_arrlit_overlong: 37 -> 50 checks (+1 accept control
alias_exact_module = ken's m7c_global_ok with a byte-id cell, +4 loud
flips alias_nested_{module,2lvl,def,field} pinning per-stage texts,
+1 REVIEW AMENDMENT alias_nested_local pinning the m7/m7b text move
— pre-fix ww was loud via the late cgen #270-1c fatal; the row reds
if the diag regresses off the checker count text).
989 ratchet zero flips — no lib module-level literal trips the gate.

Filed sibling, not folded: OUTER alias-of-array overlong
(let g: arr = [5 elems], arr=[4]int) still ww-silent-truncates at the
alias-blind call-site N_TARRAY gates; cs louds with the count text.
This commit is contained in:
2026-06-06 06:54:41 +09:00
parent 4b118fa8f8
commit 329481c920
4 changed files with 121 additions and 13 deletions

View File

@@ -14423,11 +14423,17 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
// which wwstage's untyped elements have no analog of; the
// silent accept emitted corrupted DATA / smashed frames.
// Recursion through the one choke point closes any depth.
// A named-alias element type ([2]row) still bypasses — the
// elemtn node is N_IDENT, not N_TARRAY — task #16.
if (elemtn != nil && elemtn.kind == nkind.N_TARRAY
// #105: a named-alias element type ([2]row) arrives as
// N_TNAME and bypassed the kind test — the module
// static-DATA emitter then silently TRUNCATED the overlong
// inner literal (exit-masked once the #60 read fix removed
// the segv). resolvealias is transitive, so alias spellings
// of any depth take the same recursion; a direct N_TARRAY
// passes through unchanged.
let eltr: *node = resolvealias(c, elemtn);
if (eltr != nil && eltr.kind == nkind.N_TARRAY
&& ev != nil && ev.kind == nkind.N_ARRLIT) {
checkarrlitfits(c, elemtn, ev);
checkarrlitfits(c, eltr, ev);
e = e.next;
continue;
};

View File

@@ -4142,11 +4142,17 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
// which wwstage's untyped elements have no analog of; the
// silent accept emitted corrupted DATA / smashed frames.
// Recursion through the one choke point closes any depth.
// A named-alias element type ([2]row) still bypasses — the
// elemtn node is N_IDENT, not N_TARRAY — task #16.
if (elemtn != nil && elemtn.kind == nkind.N_TARRAY
// #105: a named-alias element type ([2]row) arrives as
// N_TNAME and bypassed the kind test — the module
// static-DATA emitter then silently TRUNCATED the overlong
// inner literal (exit-masked once the #60 read fix removed
// the segv). resolvealias is transitive, so alias spellings
// of any depth take the same recursion; a direct N_TARRAY
// passes through unchanged.
let eltr: *node = resolvealias(c, elemtn);
if (eltr != nil && eltr.kind == nkind.N_TARRAY
&& ev != nil && ev.kind == nkind.N_ARRLIT) {
checkarrlitfits(c, elemtn, ev);
checkarrlitfits(c, eltr, ev);
e = e.next;
continue;
};

View File

@@ -14423,11 +14423,17 @@ fn checkarrlitfits(c: *checker, arrtn: *node, rhs: *node) void = {
// which wwstage's untyped elements have no analog of; the
// silent accept emitted corrupted DATA / smashed frames.
// Recursion through the one choke point closes any depth.
// A named-alias element type ([2]row) still bypasses — the
// elemtn node is N_IDENT, not N_TARRAY — task #16.
if (elemtn != nil && elemtn.kind == nkind.N_TARRAY
// #105: a named-alias element type ([2]row) arrives as
// N_TNAME and bypassed the kind test — the module
// static-DATA emitter then silently TRUNCATED the overlong
// inner literal (exit-masked once the #60 read fix removed
// the segv). resolvealias is transitive, so alias spellings
// of any depth take the same recursion; a direct N_TARRAY
// passes through unchanged.
let eltr: *node = resolvealias(c, elemtn);
if (eltr != nil && eltr.kind == nkind.N_TARRAY
&& ev != nil && ev.kind == nkind.N_ARRLIT) {
checkarrlitfits(c, elemtn, ev);
checkarrlitfits(c, eltr, ev);
e = e.next;
continue;
};

View File

@@ -23,10 +23,27 @@
* narrowed to non-array targets so repeat literals into arrays run the
* same checks wwstage always ran.
*
* #105 (the W3 fold): a NAMED-ALIAS element type ([2]row, row=[2]int)
* dodged wwstage's nested recursion — the choke point's inner gate
* keyed on the raw elemtn kind (N_TNAME, not N_TARRAY), so the module
* static-DATA emitter silently TRUNCATED the overlong inner literal
* (exit-masked once #60 removed the read-side segv; cstage loud-
* rejects every spelling via its typed-literal assignability net).
* Fixed by chasing elemtn through resolvealias (transitive) before
* the recursion gate — alias spellings of any depth now take the same
* count + range checks as the direct shape, at all four declaration
* contexts. The alias_nested_* rows pin the flip; alias_exact_module
* is the 0/0 byte-id control (ken's m7c_global_ok).
*
* Out of scope, probed + filed separately: UNDER-long literals (no
* `...`) stay accepted in both stages (Hare rejects); `[0]`/`[_]`
* alen==0 sentinel conflation; wwstage assign/call-arg overlong
* acceptance (cstage already rejects those positions).
* acceptance (cstage already rejects those positions); OUTER alias-
* of-array overlong (`let g: arr = [5 elems]`, arr=[4]int) — the
* call-site N_TARRAY gates are alias-blind, ww still truncates
* (#105-sibling, filed); cstage accepting the out-of-range NESTED
* narrow element ww rejects (range net asymmetry, pre-existing on
* the direct spelling).
*
* accept rows | want
* ----------------------------------+------
@@ -37,6 +54,7 @@
* repeat_fill [4]int = [9...] | runs, 0
* repeat_partial [4]int = [1,2...] | runs, 0
* infer_len [_]int = [1,2,3] | runs, 0
* alias_exact_module [2]row exact | runs, 0 (#105 control)
*
* reject rows (build must FAIL on both drivers, and stderr must name
* both counts — per-stage expected substring: cstage catches the
@@ -49,6 +67,12 @@
* nested module `[2][2]int = [[1,2,3],[4,5]]` (inner overlong —
* pre-fix wwstage emitted corrupted DATA: 1,2,4,5)
* nested field struct{f:[2][2]int} inner overlong
* alias nested module / 2lvl / def / field — the #105 flips: same
* inner-overlong shapes spelled through `type row = [2]int` (and
* `row2 = row`); pre-fix wwstage silently truncated (module DATA)
* or smashed frames (cstage was already loud on every row)
* alias nested local — ww pre-fix loud via the LATE cgen #270-1c
* fatal; pins the text move to the checker count diag
*/
#include <stdio.h>
#include <stdlib.h>
@@ -125,6 +149,21 @@ static const struct row rows[] = {
"\treturn a.len: i32 - 3;\n"
"};\n",
0 },
/* #105 control (ken's m7c_global_ok): alias-elem module global,
* exact fit — must keep building 0/0 byte-id through the chased
* recursion gate. `N: int` cast spelling: the bare-int nested
* accept is cstage-rejected (#17), out of this row's scope. */
{ "alias_exact_module",
"package main;\n"
"type row = [2]int;\n"
"let g: [2]row = [[1: int, 2: int], [4: int, 5: int]];\n"
"export fn main() i32 = {\n"
"\tif (g[0][0] != 1) { return 1; };\n"
"\tif (g[1][1] != 5) { return 2; };\n"
"\treturn 0;\n"
"};\n",
0 },
};
/* Overlong literals — both stages must FAIL the build (the old accept
@@ -210,6 +249,57 @@ static const struct negrow neg[] = {
"\treturn 0;\n"
"};\n",
"not assignable", OVERLONG_3V2 },
/* #105 flips: alias-elem inner overlong, the four declaration
* contexts through the one (now chased) choke point. Pre-fix
* wwstage built all four silently — the module row emitted
* truncated DATA (1,2,4,5; ken's m7c_global, exit-masked since
* #60). cstage was already loud on each via assignability. */
{ "alias_nested_module",
"package main;\n"
"type row = [2]int;\n"
"let g: [2]row = [[1: int, 2: int, 3: int], [4: int, 5: int]];\n"
"export fn main() i32 = { return 0; };\n",
"not assignable", OVERLONG_3V2 },
/* 2-level alias — pins the transitive chase */
{ "alias_nested_2lvl",
"package main;\n"
"type row = [2]int;\n"
"type row2 = row;\n"
"let g: [2]row2 = [[1: int, 2: int, 3: int], [4: int, 5: int]];\n"
"export fn main() i32 = { return 0; };\n",
"not assignable", OVERLONG_3V2 },
{ "alias_nested_def",
"package main;\n"
"type row = [2]int;\n"
"def TAB: [2]row = [[1: int, 2: int, 3: int], [4: int, 5: int]];\n"
"export fn main() i32 = { return 0; };\n",
"not assignable", OVERLONG_3V2 },
/* local let — ww was ALREADY loud pre-fix, but via the cgen
* #270-1c aggregate-element fatal; the chase moves it to the
* checker count diag (ken's m7/m7b text move). This row pins the
* new text so a regression back to the late generic fatal reds. */
{ "alias_nested_local",
"package main;\n"
"type row = [2]int;\n"
"export fn main() i32 = {\n"
"\tlet g: [2]row = [[1: int, 2: int, 3: int], [4: int, 5: int]];\n"
"\treturn 0;\n"
"};\n",
"not assignable", OVERLONG_3V2 },
{ "alias_nested_field",
"package main;\n"
"type row = [2]int;\n"
"type s = struct { f: [2]row };\n"
"export fn main() i32 = {\n"
"\tlet v: s = s{ f = [[1: int, 2: int, 3: int], [4: int, 5: int]] };\n"
"\treturn 0;\n"
"};\n",
"not assignable", OVERLONG_3V2 },
};
static int