Skip to content

nanvm: format bigint string coercion as decimal - #1709

Draft
sergey-shandar wants to merge 10 commits into
mainfrom
codex/fix-open-issue-in-todo-files
Draft

nanvm: format bigint string coercion as decimal#1709
sergey-shandar wants to merge 10 commits into
mainfrom
codex/fix-open-issue-in-todo-files

Conversation

@sergey-shandar

Copy link
Copy Markdown
Contributor

Motivation

  • NaNVM previously used the developer-facing hexadecimal Debug representation for bigints when coercing to strings, which diverges from JavaScript's decimal ToString behavior.
  • Shared test data in fjs/nanvm/module.f.mjs commented out bigint string cases because the VM output did not match the JS expectation.
  • Produce JavaScript-compatible decimal text for String(...) coercion and enable the shared positive/negative bigint test cases.

Description

  • Add a Display implementation for BigInt<A> that formats decimal text by repeatedly dividing limb words by 10^19 groups (nanvm-lib/src/vm/bigint/display.rs).
  • Use the new decimal Display in String coercion by replacing the Debug formatting with to_string() in StringCoercion::bigint (nanvm-lib/src/vm/string_coercion.rs).
  • Re-enable the shared bigint coercion cases in fjs/nanvm/module.f.mjs and regenerate the Rust test corpus (nanvm-lib/tests/test/generated.rs).
  • Add unit checks for decimal to_string() across limb and sign boundaries in nanvm-lib/tests/test/main.rs, and delete the resolved issue file nanvm-lib/todo/bigint-decimal-string-coercion.md.

Testing

  • Ran cargo test and all Rust unit, integration and doctests passed (110 unit tests + 1 integration test + 5 doctests; full suite passed).
  • Ran the FunctionalScript test suite with npx tsc and node ./fjs/module.mjs test, which completed successfully (3,378 JS/FunctionalScript tests passed).
  • Ran cargo clippy and cargo fmt -- --check with no issues.
  • Performed repository update via npm run ci-update / npm run update; generation succeeded, but the full deno install step could not run in this environment (deno: not found) — this does not affect the compiled/tested outcomes shown above.

Changelog:

  • nanvm: bigint string coercion now produces JavaScript-compatible decimal text

Codex Task

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
functionalscript 0d9e478 Commit Preview URL

Branch Preview URL
Aug 29 2026, 08:46 PM

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fb01c968f4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

fn bigint(self, v: BigInt<A>) -> Self::Result {
// TODO: we should use different algorithm for large numbers.
to_result(&format!("{v:?}"))
to_result(&v.to_string())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add the required unreleased changelog entry

This changes the user-visible behavior of bigint string coercion, but the commit adds no changelog/unreleased/<PR>.md, so the fix will be absent from the next published changelog. Add a short entry for the behavior change once the PR number is available.

AGENTS.md reference: AGENTS.md:L89-L93

Useful? React with 👍 / 👎.

Comment thread fjs/nanvm/module.f.mjs
Comment on lines +162 to +163
{ name: 'bigint', args: [123n], expected: '123' },
{ name: 'negativeBigint', args: [-456n], expected: '-456' },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Remove the obsolete known-divergence documentation

With bigint coercion now enabled, fjs/nanvm/README.md lines 84–91 still claims that NaNVM returns hexadecimal text and that these cases carry a rust exclusion; it also links to the todo file deleted by this commit. A repo-wide search finds no replacement target, so readers now see both incorrect behavior documentation and a broken link; remove or update that section in this change.

Useful? React with 👍 / 👎.

@o2alexanderfedin o2alexanderfedin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The formatting is correct: 2,044 cases, 0 mismatches, against two independent oracles — Node BigInt(...).toString() and Python int, both reconstructed from the raw hex limb dump so neither shares code with the implementation. 1,316 fit u128, 728 exceed it (up to 8 limbs). Corpus included non-normalized all-zero limb vectors, the 10^19 group boundary, 2^k ± 1 for k=0..127, 10^n ± 1 for n=0..38, and ~500 random vectors with interior zero limbs, each in both signs. No leading zeros, no lone -, digits only. -0 renders "0", matching JS.

generated.rs is genuine regeneration output, not hand-edited — I ran the generator (no diff), then appended a tamper line and re-ran; it was overwritten and git went clean. All three todo tasks are genuinely done.

Rust gates clean (fmt, test 110+1+5, clippy), npm test 3378/0, tsc clean. The --all-targets clippy failure is pre-existing — confirmed at the merge-base, same single eq_op error.

Two things to fix:

changelog/unreleased/1709.md is missing. The body's Changelog: section is correctly shaped and its bullet is right, but CONTRIBUTING is explicit that the file and the section are duplicated on purpose — "the file is what today's release process reads, the section is what a generator reading Git history would read. Neither is derived from the other, so keep them identical." The bullet can be copied verbatim.

fjs/nanvm/README.md:87 still carries a "Known divergence" section whose link points at the file this PR deletes, and whose every claim is now false — the divergence is gone and neither case carries a rust reason any more. That README's own text says the per-case arrangement exists because "a table of them goes stale the moment someone fixes one"; this is that. No link checker in CI will catch it. It was the only remaining reference.

Not verified: only the generator half of npm run ci-update was run (deno/bun unavailable here, same limitation you reported); the tamper test covers the one file this change regenerates.

@sergey-shandar
sergey-shandar marked this pull request as draft August 26, 2026 06:36

@o2alexanderfedin o2alexanderfedin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delta is merges of main only — git log --oneline --no-merges fb01c968f..da2e90863 --not origin/main is empty, so nothing branch-authored landed. Main moved a lot underneath (#1699, #1708, #1710-#1715, #1718), so I re-ran the gates at the merged head: npm test 3400/0, tsc --noEmit exit 0, cargo fmt -- --check exit 0, cargo test 110 + 1 + 5 doctests all passing, plain cargo clippy clean. The --all-targets eq_op failure at nanvm-lib/src/vm/mod.rs:40 is still pre-existing — identical on origin/main, and this PR doesn't touch that file.

Both of my findings are still open.

changelog/unreleased/1709.md is still missing. The body's section is correctly shaped and last before the Codex trailer, and its bullet is right — this is a behaviour change, so an entry is owed. CONTRIBUTING is explicit that the file and the section duplicate each other on purpose and must be kept identical; creating the file with that exact bullet closes it.

fjs/nanvm/README.md's "Known divergence" section is still there, now at lines 84-91, and it is still the only remaining reference to the file this PR deletes. All three of its claims are false at head: the link target nanvm-lib/todo/bigint-decimal-string-coercion.md no longer exists (dead link — grep across fjs, nanvm-lib, todo and changelog finds only this line); the "0x7Bn" divergence is what the PR fixes; and "the two affected cases carry a rust reason" is false because the PR deletes both — fjs/nanvm/module.f.mjs drops hexadecimalBigint and both rust: fields, and generated.rs:188-193 uncomments both check::<A> calls. The section should go with the file it documents.

@o2alexanderfedin o2alexanderfedin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Measured at 975745950. The delta since my last review is a merge of main only — git log --oneline --no-merges 975745950 --not origin/main is still just fb01c968f — so nothing branch-authored landed and both earlier findings stand.

Gates are green and match main: cargo build 0, cargo test 0 (110 + 1 + 5 doctests, 0 failed), cargo fmt --check 0, cargo clippy 0, npm test 3447/0. The behaviour is right: I ran the new Display on 14 bigints via BigInt::<Naive>::normalize_new and diffed against node's String(BigInt(...)) for the same limb values — 14/14 exact, including 0, -0 → "0", 2^64, 2^128 and 2^128+1 (internal zero limb), -2^128-1, 10^19, 10^19+1 and a 4-limb negative. generated.rs matches a fresh npm run ci-update with an empty git diff, so no hand edits. Deleting nanvm-lib/todo/bigint-decimal-string-coercion.md is right — it described exactly this work.

New finding: the zero-padding of non-leading decimal groups is not pinned. Mutating {group:019}{group} leaves cargo test at exit 0, 110/1/5 all passing — SURVIVED. It is a genuine defect, not a harmless one: under that mutant 10^19 prints "10". The control mutant, deleting f.write_char('-')?, is KILLED (exit 101, 1 failed), so the harness does bite — the gap is that every value in bigint_display_format (2^64, u128::MAX) happens to have full 19-digit non-leading groups. One case whose second group has leading zeros closes it, e.g. normalize_new(Positive, [10_000_000_000_000_000_000])"10000000000000000000".

Still open from before:

  • changelog/unreleased/1709.md is missing. The body's Changelog: section is present and correctly shaped, so only the file is owed.
  • fjs/nanvm/README.md:84-91 "Known divergence" outlives what it documents: its link target is the todo file this PR deletes (dead link), the "0x7Bn" claim is what the PR fixes, and "the two affected cases carry a rust reason" is now false — both rust: fields are gone from module.f.mjs and both check::<A> calls are uncommented in generated.rs. Delete the section.

Not ready to leave draft yet: behaviour is correct, but the padding is untested and the two chores above have not moved.

@o2alexanderfedin o2alexanderfedin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-measured at 61bddaab5. The delta since 975745950 is a merge of main and nothing else — git log --oneline --no-merges 61bddaab5 --not origin/main is still the single commit fb01c968f, and git diff --stat 975745950 61bddaab5 -- fjs/nanvm nanvm-lib changelog/unreleased/1709.md is empty. So all three findings stand unchanged; I re-ran the padding mutant rather than assuming, and {group:019}{group} at nanvm-lib/src/vm/bigint/display.rs:37 still leaves cargo test at exit 0 with 110 + 1 + 5 passing — SURVIVED.

One new data point: dropping .rev() from for group in groups.iter().rev() is KILLED (exit 101, bigint_display_format panics at main.rs:126). Group ordering is pinned; only the padding is not.

Gates remain green on both sides — cargo test 110+1+5, build, fmt --check, clippy all exit 0, npm test 3465/0.

Still not ready to leave draft: the padding case, changelog/unreleased/1709.md, and the fjs/nanvm/README.md:84-91 removal.

@sergey-shandar
sergey-shandar marked this pull request as ready for review August 29, 2026 15:15
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for security reviews. Please try again later.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 29, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-29T15:17:38.252988Z 7e7fe29 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@sergey-shandar
sergey-shandar marked this pull request as draft August 29, 2026 15:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants