diff --git a/quest/m2/ts-model/README.md b/quest/m2/ts-model/README.md index 6af9b4f93..a1b3f1575 100644 --- a/quest/m2/ts-model/README.md +++ b/quest/m2/ts-model/README.md @@ -42,13 +42,13 @@ 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 +refusals) translation refuses for good, and smallvec's union over a type parameter, its +`Array::Item`, and raw parts (5) 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`. Decided with the maintainer on 2026-11-04, from a census at the bumped pin diff --git a/quest/m3/ts-unsafe/README.md b/quest/m3/ts-unsafe/README.md index 86575a3a5..bb4c176e5 100644 --- a/quest/m3/ts-unsafe/README.md +++ b/quest/m3/ts-unsafe/README.md @@ -70,11 +70,10 @@ construct still open at `b3df54b6c`, all in m3: [#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. +Census at the moq pin on 2026-11-07: model 45, kio 72, moq-net 344 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 - [UnsafeCell's raw accessors](/quest/m3/ts-unsafe/unsafecell.md) - `UnsafeCell::raw_get`, `from_mut`, and `replace` translate 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 de0bcdc35..2e63bbf4c 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, 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. `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, or a mutable slice, through it, which Rust forbids, fails at run time, as do 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, 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, which `rs.arcErase` checks where the body doesn't show it, and refused where the body shows 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, 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 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. `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, or a mutable slice, through it, which Rust forbids, fails at run time, as do 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, 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, which nothing may write while the pointer lives, or an `rs.placeIn` writing a whole value into the object a `&mut` is 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, which `rs.arcErase` checks where the body doesn't show it, and refused where the body shows 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 | 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 7cb3d4fe4..38aca770d 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 } 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 } from "./lib.ts"; import * as Held from "./conversions.Held.ts"; import * as Kept from "./conversions.Kept.ts"; import * as Logged from "./conversions.Logged.ts"; @@ -843,7 +843,7 @@ 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; - put$conversions_Value_String(rs.cellReplace(rs.cellOfArraySlice(rs.arraySliceOne(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5)), 1), rs.objectCopy), [[make$conversions_Value_String(data, 28)]])[0][0], out); + put$conversions_Value_String(rs.cellReplace(rs.cellOfArraySlice(rs.arraySliceOne(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5)), 1), copy$String), [[make$conversions_Value_String(data, 28)]])[0][0], out); rs.idx(s, 0); s.data[s.start] = rs.stringCloneFrom(s.data[s.start], other); rs.idx(s, 0); @@ -905,7 +905,7 @@ 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; - put$conversions_Value_Vec_u32(rs.cellReplace(rs.cellOfArraySlice(rs.arraySliceOne(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5)), 1), rs.objectCopy), [[make$conversions_Value_Vec_u32(data, 28)]])[0][0], out); + put$conversions_Value_Vec_u32(rs.cellReplace(rs.cellOfArraySlice(rs.arraySliceOne(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5)), 1), copy$Vec_u32), [[make$conversions_Value_Vec_u32(data, 28)]])[0][0], out); rs.idx(s, 0); rs.vecCloneFrom(s.data[s.start], other, 4); rs.idx(s, 0); @@ -967,7 +967,7 @@ 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; - put$conversions_Value_Vec_u8(rs.cellReplace(rs.cellOfArraySlice(rs.arraySliceOne(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5)), 1), rs.objectCopy), [[make$conversions_Value_Vec_u8(data, 28)]])[0][0], out); + put$conversions_Value_Vec_u8(rs.cellReplace(rs.cellOfArraySlice(rs.arraySliceOne(rs.sliceOfPlace(rs.place(() => borrowed7[borrowed8], (value5) => borrowed7[borrowed8] = value5)), 1), copy$Vec_u8), [[make$conversions_Value_Vec_u8(data, 28)]])[0][0], out); rs.idx(s, 0); rs.byteVecCloneFrom(s.data[s.start], other); rs.idx(s, 0); @@ -2078,7 +2078,7 @@ 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); } @@ -2168,7 +2168,7 @@ 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); } @@ -2256,7 +2256,7 @@ 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); } @@ -2344,7 +2344,7 @@ 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); } @@ -3185,7 +3185,7 @@ 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); } @@ -3272,7 +3272,7 @@ 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); } @@ -3357,7 +3357,7 @@ 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); } diff --git a/ts/fixtures/lang/conversions/expected/lib.ts b/ts/fixtures/lang/conversions/expected/lib.ts index 0a7051530..a448b3248 100644 --- a/ts/fixtures/lang/conversions/expected/lib.ts +++ b/ts/fixtures/lang/conversions/expected/lib.ts @@ -5,6 +5,18 @@ import { conversions } 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/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 58d456119..84498f4b6 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); } @@ -92,6 +101,29 @@ 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)); + const iter2: rs.Iter = rs.iterSlice(v.items); + while (true) { + const _31: rs.ByteVec | rs.Done = iter2.next(); + if (_31 === rs.DONE) { + break; + } + 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 => { @@ -324,12 +356,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]; @@ -340,59 +372,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; } @@ -400,40 +432,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; } @@ -444,7 +476,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); } } @@ -1161,7 +1193,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 0e82c8bd5..b64244784 100644 --- a/ts/fixtures/lang/locals/expected/locals.ts +++ b/ts/fixtures/lang/locals/expected/locals.ts @@ -295,7 +295,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 8f6edbacb..fec4d68cd 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,20 @@ 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$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_; @@ -76,6 +125,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 519c4950b..564155089 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, loneBytes } 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, loneBytes } 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"; @@ -18,8 +18,21 @@ import type * as Outer from "./pointers.Outer.ts"; import type * as Pair from "./pointers.Pair.ts"; import type * as Paired from "./pointers.Paired.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); } @@ -28,6 +41,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); } @@ -52,6 +69,25 @@ export function copy$array_u8(v: Uint8Array): Uint8Array { return v.slice(); } +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; @@ -73,6 +109,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 }; } @@ -145,10 +188,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]]; } @@ -189,8 +244,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] { @@ -201,8 +256,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 { @@ -302,12 +357,16 @@ 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::lone_bytes", (_239: rs.u8, data30: rs.ByteSlice, out30: rs.ByteVec): undefined => { - loneBytes(data30, out30); + }] 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::lone_bytes", (_240: rs.u8, data31: rs.ByteSlice, out31: rs.ByteVec): undefined => { + loneBytes(data31, out31); + }] as [string, (a0: rs.u8, a1: rs.ByteSlice, a2: rs.ByteVec) => undefined], ["pointers::moved_out", (_241: rs.u8, data32: rs.ByteSlice, out32: rs.ByteVec): undefined => { + movedOut(data32, out32); }] 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.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 3f573a6dd..9f1762893 100644 --- a/ts/fixtures/lang/pointers/expected/pointers.ts +++ b/ts/fixtures/lang/pointers/expected/pointers.ts @@ -1,20 +1,22 @@ // 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_Spot, copy$pointers_Twin, copy$tuple2_array2_u32_u8, copy$tuple2_u16_u8, copy$tuple2_u32_u32, 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$173j2gr as option_mapOr$173j2gr, filter$0wvwd70 } from "./core/option.ts"; +import { copy$Cell_u32, 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_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_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_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_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$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"; import * as ByClosure from "./pointers.ByClosure.ts"; import type * as ByObject from "./pointers.ByObject.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 Holder from "./pointers.Holder.ts"; import * as Housed from "./pointers.Housed.ts"; @@ -27,6 +29,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"; @@ -40,6 +43,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"; @@ -50,13 +54,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`. */ @@ -69,9 +73,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 { @@ -84,7 +88,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; @@ -102,36 +106,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); } /** @@ -2839,13 +2844,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](); @@ -2981,6 +2986,725 @@ 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 lone: rs.u32 = bits as rs.u32; + const at: rs.Place = rs.place(() => lone, (value) => lone = value); + let end: rs.Place = rs.NULL; + end = rs.ptrAdd(at, 1); + rs.byteVecPush(out, rs.ptrOffsetFrom(end, at) & 0xff); + const _282: Nothing.Nothing = {}; + rs.byteVecPush(out, nothingTag(_282, 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 = rs.byteVecFrom(Uint8Array.of(how)); + 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] = [rs.byteVecFrom(Uint8Array.of(how)), how]; + const pointer = _22; + const old2 = copy$tuple2_Vec_u8_u8(pointer); + rs.ptrWrite(pointer, _23); + const oldT: [rs.ByteVec, rs.u8] = old2; + const oldS: TaggedVec.TaggedVec = { v: rs.byteVecFrom(Uint8Array.of(how)), tag: how }; + const x = s; + const y = oldS; + const old3 = copy$pointers_TaggedVec(x); + rs.ptrWrite(x, copy$pointers_TaggedVec(y)); + rs.ptrWrite(y, old3); + old = [slot, oldT, oldS] as [rs.ByteVec | undefined, [rs.ByteVec, rs.u8], TaggedVec.TaggedVec]; + } + if (o.v !== undefined) { + const _39: rs.ByteVec = rs.byteSliceToByteVec(with_); + refillIn(o.v, _39); + } + const _42: rs.ByteVec = rs.byteSliceToByteVec(with_); + refillIn(t[0], _42); + const _45: rs.ByteVec = rs.byteSliceToByteVec(with_); + refillIn(s.v, _45); + 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 { + rs.placeIn(r).v = new rs.Cell(n); +} + +/** + * A `HashMap` 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 { + rs.placeIn(r).v = rs.hashNew() as rs.HashMap; +} + +/** + * 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), copy$Cell_u32(rs.placeIn(c).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); + } +} + /** * 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 diff --git a/ts/fixtures/lang/pointers/src/lib.rs b/ts/fixtures/lang/pointers/src/lib.rs index 94c7f807f..6c962119d 100644 --- a/ts/fixtures/lang/pointers/src/lib.rs +++ b/ts/fixtures/lang/pointers/src/lib.rs @@ -49,10 +49,12 @@ 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) }), ("pointers::lone_bytes", |_, data, out| pointers::lone_bytes(data, out)), + ("pointers::moved_out", |_, data, out| pointers::moved_out(data, out)), ] } diff --git a/ts/fixtures/lang/pointers/src/pointers.rs b/ts/fixtures/lang/pointers/src/pointers.rs index 66c8ac8cd..9642a37f0 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), }, } @@ -2411,6 +2412,467 @@ 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 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![how]); + 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![how], how)); + let mut old_s = TaggedVec { v: vec![how], 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` 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) { + unsafe { ptr::write(ptr::from_mut(r), std::collections::HashMap::new()) } +} + +/// 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]); + } +} + /// 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 diff --git a/ts/fixtures/lang/slices/expected/lib.ts b/ts/fixtures/lang/slices/expected/lib.ts index abd0a5bb5..858b97cef 100644 --- a/ts/fixtures/lang/slices/expected/lib.ts +++ b/ts/fixtures/lang/slices/expected/lib.ts @@ -13,6 +13,10 @@ export function copy$Option_array2_u8(v: Uint8Array | undefined): Uint8Array | u return v === undefined ? v : copy$array_u8(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 4b89d5fc8..625e354c4 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_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_u8, copy$mem_MaybeUninit_array_u8, copy$slices_Pair, drop$tuple2_Vec_slices_Noisy_Vec_u8 } from "./lib.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$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, 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"; import * as Item from "./slices.Item.ts"; @@ -2970,7 +2970,7 @@ 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.replaceIn(rs.sliceView(rs.arraySliceAt(runs2, 0)), _354, 1)[0])), undefined, true); - rs.iterExtendBytes(out, rs.iterSlice(rs.byteVecView(rs.cellReplace(rs.cellOfArraySlice(rs.arraySliceOne(rs.sliceOfPlace(rs.place(() => item, (value2) => item = value2)), 1), rs.objectCopy), [[rs.byteVecFrom(Uint8Array.of(byte(3)))]])[0][0])), undefined, true); + rs.iterExtendBytes(out, rs.iterSlice(rs.byteVecView(rs.cellReplace(rs.cellOfArraySlice(rs.arraySliceOne(rs.sliceOfPlace(rs.place(() => item, (value2) => item = value2)), 1), copy$Vec_u8), [[rs.byteVecFrom(Uint8Array.of(byte(3)))]])[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 5dbe12ffc..0a012ab15 100644 --- a/ts/fixtures/reject/census.txt +++ b/ts/fixtures/reject/census.txt @@ -6,12 +6,26 @@ fixtures/reject/src/alloc.rs:alloc_retyped+8:21: memory from `alloc` cast to poi fixtures/reject/src/alloc.rs:alloc_retyped+10:17: memory from `alloc` cast to pointers to `i32` and to `u32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:allocated+4: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: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: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 fixtures/reject/src/alloc.rs:bytes_as_values+7:15: 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: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 JS can't copy apart +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 JS can't copy apart 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: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: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:zeroed+1:12: 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:zeroed+1:13: unsupported call to `std::alloc::alloc_zeroed` fixtures/reject/src/buffers.rs:blank_bytes+1:2: `Buf::copy_to_bytes` of `buffers::Blank`, a value JS copies, which is not supported diff --git a/ts/fixtures/reject/expected.txt b/ts/fixtures/reject/expected.txt index 04cd4ee91..e70a4c0a2 100644 --- a/ts/fixtures/reject/expected.txt +++ b/ts/fixtures/reject/expected.txt @@ -10,12 +10,26 @@ fixtures/reject/src/alloc.rs:alloc_retyped+8:21: memory from `alloc` cast to poi fixtures/reject/src/alloc.rs:alloc_retyped+10:17: memory from `alloc` cast to pointers to `i32` and to `u32`, which reads the bytes of one type as another fixtures/reject/src/alloc.rs:allocated+4: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: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: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 fixtures/reject/src/alloc.rs:bytes_as_values+7:15: 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: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 JS can't copy apart +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 JS can't copy apart 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: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: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:zeroed+1:12: 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:zeroed+1:13: unsupported call to `std::alloc::alloc_zeroed` fixtures/reject/src/buffers.rs:blank_bytes+1:2: `Buf::copy_to_bytes` of `buffers::Blank`, a value JS copies, which is not supported diff --git a/ts/fixtures/reject/src/alloc.rs b/ts/fixtures/reject/src/alloc.rs index dd4e03d8b..2e4e7de72 100644 --- a/ts/fixtures/reject/src/alloc.rs +++ b/ts/fixtures/reject/src/alloc.rs @@ -24,10 +24,82 @@ 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() +} + +/// 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::() } +} + +/// 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) } } /// Casts between pointers to bytes and to values outside memory from `alloc`, which read diff --git a/ts/fixtures/reject/ts.toml b/ts/fixtures/reject/ts.toml index ae956cc1c..aa68b8a22 100644 --- a/ts/fixtures/reject/ts.toml +++ b/ts/fixtures/reject/ts.toml @@ -3,9 +3,15 @@ roots = [ "alloc::addressed", "alloc::alloc_retyped", "alloc::allocated", + "alloc::beside_reference", "alloc::bytes_as_values", + "alloc::collection_moved", + "alloc::infallible_cell", + "alloc::lazy_moved", + "alloc::past_reference", + "alloc::peeked_moved", "alloc::reinterpreted", - "alloc::vec_pointer", + "alloc::unfixed_reference", "alloc::zeroed", "bound_ranges", "buffers::blank_bytes", diff --git a/ts/runtime/rs.ts b/ts/runtime/rs.ts index 7b834582f..258ca2fad 100644 --- a/ts/runtime/rs.ts +++ b/ts/runtime/rs.ts @@ -162,6 +162,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. */ @@ -671,12 +679,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); + } } /** @@ -686,9 +701,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 1d4924d77..616514c8a 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. @@ -56,6 +69,24 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { // 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)), + // 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 +99,88 @@ 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() + }) + } + + /// The collection held by value in a `ty` that a move of one out of where it is kept, as + /// `ptr::read` makes, would copy, if any: 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, + /// so no copy splits from the original as Rust's read does, sharing the storage while + /// each keeps a length of its own. 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> { + by_value(self.tcx, self.typing_env(), ty).find(|&part| { + let ty::Adt(adt, args) = part.kind() else { + return false; + }; + 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) + }) + } + /// 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 { @@ -336,6 +449,37 @@ 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) => { + let Some(inline) = self.inline_held(ty) else { + self.err( + span, + format!( + "a read moving out a `{ty}`, a library object whose inline state only its runtime class knows, which JS can't copy apart" + ), + ); + return ret(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"), 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), 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 1e32932ec..fb8fa5400 100644 --- a/ts/src/lower/mir/call.rs +++ b/ts/src/lower/mir/call.rs @@ -534,13 +534,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, &args[0].node) { + return done(self.err(span, why), false); + } + done(self.behind_reference(r, span, out), false) + } // A place of no bytes has nothing to read or write, and every value of its type // is the one it holds. What made the `&mut` still runs, as its unread temporary. ("mem", "take" | "replace" | "swap") if self.b.t.zero_sized(targ(0)?) => match name.as_str() { diff --git a/ts/src/lower/mir/mod.rs b/ts/src/lower/mir/mod.rs index 87a9bf89c..a260e5f60 100644 --- a/ts/src/lower/mir/mod.rs +++ b/ts/src/lower/mir/mod.rs @@ -3722,6 +3722,14 @@ fn subst(e: &mut Expr, name: &str, value: &Expr) -> bool { { 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().cloned(), _ => None, }; diff --git a/ts/src/lower/mir/origin.rs b/ts/src/lower/mir/origin.rs index 2b59e635c..b1467b132 100644 --- a/ts/src/lower/mir/origin.rs +++ b/ts/src/lower/mir/origin.rs @@ -65,6 +65,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, @@ -88,16 +91,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_borrow(arg(0)?, pointee, gargs.types().nth(1)?), ("Arc" | "Rc", "from_raw" | "increment_strong_count" | "decrement_strong_count") @@ -126,15 +120,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", @@ -147,15 +160,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. @@ -199,48 +276,36 @@ 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)) } + // 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") => 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 @@ -253,10 +318,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 { diff --git a/ts/src/lower/mir/place.rs b/ts/src/lower/mir/place.rs index 7818dca0b..b76111e19 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. @@ -827,7 +827,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 3446ee3c4..191a72009 100644 --- a/ts/src/lower/mir/rvalue.rs +++ b/ts/src/lower/mir/rvalue.rs @@ -79,125 +79,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"), @@ -282,6 +164,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 d4538b033..69046dd1a 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. @@ -289,7 +294,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, @@ -308,7 +314,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, } @@ -839,7 +846,10 @@ pub fn call<'tcx>( } } // A shared reference to a value JS copies is the value. - ("MaybeUninit", "assume_init_read") => b.t.snapshot(arg(), pointee(), span), + ("MaybeUninit", "assume_init_read") => match unsplit(b, pointee(), span) { + Some(refused) => refused, + None => b.t.snapshot(arg(), pointee(), span), + }, ("MaybeUninit", "assume_init_drop") | ("ManuallyDrop", "drop") => { let p = arg(); let value = match kind(b, pointee()) { @@ -851,6 +861,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, @@ -938,6 +951,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)); @@ -1168,10 +1183,38 @@ pub fn raw_pointee<'tcx>(tcx: ty::TyCtxt<'tcx>, ty: Ty<'tcx>) -> Option } } +/// The refusal of a move of a `ty` out of where it is kept, as `ptr::read` makes, that +/// would copy a collection whose JS storage 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 = 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}, a collection whose JS object keeps its length with its storage, 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. fn read<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, ty: Ty<'tcx>, span: Span) -> Expr { + if let Some(refused) = unsplit(b, ty, span) { + return refused; + } 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);