w6c+wwstage: narrow int/rune array-literal elements to the declared type (#251)

`let a:[4]u8=[65,66,67,68]`, `def D:[4]u8=['A',..]`, and `enc{m=[65,..]}`
rejected with "init [4]i32 not assignable to declared [4]u8": an array
literal's element type came from the elements via type_default (int-lit
-> i32, rune-lit -> rune) with no declared-element-type propagation. The
scalar path already narrows (`let c:u8='A'`); only array aggregation at
the let/def/struct-field sites #130 (test 920) left unwired did not.

Fix = the int/rune analogue of coerce_floatlit, realised as the EXISTING
#130 accept-if-fits range-check — NOT a node-type restamp. cgen drives
the array element WIDTH from the declared type at every site (cgen.c
local-let lu->sub, emit_array_data d->type), so a restamp would be dead
code (the array literal keeps its [N]i32/[N]rune node type; the cs==ww
byte-id gate confirms the bytes emit u8-wide regardless). Per element:
foldable int/rune literal -> defcastfits range-check vs declared T
(in-range accept, out-of-range REJECT loud, rule-7); non-foldable ->
type_assignable / isassignable.

cstage (check.c): wire arrlit_init_fits into clet (local let),
struct-field-init, and def-init — the three sites the #130 module-let
path already covered.

wwstage (check.ww): factor checkletassign's inline #130 block into
checkarrlitfits and call it from the let path, the def path, and a
TARGETED array-field walk in the N_STRUCTLIT arm. This also closes a
pre-existing rule-7 wwstage over-accept: the def path ran NO init
assignability check and the N_STRUCTLIT head-stamp parks field
assignability (#23), so out-of-range / str array elements silently
over-accepted (a truncating miscompile) at those two sites. The
struct-field walk is the array-field accept-if-fits ONLY — it reuses the
stable N_TSTRUCT field-list walk (astoffset precedent), isolated from
the broader parked #23 field-assignability walk.

Regenerated w6c + wwdump combined.ww (embed check.ww). New test 951
covers let/def/struct-field x int/rune accept (run + cs==ww byte-id) and
out-of-range/str reject (both stages). test-unit 237 + smoke green.
This commit is contained in:
2026-06-02 01:53:57 +09:00
parent 0fb4bae337
commit d56b7ca946
6 changed files with 601 additions and 157 deletions

View File

@@ -1542,6 +1542,7 @@ cexpr(Checker *c, Node *n)
f->str, type_name(c->a, t));
else if (vt != ty_err &&
!type_assignable(match->type, vt) &&
!arrlit_init_fits(c, match->type, f->lhs) &&
!assignable_addrfn(c, match->type, f->lhs))
err(c, f->pos, "field %s: %s not assignable to %s",
f->str, type_name(c->a, vt),
@@ -1897,6 +1898,7 @@ clet(Checker *c, Node *n)
}
if (declared && initt && initt != ty_err && !has_arr_repeat &&
!type_assignable(declared, initt) &&
!arrlit_init_fits(c, declared, n->rhs) &&
!assignable_addrfn(c, declared, n->rhs))
err(c, n->pos, "init %s not assignable to declared %s",
type_name(c->a, initt), type_name(c->a, declared));
@@ -2422,7 +2424,8 @@ check_file(Checker *c, Node *file)
&& def_cast_fits(d->type, dv))
art = ty_untyped_int;
if (d->type && rt != ty_err && d->type != ty_err
&& !type_assignable(d->type, art))
&& !type_assignable(d->type, art)
&& !arrlit_init_fits(c, d->type, d->rhs))
err(c, d->pos, "def %s init %s not assignable to %s",
d->str, type_name(c->a, rt),
type_name(c->a, d->type));