wcc_ww/check: skip #258 array->slice desugar at module scope (fix #18)

The #258 borrow desugar lowers `let s: []T = arr` to a runtime
`arr[0:len]` (N_SLICE over the array base) so the slice header is built
at run time. That is only meaningful for a LOCAL let — a fn body
executes the borrow. A module-level let is static data with no runtime
to run the borrow; its rhs must stay the raw N_ARRLIT so cgen can
materialize it as DATA.

cstage splits this by checker: clet (the desugar site, check.c:1993)
runs only from cstmt (local statements); module-level lets are checked
in check_file pass-2 (check.c:2549) which never desugars. wwstage runs
ONE checkletassign for both — function-body lets via resolvewalk's
post-order walk (a block scope is pushed, c.cur != c.top) and top-level
lets via checkfile pass-2 (no scope pushed, c.cur == c.top). Mirror
cstage's split by gating the desugar call on `c.cur != c.top` (the same
module-scope test as check.ww:184). The #130 module-level assignability
check in checkletassign is untouched.

Without this, a module-level `let g: []u8 = [1u8,2u8,3u8]` reached cgen
as N_SLICE in wwstage but N_ARRLIT in cstage — the cs!=ww shape that
blocked #10 part-a's wwstage data-emission. Locals stay byte-identical
(named-array->slice still works, exit 8 both stages); selfhost combined
.ww regenerated.
This commit is contained in:
2026-06-03 21:21:15 +09:00
parent 5bbb81222f
commit ad00114c7f
3 changed files with 48 additions and 6 deletions

View File

@@ -14477,8 +14477,22 @@ fn checkletassign(c: *checker, n: *node) void = {
if (!ok) { if (assignableaddrfn(c, n.lhs, n.rhs)) { ok = true; }; };
if (!conf) { return; };
if (!ok) { errnotassign(c, n.lhs, src, "let"); };
// #258: `let s: []T = arr` borrows the array as a full slice.
n.rhs = desugararrayslice(c, n.lhs, src, n.rhs);
// #258: `let s: []T = arr` borrows the array as a full slice. The
// desugar lowers to a runtime `arr[0:len]` N_SLICE, so it only
// applies to LOCAL lets (a fn body executes the borrow). A MODULE-
// level let is static data with no runtime to run the borrow — its
// rhs must stay the raw N_ARRLIT so cgen can materialize it as DATA
// (#18). cstage splits this by checker: clet (the desugar site,
// cmd/wcc/check.c:1993) runs only from cstmt (local), while module-
// level lets are checked in check_file pass-2 (check.c:2549) which
// never desugars. wwstage runs ONE checkletassign for both (pass-2
// @checkfile + resolvewalk post-order), so mirror cstage's split
// here: skip at module scope (c.cur == c.top, the same module-scope
// test as L184). Keeps the #130 module-level assignability check
// above intact.
if (c.cur != c.top) {
n.rhs = desugararrayslice(c, n.lhs, src, n.rhs);
};
};
fn checkretassign(c: *checker, n: *node) void = {

View File

@@ -4110,8 +4110,22 @@ fn checkletassign(c: *checker, n: *node) void = {
if (!ok) { if (assignableaddrfn(c, n.lhs, n.rhs)) { ok = true; }; };
if (!conf) { return; };
if (!ok) { errnotassign(c, n.lhs, src, "let"); };
// #258: `let s: []T = arr` borrows the array as a full slice.
n.rhs = desugararrayslice(c, n.lhs, src, n.rhs);
// #258: `let s: []T = arr` borrows the array as a full slice. The
// desugar lowers to a runtime `arr[0:len]` N_SLICE, so it only
// applies to LOCAL lets (a fn body executes the borrow). A MODULE-
// level let is static data with no runtime to run the borrow — its
// rhs must stay the raw N_ARRLIT so cgen can materialize it as DATA
// (#18). cstage splits this by checker: clet (the desugar site,
// cmd/wcc/check.c:1993) runs only from cstmt (local), while module-
// level lets are checked in check_file pass-2 (check.c:2549) which
// never desugars. wwstage runs ONE checkletassign for both (pass-2
// @checkfile + resolvewalk post-order), so mirror cstage's split
// here: skip at module scope (c.cur == c.top, the same module-scope
// test as L184). Keeps the #130 module-level assignability check
// above intact.
if (c.cur != c.top) {
n.rhs = desugararrayslice(c, n.lhs, src, n.rhs);
};
};
fn checkretassign(c: *checker, n: *node) void = {

View File

@@ -14477,8 +14477,22 @@ fn checkletassign(c: *checker, n: *node) void = {
if (!ok) { if (assignableaddrfn(c, n.lhs, n.rhs)) { ok = true; }; };
if (!conf) { return; };
if (!ok) { errnotassign(c, n.lhs, src, "let"); };
// #258: `let s: []T = arr` borrows the array as a full slice.
n.rhs = desugararrayslice(c, n.lhs, src, n.rhs);
// #258: `let s: []T = arr` borrows the array as a full slice. The
// desugar lowers to a runtime `arr[0:len]` N_SLICE, so it only
// applies to LOCAL lets (a fn body executes the borrow). A MODULE-
// level let is static data with no runtime to run the borrow — its
// rhs must stay the raw N_ARRLIT so cgen can materialize it as DATA
// (#18). cstage splits this by checker: clet (the desugar site,
// cmd/wcc/check.c:1993) runs only from cstmt (local), while module-
// level lets are checked in check_file pass-2 (check.c:2549) which
// never desugars. wwstage runs ONE checkletassign for both (pass-2
// @checkfile + resolvewalk post-order), so mirror cstage's split
// here: skip at module scope (c.cur == c.top, the same module-scope
// test as L184). Keeps the #130 module-level assignability check
// above intact.
if (c.cur != c.top) {
n.rhs = desugararrayslice(c, n.lhs, src, n.rhs);
};
};
fn checkretassign(c: *checker, n: *node) void = {