/* * 989_globstrslice_run — F8-c2 (report-item #47): a module-GLOBAL str * sliced with a DEFAULT high bound and passed as a call arg (`take(g[1:])`) * must load its length word for the hi — both stages, byte-identical. * * THE BUG (cat-A silent miscompile, align-UP): in cgenutil.ww the N_SLICE * call-arg pusharg builds {cap,len,ptr} on the stack. For a DEFAULT hi * (`g[lo:]`) it picks the base length per base kind. The global-base branch * handled N_TARRAY and N_TSLICE but had NO arm for a global str (N_TNAME * "str"), so AX kept the base pointer (loaded for the base push) and got * PUSHQ'd as the hi → len = ptr - lo, garbage. The LOCAL-str arm and the * alias-NAMED (bu60) arm already loaded the +8 length word; only the plain * global-str arm was missing. cstage pushargs reads the base type via * type_chase_named uniformly (cmd/w6c/cgen.c N_SLICE), so it loaded the len * and ran correct — the cat-A divergence (990-997 never slice a global str * arg with a default hi, so they stayed green). * * THE FIX: the global default-hi branch grows an N_TNAME "str" arm emitting * the same `LEAQ g(SB),CX; MOVQ 8(CX),AX` as the global N_TSLICE arm (str IS * []u8 — the len word sits at +8). align ww UP; the .s is byte-identical. * * Rows (build+run on cstage `ww` and wwstage `ww_ww`; rule-10 — agree+hit): * row | shape | want * ----------------+-------------------------------+----- * gstr_defhi | slen(g[1:]) g="hello" | 4 [#47 bug — len ptr-lo] * gstr_defhi_full | slen(g[0:]) lo=0 default hi | 5 [#47 bug] * gstr_defhi_ptr | sfirst(g[1:]) s[0]=='e' | 101 [ptr word still ok] * gstr_explicit | slen(g[1:3]) hi explicit | 2 (control: hi!=nil path * | | must not regress) */ #include #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 row { const char *label; const char *src; int want_exit; }; #define PRELUDE \ "package main;\n" \ "let g: str = \"hello\";\n" \ "fn slen(s: str) int = { return len(s): int; };\n" \ "fn sfirst(s: str) int = { return s[0]: int; };\n" static const struct row rows[] = { { "gstr_defhi", PRELUDE "export fn main() int = { return slen(g[1:]); };\n", 4 }, { "gstr_defhi_full", PRELUDE "export fn main() int = { return slen(g[0:]); };\n", 5 }, { "gstr_defhi_ptr", PRELUDE "export fn main() int = { return sfirst(g[1:]); };\n", 101 }, { "gstr_explicit", PRELUDE "export fn main() int = { return slen(g[1:3]); };\n", 2 }, }; /* run_build — build+run `src` via `driver`; returns the binary's exit * code, or -1 on a build failure. */ static int run_build(const char *driver, const struct row *r, int i) { char src[64], tmpdir[64], cmd[1024]; snprintf(src, sizeof src, "/tmp/gss_%d_%d.ww", getpid(), i); snprintf(tmpdir, sizeof tmpdir, "/tmp/gss_%d_d_%d", getpid(), i); FILE *f = fopen(src, "wb"); if (!f) return -2; fputs(r->src, f); fclose(f); mkdir(tmpdir, 0755); snprintf(cmd, sizeof cmd, "cd %s && %s build %s 2>/dev/null", tmpdir, driver, src); int brc = runwait(cmd); const char *base = strrchr(src, '/'); base = base ? base + 1 : src; char outbin[128]; snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base); char *dot = strrchr(outbin, '.'); if (dot && strcmp(dot, ".ww") == 0) *dot = '\0'; int got = -1; if (brc == 0) got = runwait(outbin); unlink(src); unlink(outbin); rmdir(tmpdir); return brc == 0 ? got : -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 cdrv[1024], wdrv[1024]; snprintf(cdrv, sizeof cdrv, "%s/ww", bin); snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); struct { const char *name; const char *drv; int gated; } drivers[] = { { "cstage", cdrv, 0 }, { "wwstage", wdrv, 1 }, { NULL, NULL, 0 }, }; int n = (int)(sizeof rows / sizeof rows[0]); int total = 0, fail = 0; for (int d = 0; drivers[d].name; d++) { if (drivers[d].gated && access(drivers[d].drv, X_OK) != 0) { fprintf(stderr, "globstrslice_run: skip %s (no %s)\n", drivers[d].name, drivers[d].drv); continue; } for (int i = 0; i < n; i++) { total++; int got = run_build(drivers[d].drv, &rows[i], i); if (got != rows[i].want_exit) { fprintf(stderr, "globstrslice_run[%s][%s]: exit=%d " "want=%d\n", drivers[d].name, rows[i].label, got, rows[i].want_exit); fail++; } } } if (fail) { fprintf(stderr, "globstrslice_run: %d/%d fixtures failed\n", fail, total); return 1; } printf("globstrslice_run: %d/%d ok\n", total, total); return 0; }