Files
scriptc/packages/compiler/test/integer-bitwise-arithmetic.test.ts
Malte Ubl 8576f48924 Keep integer-proof tests meaningful under whole-program facts
The remainder and bitwise-arithmetic fixtures relied on parameters
staying arbitrary, but each was only called with integers, so the new
call-site proofs removed the floating fallbacks they count. A fractional
call per function restores the arbitrary inputs and keeps every original
expectation. The self-hosting range cases now expect the int32-closed
range where a region's writes are all int32 literals (for-of still binds
the local to unproven elements and keeps no fact).
2026-10-09 11:59:28 -05:00

49 lines
1.9 KiB
TypeScript

import { mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { expect, test } from "vitest";
import { compile, deserializeModule } from "../src/index.js";
import { emitLlvmModule } from "../src/backend/llvm/emitter.js";
test("bounded integer arithmetic emits integer operations and leaves uncertain numbers alone", async () => {
const dir = await mkdtemp(join(tmpdir(), "scriptc-integer-bits-"));
try {
const entry = join(dir, "main.ts");
const outPath = join(dir, "main.ir.json");
await writeFile(
entry,
`
function safe(input: number): number {
let h = input >>> 0;
h = h ^ (h << 13);
h = h + (h << 3);
return h >>> 0;
}
function unknown(input: number): number { return (input + 1) >>> 0; }
function negativeZero(): number { const x = -0; return x + x; }
console.log(safe(17), unknown(1.5), 1 / negativeZero());
// A fractional argument keeps safe's input arbitrary (no call-site proof).
console.log(safe(0.5));
`,
);
const result = await compile(entry, { outDir: dir, outPath, outputKind: "ir" });
if (!result.ok)
throw new Error(result.diagnostics.map((d) => `${d.code}: ${d.message}`).join("\n"));
const mod = deserializeModule(await readFile(outPath, "utf8"));
const ll = emitLlvmModule(mod);
const llBody = (name: string): string =>
ll.match(
new RegExp(`define internal [^\\n]+ @sc_f_${name}\\([^\\n]*\\) #0 \\{([\\s\\S]*?)\\n\\}`),
)![1]!;
expect(llBody("safe")).toContain(" = add nsw i64 ");
// Only the arbitrary input's slow ToUint32 path still adds doubles.
expect(llBody("safe").match(/ = fadd double /g)).toHaveLength(1);
expect(llBody("unknown")).toContain("uint32.coerce.slow");
expect(llBody("unknown")).toContain(" = fadd double ");
expect(llBody("negativeZero")).toContain(" = fadd double ");
} finally {
await rm(dir, { recursive: true, force: true });
}
});