diff --git a/quest/m2/ts-model/README.md b/quest/m2/ts-model/README.md index 6af9b4f93..596e4c225 100644 --- a/quest/m2/ts-model/README.md +++ b/quest/m2/ts-model/README.md @@ -33,8 +33,8 @@ Decided with the maintainer on 2026-10-27, from a census of the model's 22 publi the translator is specific to moq: dependencies follow general rules, and moq's own changes (to its `unsafe` and its address keys, dropping or binding `hashlink`, `slab`, and `smallvec`) are one research-tier quest in moq's tree. -- 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 +- A dependency containing `unsafe` is bound by the consumer unless it uses only the + `unsafe` [m3's unsafe policy](/quest/m3/README.md) translates: 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 @@ -42,14 +42,14 @@ Decided with the maintainer on 2026-10-27, from a census of the model's 22 publi - The end-to-end work stays here: the model's roots, its differential harness, and the `frames.ts` workload. moq writes the binding files `model.toml` names, in its own tree. -The census at the pin on 2026-11-07: 51 refusals. `Timestamp` alone refuses nothing, and +The census at the pin on 2026-11-07: 45 refusals. `Timestamp` alone refuses nothing, and the [model fixture](/ts/README.md#tests)'s harness runs it against native moq-net at the pin; each case it adds as a type translates. Every other root reaches the rest of the model, all of it moq's, for its 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 moq rewrites or drops -them, as it does the `unsafe` slice of `model/frame.rs`'s `Filled`. +raw parts (4) stay refused under [m3's unsafe policy](/quest/m3/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 child quests, since finished. @@ -71,6 +71,6 @@ or pointers whose comparison disagreed with Rust after an address was reused: 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. + Rejected: twins until rust2-ts translates them, which [m3's unsafe policy](/quest/m3/README.md) mostly refuses. Public API: the translator's output for moq-net's model. diff --git a/quest/m3/README.md b/quest/m3/README.md index 624d4100d..8634c7400 100644 --- a/quest/m3/README.md +++ b/quest/m3/README.md @@ -42,7 +42,6 @@ memory model for values whose address is reinterpreted, without a consumer. - [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 - [Relink without rebuilding](/quest/m3/relink.md) - measure upstream's skip of dependency-only rebuilds on moq's edits once it reaches nightly -- [Unsafe code the translator can model](/quest/m3/ts-unsafe/README.md) - a dependency using only `unsafe` the translator can model translates from source - [Waiting: drop the lang fixture's mul_add allowance](/quest/m3/ts-fma-allowance.md) - the differential fails on any `mul_add` difference again once wasm32's `fma` rounds exactly - [Waiting: type-check in process with tsrs](/quest/m3/ts-tsrs.md) - rust2-ts checks bindings through tsrs instead of `tsc` once it is a stable library - [Throw only where Rust panics](/quest/m3/ts-runtime-throws.md) - no runtime `rust2-ts:` error where native Rust goes on: exact, or refused at translation diff --git a/quest/m3/ts-runtime-throws.md b/quest/m3/ts-runtime-throws.md index 8f0e83035..33adc171e 100644 --- a/quest/m3/ts-runtime-throws.md +++ b/quest/m3/ts-runtime-throws.md @@ -23,6 +23,9 @@ only because translation can't see where a pointer came from: `AtIndex.reach`, raw slices of a field's byte, and `Bytes::slice_ref` of copied bytes, safe code with no exact answer while a `&str` stays a JS string. AGENTS.md allows the unsafe ones under m3's [unsafe policy](/quest/m3/README.md). + Two pointers made from one reference, one through a branch or loop, may each be in + an array of one of its own; keep the loud check rather than trace joins (decided + 2026-11-07 in [#621](https://github.com/moq-dev/rust2/pull/621)). - Rejected: refusing all of them at translation, which refuses pointer arithmetic on any pointer parameter; keeping all as run-time checks. @@ -52,7 +55,3 @@ What remains: follow-ups). Public API: the translator's output, and which crates translate. - -## Related - -- [Unsafe code the translator can model](/quest/m3/ts-unsafe/README.md) - the raw-pointer model several of these throws guard; its 2026-11-01 run-time failures now follow this quest's rule diff --git a/quest/m3/ts-unsafe/README.md b/quest/m3/ts-unsafe/README.md deleted file mode 100644 index 39c266f21..000000000 --- a/quest/m3/ts-unsafe/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# Unsafe code the translator can model - -## Goal - -[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 - -Decided with the maintainer on 2026-10-27: a dependency containing `unsafe` is bound by -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` -is `{ array, index }`. Byte reinterpretation, pointer-to-integer casts, and raw allocation -are refused with a span. [#201](https://github.com/moq-dev/rust2/pull/201) landed `Box` -to raw and back, `NonNull`, `MaybeUninit`, raw reads and writes, `get_unchecked`, and -unions whose overlapping fields share a type, which took hashlink from 375 refusals to -none of its own `unsafe`. A union over a type parameter lays out each instance since -[#620](https://github.com/moq-dev/rust2/pull/620); smallvec's `SmallVecData` overlaps a -`MaybeUninit<[T; N]>` with a pointer and a length, byte reinterpretation, so it stays refused. - -- An `unsafe` operation outside the modeled set is refused with its span. -- Undefined behavior in Rust need not be reproduced, but a translated program never - silently differs from native where Rust's behavior is defined. -- Element pointers into arrays of values JS copies are `rs.AtIndex`, `{ array, index }`, - which `add`, `offset`, `offset_from`, `ptr::copy`, and `slice::from_raw_parts` act on. - Decided with the maintainer on 2026-11-01: where one body shows a pointer crossing what - JS keeps apart (a value in no array), translation refuses it with a span. A pointer to - an element JS keeps as an object, such as a tuple or the crate's struct, is that object, - which knows no array it is in, so arithmetic on one stays refused: hashbrown's buckets - and kio's `log.rs` reach it. -- 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. `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. 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. 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 - Rust defines the access it translates exactly or is refused with a span, and only a - throw guarding UB stays, as a precondition abort, or a loud check of a pointer from - elsewhere whose origin translation can't see (2026-11-07, - [#661](https://github.com/moq-dev/rust2/pull/661)). -- Done and dropped: a byte pointer past its own `Uint8Array` view. Byte pointers index the - whole buffer since [#400](https://github.com/moq-dev/rust2/pull/400) and - [#576](https://github.com/moq-dev/rust2/pull/576), and a byte `Vec` keeps its - capacity since [#555](https://github.com/moq-dev/rust2/pull/555) and - [#568](https://github.com/moq-dev/rust2/pull/568). `bytes`' `Buf::get_u16` and wider - map to the runtime since [#447](https://github.com/moq-dev/rust2/pull/447). - -Census at the moq pin on 2026-11-06: model 82, kio 76, moq-net 409 refusals. - -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 diff --git a/quest/m3/ts-unsafe/raw-behind-ref.md b/quest/m3/ts-unsafe/raw-behind-ref.md deleted file mode 100644 index 9fecd0d2e..000000000 --- a/quest/m3/ts-unsafe/raw-behind-ref.md +++ /dev/null @@ -1,23 +0,0 @@ -# [M] A raw pointer behind a reference - -## Goal - -[rust2-ts](/ts/README.md) translates `&raw const *r` and `ptr::read(r)` where `r` is a `&T` -to a value JS copies or a library object, so the pointer is where the reference came -from, proven by differential tests against native Rust. Today it is refused: ``a raw -pointer to `T` behind a reference, which is the object and not where it is kept`` -(`ts/src/lower/mir/rvalue.rs`). - -## Plan - -The reference is the value, not where it is kept, so the lowering needs the place the -reference was made from. Since [#576](https://github.com/moq-dev/rust2/pull/576) and -[#584](https://github.com/moq-dev/rust2/pull/584), a slice's reference knows its -`{data, start}`, which is that place for elements. - -At the moq pin it refuses 6 times in the model and 8 in moq-net, all smallvec's `Drain` -(`lib.rs:383`, `397`) and `retain` (`1733`, `1740`), plus moq-net's own -`ietf/parameters.rs:14,40` num_enum tag reads, which are also byte reinterpretation and -stay refused. - -Public API: the translator's output. diff --git a/ts/README.md b/ts/README.md index 614fb143f..0c40dff3f 100644 --- a/ts/README.md +++ b/ts/README.md @@ -110,7 +110,7 @@ crate translation reaches is refused, and so are two versions of one crate it re | `Vec` | `rs.Vec`: its items, the array `items`, and the capacity std's holds; `rs.vecFrom(items)` makes one of an array with room for exactly them. `rs.ByteVec` of bytes | | `Cow<[T]>`, `Cow` | `rs.Cow`: the `Vec` it owns, or an `rs.Borrowed` holding the slice it borrows, `rs.Cow>` holding the arrays of a slice of arrays; `rs.ByteCow` for bytes, an `rs.ByteVec` or an `rs.ByteSlice`; `rs.StrCow` for text, the `rs.String` it owns or an `rs.Borrowed` holding the string it borrows, whose text `rs.strCowStr` reads | | `&T`, `&mut T`, `Box`, `Reverse`, `NonZero`, `AssertUnwindSafe` | the value; a `&mut` to a value JS copies, such as a number, an `Option`, or a reference, or to an `Arc`, a `Waker`, or a `LocalKey`, whose copies share what it points at, is an `rs.Place`, which for what a box holds is the place holding the box | -| `*const T`, `*mut T`, `NonNull` | what a `&mut T` is: the object, an `rs.Slice` of a `[T; N]`'s elements or an `rs.ByteSlice` of a `[u8; N]`'s bytes, which compares by the array or buffer and offset they are at, refused for an array of one, which `array::from_ref`, `from_mut` of a variable, and `Option::as_slice` make of their own, or an `rs.Place` to a value JS copies and to a library type such as a `Vec` or an `Arc`, which into an array, slice, or `Vec` is the `rs.AtIndex` of its element, which `add`, `sub`, `offset`, their wrapping forms, `offset_from`, `ptr::copy`, `write_bytes` of bytes, and `slice::from_raw_parts` move, measure, copy between, set, and view; one to a value in no array is element 0 of an array of one, as in Rust, but a byte variable whose `&raw` the body takes, or that of a reference to it made there, as `&mut b as *mut u8` does, or of whose `&mut` it makes a slice of one, is element 0 of a `Uint8Array` of its own, which its raw, mutable, and shared slices view as they do any other bytes; JS keeps an array of one, and a shared subslice of a byte in no array, which JS copies, apart from what holds it in Rust, so a pointer, slice, copy, or write of bytes the body shows reaching past either, and `offset_from` between pointers the body shows JS keeps in different arrays, are refused, and through a pointer from elsewhere any of these, a raw or mutable slice of a byte in no array, such as a field's, or a read or write past the array JS keeps it in but for a `Vec`'s room up to its capacity, fails at run time; a pointer into an array of values JS keeps as objects is refused, as is comparing pointers to other values JS copies, each a place JS makes of its own; `rs.NULL` for null and `rs.DANGLING` for dangling, a read or write through either of which, or through a pointer arithmetic moved off either, aborts, as Rust leaves it undefined. `str::as_ptr` points into the text's UTF-8, which JS encodes anew at each call, so a read through it reads what Rust's does and a write through it, which Rust leaves undefined, aborts, as std's debug checks of unsafe preconditions do, while a mutable slice of it, a read before its first byte, such as a substring's pointer moved back into its text's bytes, and `offset_from` between two calls' pointers fail at run time, each refused where the body shows it. A pointer to a slice cast to one to an array of its elements is the `rs.Slice` or `rs.ByteSlice` of that many from its first, read through only where Rust reads. A `*const ()` is an identity, such as a waker's data, and `rs.DANGLING_UNIT` when dangling; one to a place of `()` or from a reference to one is refused. A pointer to a value JS keeps as an object cast to one, by `as` or `cast`, is the same object, which `rs.ptrErase` notes the type of, so a cast back to a pointer to another type fails at run time, and null and dangling stay so, dangling at the alignment of the type it was cast from; one to a value JS copies is the same place where an `Arc`'s or `Rc`'s `into_raw` or `as_ptr` gave it, its value's one place, and refused where the body doesn't show one of those gave it, or null or dangling, such as a borrow's, a new place each borrow, as is casting one to a view of bytes, a new one each borrow, or one of no fixed size. A write of a whole object copies into it one object deep, as an assignment through a `&mut` does, the state an `rs` class such as `rs.LazyCell` keeps private included, so a pointer to a part kept as an object, such as a field of a tuple or struct type, is refused wherever the crate may put another object there while a pointer into that memory may still be read, which a write into a fresh allocation, or a variable before any pointer into it is taken, can't, whether by index or through std's iterators over its slots; a read copies what the crate writes into in place | +| `*const T`, `*mut T`, `NonNull` | what a `&mut T` is: the object, an `rs.Slice` of a `[T; N]`'s elements or an `rs.ByteSlice` of a `[u8; N]`'s bytes, which compares by the array or buffer and offset they are at, refused for an array of one, which `array::from_ref`, `from_mut` of a variable, and `Option::as_slice` make of their own, or an `rs.Place` to a value JS copies and to a library type such as a `Vec` or an `Arc`, which into an array, slice, or `Vec` is the `rs.AtIndex` of its element, which `add`, `sub`, `offset`, their wrapping forms, `offset_from`, `ptr::copy`, `write_bytes` of bytes, and `slice::from_raw_parts` move, measure, copy between, set, and view; one to a value in no array is element 0 of an array of one, as in Rust, but a byte variable whose `&raw` the body takes, or that of a reference to it made there, as `&mut b as *mut u8` does, or of whose `&mut` it makes a slice of one, is element 0 of a `Uint8Array` of its own, which its raw, mutable, and shared slices view as they do any other bytes; JS keeps an array of one, and a shared subslice of a byte in no array, which JS copies, apart from what holds it in Rust, so a pointer, slice, copy, or write of bytes the body shows reaching past either, a read or write it shows one past a value in no array, a pointer it shows moved from one by an offset, wrapping or not, or a copy, write, or slice of a count or length, it doesn't fix, and `offset_from` between pointers the body shows JS keeps in different arrays, are refused, and through a pointer from elsewhere, or one a branch or loop assigns, any of these, a nonempty raw or mutable slice of a byte in no array, such as a field's, or a read or write past the array JS keeps it in but for a `Vec`'s room up to its capacity, fails at run time; a pointer into an array of values JS keeps as objects is refused, as is comparing pointers to other values JS copies, each a place JS makes of its own; `rs.NULL` for null and `rs.DANGLING` for dangling, a read or write through either of which, or through a pointer arithmetic moved off either, aborts, as Rust leaves it undefined. `str::as_ptr` points into the text's UTF-8, which JS encodes anew at each call, so a read through it reads what Rust's does and a write through it, which Rust leaves undefined, aborts, as std's debug checks of unsafe preconditions do, while a mutable slice of it, a read before its first byte, such as a substring's pointer moved back into its text's bytes, and `offset_from` between two calls' pointers fail at run time, each refused where the body shows it. A pointer to a slice cast to one to an array of its elements is the `rs.Slice` or `rs.ByteSlice` of that many from its first, read through only where Rust reads. One made from a reference, by `&raw const *r`, `as`, `ptr::from_ref` or `from_mut`, or `NonNull::from`, is where the body shows the reference borrowed, as the `rs.AtIndex` of the element `&v[i]` lends, and else a place of its own: holding the value of a shared reference to a value that is `Freeze` or keeps a tag or niche, which nothing may write whole while the pointer lives, or an `rs.placeIn` writing a whole value into the object a `&mut`, or a shared reference to any other value, is in place, refused where JS can't write into one in place. A `*const ()` is an identity, such as a waker's data, and `rs.DANGLING_UNIT` when dangling; one to a place of `()` or from a reference to one is refused. A pointer to a value JS keeps as an object cast to one, by `as` or `cast`, is the same object, which `rs.ptrErase` notes the type of, so a cast back to a pointer to another type fails at run time, and null and dangling stay so, dangling at the alignment of the type it was cast from; one to a value JS copies is the same place where an `Arc`'s or `Rc`'s `into_raw` or `as_ptr` gave it, its value's one place, and refused where the body doesn't show one of those gave it, or null or dangling, such as a borrow's, a new place each borrow, as is casting one to a view of bytes, a new one each borrow, or one of no fixed size. A write of a whole object copies into it one object deep, as an assignment through a `&mut` does, the state an `rs` class such as `rs.LazyCell` keeps private included, so a pointer to a part kept as an object, such as a field of a tuple or struct type, is refused wherever the crate may put another object there while a pointer into that memory may still be read, which a write into a fresh allocation, or a variable before any pointer into it is taken, can't, whether by index or through std's iterators over its slots; a read that moves a value out, by `ptr::read`, `replace`, `swap`, or `copy`, copies each object it holds at any depth that the crate writes into in place, a library object such as a `RefCell` or a `Vec` one object deep, with what a cell, lock, or range holds inline, and one with interior mutability, such as a `Cell`, always; a collection whose JS object keeps its length with its storage, such as a `Vec` of other than bytes, a `VecDeque`, a `String`, a map, or a set, which no copy splits from the original as Rust's read does, and any other library object such as a `LazyCell` or a `Peekable`, whose inline state only its runtime class knows, are refused where the read would copy them | | `MaybeUninit` | `T`: `undefined` until written, `0` for a byte, or an object whose parts kept as objects are there, holding `undefined`, as `Box::new_uninit` and each of `Box::new_uninit_slice`'s is; a slice's `write_clone_of_slice` clones into its slots without dropping what they held. `Box::new_zeroed`'s holds the value of zero bytes in each part they are one of, such as a number, `false`, `None`, a null pointer, or an enum's variant of a tag or niche of zero, and `undefined` in each part they are none of, such as a reference, for a write to fill before a read | | `ManuallyDrop` | `T`, which nothing drops but its own `drop`; its `Debug`, comparisons, and constants are refused | | `union` | an object of slots named by offset, `at0` and `at8`, each holding the field part kept there; fields must overlap in the same type, or in a non-null pointer and an `Option` of it | @@ -1193,7 +1193,7 @@ they need. ## Not yet Byte reinterpretation, pointer addresses, pointer arithmetic on a value JS keeps as an object (other than wrapping arithmetic on a null or dangling pointer), memory from `alloc` other than an array of values JS copies, and unions whose -fields overlap in different types ([unsafe](/quest/m3/ts-unsafe/README.md)), and trait objects of a trait +fields overlap in different types ([m3's unsafe policy](/quest/m3/README.md)), and trait objects of a trait with a const parameter, of an array, or behind a pointer other than `&`, `&mut`, `Box`, `Arc`, `Rc`, `Weak`, or `Pin`, fail translation. Until they translate, an item that needs one, such as a `Box` made `From` a string, binds to hand-written TS ([bindings](#bindings)). diff --git a/ts/fixtures/census/census.md b/ts/fixtures/census/census.md index 3f0f658c1..aecd16873 100644 --- a/ts/fixtures/census/census.md +++ b/ts/fixtures/census/census.md @@ -1,6 +1,6 @@ # Census of `census-fixture` -- Refusals: 13 +- Refusals: 9 - Function instances: 6 - Generic functions no instance reached, lowered over their type parameters: 1 @@ -8,7 +8,6 @@ | Count | Construct | First | | ---: | --- | --- | -| 4 | a raw pointer to `_` behind a reference, which is the object and not where it is kept | fixtures/census/src/lib.rs:9:2 | | 3 | a cast from `_` to `_`, between a pointer and an integer | fixtures/census/src/lib.rs:9:2 | | 2 | a cast from a pointer to `_` to one to `_`, which reads the bytes of one type as another | fixtures/census/src/lib.rs:19:12 | | 2 | a mutable static, which any code may write | fixtures/census/src/lib.rs:38:3 | @@ -21,41 +20,28 @@ The type or module each refused type or path belongs to. | Count | API | First | | ---: | --- | --- | -| 4 | `u32` | fixtures/census/src/lib.rs:9:2 | | 3 | `usize` | fixtures/census/src/lib.rs:9:2 | -| 2 | `u64` | fixtures/census/src/lib.rs:19:47 | +| 2 | `u32` | fixtures/census/src/lib.rs:19:12 | | 2 | `u8` | fixtures/census/src/lib.rs:19:12 | | 1 | `*const T` | fixtures/census/src/lib.rs:14:2 | | 1 | `*const bytes::Bytes` | fixtures/census/src/lib.rs:45:2 | | 1 | `*const u32` | fixtures/census/src/lib.rs:9:2 | | 1 | `Bits` | fixtures/census/src/lib.rs:28:1 | -| 1 | `bytes::Bytes` | fixtures/census/src/lib.rs:45:2 | +| 1 | `u64` | fixtures/census/src/lib.rs:19:47 | ## By type or path | Count | Construct | Type or path | First | | ---: | --- | --- | --- | -| 2 | a raw pointer to `_` behind a reference, which is the object and not where it is kept | `u32` | fixtures/census/src/lib.rs:9:2 | | 1 | a cast from `_` to `_`, between a pointer and an integer | `*const T`, `usize` | fixtures/census/src/lib.rs:14:2 | | 1 | a cast from `_` to `_`, between a pointer and an integer | `*const bytes::Bytes`, `usize` | fixtures/census/src/lib.rs:45:2 | | 1 | a cast from `_` to `_`, between a pointer and an integer | `*const u32`, `usize` | fixtures/census/src/lib.rs:9:2 | | 1 | a cast from a pointer to `_` to one to `_`, which reads the bytes of one type as another | `u32`, `u8` | fixtures/census/src/lib.rs:19:12 | | 1 | a cast from a pointer to `_` to one to `_`, which reads the bytes of one type as another | `u64`, `u8` | fixtures/census/src/lib.rs:19:47 | -| 1 | a raw pointer to `_` behind a reference, which is the object and not where it is kept | `bytes::Bytes` | fixtures/census/src/lib.rs:45:2 | -| 1 | a raw pointer to `_` behind a reference, which is the object and not where it is kept | `u64` | fixtures/census/src/lib.rs:19:48 | | 1 | union `_`, whose fields overlap in `_` and `f64`, which JS keeps apart | `Bits`, `u32` | fixtures/census/src/lib.rs:28:1 | ## Spans -
a raw pointer to `_` behind a reference, which is the object and not where it is kept (4) - -- fixtures/census/src/lib.rs:9:2: a raw pointer to `u32` behind a reference, which is the object and not where it is kept -- fixtures/census/src/lib.rs:19:13: a raw pointer to `u32` behind a reference, which is the object and not where it is kept -- fixtures/census/src/lib.rs:19:48: a raw pointer to `u64` behind a reference, which is the object and not where it is kept -- fixtures/census/src/lib.rs:45:2: a raw pointer to `bytes::Bytes` behind a reference, which is the object and not where it is kept - -
-
a cast from `_` to `_`, between a pointer and an integer (3) - fixtures/census/src/lib.rs:9:2: a cast from `*const u32` to `usize`, between a pointer and an integer diff --git a/ts/fixtures/lang/conversions/expected/conversions.ts b/ts/fixtures/lang/conversions/expected/conversions.ts index a3aa0d3aa..955bbb90c 100644 --- a/ts/fixtures/lang/conversions/expected/conversions.ts +++ b/ts/fixtures/lang/conversions/expected/conversions.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rs from "./rs.ts"; -import { copy$array_Option_u16, copy$array_array0_u8, copy$array_array1_u8, copy$array_array2_u32, copy$array_array3_u8, copy$array_array40_u8, copy$array_u32, copy$array_u64, copy$array_u8, copy$conversions_Logged, copy$conversions_Named, drop$tuple2_conversions_Logged_u8, out$u8 } from "./lib.ts"; +import { copy$String, copy$Vec_u32, copy$Vec_u8, copy$array_Option_u16, copy$array_array0_u8, copy$array_array1_u8, copy$array_array2_u32, copy$array_array3_u8, copy$array_array40_u8, copy$array_u32, copy$array_u64, copy$array_u8, copy$conversions_Logged, copy$conversions_Named, drop$tuple2_conversions_Logged_u8, out$u8 } from "./lib.ts"; import * as Cursor from "./conversions.Cursor.ts"; import * as Header from "./conversions.Header.ts"; import * as Held from "./conversions.Held.ts"; @@ -888,10 +888,10 @@ export function lentParam$String(to: rs.String, data: rs.ByteSlice, out: rs.Byte rs.idx(s, 0); const borrowed7 = s.data; const borrowed8 = s.start; - const _147: rs.Cell = rs.cellOfArraySlice(rs.arraySliceOne(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5)), 1), rs.objectCopy); + const _147: rs.Cell = rs.cellOfArraySlice(rs.arraySliceOne(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5)), 1), copy$String); const _148: rs.String[][] = [[make$conversions_Value_String(data, 28)]]; const pointer2 = rs.cellArray(_147, rs.arraySliceOf(rs.sliceFrom)); - const old7 = rs.arraySliceCopy(pointer2, rs.objectCopy); + const old7 = rs.arraySliceCopy(pointer2, copy$String); rs.arraySliceSet(pointer2, _148); put$conversions_Value_String(old7[0][0], out); rs.idx(s, 0); @@ -960,10 +960,10 @@ export function lentParam$Vec_u32(to: rs.Vec, data: rs.ByteSlice, out: r rs.idx(s, 0); const borrowed7 = s.data; const borrowed8 = s.start; - const _147: rs.Cell[][]> = rs.cellOfArraySlice(rs.arraySliceOne(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5)), 1), rs.objectCopy); + const _147: rs.Cell[][]> = rs.cellOfArraySlice(rs.arraySliceOne(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5)), 1), copy$Vec_u32); const _148: rs.Vec[][] = [[make$conversions_Value_Vec_u32(data, 28)]]; const pointer2 = rs.cellArray(_147, rs.arraySliceOf(rs.sliceFrom)); - const old7 = rs.arraySliceCopy(pointer2, rs.objectCopy); + const old7 = rs.arraySliceCopy(pointer2, copy$Vec_u32); rs.arraySliceSet(pointer2, _148); put$conversions_Value_Vec_u32(old7[0][0], out); rs.idx(s, 0); @@ -1032,10 +1032,10 @@ export function lentParam$Vec_u8(to: rs.ByteVec, data: rs.ByteSlice, out: rs.Byt rs.idx(s, 0); const borrowed7 = s.data; const borrowed8 = s.start; - const _147: rs.Cell = rs.cellOfArraySlice(rs.arraySliceOne(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5)), 1), rs.objectCopy); + const _147: rs.Cell = rs.cellOfArraySlice(rs.arraySliceOne(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5)), 1), copy$Vec_u8); const _148: rs.ByteVec[][] = [[make$conversions_Value_Vec_u8(data, 28)]]; const pointer2 = rs.cellArray(_147, rs.arraySliceOf(rs.sliceFrom)); - const old7 = rs.arraySliceCopy(pointer2, rs.objectCopy); + const old7 = rs.arraySliceCopy(pointer2, copy$Vec_u8); rs.arraySliceSet(pointer2, _148); put$conversions_Value_Vec_u8(old7[0][0], out); rs.idx(s, 0); @@ -2420,9 +2420,12 @@ export function converted$Option_u16(data: rs.ByteSlice, out: rs.ByteVec): void put$conversions_Value_Option_u16(_199, out); } boxed$Option_u16(data, out); - put$conversions_Value_Option_u16({ v: b }.v, out); + put$conversions_Value_Option_u16(b, out); c = make$conversions_Value_Option_u16(data, 16); put$conversions_Value_Option_u16(c, out); + const kept: rs.u8 = byte(data, 17); + ({ v: kept }.v = 2); + rs.byteVecPush(out, kept); } /** Each conversion of a `T`, its copies put after a write to what they copied. */ @@ -2510,9 +2513,12 @@ export function converted$String(data: rs.ByteSlice, out: rs.ByteVec): void { put$conversions_Value_String(_199, out); } boxed$String(data, out); - put$conversions_Value_String({ v: b }.v, out); + put$conversions_Value_String(b, out); c = make$conversions_Value_String(data, 16); put$conversions_Value_String(c, out); + const kept: rs.u8 = byte(data, 17); + ({ v: kept }.v = 2); + rs.byteVecPush(out, kept); } /** Each conversion of a `T`, its copies put after a write to what they copied. */ @@ -2598,9 +2604,12 @@ export function converted$Vec_u32(data: rs.ByteSlice, out: rs.ByteVec): void { put$conversions_Value_Vec_u32(_199, out); } boxed$Vec_u32(data, out); - put$conversions_Value_Vec_u32({ v: b }.v, out); + put$conversions_Value_Vec_u32(b, out); c = make$conversions_Value_Vec_u32(data, 16); put$conversions_Value_Vec_u32(c, out); + const kept: rs.u8 = byte(data, 17); + ({ v: kept }.v = 2); + rs.byteVecPush(out, kept); } /** Each conversion of a `T`, its copies put after a write to what they copied. */ @@ -2686,9 +2695,12 @@ export function converted$Vec_u8(data: rs.ByteSlice, out: rs.ByteVec): void { put$conversions_Value_Vec_u8(_199, out); } boxed$Vec_u8(data, out); - put$conversions_Value_Vec_u8({ v: b }.v, out); + put$conversions_Value_Vec_u8(b, out); c = make$conversions_Value_Vec_u8(data, 16); put$conversions_Value_Vec_u8(c, out); + const kept: rs.u8 = byte(data, 17); + ({ v: kept }.v = 2); + rs.byteVecPush(out, kept); } /** Each conversion of a `T`, its copies put after a write to what they copied. */ @@ -2799,6 +2811,9 @@ export function converted$array0_u8(data: rs.ByteSlice, out: rs.ByteVec): void { rs.byteSliceFrom(c); copy$array_u8(make$conversions_Value_array0_u8(data, 16)); put$conversions_Value_array0_u8(rs.byteSliceFrom(c), out); + const kept: rs.u8 = byte(data, 17); + ({ v: kept }.v = 2); + rs.byteVecPush(out, kept); } /** Each conversion of a `T`, its copies put after a write to what they copied. */ @@ -2917,6 +2932,9 @@ export function converted$array1_u8(data: rs.ByteSlice, out: rs.ByteVec): void { const _211: Uint8Array = copy$array_u8(make$conversions_Value_array1_u8(data, 16)); q.data.set(_211, q.start); put$conversions_Value_array1_u8(rs.byteSliceFrom(c), out); + const kept: rs.u8 = byte(data, 17); + ({ v: kept }.v = 2); + rs.byteVecPush(out, kept); } /** Each conversion of a `T`, its copies put after a write to what they copied. */ @@ -3019,6 +3037,9 @@ export function converted$array2_u32(data: rs.ByteSlice, out: rs.ByteVec): void put$conversions_Value_array2_u32(rs.sliceFrom(b), out); rs.sliceSet(rs.sliceFrom(c), copy$array_u32(make$conversions_Value_array2_u32(data, 16))); put$conversions_Value_array2_u32(rs.sliceFrom(c), out); + const kept: rs.u8 = byte(data, 17); + ({ v: kept }.v = 2); + rs.byteVecPush(out, kept); } /** Each conversion of a `T`, its copies put after a write to what they copied. */ @@ -3137,6 +3158,9 @@ export function converted$array3_u8(data: rs.ByteSlice, out: rs.ByteVec): void { const _211: Uint8Array = copy$array_u8(make$conversions_Value_array3_u8(data, 16)); q.data.set(_211, q.start); put$conversions_Value_array3_u8(rs.byteSliceFrom(c), out); + const kept: rs.u8 = byte(data, 17); + ({ v: kept }.v = 2); + rs.byteVecPush(out, kept); } /** Each conversion of a `T`, its copies put after a write to what they copied. */ @@ -3255,6 +3279,9 @@ export function converted$array40_u8(data: rs.ByteSlice, out: rs.ByteVec): void const _211: Uint8Array = copy$array_u8(make$conversions_Value_array40_u8(data, 16)); q.data.set(_211, q.start); put$conversions_Value_array40_u8(rs.byteSliceFrom(c), out); + const kept: rs.u8 = byte(data, 17); + ({ v: kept }.v = 2); + rs.byteVecPush(out, kept); } /** Each conversion of a `T`, its copies put after a write to what they copied. */ @@ -3377,6 +3404,9 @@ export function converted$conversions_Logged(data: rs.ByteSlice, out: rs.ByteVec q[Symbol.dispose](); rs.ptrWrite(q, _211); Logged.put$conversions_Value(c, out); + const kept: rs.u8 = byte(data, 17); + ({ v: kept }.v = 2); + rs.byteVecPush(out, kept); rs.dropVec(none); rs.dropVec(list); rs.dropAll(array); @@ -3483,6 +3513,9 @@ export function converted$conversions_Named(data: rs.ByteSlice, out: rs.ByteVec) Named.put$conversions_Value(b, out); rs.ptrWrite(c, Named.make$conversions_Value(data, 16)); Named.put$conversions_Value(c, out); + const kept: rs.u8 = byte(data, 17); + ({ v: kept }.v = 2); + rs.byteVecPush(out, kept); } /** Each conversion of a `T`, its copies put after a write to what they copied. */ @@ -3567,9 +3600,12 @@ export function converted$u32(data: rs.ByteSlice, out: rs.ByteVec): void { put$conversions_Value_u32(_199, out); } boxed$u32(data, out); - put$conversions_Value_u32({ v: b }.v, out); + put$conversions_Value_u32(b, out); c = make$conversions_Value_u32(data, 16); put$conversions_Value_u32(c, out); + const kept: rs.u8 = byte(data, 17); + ({ v: kept }.v = 2); + rs.byteVecPush(out, kept); } /** Each conversion of a `T`, its copies put after a write to what they copied. */ @@ -3654,9 +3690,12 @@ export function converted$u64(data: rs.ByteSlice, out: rs.ByteVec): void { put$conversions_Value_u64(_199, out); } boxed$u64(data, out); - put$conversions_Value_u64({ v: b }.v, out); + put$conversions_Value_u64(b, out); c = make$conversions_Value_u64(data, 16); put$conversions_Value_u64(c, out); + const kept: rs.u8 = byte(data, 17); + ({ v: kept }.v = 2); + rs.byteVecPush(out, kept); } /** Each conversion of a `T`, its copies put after a write to what they copied. */ @@ -3739,9 +3778,12 @@ export function converted$u8(data: rs.ByteSlice, out: rs.ByteVec): void { put$conversions_Value_u8(_199, out); } boxed$u8(data, out); - put$conversions_Value_u8({ v: b }.v, out); + put$conversions_Value_u8(b, out); c = make$conversions_Value_u8(data, 16); put$conversions_Value_u8(c, out); + const kept: rs.u8 = byte(data, 17); + ({ v: kept }.v = 2); + rs.byteVecPush(out, kept); } /** diff --git a/ts/fixtures/lang/conversions/expected/lib.ts b/ts/fixtures/lang/conversions/expected/lib.ts index 3ecbccb9e..4404843be 100644 --- a/ts/fixtures/lang/conversions/expected/lib.ts +++ b/ts/fixtures/lang/conversions/expected/lib.ts @@ -5,6 +5,18 @@ import { conversions, bytePlaces } from "./conversions.ts"; import * as Logged from "./conversions.Logged.ts"; import type * as Named from "./conversions.Named.ts"; +export function copy$String(v: rs.String): rs.String { + return rs.objectCopy(v); +} + +export function copy$Vec_u32(v: rs.Vec): rs.Vec { + return rs.objectCopy(v); +} + +export function copy$Vec_u8(v: rs.ByteVec): rs.ByteVec { + return rs.objectCopy(v); +} + export function copy$array_Option_u16(v: Array): Array { return v.slice(); } @@ -46,7 +58,7 @@ export function copy$conversions_Logged(v: Logged.Logged): Logged.Logged { } export function copy$conversions_Named(v: Named.Named): Named.Named { - return { id: v.id, name: v.name }; + return { id: v.id, name: copy$String(v.name) }; } export function drop$tuple2_conversions_Logged_u8(_1: [Logged.Logged, rs.u8]): void { diff --git a/ts/fixtures/lang/conversions/src/conversions.rs b/ts/fixtures/lang/conversions/src/conversions.rs index 1bf23ecd9..c7607bbc6 100644 --- a/ts/fixtures/lang/conversions/src/conversions.rs +++ b/ts/fixtures/lang/conversions/src/conversions.rs @@ -467,6 +467,11 @@ fn converted(data: &[u8], out: &mut Vec) { let q = std::ptr::from_mut(&mut c); unsafe { *q = T::make(data, 16) }; c.put(out); + // A box's pointer written through once, the only use of it, leaking the box, which + // holds a copy of what it was made of. + let kept = byte(data, 17); + unsafe { *Box::into_raw(Box::new(kept)) = 2 }; + out.push(kept); } /// Boxes, which clone what they hold by its own `clone_from`: a box, one in a box, in an diff --git a/ts/fixtures/lang/elements/expected/lib.ts b/ts/fixtures/lang/elements/expected/lib.ts index 63ac32a2b..fb0fc40fd 100644 --- a/ts/fixtures/lang/elements/expected/lib.ts +++ b/ts/fixtures/lang/elements/expected/lib.ts @@ -9,6 +9,10 @@ export function copy$Option_tuple2_u8_u8(v: [rs.u8, rs.u8] | undefined): [rs.u8, return v === undefined ? v : copy$tuple2_u8_u8(v); } +export function copy$Vec_u8(v: rs.ByteVec): rs.ByteVec { + return rs.objectCopy(v); +} + export function copy$array_Option_tuple2_u8_u8(v: Array<[rs.u8, rs.u8] | undefined>): Array<[rs.u8, rs.u8] | undefined> { return v.map(copy$Option_tuple2_u8_u8); } diff --git a/ts/fixtures/lang/elements/expected/spare.ts b/ts/fixtures/lang/elements/expected/spare.ts index 4e6d3bd68..d8511a077 100644 --- a/ts/fixtures/lang/elements/expected/spare.ts +++ b/ts/fixtures/lang/elements/expected/spare.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rs from "./rs.ts"; -import { copy$mem_MaybeUninit_tuple2_u8_u32, copy$tuple2_u8_u32 } from "./lib.ts"; +import { copy$Vec_u8, copy$mem_MaybeUninit_tuple2_u8_u32, copy$tuple2_u8_u32 } from "./lib.ts"; import { thenSome$1gvhopg } from "./core/bool.ts"; import { mapOr$17z0l8m as option_mapOr$17z0l8m } from "./core/option.ts"; import * as Noted from "./spare.Noted.ts"; @@ -25,6 +25,15 @@ export function put$spare_Item_String(self: rs.String, out: rs.ByteVec): void { rs.byteVecPush(out, 255); } +export function of$spare_Item_Vec_u8(n: rs.u8): rs.ByteVec { + return rs.byteVecOf(n, rs.u8Rem(n, 3) as rs.usize); +} + +export function put$spare_Item_Vec_u8(self: rs.ByteVec, out: rs.ByteVec): void { + rs.byteVecExtendFromSlice(out, rs.byteVecSlice(self)); + rs.byteVecPush(out, 255); +} + export function of$spare_Item_Option_u16(n: rs.u8): rs.u16 | undefined { return thenSome$1gvhopg(rs.u8Rem(n, 3) !== 0, ((n as rs.u16) << 4) & 0xffff); } @@ -82,6 +91,24 @@ export function state$String(v: rs.Vec, out: rs.ByteVec): void { } } +/** The length, capacity, and room of `v`, how many `Noted`s dropped, and its items. */ +export function state$Vec_u8(v: rs.Vec, out: rs.ByteVec): void { + const iter: rs.Iter = rs.iterSlice([v.items.length, rs.vecCapacity(v, 12), rs.vecSpare(v, 12, (): rs.ByteVec => { + return (undefined as unknown) as rs.ByteVec; + }).length]); + while (true) { + const _13: rs.usize | rs.Done = iter.next(); + if (_13 === rs.DONE) { + break; + } + rs.byteVecExtendFromSlice(out, rs.byteSliceFrom(rs.u32ToBeBytes(_13 as rs.u32).reverse())); + } + rs.byteVecPush(out, rs.atomicLoad(DROPS, 0)); + for (const _31 of v.items) { + put$spare_Item_Vec_u8(_31, out); + } +} + /** The length, capacity, and room of `v`, how many `Noted`s dropped, and its items. */ export function state$spare_Noted(v: rs.Vec, out: rs.ByteVec): void { const iter: rs.Iter = rs.iterSlice([v.items.length, rs.vecCapacity(v, 1), rs.vecSpare(v, 1, (): Noted.Noted => { @@ -294,12 +321,12 @@ export function pointers$Option_u16(data: rs.ByteSlice, out: rs.ByteVec): void { * `Drain` does; a filter that shortens it to nothing and writes back what it keeps, as * std's `retain` does; grown, cut, and refilled. */ -export function pointers$String(data: rs.ByteSlice, out: rs.ByteVec): void { +export function pointers$Vec_u8(data: rs.ByteSlice, out: rs.ByteVec): void { let _43!: rs.usize; let _165!: rs.usize; let _194!: rs.usize; const ops: Ops.Ops = { _0Data: data.data, _0Start: data.start, _0Len: data.length }; - let v: rs.Vec = rs.vecWithCapacity(rs.u8Rem(Ops.byte(ops), 12) as rs.usize, 12); + let v: rs.Vec = rs.vecWithCapacity(rs.u8Rem(Ops.byte(ops), 12) as rs.usize, 12); while (!(ops._0Len === 0)) { const _13: [rs.u8, rs.u8] = [Ops.byte(ops), Ops.byte(ops)]; const op: rs.u8 = _13[0]; @@ -310,59 +337,59 @@ export function pointers$String(data: rs.ByteSlice, out: rs.ByteVec): void { b2: { const _35: rs.u8 = rs.u8Rem(op, 8); if (_35 === 0) { - const end: rs.Place = rs.ptrAdd(rs.vecAsPtr(v, 12), len); + const end: rs.Place = rs.ptrAdd(rs.vecAsPtr(v, 12), len); let iterStart: rs.usize = 0; const iterEnd: rs.usize = k; while ((_43 = iterStart++) < iterEnd) { const i: rs.usize = _43; - rs.ptrAdd(end, i).v = of$spare_Item_String(rs.u8WrappingAdd(n, i & 0xff)); + rs.ptrAdd(end, i).v = of$spare_Item_Vec_u8(rs.u8WrappingAdd(n, i & 0xff)); } if (k >= 2) { - const _54: rs.Slice = new rs.RawSlice(end, k).array(2); + const _54: rs.Slice = new rs.RawSlice(end, k).array(2); rs.sliceSwap(_54, 0, 1); - put$spare_Item_String(_54.data[_54.start], out); + put$spare_Item_Vec_u8(_54.data[_54.start], out); } - const moved: rs.Vec = rs.replaceIn(v, new rs.Vec()); + const moved: rs.Vec = rs.replaceIn(v, new rs.Vec()); for (const _71 of rs.sliceItems(rs.ptrSlice(rs.ptrAdd(end, -len), rs.usizeAdd(len, k)))) { - put$spare_Item_String(_71, out); + put$spare_Item_Vec_u8(_71, out); } - rs.vecSetLen(moved, rs.usizeAdd(len, k), 12, (): rs.String => { - return (undefined as unknown) as rs.String; + rs.vecSetLen(moved, rs.usizeAdd(len, k), 12, (): rs.ByteVec => { + return (undefined as unknown) as rs.ByteVec; }); v = moved; break b2; } if (_35 === 1) { - rs.ptrCopy(rs.vecAsPtr(rs.iterToVec(rs.iterMap(new rs.Range(0, k), (i5: rs.usize): rs.String => { - return of$spare_Item_String(n ^ (i5 & 0xff)); - }), undefined, 12, true), 12), rs.ptrAdd(rs.vecAsPtr(v, 12), len), k); - rs.vecSetLen(v, rs.usizeAdd(len, k), 12, (): rs.String => { - return (undefined as unknown) as rs.String; + rs.ptrCopy(rs.vecAsPtr(rs.iterToVec(rs.iterMap(new rs.Range(0, k), (i5: rs.usize): rs.ByteVec => { + return of$spare_Item_Vec_u8(n ^ (i5 & 0xff)); + }), undefined, 12, true), 12), rs.ptrAdd(rs.vecAsPtr(v, 12), len), k, copy$Vec_u8); + rs.vecSetLen(v, rs.usizeAdd(len, k), 12, (): rs.ByteVec => { + return (undefined as unknown) as rs.ByteVec; }); break b2; } if (_35 === 2) { - const iter3: rs.Iter<[rs.usize, rs.Place]> = rs.iterRev(rs.iterEnumerate(rs.iterSliceMut(rs.ptrSlice(rs.ptrAdd(rs.vecAsPtr(v, 12), len), k)))); + const iter3: rs.Iter<[rs.usize, rs.Place]> = rs.iterRev(rs.iterEnumerate(rs.iterSliceMut(rs.ptrSlice(rs.ptrAdd(rs.vecAsPtr(v, 12), len), k)))); while (true) { - const _108: [rs.usize, rs.Place] | rs.Done = iter3.next(); + const _108: [rs.usize, rs.Place] | rs.Done = iter3.next(); if (_108 === rs.DONE) { break; } - _108[1].v = of$spare_Item_String(rs.u8WrappingSub(n, _108[0] & 0xff)); + _108[1].v = of$spare_Item_Vec_u8(rs.u8WrappingSub(n, _108[0] & 0xff)); } - rs.vecSetLen(v, rs.usizeAdd(len, k), 12, (): rs.String => { - return (undefined as unknown) as rs.String; + rs.vecSetLen(v, rs.usizeAdd(len, k), 12, (): rs.ByteVec => { + return (undefined as unknown) as rs.ByteVec; }); break b2; } if (_35 === 3) { rs.vecReserve(v, 1, 12); const at: rs.usize = rs.usizeRem(n as rs.usize, rs.usizeAdd(len, 1)); - const p: rs.Place = rs.ptrAdd(rs.vecAsPtr(v, 12), at); - rs.ptrCopy(p, rs.ptrAdd(p, 1), rs.usizeSub(len, at)); - p.v = of$spare_Item_String(n); - rs.vecSetLen(v, rs.usizeAdd(len, 1), 12, (): rs.String => { - return (undefined as unknown) as rs.String; + const p: rs.Place = rs.ptrAdd(rs.vecAsPtr(v, 12), at); + rs.ptrCopy(p, rs.ptrAdd(p, 1), rs.usizeSub(len, at), copy$Vec_u8); + p.v = of$spare_Item_Vec_u8(n); + rs.vecSetLen(v, rs.usizeAdd(len, 1), 12, (): rs.ByteVec => { + return (undefined as unknown) as rs.ByteVec; }); break b2; } @@ -370,40 +397,40 @@ export function pointers$String(data: rs.ByteSlice, out: rs.ByteVec): void { if (len > 0) { const at2: rs.usize = rs.usizeRem(n as rs.usize, len); const end2: rs.usize = rs.usizeAdd(at2, rs.usizeRem(n >> 4 as rs.usize, rs.usizeAdd(rs.usizeSub(len, at2), 1))); - rs.vecSetLen(v, at2, 12, (): rs.String => { - return (undefined as unknown) as rs.String; + rs.vecSetLen(v, at2, 12, (): rs.ByteVec => { + return (undefined as unknown) as rs.ByteVec; }); - const p2: rs.Place = rs.vecAsPtr(v, 12); + const p2: rs.Place = rs.vecAsPtr(v, 12); let iterStart2: rs.usize = at2; const iterEnd2: rs.usize = end2; while ((_165 = iterStart2++) < iterEnd2) { - put$spare_Item_String(rs.ptrAdd(p2, _165).v, out); + put$spare_Item_Vec_u8(copy$Vec_u8(rs.ptrAdd(p2, _165).v), out); } - rs.ptrCopy(rs.ptrAdd(p2, end2), rs.ptrAdd(p2, at2), rs.usizeSub(len, end2)); - rs.vecSetLen(v, rs.usizeSub(len, rs.usizeSub(end2, at2)), 12, (): rs.String => { - return (undefined as unknown) as rs.String; + rs.ptrCopy(rs.ptrAdd(p2, end2), rs.ptrAdd(p2, at2), rs.usizeSub(len, end2), copy$Vec_u8); + rs.vecSetLen(v, rs.usizeSub(len, rs.usizeSub(end2, at2)), 12, (): rs.ByteVec => { + return (undefined as unknown) as rs.ByteVec; }); break b2; } } else { if (_35 === 5) { - rs.vecSetLen(v, 0, 12, (): rs.String => { - return (undefined as unknown) as rs.String; + rs.vecSetLen(v, 0, 12, (): rs.ByteVec => { + return (undefined as unknown) as rs.ByteVec; }); - const p3: rs.Place = rs.vecAsPtr(v, 12); + const p3: rs.Place = rs.vecAsPtr(v, 12); let kept: rs.usize = 0; let iterStart3: rs.usize = 0; const iterEnd3: rs.usize = len; while ((_194 = iterStart3++) < iterEnd3) { const i4: rs.usize = _194; - const x: rs.String = rs.ptrAdd(p3, i4).v; + const x: rs.ByteVec = copy$Vec_u8(rs.ptrAdd(p3, i4).v); if (rs.usizeRem(rs.usizeAdd(i4, n as rs.usize), 3) !== 0) { rs.ptrAdd(p3, kept).v = x; kept = rs.usizeAdd(kept, 1); } } - rs.vecSetLen(v, kept, 12, (): rs.String => { - return (undefined as unknown) as rs.String; + rs.vecSetLen(v, kept, 12, (): rs.ByteVec => { + return (undefined as unknown) as rs.ByteVec; }); break b2; } @@ -414,7 +441,7 @@ export function pointers$String(data: rs.ByteSlice, out: rs.ByteVec): void { } rs.vecTruncate(v, rs.usizeRem(n as rs.usize, rs.usizeAdd(len, 1))); } - state$String(v, out); + state$Vec_u8(v, out); } } @@ -1131,7 +1158,7 @@ export function spares(kind: rs.u8, data: rs.ByteSlice, out: rs.ByteVec): void { if (_8 === 0) { pointers$u32(data, out); } else if (_8 === 1) { - pointers$String(data, out); + pointers$Vec_u8(data, out); } else if (_8 === 2) { pointers$Option_u16(data, out); } else if (_8 === 3) { diff --git a/ts/fixtures/lang/elements/src/spare.rs b/ts/fixtures/lang/elements/src/spare.rs index 3faaf2765..4408ccd8c 100644 --- a/ts/fixtures/lang/elements/src/spare.rs +++ b/ts/fixtures/lang/elements/src/spare.rs @@ -3,8 +3,8 @@ //! through the slots `spare_capacity_mut` lends, then taken in by `set_len`; shortened by //! `set_len` and read back past its length, as std's `Drain` and `retain` read theirs, then //! lengthened over what it gave up; and slots never written, copied as they are. Of numbers, -//! `String`s, `Option`s, values that count their drops, and values JS keeps as objects, by -//! a generic function too. +//! bytes, `String`s, `Option`s, values that count their drops, and values JS keeps as +//! objects, by a generic function too. use std::mem::MaybeUninit; use std::ptr; @@ -38,6 +38,18 @@ impl Item for String { } } +/// Bytes, whose length JS keeps apart from them, as Rust does, so a pointer moves one exactly. +impl Item for Vec { + fn of(n: u8) -> Self { + vec![n; usize::from(n % 3)] + } + + fn put(&self, out: &mut Vec) { + out.extend_from_slice(self); + out.push(0xff); + } +} + impl Item for Option { fn of(n: u8) -> Self { (n % 3 != 0).then_some(u16::from(n) << 4) @@ -301,7 +313,8 @@ pub fn spares(kind: u8, data: &[u8], out: &mut Vec) { DROPS.store(0, Ordering::Relaxed); match kind % 10 { 0 => pointers::(data, out), - 1 => pointers::(data, out), + // A `String`'s JS object keeps its length with its text, so a pointer can't move one. + 1 => pointers::>(data, out), 2 => pointers::>(data, out), 3 => slots::(data, out), 4 => slots::(data, out), diff --git a/ts/fixtures/lang/locals/expected/locals.ts b/ts/fixtures/lang/locals/expected/locals.ts index 6dcc73831..494cb3d31 100644 --- a/ts/fixtures/lang/locals/expected/locals.ts +++ b/ts/fixtures/lang/locals/expected/locals.ts @@ -296,7 +296,7 @@ export function keys(data: rs.ByteSlice, out: rs.ByteVec): void { } else if (_12 === 1) { const old = key.v; key.v = RIGHT; - rs.byteVecPush(out, get$09fnt4b({ v: old }.v)); + rs.byteVecPush(out, get$09fnt4b(old)); } else if (_12 === 2) { const other: rs.Place>> = { v: RIGHT }; const swapped = key.v; diff --git a/ts/fixtures/lang/pointers/expected/core/bool.ts b/ts/fixtures/lang/pointers/expected/core/bool.ts index e694d25bc..ba69bc95a 100644 --- a/ts/fixtures/lang/pointers/expected/core/bool.ts +++ b/ts/fixtures/lang/pointers/expected/core/bool.ts @@ -3,6 +3,20 @@ import * as rs from "../rs.ts"; import type * as Spot from "../pointers.Spot.ts"; +export function thenSome$1gvhopg(self: boolean, t: rs.u16): rs.u16 | undefined { + if (!self) { + return undefined; + } + return t; +} + +export function then$01qpu7s(self: boolean, f: () => rs.Cell): rs.Cell | undefined { + if (!self) { + return undefined; + } + return f(); +} + export function then$0bgt67i(self: boolean, f: () => rs.Weak): rs.Weak | undefined { if (!self) { return undefined; diff --git a/ts/fixtures/lang/pointers/expected/core/option.ts b/ts/fixtures/lang/pointers/expected/core/option.ts index 13623916b..7ccd7868f 100644 --- a/ts/fixtures/lang/pointers/expected/core/option.ts +++ b/ts/fixtures/lang/pointers/expected/core/option.ts @@ -48,6 +48,20 @@ export function unwrapOr$1fr498u(self: rs.u8 | undefined, default_: rs.u8): rs.u return self; } +export function map$00buaky(self: rs.u16 | undefined | rs.None, f: (a0: rs.u16 | undefined) => rs.u16 | undefined): rs.u16 | undefined | rs.None { + if (self === rs.None) { + return rs.None; + } + return f(self); +} + +export function map$1h9tk16(self: rs.ByteVec | undefined, f: (a0: rs.ByteVec) => rs.ByteVec): rs.ByteVec | undefined { + if (self === undefined) { + return undefined; + } + return f(self); +} + export function map$1cfrse5(self: Loaded.Loaded | undefined, f: (a0: Loaded.Loaded) => Loaded.Loaded): Loaded.Loaded | undefined { if (self === undefined) { return undefined; @@ -55,6 +69,27 @@ export function map$1cfrse5(self: Loaded.Loaded | undefined, f: (a0: Loaded.Load return f(self); } +export function map$1ng7x7l(self: rs.Weak | undefined, f: (a0: rs.Weak) => rs.Weak): rs.Weak | undefined { + if (self === undefined) { + return undefined; + } + return f(self); +} + +export function map$070fw7m(self: rs.u32 | undefined, f: (a0: rs.u32) => rs.u32): rs.u32 | undefined { + if (self === undefined) { + return undefined; + } + return f(self); +} + +export function map$1px2rk4(self: rs.u8 | undefined, f: (a0: rs.u8) => rs.ByteVec): rs.ByteVec | undefined { + if (self === undefined) { + return undefined; + } + return f(self); +} + export function mapOr$078vnqn(self: Pair.Pair | undefined, default_: rs.u8, f: (a0: Pair.Pair) => rs.u8): rs.u8 { if (self === undefined) { return default_; @@ -69,6 +104,13 @@ export function mapOr$00zno7m(self: Spot.Spot | undefined, default_: Spot.Spot, return f(copy$pointers_Spot(self)); } +export function mapOr$18adzvj(self: rs.Cell | undefined, default_: rs.u32, _: (a0: rs.Cell) => rs.u32): rs.u32 { + if (self === undefined) { + return default_; + } + return self.v; +} + export function mapOr$0m14xib(self: Uint8Array | rs.None, default_: Uint8Array, f: (a0: Uint8Array) => Uint8Array): Uint8Array { if (self === rs.None) { return default_; @@ -83,6 +125,13 @@ export function mapOr$1wlsr3s(self: rs.u8 | undefined, default_: rs.u32, f: (a0: return f(self); } +export function mapOr$0emjeap(self: rs.Arc | undefined, default_: rs.u8, f: (a0: rs.Arc) => rs.u8): rs.u8 { + if (self === undefined) { + return default_; + } + return f(self); +} + export function mapOr$173j2gr(self: [rs.u8, rs.Arc] | undefined, default_: rs.u8, f: (a0: [rs.u8, rs.Arc]) => rs.u8): rs.u8 { if (self === undefined) { return default_; @@ -90,6 +139,13 @@ export function mapOr$173j2gr(self: [rs.u8, rs.Arc] | undefined, default_ return f(self); } +export function mapOr$0smr0aj(self: rs.u16 | undefined, default_: Uint8Array, _: (a0: rs.u16) => Uint8Array): Uint8Array { + if (self === undefined) { + return default_; + } + return rs.u16ToBeBytes(self); +} + export function filter$0wvwd70(self: rs.u32 | undefined, predicate: (a0: rs.u32) => boolean): rs.u32 | undefined { if (self !== undefined) { const x: rs.u32 = self; diff --git a/ts/fixtures/lang/pointers/expected/lib.ts b/ts/fixtures/lang/pointers/expected/lib.ts index 9de202b84..9e95e0b21 100644 --- a/ts/fixtures/lang/pointers/expected/lib.ts +++ b/ts/fixtures/lang/pointers/expected/lib.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rs from "./rs.ts"; -import { lists, shared, places, unchecked, nested, copies, sentinels, cells, rebound, lapsed, writers, made, segments, uninitRuns, iteratedRuns, besideWritten, waiters, taken, bytes, loaded, reattached, partWritten, aimed, distinct, rawArcs, rawPlaces, arrays, arrayCasts, genericUnions, rawCells, cellWrites, nestedCells, loneBytes, arrayParts } from "./pointers.ts"; +import { lists, shared, places, unchecked, nested, copies, sentinels, cells, rebound, lapsed, writers, made, segments, uninitRuns, iteratedRuns, besideWritten, waiters, taken, bytes, loaded, reattached, partWritten, aimed, distinct, rawArcs, rawPlaces, arrays, behindRefs, movedOut, arrayCasts, genericUnions, rawCells, cellWrites, nestedCells, loneBytes, arrayParts } from "./pointers.ts"; import type * as Aimed from "./pointers.Aimed.ts"; import * as Bin from "./pointers.Bin.ts"; import type * as ByCall from "./pointers.ByCall.ts"; @@ -10,6 +10,7 @@ import type * as ByObject from "./pointers.ByObject.ts"; import type * as Duo from "./pointers.Duo.ts"; import type * as Either from "./pointers.Either.ts"; import type * as Halves from "./pointers.Halves.ts"; +import type * as HeldCell from "./pointers.HeldCell.ts"; import type * as Housed from "./pointers.Housed.ts"; import type * as Inner from "./pointers.Inner.ts"; import type * as Leaning from "./pointers.Leaning.ts"; @@ -19,8 +20,21 @@ import type * as Pair from "./pointers.Pair.ts"; import type * as Paired from "./pointers.Paired.ts"; import type * as Reentrant from "./pointers.Reentrant.ts"; import type * as Spot from "./pointers.Spot.ts"; +import type * as TaggedVec from "./pointers.TaggedVec.ts"; import type * as Twin from "./pointers.Twin.ts"; +export function copy$Cell_u32(v: rs.Cell): rs.Cell { + return new rs.Cell(v.v); +} + +export function copy$Option_Cell_u32(v: rs.Cell | undefined): rs.Cell | undefined { + return v === undefined ? v : copy$Cell_u32(v); +} + +export function copy$Option_Vec_u8(v: rs.ByteVec | undefined): rs.ByteVec | undefined { + return v === undefined ? v : copy$Vec_u8(v); +} + export function copy$Option_pointers_Pair(v: Pair.Pair | undefined): Pair.Pair | undefined { return v === undefined ? v : copy$pointers_Pair(v); } @@ -29,6 +43,10 @@ export function copy$Option_tuple2_u16_u8(v: [rs.u16, rs.u8] | undefined): [rs.u return v === undefined ? v : copy$tuple2_u16_u8(v); } +export function copy$Vec_u8(v: rs.ByteVec): rs.ByteVec { + return rs.objectCopy(v); +} + export function copy$array_array2_u32(v: rs.u32[][]): rs.u32[][] { return v.map(copy$array_u32); } @@ -53,6 +71,29 @@ export function copy$array_u8(v: Uint8Array): Uint8Array { return v.slice(); } +export function copy$cell_LazyCell_u32(v: rs.LazyCell): rs.LazyCell { + return rs.objectCopy(v); +} + +export function copy$cell_OnceCell_Cell_u32(v: rs.OnceCell>): rs.OnceCell> { + const c = rs.objectCopy(v); + c.v = copy$Option_Cell_u32(c.v); + return c; +} + +export function copy$cell_RefCell_Cell_u32(v: rs.RefCell>): rs.RefCell> { + const c = rs.objectCopy(v); + c.value = copy$Cell_u32(c.value); + return c; +} + +export function copy$mem_MaybeUninit_Vec_u8(v: rs.ByteVec): rs.ByteVec { + if (v === undefined) { + return v; + } + return rs.objectCopy(v); +} + export function copy$mem_MaybeUninit_array_u32(v: rs.u32[]): rs.u32[] { if (v === undefined) { return v; @@ -67,6 +108,27 @@ export function copy$mem_MaybeUninit_array_u8(v: Uint8Array): Uint8Array { return v.slice(); } +export function copy$mem_MaybeUninit_cell_RefCell_u32(v: rs.RefCell): rs.RefCell { + if (v === undefined) { + return v; + } + return rs.objectCopy(v); +} + +export function copy$mem_MaybeUninit_cell_UnsafeCell_cell_RefCell_u32(v: rs.Cell>): rs.Cell> { + if (v === undefined) { + return v; + } + return new rs.Cell(copy$mem_MaybeUninit_cell_RefCell_u32(v.v)); +} + +export function copy$mem_MaybeUninit_pointers_HeldCell(v: HeldCell.HeldCell): HeldCell.HeldCell { + if (v === undefined) { + return v; + } + return { c: copy$mem_MaybeUninit_cell_UnsafeCell_cell_RefCell_u32(v.c) }; +} + export function copy$mem_MaybeUninit_tuple2_u8_u8(v: [rs.u8, rs.u8]): [rs.u8, rs.u8] { if (v === undefined) { return v; @@ -74,6 +136,13 @@ export function copy$mem_MaybeUninit_tuple2_u8_u8(v: [rs.u8, rs.u8]): [rs.u8, rs return [v[0], v[1]]; } +export function copy$ops_Range_Cell_u32(v: rs.Range>): rs.Range> { + const c = rs.objectCopy(v); + c.start = copy$Cell_u32(c.start); + c.end = copy$Cell_u32(c.end); + return c; +} + export function copy$pointers_Aimed_Cell_u8(v: Aimed.Aimed>): Aimed.Aimed> { return { at: v.at as rs.Place>, seen: v.seen as rs.Place>, held: v.held as rs.Place>, maybe: v.maybe as rs.Place> | undefined, tagged: copy$tuple2_mutPtr_Cell_u8_u8(v.tagged as [rs.Place>, rs.u8]), to: v.to, read: (v.read as (a0: rs.Place>) => rs.u8) as (a0: rs.Ptr>) => rs.u8 }; } @@ -150,10 +219,22 @@ export function copy$pointers_Spot(v: Spot.Spot): Spot.Spot { return { at: copy$tuple2_u16_u8(v.at), tag: v.tag }; } +export function copy$pointers_TaggedVec(v: TaggedVec.TaggedVec): TaggedVec.TaggedVec { + return { v: copy$Vec_u8(v.v), tag: v.tag }; +} + export function copy$pointers_Twin(v: Twin.Twin): Twin.Twin { return { _0: v._0, _1: v._1 }; } +export function copy$sync_Weak_u32(v: rs.Weak): rs.Weak { + return rs.objectCopy(v); +} + +export function copy$tuple2_Vec_u8_u8(v: [rs.ByteVec, rs.u8]): [rs.ByteVec, rs.u8] { + return [copy$Vec_u8(v[0]), v[1]]; +} + export function copy$tuple2_array2_u32_u8(v: [rs.u32[], rs.u8]): [rs.u32[], rs.u8] { return [copy$array_u32(v[0]), v[1]]; } @@ -194,8 +275,8 @@ export function copy$tuple2_u32_u32(v: [rs.u32, rs.u32]): [rs.u32, rs.u32] { return [v[0], v[1]]; } -export function copy$tuple2_u8_String(v: [rs.u8, rs.String]): [rs.u8, rs.String] { - return [v[0], v[1]]; +export function copy$tuple2_u8_Vec_u8(v: [rs.u8, rs.ByteVec]): [rs.u8, rs.ByteVec] { + return [v[0], copy$Vec_u8(v[1])]; } export function copy$tuple2_u8_sync_Arc_u8(v: [rs.u8, rs.Arc]): [rs.u8, rs.Arc] { @@ -210,8 +291,8 @@ export function copy$tuple2_u8_u8(v: [rs.u8, rs.u8]): [rs.u8, rs.u8] { return [v[0], v[1]]; } -export function drop$pointers_List_String(_1: List.List): void { - List.drop$ops_Drop_String(_1); +export function drop$pointers_List_Vec_u8(_1: List.List): void { + List.drop$ops_Drop_Vec_u8(_1); } export function drop$pointers_List_sync_Arc_u8(_1: List.List>): void { @@ -311,20 +392,24 @@ export function cases(): rs.Vec<[string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.By rawPlaces(data26, out26); }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::arrays", (_236: rs.u8, data27: rs.ByteSlice, out27: rs.ByteVec): undefined => { arrays(data27, out27); - }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::array_casts", (_237: rs.u8, data28: rs.ByteSlice, out28: rs.ByteVec): undefined => { - arrayCasts(data28, out28); - }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::generic_unions", (_238: rs.u8, data29: rs.ByteSlice, out29: rs.ByteVec): undefined => { - genericUnions(data29, out29); - }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::raw_cells", (_239: rs.u8, data30: rs.ByteSlice, out30: rs.ByteVec): undefined => { - rawCells(data30, out30); - }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::cell_writes", (_240: rs.u8, data31: rs.ByteSlice, out31: rs.ByteVec): undefined => { - cellWrites(data31, out31); - }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::nested_cells", (_241: rs.u8, data32: rs.ByteSlice, out32: rs.ByteVec): undefined => { - nestedCells(data32, out32); - }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::lone_bytes", (_242: rs.u8, data33: rs.ByteSlice, out33: rs.ByteVec): undefined => { - loneBytes(data33, out33); - }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::array_parts", (_243: rs.u8, data34: rs.ByteSlice, out34: rs.ByteVec): undefined => { - arrayParts(data34, out34); + }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::behind_refs", (_237: rs.u8, data28: rs.ByteSlice, out28: rs.ByteVec): undefined => { + behindRefs(data28, out28); + }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::array_casts", (_238: rs.u8, data29: rs.ByteSlice, out29: rs.ByteVec): undefined => { + arrayCasts(data29, out29); + }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::generic_unions", (_239: rs.u8, data30: rs.ByteSlice, out30: rs.ByteVec): undefined => { + genericUnions(data30, out30); + }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::raw_cells", (_240: rs.u8, data31: rs.ByteSlice, out31: rs.ByteVec): undefined => { + rawCells(data31, out31); + }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::cell_writes", (_241: rs.u8, data32: rs.ByteSlice, out32: rs.ByteVec): undefined => { + cellWrites(data32, out32); + }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::nested_cells", (_242: rs.u8, data33: rs.ByteSlice, out33: rs.ByteVec): undefined => { + nestedCells(data33, out33); + }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::lone_bytes", (_243: rs.u8, data34: rs.ByteSlice, out34: rs.ByteVec): undefined => { + loneBytes(data34, out34); + }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::moved_out", (_244: rs.u8, data35: rs.ByteSlice, out35: rs.ByteVec): undefined => { + movedOut(data35, out35); + }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::array_parts", (_245: rs.u8, data36: rs.ByteSlice, out36: rs.ByteVec): undefined => { + arrayParts(data36, out36); }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined]]); } diff --git a/ts/fixtures/lang/pointers/expected/pointers.Drained.ts b/ts/fixtures/lang/pointers/expected/pointers.Drained.ts new file mode 100644 index 000000000..36f4394f1 --- /dev/null +++ b/ts/fixtures/lang/pointers/expected/pointers.Drained.ts @@ -0,0 +1,63 @@ +// Generated by rust2-ts. Do not edit. + +import * as rs from "./rs.ts"; +import { copy$Vec_u8, copy$sync_Weak_u32 } from "./lib.ts"; +import { map$00buaky as option_map$00buaky, map$1h9tk16 as option_map$1h9tk16, map$1ng7x7l as option_map$1ng7x7l, map$070fw7m as option_map$070fw7m } from "./core/option.ts"; + +/** + * What smallvec's `Drain` is: the elements of a slice nothing will drop again, each read + * out through the reference its iterator lends. + */ +export interface Drained { + iter: rs.Iter>; +} + +export function next$iter_Iterator_Option_u16(self: Drained): rs.u16 | undefined | rs.Done { + return rs.iterFromOption(option_map$00buaky(rs.iterToOption((self.iter as rs.Iter).next(), rs.None), (r: rs.u16 | undefined): rs.u16 | undefined => { + return r; + }), rs.None); +} + +export function next$iter_Iterator_Vec_u8(self: Drained): rs.ByteVec | rs.Done { + return rs.iterFromOption(option_map$1h9tk16(rs.iterToOption((self.iter as rs.Iter).next()), (r: rs.ByteVec): rs.ByteVec => { + return copy$Vec_u8(r); + })); +} + +export function next$iter_Iterator_sync_Weak_u32(self: Drained>): rs.Weak | rs.Done { + return rs.iterFromOption(option_map$1ng7x7l(rs.iterToOption((self.iter as rs.Iter>).next()), (r: rs.Weak): rs.Weak => { + return copy$sync_Weak_u32(r); + })); +} + +export function next$iter_Iterator_u32(self: Drained): rs.u32 | rs.Done { + return rs.iterFromOption(option_map$070fw7m(rs.iterToOption((self.iter as rs.Iter).next()), (r: rs.u32): rs.u32 => { + return r; + })); +} + +export function nextBack$iter_DoubleEndedIterator_Option_u16(self: Drained): rs.u16 | undefined | rs.None { + return option_map$00buaky(rs.iterToOption((self.iter as rs.Iter).nextBack(), rs.None), (r: rs.u16 | undefined): rs.u16 | undefined => { + return r; + }); +} + +export function nextBack$iter_DoubleEndedIterator_Vec_u8(self: Drained): rs.ByteVec | undefined { + return option_map$1h9tk16(rs.iterToOption((self.iter as rs.Iter).nextBack()), (r: rs.ByteVec): rs.ByteVec => { + return copy$Vec_u8(r); + }); +} + +export function nextBack$iter_DoubleEndedIterator_sync_Weak_u32(self: Drained>): rs.Weak | undefined { + return option_map$1ng7x7l(rs.iterToOption((self.iter as rs.Iter>).nextBack()), (r: rs.Weak): rs.Weak => { + return copy$sync_Weak_u32(r); + }); +} + +export function nextBack$iter_DoubleEndedIterator_u32(self: Drained): rs.u32 | undefined { + return option_map$070fw7m(rs.iterToOption((self.iter as rs.Iter).nextBack()), (r: rs.u32): rs.u32 => { + return r; + }); +} + +//# sourceMappingURL=pointers.Drained.ts.map diff --git a/ts/fixtures/lang/pointers/expected/pointers.Halves32.ts b/ts/fixtures/lang/pointers/expected/pointers.Halves32.ts new file mode 100644 index 000000000..2698afc9d --- /dev/null +++ b/ts/fixtures/lang/pointers/expected/pointers.Halves32.ts @@ -0,0 +1,11 @@ +// Generated by rust2-ts. Do not edit. + +import * as rs from "./rs.ts"; + +/** Two numbers, the second of which a pointer from a reference to them points at. */ +export interface Halves32 { + first: rs.u32; + second: rs.u32; +} + +//# sourceMappingURL=pointers.Halves32.ts.map diff --git a/ts/fixtures/lang/pointers/expected/pointers.HeldCell.ts b/ts/fixtures/lang/pointers/expected/pointers.HeldCell.ts new file mode 100644 index 000000000..c56e811a2 --- /dev/null +++ b/ts/fixtures/lang/pointers/expected/pointers.HeldCell.ts @@ -0,0 +1,10 @@ +// Generated by rust2-ts. Do not edit. + +import * as rs from "./rs.ts"; + +/** A cell of a `RefCell`, which an uninitialized shell holds nothing in. */ +export interface HeldCell { + c: rs.Cell>; +} + +//# sourceMappingURL=pointers.HeldCell.ts.map diff --git a/ts/fixtures/lang/pointers/expected/pointers.List.ts b/ts/fixtures/lang/pointers/expected/pointers.List.ts index b812f3f11..4bbebb0fb 100644 --- a/ts/fixtures/lang/pointers/expected/pointers.List.ts +++ b/ts/fixtures/lang/pointers/expected/pointers.List.ts @@ -1,8 +1,8 @@ // Generated by rust2-ts. Do not edit. import * as rs from "./rs.ts"; -import { copy$tuple2_u8_String, copy$tuple2_u8_sync_Arc_u8, drop$tuple2_u8_sync_Arc_u8 } from "./lib.ts"; -import { attachBefore$String, attachBefore$sync_Arc_u8, detach$String, detach$sync_Arc_u8 } from "./pointers.ts"; +import { copy$tuple2_u8_Vec_u8, copy$tuple2_u8_sync_Arc_u8, drop$tuple2_u8_sync_Arc_u8 } from "./lib.ts"; +import { attachBefore$Vec_u8, attachBefore$sync_Arc_u8, detach$Vec_u8, detach$sync_Arc_u8 } from "./pointers.ts"; import type * as Free from "./pointers.Free.ts"; import type * as Links from "./pointers.Links.ts"; import * as Node from "./pointers.Node.ts"; @@ -21,7 +21,7 @@ export class List { } } -export function new$String(): List { +export function new$Vec_u8(): List { return new List(undefined, undefined, 0); } @@ -29,13 +29,13 @@ export function new$sync_Arc_u8(): List> { return new List(undefined, undefined, 0); } -export function guard$String(self: List): Node.Node { +export function guard$Vec_u8(self: List): Node.Node { if (self.guard !== undefined) { return self.guard; } - const guard2: Node.Node = Node.new$String(); - const _8: Links.Links = { next: guard2, prev: guard2 }; - const _11: Node.Node = guard2; + const guard2: Node.Node = Node.new$Vec_u8(); + const _8: Links.Links = { next: guard2, prev: guard2 }; + const _11: Node.Node = guard2; _11.link.at0 = _8.next; _11.link.at4 = _8.prev; self.guard = guard2; @@ -55,11 +55,11 @@ export function guard$sync_Arc_u8(self: List>): Node.Node): Node.Node { +export function alloc$Vec_u8(self: List): Node.Node { if (self.free === undefined) { - return Node.new$String(); + return Node.new$Vec_u8(); } - const free: Node.Node = self.free; + const free: Node.Node = self.free; self.free = free.link.at0; return free; } @@ -73,11 +73,11 @@ export function alloc$sync_Arc_u8(self: List>): Node.Node, key: rs.u8, value: rs.String): void { - const guard: Node.Node = guard$String(self); - const node: Node.Node = alloc$String(self); - rs.ptrWrite(node.entry, [key, value] as [rs.u8, rs.String]); - attachBefore$String(node, guard); +export function pushBack$Vec_u8(self: List, key: rs.u8, value: rs.ByteVec): void { + const guard: Node.Node = guard$Vec_u8(self); + const node: Node.Node = alloc$Vec_u8(self); + rs.ptrWrite(node.entry, [key, value] as [rs.u8, rs.ByteVec]); + attachBefore$Vec_u8(node, guard); self.len = rs.usizeAdd(self.len, 1); } @@ -89,14 +89,14 @@ export function pushBack$sync_Arc_u8(self: List>, key: rs.u8, valu self.len = rs.usizeAdd(self.len, 1); } -export function find$String(self: List, key: rs.u8): Node.Node | undefined { - const _4: Node.Node | undefined = self.guard; +export function find$Vec_u8(self: List, key: rs.u8): Node.Node | undefined { + const _4: Node.Node | undefined = self.guard; if (_4 !== undefined) { - const guard: Node.Node = _4; - let at: Node.Node = guard.link.at0 as Node.Node; + const guard: Node.Node = _4; + let at: Node.Node = guard.link.at0 as Node.Node; while (at !== guard) { if (!(at.entry[0] === key)) { - at = at.link.at0 as Node.Node; + at = at.link.at0 as Node.Node; } else { return at; } @@ -123,13 +123,13 @@ export function find$sync_Arc_u8(self: List>, key: rs.u8): Node.No return undefined; } -export function remove$String(self: List, key: rs.u8): [rs.u8, rs.String] | undefined { - const _4: Node.Node | undefined = find$String(self, key); +export function remove$Vec_u8(self: List, key: rs.u8): [rs.u8, rs.ByteVec] | undefined { + const _4: Node.Node | undefined = find$Vec_u8(self, key); if (_4 !== undefined) { - const node: Node.Node = _4; - detach$String(node); - const entry: [rs.u8, rs.String] = copy$tuple2_u8_String(node.entry); - const _16: Free.Free = { next: self.free }; + const node: Node.Node = _4; + detach$Vec_u8(node); + const entry: [rs.u8, rs.ByteVec] = copy$tuple2_u8_Vec_u8(node.entry); + const _16: Free.Free = { next: self.free }; node.link.at0 = _16.next; self.free = node; self.len = rs.usizeSub(self.len, 1); @@ -153,14 +153,14 @@ export function remove$sync_Arc_u8(self: List>, key: rs.u8): [rs.u return undefined; } -export function toFront$String(self: List, key: rs.u8): boolean { - const _4: Node.Node | undefined = find$String(self, key); +export function toFront$Vec_u8(self: List, key: rs.u8): boolean { + const _4: Node.Node | undefined = find$Vec_u8(self, key); if (_4 !== undefined) { - const node: Node.Node = _4; + const node: Node.Node = _4; if (self.guard !== undefined) { - const guard: Node.Node = self.guard; - detach$String(node); - attachBefore$String(node, guard.link.at0 as Node.Node); + const guard: Node.Node = self.guard; + detach$Vec_u8(node); + attachBefore$Vec_u8(node, guard.link.at0 as Node.Node); return true; } return false; @@ -183,38 +183,38 @@ export function toFront$sync_Arc_u8(self: List>, key: rs.u8): bool return false; } -export function getMut$String(self: List, key: rs.u8): rs.String | undefined { - const _3: Node.Node | undefined = find$String(self, key); +export function getMut$Vec_u8(self: List, key: rs.u8): rs.ByteVec | undefined { + const _3: Node.Node | undefined = find$Vec_u8(self, key); if (_3 !== undefined) { return _3.entry[1]; } return undefined; } -export function each$String(self: List, out: rs.ByteVec): void { +export function each$Vec_u8(self: List, out: rs.ByteVec): void { b0: { if (self.guard !== undefined) { - const guard: Node.Node = self.guard; - let at: Node.Node = guard.link.at0 as Node.Node; + const guard: Node.Node = self.guard; + let at: Node.Node = guard.link.at0 as Node.Node; while (true) { if (!(at !== guard)) { break b0; } - const _13: [rs.u8, rs.String] = at.entry; + const _13: [rs.u8, rs.ByteVec] = at.entry; rs.byteVecPush(out, _13[0]); - rs.byteVecExtendFromSlice(out, rs.stringAsBytes(_13[1].v)); - at = at.link.at0 as Node.Node; + rs.byteVecExtendFromSlice(out, rs.byteVecSlice(_13[1])); + at = at.link.at0 as Node.Node; } } } } -export function drop$ops_Drop_String(self: List): void { +export function drop$ops_Drop_Vec_u8(self: List): void { if (self.guard !== undefined) { - const guard: Node.Node = self.guard; - let at: Node.Node = guard.link.at0 as Node.Node; + const guard: Node.Node = self.guard; + let at: Node.Node = guard.link.at0 as Node.Node; while (at !== guard) { - at = at.link.at0 as Node.Node; + at = at.link.at0 as Node.Node; } } while (self.free !== undefined) { diff --git a/ts/fixtures/lang/pointers/expected/pointers.Node.ts b/ts/fixtures/lang/pointers/expected/pointers.Node.ts index bcee03554..df628a145 100644 --- a/ts/fixtures/lang/pointers/expected/pointers.Node.ts +++ b/ts/fixtures/lang/pointers/expected/pointers.Node.ts @@ -9,10 +9,10 @@ export interface Node { link: Link.Link; } -export function new$String(): Node { - const _1: [rs.u8, rs.String] = ([undefined, undefined] as unknown) as [rs.u8, rs.String]; - const _3: Links.Links = { next: rs.DANGLING, prev: rs.DANGLING }; - return { entry: _1, link: ({ at0: _3.next, at4: _3.prev } as unknown) as Link.Link }; +export function new$Vec_u8(): Node { + const _1: [rs.u8, rs.ByteVec] = ([undefined, undefined] as unknown) as [rs.u8, rs.ByteVec]; + const _3: Links.Links = { next: rs.DANGLING, prev: rs.DANGLING }; + return { entry: _1, link: ({ at0: _3.next, at4: _3.prev } as unknown) as Link.Link }; } export function new$sync_Arc_u8(): Node> { diff --git a/ts/fixtures/lang/pointers/expected/pointers.Nothing.ts b/ts/fixtures/lang/pointers/expected/pointers.Nothing.ts new file mode 100644 index 000000000..eb46c21cc --- /dev/null +++ b/ts/fixtures/lang/pointers/expected/pointers.Nothing.ts @@ -0,0 +1,7 @@ +// Generated by rust2-ts. Do not edit. + +/** A value of no bytes, whose every pointer is where it was. */ +export interface Nothing { +} + +//# sourceMappingURL=pointers.Nothing.ts.map diff --git a/ts/fixtures/lang/pointers/expected/pointers.TaggedVec.ts b/ts/fixtures/lang/pointers/expected/pointers.TaggedVec.ts new file mode 100644 index 000000000..4c23eb3aa --- /dev/null +++ b/ts/fixtures/lang/pointers/expected/pointers.TaggedVec.ts @@ -0,0 +1,11 @@ +// Generated by rust2-ts. Do not edit. + +import * as rs from "./rs.ts"; + +/** A `Vec` with a tag, which a pointer to one moves out whole. */ +export interface TaggedVec { + v: rs.ByteVec; + tag: rs.u8; +} + +//# sourceMappingURL=pointers.TaggedVec.ts.map diff --git a/ts/fixtures/lang/pointers/expected/pointers.ts b/ts/fixtures/lang/pointers/expected/pointers.ts index 096ff2931..db0886202 100644 --- a/ts/fixtures/lang/pointers/expected/pointers.ts +++ b/ts/fixtures/lang/pointers/expected/pointers.ts @@ -1,9 +1,9 @@ // Generated by rust2-ts. Do not edit. import * as rs from "./rs.ts"; -import { copy$Option_pointers_Pair, copy$Option_tuple2_u16_u8, copy$array_array2_u32, copy$array_ptr_array4_u8, copy$array_tuple2_u8_u8, copy$array_u16, copy$array_u32, copy$array_u8, copy$mem_MaybeUninit_array_u32, copy$mem_MaybeUninit_array_u8, copy$mem_MaybeUninit_tuple2_u8_u8, copy$pointers_Aimed_Cell_u8, copy$pointers_Aimed_array2_u16, copy$pointers_Aimed_array4_u8, copy$pointers_Aimed_pointers_Housed_u8, copy$pointers_Aimed_pointers_Leaning, copy$pointers_Aimed_pointers_Paired, copy$pointers_Aimed_u8, copy$pointers_ByCall, copy$pointers_ByClosure, copy$pointers_ByObject, copy$pointers_Duo, copy$pointers_Either_u32, copy$pointers_Either_u8, copy$pointers_Halves, copy$pointers_Outer, copy$pointers_Pair, copy$pointers_Reentrant, copy$pointers_Spot, copy$pointers_Twin, copy$tuple2_array2_u32_u8, copy$tuple2_u16_u8, copy$tuple2_u32_u32, copy$tuple2_u8_u16, copy$tuple2_u8_u8, drop$pointers_List_String, drop$pointers_List_sync_Arc_u8, drop$tuple2_sync_Arc_pointers_Twin_Rc_pointers_Twin, drop$tuple3_sync_Arc_u32_Rc_pointers_Duo_sync_Arc_unit, drop$tuple4_sync_Arc_pointers_Twin_sync_Arc_pointers_Twin_Rc_pointers_Twin_Rc_pointers_Twin, of$pointers_Bin_pointers_ByObject } from "./lib.ts"; -import { then$0bgt67i, then$1a5xgoh, then$0jw3xip } from "./core/bool.ts"; -import { isSomeAnd$1s2u5lz, isSomeAnd$1nosv3e, isSomeAnd$07atiew, unwrapOr$0mkoact as option_unwrapOr$0mkoact, unwrapOr$179xo1w as option_unwrapOr$179xo1w, unwrapOr$1fr498u as option_unwrapOr$1fr498u, map$1cfrse5 as option_map$1cfrse5, mapOr$078vnqn as option_mapOr$078vnqn, mapOr$00zno7m as option_mapOr$00zno7m, mapOr$0m14xib as option_mapOr$0m14xib, mapOr$1wlsr3s as option_mapOr$1wlsr3s, mapOr$173j2gr as option_mapOr$173j2gr, filter$0wvwd70 } from "./core/option.ts"; +import { copy$Option_Cell_u32, copy$Option_Vec_u8, copy$Option_pointers_Pair, copy$Option_tuple2_u16_u8, copy$Vec_u8, copy$array_array2_u32, copy$array_ptr_array4_u8, copy$array_tuple2_u8_u8, copy$array_u16, copy$array_u32, copy$array_u8, copy$cell_LazyCell_u32, copy$cell_OnceCell_Cell_u32, copy$cell_RefCell_Cell_u32, copy$mem_MaybeUninit_Vec_u8, copy$mem_MaybeUninit_array_u32, copy$mem_MaybeUninit_array_u8, copy$mem_MaybeUninit_pointers_HeldCell, copy$mem_MaybeUninit_tuple2_u8_u8, copy$ops_Range_Cell_u32, copy$pointers_Aimed_Cell_u8, copy$pointers_Aimed_array2_u16, copy$pointers_Aimed_array4_u8, copy$pointers_Aimed_pointers_Housed_u8, copy$pointers_Aimed_pointers_Leaning, copy$pointers_Aimed_pointers_Paired, copy$pointers_Aimed_u8, copy$pointers_ByCall, copy$pointers_ByClosure, copy$pointers_ByObject, copy$pointers_Duo, copy$pointers_Either_u32, copy$pointers_Either_u8, copy$pointers_Halves, copy$pointers_Outer, copy$pointers_Pair, copy$pointers_Reentrant, copy$pointers_Spot, copy$pointers_TaggedVec, copy$pointers_Twin, copy$sync_Weak_u32, copy$tuple2_Vec_u8_u8, copy$tuple2_array2_u32_u8, copy$tuple2_u16_u8, copy$tuple2_u32_u32, copy$tuple2_u8_u16, copy$tuple2_u8_u8, drop$pointers_List_Vec_u8, drop$pointers_List_sync_Arc_u8, drop$tuple2_sync_Arc_pointers_Twin_Rc_pointers_Twin, drop$tuple3_sync_Arc_u32_Rc_pointers_Duo_sync_Arc_unit, drop$tuple4_sync_Arc_pointers_Twin_sync_Arc_pointers_Twin_Rc_pointers_Twin_Rc_pointers_Twin, of$pointers_Bin_pointers_ByObject } from "./lib.ts"; +import { thenSome$1gvhopg, then$01qpu7s, then$0bgt67i, then$1a5xgoh, then$0jw3xip } from "./core/bool.ts"; +import { isSomeAnd$1s2u5lz, isSomeAnd$1nosv3e, isSomeAnd$07atiew, unwrapOr$0mkoact as option_unwrapOr$0mkoact, unwrapOr$179xo1w as option_unwrapOr$179xo1w, unwrapOr$1fr498u as option_unwrapOr$1fr498u, map$1cfrse5 as option_map$1cfrse5, map$1px2rk4 as option_map$1px2rk4, mapOr$078vnqn as option_mapOr$078vnqn, mapOr$00zno7m as option_mapOr$00zno7m, mapOr$18adzvj as option_mapOr$18adzvj, mapOr$0m14xib as option_mapOr$0m14xib, mapOr$1wlsr3s as option_mapOr$1wlsr3s, mapOr$0emjeap as option_mapOr$0emjeap, mapOr$173j2gr as option_mapOr$173j2gr, mapOr$0smr0aj as option_mapOr$0smr0aj, filter$0wvwd70 } from "./core/option.ts"; import type * as Aimed from "./pointers.Aimed.ts"; import * as Bin from "./pointers.Bin.ts"; import type * as ByCall from "./pointers.ByCall.ts"; @@ -11,12 +11,15 @@ import * as ByClosure from "./pointers.ByClosure.ts"; import type * as ByObject from "./pointers.ByObject.ts"; import type * as Celled from "./pointers.Celled.ts"; import * as Counted from "./pointers.Counted.ts"; +import * as Drained from "./pointers.Drained.ts"; import type * as Duo from "./pointers.Duo.ts"; import type * as Either from "./pointers.Either.ts"; import type * as Enclosed from "./pointers.Enclosed.ts"; import type * as Gauge from "./pointers.Gauge.ts"; import type * as Halves from "./pointers.Halves.ts"; +import type * as Halves32 from "./pointers.Halves32.ts"; import * as Held from "./pointers.Held.ts"; +import type * as HeldCell from "./pointers.HeldCell.ts"; import type * as Holder from "./pointers.Holder.ts"; import * as Housed from "./pointers.Housed.ts"; import type * as Inner from "./pointers.Inner.ts"; @@ -28,6 +31,7 @@ import type * as Loaded from "./pointers.Loaded.ts"; import type * as Log from "./pointers.Log.ts"; import * as Named from "./pointers.Named.ts"; import type * as Node from "./pointers.Node.ts"; +import type * as Nothing from "./pointers.Nothing.ts"; import type * as Outer from "./pointers.Outer.ts"; import type * as Pair from "./pointers.Pair.ts"; import * as Paired from "./pointers.Paired.ts"; @@ -43,6 +47,7 @@ import type * as Span from "./pointers.Span.ts"; import type * as Spot from "./pointers.Spot.ts"; import type * as Stable from "./pointers.Stable.ts"; import type * as Swapped from "./pointers.Swapped.ts"; +import type * as TaggedVec from "./pointers.TaggedVec.ts"; import type * as To from "./pointers.To.ts"; import type * as Twin from "./pointers.Twin.ts"; import type * as Viewed from "./pointers.Viewed.ts"; @@ -53,13 +58,13 @@ import * as Wrapped from "./pointers.Wrapped.ts"; import type * as Written from "./pointers.Written.ts"; /** Put `node` in the list just before `at`. */ -export function attachBefore$String(node: Node.Node, at: Node.Node): void { - const _3: Links.Links = { next: at, prev: at.link.at4 }; - const _8: Node.Node = node; +export function attachBefore$Vec_u8(node: Node.Node, at: Node.Node): void { + const _3: Links.Links = { next: at, prev: at.link.at4 }; + const _8: Node.Node = node; _8.link.at0 = _3.next; _8.link.at4 = _3.prev; at.link.at4 = node; - (node.link.at4.link.at0 as Node.Node) = node; + (node.link.at4.link.at0 as Node.Node) = node; } /** Put `node` in the list just before `at`. */ @@ -72,9 +77,9 @@ export function attachBefore$sync_Arc_u8(node: Node.Node>, at: Nod (node.link.at4.link.at0 as Node.Node>) = node; } -export function detach$String(node: Node.Node): void { - (node.link.at4.link.at0 as Node.Node) = node.link.at0 as Node.Node; - (node.link.at0 as Node.Node).link.at4 = node.link.at4; +export function detach$Vec_u8(node: Node.Node): void { + (node.link.at4.link.at0 as Node.Node) = node.link.at0 as Node.Node; + (node.link.at0 as Node.Node).link.at4 = node.link.at4; } export function detach$sync_Arc_u8(node: Node.Node>): void { @@ -87,7 +92,7 @@ export function detach$sync_Arc_u8(node: Node.Node>): void { * finds and then the list in order. */ export function lists(dataData: Uint8Array, dataStart: number, dataLen: number, out: rs.ByteVec): void { - const list: List.List = List.new$String(); + const list: List.List = List.new$Vec_u8(); let dataData2: Uint8Array = dataData; let dataStart2: number = dataStart; let dataLen2: number = dataLen; @@ -105,36 +110,37 @@ export function lists(dataData: Uint8Array, dataStart: number, dataLen: number, const key2: rs.u8 = _13[1]; const _18: rs.u8 = rs.u8Rem(op2, 4); if (_18 === 0) { - const _26: rs.usize = rs.u8Rem(op2, 3) as rs.usize; - if (!(_26 < 3)) { - rs.panic("index out of bounds: the len is " + 3 + " but the index is " + _26); + const _22: rs.ByteSlice[] = [rs.byteSliceRange(rs.byteSliceFrom(Uint8Array.of()), 0), rs.byteSliceFrom(Uint8Array.of(118)), rs.byteSliceFrom(Uint8Array.of(118, 118))]; + const _29: rs.usize = rs.u8Rem(op2, 3) as rs.usize; + if (!(_29 < 3)) { + rs.panic("index out of bounds: the len is " + 3 + " but the index is " + _29); } - const _21: rs.String = rs.stringFrom(["", "v", "vv"][_26]); - List.pushBack$String(list, key2, _21); + const _21: rs.ByteVec = rs.byteSliceToByteVec(_22[_29]); + List.pushBack$Vec_u8(list, key2, _21); } else if (_18 === 1) { - const _30: [rs.u8, rs.String] | undefined = List.remove$String(list, key2); - if (_30 === undefined) { + const _33: [rs.u8, rs.ByteVec] | undefined = List.remove$Vec_u8(list, key2); + if (_33 === undefined) { rs.byteVecPush(out, 255); } else { - const k: rs.u8 = _30[0]; - const v: rs.String = _30[1]; + const k: rs.u8 = _33[0]; + const v: rs.ByteVec = _33[1]; rs.byteVecPush(out, k); - rs.byteVecExtendFromSlice(out, rs.stringAsBytes(v.v)); + rs.byteVecExtendFromSlice(out, rs.byteVecSlice(v)); } } else if (_18 === 2) { - rs.byteVecPush(out, List.toFront$String(list, key2) ? 1 : 0); + rs.byteVecPush(out, List.toFront$Vec_u8(list, key2) ? 1 : 0); } else { - const _42: rs.String | undefined = List.getMut$String(list, key2); - if (_42 === undefined) { + const _45: rs.ByteVec | undefined = List.getMut$Vec_u8(list, key2); + if (_45 === undefined) { rs.byteVecPush(out, 254); } else { - rs.stringPush(_42, 119); + rs.byteVecPush(_45, 119); } } } rs.byteVecPush(out, list.len & 0xff); - List.each$String(list, out); - drop$pointers_List_String(list); + List.each$Vec_u8(list, out); + drop$pointers_List_Vec_u8(list); } /** @@ -2847,13 +2853,13 @@ export function rawPlaces(data: rs.ByteSlice, out: rs.ByteVec): void { })) { shelf = second; } - rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of({ v: value }.v & 0xff, shelf._0.value & 0xff)), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(value & 0xff, shelf._0.value & 0xff)), undefined, true); let shelf2: Shelf.Shelf = first; const value2: rs.u32 = shelf2._0.value; if (rs.usizeRem(data.length, 3) === 0) { shelf2 = second; } - rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of({ v: value2 }.v & 0xff, shelf2._0.value & 0xff)), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(value2 & 0xff, shelf2._0.value & 0xff)), undefined, true); second[Symbol.dispose](); first[Symbol.dispose](); flag[Symbol.dispose](); @@ -2989,6 +2995,775 @@ export function lent(n: rs.u8): rs.ByteVec { return o; } +/** `v`'s elements, read out from both ends as `bits` picks, which nothing drops in `v`. */ +export function drained$Option_u16(v: rs.Vec, bits: rs.u8): rs.Vec { + let next!: rs.u16 | undefined | rs.None; + const v2: rs.Vec = v; + const iter: Drained.Drained = { iter: rs.iterSlice(v2.items) as rs.Iter> }; + const out: rs.Vec = new rs.Vec(); + let i: rs.i32 = 0; + while (true) { + if ((rs.u8Shr(bits, rs.i32Rem(i, 8)) & 1) === 0) { + next = rs.iterToOption(Drained.next$iter_Iterator_Option_u16(iter), rs.None); + } else { + next = Drained.nextBack$iter_DoubleEndedIterator_Option_u16(iter); + } + if (next !== rs.None) { + out.items.push(next); + i = rs.i32Add(i, 1); + } else { + rs.vecSetLen(v2, 0, 4, (): rs.u16 | undefined => { + return (undefined as unknown) as rs.u16 | undefined; + }); + return out; + } + } +} + +/** `v`'s elements, read out from both ends as `bits` picks, which nothing drops in `v`. */ +export function drained$Vec_u8(v: rs.Vec, bits: rs.u8): rs.Vec { + let next!: rs.ByteVec | undefined; + const v2: rs.Vec = v; + const iter: Drained.Drained = { iter: rs.iterSlice(v2.items) as rs.Iter> }; + const out: rs.Vec = new rs.Vec(); + let i: rs.i32 = 0; + while (true) { + if ((rs.u8Shr(bits, rs.i32Rem(i, 8)) & 1) === 0) { + next = rs.iterToOption(Drained.next$iter_Iterator_Vec_u8(iter)); + } else { + next = Drained.nextBack$iter_DoubleEndedIterator_Vec_u8(iter); + } + if (next !== undefined) { + out.items.push(next); + i = rs.i32Add(i, 1); + } else { + rs.vecSetLen(v2, 0, 12, (): rs.ByteVec => { + return (undefined as unknown) as rs.ByteVec; + }); + return out; + } + } +} + +/** `v`'s elements, read out from both ends as `bits` picks, which nothing drops in `v`. */ +export function drained$sync_Weak_u32(v: rs.Vec>, bits: rs.u8): rs.Vec> { + let next!: rs.Weak | undefined; + const v2: rs.Vec> = v; + const iter: Drained.Drained> = { iter: rs.iterSlice(v2.items) as rs.Iter>> }; + const out: rs.Vec> = new rs.Vec(); + let i: rs.i32 = 0; + while (true) { + if ((rs.u8Shr(bits, rs.i32Rem(i, 8)) & 1) === 0) { + next = rs.iterToOption(Drained.next$iter_Iterator_sync_Weak_u32(iter)); + } else { + next = Drained.nextBack$iter_DoubleEndedIterator_sync_Weak_u32(iter); + } + if (next !== undefined) { + out.items.push(next); + i = rs.i32Add(i, 1); + } else { + const v3: rs.Vec> = v2; + rs.vecSetLen(v3, 0, 4, (): rs.Weak => { + return (undefined as unknown) as rs.Weak; + }); + rs.dropVec(v3); + return out; + } + } +} + +/** `v`'s elements, read out from both ends as `bits` picks, which nothing drops in `v`. */ +export function drained$u32(v: rs.Vec, bits: rs.u8): rs.Vec { + let next!: rs.u32 | undefined; + const v2: rs.Vec = v; + const iter: Drained.Drained = { iter: rs.iterSlice(v2.items) as rs.Iter> }; + const out: rs.Vec = new rs.Vec(); + let i: rs.i32 = 0; + while (true) { + if ((rs.u8Shr(bits, rs.i32Rem(i, 8)) & 1) === 0) { + next = rs.iterToOption(Drained.next$iter_Iterator_u32(iter)); + } else { + next = Drained.nextBack$iter_DoubleEndedIterator_u32(iter); + } + if (next !== undefined) { + out.items.push(next); + i = rs.i32Add(i, 1); + } else { + rs.vecSetLen(v2, 0, 4, (): rs.u32 => { + return (undefined as unknown) as rs.u32; + }); + return out; + } + } +} + +/** + * What smallvec's `retain` is: each element lent as a `&mut`, which a pointer from it then + * drops or moves to the front. + */ +export function retained$Rc_pointers_Counted_pointers_behind_refs_closure7(v: rs.Vec>, keep: (a0: rs.Place>) => boolean): void { + let _10!: rs.usize; + const len: rs.usize = v.items.length; + const p: rs.Place> = rs.vecAsPtr(v, 4); + let write: rs.usize = 0; + let iterStart: rs.usize = 0; + const iterEnd: rs.usize = len; + while ((_10 = iterStart++) < iterEnd) { + const _15: rs.Place> = rs.ptrAdd(p, _10); + if (!keep(_15)) { + _15.v[Symbol.dispose](); + } else { + rs.ptrCopy(_15, rs.ptrAdd(p, write), 1); + write = rs.usizeAdd(write, 1); + } + } + rs.vecSetLen(v, write, 4, (): rs.Arc => { + return (undefined as unknown) as rs.Arc; + }); +} + +/** + * What smallvec's `retain` is: each element lent as a `&mut`, which a pointer from it then + * drops or moves to the front. + */ +export function retained$Vec_u8_pointers_behind_refs_closure5(v: rs.Vec, keep: (a0: rs.ByteVec) => boolean): void { + let _10!: rs.usize; + const len: rs.usize = v.items.length; + const p: rs.Place = rs.vecAsPtr(v, 12); + let write: rs.usize = 0; + let iterStart: rs.usize = 0; + const iterEnd: rs.usize = len; + while ((_10 = iterStart++) < iterEnd) { + const _15: rs.Place = rs.ptrAdd(p, _10); + if (keep(_15.v)) { + rs.ptrCopy(_15, rs.ptrAdd(p, write), 1, copy$Vec_u8); + write = rs.usizeAdd(write, 1); + } + } + rs.vecSetLen(v, write, 12, (): rs.ByteVec => { + return (undefined as unknown) as rs.ByteVec; + }); +} + +/** + * What smallvec's `retain` is: each element lent as a `&mut`, which a pointer from it then + * drops or moves to the front. + */ +export function retained$sync_Weak_u32_pointers_behind_refs_closure11(v: rs.Vec>, keep: (a0: rs.Weak) => boolean): void { + let _10!: rs.usize; + const len: rs.usize = v.items.length; + const p: rs.Place> = rs.vecAsPtr(v, 4); + let write: rs.usize = 0; + let iterStart: rs.usize = 0; + const iterEnd: rs.usize = len; + while ((_10 = iterStart++) < iterEnd) { + const _15: rs.Place> = rs.ptrAdd(p, _10); + if (!keep(_15.v)) { + _15.v[Symbol.dispose](); + } else { + rs.ptrCopy(_15, rs.ptrAdd(p, write), 1, copy$sync_Weak_u32); + write = rs.usizeAdd(write, 1); + } + } + rs.vecSetLen(v, write, 4, (): rs.Weak => { + return (undefined as unknown) as rs.Weak; + }); +} + +/** + * What smallvec's `retain` is: each element lent as a `&mut`, which a pointer from it then + * drops or moves to the front. + */ +export function retained$u32_pointers_behind_refs_closure3(v: rs.Vec, keep: (a0: rs.Place) => boolean): void { + let _10!: rs.usize; + const len: rs.usize = v.items.length; + const p: rs.Place = rs.vecAsPtr(v, 4); + let write: rs.usize = 0; + let iterStart: rs.usize = 0; + const iterEnd: rs.usize = len; + while ((_10 = iterStart++) < iterEnd) { + const _15: rs.Place = rs.ptrAdd(p, _10); + if (keep(_15)) { + rs.ptrCopy(_15, rs.ptrAdd(p, write), 1); + write = rs.usizeAdd(write, 1); + } + } + rs.vecSetLen(v, write, 4, (): rs.u32 => { + return (undefined as unknown) as rs.u32; + }); +} + +/** The length of the `Vec` a pointer from a reference the body is given points at. */ +export function vecLen(v: rs.ByteVec): rs.usize { + return v.length; +} + +/** + * `with` written whole through a pointer from a `&mut` the body is given, made each way + * std makes one, which its caller's `Vec` holds after. + */ +export function refilled(v: rs.ByteVec, with_: rs.ByteVec, how: rs.u8): void { + let p!: rs.Place; + const _5: rs.u8 = rs.u8Rem(how, 4); + if (_5 === 0) { + p = rs.placeIn(v); + } else if (_5 === 1) { + p = rs.placeIn(v); + } else if (_5 === 2) { + p = rs.placeIn(v); + } else { + p = rs.placeIn(v); + } + p.v = with_; + rs.byteVecPush(p.v, how); +} + +/** + * What the `Vec` a pointer from a `&mut` the body is given points at held, moved out + * each way std moves one out as `with` is written whole in its place, and then swapped + * with `w` through pointers from `&mut`s to each. + */ +export function replaced(v: rs.ByteVec, w: rs.ByteVec, with_: rs.ByteVec, how: rs.u8): rs.ByteVec { + let old!: rs.ByteVec; + const p: rs.Place = rs.placeIn(v); + const _7: rs.u8 = rs.u8Rem(how, 3); + if (_7 === 0) { + const pointer = p; + const old3 = copy$Vec_u8(pointer.v); + pointer.v = with_; + old = old3; + } else if (_7 === 1) { + const old2: rs.ByteVec = copy$Vec_u8(p.v); + p.v = with_; + old = old2; + } else { + let slot: rs.ByteVec = (undefined as unknown) as rs.ByteVec; + rs.ptrCopy(p, rs.place(() => slot, (value) => slot = value), 1, copy$Vec_u8); + p.v = with_; + old = slot; + } + const x = rs.placeIn(v); + const y = rs.placeIn(w); + const old4 = copy$Vec_u8(x.v); + x.v = copy$Vec_u8(y.v); + y.v = old4; + return old; +} + +/** + * The value a pointer from a shared reference the body is given points at, made each way + * std makes one. + */ +export function behind(r: rs.u32, how: rs.u8): rs.u32 { + let p!: rs.Place; + const _4: rs.u8 = rs.u8Rem(how, 3); + if (_4 === 0) { + p = { v: r }; + } else if (_4 === 1) { + p = { v: r }; + } else { + p = { v: r }; + } + return p.v; +} + +/** + * Whether the `Option` a pointer from a shared reference the body is given points at + * holds a cell, and the number in it, read through the pointer made each way std makes + * one. + */ +export function cellBehind(r: rs.Cell | undefined, how: rs.u8): rs.u32 { + let p!: rs.Place | undefined>; + if (rs.u8Rem(how, 2) === 0) { + p = { v: r }; + } else { + p = { v: r }; + } + const _6: rs.Cell | undefined = p.v; + if (_6 === undefined) { + return 0; + } + return rs.u32Add(_6.v, 1); +} + +/** + * Raw pointers from references: smallvec's `Drain` and `retain`, over a number, library + * types, and an `Option`; pointers from a reference the body borrowed, which move within + * the array it borrowed from; from references the body is given, to a number, to an + * `Option` of a cell, and to a `Vec`, read, written, moved out of, and swapped whole; and + * from references to an `Arc`'s value, measured against each other. + */ +export function behindRefs(data: rs.ByteSlice, out: rs.ByteVec): void { + let _151!: rs.usize; + let _191!: rs.u8; + const bits: rs.u8 = option_unwrapOr$1fr498u(rs.byteSliceFirst(data), 0); + const nums: rs.Vec = rs.iterToVec(rs.iterMap(rs.iterSlice(rs.byteSliceView(data)), (_2: rs.u8): rs.u32 => { + return rs.u32Mul(_2 as rs.u32, 3); + }), undefined, 4, true); + rs.iterExtendBytes(out, rs.iterMap(rs.iterSlice(drained$u32(rs.vecFrom(nums.items.slice()), bits).items), (_23: rs.u32): rs.u8 => { + return _23 & 0xff; + }), undefined, true); + const runs: rs.Vec = rs.iterToVec(rs.iterMap(rs.byteSliceChunks(data, 3), (a0: rs.ByteSlice): rs.ByteVec => { + return rs.byteSliceToByteVec(a0); + }), undefined, 12, true); + for (const r of drained$Vec_u8(rs.vecFrom(runs.items.map((a02: rs.ByteVec): rs.ByteVec => { + return rs.byteVecFrom(rs.byteVecView(a02)); + })), bits).items) { + rs.byteVecPush(out, r.length & 0xff); + rs.iterExtendBytes(out, rs.byteVecIntoIter(r), undefined, true); + } + for (const _40 of drained$Option_u16(rs.iterToVec(rs.iterMap(rs.iterSlice(rs.byteSliceView(data)), (_24: rs.u8): rs.u16 | undefined => { + const b3: rs.u8 = _24; + return thenSome$1gvhopg(rs.u8Rem(b3, 3) !== 0, b3 as rs.u16); + }), undefined, 4, true), bits).items) { + rs.iterExtendBytes(out, rs.iterSlice(option_mapOr$0smr0aj(_40, Uint8Array.of(255, 255), (a03: rs.u16): Uint8Array => { + return rs.u16ToBeBytes(a03); + })), undefined, true); + } + const nums2: rs.Vec = nums; + retained$u32_pointers_behind_refs_closure3(nums2, (n3: rs.Place): boolean => { + n3.v = rs.u32Add(n3.v, 1); + return rs.u32Rem(n3.v, 2) === 0; + }); + rs.iterExtendBytes(out, rs.iterMap(rs.iterSlice(nums2.items), (_26: rs.u32): rs.u8 => { + return _26 & 0xff; + }), undefined, true); + const runs2: rs.Vec = runs; + retained$Vec_u8_pointers_behind_refs_closure5(runs2, (r2: rs.ByteVec): boolean => { + rs.byteVecPush(r2, 7); + return rs.u8Rem(r2.data[rs.idx(r2, 0)], 2) === 0; + }); + rs.iterExtendBytes(out, rs.iterFlatten(rs.iterSlice(runs2.items), (value: rs.ByteVec) => rs.iterSlice(rs.byteVecView(value)), true), "next"); + const dropped: rs.Cell = new rs.Cell(0); + const counted: rs.Vec> = rs.iterToVec(rs.iterMap(rs.iterSlice(rs.byteSliceView(data)), (_27: rs.u8): rs.Arc => { + return new rs.Arc(new Counted.Counted(_27, dropped)); + }), undefined, 4, true); + retained$Rc_pointers_Counted_pointers_behind_refs_closure7(counted, (c: rs.Place>): boolean => { + return rs.u8Rem(c.v.value.id, 2) === 0; + }); + rs.byteVecPush(out, dropped.v); + rs.iterExtendBytes(out, rs.iterMap(rs.iterSlice(counted.items), (c2: rs.Arc): rs.u8 => { + return c2.value.id; + }), undefined, true); + rs.dropVec(counted); + rs.byteVecPush(out, dropped.v); + const arc: rs.Arc = new rs.Arc(bits as rs.u32); + const weaks: rs.Vec> = rs.iterToVec(rs.iterMap(rs.iterSlice(rs.byteSliceView(data)), (_28: rs.u8): rs.Weak => { + if (rs.u8Rem(_28, 2) === 0) { + return new rs.Weak(arc); + } + return new rs.Weak(); + }), undefined, 4, true); + for (const w of drained$sync_Weak_u32(rs.vecFrom(weaks.items.map((a04: rs.Weak): rs.Weak => { + return rs.weakClone(a04); + })), bits).items) { + rs.byteVecPush(out, option_mapOr$0emjeap(rs.weakUpgrade(w), 255, (n5: rs.Arc): rs.u8 => { + const _015: rs.u8 = n5.value & 0xff; + n5[Symbol.dispose](); + return _015; + })); + w[Symbol.dispose](); + } + const weaks2: rs.Vec> = weaks; + let i: rs.u8 = 0; + retained$sync_Weak_u32_pointers_behind_refs_closure11(weaks2, (w3: rs.Weak): boolean => { + i = rs.u8Add(i, 1); + if (rs.u8Rem(i, 3) === 0) { + const _84: rs.Weak = new rs.Weak(arc); + w3[Symbol.dispose](); + Object.assign(w3, _84); + } + return rs.u8Rem(i, 2) === 0; + }); + rs.iterExtendBytes(out, rs.iterMap(rs.iterSlice(weaks2.items), (w4: rs.Weak): rs.u8 => { + return option_mapOr$0emjeap(rs.weakUpgrade(w4), 255, (n6: rs.Arc): rs.u8 => { + const _018: rs.u8 = n6.value & 0xff; + n6[Symbol.dispose](); + return _018; + }); + }), undefined, true); + rs.byteVecPush(out, arc.weak & 0xff); + rs.dropVec(weaks2); + rs.byteVecPush(out, arc.weak & 0xff); + const nums3: rs.Vec = rs.iterToVec(rs.iterMap(rs.iterSlice(rs.byteSliceView(data)), (_29: rs.u8): rs.u32 => { + return _29 as rs.u32; + }), undefined, 4, true); + if (rs.sliceFrom(nums3.items).length >= 2) { + const first: rs.Place = rs.arrayAsPtr(nums3.items, rs.idx(nums3.items, 0)); + rs.byteVecPush(out, rs.ptrOffsetFrom(rs.arrayAsPtr(nums3.items, rs.idx(nums3.items, rs.usizeSub(nums3.items.length, 1))), first) & 0xff); + const _148: rs.usize = nums3.items.length; + let iterStart: rs.usize = 0; + const iterEnd: rs.usize = _148; + while ((_151 = iterStart++) < iterEnd) { + rs.byteVecPush(out, rs.ptrAdd(first, _151).v & 0xff); + } + rs.byteVecPush(out, rs.ptrAdd(rs.arrayAsPtr(nums3.items, rs.idx(nums3.items, 1)), -1).v & 0xff); + rs.ptrAdd(rs.arrayAsPtr(nums3.items, rs.idx(nums3.items, 1)), -1).v = 9; + rs.byteVecPush(out, nums3.items[rs.idx(nums3.items, 0)] & 0xff); + } + const v: rs.ByteVec = rs.byteSliceToByteVec(data); + rs.byteVecPush(out, vecLen(v) & 0xff); + const _182: rs.ByteVec = rs.iterToByteVec(rs.iterRev(rs.iterSlice(rs.byteSliceView(data))), undefined, true); + refilled(v, _182, bits); + rs.iterExtendBytes(out, rs.iterSlice(rs.byteVecView(v)), undefined, true); + let iterStart2: rs.u8 = 0; + const iterEnd2: rs.u8 = 3; + while ((_191 = iterStart2++) < iterEnd2) { + const how: rs.u8 = _191; + const w2: rs.ByteVec = rs.iterToByteVec(rs.iterMap(rs.iterSlice(rs.byteSliceView(data)), (b6: rs.u8): rs.u8 => { + return b6 ^ how; + }), undefined, true); + const _203: rs.ByteVec = rs.byteVecOf(bits, rs.usizeAdd(how as rs.usize, 1)); + rs.iterExtendBytes(out, rs.iterSlice(rs.byteVecView(replaced(v, w2, _203, how))), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(rs.byteVecView(v)), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(rs.byteVecView(w2)), undefined, true); + rs.byteVecPush(out, 255); + } + const iter6: rs.Iter<[rs.usize, rs.u8]> = rs.iterEnumerate(rs.iterSlice(rs.byteSliceView(data))); + while (true) { + const _218: [rs.usize, rs.u8] | rs.Done = iter6.next(); + if (_218 === rs.DONE) { + break; + } + const i3: rs.usize = _218[0]; + const b: rs.u8 = _218[1]; + rs.byteVecPush(out, behind(b as rs.u32, i3 & 0xff) & 0xff); + rs.byteVecPush(out, cellBehind(then$01qpu7s(rs.u8Rem(b, 3) !== 0, (): rs.Cell => { + return new rs.Cell(b as rs.u32); + }), b) & 0xff); + } + const arc2: rs.Arc = new rs.Arc(bits as rs.u32); + const p: rs.Place = rs.arcRaw(arc2, rs.arcPlace(arc2), "Arc"); + const q: rs.Place = rs.arcRaw(arc2, rs.arcPlace(arc2), "Arc"); + rs.byteVecPush(out, rs.ptrOffsetFrom(p, q) & 0xff); + rs.byteVecPush(out, q.v & 0xff); + rs.byteVecPush(out, rs.ptrOffsetFrom(rs.arcRaw(arc2, rs.arcPlace(arc2), "Arc"), p) & 0xff); + rs.byteVecPush(out, from$convert_From_ref_pointers_Halves32_ptr_NonNull_u32({ first: bits as rs.u32, second: ((bits as rs.u32) ^ 85) >>> 0 }).v & 0xff); + let cell2: rs.Cell = new rs.Cell(bits as rs.u32); + const atCell: rs.Place> = rs.place(() => cell2, (value) => cell2 = value); + const _276: rs.u32 = vecLen(v) as rs.u32; + const _277: rs.Place = atCell.v; + _277.v = rs.u32Add(_277.v, _276); + rs.byteVecPush(out, cell2.v & 0xff); + let lone: rs.u32 = bits as rs.u32; + const at: rs.Place = rs.place(() => lone, (value2) => lone = value2); + let end: rs.Place = rs.NULL; + end = rs.ptrAdd(at, 1); + rs.byteVecPush(out, rs.ptrOffsetFrom(end, at) & 0xff); + const _296: Nothing.Nothing = {}; + rs.byteVecPush(out, nothingTag(_296, bits)); + arc2[Symbol.dispose](); + arc[Symbol.dispose](); +} + +/** `bits`, given beside a `Nothing` borrowed through a pointer moved off one. */ +export function nothingTag(_nothing: Nothing.Nothing, bits: rs.u8): rs.u8 { + return bits ^ 60; +} + +export function from$convert_From_ref_pointers_Halves32_ptr_NonNull_u32(halves: Halves32.Halves32): rs.Place { + return rs.place(() => halves.second, (value) => halves.second = value); +} + +/** + * `with` written whole into the `Vec` `r` is, in place, dropping nothing, as a body that + * can't see where it is kept writes it. + */ +export function refillIn(r: rs.ByteVec, with_: rs.ByteVec): void { + rs.placeIn(r).v = with_; +} + +/** + * What the `Vec` `v` held, moved out through a pointer made each way `how` picks: from a + * shared reborrow, cast to one to a `MaybeUninit` or a `ManuallyDrop` of it, or swapped + * through such a cast with another; then `with` is written whole into `v` in place. + */ +export function movedVec(v: rs.ByteVec, with_: rs.ByteVec, how: rs.u8): rs.ByteVec { + let old!: rs.ByteVec; + const _5: rs.u8 = rs.u8Rem(how, 4); + if (_5 === 0) { + old = copy$Vec_u8(v); + } else if (_5 === 1) { + old = copy$mem_MaybeUninit_Vec_u8(rs.placeIn(v).v); + } else if (_5 === 2) { + old = copy$Vec_u8(rs.placeIn(v).v); + } else { + let other: rs.ByteVec = new rs.ByteVec(); + const x = rs.placeIn(v); + const y = rs.place(() => other, (value) => other = value); + const old2 = copy$mem_MaybeUninit_Vec_u8(x.v); + x.v = copy$mem_MaybeUninit_Vec_u8(y.v); + y.v = old2; + old = other; + } + refillIn(v, with_); + return old; +} + +/** + * What an `Option`, a tuple, and a struct each holding a `Vec` held, moved out through + * pointers from `&mut`s to them: read, or copied, replaced, and swapped, as `how` picks; + * then `with` is written whole into each `Vec` they hold, in place. + */ +export function movedNested(o: rs.Place, t: [rs.ByteVec, rs.u8], s: TaggedVec.TaggedVec, with_: rs.ByteSlice, how: rs.u8): [rs.ByteVec | undefined, [rs.ByteVec, rs.u8], TaggedVec.TaggedVec] { + let old!: [rs.ByteVec | undefined, [rs.ByteVec, rs.u8], TaggedVec.TaggedVec]; + if (rs.u8Rem(how, 2) === 0) { + old = [copy$Option_Vec_u8(o.v), copy$tuple2_Vec_u8_u8(t), copy$pointers_TaggedVec(s)] as [rs.ByteVec | undefined, [rs.ByteVec, rs.u8], TaggedVec.TaggedVec]; + } else { + let slot: rs.ByteVec | undefined = (undefined as unknown) as rs.ByteVec | undefined; + rs.ptrCopy(o, rs.place(() => slot, (value) => slot = value), 1, copy$Option_Vec_u8); + const _22: [rs.ByteVec, rs.u8] = t; + const _23: [rs.ByteVec, rs.u8] = [new rs.ByteVec(), how]; + const pointer = _22; + const old2 = rs.objectCopy(pointer); + rs.ptrWrite(pointer, _23); + const oldT: [rs.ByteVec, rs.u8] = old2; + const oldS: TaggedVec.TaggedVec = { v: new rs.ByteVec(), tag: how }; + const x = s; + const y = oldS; + const old3 = rs.objectCopy(x); + rs.ptrWrite(x, rs.objectCopy(y)); + rs.ptrWrite(y, old3); + old = [slot, oldT, oldS] as [rs.ByteVec | undefined, [rs.ByteVec, rs.u8], TaggedVec.TaggedVec]; + } + if (o.v !== undefined) { + const _37: rs.ByteVec = rs.byteSliceToByteVec(with_); + refillIn(o.v, _37); + } + const _40: rs.ByteVec = rs.byteSliceToByteVec(with_); + refillIn(t[0], _40); + const _43: rs.ByteVec = rs.byteSliceToByteVec(with_); + refillIn(s.v, _43); + return old; +} + +/** + * The `Option` of a cell a shared reference points at, moved out through a pointer from + * it, whose cell is then set. + */ +export function cellMoved(r: rs.Cell | undefined): rs.Cell | undefined { + const old: rs.Cell | undefined = copy$Option_Cell_u32(r); + if (old !== undefined) { + rs.cellSet(old, rs.u32Add(old.v, 5)); + return old; + } + return old; +} + +/** + * The `RefCell` and the `OnceCell` of a cell that shared references point at, moved out + * through pointers from them, whose cells, held inline, are then set. + */ +export function inlineMoved(r: rs.RefCell>, o: rs.OnceCell>): [rs.RefCell>, rs.OnceCell>] { + const old: [rs.RefCell>, rs.OnceCell>] = [copy$cell_RefCell_Cell_u32(rs.placeIn(r).v), copy$cell_OnceCell_Cell_u32(rs.placeIn(o).v)]; + const cell: rs.RefCell> = rs.refCellBorrow(old[0]); + rs.cellSet(cell.value, rs.u32Add(cell.value.v, 9)); + rs.refRelease(cell); + const _19: rs.Cell | undefined = old[1].v; + if (_19 !== undefined) { + rs.cellSet(_19, rs.u32Add(_19.v, 9)); + return old; + } + return old; +} + +/** + * A cell written whole through a pointer from the shared reference given, which may be + * the cell `Cell::from_mut` makes of another place. + */ +export function cellRewritten(r: rs.Cell, n: rs.u32): void { + const pointer = rs.placeIn(r); + const value = new rs.Cell(n); + if (pointer.v === undefined) { + pointer.v = value; + } else { + pointer.v.v = value.v; + } + undefined; +} + +/** + * A `HashMap` dropped in place and written whole through a pointer from the `&mut` given, + * which holds what the new one does, its capacity included. + */ +export function mapRewritten(r: rs.HashMap): void { + const p: rs.Place> = rs.placeIn(r); + undefined; + p.v = rs.hashNew() as rs.HashMap; +} + +/** + * What the uninitialized `HeldCell` a shared reference points at holds, moved out through + * a pointer from it. + */ +export function uninitMoved(r: HeldCell.HeldCell): HeldCell.HeldCell { + return copy$mem_MaybeUninit_pointers_HeldCell(r); +} + +/** + * A range of cells and a cell `Cell::from_mut` made of a variable's place, moved out + * through pointers from shared references to them, whose cells are then set. + */ +export function endsMoved(r: rs.Range>, c: rs.Cell): [rs.Range>, rs.Cell] { + const old: [rs.Range>, rs.Cell] = [copy$ops_Range_Cell_u32(rs.placeIn(r).v), new rs.Cell(rs.placeIn(c).v.v)]; + rs.cellSet(old[0].start, rs.u32Add(old[0].start.v, 9)); + rs.cellSet(old[0].end, rs.u32Add(old[0].end.v, 9)); + rs.cellSet(old[1], rs.u32Add(old[1].v, 9)); + return old; +} + +/** + * Values holding a library object, moved out through pointers made each way, at any + * depth, and then written in place: what was moved out keeps what it held. + */ +export function movedOut(data: rs.ByteSlice, out: rs.ByteVec): void { + let _7!: rs.u8; + let _28!: rs.u8; + const with_: (a0: rs.u8) => rs.ByteVec = (n: rs.u8): rs.ByteVec => { + return rs.iterToByteVec(rs.iterMap(rs.iterSlice(rs.byteSliceView(data)), (b3: rs.u8): rs.u8 => { + return rs.u8WrappingAdd(b3, n); + }), undefined, true); + }; + let iterStart: rs.u8 = 0; + const iterEnd: rs.u8 = 4; + while ((_7 = iterStart++) < iterEnd) { + const how: rs.u8 = _7; + const v: rs.ByteVec = rs.byteSliceToByteVec(data); + const _14: rs.ByteVec = with_(rs.u8Add(how, 1)); + rs.iterExtendBytes(out, rs.iterSlice(rs.byteVecView(movedVec(v, _14, how))), undefined, true); + rs.byteVecPush(out, 254); + rs.iterExtendBytes(out, rs.iterSlice(rs.byteVecView(v)), undefined, true); + rs.byteVecPush(out, 255); + } + let iterStart2: rs.u8 = 0; + const iterEnd2: rs.u8 = 2; + while ((_28 = iterStart2++) < iterEnd2) { + const how2: rs.u8 = _28; + let o: rs.ByteVec | undefined = option_map$1px2rk4(rs.byteSliceFirst(data), (_2: rs.u8): rs.ByteVec => { + return rs.byteSliceToByteVec(data); + }); + const t: [rs.ByteVec, rs.u8] = [rs.byteSliceToByteVec(data), how2]; + const s: TaggedVec.TaggedVec = { v: rs.byteSliceToByteVec(data), tag: how2 }; + const _46: rs.ByteSlice = rs.byteVecSlice(with_(9)); + const _42: [rs.ByteVec | undefined, [rs.ByteVec, rs.u8], TaggedVec.TaggedVec] = movedNested(rs.place(() => o, (value) => o = value), t, s, _46, how2); + const oldO: rs.ByteVec | undefined = _42[0]; + const oldT: [rs.ByteVec, rs.u8] = _42[1]; + const oldS: TaggedVec.TaggedVec = _42[2]; + const iter3: rs.Iter = rs.iterFlatten(rs.iterSlice([oldO, oldT[0], oldS.v, o, t[0], s.v]), (value: rs.ByteVec | undefined) => rs.iterOption(value), true, undefined, true); + while (true) { + const _68: rs.ByteVec | rs.Done = iter3.next(); + if (_68 === rs.DONE) { + break; + } + rs.iterExtendBytes(out, rs.iterSlice(rs.byteVecView(_68)), undefined, true); + rs.byteVecPush(out, 254); + } + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(oldT[1], oldS.tag, t[1], s.tag)), undefined, true); + } + for (const b of rs.byteSliceView(data)) { + const cell: rs.Cell | undefined = then$01qpu7s(rs.u8Rem(b, 3) !== 0, (): rs.Cell => { + return new rs.Cell(b as rs.u32); + }); + const old2: rs.Cell | undefined = cellMoved(cell); + const iter5: rs.Iter | undefined> = rs.iterSlice([cell, old2]); + while (true) { + const _99: rs.Cell | undefined | rs.Done = iter5.next(); + if (_99 === rs.DONE) { + break; + } + rs.byteVecPush(out, option_mapOr$18adzvj(_99, 0, (a0: rs.Cell): rs.u32 => { + return a0.v; + }) & 0xff); + } + const r: rs.RefCell> = new rs.RefCell(new rs.Cell(b as rs.u32)); + const o2: rs.OnceCell> = new rs.OnceCell(undefined); + if (rs.u8Rem(b, 2) === 0) { + const _116: rs.Cell = new rs.Cell(rs.u32Add(b as rs.u32, 1)); + rs.onceSet(o2, _116); + } + const _122: [rs.RefCell>, rs.OnceCell>] = inlineMoved(r, o2); + const oldR: rs.RefCell> = _122[0]; + const oldO2: rs.OnceCell> = _122[1]; + const iter6: rs.Iter>> = rs.iterSlice([r, oldR]); + while (true) { + const _130: rs.RefCell> | rs.Done = iter6.next(); + if (_130 === rs.DONE) { + break; + } + const _139: rs.RefCell> = rs.refCellBorrow(_130); + rs.byteVecPush(out, _139.value.v & 0xff); + rs.refRelease(_139); + } + const iter7: rs.Iter>> = rs.iterSlice([o2, oldO2]); + while (true) { + const _145: rs.OnceCell> | rs.Done = iter7.next(); + if (_145 === rs.DONE) { + break; + } + rs.byteVecPush(out, option_mapOr$18adzvj(_145.v, 0, (a02: rs.Cell): rs.u32 => { + return a02.v; + }) & 0xff); + } + const r2: rs.Range> = new rs.Range(new rs.Cell(b as rs.u32), new rs.Cell(rs.u32Add(b as rs.u32, 1))); + let x: rs.u32 = rs.u32Add(b as rs.u32, 2); + const _167: rs.Cell = rs.cellOfPlace(rs.place(() => x, (value2) => x = value2)); + const _165: [rs.Range>, rs.Cell] = endsMoved(r2, _167); + const oldR2: rs.Range> = _165[0]; + const oldC: rs.Cell = _165[1]; + const iter8: rs.Iter>> = rs.iterSlice([r2, oldR2]); + while (true) { + const _174: rs.Range> | rs.Done = iter8.next(); + if (_174 === rs.DONE) { + break; + } + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(_174.start.v & 0xff, _174.end.v & 0xff)), undefined, true); + } + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(oldC.v & 0xff, x & 0xff)), undefined, true); + } + const map: rs.HashMap = rs.hashWithCapacity(rs.usizeMul(data.length, 8), 117440512) as rs.HashMap; + const _199: rs.Iter<[rs.u32, rs.u32]> = rs.iterMap(rs.iterSlice(rs.byteSliceView(data)), (_23: rs.u8): [rs.u32, rs.u32] => { + const b4: rs.u8 = _23; + return [b4 as rs.u32, b4 as rs.u32] as [rs.u32, rs.u32]; + }); + rs.hashExtend(map, _199, undefined, undefined, undefined, 117440512); + mapRewritten(map); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(rs.hashLen(map) & 0xff, rs.hashCapacity(map) & 0xff)), undefined, true); + for (const b2 of rs.byteSliceView(data)) { + let x2: rs.u32 = b2 as rs.u32; + cellRewritten(rs.cellOfPlace(rs.place(() => x2, (value3) => x2 = value3)), rs.u32Add(b2 as rs.u32, 1)); + const cell2: rs.Cell = new rs.Cell(b2 as rs.u32); + cellRewritten(cell2, rs.u32Add(b2 as rs.u32, 2)); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(x2 & 0xff, cell2.v & 0xff)), undefined, true); + } + const held: HeldCell.HeldCell = ({ c: new rs.Cell(undefined) } as unknown) as HeldCell.HeldCell; + const old3: HeldCell.HeldCell = uninitMoved(held); + const iter10: rs.Iter<[rs.usize, HeldCell.HeldCell]> = rs.iterEnumerate(rs.iterSlice([held, old3])); + while (true) { + const _249: [rs.usize, HeldCell.HeldCell] | rs.Done = iter10.next(); + if (_249 === rs.DONE) { + break; + } + const i: rs.usize = _249[0]; + const _259: rs.u32 = option_unwrapOr$1fr498u(rs.byteSliceFirst(data), 0) as rs.u32; + const object = _249[1]; + const value4 = { c: new rs.Cell(new rs.RefCell(rs.u32Add(_259, i as rs.u32))) }; + const cell3 = object.c; + rs.ptrWrite(object, value4); + if (cell3 !== undefined) { + object.c = cell3; + cell3.v = value4.c.v; + } + } + const iter11: rs.Iter = rs.iterSlice([held, old3]); + while (true) { + const _270: HeldCell.HeldCell | rs.Done = iter11.next(); + if (_270 === rs.DONE) { + break; + } + rs.byteVecPush(out, rs.valuePlace(_270.c.v).v & 0xff); + } +} + /** * Pointers to slices cast to ones to arrays of their elements, of bytes and of numbers: * read whole, borrowed, and written through, at every offset; compared with the arrays @@ -3219,7 +3994,7 @@ export function rawCells(data: rs.ByteSlice, out: rs.ByteVec): void { const _128: [rs.u8, rs.u16] = s.pair.v; _128[0] = _128[0] ^ n; const _130: rs.u32 = n as rs.u32; - const _131: rs.Place = { v: slot }.v; + const _131: rs.Place = slot; _131.v = (_131.v ^ _130) >>> 0; rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(num.v).reverse()), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.u32ToBeBytes(slots.num.v).reverse()), undefined, true); @@ -4103,17 +4878,17 @@ export function arrayParts(data: rs.ByteSlice, out: rs.ByteVec): void { } const _25: rs.u32[] = [rs.u32Add(n, 1), rs.u32Add(n, 2)]; const pointer2 = p; - const old2 = rs.sliceCopy(pointer2); + const old3 = rs.sliceCopy(pointer2); rs.sliceSet(pointer2, _25); - rs.iterExtendBytes(out, rs.iterMap(rs.iterSlice(old2), (_24: rs.u32): rs.u8 => { + rs.iterExtendBytes(out, rs.iterMap(rs.iterSlice(old3), (_24: rs.u32): rs.u8 => { return _24 & 0xff; }), undefined, true); const q: rs.Slice = rs.optionUnwrap(rs.sliceGetRange(rs.sliceRange(rs.sliceFrom(words.items), 3), 0, 2)); const x = rs.optionUnwrap(rs.sliceGetRange(rs.sliceRange(rs.sliceFrom(words.items), 1), 0, 2)); const y = q; - const old3 = rs.sliceCopy(x); + const old4 = rs.sliceCopy(x); rs.sliceSet(x, rs.sliceCopy(y)); - rs.sliceSet(y, old3); + rs.sliceSet(y, old4); const nn: rs.Slice = rs.optionUnwrap(rs.sliceGetRange(rs.sliceRange(rs.sliceFrom(words.items), 2), 0, 2)); const _54: rs.u32 = rs.u32Add(n, 3); const _55: rs.Slice = nn; @@ -4134,8 +4909,8 @@ export function arrayParts(data: rs.ByteSlice, out: rs.ByteVec): void { rs.iterExtendBytes(out, rs.iterSlice(rs.bytesMap([rs.sliceFrom(fewer).length, rs.iterCount(rs.iterSlice(more)), rs.sliceFrom(none2).length], (n2: rs.usize): rs.u8 => { return n2 & 0xff; })), undefined, true); - rs.iterExtendBytes(out, rs.iterMap(rs.iterSlice(words.items), (_211: rs.u32): rs.u8 => { - return _211 & 0xff; + rs.iterExtendBytes(out, rs.iterMap(rs.iterSlice(words.items), (_212: rs.u32): rs.u8 => { + return _212 & 0xff; }), undefined, true); const rows: rs.Vec = rs.iterToVec(rs.iterMap(rs.sliceChunks(rs.sliceFrom(words.items), 2), (c: rs.Slice): rs.u32[] => { rs.idx(c, 0); @@ -4157,8 +4932,8 @@ export function arrayParts(data: rs.ByteSlice, out: rs.ByteVec): void { const _127: rs.u32 = rs.u32Add(n, 5); const slice8 = rs.arraySliceAt(nn2, 0); slice8.data[slice8.start + 1] = _127; - rs.iterExtendBytes(out, rs.iterMap(rs.iterFlatten(rs.arraySliceIter(rs.arraySliceFrom(rows.items, rs.sliceFrom)), (value: rs.Slice) => rs.iterSlice(rs.sliceItems(value)), true, 2), (_212: rs.u32): rs.u8 => { - return _212 & 0xff; + rs.iterExtendBytes(out, rs.iterMap(rs.iterFlatten(rs.arraySliceIter(rs.arraySliceFrom(rows.items, rs.sliceFrom)), (value: rs.Slice) => rs.iterSlice(rs.sliceItems(value)), true, 2), (_215: rs.u32): rs.u8 => { + return _215 & 0xff; }), undefined, true); const pointer5 = rs.byteSliceFrom(Uint8Array.of(n & 0xff, 5)); rs.iterExtendBytes(out, rs.iterSlice(option_mapOr$0m14xib(pointer5 === rs.NULL ? rs.None : rs.byteSliceView(pointer5), rs.bytes(2), (m: Uint8Array): Uint8Array => { @@ -4178,6 +4953,20 @@ export function arrayParts(data: rs.ByteSlice, out: rs.ByteVec): void { rs.iterExtendBytes(out, rs.iterSlice(rs.bytesMap(uc.v, (w5: rs.u32): rs.u8 => { return w5 & 0xff; })), undefined, true); + const _184: [() => rs.u32, () => rs.u32] = [(): rs.u32 => { + return 7; + }, (): rs.u32 => { + return 8; + }]; + const seven: () => rs.u32 = _184[0]; + const eight: () => rs.u32 = _184[1]; + const lazies: rs.LazyCell[][] = [[new rs.LazyCell(seven)]]; + const _191: rs.Cell[][]> = rs.cellOfArraySlice(rs.arraySliceFrom(lazies, rs.sliceFrom), copy$cell_LazyCell_u32); + const _193: rs.LazyCell[][] = [[new rs.LazyCell(eight)]]; + const pointer6 = rs.cellArray(_191, rs.arraySliceOf(rs.sliceFrom)); + const old5 = rs.arraySliceCopy(pointer6, copy$cell_LazyCell_u32); + rs.arraySliceSet(pointer6, _193); + rs.iterExtendBytes(out, rs.iterSlice(Uint8Array.of(old5[0][0].force() & 0xff, lazies[0][0].force() & 0xff, n & 0xff)), undefined, true); } //# sourceMappingURL=pointers.ts.map diff --git a/ts/fixtures/lang/pointers/src/lib.rs b/ts/fixtures/lang/pointers/src/lib.rs index 52f681baf..6598c5db3 100644 --- a/ts/fixtures/lang/pointers/src/lib.rs +++ b/ts/fixtures/lang/pointers/src/lib.rs @@ -49,6 +49,7 @@ fn cases() -> Vec<(&'static str, Case)> { ("pointers::raw_arcs", |_, data, out| pointers::raw_arcs(data, out)), ("pointers::raw_places", |_, data, out| pointers::raw_places(data, out)), ("pointers::arrays", |_, data, out| pointers::arrays(data, out)), + ("pointers::behind_refs", |_, data, out| pointers::behind_refs(data, out)), ("pointers::array_casts", |_, data, out| pointers::array_casts(data, out)), ("pointers::generic_unions", |_, data, out| { pointers::generic_unions(data, out) @@ -59,6 +60,7 @@ fn cases() -> Vec<(&'static str, Case)> { pointers::nested_cells(data, out) }), ("pointers::lone_bytes", |_, data, out| pointers::lone_bytes(data, out)), + ("pointers::moved_out", |_, data, out| pointers::moved_out(data, out)), ("pointers::array_parts", |_, data, out| pointers::array_parts(data, out)), ] } diff --git a/ts/fixtures/lang/pointers/src/pointers.rs b/ts/fixtures/lang/pointers/src/pointers.rs index 637f7ec4a..a5b9b71a7 100644 --- a/ts/fixtures/lang/pointers/src/pointers.rs +++ b/ts/fixtures/lang/pointers/src/pointers.rs @@ -11,8 +11,9 @@ //! raw pointers of an `Arc` and an `Rc`, counted and taken back; and unions over a type //! parameter, each instance laid out at its own offsets. -use std::cell::{Cell, UnsafeCell}; +use std::cell::{Cell, OnceCell, RefCell, UnsafeCell}; use std::mem::{self, MaybeUninit}; +use std::ops; use std::ptr::{self, NonNull}; use std::rc::Rc; use std::sync::Arc; @@ -152,7 +153,7 @@ impl List { } } -impl List { +impl List> { fn each(&self, out: &mut Vec) { let Some(guard) = self.guard else { return; @@ -161,7 +162,7 @@ impl List { while at != guard { let (key, value) = unsafe { at.as_ref().entry.assume_init_ref() }; out.push(*key); - out.extend_from_slice(value.as_bytes()); + out.extend_from_slice(value); at = unsafe { at.as_ref().link.value.next }; } } @@ -226,23 +227,23 @@ impl Drop for List { /// Steps on a list of the operation and key each pair of bytes picks, putting what each /// finds and then the list in order. pub fn lists(data: &[u8], out: &mut Vec) { - let mut list = List::::new(); + let mut list = List::>::new(); let mut data = data; while let [op, key, rest @ ..] = data { data = rest; let (op, key) = (*op, *key % 8); match op % 4 { - 0 => list.push_back(key, String::from(["", "v", "vv"][usize::from(op % 3)])), + 0 => list.push_back(key, [&b""[..], b"v", b"vv"][usize::from(op % 3)].to_vec()), 1 => match list.remove(key) { Some((k, v)) => { out.push(k); - out.extend_from_slice(v.as_bytes()); + out.extend_from_slice(&v); } None => out.push(0xff), }, 2 => out.push(list.to_front(key) as u8), _ => match list.get_mut(key) { - Some(value) => value.push('w'), + Some(value) => value.push(b'w'), None => out.push(0xfe), }, } @@ -2414,6 +2415,504 @@ struct Holder { arr: [u32; 2], } +/// What smallvec's `Drain` is: the elements of a slice nothing will drop again, each read +/// out through the reference its iterator lends. +struct Drained<'a, T> { + iter: std::slice::Iter<'a, T>, +} + +impl Iterator for Drained<'_, T> { + type Item = T; + + fn next(&mut self) -> Option { + self.iter.next().map(|r| unsafe { ptr::read(r) }) + } +} + +impl DoubleEndedIterator for Drained<'_, T> { + fn next_back(&mut self) -> Option { + self.iter.next_back().map(|r| unsafe { ptr::read(r) }) + } +} + +/// `v`'s elements, read out from both ends as `bits` picks, which nothing drops in `v`. +fn drained(v: Vec, bits: u8) -> Vec { + let v = mem::ManuallyDrop::new(v); + let mut iter = Drained { iter: v.iter() }; + let mut out = Vec::new(); + let mut i = 0; + loop { + let next = match bits >> (i % 8) & 1 { + 0 => iter.next(), + _ => iter.next_back(), + }; + let Some(next) = next else { break }; + out.push(next); + i += 1; + } + // Its elements moved out, its room is freed without dropping them again. + let mut v = mem::ManuallyDrop::into_inner(v); + unsafe { v.set_len(0) }; + out +} + +/// What smallvec's `retain` is: each element lent as a `&mut`, which a pointer from it then +/// drops or moves to the front. +fn retained(v: &mut Vec, mut keep: impl FnMut(&mut T) -> bool) { + let len = v.len(); + let p = v.as_mut_ptr(); + let mut write = 0; + for read in 0..len { + let cur = unsafe { &mut *p.add(read) }; + if !keep(cur) { + unsafe { ptr::drop_in_place(cur) }; + } else { + unsafe { ptr::copy(cur, p.add(write), 1) }; + write += 1; + } + } + // What is past the length, dropped or moved, which nothing drops again. + unsafe { v.set_len(write) }; +} + +/// The length of the `Vec` a pointer from a reference the body is given points at. +fn vec_len(v: &Vec) -> usize { + let p = &raw const *v; + unsafe { (*p).len() } +} + +/// `with` written whole through a pointer from a `&mut` the body is given, made each way +/// std makes one, which its caller's `Vec` holds after. +fn refilled(v: &mut Vec, with: Vec, how: u8) { + let p = match how % 4 { + 0 => v as *mut Vec, + 1 => ptr::from_mut(v), + 2 => NonNull::from(v).as_ptr(), + _ => Into::>>::into(v).as_ptr(), + }; + unsafe { *p = with }; + unsafe { (*p).push(how) }; +} + +/// What the `Vec` a pointer from a `&mut` the body is given points at held, moved out +/// each way std moves one out as `with` is written whole in its place, and then swapped +/// with `w` through pointers from `&mut`s to each. +fn replaced(v: &mut Vec, w: &mut Vec, with: Vec, how: u8) -> Vec { + let p = ptr::from_mut(v); + let old = match how % 3 { + 0 => unsafe { ptr::replace(p, with) }, + 1 => { + let old = unsafe { ptr::read(p) }; + unsafe { ptr::write(p, with) }; + old + } + _ => { + let mut slot = MaybeUninit::>::uninit(); + unsafe { ptr::copy_nonoverlapping(p, slot.as_mut_ptr(), 1) }; + unsafe { ptr::write(p, with) }; + unsafe { slot.assume_init() } + } + }; + unsafe { ptr::swap(ptr::from_mut(v), ptr::from_mut(w)) }; + old +} + +/// The value a pointer from a shared reference the body is given points at, made each way +/// std makes one. +fn behind(r: &u32, how: u8) -> u32 { + let p = match how % 3 { + 0 => r as *const u32, + 1 => ptr::from_ref(r), + _ => NonNull::from(r).as_ptr(), + }; + unsafe { ptr::read(p) } +} + +/// Whether the `Option` a pointer from a shared reference the body is given points at +/// holds a cell, and the number in it, read through the pointer made each way std makes +/// one. +fn cell_behind(r: &Option>, how: u8) -> u32 { + let p = match how % 2 { + 0 => &raw const *r, + _ => ptr::from_ref(r), + }; + match unsafe { &*p } { + Some(cell) => cell.get() + 1, + None => 0, + } +} + +/// Raw pointers from references: smallvec's `Drain` and `retain`, over a number, library +/// types, and an `Option`; pointers from a reference the body borrowed, which move within +/// the array it borrowed from; from references the body is given, to a number, to an +/// `Option` of a cell, and to a `Vec`, read, written, moved out of, and swapped whole; and +/// from references to an `Arc`'s value, measured against each other. +pub fn behind_refs(data: &[u8], out: &mut Vec) { + let bits = data.first().copied().unwrap_or(0); + let nums: Vec = data.iter().map(|&b| u32::from(b) * 3).collect(); + out.extend(drained(nums.clone(), bits).iter().map(|&n| n as u8)); + let runs: Vec> = data.chunks(3).map(<[u8]>::to_vec).collect(); + for r in drained(runs.clone(), bits) { + out.push(r.len() as u8); + out.extend(r); + } + let opts: Vec> = data.iter().map(|&b| (b % 3 != 0).then_some(u16::from(b))).collect(); + for o in drained(opts, bits) { + out.extend(o.map_or([0xff, 0xff], u16::to_be_bytes)); + } + + let mut nums = nums; + retained(&mut nums, |n| { + *n += 1; + *n % 2 == 0 + }); + out.extend(nums.iter().map(|&n| n as u8)); + let mut runs = runs; + retained(&mut runs, |r| { + r.push(7); + r[0] % 2 == 0 + }); + out.extend(runs.iter().flatten()); + // Each element it drops, dropped once. + let dropped = Cell::new(0); + let mut counted: Vec> = data + .iter() + .map(|&id| Rc::new(Counted { id, dropped: &dropped })) + .collect(); + retained(&mut counted, |c| c.id % 2 == 0); + out.push(dropped.get()); + out.extend(counted.iter().map(|c| c.id)); + drop(counted); + out.push(dropped.get()); + // A `Weak` holds only its pointer to what it shares, which a read out copies as is, with + // one written whole in place. + let arc = Arc::new(u32::from(bits)); + let weaks: Vec> = data + .iter() + .map(|&b| match b % 2 { + 0 => Arc::downgrade(&arc), + _ => std::sync::Weak::new(), + }) + .collect(); + for w in drained(weaks.clone(), bits) { + out.push(w.upgrade().map_or(0xff, |n| *n as u8)); + } + let mut weaks = weaks; + let mut i = 0u8; + retained(&mut weaks, |w| { + i += 1; + if i % 3 == 0 { + *w = Arc::downgrade(&arc); + } + i % 2 == 0 + }); + out.extend(weaks.iter().map(|w| w.upgrade().map_or(0xff, |n| *n as u8))); + out.push(Arc::weak_count(&arc) as u8); + drop(weaks); + out.push(Arc::weak_count(&arc) as u8); + + // From a reference the body borrowed, where the element is. + let mut nums: Vec = data.iter().map(|&b| u32::from(b)).collect(); + if let [_, _, ..] = nums[..] { + let first = &nums[0] as *const u32; + let last = &nums[nums.len() - 1] as *const u32; + out.push(unsafe { last.offset_from(first) } as u8); + for i in 0..nums.len() { + out.push(unsafe { *first.add(i) } as u8); + } + let second = ptr::from_ref(&nums[1]); + out.push(unsafe { *second.sub(1) } as u8); + let m = &mut nums[1] as *mut u32; + unsafe { *m.sub(1) = 9 }; + out.push(nums[0] as u8); + } + + let mut v = data.to_vec(); + out.push(vec_len(&v) as u8); + refilled(&mut v, data.iter().rev().copied().collect(), bits); + out.extend(&v); + for how in 0..3 { + let mut w: Vec = data.iter().map(|b| b ^ how).collect(); + let old = replaced(&mut v, &mut w, vec![bits; usize::from(how) + 1], how); + out.extend(&old); + out.extend(&v); + out.extend(&w); + out.push(0xff); + } + for (i, &b) in data.iter().enumerate() { + out.push(behind(&u32::from(b), i as u8) as u8); + let cell = (b % 3 != 0).then(|| Cell::new(u32::from(b))); + out.push(cell_behind(&cell, b) as u8); + } + // Pointers from references to an `Arc`'s value, which is its one place. + let arc = Arc::new(u32::from(bits)); + let p = ptr::from_ref(&*arc); + let q = ptr::from_ref(&*arc); + out.push(unsafe { p.offset_from(q) } as u8); + out.push(unsafe { *q } as u8); + let n: NonNull = (&*arc).into(); + out.push(unsafe { n.as_ptr().offset_from(p) } as u8); + // A conversion of the crate's own, which runs as written. + let halves = Halves32 { + first: u32::from(bits), + second: u32::from(bits) ^ 0x55, + }; + let second: NonNull = (&halves).into(); + out.push(unsafe { *second.as_ptr() } as u8); + // A pointer to a cell from a shared reference to it, read and written in place by + // `UnsafeCell::raw_get` after a call between. + let cell = UnsafeCell::new(u32::from(bits)); + let shared = &cell; + let at_cell: *const UnsafeCell = shared; + let len = vec_len(&v); + unsafe { *UnsafeCell::raw_get(at_cell) += len as u32 }; + out.push(unsafe { *cell.get() } as u8); + // A pointer one past a variable, stored whole through a pointer to a pointer, and one + // moved off a value of no bytes and borrowed: valid, as neither reads past its value. + let lone = u32::from(bits); + let at = &raw const lone; + let mut end = ptr::null::(); + unsafe { ptr::write(&raw mut end, at.add(1)) }; + out.push(unsafe { end.offset_from(at) } as u8); + let none = Nothing; + let past = unsafe { (&raw const none).add(1) }; + out.push(nothing_tag(unsafe { &*past }, bits)); +} + +/// A value of no bytes, whose every pointer is where it was. +struct Nothing; + +/// `bits`, given beside a `Nothing` borrowed through a pointer moved off one. +#[inline(never)] +fn nothing_tag(nothing: &Nothing, bits: u8) -> u8 { + let Nothing = nothing; + bits ^ 0x3c +} + +/// Two numbers, the second of which a pointer from a reference to them points at. +struct Halves32 { + first: u32, + second: u32, +} + +impl From<&Halves32> for NonNull { + fn from(halves: &Halves32) -> Self { + let _ = halves.first; + NonNull::from(&halves.second) + } +} + +/// `with` written whole into the `Vec` `r` is, in place, dropping nothing, as a body that +/// can't see where it is kept writes it. +#[inline(never)] +fn refill_in(r: &mut Vec, with: Vec) { + unsafe { ptr::write(r, with) }; +} + +/// What the `Vec` `v` held, moved out through a pointer made each way `how` picks: from a +/// shared reborrow, cast to one to a `MaybeUninit` or a `ManuallyDrop` of it, or swapped +/// through such a cast with another; then `with` is written whole into `v` in place. +fn moved_vec(v: &mut Vec, with: Vec, how: u8) -> Vec { + let old = match how % 4 { + 0 => unsafe { ptr::read(&*v as *const Vec) }, + 1 => unsafe { ptr::read(ptr::from_mut(v).cast::>>()).assume_init() }, + 2 => mem::ManuallyDrop::into_inner(unsafe { ptr::read(ptr::from_mut(v).cast::>>()) }), + _ => { + let mut other = MaybeUninit::new(Vec::new()); + unsafe { ptr::swap(ptr::from_mut(v).cast::>>(), &mut other) }; + unsafe { other.assume_init() } + } + }; + refill_in(v, with); + old +} + +/// A `Vec` with a tag, which a pointer to one moves out whole. +struct TaggedVec { + v: Vec, + tag: u8, +} + +/// What an `Option`, a tuple, and a struct each holding a `Vec` held, moved out through +/// pointers from `&mut`s to them: read, or copied, replaced, and swapped, as `how` picks; +/// then `with` is written whole into each `Vec` they hold, in place. +fn moved_nested( + o: &mut Option>, + t: &mut (Vec, u8), + s: &mut TaggedVec, + with: &[u8], + how: u8, +) -> (Option>, (Vec, u8), TaggedVec) { + let old = match how % 2 { + 0 => unsafe { (ptr::read(o), ptr::read(t), ptr::read(s)) }, + _ => unsafe { + let mut slot = MaybeUninit::uninit(); + ptr::copy_nonoverlapping(ptr::from_mut(o), slot.as_mut_ptr(), 1); + let old_t = ptr::replace(ptr::from_mut(t), (Vec::new(), how)); + let mut old_s = TaggedVec { + v: Vec::new(), + tag: how, + }; + ptr::swap(ptr::from_mut(s), &mut old_s); + (slot.assume_init(), old_t, old_s) + }, + }; + if let Some(v) = o { + refill_in(v, with.to_vec()); + } + refill_in(&mut t.0, with.to_vec()); + refill_in(&mut s.v, with.to_vec()); + old +} + +/// The `Option` of a cell a shared reference points at, moved out through a pointer from +/// it, whose cell is then set. +fn cell_moved(r: &Option>) -> Option> { + let old = unsafe { ptr::read(r) }; + if let Some(cell) = &old { + cell.set(cell.get() + 5); + } + old +} + +/// The `RefCell` and the `OnceCell` of a cell that shared references point at, moved out +/// through pointers from them, whose cells, held inline, are then set. +fn inline_moved(r: &RefCell>, o: &OnceCell>) -> (RefCell>, OnceCell>) { + let old = unsafe { (ptr::read(r), ptr::read(o)) }; + let cell = old.0.borrow(); + cell.set(cell.get() + 9); + drop(cell); + if let Some(cell) = old.1.get() { + cell.set(cell.get() + 9); + } + old +} + +/// A cell written whole through a pointer from the shared reference given, which may be +/// the cell `Cell::from_mut` makes of another place. +#[inline(never)] +fn cell_rewritten(r: &Cell, n: u32) { + unsafe { ptr::from_ref(r).cast_mut().write(Cell::new(n)) } +} + +/// A `HashMap` dropped in place and written whole through a pointer from the `&mut` given, +/// which holds what the new one does, its capacity included. +#[inline(never)] +fn map_rewritten(r: &mut std::collections::HashMap) { + let p = ptr::from_mut(r); + unsafe { + ptr::drop_in_place(p); + ptr::write(p, std::collections::HashMap::new()); + } +} + +/// A cell of a `RefCell`, which an uninitialized shell holds nothing in. +struct HeldCell { + c: UnsafeCell>, +} + +/// What the uninitialized `HeldCell` a shared reference points at holds, moved out through +/// a pointer from it. +#[inline(never)] +fn uninit_moved(r: &MaybeUninit) -> MaybeUninit { + unsafe { ptr::read(r) } +} + +/// A range of cells and a cell `Cell::from_mut` made of a variable's place, moved out +/// through pointers from shared references to them, whose cells are then set. +fn ends_moved(r: &ops::Range>, c: &Cell) -> (ops::Range>, Cell) { + let old = unsafe { (ptr::read(r), ptr::read(c)) }; + old.0.start.set(old.0.start.get() + 9); + old.0.end.set(old.0.end.get() + 9); + old.1.set(old.1.get() + 9); + old +} + +/// Values holding a library object, moved out through pointers made each way, at any +/// depth, and then written in place: what was moved out keeps what it held. +pub fn moved_out(data: &[u8], out: &mut Vec) { + let with = |n: u8| -> Vec { data.iter().map(|b| b.wrapping_add(n)).collect() }; + for how in 0..4 { + let mut v = data.to_vec(); + let old = moved_vec(&mut v, with(how + 1), how); + out.extend(&old); + out.push(0xfe); + out.extend(&v); + out.push(0xff); + } + for how in 0..2 { + let mut o = data.first().map(|_| data.to_vec()); + let mut t = (data.to_vec(), how); + let mut s = TaggedVec { + v: data.to_vec(), + tag: how, + }; + let (old_o, old_t, old_s) = moved_nested(&mut o, &mut t, &mut s, &with(9), how); + for v in [ + old_o.as_ref(), + Some(&old_t.0), + Some(&old_s.v), + o.as_ref(), + Some(&t.0), + Some(&s.v), + ] + .into_iter() + .flatten() + { + out.extend(v); + out.push(0xfe); + } + out.extend([old_t.1, old_s.tag, t.1, s.tag]); + } + for &b in data { + let cell = (b % 3 != 0).then(|| Cell::new(u32::from(b))); + let old = cell_moved(&cell); + for c in [&cell, &old] { + out.push(c.as_ref().map_or(0, Cell::get) as u8); + } + let r = RefCell::new(Cell::new(u32::from(b))); + let o = OnceCell::new(); + if b % 2 == 0 { + let _ = o.set(Cell::new(u32::from(b) + 1)); + } + let (old_r, old_o) = inline_moved(&r, &o); + for r in [&r, &old_r] { + out.push(r.borrow().get() as u8); + } + for o in [&o, &old_o] { + out.push(o.get().map_or(0, Cell::get) as u8); + } + let r = Cell::new(u32::from(b))..Cell::new(u32::from(b) + 1); + let mut x = u32::from(b) + 2; + let (old_r, old_c) = ends_moved(&r, Cell::from_mut(&mut x)); + for r in [&r, &old_r] { + out.extend([r.start.get() as u8, r.end.get() as u8]); + } + out.extend([old_c.get() as u8, x as u8]); + } + let mut map = std::collections::HashMap::::with_capacity(data.len() * 8); + map.extend(data.iter().map(|&b| (u32::from(b), u32::from(b)))); + map_rewritten(&mut map); + out.extend([map.len() as u8, map.capacity() as u8]); + for &b in data { + let mut x = u32::from(b); + cell_rewritten(Cell::from_mut(&mut x), u32::from(b) + 1); + let cell = Cell::new(u32::from(b)); + cell_rewritten(&cell, u32::from(b) + 2); + out.extend([x as u8, cell.get() as u8]); + } + let mut held = Box::::new_uninit(); + let mut old = uninit_moved(&held); + for (i, h) in [&mut *held, &mut old].into_iter().enumerate() { + h.write(HeldCell { + c: UnsafeCell::new(RefCell::new(u32::from(data.first().copied().unwrap_or(0)) + i as u32)), + }); + } + for h in [&mut *held, &mut old] { + out.push(*unsafe { h.assume_init_mut() }.c.get_mut().get_mut() as u8); + } +} + /// Pointers to slices cast to ones to arrays of their elements, of bytes and of numbers: /// read whole, borrowed, and written through, at every offset; compared with the arrays /// std's safe casts give; cast past where the slice ends and read only where its elements @@ -3093,4 +3592,9 @@ pub fn array_parts(data: &[u8], out: &mut Vec) { unsafe { uc.get_mut().assume_init_mut()[0] = n + 8 }; unsafe { uc.as_mut_unchecked().assume_init_mut()[1] = n + 9 }; out.extend(unsafe { uc.into_inner().assume_init() }.map(|w| w as u8)); + // Lazy cells in arrays a `Cell` replaces, moved out safely and forced. + let (seven, eight): (fn() -> u32, fn() -> u32) = (|| 7, || 8); + let mut lazies = [[std::cell::LazyCell::new(seven)]]; + let old = Cell::from_mut(&mut lazies).replace([[std::cell::LazyCell::new(eight)]]); + out.extend([*old[0][0] as u8, *lazies[0][0] as u8, n as u8]); } diff --git a/ts/fixtures/lang/slices/expected/lib.ts b/ts/fixtures/lang/slices/expected/lib.ts index e894c911a..fbda4852a 100644 --- a/ts/fixtures/lang/slices/expected/lib.ts +++ b/ts/fixtures/lang/slices/expected/lib.ts @@ -13,6 +13,14 @@ export function copy$Option_array2_u8(v: Uint8Array | undefined): Uint8Array | u return v === undefined ? v : copy$array_u8(v); } +export function copy$String(v: rs.String): rs.String { + return rs.objectCopy(v); +} + +export function copy$Vec_u8(v: rs.ByteVec): rs.ByteVec { + return rs.objectCopy(v); +} + export function copy$array_array2_array2_u8(v: Uint8Array[][]): Uint8Array[][] { return v.map(copy$array_array2_u8); } diff --git a/ts/fixtures/lang/slices/expected/slices.ts b/ts/fixtures/lang/slices/expected/slices.ts index ab1bb1ea1..e10da0658 100644 --- a/ts/fixtures/lang/slices/expected/slices.ts +++ b/ts/fixtures/lang/slices/expected/slices.ts @@ -1,7 +1,7 @@ // Generated by rust2-ts. Do not edit. import * as rs from "./rs.ts"; -import { copy$Option_array2_u16, copy$Option_array2_u8, copy$array_array2_array2_u8, copy$array_array2_u16, copy$array_array2_u8, copy$array_u16, copy$array_u32, copy$array_u8, copy$mem_MaybeUninit_array_u8, copy$slices_Pair, drop$tuple2_Vec_slices_Noisy_Vec_u8 } from "./lib.ts"; +import { copy$Option_array2_u16, copy$Option_array2_u8, copy$Vec_u8, copy$array_array2_array2_u8, copy$array_array2_u16, copy$array_array2_u8, copy$array_u16, copy$array_u32, copy$array_u8, copy$mem_MaybeUninit_array_u8, copy$slices_Pair, drop$tuple2_Vec_slices_Noisy_Vec_u8 } from "./lib.ts"; import { thenSome$1gvhopg } from "./core/bool.ts"; import { isSomeAnd$1ouf3e6, unwrapOr$1la41a6 as option_unwrapOr$1la41a6, unwrapOr$1fr498u as option_unwrapOr$1fr498u, unwrapOrDefault$1n9qa5p as option_unwrapOrDefault$1n9qa5p, unwrapOrDefault$0h81s8g as option_unwrapOrDefault$0h81s8g, map$13tab2s as option_map$13tab2s, map$0cigbgc as option_map$0cigbgc, map$0duedp1 as option_map$0duedp1, map$0q6fk7x as option_map$0q6fk7x, map$1uud0v7 as option_map$1uud0v7, map$1j0y4j0 as option_map$1j0y4j0, map$0vk4zok as option_map$0vk4zok, map$1aqm6sp as option_map$1aqm6sp, map$04ry0e1 as option_map$04ry0e1, mapOr$135q041 as option_mapOr$135q041, mapOr$17z0l8m as option_mapOr$17z0l8m, or$0as8tia as option_or$0as8tia, zip$0wdt346, clone$1omlcob as option_clone$1omlcob } from "./core/option.ts"; import * as Counted from "./slices.Counted.ts"; @@ -2966,10 +2966,10 @@ export function arrayForms(kind: rs.u8, data: rs.ByteSlice, out: rs.ByteVec): vo rs.idx(runs2, 0); const _354: rs.ByteVec[] = [rs.byteVecFrom(Uint8Array.of(byte(1), byte(2)))]; rs.iterExtendBytes(out, rs.iterSlice(rs.byteVecView(rs.sliceReplaceAll(rs.arraySliceAt(runs2, 0), _354, 1)[0])), undefined, true); - const _367: rs.Cell = rs.cellOfArraySlice(rs.arraySliceOne(rs.sliceOfPlace(rs.place(() => item, (value2) => item = value2)), 1), rs.objectCopy); + const _367: rs.Cell = rs.cellOfArraySlice(rs.arraySliceOne(rs.sliceOfPlace(rs.place(() => item, (value2) => item = value2)), 1), copy$Vec_u8); const _371: rs.ByteVec[][] = [[rs.byteVecFrom(Uint8Array.of(byte(3)))]]; const pointer = rs.cellArray(_367, rs.arraySliceOf(rs.sliceFrom)); - const old4 = rs.arraySliceCopy(pointer, rs.objectCopy); + const old4 = rs.arraySliceCopy(pointer, copy$Vec_u8); rs.arraySliceSet(pointer, _371); rs.iterExtendBytes(out, rs.iterSlice(rs.byteVecView(old4[0][0])), undefined, true); rs.iterExtendBytes(out, rs.iterSlice(rs.byteVecView(item)), undefined, true); diff --git a/ts/fixtures/reject/census.txt b/ts/fixtures/reject/census.txt index dd50d5ed5..f9a4ec104 100644 --- a/ts/fixtures/reject/census.txt +++ b/ts/fixtures/reject/census.txt @@ -8,6 +8,9 @@ fixtures/reject/src/alloc.rs:allocated+4:3: a byte of memory from `alloc`, which fixtures/reject/src/alloc.rs:allocated+5:11: a byte of memory from `alloc`, which JS keeps as the values a cast of its pointer types it as fixtures/reject/src/alloc.rs:arrays_boxed+4:22: `Box::from_raw` of `[[u32; 2]; 2]`, which may be arrays `as_chunks` split from one flat array, which is not supported yet fixtures/reject/src/alloc.rs:arrays_boxed+6:23: `Box::from_raw` of `[alloc::Only; 2]`, a value of no bytes made of more than structs and tuples +fixtures/reject/src/alloc.rs:beside_reference+2:11: a read or write reaching past a value in no array, which JS keeps apart from what holds it in Rust +fixtures/reject/src/alloc.rs:beside_reference+2:32: a read or write reaching past a value in no array, which JS keeps apart from what holds it in Rust +fixtures/reject/src/alloc.rs:beside_reference+2:44: a slice of a length the body doesn't fix, from a value in no array, which JS keeps apart from what holds it in Rust fixtures/reject/src/alloc.rs:bytes_as_values+3:3: a byte of memory from `alloc`, which JS keeps as the values a cast of its pointer types it as fixtures/reject/src/alloc.rs:bytes_as_values+4:15: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+5:16: a cast from a pointer to `u8` to one to `pointers::Halves`, which reads the bytes of one type as another @@ -19,7 +22,6 @@ fixtures/reject/src/alloc.rs:cell_of_objects+2:10: `UnsafeCell::from_mut` of `st fixtures/reject/src/alloc.rs:cell_of_objects+3:10: `UnsafeCell::from_mut` of `std::pin::Pin>`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address fixtures/reject/src/alloc.rs:cell_of_objects+7:15: `Cell::from_mut` of `std::pin::Pin>`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address fixtures/reject/src/alloc.rs:cell_of_objects+8:34: `array::from_mut` of `std::pin::Pin>`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address -fixtures/reject/src/alloc.rs:cell_pointer_kept+2:45: a raw pointer to `std::cell::UnsafeCell` behind a reference, which is the object and not where it is kept fixtures/reject/src/alloc.rs:cell_pointer_reassigned+2:10: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there fixtures/reject/src/alloc.rs:cell_raw_reassigned+4:19: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there fixtures/reject/src/alloc.rs:cell_raw_reassigned+6:19: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there @@ -31,9 +33,28 @@ fixtures/reject/src/alloc.rs:cell_raw_unknown+5:15: `UnsafeCell::raw_get` of a p fixtures/reject/src/alloc.rs:cell_raw_unknown+6:3: `UnsafeCell::raw_get` of a pointer not made of a place of `std::cell::UnsafeCell`, such as a parameter, a vector's or a slice's, or one moved by arithmetic, which may point where no cell is fixtures/reject/src/alloc.rs:cell_raw_unknown+8:15: `UnsafeCell::raw_get` of a pointer not made of a place of `std::cell::UnsafeCell`, such as a parameter, a vector's or a slice's, or one moved by arithmetic, which may point where no cell is fixtures/reject/src/alloc.rs:cell_raw_unknown+9:13: `UnsafeCell::raw_get` of a pointer not made of a place of `std::cell::UnsafeCell`, such as a parameter, a vector's or a slice's, or one moved by arithmetic, which may point where no cell is +fixtures/reject/src/alloc.rs:closure_moved+2:12: a read moving out a `{closure@fixtures/reject/src/alloc.rs:closure_moved+11:12: +11:19}`, a closure or coroutine whose state JS keeps in the function itself, which no copy splits from the original as Rust's read does +fixtures/reject/src/alloc.rs:closure_moved+2:12: a read moving out a `{closure@fixtures/reject/src/alloc.rs:closure_moved+15:13: +15:20}`, a closure or coroutine whose state JS keeps in the function itself, which no copy splits from the original as Rust's read does +fixtures/reject/src/alloc.rs:closure_moved+2:12: a read moving out a `{closure@fixtures/reject/src/alloc.rs:closure_moved+5:18: +5:25}`, a closure or coroutine whose state JS keeps in the function itself, which no copy splits from the original as Rust's read does +fixtures/reject/src/alloc.rs:closure_moved+2:27: a raw pointer to `{closure@fixtures/reject/src/alloc.rs:closure_moved+11:12: +11:19}` behind a reference, which JS can't write a whole value into in place +fixtures/reject/src/alloc.rs:collection_moved+2:23: a read moving out a `std::vec::Vec`, a collection whose JS object keeps its length with its storage, which no copy splits from the original as Rust's read does +fixtures/reject/src/alloc.rs:collection_moved+4:23: a read moving out a `std::collections::VecDeque`, a collection whose JS object keeps its length with its storage, which no copy splits from the original as Rust's read does +fixtures/reject/src/alloc.rs:collection_moved+10:3: a read moving out a `std::string::String`, a collection whose JS object keeps its length with its storage, which no copy splits from the original as Rust's read does +fixtures/reject/src/alloc.rs:collection_moved+14:28: a read moving out a `(std::string::String, u8)`, which holds a `std::string::String`, a collection whose JS object keeps its length with its storage, which no copy splits from the original as Rust's read does +fixtures/reject/src/alloc.rs:collection_moved+16:23: a read moving out a `std::vec::Vec`, a collection whose JS object keeps its length with its storage, which no copy splits from the original as Rust's read does +fixtures/reject/src/alloc.rs:infallible_cell+1:11: a raw pointer to `std::result::Result, !>` behind a reference, which JS can't write a whole value into in place +fixtures/reject/src/alloc.rs:lazy_moved+2:22: a read moving out a `std::cell::LazyCell`, a library object whose inline state only its runtime class knows, which no copy splits from the original as Rust's read does +fixtures/reject/src/alloc.rs:past_reference+1:30: a pointer reaching past a value in no array, which JS keeps apart from what holds it in Rust +fixtures/reject/src/alloc.rs:peeked_moved+3:26: a read moving out a `std::iter::Peekable>>`, a library object whose inline state only its runtime class knows, which no copy splits from the original as Rust's read does fixtures/reject/src/alloc.rs:pinned_lent+1:32: `UnsafeCell::get_mut` of `std::pin::Pin>`, an object a whole write through the reference changes in place, so a value read out before would be a copy at another address fixtures/reject/src/alloc.rs:pinned_lent+2:43: `UnsafeCell::as_mut_unchecked` of `std::pin::Pin>`, an object a whole write through the reference changes in place, so a value read out before would be a copy at another address +fixtures/reject/src/alloc.rs:recast_reference+4:11: a read or write reaching past a value in no array, which JS keeps apart from what holds it in Rust +fixtures/reject/src/alloc.rs:recast_reference+4:48: a read or write reaching past a value in no array, which JS keeps apart from what holds it in Rust +fixtures/reject/src/alloc.rs:recast_reference+4:76: a read or write reaching past a value in no array, which JS keeps apart from what holds it in Rust fixtures/reject/src/alloc.rs:reinterpreted+1:27: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another +fixtures/reject/src/alloc.rs:unfixed_reference+3:35: a pointer moved by an offset the body doesn't fix, from a value in no array, which JS keeps apart from what holds it in Rust +fixtures/reject/src/alloc.rs:unfixed_reference+4:35: a pointer moved by an offset the body doesn't fix, from a value in no array, which JS keeps apart from what holds it in Rust +fixtures/reject/src/alloc.rs:unfixed_reference+5:3: a copy of a count the body doesn't fix, from a value in no array, which JS keeps apart from what holds it in Rust fixtures/reject/src/alloc.rs:uninit_array_lent+1:25: a reborrow of `std::mem::MaybeUninit<[u32; 2]>` through a raw pointer, which may point into part of an array with no JS array of its own to lend, which is not supported yet fixtures/reject/src/alloc.rs:uninit_array_lent+2:59: a reborrow of `std::mem::MaybeUninit<[u32; 2]>` through a raw pointer, which may point into part of an array with no JS array of its own to lend, which is not supported yet fixtures/reject/src/alloc.rs:uninit_array_lent+3:25: a reborrow of `std::mem::MaybeUninit<[u32; 2]>` through a raw pointer, which may point into part of an array with no JS array of its own to lend, which is not supported yet @@ -41,7 +62,6 @@ fixtures/reject/src/alloc.rs:uninit_array_lent+4:59: a reborrow of `std::mem::Ma fixtures/reject/src/alloc.rs:uninit_array_lent+5:23: a reborrow of `std::mem::MaybeUninit<[u32; 2]>` through a raw pointer, which may point into part of an array with no JS array of its own to lend, which is not supported yet fixtures/reject/src/alloc.rs:uninit_array_lent+6:23: a reborrow of `std::mem::MaybeUninit<[u32; 2]>` through a raw pointer, which may point into part of an array with no JS array of its own to lend, which is not supported yet fixtures/reject/src/alloc.rs:uninit_cell_lent+2:17: `MaybeUninit::assume_init_mut` of `std::cell::UnsafeCell`, whose mutable reference is the object, but the uninitialized value's is the place it is kept in -fixtures/reject/src/alloc.rs:vec_pointer+1:10: a raw pointer to `std::vec::Vec` behind a reference, which is the object and not where it is kept fixtures/reject/src/alloc.rs:wrapped_cells+8:19: a whole write of `std::mem::MaybeUninit<(std::cell::UnsafeCell, u32)>`, which would put another cell in place of one a `MaybeUninit` or `ManuallyDrop` holds fixtures/reject/src/alloc.rs:wrapped_cells+9:22: a whole write of `std::mem::ManuallyDrop>`, which would put another cell in place of one a `MaybeUninit` or `ManuallyDrop` holds fixtures/reject/src/alloc.rs:wrapped_cells+10:3: a whole write of `(std::mem::MaybeUninit>, u32)`, which would put another cell in place of one a `MaybeUninit` or `ManuallyDrop` holds diff --git a/ts/fixtures/reject/expected.txt b/ts/fixtures/reject/expected.txt index 8230089d2..ff32bc79b 100644 --- a/ts/fixtures/reject/expected.txt +++ b/ts/fixtures/reject/expected.txt @@ -12,6 +12,9 @@ fixtures/reject/src/alloc.rs:allocated+4:3: a byte of memory from `alloc`, which fixtures/reject/src/alloc.rs:allocated+5:11: a byte of memory from `alloc`, which JS keeps as the values a cast of its pointer types it as fixtures/reject/src/alloc.rs:arrays_boxed+4:22: `Box::from_raw` of `[[u32; 2]; 2]`, which may be arrays `as_chunks` split from one flat array, which is not supported yet fixtures/reject/src/alloc.rs:arrays_boxed+6:23: `Box::from_raw` of `[alloc::Only; 2]`, a value of no bytes made of more than structs and tuples +fixtures/reject/src/alloc.rs:beside_reference+2:11: a read or write reaching past a value in no array, which JS keeps apart from what holds it in Rust +fixtures/reject/src/alloc.rs:beside_reference+2:32: a read or write reaching past a value in no array, which JS keeps apart from what holds it in Rust +fixtures/reject/src/alloc.rs:beside_reference+2:44: a slice of a length the body doesn't fix, from a value in no array, which JS keeps apart from what holds it in Rust fixtures/reject/src/alloc.rs:bytes_as_values+3:3: a byte of memory from `alloc`, which JS keeps as the values a cast of its pointer types it as fixtures/reject/src/alloc.rs:bytes_as_values+4:15: a cast from a pointer to `u8` to one to `u32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:bytes_as_values+5:16: a cast from a pointer to `u8` to one to `pointers::Halves`, which reads the bytes of one type as another @@ -23,7 +26,6 @@ fixtures/reject/src/alloc.rs:cell_of_objects+2:10: `UnsafeCell::from_mut` of `st fixtures/reject/src/alloc.rs:cell_of_objects+3:10: `UnsafeCell::from_mut` of `std::pin::Pin>`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address fixtures/reject/src/alloc.rs:cell_of_objects+7:15: `Cell::from_mut` of `std::pin::Pin>`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address fixtures/reject/src/alloc.rs:cell_of_objects+8:34: `array::from_mut` of `std::pin::Pin>`, an object a whole write through it changes in place, so a value read out before would change too or be a copy at another address -fixtures/reject/src/alloc.rs:cell_pointer_kept+2:45: a raw pointer to `std::cell::UnsafeCell` behind a reference, which is the object and not where it is kept fixtures/reject/src/alloc.rs:cell_pointer_reassigned+2:10: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there fixtures/reject/src/alloc.rs:cell_raw_reassigned+4:19: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there fixtures/reject/src/alloc.rs:cell_raw_reassigned+6:19: a raw pointer to `std::cell::UnsafeCell` in a variable assigned again while it may point there @@ -35,9 +37,28 @@ fixtures/reject/src/alloc.rs:cell_raw_unknown+5:15: `UnsafeCell::raw_get` of a p fixtures/reject/src/alloc.rs:cell_raw_unknown+6:3: `UnsafeCell::raw_get` of a pointer not made of a place of `std::cell::UnsafeCell`, such as a parameter, a vector's or a slice's, or one moved by arithmetic, which may point where no cell is fixtures/reject/src/alloc.rs:cell_raw_unknown+8:15: `UnsafeCell::raw_get` of a pointer not made of a place of `std::cell::UnsafeCell`, such as a parameter, a vector's or a slice's, or one moved by arithmetic, which may point where no cell is fixtures/reject/src/alloc.rs:cell_raw_unknown+9:13: `UnsafeCell::raw_get` of a pointer not made of a place of `std::cell::UnsafeCell`, such as a parameter, a vector's or a slice's, or one moved by arithmetic, which may point where no cell is +fixtures/reject/src/alloc.rs:closure_moved+2:12: a read moving out a `{closure@fixtures/reject/src/alloc.rs:closure_moved+11:12: +11:19}`, a closure or coroutine whose state JS keeps in the function itself, which no copy splits from the original as Rust's read does +fixtures/reject/src/alloc.rs:closure_moved+2:12: a read moving out a `{closure@fixtures/reject/src/alloc.rs:closure_moved+15:13: +15:20}`, a closure or coroutine whose state JS keeps in the function itself, which no copy splits from the original as Rust's read does +fixtures/reject/src/alloc.rs:closure_moved+2:12: a read moving out a `{closure@fixtures/reject/src/alloc.rs:closure_moved+5:18: +5:25}`, a closure or coroutine whose state JS keeps in the function itself, which no copy splits from the original as Rust's read does +fixtures/reject/src/alloc.rs:closure_moved+2:27: a raw pointer to `{closure@fixtures/reject/src/alloc.rs:closure_moved+11:12: +11:19}` behind a reference, which JS can't write a whole value into in place +fixtures/reject/src/alloc.rs:collection_moved+2:23: a read moving out a `std::vec::Vec`, a collection whose JS object keeps its length with its storage, which no copy splits from the original as Rust's read does +fixtures/reject/src/alloc.rs:collection_moved+4:23: a read moving out a `std::collections::VecDeque`, a collection whose JS object keeps its length with its storage, which no copy splits from the original as Rust's read does +fixtures/reject/src/alloc.rs:collection_moved+10:3: a read moving out a `std::string::String`, a collection whose JS object keeps its length with its storage, which no copy splits from the original as Rust's read does +fixtures/reject/src/alloc.rs:collection_moved+14:28: a read moving out a `(std::string::String, u8)`, which holds a `std::string::String`, a collection whose JS object keeps its length with its storage, which no copy splits from the original as Rust's read does +fixtures/reject/src/alloc.rs:collection_moved+16:23: a read moving out a `std::vec::Vec`, a collection whose JS object keeps its length with its storage, which no copy splits from the original as Rust's read does +fixtures/reject/src/alloc.rs:infallible_cell+1:11: a raw pointer to `std::result::Result, !>` behind a reference, which JS can't write a whole value into in place +fixtures/reject/src/alloc.rs:lazy_moved+2:22: a read moving out a `std::cell::LazyCell`, a library object whose inline state only its runtime class knows, which no copy splits from the original as Rust's read does +fixtures/reject/src/alloc.rs:past_reference+1:30: a pointer reaching past a value in no array, which JS keeps apart from what holds it in Rust +fixtures/reject/src/alloc.rs:peeked_moved+3:26: a read moving out a `std::iter::Peekable>>`, a library object whose inline state only its runtime class knows, which no copy splits from the original as Rust's read does fixtures/reject/src/alloc.rs:pinned_lent+1:32: `UnsafeCell::get_mut` of `std::pin::Pin>`, an object a whole write through the reference changes in place, so a value read out before would be a copy at another address fixtures/reject/src/alloc.rs:pinned_lent+2:43: `UnsafeCell::as_mut_unchecked` of `std::pin::Pin>`, an object a whole write through the reference changes in place, so a value read out before would be a copy at another address +fixtures/reject/src/alloc.rs:recast_reference+4:11: a read or write reaching past a value in no array, which JS keeps apart from what holds it in Rust +fixtures/reject/src/alloc.rs:recast_reference+4:48: a read or write reaching past a value in no array, which JS keeps apart from what holds it in Rust +fixtures/reject/src/alloc.rs:recast_reference+4:76: a read or write reaching past a value in no array, which JS keeps apart from what holds it in Rust fixtures/reject/src/alloc.rs:reinterpreted+1:27: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another +fixtures/reject/src/alloc.rs:unfixed_reference+3:35: a pointer moved by an offset the body doesn't fix, from a value in no array, which JS keeps apart from what holds it in Rust +fixtures/reject/src/alloc.rs:unfixed_reference+4:35: a pointer moved by an offset the body doesn't fix, from a value in no array, which JS keeps apart from what holds it in Rust +fixtures/reject/src/alloc.rs:unfixed_reference+5:3: a copy of a count the body doesn't fix, from a value in no array, which JS keeps apart from what holds it in Rust fixtures/reject/src/alloc.rs:uninit_array_lent+1:25: a reborrow of `std::mem::MaybeUninit<[u32; 2]>` through a raw pointer, which may point into part of an array with no JS array of its own to lend, which is not supported yet fixtures/reject/src/alloc.rs:uninit_array_lent+2:59: a reborrow of `std::mem::MaybeUninit<[u32; 2]>` through a raw pointer, which may point into part of an array with no JS array of its own to lend, which is not supported yet fixtures/reject/src/alloc.rs:uninit_array_lent+3:25: a reborrow of `std::mem::MaybeUninit<[u32; 2]>` through a raw pointer, which may point into part of an array with no JS array of its own to lend, which is not supported yet @@ -45,7 +66,6 @@ fixtures/reject/src/alloc.rs:uninit_array_lent+4:59: a reborrow of `std::mem::Ma fixtures/reject/src/alloc.rs:uninit_array_lent+5:23: a reborrow of `std::mem::MaybeUninit<[u32; 2]>` through a raw pointer, which may point into part of an array with no JS array of its own to lend, which is not supported yet fixtures/reject/src/alloc.rs:uninit_array_lent+6:23: a reborrow of `std::mem::MaybeUninit<[u32; 2]>` through a raw pointer, which may point into part of an array with no JS array of its own to lend, which is not supported yet fixtures/reject/src/alloc.rs:uninit_cell_lent+2:17: `MaybeUninit::assume_init_mut` of `std::cell::UnsafeCell`, whose mutable reference is the object, but the uninitialized value's is the place it is kept in -fixtures/reject/src/alloc.rs:vec_pointer+1:10: a raw pointer to `std::vec::Vec` behind a reference, which is the object and not where it is kept fixtures/reject/src/alloc.rs:wrapped_cells+8:19: a whole write of `std::mem::MaybeUninit<(std::cell::UnsafeCell, u32)>`, which would put another cell in place of one a `MaybeUninit` or `ManuallyDrop` holds fixtures/reject/src/alloc.rs:wrapped_cells+9:22: a whole write of `std::mem::ManuallyDrop>`, which would put another cell in place of one a `MaybeUninit` or `ManuallyDrop` holds fixtures/reject/src/alloc.rs:wrapped_cells+10:3: a whole write of `(std::mem::MaybeUninit>, u32)`, which would put another cell in place of one a `MaybeUninit` or `ManuallyDrop` holds diff --git a/ts/fixtures/reject/src/alloc.rs b/ts/fixtures/reject/src/alloc.rs index 723532aee..0daaa6a71 100644 --- a/ts/fixtures/reject/src/alloc.rs +++ b/ts/fixtures/reject/src/alloc.rs @@ -26,10 +26,117 @@ pub fn allocated() -> u8 { } } -/// A pointer to a `Vec`, whose JS array is its heap, not where Rust keeps the `Vec`. -pub fn vec_pointer(v: &mut Vec) -> usize { - let p = &raw const *v; - unsafe { (*p).len() } +/// A whole value written through a pointer from a shared reference to a `Result` of a cell +/// whose only inhabited variant leaves it no tag, all of it in an `UnsafeCell`, which JS +/// can't write into in place. +pub fn infallible_cell(r: &Result, std::convert::Infallible>) { + unsafe { std::ptr::from_ref(r).cast_mut().write(Ok(std::cell::Cell::new(19))) } +} + +/// A `LazyCell` moved out through a pointer before its initializer, which holds a cell, +/// runs: the closure is state only `rs.LazyCell` knows, which a copy can't reach. +pub fn lazy_moved(cell: std::cell::Cell) -> u32 { + let lazy = std::cell::LazyCell::new(move || cell.get()); + let copy = unsafe { std::ptr::read(&lazy) }; + std::mem::forget(lazy); + *copy +} + +/// A pointer from a reference the body is given moved by an offset the body doesn't fix, +/// wrapping or not, and copied from by a count it doesn't fix, each of which may reach +/// past the value JS keeps in no array. +pub fn unfixed_reference(r: &u32, n: usize) -> [u32; 3] { + let mut out = [0u32; 3]; + unsafe { + out[0] = *std::ptr::from_ref(r).add(n); + out[1] = *std::ptr::from_ref(r).wrapping_add(n); + std::ptr::copy_nonoverlapping(std::ptr::from_ref(r), out.as_mut_ptr(), n); + } + out +} + +/// A `Peekable` moved out through a pointer with a cell peeked, which only its runtime +/// class holds. +pub fn peeked_moved(cells: Vec>) -> u32 { + let mut iter = cells.into_iter().peekable(); + iter.peek(); + let mut copy = unsafe { std::ptr::read(&iter) }; + std::mem::forget(iter); + copy.next().map_or(0, |cell| cell.get()) +} + +/// Collections whose JS object keeps its length with its storage, moved out through +/// pointers while the original lives on: by `ptr::read`, `ptr::copy` within a `Vec` as +/// smallvec's `retain` moves its elements, `MaybeUninit::assume_init_read` of a tuple +/// holding one, and `ManuallyDrop::take`. Rust's copy shares the storage and keeps a +/// length of its own, which no JS copy does. +pub fn collection_moved(v: Vec, mut q: std::collections::VecDeque, s: String) -> usize { + let mut v = std::mem::ManuallyDrop::new(v); + let old_v = unsafe { std::ptr::read(&*v) }; + unsafe { v.set_len(0) }; + let old_q = unsafe { std::ptr::read(&q) }; + q.pop_front(); + std::mem::forget(q); + let mut words = vec![s.clone(), s.clone()]; + words[1].push('!'); + unsafe { + std::ptr::copy(words.as_ptr().add(1), words.as_mut_ptr(), 1); + words.set_len(1); + } + let pair = std::mem::MaybeUninit::new((s, 0u8)); + let old_s = unsafe { pair.assume_init_read() }; + let mut taken = std::mem::ManuallyDrop::new(vec![1u32]); + let old_t = unsafe { std::mem::ManuallyDrop::take(&mut taken) }; + old_v.len() + old_q.len() + words[0].len() + old_s.0.len() + old_t.len() +} + +/// Closures read out through a pointer from a shared reference and both called: one that +/// counts in its own capture, one capturing a cell it sets, and one moving out a vector +/// the crate writes into. Rust's copy keeps state of its own, which JS keeps in the one +/// function. +pub fn closure_moved(n: u32, bytes: Vec) -> u32 { + fn read(r: &F) -> F { + unsafe { std::ptr::read(r) } + } + let mut m = n; + let mut count = move || { + m += 1; + m + }; + let mut old = read(&count); + let cell = std::cell::Cell::new(n); + let set = move || cell.set(cell.get() + 1); + let old_set = read(&set); + set(); + old_set(); + let give = move || bytes; + let old_give = read(&give); + let mut first = std::mem::ManuallyDrop::new(give()); + unsafe { first.set_len(0) }; + count() + old() + old_give().len() as u32 +} + +/// A pointer from a reference the body is given, which JS keeps in no array, read through +/// past it by a constant wrapping offset and one past it, and viewed as a slice of a +/// length the body doesn't fix: each reaches the neighbour Tree Borrows allows it to. +pub fn beside_reference(r: &u32, n: usize) -> u32 { + let p = std::ptr::from_ref(r); + unsafe { *p.wrapping_add(1) + *p.add(1) + std::slice::from_raw_parts(p, n).iter().sum::() } +} + +/// `beside_reference`'s read one past, through a pointer cast to one JS keeps the same: +/// to a `MaybeUninit`, by `cast_mut`, and by `cast` to a `ManuallyDrop`. +pub fn recast_reference(r: &u32) -> u32 { + let p = (&raw const *r) as *const std::mem::MaybeUninit; + let q = std::ptr::from_ref(r).cast_mut(); + let m = std::ptr::from_ref(r).cast::>(); + unsafe { (*p.add(1)).assume_init() + q.add(1).read() + *m.wrapping_add(1).read() } +} + +/// A pointer from a reference the body is given, which JS keeps in no array, moved past +/// it. +pub fn past_reference(r: &u32) -> u32 { + unsafe { *(r as *const u32).add(2) } } /// `UnsafeCell`s of objects a pointer is the place of, which a whole write through `get` @@ -47,6 +154,13 @@ pub fn cell_of_objects(v: &mut Vec, s: &mut String, p: &mut Pin) -> usize { + let p = &raw const *v; + unsafe { (*p).len() } +} + /// A pointer to a cell behind a reference, made before a call that `UnsafeCell::raw_get` /// comes after, which reads it in its place only right after it is made. pub fn cell_pointer_kept(c: &std::cell::UnsafeCell, n: u32) -> u32 { diff --git a/ts/fixtures/reject/ts.toml b/ts/fixtures/reject/ts.toml index f15db3536..ec178a04e 100644 --- a/ts/fixtures/reject/ts.toml +++ b/ts/fixtures/reject/ts.toml @@ -4,6 +4,7 @@ roots = [ "alloc::alloc_retyped", "alloc::allocated", "alloc::arrays_boxed", + "alloc::beside_reference", "alloc::bytes_as_values", "alloc::cell_of_arrays", "alloc::cell_of_objects", @@ -11,8 +12,16 @@ roots = [ "alloc::cell_pointer_reassigned", "alloc::cell_raw_reassigned", "alloc::cell_raw_unknown", + "alloc::closure_moved", + "alloc::collection_moved", + "alloc::infallible_cell", + "alloc::lazy_moved", + "alloc::past_reference", + "alloc::peeked_moved", "alloc::pinned_lent", + "alloc::recast_reference", "alloc::reinterpreted", + "alloc::unfixed_reference", "alloc::uninit_array_lent", "alloc::uninit_cell_lent", "alloc::vec_pointer", diff --git a/ts/runtime/rs.ts b/ts/runtime/rs.ts index 27322f67e..4281a336f 100644 --- a/ts/runtime/rs.ts +++ b/ts/runtime/rs.ts @@ -176,6 +176,14 @@ export function place(get: () => T, set: (value: T) => void): Place { return new Accessed(get, set); } +/** + * The raw pointer from a `&mut` to `target`, an object others may hold, which a write of a + * whole value lands in, as `replaceIn` writes. + */ +export function placeIn(target: T): Place { + return place(() => target, (value) => writeIn(target, value)); +} + // Raw pointers /** A null raw pointer, of any type: nothing reads through it. */ @@ -690,12 +698,19 @@ export function ptrByteSlice(p: Place, len: number, shared: boolean): By /** * `ptr::write` of `value` through a pointer to an object: `value` copied into it, one - * object deep, or moved by `$move`, so every pointer to it sees the write. + * object deep, or moved by `$move`, so every pointer to it sees the write. A field `value` + * leaves out, such as a `HashMap`'s capacity it never set, goes from `p` too, unless `p` + * is of another class, such as the place `Cell::from_mut` makes, whose setters take it. */ export function ptrWrite(p: T, value: T): void { if (ArrayBuffer.isView(p)) (p as unknown as Uint8Array).set(value as unknown as Uint8Array); else if (moved(value)) value.$move(p as T & Moved); - else Object.assign(p, value); + else { + if (!Array.isArray(p) && Object.getPrototypeOf(p) === Object.getPrototypeOf(value)) { + for (const key of Object.keys(p)) if (!Object.hasOwn(value, key)) delete (p as Record)[key]; + } + Object.assign(p, value); + } } /** @@ -705,9 +720,14 @@ export function ptrWrite(p: T, value: T): void { */ export function replaceIn(p: T, value: T, depth = 0): T { const old = objectCopy(p, depth); + writeIn(p, value); + return old; +} + +/** `value`'s fields, or its items for a JS array, moved into `p`. */ +function writeIn(p: T, value: T): void { if (Array.isArray(p)) arrayAssign(p, value as unknown[]); else ptrWrite(p, value); - return old; } /** diff --git a/ts/src/lower/copy.rs b/ts/src/lower/copy.rs index 05253cecd..4414d8ee5 100644 --- a/ts/src/lower/copy.rs +++ b/ts/src/lower/copy.rs @@ -1,10 +1,13 @@ -//! Copies of `Copy` aggregates, by their type. +//! Copies of `Copy` aggregates, and of values a read through a pointer moves out, by +//! their type. //! //! JS shares an object where Rust copies its value, so a `Copy` value that is a JS object //! the crate's code writes into in place, or compares pointers to, is copied where Rust -//! copies it. A function made for its type copies what it holds by value and shares what -//! it holds by reference, as Rust does. A value of a type nothing writes into or compares -//! pointers to never changes once made, so sharing it is a copy no one can tell apart. +//! copies it, as is each such object a read through a pointer moves out, a library +//! object such as a `RefCell` included, one object deep. A function made for its type copies +//! what it holds by value and shares what it holds by reference, as Rust does. A value of +//! a type nothing writes into or compares pointers to never changes once made, so sharing +//! it is a copy no one can tell apart. use std::cell::RefCell; @@ -26,6 +29,16 @@ use super::{AdtRepr, Translator, field_name, names}; use crate::program::{SymKind, Symbol}; use crate::ts::{self, Expr, Key, Param, Stmt, StmtKind}; +/// What a library object holds inline, which its copy one object deep would share. +#[derive(Default)] +struct Inline<'tcx> { + /// Each field of its JS object holding a value inline, and the type held there. + fields: Vec<(&'static str, Ty<'tcx>)>, + /// The types it holds inline that its copy shares, a hash map's hasher, which a move + /// out of it through a pointer counts as it counts the collection. + shared: Vec>, +} + impl<'a, 'tcx> Translator<'a, 'tcx> { /// Whether a `ty` is copied where Rust copies it: a `Copy` value that is, or holds by /// value, a JS object the crate's code writes into in place or compares pointers to. @@ -55,7 +68,26 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { Some(Std::Bound) => (true, args.types().collect()), // Records of functions and pointers, which JS shares. Some(Std::WakerVTable | Std::RawWaker) => (true, Vec::new()), - Some(Std::MaybeUninit | Std::ManuallyDrop) => return self.changes(args.type_at(0)), + // What it holds, which a `Pin` is in JS too. + Some(Std::MaybeUninit | Std::ManuallyDrop | Std::Pin) => return self.changes(args.type_at(0)), + // A library object a whole write lands in in place, which a copy copies one + // object deep; through a shared reference too, where it has interior + // mutability, which no write the crate shows records. + Some(_) if self.write_in(ty).is_some() => { + return self.writes.contains(&self.writes.into, false, self.tcx, ty) + || self.addressed(ty) + || !ty.is_freeze(self.tcx, self.typing_env()) + || match self.inline_held(ty) { + Some(inline) => inline + .fields + .into_iter() + .map(|(_, t)| t) + .chain(inline.shared) + .any(|t| self.changes(t)), + // Any type it is over may be held inline, which its copy refuses. + None => args.types().any(|t| self.changes(t)), + }; + } Some(_) => return false, None if self.own(adt.did()) && adt.all_fields().next().is_some() => { (true, adt.all_fields().map(|f| self.field_ty(f.did, args)).collect()) @@ -68,6 +100,159 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { || parts.into_iter().any(|t| self.changes(t)) } + /// What a library object `ty` holds inline rather than on the heap, which a copy of the + /// object one object deep shares: each field of its JS object holding such a value and + /// the type held there, a cell's, a lock's, a range's ends, or a one-time cell's, which + /// holds nothing until set; and the types it holds where no copy reaches, a hash map's + /// hasher. A collection's items, and what a `Weak` points at, are on the heap, which + /// Rust's read shares too. `None` for any other library object, such as a `LazyCell` or + /// a `Peekable`, whose inline state only its runtime class knows, so a read that would + /// copy one is refused. + fn inline_held(&self, ty: Ty<'tcx>) -> Option> { + let ty::Adt(adt, args) = ty.kind() else { + return None; + }; + let at = |i: usize| args.type_at(i); + let fields = match Std::of(self.tcx, adt.did()) { + Some(Std::Cell) => vec![("v", at(0))], + Some(Std::RefCell | Std::Mutex) => vec![("value", at(0))], + Some(Std::OnceCell | Std::OnceLock) => vec![("v", Ty::new_option(self.tcx, at(0)))], + Some(Std::Range) => vec![("start", at(0)), ("end", at(0))], + Some(Std::RangeInclusive) => vec![("lo", at(0)), ("hi", at(0))], + Some(Std::RangeFrom) => vec![("start", at(0))], + Some( + Std::Vec + | Std::VecDeque + | Std::String + | Std::LinkedList + | Std::BinaryHeap + | Std::BTreeMap + | Std::BTreeSet + | Std::HashTable + | Std::BytesMut + | Std::Weak, + ) => Vec::new(), + Some(Std::HashMap) => { + return Some(Inline { + shared: vec![at(2)], + ..Inline::default() + }); + } + Some(Std::HashSet) => { + return Some(Inline { + shared: vec![at(1)], + ..Inline::default() + }); + } + _ => return None, + }; + Some(Inline { + fields, + ..Inline::default() + }) + } + + /// What a move of a `ty` out of where it is kept, as `ptr::read` makes, would copy that no + /// JS copy splits from the original as Rust's read does, if anything, and why: a + /// collection held by value, a `Vec` of other than bytes, a `VecDeque`, a `String`, a + /// map, a set, or a list, whose JS object keeps its length with its storage, sharing the + /// storage while each keeps a length of its own; a closure whose captures its copy + /// would split, or a coroutine, whose state is the JS function's or generator's own; or + /// a library object whose inline state only its runtime class knows, which the copy + /// copies one object deep. A safe move leaves nothing pointing into the original, + /// which a copy one object deep is exact for. + pub fn unsplit(&self, ty: Ty<'tcx>) -> Option<(Ty<'tcx>, &'static str)> { + if let Some(opaque) = self.opaque(ty) { + return Some(( + opaque, + "a library object whose inline state only its runtime class knows", + )); + } + by_value(self.tcx, self.typing_env(), ty).find_map(|part| { + if self.stateful(part) { + return Some(( + part, + "a closure or coroutine whose state JS keeps in the function itself", + )); + } + let ty::Adt(adt, args) = part.kind() else { + return None; + }; + let joined = match Std::of(self.tcx, adt.did()) { + Some(Std::Vec) => !super::byte(args.type_at(0)), + Some( + Std::VecDeque + | Std::BinaryHeap + | Std::String + | Std::LinkedList + | Std::BTreeMap + | Std::BTreeSet + | Std::HashMap + | Std::HashSet + | Std::HashTable, + ) => true, + _ => false, + }; + (joined && self.changes(part)) + .then_some((part, "a collection whose JS object keeps its length with its storage")) + }) + } + + /// The library object a copy of `ty` reaches whose inline state only its runtime class + /// knows, such as a `LazyCell` or a `Peekable`, if any, as [`Self::copy_parts`] walks it: + /// copied one object deep, which an unsafe move can tell apart. + fn opaque(&self, ty: Ty<'tcx>) -> Option> { + if !self.changes(ty) { + return None; + } + match ty.kind() { + ty::Tuple(tys) => tys.iter().find_map(|t| self.opaque(t)), + ty::Array(elem, _) | ty::Slice(elem) => self.opaque(*elem), + ty::Adt(adt, args) => match Std::of(self.tcx, adt.did()) { + Some(Std::WakerVTable | Std::RawWaker) => None, + Some( + Std::MaybeUninit + | Std::ManuallyDrop + | Std::Pin + | Std::Option + | Std::Poll + | Std::Result + | Std::ControlFlow + | Std::Reverse + | Std::AssertUnwindSafe + | Std::Wrapping + | Std::Saturating + | Std::Bound, + ) => args.types().find_map(|t| self.opaque(t)), + Some(_) => match self.inline_held(ty) { + Some(inline) => inline.fields.into_iter().find_map(|(_, t)| self.opaque(t)), + None => Some(ty), + }, + None => adt.all_fields().find_map(|f| self.opaque(self.field_ty(f.did, args))), + }, + _ => None, + } + } + + /// Whether `ty` is a closure whose copy Rust can tell apart from the original, as it + /// writes its captures or captures, at any depth, a value with interior mutability or + /// one a copy copies, such as a `Vec` the crate writes into, which the closure may move + /// out, or a coroutine. + fn stateful(&self, ty: Ty<'tcx>) -> bool { + let (tcx, env) = (self.tcx, self.typing_env()); + match ty.kind() { + ty::Coroutine(..) => true, + ty::Closure(..) | ty::CoroutineClosure(..) => { + super::mir::writes_captures(tcx, ty) + || (super::captured(ty).unwrap_or_default().iter()).any(|t| { + by_value(tcx, env, t) + .any(|part| !part.is_freeze(tcx, env) || self.changes(part) || self.stateful(part)) + }) + } + _ => false, + } + } + /// Whether the runs before compared pointers to a `ty`, or to a type it holds by value, /// so each copy of one is at an address of its own, as in Rust, wherever it is read. pub fn held_addressed(&self, ty: Ty<'tcx>) -> bool { @@ -213,7 +398,7 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { let tcx = self.tcx; let (ty, any_length) = match ty.kind() { // The copy of what it holds. - ty::Adt(adt, args) if Std::of(tcx, adt.did()) == Some(Std::ManuallyDrop) => { + ty::Adt(adt, args) if matches!(Std::of(tcx, adt.did()), Some(Std::ManuallyDrop | Std::Pin)) => { return self.shim(args.type_at(0), span); } ty::Array(elem, _) => (Ty::new_array(tcx, *elem, 0), true), @@ -268,7 +453,8 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { } /// The body of the function copying `v`, a `ty`, or a `MaybeUninit` of one if `uninit`: - /// then any of its parts kept as objects may hold nothing yet. + /// then any of its parts kept as objects may hold nothing yet, as a cell in an + /// uninitialized shell holds nothing. fn copy_parts(&mut self, ty: Ty<'tcx>, uninit: bool, span: Span) -> Vec { let v = || Expr::ident("v"); let ret = |e: Expr| vec![StmtKind::Return(Some(e.typed())).at(None)]; @@ -303,12 +489,13 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { body.extend(self.copy_parts(args.type_at(0), true, span)); body } - Some(Std::ManuallyDrop) => self.copy_parts(args.type_at(0), uninit, span), + // What it holds, as [`Self::shim`] copies it. + Some(Std::ManuallyDrop | Std::Pin) => self.copy_parts(args.type_at(0), uninit, span), Some(Std::Option) => ret(self.copy_unless(self.none(args.type_at(0)), args.type_at(0), span)), Some(Std::Poll) => ret(self.copy_unless(self.pending(args.type_at(0)), args.type_at(0), span)), Some(Std::Result) => ret(self.copy_result(args.type_at(0), args.type_at(1), span)), Some(Std::Reverse | Std::AssertUnwindSafe | Std::Wrapping | Std::Saturating) => { - ret(self.snapshot(v(), args.type_at(0), span)) + ret(self.snapshot(v(), part(args.type_at(0)), span)) } Some(Std::Bound) => { let ts_ty = self.ty(ty, span); @@ -336,6 +523,33 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { _ => self.copy_result(value, residual, span), }) } + // A library object, which [`Self::changes`] says a whole write lands in, and a + // copy of what it holds inline. + Some(std) => { + // One object deep, which a safe move is exact for; an unsafe one is refused + // ([`Self::unsplit`]). + let Some(inline) = self.inline_held(ty) else { + return ret(Expr::rs("objectCopy", vec![v()])); + }; + // A cell of its own, as `Cell::from_mut`'s place reads and writes another's. + if std == Std::Cell { + let held = self.snapshot(v().member("v"), part(args.type_at(0)), span); + return ret(Expr::New(Box::new(Expr::Rs("Cell".into())), vec![held])); + } + let fields: Vec<_> = inline.fields.into_iter().filter(|&(_, t)| self.changes(t)).collect(); + let copy = Expr::rs("objectCopy", vec![v()]); + if fields.is_empty() { + return ret(copy); + } + let c = || Expr::ident("c"); + let mut body = vec![StmtKind::decl(false, "c", None, Some(copy)).at(None)]; + for (field, t) in fields { + let read = self.snapshot(c().member(field), part(t), span); + body.push(StmtKind::Expr(c().member(field).assign(read)).at(None)); + } + body.push(StmtKind::Return(Some(c())).at(None)); + body + } _ => { let info = self.adt(*adt, span); match info.repr { diff --git a/ts/src/lower/mir/call.rs b/ts/src/lower/mir/call.rs index fa8fcd6a9..2923d0db0 100644 --- a/ts/src/lower/mir/call.rs +++ b/ts/src/lower/mir/call.rs @@ -593,13 +593,24 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { let done = |e: Expr, effect: bool| Some(Some((e, effect))); match (owner.as_str(), name.as_str()) { // A pointer from a reference to an `Arc`'s value, which is that value's one place. - ("ptr", "from_ref") | ("From", "from") - if (owner == "ptr" - || crate::lower::pointer::raw_pointee(tcx, dest.ty(&self.mir.local_decls, tcx).ty).is_some()) + ("ptr", "from_ref") | ("From", "from") | ("Into", "into") + if crate::lower::pointer::from_reference(tcx, def_id, gargs) && let Some(p) = self.arc_raw(&args[0].node, span, out) => { done(p, false) } + // A pointer from a reference to a value JS keeps in a place, as `&raw const *r` makes. + ("ptr", "from_ref" | "from_mut") | ("From", "from") | ("Into", "into") + if crate::lower::pointer::from_reference(tcx, def_id, gargs) + && let Some(r) = args[0].node.place() + && crate::lower::pointer::raw_pointee(tcx, dest.ty(&self.mir.local_decls, tcx).ty) + .is_some_and(|pointee| self.behind_place(r, pointee)) => + { + if let Some(why) = self.made_out_of_step(def_id, gargs, tcx.mk_place_deref(r)) { + return done(self.err(span, why), false); + } + done(self.behind_reference(r, span, out), false) + } // `get` of the cell a reference points at, which the pointer it was made into // would only be where that cell is kept. ("UnsafeCell", "raw_get") diff --git a/ts/src/lower/mir/mod.rs b/ts/src/lower/mir/mod.rs index cffdde7e1..6a6c57b12 100644 --- a/ts/src/lower/mir/mod.rs +++ b/ts/src/lower/mir/mod.rs @@ -3853,6 +3853,36 @@ fn located(value: Expr, loc: Option) -> Expr { } } +/// What the member `key` of `base`, which a substitution made, reads, if `base` shows it. +fn folded_member(base: &Expr, key: &ts::Key) -> Option { + // The payload of a `Result` made here is what it was made of, which `tsc` reads where it + // can't narrow the `Result`, cast to its variant or not, and so is what an `rs.Place` + // made here holds. + let made = match step::peel(base) { + Expr::As(made, _) => step::peel(made), + base => base, + }; + match (made, key) { + (Expr::Call(f, args), ts::Key::Name(key)) + if let Expr::Rs(ctor) = step::peel(f) + && matches!((ctor.as_str(), key.as_str()), ("Ok", "value") | ("Err", "error")) + && let [payload] = args.as_slice() => + { + Some(payload.clone()) + } + // A pointer made here of a shared reference holds its value. + (Expr::Object(members), ts::Key::Name(key)) + if key == "v" + && let [(ts::Key::Name(v), value)] = members.as_slice() + && v == "v" => + { + Some(value.clone()) + } + (_, ts::Key::Name(key)) if key == "v" => step::peel(base).place_read(), + _ => None, + } +} + /// Replace the one read of `name` in `stmt` with `value`; whether it was there. fn subst_stmt(stmt: &mut Stmt, name: &str, value: &Expr) -> bool { match &mut stmt.kind { @@ -3876,25 +3906,7 @@ fn subst(e: &mut Expr, name: &str, value: &Expr) -> bool { if !subst(base, name, value) { return false; } - // The payload of a `Result` made here is what it was made of, which `tsc` reads - // where it can't narrow the `Result`, cast to its variant or not, and so is what an - // `rs.Place` made here holds. - let made = match step::peel(base) { - Expr::As(made, _) => step::peel(made), - base => base, - }; - let folded = match (made, &*key) { - (Expr::Call(f, args), ts::Key::Name(key)) - if let Expr::Rs(ctor) = step::peel(f) - && matches!((ctor.as_str(), key.as_str()), ("Ok", "value") | ("Err", "error")) - && let [payload] = args.as_slice() => - { - Some(payload.clone()) - } - (_, ts::Key::Name(key)) if key == "v" => step::peel(base).place_read(), - _ => None, - }; - if let Some(folded) = folded { + if let Some(folded) = folded_member(base, key) { *e = folded; } true @@ -3970,7 +3982,22 @@ fn subst(e: &mut Expr, name: &str, value: &Expr) -> bool { } true } - Expr::Assign(l, r) => subst(l, name, value) || subst(r, name, value), + // A member assigned to folds only to the place an `rs.place` made here reads, as a + // value made here, such as `{ v: x }`'s, holds a copy of its own. + Expr::Assign(l, r) => { + let Expr::Member(base, key) = &mut **l else { + return subst(l, name, value) || subst(r, name, value); + }; + if !subst(base, name, value) { + return subst(r, name, value); + } + if matches!(key, ts::Key::Name(key) if key == "v") + && let Some(place) = step::peel(base).place_read() + { + **l = place; + } + true + } Expr::Cond(c, a, b) => subst(c, name, value) || subst(a, name, value) || subst(b, name, value), Expr::Arrow(..) | Expr::Lambda(..) => false, } diff --git a/ts/src/lower/mir/origin.rs b/ts/src/lower/mir/origin.rs index 4d914d95c..ef3826c59 100644 --- a/ts/src/lower/mir/origin.rs +++ b/ts/src/lower/mir/origin.rs @@ -66,6 +66,9 @@ impl<'tcx> Fx<'_, '_, '_, 'tcx> { /// Why the call of std's `def_id` would fail at run time where Rust's is defined, if the /// body proves it would: `offset_from` between pointers JS keeps in different arrays, or /// a pointer, slice, copy, or write reaching out of an array of one or a copied byte. + /// From an array of one, every offset, count, length, and access, wrapping or not, must + /// be one the body fixes and proves stays within it: past it, Rust may reach the + /// neighbour the reference was borrowed beside, which JS keeps apart. pub(super) fn stray( &self, def_id: rustc_hir::def_id::DefId, @@ -89,16 +92,7 @@ impl<'tcx> Fx<'_, '_, '_, 'tcx> { { return Some(super::alloc::BYTES.into()); } - let out = |o: Origin<'tcx>, n: Option, what: &str| { - let index = o.index?; - let past = o.array.len().is_some_and(|len| index + n.unwrap_or(0) > len); - (index < 0 || past).then(|| { - format!( - "{what} reaching past {}, which JS keeps apart from what holds it in Rust", - o.array.what() - ) - }) - }; + let (unfixed, out) = (Self::unfixed, Self::out); match (owner.as_str(), name.as_str()) { ("ptr" | "NonNull", "cast") => self.erased_place(arg(0)?, pointee, gargs.types().nth(1)?), ("Arc" | "Rc", "from_raw" | "increment_strong_count" | "decrement_strong_count") @@ -127,15 +121,34 @@ impl<'tcx> Fx<'_, '_, '_, 'tcx> { let o = self.moved(self.origin(arg(0)?)?, name.as_str(), arg(1)?); out(o, None, "a pointer") } + // A wrapping offset may leave any other array while nothing reads through it. + ("ptr" | "NonNull", "wrapping_add" | "wrapping_sub" | "wrapping_offset") => { + let o = self.lone_origin(arg(0)?)?; + out(self.moved(o, name.as_str(), arg(1)?), None, "a pointer") + } + // An access through a pointer, which the body shows one element at: both of a + // swap's, and else the first argument, as the second is the value written. + ( + "ptr" | "NonNull", + "read" | "read_unaligned" | "read_volatile" | "write" | "write_unaligned" | "write_volatile" + | "replace" | "swap" | "as_ref" | "as_mut" | "drop_in_place", + ) => [Some(arg(0)?), arg(1).filter(|_| name == "swap")] + .into_iter() + .flatten() + .find_map(|p| self.access(self.lone_origin(p)?)), ("raw", "from_raw_parts" | "from_raw_parts_mut") | ("ptr", "slice_from_raw_parts" | "slice_from_raw_parts_mut") | ("NonNull", "slice_from_raw_parts") => { let o = self.origin(arg(0)?)?; - // A raw pointer to a slice reads nothing until a read through it. - if owner != "raw" { + // A raw pointer to a slice of any other array reads nothing until a read + // through it. + if owner != "raw" && !matches!(o.array, Array::Lone(_)) { return None; } - out(o, Some(self.const_int(arg(1)?)?), "a slice") + match self.const_int(arg(1)?) { + Some(n) => out(o, Some(n), "a slice"), + None => unfixed(o, "a slice of a length"), + } } ( "ptr", @@ -148,15 +161,79 @@ impl<'tcx> Fx<'_, '_, '_, 'tcx> { ) | ("NonNull", "copy_to" | "copy_to_nonoverlapping" | "copy_from" | "copy_from_nonoverlapping") => { let n = self.const_int(arg(2)?); - [arg(0)?, arg(1)?] - .into_iter() - .find_map(|p| out(self.origin(p)?, n, "a copy")) + [arg(0)?, arg(1)?].into_iter().find_map(|p| { + let o = self.origin(p)?; + match n { + Some(_) => out(o, n, "a copy"), + None => unfixed(o, "a copy of a count"), + } + }) + } + ("ptr" | "NonNull", "write_bytes") => { + let o = self.origin(arg(0)?)?; + match self.const_int(arg(2)?) { + Some(n) => out(o, Some(n), "a write"), + None => unfixed(o, "a write of a count"), + } } - ("ptr" | "NonNull", "write_bytes") => out(self.origin(arg(0)?)?, self.const_int(arg(2)?), "a write"), _ => None, } } + /// Why a read or write through the raw pointer `place` derefs would fail at run time, if + /// the body shows that pointer off the element of an array of one. + pub(super) fn beside(&self, place: mir::Place<'tcx>) -> Option { + let [ProjectionElem::Deref, ..] = place.projection.as_slice() else { + return None; + }; + // Every pointer to a value of no bytes is where it was, as `stray` leaves it. + let ty::RawPtr(pointee, _) = *self.mir.local_decls[place.local].ty.kind() else { + return None; + }; + if self.b.t.zero_sized(pointee) || self.b.t.pointee(pointee) != Pointee::Place { + return None; + } + self.access(self.lone_origin(&Operand::Copy(mir::Place::from(place.local)))?) + } + + /// Why an access of the one element a pointer at `o` reads or writes reaches out of its + /// array, if the body fixes where it is: one past it, as a pointer further out, or at + /// an offset the body doesn't fix, is refused where it is made. + fn access(&self, o: Origin<'tcx>) -> Option { + let len = o.array.len()?; + (o.index? == len).then(|| Self::out(o, Some(1), "a read or write"))? + } + + /// Where the pointer `op` points, if the body shows it in an array of one. + fn lone_origin(&self, op: &Operand<'tcx>) -> Option> { + self.origin(op).filter(|o| matches!(o.array, Array::Lone(_))) + } + + /// Why an offset or a count the body doesn't fix, from `o`, may reach out of it: only + /// from an array of one. + fn unfixed(o: Origin<'tcx>, what: &str) -> Option { + matches!(o.array, Array::Lone(_)).then(|| { + format!( + "{what} the body doesn't fix, from {}, which JS keeps apart from what holds it in Rust", + o.array.what() + ) + }) + } + + /// Why `what`, at `o` and reaching `n` elements past it if a count, leaves `o`'s array. + fn out(o: Origin<'tcx>, n: Option, what: &str) -> Option { + let Some(index) = o.index else { + return Self::unfixed(o, &format!("{what} moved by an offset")); + }; + let past = o.array.len().is_some_and(|len| index + n.unwrap_or(0) > len); + (index < 0 || past).then(|| { + format!( + "{what} reaching past {}, which JS keeps apart from what holds it in Rust", + o.array.what() + ) + }) + } + /// Whether JS keeps `a` and `b` in different arrays. Arrays of different variables are; /// of one variable's, those at different fields or constant indices are, where Rust's /// are one allocation. @@ -200,49 +277,44 @@ impl<'tcx> Fx<'_, '_, '_, 'tcx> { op, _, ) if self.same_pointee(op, local) => self.origin(op), - Rvalue::RawPtr(mir::RawPtrKind::Const | mir::RawPtrKind::Mut, place) - if let Some(local) = self.raw_byte(*place) - && matches!(self.slots[local], Slot::Byte(_)) => - { - Some(Origin { - array: Array::Byte(local), - index: Some(0), - }) - } - Rvalue::RawPtr(mir::RawPtrKind::Const | mir::RawPtrKind::Mut, place) => { - match place.projection.as_slice() { - // `&raw mut *p` is `p`. - [ProjectionElem::Deref] => self.origin(&Operand::Copy(mir::Place::from(place.local))), - [.., ProjectionElem::Deref] => None, - [ - array @ .., - ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. }, - ] => (!place.projection.contains(&ProjectionElem::Deref)).then(|| Origin { - array: Array::Owned(mir::Place { - local: place.local, - projection: self.tcx.mk_place_elems(array), - }), - index: None, - }), - _ => Some(Origin { - array: Array::Lone(Location { - block: bb, - statement_index: i, - }), - index: Some(0), - }), - } - } + Rvalue::RawPtr(mir::RawPtrKind::Const | mir::RawPtrKind::Mut, place) => self.raw_origin( + *place, + Location { + block: bb, + statement_index: i, + }, + ), _ => None, }; } - let (def_id, _, args) = self.def_call(local)?; + let (def_id, gargs, args) = self.def_call(local)?; let (owner, name) = library::key(self.tcx, def_id)?; match (owner.as_str(), name.as_str()) { + ("ptr", "from_ref" | "from_mut") | ("From", "from") | ("Into", "into") + if crate::lower::pointer::from_reference(self.tcx, def_id, gargs) => + { + let block = self.def_block(local)?; + let at = Location { + block, + statement_index: self.mir.basic_blocks[block].statements.len(), + }; + self.reference_origin(args[0].node.place()?, at) + } ("ptr" | "NonNull", "add" | "sub" | "offset") => { Some(self.moved(self.origin(&args[0].node)?, name.as_str(), &args[1].node)) } - ("NonNull", "as_ptr" | "new_unchecked") => self.origin(&args[0].node), + // Only an array of one's, which every wrapping offset must stay within. + ("ptr" | "NonNull", "wrapping_add" | "wrapping_sub" | "wrapping_offset") => { + Some(self.moved(self.lone_origin(&args[0].node)?, name.as_str(), &args[1].node)) + } + ("NonNull", "as_ptr" | "new_unchecked") | ("ptr" | "NonNull", "cast_const" | "cast_mut") => { + self.origin(&args[0].node) + } + ("ptr" | "NonNull", "cast") + if self.b.t.uninit_inner(gargs.type_at(0)) == self.b.t.uninit_inner(gargs.type_at(1)) => + { + self.origin(&args[0].node) + } ("slice" | "Vec", "as_ptr" | "as_mut_ptr") => self.start(&args[0].node), // The start of its own text's encoding, though a substring's bytes start past its // text's in Rust, so a move back before it is refused, as one off a copy is. @@ -254,10 +326,90 @@ impl<'tcx> Fx<'_, '_, '_, 'tcx> { } } - /// `o` moved by the offset `op` as `add`, `sub`, or `offset` moves it. + /// Where a raw pointer to `place`, made at `at`, points, if the body shows it. + fn raw_origin(&self, place: mir::Place<'tcx>, at: Location) -> Option> { + if let Some(local) = self.raw_byte(place) + && matches!(self.slots[local], Slot::Byte(_)) + { + return Some(Origin { + array: Array::Byte(local), + index: Some(0), + }); + } + match place.projection.as_slice() { + // `&raw mut *p` is `p`. + [ProjectionElem::Deref] if self.mir.local_decls[place.local].ty.is_raw_ptr() => { + self.origin(&Operand::Copy(mir::Place::from(place.local))) + } + [rest @ .., ProjectionElem::Deref] => self.reference_origin( + mir::Place { + local: place.local, + projection: self.tcx.mk_place_elems(rest), + }, + at, + ), + [ + array @ .., + ProjectionElem::Index(_) | ProjectionElem::ConstantIndex { .. }, + ] => (!place.projection.contains(&ProjectionElem::Deref)).then(|| Origin { + array: Array::Owned(mir::Place { + local: place.local, + projection: self.tcx.mk_place_elems(array), + }), + index: None, + }), + _ => Some(Origin { + array: Array::Lone(at), + index: Some(0), + }), + } + } + + /// Where a raw pointer to what the reference `r` points at, made at `at`, points, as + /// [`Self::behind_reference`] makes it: where the body shows `r` borrowed, and else at + /// a place of its own, or, where `r` is an `rs.Place`, where that points. + fn reference_origin(&self, r: mir::Place<'tcx>, at: Location) -> Option> { + let ty = r.ty(&self.mir.local_decls, self.tcx).ty; + // A reference to an `Arc`'s value points at that value's one place. + if !ty.is_ref() || self.arc_of(&Operand::Copy(r)).is_some() { + return None; + } + let pointee = self.tcx.mk_place_deref(r); + let borrowed = self.through_aliases(pointee); + if borrowed != pointee { + return self.raw_origin(borrowed, at); + } + if self.indexed(r).is_some() { + let (def_id, _, args) = self.def_call(r.as_local()?)?; + let (owner, name) = library::key(self.tcx, def_id)?; + if !matches!( + (owner.as_str(), name.as_str()), + ("Index" | "IndexMut", "index" | "index_mut") + ) { + return None; + } + let o = self.start(&args[0].node)?; + return Some(Origin { + index: o.index.zip(self.const_int(&args[1].node)).map(|(i, n)| i + n), + ..o + }); + } + if let Some(p) = self.reborrowed_pointer(r) { + return self.origin(&Operand::Copy(p)); + } + if self.b.t.place_pointee(ty).is_some() { + return None; + } + Some(Origin { + array: Array::Lone(at), + index: Some(0), + }) + } + + /// `o` moved by the offset `op` as `add`, `sub`, or `offset`, or a wrapping one, moves it. fn moved(&self, o: Origin<'tcx>, name: &str, op: &Operand<'tcx>) -> Origin<'tcx> { let n = self.const_int(op).map(|n| match name { - "sub" => -n, + "sub" | "wrapping_sub" => -n, _ => n, }); Origin { @@ -516,7 +668,7 @@ impl<'tcx> Fx<'_, '_, '_, 'tcx> { } /// Whether `op` is a pointer, raw or `NonNull`, to what `local` points at, through any - /// `MaybeUninit`s, which a cast between keeps the pointer of. + /// `MaybeUninit`s or `ManuallyDrop`s, which a cast between keeps the pointer of. fn same_pointee(&self, op: &Operand<'tcx>, local: Local) -> bool { let pointee = |ty: ty::Ty<'tcx>| { let pointee = match ty.kind() { diff --git a/ts/src/lower/mir/place.rs b/ts/src/lower/mir/place.rs index b19a8853e..9206650c5 100644 --- a/ts/src/lower/mir/place.rs +++ b/ts/src/lower/mir/place.rs @@ -64,7 +64,7 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { /// The JS expression of `place`, read without a copy. pub fn place_expr(&mut self, place: Place<'tcx>, span: Span, out: &mut Vec) -> Expr { let tcx = self.tcx; - if let Some(why) = self.alloc_bytes(place) { + if let Some(why) = self.alloc_bytes(place).map(String::from).or_else(|| self.beside(place)) { return self.err(span, why); } // A local held as its parts reads them, as far as they go. @@ -847,7 +847,7 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { pub(super) fn store(&mut self, place: Place<'tcx>, value: Expr, span: Span) -> Vec { let (place, value) = self.rebuilt(place, value, span); let mut out = Vec::new(); - if let Some(why) = self.alloc_bytes(place) { + if let Some(why) = self.alloc_bytes(place).map(String::from).or_else(|| self.beside(place)) { out.push(StmtKind::Expr(self.err(span, why)).at(None)); return out; } diff --git a/ts/src/lower/mir/rvalue.rs b/ts/src/lower/mir/rvalue.rs index 72e805675..5d308e05d 100644 --- a/ts/src/lower/mir/rvalue.rs +++ b/ts/src/lower/mir/rvalue.rs @@ -71,125 +71,7 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { // A bounds check of a `&mut` slice reads its length through a raw pointer that // only `PtrMetadata` reads, which reads the slice's. Rvalue::RawPtr(mir::RawPtrKind::FakeForPtrMetadata, place) => self.place_expr(*place, span, out), - Rvalue::RawPtr(_, place) => { - if self.union_whole(*place, span).is_some() { - return Some(self.err(span, "a pointer to a union's field kept in more than one slot")); - } - if let Some(why) = self.out_of_step(*place, false) { - return Some(self.err(span, why)); - } - let pointee = place.ty(&self.mir.local_decls, tcx).ty; - match self.b.t.pointee(pointee) { - Pointee::Object => self.place_expr(*place, span, out), - // `&raw mut *p` of a raw pointer is `p`; of a reference, the slice's start and - // length. - Pointee::Slice => match place.projection.as_slice() { - [rest @ .., mir::ProjectionElem::Deref] - if Place::ty_from(place.local, rest, &self.mir.local_decls, tcx) - .ty - .is_raw_ptr() => - { - let base = Place { - local: place.local, - projection: tcx.mk_place_elems(rest), - }; - self.place_expr(base, span, out) - } - _ => { - let value = self.place_expr(*place, span, out); - match pointee { - _ if crate::lower::pointer::slice_of_bytes(pointee) => { - Expr::rs("rawByteSliceOf", vec![value]) - } - _ if crate::lower::pointer::slice_of_arrays(pointee) => { - Expr::rs("rawArraySliceOf", vec![value]) - } - _ => Expr::rs("rawSliceOf", vec![value]), - } - } - }, - // A slice of all of an array, as a borrow of it is. - Pointee::Array => { - if let Some(e) = self.array_subslice(mir::BorrowKind::Shared, *place, span, out) { - return Some(e); - } - let value = self.place_expr(*place, span, out); - match self.in_array(*place) { - true => value, - false => crate::lower::array_slice(self.b.t.uninit_inner(pointee), value), - } - } - // `&raw mut *p` is `p`. A pointer to `()` is an identity, which only another - // pointer is: not the `rs.Place` of a place or of a `&mut`, each borrow's own. - Pointee::Place => match place.projection.as_slice() { - // A byte in a buffer of its own: element 0 of it. - _ if let Some(local) = self.raw_byte(*place) - && let Slot::Byte(name) = &self.slots[local] => - { - Expr::rs("arrayAsPtr", vec![Expr::ident(name)]) - } - [rest @ .., mir::ProjectionElem::Deref] - if !self.box_pointer(place.local) && { - let base = Place::ty_from(place.local, rest, &self.mir.local_decls, tcx).ty; - match self.b.t.identity_pointee(pointee) { - true => base.is_raw_ptr(), - false => self.b.t.place_pointee(base).is_some(), - } - } => - { - let base = Place { - local: place.local, - projection: tcx.mk_place_elems(rest), - }; - self.place_expr(base, span, out) - } - // A reference to an `Arc`'s value, which is that value's one place. - [mir::ProjectionElem::Deref] - if let Some(p) = self.arc_raw(&Operand::Copy(Place::from(place.local)), span, out) => - { - p - } - _ if self.b.t.identity_pointee(pointee) => self.err( - span, - "a raw pointer to a place of `()`, which has no address of its own", - ), - // A reference to an object is the object, not the place holding it. - [.., mir::ProjectionElem::Deref] if !self.box_pointer(place.local) => self.err( - span, - format!( - "a raw pointer to `{pointee}` behind a reference, which is the object and not where it is kept" - ), - ), - // A pointer to an element is where it is in its array, which pointer - // arithmetic moves within. - [ - array @ .., - mir::ProjectionElem::Index(_) | mir::ProjectionElem::ConstantIndex { .. }, - ] => { - // It points into the array, which must stay where it is. - let array = Place { - local: place.local, - projection: tcx.mk_place_elems(array), - }; - if let Some(why) = self.out_of_step(array, false) { - return Some(self.err(span, why)); - } - let target = self.place_expr(*place, span, out); - let target = self.stable_target(*place, target, span, out); - match target.unlocated() { - Expr::Index(array, index) => { - Expr::rs("arrayAsPtr", vec![*array.clone(), *index.clone()]) - } - _ => self.place_of(target, None), - } - } - _ => { - let target = self.place_expr(*place, span, out); - self.make_place(*place, target, span, out) - } - }, - } - } + Rvalue::RawPtr(_, place) => self.raw_pointer(*place, span, out), Rvalue::Reborrow(..) => self.err(span, "reborrowing a type by its `Reborrow` impl is not supported"), Rvalue::ThreadLocalRef(_) => self.err(span, "thread locals are not supported"), Rvalue::WrapUnsafeBinder(..) => self.err(span, "unsafe binders are not supported"), @@ -274,6 +156,235 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { }) } + /// A raw pointer to `place`. + pub(super) fn raw_pointer(&mut self, place: Place<'tcx>, span: Span, out: &mut Vec) -> Expr { + let tcx = self.tcx; + if self.union_whole(place, span).is_some() { + return self.err(span, "a pointer to a union's field kept in more than one slot"); + } + if let Some(why) = self.out_of_step(place, false) { + return self.err(span, why); + } + let pointee = place.ty(&self.mir.local_decls, tcx).ty; + match self.b.t.pointee(pointee) { + Pointee::Object => self.place_expr(place, span, out), + // `&raw mut *p` of a raw pointer is `p`; of a reference, the slice's start and + // length. + Pointee::Slice => match place.projection.as_slice() { + [rest @ .., mir::ProjectionElem::Deref] + if Place::ty_from(place.local, rest, &self.mir.local_decls, tcx) + .ty + .is_raw_ptr() => + { + let base = Place { + local: place.local, + projection: tcx.mk_place_elems(rest), + }; + self.place_expr(base, span, out) + } + _ => { + let value = self.place_expr(place, span, out); + match pointee { + _ if crate::lower::pointer::slice_of_bytes(pointee) => Expr::rs("rawByteSliceOf", vec![value]), + _ if crate::lower::pointer::slice_of_arrays(pointee) => { + Expr::rs("rawArraySliceOf", vec![value]) + } + _ => Expr::rs("rawSliceOf", vec![value]), + } + } + }, + // A slice of all of an array, as a borrow of it is. + Pointee::Array => { + if let Some(e) = self.array_subslice(mir::BorrowKind::Shared, place, span, out) { + return e; + } + let value = self.place_expr(place, span, out); + match self.in_array(place) { + true => value, + false => crate::lower::array_slice(self.b.t.uninit_inner(pointee), value), + } + } + // `&raw mut *p` is `p`. A pointer to `()` is an identity, which only another + // pointer is: not the `rs.Place` of a place or of a `&mut`, each borrow's own. + Pointee::Place => match place.projection.as_slice() { + // A byte in a buffer of its own: element 0 of it. + _ if let Some(local) = self.raw_byte(place) + && let Slot::Byte(name) = &self.slots[local] => + { + Expr::rs("arrayAsPtr", vec![Expr::ident(name)]) + } + [rest @ .., mir::ProjectionElem::Deref] + if !self.box_pointer(place.local) && { + let base = Place::ty_from(place.local, rest, &self.mir.local_decls, tcx).ty; + match self.b.t.identity_pointee(pointee) { + true => base.is_raw_ptr(), + false => self.b.t.place_pointee(base).is_some(), + } + } => + { + let base = Place { + local: place.local, + projection: tcx.mk_place_elems(rest), + }; + let borrowed = match Place::ty_from(place.local, rest, &self.mir.local_decls, tcx) + .ty + .is_ref() + { + true => self.borrowed_pointer(base, span, out), + false => None, + }; + match borrowed { + Some(p) => p, + None => self.place_expr(base, span, out), + } + } + // A reference to an `Arc`'s value, which is that value's one place. + [mir::ProjectionElem::Deref] + if let Some(p) = self.arc_raw(&Operand::Copy(Place::from(place.local)), span, out) => + { + p + } + _ if self.b.t.identity_pointee(pointee) => self.err( + span, + "a raw pointer to a place of `()`, which has no address of its own", + ), + // A reference to an object is the object, not the place holding it. + // A reference to an object is the object, not the place holding it. + [rest @ .., mir::ProjectionElem::Deref] if !self.box_pointer(place.local) => { + let reference = Place { + local: place.local, + projection: tcx.mk_place_elems(rest), + }; + self.behind_reference(reference, span, out) + } + // A pointer to an element is where it is in its array, which pointer + // arithmetic moves within. + [ + array @ .., + mir::ProjectionElem::Index(_) | mir::ProjectionElem::ConstantIndex { .. }, + ] => { + // It points into the array, which must stay where it is. + let array = Place { + local: place.local, + projection: tcx.mk_place_elems(array), + }; + if let Some(why) = self.out_of_step(array, false) { + return self.err(span, why); + } + let target = self.place_expr(place, span, out); + let target = self.stable_target(place, target, span, out); + match target.unlocated() { + Expr::Index(array, index) => Expr::rs("arrayAsPtr", vec![*array.clone(), *index.clone()]), + _ => self.place_of(target, None), + } + } + _ => { + let target = self.place_expr(place, span, out); + self.make_place(place, target, span, out) + } + }, + } + } + + /// A raw pointer to what the reference `r` points at, a value whose pointer is an + /// `rs.Place`, where `r` is no `rs.Place` itself but the value: the place the body shows + /// `r` borrowed, as [`Self::borrowed_pointer`] finds it. Otherwise it is a place of its + /// own, as `ptr::from_ref` makes: of the value, where `r` is shared and nothing may write + /// a whole value through the pointer, and else an `rs.placeIn` writing a whole value into + /// the object `r` is in place, as `*r = v` does. + pub(super) fn behind_reference(&mut self, r: Place<'tcx>, span: Span, out: &mut Vec) -> Expr { + let tcx = self.tcx; + if let Some(p) = self.borrowed_pointer(r, span, out) { + return p; + } + let pointee = tcx.mk_place_deref(r); + let ty = pointee.ty(&self.mir.local_decls, tcx).ty; + let shared = matches!( + r.ty(&self.mir.local_decls, tcx).ty.kind(), + ty::Ref(_, _, rustc_ast::Mutability::Not) + ); + // Through a shared reference, a whole write is undefined unless every byte of the + // value is in an `UnsafeCell`, as no tag or niche of a layout's variants is. + let env = self.b.t.typing_env_pub(); + let tagged = tcx + .layout_of(env.as_query_input(ty)) + .is_ok_and(|layout| matches!(layout.variants, rustc_abi::Variants::Multiple { .. })); + let value = self.place_expr(pointee, span, out); + if shared && (tagged || ty.is_freeze(tcx, env)) { + return Expr::Object(vec![(Key::Name("v".into()), value)]); + } + if self.b.t.write_in(ty).is_none() { + return self.err( + span, + format!("a raw pointer to `{ty}` behind a reference, which JS can't write a whole value into in place"), + ); + } + Expr::rs("placeIn", vec![value]) + } + + /// The raw pointer to the place the body shows the reference `r` borrowed, if it does: + /// the place an alias of a borrow stands for, the element an index lends, or where the + /// raw pointer `r` reborrows what it points at from points. + fn borrowed_pointer(&mut self, r: Place<'tcx>, span: Span, out: &mut Vec) -> Option { + let pointee = self.tcx.mk_place_deref(r); + let borrowed = self.through_aliases(pointee); + if borrowed != pointee { + return Some(self.raw_pointer(borrowed, span, out)); + } + if let Some((array, index)) = self.indexed(r) { + return Some(Expr::rs("arrayAsPtr", vec![array, index])); + } + let p = self.reborrowed_pointer(r)?; + Some(self.place_expr(p, span, out)) + } + + /// Whether a raw pointer to `pointee`, made from the reference `r`, is what + /// [`Self::behind_reference`] makes: one to a value whose pointer is an `rs.Place`, which + /// `r` is not. + pub(super) fn behind_place(&self, r: Place<'tcx>, pointee: Ty<'tcx>) -> bool { + let ty = r.ty(&self.mir.local_decls, self.tcx).ty; + let t = &self.b.t; + ty.is_ref() + && t.place_pointee(ty).is_none() + && t.pointee(pointee) == Pointee::Place + && !t.identity_pointee(pointee) + && !t.zero_sized(pointee) + } + + /// The array and index of the element the reference `r` is an alias of, as indexing a + /// slice, an array, or a `Vec` by a number makes one. + pub(super) fn indexed(&self, r: Place<'tcx>) -> Option<(Expr, Expr)> { + let local = r.as_local()?; + if self.def_rvalue(local).is_some() { + return None; + } + let Slot::Alias(Some(target)) = &self.slots[local] else { + return None; + }; + let Expr::Index(array, index) = target.unlocated() else { + return None; + }; + Some((*array.clone(), *index.clone())) + } + + /// The raw pointer whose pointee the reference `r` borrows, if the body shows it: `r` is + /// `&*p` or `&mut *p`, each of which it assigns once, so `p` still points there. + pub(super) fn reborrowed_pointer(&self, r: Place<'tcx>) -> Option> { + let Rvalue::Ref(_, _, p) = self.def_rvalue(self.traced(&Operand::Copy(r))?)? else { + return None; + }; + let [mir::ProjectionElem::Deref] = p.projection.as_slice() else { + return None; + }; + let pointer = Place::from(p.local); + let fixed = match self.is_arg(p.local) { + true => self.defs[p.local] == 0, + false => self.traced(&Operand::Copy(pointer)).is_some(), + }; + let raw = self.mir.local_decls[p.local].ty.is_raw_ptr() && !self.box_pointer(p.local); + (raw && fixed && !self.borrowed[p.local]).then_some(pointer) + } + /// An unsigned integer of at most 32 bits, as a `u32`. fn half(&mut self, op: &Operand<'tcx>, span: Span, out: &mut Vec) -> Expr { let from = op.ty(&self.mir.local_decls, self.tcx); diff --git a/ts/src/lower/pointer.rs b/ts/src/lower/pointer.rs index ae0eb6a32..ef0b53e96 100644 --- a/ts/src/lower/pointer.rs +++ b/ts/src/lower/pointer.rs @@ -12,6 +12,11 @@ //! pointer arithmetic moves within the array. A pointer to an element JS keeps as an //! object is that object, which knows no array it is in, so arithmetic on one is refused. //! +//! A reference to a value JS copies is the value, not where it is kept, so a pointer made +//! from one is the place the body shows the reference borrowed, and else a place of its +//! own: of a shared reference's value, which nothing may write while the pointer lives, +//! or writing a whole value into the object a `&mut` is in place. +//! //! A pointer to `()`, as std erases one, is an identity that compares as itself: null, //! `rs.DANGLING_UNIT`, or a waker's data. One to a place of `()` or from a reference to //! one is refused, as an `rs.Place` of it would be an identity of its own each borrow. @@ -352,7 +357,8 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { /// Whether std's `def_id` with `args` makes a raw pointer to what its first argument, a /// reference, points at: `UnsafeCell::get`, `MaybeUninit::as_ptr` and `as_mut_ptr`, -/// `ptr::from_ref` and `from_mut`, and `NonNull::from` and `into`. +/// `ptr::from_ref` and `from_mut`, and `NonNull::from` and `into` of a reference to its +/// pointee, which only std's impl converts, as any other would overlap it. pub fn from_reference<'tcx>( tcx: ty::TyCtxt<'tcx>, def_id: rustc_hir::def_id::DefId, @@ -371,7 +377,8 @@ pub fn from_reference<'tcx>( "From" => (args.type_at(0), args.type_at(1)), _ => (args.type_at(1), args.type_at(0)), }; - matches!(to.kind(), ty::Adt(adt, _) if Std::of(tcx, adt.did()) == Some(Std::NonNull)) && from.is_ref() + matches!(to.kind(), ty::Adt(adt, to) if Std::of(tcx, adt.did()) == Some(Std::NonNull) + && from.ref_mutability().is_some() && from.builtin_deref(true) == Some(to.type_at(0))) } _ => false, } @@ -866,6 +873,14 @@ pub fn call<'tcx>( }, _ => arg(), }, + // Refused here rather than in `read`, `replace`, and `swap`, which the safe `Cell` + // methods share, as only an unsafe move leaves a pointer into the original. + ("ptr", "read" | "read_unaligned" | "read_volatile" | "replace" | "swap") + if !b.t.zero_sized(pointee()) + && let Some(refused) = unsplit(b, pointee(), span) => + { + refused + } ("ptr", "read" | "read_unaligned" | "read_volatile") => { let p = arg(); read(b, p, pointee(), span, out) @@ -959,6 +974,7 @@ pub fn call<'tcx>( } } // A shared reference to a value JS copies is the value. + ("MaybeUninit", "assume_init_read") if let Some(refused) = unsplit(b, pointee(), span) => refused, ("MaybeUninit", "assume_init_read") if b.t.keeps_cells(pointee()) => copy_kept(b, arg(), pointee(), span, out), ("MaybeUninit", "assume_init_read") => b.t.snapshot(arg(), pointee(), span), ("MaybeUninit", "assume_init_drop") | ("ManuallyDrop", "drop") => { @@ -972,6 +988,9 @@ pub fn call<'tcx>( // What it holds, which it leaves for nothing to read or drop again. ("ManuallyDrop", "take") => { let p = arg(); + if let Some(refused) = unsplit(b, pointee(), span) { + return Ok(Some(refused)); + } let value = match kind(b, pointee()) { Some(Pointee::Place) => p.member("v"), _ => p, @@ -1113,6 +1132,8 @@ pub fn call<'tcx>( Expr::undefined() } else if let Some(why) = unmodeled(b, pointee(), "a copy between pointers to") { b.err(span, why) + } else if let Some(refused) = unsplit(b, pointee(), span) { + refused } else { let mut args = vec![src, dst, n]; args.extend(b.t.snapshot_fn(pointee(), span)); @@ -1365,6 +1386,20 @@ pub(super) fn cell_pointer<'tcx>(b: &Body<'_, '_, 'tcx>, cell: Expr, ty: Ty<'tcx } } +/// The refusal of a move of a `ty` out of where it is kept, as `ptr::read` makes, that +/// would copy what JS can't split from the original as Rust's does. +fn unsplit<'tcx>(b: &mut Body<'_, '_, 'tcx>, ty: Ty<'tcx>, span: Span) -> Option { + let (held, why) = b.t.unsplit(ty)?; + let what = match held == ty { + true => String::new(), + false => format!(", which holds a `{held}`"), + }; + Some(b.err( + span, + format!("a read moving out a `{ty}`{what}, {why}, which no copy splits from the original as Rust's read does"), + )) +} + /// The value `p`, a pointer to `ty`, points at, as a value of its own. pub(super) fn read<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, ty: Ty<'tcx>, span: Span, out: &mut Vec) -> Expr { if b.t.keeps_cells(ty) { @@ -1372,7 +1407,16 @@ pub(super) fn read<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, ty: Ty<'tcx>, span return copy_kept(b, at, ty, span, out); } let value = match b.t.pointee(ty) { - Pointee::Place => p.member("v"), + // The value a pointer made here of a shared reference holds. + Pointee::Place => match p.unlocated() { + Expr::Object(members) + if let [(Key::Name(v), value)] = members.as_slice() + && v == "v" => + { + value.clone() + } + _ => p.member("v"), + }, Pointee::Object => p, Pointee::Array => { let ty = b.t.uninit_inner(ty);