/* * 794_xmod_ident_prefer — project #55 close. Pins that the wwstage * checker resolves a BARE value-ident with same-MODULE preference * (selfhost/cmd/wcc/check.ww exprtype N_IDENT), mirroring cstage's * scope_lookup_prefer (cmd/wcc/check.c:66). * * THE BUG (wwstage-CHECKER-only, #55): exprtype's N_IDENT branch looked * a bare value-ident up via the flat-scope `scopelookup`, which bucket- * walks and returns whichever same-leaf symbol heads the bucket — the * LAST-registered one (scopedefineinmodule prepends). In a combined * unit the root `main` package registers last, so a bare `v` referenced * inside an IMPORTED module `aa` (curmod=aa) resolved to `main.v`, not * `aa.v` — dragging the wrong decl's type. cstage routes the same site * through scope_lookup_prefer(cur, cur_mod, name) and binds aa.v, so it * built fine; wwstage mis-typed the use and REJECTED. This is the * exprtype-N_IDENT member of the #55 bare-leaf cluster (sibling #56 * fixed the N_CALL-callee path, #53 the bare-TNAME path). * * THE FIX (#55): scopelookup(c.cur, e.str) -> scopelookupprefer(c.cur, * c.curmod, e.str) at the single exprtype N_IDENT site. * * THE FIXTURE: `aa` exports a global `v: i32` and `fn getv() i32 = * { return v; }`. The root `main` declares a same-leaf `fn v() i64` and * calls aa.getv(). Registration order puts main.v (the i64 fn) at the * head of the flat-scope bucket, so a non-preferring lookup binds it. * - cstage builds (prefer -> aa.v: i32, `return v` is i32) and runs: * main returns aa.getv() == 7. * - PRE-fix wwstage resolved `v` -> main.v (i64) and rejected * `return: not assignable (i64 -> i32)` on the combined unit. * - POST-fix wwstage prefers curmod=aa -> aa.v (i32) and ACCEPTS. * * DISCRIMINATOR (787 #13 model): the cstage driver emits the combined * unit (its checker is already correct), then we re-check that combined * with w6c_ww. PRE-fix it errored out (non-zero); POST-fix it succeeds. * w6c_ww REJECTING the combined is the #55 red. * * NOTE — no cs==ww byte-id assertion here, deliberately: a minimal * cross-module same-leaf VALUE-ident reference also trips a SEPARATE, * still-open cgen-side bare-leaf bug (cgenexpr.ww cgident likewise uses * the non-preferring scopelookup, so the value-load resolves the wrong * symbol/width), which would diverge the asm independently of this * CHECKER fix. The real #55 fmt manifestation flows through checker- * stamped tagged-union variant indices (not a value-load), so it does * not hit the cgen path; this minimal probe does, hence the reject-> * accept polarity is the sound discriminator for the checker fix. The * cgen-side sibling is tracked separately (the #55 cluster residual). * * GATE POLARITY: must stay GREEN. Red means either the cstage routing * regressed (build/run) or wwstage rejected a valid same-module-preferred * ident again (w6c_ww rejects the combined). */ #include #include #include #include #include static int runwait(const char *cmd) { int rc = system(cmd); if (rc == -1) return -1; if (WIFEXITED(rc)) return WEXITSTATUS(rc); return -1; } struct file { const char *name; const char *src; }; static const struct file files[] = { { "aa.ww", "package aa;\n" "export let v: i32 = 7;\n" "export fn getv() i32 = {\n" " return v;\n" "};\n" }, { "main.ww", "package main;\n" "import aa;\n" "fn v() i64 = { return 100; };\n" "export fn main() i32 = {\n" " return aa.getv();\n" "};\n" }, { NULL, NULL } }; int main(void) { const char *bin = getenv("BIN"); if (!bin) bin = "out/bin"; char absbin[2048]; 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 cdrv[2100], wdrv[2100]; snprintf(cdrv, sizeof cdrv, "%s/ww", bin); snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); if (access(wdrv, X_OK) != 0) { fprintf(stderr, "794: ww_ww missing — cannot run the wwstage " "accept gate (the whole point of this test)\n"); return 1; } char dir[] = "/tmp/ww794_XXXXXX"; if (mkdtemp(dir) == NULL) { fprintf(stderr, "794: mkdtemp failed\n"); return 1; } char path[1024], cmd[4096]; int rc = 0; for (int i = 0; files[i].name; i++) { snprintf(path, sizeof path, "%s/%s", dir, files[i].name); FILE *f = fopen(path, "wb"); if (!f) { fprintf(stderr, "794: write %s\n", files[i].name); rc = 1; goto done; } fputs(files[i].src, f); fclose(f); } /* #94 sep layout: cstage's --sep build (prefer is correct) compiles * the units and links the binary; pin WW_PKGCACHE under the scratch * dir so out/.pkgcache is untouched. NO cs==ww byte-id here — the #55 * checker fix has an open cgen-side bare-leaf sibling that diverges the * asm independently; the sound discriminator is the wwstage ACCEPT. */ snprintf(cmd, sizeof cmd, "cd %s && WW_PKGCACHE=%s/pkgc_c %s build --sep -I %s -o %s/main %s/main.ww", dir, dir, cdrv, dir, dir, dir); if (runwait(cmd) != 0) { fprintf(stderr, "794: cstage build failed\n"); rc = 1; goto done; } /* cstage runtime pins the routing: main returns aa.getv() == 7. */ snprintf(path, sizeof path, "%s/main", dir); int got = runwait(path); if (got != 7) { fprintf(stderr, "794: cstage exit %d, want 7\n", got); rc = 1; goto done; } /* The #55 discriminator: the wwstage driver's --sep build runs the * wwstage checker over the same units and must ACCEPT. PRE-fix it * rejected (bare `v` -> main.v: i64 -> return mismatch); POST-fix it * prefers curmod=aa -> aa.v: i32 and accepts. */ snprintf(cmd, sizeof cmd, "cd %s && WW_PKGCACHE=%s/pkgc_w %s build --sep -I %s -o %s/mainww %s/main.ww " ">/dev/null 2>&1", dir, dir, wdrv, dir, dir, dir); if (runwait(cmd) != 0) { fprintf(stderr, "794: ww_ww REJECTED the units (#55: bare " "value-ident resolved the wrong module's same-leaf symbol)\n"); rc = 1; goto done; } done: snprintf(cmd, sizeof cmd, "rm -rf %s", dir); (void)runwait(cmd); if (rc) { fprintf(stderr, "794 xmod_ident_prefer: FAILED\n"); return 1; } printf("xmod_ident_prefer: ok (cstage run==7 + w6c_ww accepts combined)\n"); return 0; }