wwstage: accept module-level const/let slice-from-arrlit (#28)

wwstage rejected a module-level `const/let []T = [arrlit]` global with "let: not assignable"; cstage accepts (textbook Hare, ref/hare/path/stack.ha:30). The arrlit->slice admission in checkletassign was gated local-only; lift it to module scope too, aligning wwstage UP to cstage's arrlit_init_fits (check.c:3406-3409, slice arm 519-520). cstage unchanged.

Two guards the un-gating requires: the n.rhs.lhs=arr stash stays local-only (a module decl keeps its raw N_ARRLIT for DATA emit, so stashing would leave an untyped count node for the pass-3 asserttyped walker); and tuple-element slice globals are excluded at module scope, because the synthesis delegates element checks to isassignable which lacks a strict tuple arm (#38) -- a [](str,*fn) table would over-accept a sig-mismatched &fn that cstage's strict type_assignable rejects (#124) -- so they stay on the existing typeeqast path.

Closes two divergences 944_alias_emit_b7 pinned: Group A (cstage-runs/ww-rejects) migrates to test/lang/slice_global_arg_test.ww (promoted from _runonly, now cs==ww byte-id); Group B converges to a shared emit_slice_data reject with the identical diagnostic.
This commit is contained in:
2026-06-26 23:16:19 +09:00
parent f476797a47
commit e60297085d
4 changed files with 172 additions and 92 deletions

View File

@@ -5975,16 +5975,39 @@ fn checkletassign(c: *checker, n: *syntax.node) void = {
&& n.rhs.kind == syntax.nkind.N_TUPLE) {
checktuplearrfits(c, llhs, n.rhs);
};
// #25/#31: an array literal initialising a SLICE local. Re-stamp the
// #25/#31: an array literal initialising a SLICE binding. Re-stamp the
// literal as [count]T (the slice element) so the #258 borrow's exact-
// element typeeq holds and the cgen N_SLICE-over-N_ARRLIT arm reads the
// declared element width. Run the same per-element coercion + range-
// check the array path runs (checkarrlitfits against a synthesized
// [count]T), then drive isassignable + the borrow off [count]T. Twin of
// cstage arrlit_init_fits' slice arm. Local-only (c.cur != c.top): the
// borrow runs at runtime; module-level slice-from-arrlit stays #32.
if (c.cur != c.top && n.lhs.kind == syntax.nkind.N_TSLICE
&& n.rhs.kind == syntax.nkind.N_ARRLIT) {
// [count]T), then drive isassignable off [count]T. Twin of cstage
// arrlit_init_fits' slice arm (cmd/wcc/check.c:519-520). #28/#32: this
// admission runs at BOTH scopes. The matching DATA-vs-borrow split is
// the desugar at the foot of this fn (gated c.cur != c.top): a LOCAL
// `let []u8 = [...]` lowers to a runtime arr[0:len] borrow, while a
// MODULE-level `const/let []u8 = [...]` keeps its raw N_ARRLIT for cgen
// to materialize as DATA (#18). cstage admits both — module-level via
// arrlit_init_fits in check_file pass-2 (check.c:3406-3409), which never
// desugars — so gating the admission local-only made wwstage REJECT
// valid Hare (`const dotdot: []u8 = ['.', '.'];` ref/hare/path/stack.ha:30).
// #28: a TUPLE-element slice global ([](str,*fn) tables) is EXCLUDED at
// module scope — the synthesis delegates per-element validation to
// checkarrlitfits, whose element check is isassignable, which has NO
// strict tuple arm (#38). cstage's arrlit_init_fits uses type_assignable
// (strict on tuples), so routing a tuple-element slice through the
// synthesis would over-accept a sig-mismatched `&fn` element that cstage
// rejects (#124 wrong_sig_table). Tuple-element slices instead stay on
// the natural whole-element typeeqast path below (isassignable's #258
// array→slice arm), which IS strict and reaches cstage's same decision.
let elemtup: bool = false;
if (c.cur == c.top && n.lhs.lhs != nil) {
let etn: *syntax.node = resolvealias(c, unwrapbang(n.lhs.lhs));
if (etn != nil && etn.kind == syntax.nkind.N_TTUPLE) {
elemtup = true;
};
};
if (n.lhs.kind == syntax.nkind.N_TSLICE
&& n.rhs.kind == syntax.nkind.N_ARRLIT && !elemtup) {
let cnt: u64 = 0u64;
let e0: *syntax.node = n.rhs.list;
for (e0 != nil) {
@@ -6007,7 +6030,13 @@ fn checkletassign(c: *checker, n: *syntax.node) void = {
// can size the backing NODE-wise via elemsizeofc(base.lhs). wwstage
// narrow-primitive tinfos are unsized (i32/u8 .size==0, #8), so the
// element width must come from the type NODE, not the tinfo.
n.rhs.lhs = arr;
// #28: LOCAL ONLY. The local desugar replaces n.rhs with the N_SLICE
// borrow, so this stash is consumed and then unreachable. A MODULE-
// level decl keeps its raw N_ARRLIT (DATA emit), and its emitslicedata
// sizes off the DECLARED slice tnode (d.lhs), never the stash — so the
// stash would only leave the synthesized, untyped count node (cn) on
// the live tree for the pass-3 asserttyped walker to trip on.
if (c.cur != c.top) { n.rhs.lhs = arr; };
src = arr;
};
// #29: an un-suffixed rune literal narrowing into an integer let target