From 8e287dddc817cc3ee1c02b13dc57b13211430c23 Mon Sep 17 00:00:00 2001 From: Luke Curley <432854+kixelated@users.noreply.github.com> Date: Fri, 6 Nov 2026 18:00:33 -0800 Subject: [PATCH] quest: delete addresses as identity; addresses stay refused Co-Authored-By: Claude Opus 5.5 --- quest/m2/ts-model/README.md | 35 +++++++++++++++++++++++----------- quest/m2/ts-model/addresses.md | 24 ----------------------- quest/m3/ts-runtime-throws.md | 5 +++-- quest/m3/ts-unsafe/README.md | 9 +++++---- 4 files changed, 32 insertions(+), 41 deletions(-) delete mode 100644 quest/m2/ts-model/addresses.md diff --git a/quest/m2/ts-model/README.md b/quest/m2/ts-model/README.md index e290815eb..36f89fe7d 100644 --- a/quest/m2/ts-model/README.md +++ b/quest/m2/ts-model/README.md @@ -31,15 +31,12 @@ Decided with the maintainer on 2026-10-27, from a census of the model's 22 publi - Each group of refusals is its own quest. moq must work, but nothing in the translator is specific to moq: dependencies follow general rules, and moq's own - changes (safe `cfg` twins of its `unsafe`, dropping or binding `hashlink`, `slab`, and + changes (to its `unsafe` and its address keys, dropping or binding `hashlink`, `slab`, and `smallvec`) are one research-tier quest in moq's tree. -- Until `unsafe` and trait objects translate, internal `unsafe` in moq-net and kio gets - safe `cfg` twins in moq, so differential tests cover it, and a dependency containing - `unsafe` is bound by the consumer. These are stopgaps: decided with the maintainer on - 2026-10-27, a stock crate translates with no annotations or config, so - [unsafe code the translator can model](/quest/m3/ts-unsafe/README.md) and - [trait objects](/quest/m3/ts-dyn.md) remove them. Only true platform boundaries (clocks and - DEFLATE) stay bound. +- A dependency containing `unsafe` is bound by the consumer until + [unsafe code the translator can model](/quest/m3/ts-unsafe/README.md) translates it: decided + with the maintainer on 2026-10-27, a stock crate translates with no annotations or config. + Only true platform boundaries (clocks and DEFLATE) stay bound. - Each child measures this target by the census of `ts/moq/model.toml`, rooted at those types with the boundaries bound, which nightly reports beside the whole crate. - The end-to-end work stays here: the model's roots, its differential harness, and the @@ -52,17 +49,33 @@ model, so each child below unblocks all of them together. The rest is moq's, for research quest: kio's `log.rs` reads bytes as its segments and retires them through crossbeam-epoch, whose tagged pointers and inline closures (36 refusals) translation refuses for good, and smallvec's union over a type parameter and raw parts (10) wait on -[unsafe](/quest/m3/ts-unsafe/README.md), so both need safe `cfg(rust2_ts)` twins or bindings -there, as does the `unsafe` slice of `model/frame.rs`'s `Filled`. +[unsafe](/quest/m3/ts-unsafe/README.md), so moq rewrites or drops them, as it does the +`unsafe` slice of `model/frame.rs`'s `Filled`. Decided with the maintainer on 2026-11-04, from a census at the bumped pin (moq-dev/rust2#390): the refusals no quest owned became the children below. ✅ arrayvec's `ArrayString` needs three of them (const generic types, `begin_panic`, `str::as_ptr`), kept separate since each lands alone; rejected: one quest for `ArrayString`. +Decided with the maintainer on 2026-11-07, after three designs for addresses as identity +([#596](https://github.com/moq-dev/rust2/pull/596)) each left values whose ids were never freed +or pointers whose comparison disagreed with Rust after an address was reused: + +- ✅ Addresses stay refused: every read of a pointer's address as an integer (`as usize`, + `addr`, `expose_provenance`, a raw pointer's `Hash` or `Ord`) is refused with a span, and + pointer identity (`ptr::eq`, `Arc::ptr_eq`, `Weak::ptr_eq`) keeps translating as JS + identity. Rejected: ids for an `Arc`'s or `Rc`'s value, and only for pointers that visibly + come from one (each still refuses moq's two reads in `Drop`); exact addresses for every + pointer, which needs a linear-memory model. Plan pointer addresses again once a library + other than moq needs them. +- ✅ moq keys `Track` and `FrameContent` by an explicit id in both builds, in place of their + addresses (`model/cache.rs`, `model/frame.rs`). +- ✅ moq gives its `unsafe` no `cfg(rust2_ts)` twins; it decides per item, when its quest + starts, whether to rewrite one in safe Rust for both builds or wait for this translator. + Rejected: twins until [unsafe](/quest/m3/ts-unsafe/README.md) translates them. + ## Required -- [Addresses as identity](/quest/m2/ts-model/addresses.md) - `Arc::as_ptr(a) as usize` as a key, which needs a decision - [Option fields at a unit instance](/quest/m2/ts-model/unit-option-fields.md) - `Idle<()>`'s `Option` field tells `Some(())` from `None` - [panic! in a 2015 or 2018 crate](/quest/m2/ts-model/begin-panic.md) - the `begin_panic` mapping keyed by its defining module - [A string's bytes through its pointer](/quest/m2/ts-model/str-as-ptr.md) - `str::as_ptr` reads the string's UTF-8 diff --git a/quest/m2/ts-model/addresses.md b/quest/m2/ts-model/addresses.md deleted file mode 100644 index f78036cfb..000000000 --- a/quest/m2/ts-model/addresses.md +++ /dev/null @@ -1,24 +0,0 @@ -# [M] Addresses as identity - -## Goal - -[rust2-ts](/ts/README.md) translates a pointer's address read as an integer where a crate -uses it only as an identity: compared, hashed, or ordered as a key, such as -`Arc::as_ptr(a) as usize` and `Weak::as_ptr(w) as usize`, proven by differential tests -against native Rust. Today each pointer-to-integer cast is refused. - -## Plan - -moq-net's model keys its cache and frame accounting by address six times at the pin -(`model/cache.rs`, `model/frame.rs`, `model/group/content.rs`). - -Open question for the maintainer, since the 2026-10-28 decision refuses pointer-to-integer -casts: - -1. An address is an id unique among live objects, assigned on first read and kept in a - `WeakMap`, a multiple of the type's alignment so alignment checks hold, while an - integer cast back to a pointer stays refused (recommended: it closes the common - identity idiom for every crate, and Rust promises nothing more of an address's value). -2. Keep the refusal, and moq gives these a `cfg(rust2_ts)` twin keyed by an id of its own. - -Either way, crossbeam-epoch's tagged pointers stay refused: they cast the integer back. diff --git a/quest/m3/ts-runtime-throws.md b/quest/m3/ts-runtime-throws.md index 7cac8d006..d213f6ecd 100644 --- a/quest/m3/ts-runtime-throws.md +++ b/quest/m3/ts-runtime-throws.md @@ -29,8 +29,9 @@ JS keeps in two arrays, a pointer moved past the array JS keeps it in (`AtIndex. owns, or a pointer kept in a field), `Bytes::slice_ref` of other storage, `alloc`'s bytes read as values, ranges stepped without their type, and fmt widths written as a placeholder. `git grep 'rust2-ts: ' ts/runtime` lists them; classify each before fixing. The pointer -comparisons [#584](https://github.com/moq-dev/rust2/pull/584) refuses at translation are -in scope too, through [addresses](/quest/m2/ts-model/addresses.md)' identities. +comparisons [#584](https://github.com/moq-dev/rust2/pull/584) refuses at translation, where +JS may make two `rs.Place`s for one place, are out of scope: addresses were their route, and +addresses stay refused (decided 2026-11-07 in [the model epic](/quest/m2/ts-model/README.md)). Public API: the translator's output, and which crates translate. diff --git a/quest/m3/ts-unsafe/README.md b/quest/m3/ts-unsafe/README.md index fbe362cfd..e86bbe49e 100644 --- a/quest/m3/ts-unsafe/README.md +++ b/quest/m3/ts-unsafe/README.md @@ -4,14 +4,15 @@ [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 moq `cfg(rust2_ts)` twin. +never needs a hand binding or a rewrite in its consumer. ## Plan Decided with the maintainer on 2026-10-27: a dependency containing `unsafe` is bound by -the consumer [at first](/ts/README.md#bindings), and moq gives its own `unsafe` safe -`cfg(rust2_ts)` twins. Both are stopgaps, since a stock crate should translate with no -annotations or config; this epic is what retires them. +the consumer [at first](/ts/README.md#bindings), a stopgap, since a stock crate should +translate with no annotations or config; this epic is what retires it. Decided on 2026-11-07 +in [the model epic](/quest/m2/ts-model/README.md): moq writes no `cfg(rust2_ts)` twins of +its own `unsafe`. Decided with the maintainer on 2026-10-28, typed pointers: a raw pointer is what its reference is, the object, an `rs.Place`, or `rs.NULL`, and a pointer into a slice or `Vec`