Files
ww/test/wcc/947_inferred_scalar_global_run.c
Hojun-Cho 2138e28f65 wcc/cgen: #134 wwstage inferred unary-int scalar global — peel +/-/~ over int-literal, default to int (align up)
defaultinferredlets gained a single unary peel: an inferred module-global let s = -42 (untyped_int annotation over N_UN(+/-/~) of N_INTLIT) was wwstage SILENT — no DATAW, no MOVQ, MOVSXD on stale AX (exit 168 vs cstage 214). Peel one unary level to the int literal and default the annotation to the 8B machine word int, so the inferred decl is structurally the typed control and the existing typed-path emit/read fires (DATAW + MOVQ, byte-identical to cstage). ww-only align-up; cstage already correct on neg.

Float leg (N_FLOATLIT) carved to #135: cstage integer-types inferred float globals (MOVQ not MOVSD), so a ww-only f64 default would be cs != ww (rule-10) — needs the both-stage cstage-use-site fix. Nested unary - -42 is #136 (single-level peel). #133 (const-expr 7*6) and unary-over-nonident stay loud.

Pin: 947 neg rows (inferred + typed control), cs == ww .s byte-id.
2026-06-07 22:24:03 +09:00

204 lines
6.5 KiB
C

/*
* 947_inferred_scalar_global_run — runtime + byte-id net for #66(b-i): an
* INFERRED int-literal module-global `let s = 42;` (no type annotation) was
* wwstage SILENT-WRONG. The checker stamps such a global with an
* N_TNAME("untyped_int") annotation; letemitsize / emitletdataw / the cgident
* global-read arm key on that annotation's name, which letscalarprim doesn't
* recognise — so the global was dropped from collectlets (no DATAW emitted)
* AND the read fell to the silent module-leaf (no `MOVQ main.s(SB)`), leaving
* `MOVSXD` to sign-extend a STALE AX. cstage instead type_default's the untyped
* int to the 8B machine word before emit, so it produced the correct
* `DATAW main.s` + `MOVQ main.s(SB), AX` (exit 42). cs≠ww, ww silent-wrong.
*
* Fix (wwstage only, cgen.ww defaultinferredlets, one choke-point before
* collectlets): rewrite the untyped_int annotation to the concrete machine
* word `int` — NOT i32 (that's the #108 truncation trap, opposite polarity) —
* so all three consumers resolve it as an 8B int global. The emitted DATAW +
* MOVQ then match cstage byte-for-byte (align ww UP). Guarded to N_INTLIT rhs
* ONLY: a const-EXPR inferred global (`let s = 7*6`) is #66(b-ii), a SEPARATE
* loud both-stage no-DATA gap, and must stay on its loud route.
*
* Each row carries (a) a cstage `ww build` + run asserting the exit code (R1
* pre-fix: wwstage exit 136 / asm differs), and (b) a w6c vs w6c_ww `.s` cmp
* (rule-10 byte-id). R2 is the typed-annotation control (already cs==ww/42),
* locking no regression on the annotated path.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.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;
}
struct row { const char *label; const char *src; int want_exit; };
static const struct row rows[] = {
/* R1 — the #66(b-i) repro: inferred int-literal module-global, read
* back from main. Pre-fix wwstage emitted neither the DATAW nor the
* MOVQ → MOVSXD on stale AX → exit 136. Post-fix cs==ww, exit 42. */
{ "inferred",
"package main;\n"
"let s = 42;\n"
"export fn main() i32 = {\n"
" return s: i32;\n"
"};\n", 42 },
/* R2 — typed-annotation control: `let s: i64 = 42;` already worked
* (cs==ww, DATA emitted). Regression guard for the annotated path. */
{ "typed_ctrl",
"package main;\n"
"let s: i64 = 42;\n"
"export fn main() i32 = {\n"
" return s: i32;\n"
"};\n", 42 },
/* R3 — #134 neg leg: inferred unary-over-int-literal module-global
* `let s = -42;` (rhs N_UN(TK_MINUS) over N_INTLIT). Pre-fix wwstage
* emitted neither DATAW nor MOVQ (un-defaulted untyped_int annotation)
* → exit 168 (garbage). cstage folds the unary, emits DATAW + load
* (exit 214 = -42 low-8). Post-fix defaultinferredlets peels the unary
* and defaults to `int`, so cs==ww and both run -42 (exit 214). */
{ "neg_inferred",
"package main;\n"
"let s = -42;\n"
"export fn main() i32 = {\n"
" return s: i32;\n"
"};\n", 214 },
/* R4 — #134 neg typed control: `let s: int = -42;` (TYPED). Already
* cs==ww today (concrete annotation → letscalarprim fires). Locks the
* construction proof: R3's emitted .s is byte-identical to this typed
* control (the inferred decl IS the typed decl after defaulting). */
{ "neg_typed_ctrl",
"package main;\n"
"let s: int = -42;\n"
"export fn main() i32 = {\n"
" return s: i32;\n"
"};\n", 214 },
{ NULL, NULL, 0 }
};
static int
slurp_eq(const char *a, const char *b)
{
FILE *fa = fopen(a, "rb");
FILE *fb = fopen(b, "rb");
if (!fa || !fb) { if (fa) fclose(fa); if (fb) fclose(fb); return -1; }
int rc = 0;
for (;;) {
int ca = fgetc(fa);
int cb = fgetc(fb);
if (ca != cb) { rc = -1; break; }
if (ca == EOF) break;
}
fclose(fa); fclose(fb);
return rc;
}
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 w6c[1100], w6c_ww[1100];
snprintf(w6c, sizeof w6c, "%s/w6c", bin);
snprintf(w6c_ww, sizeof w6c_ww, "%s/w6c_ww", bin);
if (access(w6c_ww, X_OK) != 0) {
fprintf(stderr, "infglobal: w6c_ww missing — cannot run the "
"cs==ww byte-id gate (the whole point of this test)\n");
return 1;
}
int n = 0, fail = 0;
for (int i = 0; rows[i].src; i++, n++) {
char src[64];
snprintf(src, sizeof src, "/tmp/wwisg_%d_%d.ww", getpid(), i);
FILE *f = fopen(src, "wb");
if (f == NULL) { fail++; continue; }
fputs(rows[i].src, f);
fclose(f);
/* (a) cstage build + run. */
char tmpdir[64];
snprintf(tmpdir, sizeof tmpdir, "/tmp/wwisg_%d_d_%d",
getpid(), i);
mkdir(tmpdir, 0755);
char cmd[2048];
snprintf(cmd, sizeof cmd, "cd %s && %s/ww build %s",
tmpdir, bin, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: cstage build failed\n",
rows[i].label);
fail++;
unlink(src); rmdir(tmpdir);
continue;
}
char outbin[128];
const char *base = strrchr(src, '/');
base = base ? base + 1 : src;
snprintf(outbin, sizeof outbin, "%s/%s", tmpdir, base);
char *dot = strrchr(outbin, '.');
if (dot && strcmp(dot, ".ww") == 0) *dot = '\0';
int got = runwait(outbin);
if (got != rows[i].want_exit) {
fprintf(stderr, "row[%s]: cstage exit %d, want %d\n",
rows[i].label, got, rows[i].want_exit);
fail++;
}
unlink(outbin); rmdir(tmpdir);
/* (b) cs==ww byte-id gate. */
char cs_s[64], ws_s[64];
snprintf(cs_s, sizeof cs_s, "/tmp/wwisg_%d_%d_cs.s",
getpid(), i);
snprintf(ws_s, sizeof ws_s, "/tmp/wwisg_%d_%d_ww.s",
getpid(), i);
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
w6c, cs_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c failed\n", rows[i].label);
fail++; unlink(src); continue;
}
snprintf(cmd, sizeof cmd, "%s -o %s %s 2>/dev/null",
w6c_ww, ws_s, src);
if (runwait(cmd) != 0) {
fprintf(stderr, "row[%s]: w6c_ww failed\n",
rows[i].label);
fail++; unlink(src); unlink(cs_s); continue;
}
if (slurp_eq(cs_s, ws_s) != 0) {
fprintf(stderr,
"row[%s]: cstage/wwstage .s DIFFER (rule-10 "
"byte-id violation)\n", rows[i].label);
fail++;
}
unlink(src); unlink(cs_s); unlink(ws_s);
}
if (fail) {
fprintf(stderr, "%d/%d inferred-scalar-global tests failed\n",
fail, n);
return 1;
}
printf("infglobal: %d/%d ok (cstage run + cs==ww byte-id)\n", n, n);
return 0;
}