/* * 640_int_cast_signed — signed narrow `(i64): i8|i16|i32` must * sign-extend the narrowed value, not silently truncate. Each * fixture casts a high-bit-set source to a narrow signed type, * widens back to i64, and returns 42 on the expected match. A * cast that fails to sign-extend leaks the low bits and the * comparison falls through to the `0` arm. * * Exercises both stages via the user-facing `ww run` (cstage) * and `ww_ww run` (wwstage) when present; the loop runs each * driver in turn so a regression on either side is caught * here without spawning extra targets. */ #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[] = { /* i8 ← 0xFFFF_FF80: low byte is 0x80, expect -128 after * narrow + widen-back round trip. */ { "i8_high_bits_set", "fn main() i32 = {\n" " let x: i64 = 0xFFFFFF80i64;\n" " let y: i8 = x: i8;\n" " let z: i64 = y: i64;\n" " if (z == -128i64) { return 42; };\n" " return 0;\n" "};\n", 42 }, /* i8 ← 0x80: bare low byte still encodes -128 once narrowed. */ { "i8_low_byte_negative", "fn main() i32 = {\n" " let x: i64 = 0x80i64;\n" " let y: i8 = x: i8;\n" " let z: i64 = y: i64;\n" " if (z == -128i64) { return 42; };\n" " return 0;\n" "};\n", 42 }, /* i8 ← 0x7F: positive, no sign-extend. */ { "i8_positive_max", "fn main() i32 = {\n" " let x: i64 = 0x7Fi64;\n" " let y: i8 = x: i8;\n" " let z: i64 = y: i64;\n" " if (z == 127i64) { return 42; };\n" " return 0;\n" "};\n", 42 }, /* i16 ← 0xFFFF_8000: low 16 bits encode -32768. */ { "i16_high_bits_set", "fn main() i32 = {\n" " let x: i64 = 0xFFFF8000i64;\n" " let y: i16 = x: i16;\n" " let z: i64 = y: i64;\n" " if (z == -32768i64) { return 42; };\n" " return 0;\n" "};\n", 42 }, /* i16 ← 0x8000: low half-word's sign bit alone. */ { "i16_low_word_negative", "fn main() i32 = {\n" " let x: i64 = 0x8000i64;\n" " let y: i16 = x: i16;\n" " let z: i64 = y: i64;\n" " if (z == -32768i64) { return 42; };\n" " return 0;\n" "};\n", 42 }, /* i32 ← 0xFFFFFFFF_80000000: low 32 bits encode INT32_MIN. */ { "i32_high_bits_set", "fn main() i32 = {\n" " let x: i64 = -2147483648i64;\n" " let y: i32 = x: i32;\n" " let z: i64 = y: i64;\n" " if (z == -2147483648i64) { return 42; };\n" " return 0;\n" "};\n", 42 }, /* i32 ← 0x0000_0000_8000_0000: low dword's sign bit alone. */ { "i32_low_dword_negative", "fn main() i32 = {\n" " let x: i64 = 0x80000000i64;\n" " let y: i32 = x: i32;\n" " let z: i64 = y: i64;\n" " if (z == -2147483648i64) { return 42; };\n" " return 0;\n" "};\n", 42 }, /* Compare against the narrow target's own range, not the * widened slot — i32 - 1 must roll over to INT32_MAX after * MOVSXD sign-extends the wrap-around value. */ { "i32_wrap_negative_to_positive", "fn main() i32 = {\n" " let x: i64 = 0x80000000i64;\n" " let y: i32 = (x - 1i64): i32;\n" " let z: i64 = y: i64;\n" " if (z == 2147483647i64) { return 42; };\n" " return 0;\n" "};\n", 42 }, /* `!T` error-flag wrapper must not block the narrow clamp. * Pins the wwstage N_TBANG peel in cgcast: without it, the * unsigned u8 mask was skipped and 0x1FF leaked through. */ { "u8bang_unsigned_narrow", "type u8x = !u8;\n" "fn main() i32 = {\n" " let y: u8x = 0x1FFu64: u8x;\n" " let z: u64 = y: u64;\n" " if (z == 0xFFu64) { return 42; };\n" " return 0;\n" "};\n", 42 }, /* Signed `!T` companion — sign-extend must fire through the * bang wrapper so `(0xFF80): !i8 → i64` reads back -128. */ { "i8bang_signed_narrow", "type i8x = !i8;\n" "fn main() i32 = {\n" " let y: i8x = 0xFF80i64: i8x;\n" " let z: i64 = y: i64;\n" " if (z == -128i64) { return 42; };\n" " return 0;\n" "};\n", 42 }, }; static int run_driver(const char *driver, const struct row *r, int i) { char src[64], tmpdir[64], cmd[1024]; snprintf(src, sizeof src, "/tmp/wwic_%d_%d.ww", getpid(), i); snprintf(tmpdir, sizeof tmpdir, "/tmp/wwic_%d_d_%d", getpid(), i); FILE *f = fopen(src, "wb"); if (!f) return -1; fputs(r->src, f); fclose(f); mkdir(tmpdir, 0755); snprintf(cmd, sizeof cmd, "cd %s && %s build %s", tmpdir, driver, src); if (runwait(cmd) != 0) { fprintf(stderr, "row[%s]: build via %s failed\n", r->label, driver); unlink(src); rmdir(tmpdir); return -1; } 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 = runwait(outbin); unlink(src); unlink(outbin); rmdir(tmpdir); return got; } 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]; snprintf(cdrv, sizeof cdrv, "%s/ww", bin); char wdrv[1024]; snprintf(wdrv, sizeof wdrv, "%s/ww_ww", bin); struct { const char *name; const char *path; int gated_on_existence; } 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_on_existence && access(drivers[d].path, X_OK) != 0) { fprintf(stderr, "int_cast_signed: skip %s (no %s)\n", drivers[d].name, drivers[d].path); continue; } for (int i = 0; i < n; i++) { int got = run_driver(drivers[d].path, &rows[i], i); total++; if (got != rows[i].want) { fprintf(stderr, "int_cast_signed[%s][%s]: exit=%d want=%d\n", drivers[d].name, rows[i].label, got, rows[i].want); fail++; } } } if (fail) { fprintf(stderr, "int_cast_signed: %d/%d fixtures failed\n", fail, total); return 1; } printf("int_cast_signed: %d/%d ok\n", total, total); return 0; }