Skip to content

feat(ts): translate a guard's downgrade, leak, and whole byte-array map - #402

Merged
moq-bot[bot] merged 6 commits into
mainfrom
quest/m3/ts-guard-leftovers
Nov 1, 2026
Merged

moq-bot[bot] merged 6 commits into
mainfrom
quest/m3/ts-guard-leftovers

Conversation

@kixelated

@kixelated kixelated commented Nov 1, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

rust2-ts refused three parts of std's guard API that #392 found:

  • RwLockWriteGuard::downgrade (stable since 1.92).
  • Ref::leak and RefMut::leak, plus RefCell::undo_leak, which ends a leaked borrow.
  • A guard's map over the whole of a [u8; N], such as RefMut::map(cell.borrow_mut(), |q| q), refused with "a reference to an array of bytes it is generic over". This affected every guard's map, filter_map, and try_map (Ref, RefMut, MutexGuard, and the RwLock guards, mapped or not), not only RwLockWriteGuard and RefMut.

Approach

  • downgrade: rt.rwLockDowngrade turns the lock's one exclusive borrow into one shared borrow and returns the same guard. A try_write in between gets WouldBlock and a try_read succeeds, as on wasm32. Only an unmapped write guard has downgrade, so the guard is always the lock itself.
  • leak: Ref::leak lends the guard's value, and RefMut::leak lends it the way get_mut does (an rt.Place where JS copies the value). Neither releases the borrow. RefCell::undo_leak (rt.refCellUndoLeak) clears the count and then lends like get_mut. discards lists leak and downgrade, since each takes over its guard without dropping it.
  • Whole byte-array maps: lend::plan refused a closure that takes a &[u8; N] std holds and also returns one. A guard's map already views each byte-array part its closure returns (task::guard_map), so plan now accepts these closures for guard maps. The closure is given an rt.ByteSlice of the guarded array, and the guard keeps a Uint8Array view of the same bytes, so writes land in place. The guard-map key list moved into library::maps_guard, which discards shares.
  • Tests: two lang-fixture cases run differentially against native wasm32:
    • locks::downgraded: a lock's state before and after the downgrade, while the downgraded read drops before or after another read, and after mapping a downgraded guard. Also whole-array maps through RwLockWriteGuard, MappedRwLockWriteGuard, RwLockReadGuard, and MutexGuard.
    • borrows::leaks: the borrow state after each leak, shared and exclusive, whole and mapped, and after undo_leak. It covers writes through a leaked &mut, both to a number JS copies and to a value it doesn't, and whole writes. It also covers map, filter_map, try_map, and leak over a whole [u8; N], and the panic from borrowing again while a leak holds the cell, with wasm32's message.
    • Mutation check: when rt.rwLockDowngrade leaves zero or two readers, or rt.refCellUndoLeak keeps the count, these cases report differences.

Impact

  • Translated output: rt.rwLockDowngrade and rt.refCellUndoLeak are new runtime exports. These calls now translate where they were refused before. No config or CLI change. Nothing breaks, so no MIGRATE.md entry.
  • Consumer: the m3 goal of translating all of std. No translated moq code calls these: moq-net's and kio's downgrade calls are Arc/Weak ones.
  • Model census (ts/moq/model.toml): unchanged. just census on this branch and on origin/main gives identical reports: model 222 refusals over 3631 function instances, moq-net 941, kio 110.

Alternatives

  • Have guard_map lend the guarded byte array as an rt.ByteSlice itself, without going through lend::plan. I rejected this because plan already wraps the closure that way for map_split, so a second path would duplicate it.
  • Leave RefCell::undo_leak out. I included it because it ends what leak starts (same cell_leak feature), and without it the tests could only show a leaked borrow by panicking.

Follow-ups

  • None found within scope.

Review

🤖 Generated with Claude Code

kixelated and others added 5 commits November 1, 2026 12:16
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tovers

# Conflicts:
#	ts/fixtures/lang/expected/harness.ts
#	ts/fixtures/lang/src/harness.rs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title A guard's downgrade, leak, and whole byte-array map feat(ts): translate a guard's downgrade, leak, and whole byte-array map Nov 1, 2026
@kixelated
kixelated marked this pull request as ready for review November 1, 2026 22:08
@coderabbitai

coderabbitai Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: aded84ca-0b47-486d-99aa-f61d77643b2b
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@kixelated
kixelated enabled auto-merge (squash) November 1, 2026 22:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant