Skip to content

fix(compiler): keep maybeNaN on the failed edge of ordered comparisons - #141

Open
codeAnqiang-ma wants to merge 1 commit into
vercel-labs:mainfrom
codeAnqiang-ma:fix/int-infer-false-edge-nan
Open

fix(compiler): keep maybeNaN on the failed edge of ordered comparisons#141
codeAnqiang-ma wants to merge 1 commit into
vercel-labs:mainfrom
codeAnqiang-ma:fix/int-infer-false-edge-nan

Conversation

@codeAnqiang-ma

Copy link
Copy Markdown
Contributor

Fixes #139

What

refine() derived its NaN-exclusion from the negated operator instead of the relation that actually held on the edge:

const op = branch ? cond.op : NEGATE[cond.op]!;   // NEGATE["<"] === ">="
const clearNaN = op !== "!==";

¬(a < b) does not imply a >= b — both are false when a is NaN. So on the false edge of <, <=, >, >= the analyzer refined as if the reversed comparison had held and dropped maybeNaN, which lets the guard-clause spelling

const q = a / b;
if (q < 0) return 0;
if (q > 100) return 100;
return Math.trunc(q);   // NaN falls through both guards

"prove" a runtime NaN as an integer in [0, 100]. The build then succeeds, the sidecar attests the class, and the NaN crosses the C ABI through a bare (int64_t) cast / fptosi — UB in C, poison in LLVM IR, and a platform-dependent value in practice (0 on arm64, INT64_MIN on x86-64). The function's own doc comment already states the intended rule ("on the failed edge NaN survives while the negated comparison still refines the numeric members"); this change makes the code match it.

Fix

One line: derive clearNaN from the relation that actually held on this edge, not its negated spelling —

const clearNaN = branch !== (cond.op === "!==");

The interval refinement on the failed edge is untouched (it is sound for the non-NaN members); only the NaN bit is preserved. !== keeps its mirror-image behavior: its failed edge means === held, which does exclude NaN.

Regression tests

  • packages/compiler/src/library/int-infer.test.ts: four new cases in "the domain's edges beyond the corpus" — the guard-clause shape, the else spelling, a u64 slot behind failed-edge guards (all REFUSE on wholeness with the NaN teaching; they reported prove with fabricated bounds [0, 100] before the fix), and a control pinning that failed edges still refine numeric members to an exact PROVE once a === a excludes NaN.
  • tests/harness/library-int.test.ts: one new end-to-end corpus case (nan-survives-failed-guard-edge) driving the real pipeline on both emissions to the SC4022 refusal.

Test evidence

With the fix:

$ npx vitest run packages/compiler/src/library/int-infer.test.ts
      Tests  43 passed (43)          # 39 existing + 4 new

$ SCRIPTC_TEST_WORKERS=4 npx vitest run tests/harness/library-int.test.ts
      Tests  84 passed (84)          # includes the new corpus case, both emissions

With the int-infer.ts fix stashed and the new tests kept (red check):

 ❯ packages/compiler/src/library/int-infer.test.ts (43 tests | 3 failed)
   × the failed edge of an ordered comparison keeps NaN alive (guard clauses)
     → expected 'prove' to be 'refuse'
   × the else spelling of the failed edge keeps NaN alive too
     → expected 'prove' to be 'refuse'
   × a u64 slot behind failed-edge guards refuses instead of fabricating [0, 100]
     → expected 'prove' to be 'refuse'

Not run: the full sandbox gate (pnpm test:sandbox — no Vercel Sandbox credentials on this machine).


This fix was prepared with AI assistance; the author reproduced the bug locally (unit-level and end-to-end through the C ABI) and reviewed every change.

refine() derived clearNaN from the negated operator, treating the
failed edge of a < b as a >= b having held — the two differ exactly
when a side is NaN, so guard-clause spellings let NaN be "proven"
whole and cross a declared i64/u64 slot as an unchecked (int64_t) /
fptosi conversion. Judge NaN exclusion by the relation that actually
held on the edge; the numeric interval refinement is unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
@vercel

vercel Bot commented Aug 12, 2026

Copy link
Copy Markdown

@codeAnqiang-ma is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Library integer-boundary inference clears maybeNaN on the false edge of an ordered comparison, letting NaN cross a "proven" i64/u64 slot

1 participant