/* * 751_vararg_seq_percall — sentinel for task #8. Post-#15 wwstage's * cgcall never bumped c.varargseq, so every variadic callsite in a fn * read seq=0 and minted the same `@vararg_d_0` / `@vararg_sl_0` slot. * Two variadic callsites with different arities in one fn hit #15's * `localadd: @-prefix slot grew within fn` fail-loud — the second * gather asked for a different element-buffer size than the pinned * slot. Cstage was unaffected: its `mklabel("vararg_d/sl")` bumps * labelseq naturally per call. * * Fix: cgcall reads `c.varargseq` and bumps it at each gather emit; * mirrors cstage's mklabel freshness. * * row | what it pins * -----------------------------+--------------------------------- * mixed_arity_two_calls | original wedge — pick(1 arg) + * | pick(3 args) in main. Pre-fix * | wwstage fatals at the second * | gather; cstage rc=0. Post-fix * | both rc=0. * same_arity_two_calls | non-regression — pre and post * | both stages rc=0; same arity * | reuses the same slot size so * | the size-grow check never fired. * three_arity_drift | 3 callsites with arities 1/2/3 * | in one fn. Post-fix both stages * | rc=0 and three distinct seq slots * | get minted (`@vararg_d_0/1/2`). */ #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; }; static const struct row rows[] = { /* 1. Two-call arity drift. Pre-fix wwstage fatals on the second * gather (1 rune vs 3 runes -> different element-buffer size). */ { "mixed_arity_two_calls", "package main;\n" "import os;\n" "fn pick(args: (str | rune)...) i32 = { return args.len; };\n" "export fn main() i32 = {\n" " if (pick(\"a\") != 1) { os.exit(11); };\n" " if (pick(\"a\", \"b\", \"c\") != 3) { os.exit(12); };\n" " return 0;\n" "};\n", 0 }, /* 2. Non-regression: same-arity calls. */ { "same_arity_two_calls", "package main;\n" "import os;\n" "fn pick(args: (str | rune)...) i32 = { return args.len; };\n" "export fn main() i32 = {\n" " if (pick(\"a\") != 1) { os.exit(11); };\n" " if (pick(\"b\") != 1) { os.exit(12); };\n" " return 0;\n" "};\n", 0 }, /* 3. Three callsites with arities 1/2/3 in one fn. Pre-fix * wwstage fatals at the second gather. Post-fix three distinct * @vararg_d_0/1/2 slots are minted. */ { "three_arity_drift", "package main;\n" "import os;\n" "fn pick(args: (str | rune)...) i32 = { return args.len; };\n" "export fn main() i32 = {\n" " if (pick(\"a\") != 1) { os.exit(11); };\n" " if (pick(\"a\", \"b\") != 2) { os.exit(12); };\n" " if (pick(\"a\", \"b\", \"c\") != 3) { os.exit(13); };\n" " return 0;\n" "};\n", 0 }, }; static int build_with(const char *driver, const char *src_path, const char *tmpdir) { char cmd[1024]; snprintf(cmd, sizeof cmd, "cd %s && %s build %s 2>/dev/null", tmpdir, driver, src_path); return runwait(cmd); } static int exec_bin(const char *bin) { return runwait(bin); } int main(void) { const char *bin = getenv("BIN"); if (!bin) bin = "out/bin"; char absbin[512]; if (bin[0] != '/') { char cwd[256]; if (getcwd(cwd, sizeof cwd) == NULL) return 1; snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin); bin = absbin; } char cdrv[640], wdrv[640]; snprintf(cdrv, sizeof cdrv, "%s/ww", bin); snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); int have_ww = (access(wdrv, X_OK) == 0); int n = (int)(sizeof rows / sizeof rows[0]); int total = 0, fail = 0; for (int i = 0; i < n; i++) { const struct row *r = &rows[i]; char src[64], tmpdir[64]; snprintf(src, sizeof src, "/tmp/vararg_seq_%d_%d.ww", getpid(), i); snprintf(tmpdir, sizeof tmpdir, "/tmp/vararg_seq_%d_d_%d", getpid(), i); FILE *f = fopen(src, "wb"); if (!f) { fail++; total++; continue; } fputs(r->src, f); fclose(f); mkdir(tmpdir, 0755); 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'; total++; if (build_with(cdrv, src, tmpdir) != 0) { fprintf(stderr, "vararg_seq_percall[cs][%s]: build failed\n", r->label); fail++; } else { int got = exec_bin(outbin); if (got != r->want) { fprintf(stderr, "vararg_seq_percall[cs][%s]: rc=%d want=%d\n", r->label, got, r->want); fail++; } } unlink(outbin); if (have_ww) { total++; if (build_with(wdrv, src, tmpdir) != 0) { fprintf(stderr, "vararg_seq_percall[ws][%s]: build failed\n", r->label); fail++; } else { int got = exec_bin(outbin); if (got != r->want) { fprintf(stderr, "vararg_seq_percall[ws][%s]: rc=%d want=%d\n", r->label, got, r->want); fail++; } } unlink(outbin); } unlink(src); rmdir(tmpdir); } if (fail) { fprintf(stderr, "vararg_seq_percall: %d/%d rows failed\n", fail, total); return 1; } printf("vararg_seq_percall: %d/%d ok\n", total, total); return 0; }