Skip to content

feat(ts): translate atomics - #167

Merged
kixelated merged 11 commits into
mainfrom
quest/m2/ts-shared
Oct 28, 2026
Merged

kixelated merged 11 commits into
mainfrom
quest/m2/ts-shared

Conversation

@kixelated

@kixelated kixelated commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Part of quest/m2/ts-shared.md: this lands atomics. The quest file is rewritten to the work that remains.

Problem

rust2-ts refused every atomic. On the moq census at the pinned revision, that came to 586 refusals in moq-net (359 for Atomic and 227 for atomic::Ordering) and 136 in kio. Most of kio's are counters and flags in log.rs, consumer.rs, and producer.rs.

Approach

  • Atomic<T> of a bool or an integer becomes rt.Atomic<T>, a class that implements rt.Place<T>. Because of that, get_mut returns the atomic itself. AtomicPtr is still refused, since raw pointers are.
  • atomic::Ordering becomes its discriminant (rt.AtomicOrdering, a number). Constants, aggregates, and matches on it use the same paths as cmp::Ordering.
  • Every operation passes its orderings to the runtime. Where an operation can't take an ordering, the runtime panics with std's own message: a release load, an acquire store, a release or acq-rel failure ordering, a relaxed fence.
  • Read-modify-writes go through rt.atomicFetch(atomic, f, value):
    • fetch_add and fetch_sub use the integer type's wrappingAdd and wrappingSub, because atomics wrap.
    • fetch_max and fetch_min use max and min.
    • fetch_and, fetch_or, fetch_xor, fetch_nand, and fetch_not use a lambda built by num::binary.
  • compare_exchange and compare_exchange_weak compare with ===, or with the type's eq for 64- and 128-bit integers stored as limbs. The weak form never fails spuriously on one thread.
  • try_update, fetch_update, and update run std's loop of compare-exchanges in the runtime. The loop retries only when the callback itself changed the atomic. They can't lower from std's MIR because that body writes in place.
  • Default gives an atomic of the inner type's default value. fence and compiler_fence only check their ordering.

Testing: a new atomics case in the lang fixture runs against native wasm32. It covers every width from u8 to u64, usize, isize, and bool:

  • each operation listed above, with orderings sampled so that each panic message actually fires;
  • a &mut write through get_mut;
  • Arc-shared counters in a struct field with #[derive(Default)];
  • an ordering passed in as a parameter.

Impact

  • New translator output: rt.Atomic<T>, rt.AtomicOrdering, and the rt.atomic* and rt.fence runtime functions in rt/task.ts. Nothing breaks, so there is no MIGRATE.md entry.
  • Census at the pinned moq revision: moq-net goes from 9052 to 8466 refusals, and kio from 327 to 191.
  • The lang snapshots also rename one isSomeAnd instance. The instance is shared with the new module, and that module now comes first in source order.

Choices made without asking

  • rt.AtomicOrdering is number, not 0 | 1 | 2 | 3 | 4. tsc widens an inlined constant array of orderings, such as [Relaxed, Acquire], to number[].
  • Orderings are checked at run time even when they are constants. Checking constants at translation time would duplicate std's rules in Rust and in TS. The cost is two integer compares per call, which I have not measured.

Alternatives

  • Lowering try_update and update from core's MIR: refused, because a std body may not write in place.
  • Dropping the ordering arguments: refused, because it would lose std's panics, and with them its exact error paths.

Follow-ups

  • static atomics (static NEXT: AtomicU64: 2 in moq-net, 1 in kio) are still refused as a constant of type &Atomic. Translating them needs a shared module-level value. This is recorded in the quest.
  • [Atomic<u64>; 8]::default() in kio is refused. Array Default is a general gap, not specific to atomics.
  • Not run: the root just test. Only ts/ changed. just ts test passed, and the root just check passed.

🤖 Generated with Claude Code

(written by Claude Opus 5.5)

kixelated and others added 4 commits October 28, 2026 01:45
Quest-Claim: 8cff16a14e130721
`Atomic<T>` of a `bool` or an integer becomes `rt.Atomic<T>`, the `rt.Place` of its
value, so `get_mut` is the atomic itself. `atomic::Ordering` is its discriminant, and
each operation checks the orderings it can't take with std's panics. Read-modify-writes
wrap as std's do, `try_update`, `update`, and `fetch_update` load and store once, and
`fence` and `compiler_fence` order nothing on one thread.

The quest keeps what remains: `Rc`, `weak_count`, the other locks and cells, and
`static` atomics.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	ts/fixtures/lang/expected/harness.ts
#	ts/fixtures/lang/src/harness.rs
#	ts/src/lower/consts.rs
#	ts/src/lower/library.rs
#	ts/src/lower/mir/place.rs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title Shared ownership in the TypeScript translator feat(ts): translate atomics Oct 28, 2026
@kixelated
kixelated marked this pull request as ready for review October 28, 2026 09:07
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 28, 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-10-28T12:43:36.921058Z eb6adcb New commits
🔒 Security Review ✅ Completed 2026-10-28T12:44:58.059968Z eb6adcb New commits
ℹ️ 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.

@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: 0370c8d484

ℹ️ 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".

Comment thread ts/runtime/rt/task.ts Resolved

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Automated review by review (OpenAI)

Reviewed commit: 0370c8d48422b4f223b94cf015e54a5455532b29

The small Atomic<T> place and reuse of width-aware arithmetic are a sensible direction; retaining ordering checks preserves observable error paths without introducing unnecessary threading machinery.

One actionable correctness issue remains, already independently reported in the callback-mutation finding, so no duplicate inline comment: ts/runtime/rt/task.ts:200-215 unconditionally stores the callback result in atomicTryUpdate/atomicUpdate. A callback can mutate the same atomic through a shared reference even on one thread, requiring compare-exchange retries and value equality for wide integers. The differential fixture's callbacks do not exercise that re-entrant case. Restore that loop and include callback-side mutation in the fixture.

No additional actionable bugs found in the changed lowering, ordering representation, integer operations, and fixtures. Verification was static GitHub source/diff review; I did not run tests or independently verify the reported test results.

(Written by OpenAI)

kixelated and others added 2 commits October 28, 2026 02:18
`try_update`, `fetch_update`, and `update` ran their callback once and stored its
result, overwriting a store the callback made to the same atomic. They now run std's
loop of compare-exchanges, which retries with the value the callback left.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@kixelated kixelated left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Automated review by review (OpenAI)

Reviewed commit: 11febd4d6e0e696358cf5cc29bbf2fe0b793e68b

Compared with my previous review at 0370c8d48422b4f223b94cf015e54a5455532b29, separating the main-branch merge from the atomic changes. The previous callback-mutation finding is fixed: both update helpers now retry compare-exchange with the observed value, and lowering passes width-aware equality for wide integers. The differential fixtures now make callback-side stores and record invocation counts.

No new actionable bugs found in this update. Reusing atomicCompareExchange keeps the fix small and preserves its ordering checks; the extra loop is necessary even on one thread because callbacks can re-enter.

Verification: static GitHub source/diff and generated-snapshot review only. I did not run tests or independently verify the reported differential-test results.

(Written by OpenAI)

@kixelated

Copy link
Copy Markdown
Collaborator Author

Merged main, which brought in #159 and #166. I resolved the conflicts by hand:

  • lang fixture: feat(ts): translate str::from_utf8 and Utf8Error #159's utf8::decodes had also taken case 57. It keeps 57, atomics moves to the fallthrough arm (58), and the modulus is now 59. I regenerated expected/harness.ts to match.
  • ts/src/lower/task.rs: I kept both sides. The imports are the union of the two, both new function blocks are kept, and the module doc lists atomics and std's futures.

just ts test and just ts check pass on 7adc8f2.

(written by Claude Opus 5.5)

# Conflicts:
#	ts/README.md
#	ts/fixtures/lang/expected/harness.ts
#	ts/fixtures/lang/src/harness.rs
#	ts/fixtures/lang/src/lib.rs
@kixelated

Copy link
Copy Markdown
Collaborator Author

Merged main again, which brought in #165. I resolved the conflicts by hand:

  • lang fixture: feat(ts): translate f64 and Duration's f64 methods #165's floats had taken case 58. It keeps 58, atomics moves to the fallthrough arm (59), and the modulus is now 60. caller.ts's within, the ULP tolerance for floats, hard-coded % 59. Left at 59, it fed atomics outputs into the float comparison and threw. It now uses % 60.
  • ts/README.md: the runtime file list now names both atomics and rt/float.ts.

just ts test and just ts check pass on 18180aa.

(written by Claude Opus 5.5)

# Conflicts:
#	ts/fixtures/lang/caller.ts
#	ts/fixtures/lang/expected/harness.ts
#	ts/fixtures/lang/src/harness.rs
@kixelated

Copy link
Copy Markdown
Collaborator Author

Merged main again, which brought in #164, the named fixture cases. I resolved the conflicts by hand:

just ts test and just ts check pass on 9dbacd2.

(written by Claude Opus 5.5)

@kixelated

Copy link
Copy Markdown
Collaborator Author

Landing summary: this PR translates atomics: rt.Atomic<T>, orderings as their discriminant, and std's panics for orderings an operation can't take. The callback-mutation finding from review is fixed: try_update and update now run std's loop of compare-exchanges. Four merges from main, each resolved by hand (#159, #166, #165, #164), keep the named lang case list, with atomics::atomics appended. Codex reviewed 9dbacd2 with no findings, and CI passed. The quest file is rewritten to the remaining work: static atomics and the rest of the quest.

(written by Claude Opus 5.5)

@kixelated

Copy link
Copy Markdown
Collaborator Author

Merged main again, which brought in #170. There was one hand-resolved conflict, in the ts/src/lower/task.rs imports: this PR's num::{self, Int, Operand} and #170's Stmt are now both imported. No snapshots changed. just ts test and just ts check pass on eb6adcb.

(written by Claude Opus 5.5)

@kixelated
kixelated merged commit b624403 into main Oct 28, 2026
1 check passed
@kixelated
kixelated deleted the quest/m2/ts-shared branch October 28, 2026 12:57
kixelated added a commit that referenced this pull request Oct 28, 2026
Fold #167's atomics into `place_call`'s table, with VecDeque's methods
as one more row at index 0 rather than an early return.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

1 participant