Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions quest/m3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,26 @@ library's unsafe internals may be replaced by a runtime mapping over its public
consumer reaches, such as `MaybeUninit::zeroed`, `Rc::new_uninit`, and
`ptr::write_bytes`).

Decided with the maintainer on 2026-11-07, the unsafe policy, to stop one-off fixes for
ever rarer `unsafe`:

1. ✅ Safe Rust is the contract, and where translation effort goes.
2. ✅ `unsafe` inside std and common crates is wrapped at their safe API, as std,
`hashbrown`, `bytes`, and [tokio's channels](/quest/m3/ts-wasm-bindgen/tokio-sync.md)
are, rather than translated.
3. ✅ Other `unsafe` translates only where it fits the translator's existing pointer forms
(a place, an array element, a byte in a buffer). A construct needing a new pointer form
or pointer metadata is refused with its span, unless a consumer needs it and no library
wrapper covers it.
4. ✅ An `unsafe`-heavy crate a consumer needs is compiled to WASM behind its public API
rather than fixed construct by construct. Not planned until a consumer needs it.

Rejected: an unsafe std gap with no consumer becoming its own quest, as
`MaybeUninit::zeroed`, `Rc::new_uninit`, and `ptr::write_bytes` would have
([#647](https://github.com/moq-dev/rust2/pull/647)), or as `Vec::from_raw_parts`'s adopted
storage did ([#635](https://github.com/moq-dev/rust2/pull/635)); and a second, byte-backed
memory model for values whose address is reinterpreted, without a consumer.

## Required

- [Retrial Cranelift on moq's tests](/quest/m3/cranelift-unwind.md) - waits on upstream: moq's tests pass under Cranelift, so its CI can weigh it again
Expand Down
21 changes: 12 additions & 9 deletions quest/m3/ts-unsafe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

## Goal

[rust2-ts](/ts/README.md) translates every `unsafe` construct it can give exact semantics
in JS, each proven by differential tests against native Rust, so a stock crate's `unsafe`
never needs a hand binding or a rewrite in its consumer.
[rust2-ts](/ts/README.md) translates the `unsafe` constructs that fit its pointer forms
with exact semantics in JS, each proven by differential tests against native Rust, and
refuses the rest with a span, as [m3's unsafe policy](/quest/m3/README.md) decides.

## Plan

Expand Down Expand Up @@ -37,23 +37,27 @@ none of its own `unsafe`. A union over a type parameter lays out each instance s
- Decided with the maintainer on 2026-11-01, typed allocation: memory from `alloc` for a
`Layout::array::<T>(n)`, or an equal layout of `T`'s size and alignment, cast to `*mut T`
is a fresh JS array of `T`s, which `realloc` copies and `dealloc` frees. Untyped and
byte allocation stay refused. smallvec spills to it, and
[`Vec::from_raw_parts` of it](/quest/m3/ts-unsafe/vec-from-raw-parts.md) is left.
byte allocation stay refused. smallvec spills to it. `Vec::from_raw_parts` of it stays
refused, deleted on 2026-11-07 under m3's unsafe policy: a `Vec` adopting that array
needs storage tracked apart from the pointer
([#635](https://github.com/moq-dev/rust2/pull/635)).
- Decided with the maintainer on 2026-11-02: a raw slice pointer is `rs.RawSlice`, its
first element's pointer and its length, read through only where Rust reads, and an empty
slice from a pointer into no array keeps that pointer through every part of it.

Decided with the maintainer on 2026-11-06, splitting this quest into one child per
construct still open at `b3df54b6c`, all in m3:

- ✅ Goal: every `unsafe` construct rust2-ts can make exact, one quest each.
Rejected: only the constructs moq's dependencies hit.
- Goal: every `unsafe` construct rust2-ts can make exact, one quest each. Rejected: only
the constructs moq's dependencies hit. Narrowed on 2026-11-07 by m3's unsafe policy to
the constructs that fit the existing pointer forms.
- ✅ All children in m3. Rejected: spare capacity in m2; smallvec's constructs in m2.
No child blocks moq: the model's only hits are smallvec's (7 of its 10 refusals), and
moq drops or binds smallvec.
- `Vec::from_raw_parts` of a typed allocation is its own child, restored on review of
[#618](https://github.com/moq-dev/rust2/pull/618): the spare-capacity child fills an
existing `Vec`, not one adopting an allocation.
existing `Vec`, not one adopting an allocation. Deleted on 2026-11-07 under m3's unsafe
policy, above.
- The 2026-11-01 clauses that let a pointer from elsewhere, a `Vec`'s spare capacity, and
a byte pointer past its view fail loudly at run time are superseded by
[Throw only where Rust panics](/quest/m3/ts-runtime-throws.md) (2026-11-04, #597): where
Expand All @@ -73,7 +77,6 @@ Public API: the translator's output.
## Required

- [A raw pointer behind a reference](/quest/m3/ts-unsafe/raw-behind-ref.md) - `&raw const *r` of a value JS copies points where the reference came from
- [Vec::from_raw_parts of a typed allocation](/quest/m3/ts-unsafe/vec-from-raw-parts.md) - a `Vec` adopts an `alloc`ed array of `T`s, with its length, capacity, and drops
- [NonZero's unchecked arithmetic](/quest/m3/ts-unsafe/nonzero-unchecked-ops.md) - `NonZero::unchecked_add` and `unchecked_mul` translate, aborting at std's site
- [UnsafeCell's raw accessors](/quest/m3/ts-unsafe/unsafecell.md) - `UnsafeCell::raw_get`, `from_mut`, and `replace` translate
- [A byte local whose address is taken, in a buffer of its own](/quest/m3/ts-unsafe/lone-byte-buffers.md) - raw and mutable slices of a `u8` local, and casts of them to byte arrays, translate instead of being refused
18 changes: 0 additions & 18 deletions quest/m3/ts-unsafe/vec-from-raw-parts.md

This file was deleted.