diff --git a/quest/m3/README.md b/quest/m3/README.md index 920f1aa23..e74634a08 100644 --- a/quest/m3/README.md +++ b/quest/m3/README.md @@ -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 diff --git a/quest/m3/ts-unsafe/README.md b/quest/m3/ts-unsafe/README.md index ab78eb56c..d64a28b51 100644 --- a/quest/m3/ts-unsafe/README.md +++ b/quest/m3/ts-unsafe/README.md @@ -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 @@ -37,8 +37,10 @@ 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::(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. @@ -46,14 +48,16 @@ none of its own `unsafe`. A union over a type parameter lays out each instance s 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 @@ -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 diff --git a/quest/m3/ts-unsafe/vec-from-raw-parts.md b/quest/m3/ts-unsafe/vec-from-raw-parts.md deleted file mode 100644 index 7e77b7ed5..000000000 --- a/quest/m3/ts-unsafe/vec-from-raw-parts.md +++ /dev/null @@ -1,18 +0,0 @@ -# [S] Vec::from_raw_parts of a typed allocation - -## Goal - -[rust2-ts](/ts/README.md) translates `Vec::from_raw_parts` of memory `alloc` gave for a -`Layout::array::(n)` (or an equal layout), so the `Vec` adopts that JS array of `T`s -with its length and capacity and drops its elements and frees it as std does, proven by -differential tests against native Rust that check length, capacity, and drops. Today it -is an unsupported call. - -## Plan - -Typed allocation is a fresh JS array of `T`s since the 2026-11-01 decision in -[the epic](/quest/m3/ts-unsafe/README.md), and `rs.Vec` holds std's capacity since -[#555](https://github.com/moq-dev/rust2/pull/555). smallvec spills its inline buffer this -way: 2 of its 10 model refusals at the moq pin, which moq drops or binds. - -Public API: the translator's output.