Files
ww/test/wcc/989_letshadow_run.c
Hojun-Cho feae910a9b wcc: #152 let-initializer scope — defer the binding's localfind link past its own init (both stages)
A let's own name was visible during its OWN initializer: cgen prepended the
new local into the name-keyed localfind chain BEFORE emitting the init, so
`let x = f(x)` read the fresh UNINIT slot, not the outer/param x. Both-wrong-
identical silent miscompile (gate-blind byte-id). Surfaced by path
dirname/basename (was the c3-posix path->p rename).

Align to Hare (harec check.c:1439 evals the init, then scope_insert). Fix,
both stages, IDENTICAL asm: reserve the frame slot BEFORE the init emits,
link the binding's name into the localfind chain only AFTER.
- cstage cgen.c: split localoff -> localslot(reserve)+link; N_LET's 12
  case-level breaks -> goto letlink (tail links once); the inner-for break
  is preserved; the 4 fatal() arms untouched.
- wwstage cgen.ww/cgenstmt.ww: new localreserve (= localalloc minus the
  chain-link); cglet -> cgletbody(c,n,off) + a cglet wrapper that
  reserves -> calls body -> links after.

Byte-id-safe on existing code: localfind is by-name, so deferring the link
is a no-op on every non-self-shadow let (grep = 0 self-shadow sites) — 990-997
stay green. Because both stages emit identical now-correct asm, byte-id
CANNOT catch this; the pin is a RUNTIME test, teeth-proven (revert -> pin
fails). test/wcc/989_letshadow{.ww,_run.c}: param-shadow, let-in-init shadow,
rename control, arrlit self-ref.

Embedded regen: selfhost/cmd/{w6c,wwdump}/main.combined.ww. Gate: all 325
passed, byte-id 990-997 green, w6c c587f4a1 / w6c_ww 7a69f898 (deterministic).
2026-06-08 12:17:18 +09:00

51 lines
1.3 KiB
C

/*
* 989_letshadow_run — #152 regression pin. Compile + run the letshadow
* fixture under the C-side `ww run` driver (cstage w6c) and assert exit 0.
*
* The bug (link-the-let-before-its-init) miscompiled IDENTICALLY on both
* stages, so byte-id 990-997 is blind to it; only a runtime value check
* catches the regression. Same thin-wrapper shape as 989_path_run.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
static int
runwait(const char *cmd)
{
int rc = system(cmd);
if (rc == -1) return -1;
if (WIFEXITED(rc)) return WEXITSTATUS(rc);
return 1;
}
int
main(void)
{
const char *bin = getenv("BIN");
if (!bin) bin = "out/bin";
char absbin[1024];
if (bin[0] != '/') {
char cwd[1024];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin);
bin = absbin;
}
char cwd[1024];
if (getcwd(cwd, sizeof cwd) == NULL) return 1;
const char *src = "test/wcc/989_letshadow.ww";
char path[1024], cmd[2048];
snprintf(path, sizeof path, "%s/%s", cwd, src);
snprintf(cmd, sizeof cmd, "%s/ww run %s", bin, path);
int rc = runwait(cmd);
if (rc != 0) {
fprintf(stderr, "letshadow_run FAIL: %s exited %d "
"(row %d miscompiled — #152)\n", src, rc, rc - 10);
return 1;
}
printf("letshadow_run: %s ok\n", src);
return 0;
}