w6c+wwstage: store struct-literal array-field init (#249 BUG A)
A struct literal initialising an array-typed field as a local
(`e{ encmap = [..] }`) silently dropped the initializer: cg_structlit_fill
(cstage) / cgstructlitfill (wwstage) had no TY_ARRAY field arm, so the
array field fell to the generic scalar tail — cgexpr the N_ARRLIT (→ AX≈0)
then store one sized word — losing every element. cstage returned 0;
wwstage emitted byte-identical wrong code. (The GLOBAL literal-init path
is unaffected: it goes through emit_struct_lit_bytes, already correct via
#129 A.3.)
Both stages now element-wise store the N_ARRLIT at base+field_off+i*esz,
reusing the proven N_LET array-init shape (cgen.c:8467 / cgenstmt.ww:1393)
for int and float elements plus its `...` repeat fill; esz routes through
the type table (rule 13). str/slice/struct/tagged ELEMENT arrays are the
N_LET path's documented multi-word gap (cgen.c:8462) — converted from the
silent drop to a LOUD rule-7 error in both stages, not left silent.
Symmetric both stages (rule 10), byte-identical .s.
The `...` repeat in a struct-literal array field is checker-unreachable
today (the field type-check rejects `[v...]` length inference — a
separate checker gap); the arm mirrors N_LET's repeat for symmetry.
Test 949_structlit_arrfield_run: +local literal-init reads (idx 0 / last
element), cstage run + cs==ww byte-id.
This commit is contained in:
@@ -64,6 +64,33 @@ static const struct row rows[] = {
|
||||
"def G: e = e { pad = [9u8, 9u8, 9u8],"
|
||||
" encmap = [65u8, 66u8, 67u8, 68u8] };\n"
|
||||
"export fn main() i32 = { return G.encmap[2]: i32; };\n", 67 },
|
||||
/* BUG A: local struct-literal init of a [4]u8 field, read idx 0.
|
||||
* Pre-fix the array field fell to the generic scalar tail (cgexpr
|
||||
* the N_ARRLIT → AX, store one word) and silently dropped every
|
||||
* element → 0. encmap[0]='A' (65). */
|
||||
{ "local_lit_read0",
|
||||
"package main;\n"
|
||||
"type e = struct { encmap: [4]u8 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let g: e = e { encmap = [65u8, 66u8, 67u8, 68u8] };\n"
|
||||
" return g.encmap[0]: i32;\n"
|
||||
"};\n", 65 },
|
||||
/* BUG A: same init, read idx 3 — asserts the LAST element landed
|
||||
* (the pre-fix single-word store would never reach it). 'D' (68). */
|
||||
{ "local_lit_read3",
|
||||
"package main;\n"
|
||||
"type e = struct { encmap: [4]u8 };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let g: e = e { encmap = [65u8, 66u8, 67u8, 68u8] };\n"
|
||||
" return g.encmap[3]: i32;\n"
|
||||
"};\n", 68 },
|
||||
/* NB: a trailing `...` repeat in a struct-LITERAL array field
|
||||
* (`encmap = [7u8...]`) is rejected by the CHECKER ("[1]u8 not
|
||||
* assignable to [4]u8") — the field type-check doesn't apply the
|
||||
* repeat-length inference that bare `let a: [N]T = [v...]` gets.
|
||||
* The cg_structlit_fill array arm mirrors the N_LET `...` handling
|
||||
* for symmetry, but that path is checker-unreachable today (separate
|
||||
* checker gap, not #249). No row exercises it. */
|
||||
{ NULL, NULL, 0 }
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user