diff --git a/quest/m3/README.md b/quest/m3/README.md index 2f0285328..805940520 100644 --- a/quest/m3/README.md +++ b/quest/m3/README.md @@ -19,6 +19,7 @@ generic std gap is planned here even when moq never calls it. - [The capacity of a BytesMut](/quest/m3/ts-bytes-capacity.md) - `BytesMut::capacity` and what reads it translate with `bytes`' values - [Trait objects](/quest/m3/ts-dyn.md) - `dyn Error`, `dyn Iterator`, and `dyn Buf` translate, so a consumer neither rewrites nor binds them - [Placeholders written to a crate's own fmt::Write](/quest/m3/ts-fmt-calls.md) - `write!` with placeholders into a crate's own writer translates, its `write_str` seeing each call std makes +- [Waiting: drop the lang fixture's mul_add allowance](/quest/m3/ts-fma-allowance.md) - the differential fails on any `mul_add` difference again once wasm32's `fma` rounds exactly - [Waiting: type-check in process with tsrs](/quest/m3/ts-tsrs.md) - rust2-ts checks bindings through tsrs instead of `tsc` once it is a stable library - [Drop order in std's folds and iterators](/quest/m3/ts-drop-order.md) - the values that drop still refused in std's consumers, flattens, and collects drop as std's do - [A slice of one value behind any &mut](/quest/m3/ts-slice-of-one.md) - `slice::from_mut` and `array::from_mut` of a `&mut u8`, or of an object no place names, translate diff --git a/quest/m3/ts-fma-allowance.md b/quest/m3/ts-fma-allowance.md new file mode 100644 index 000000000..3e33b25ea --- /dev/null +++ b/quest/m3/ts-fma-allowance.md @@ -0,0 +1,17 @@ +# [XS] Waiting: drop the lang fixture's mul_add allowance + +## Goal + +Waits on upstream. wasm32's `fma`, compiler-builtins' generic libm, rounds some +near-ties a last place off +([rust-lang/compiler-builtins#1353](https://github.com/rust-lang/compiler-builtins/issues/1353)), +so the lang fixture's differential accepts a `floats::mul_adds` result where the +translation is the exactly rounded one and native is a last place from it +(`ts/fixtures/lang/fma.ts`). + +Once [rust-lang/compiler-builtins#1358](https://github.com/rust-lang/compiler-builtins/pull/1358) +ships in the compiler-builtins that the pinned nightly in `ts/rust-toolchain.toml` +builds, delete `fma.ts` and every use of it: `caller.ts`'s import and `within`'s +call, its copy in `ts/tests/fixtures.rs`'s `lang_differential`, and its test +`ts/tests/fma.ts` with `fma_allowance` in `ts/tests/runtime.rs`, so any difference +fails again, and delete this quest. diff --git a/quest/m3/ts-unsafe.md b/quest/m3/ts-unsafe.md index 286651d7c..63185fe50 100644 --- a/quest/m3/ts-unsafe.md +++ b/quest/m3/ts-unsafe.md @@ -4,8 +4,9 @@ [rust2-ts](/ts/README.md) translates the `unsafe` operations it can give exact semantics in JS, so a dependency using only those translates from source instead of being bound by -hand. What is left: pointers into a slice or `Vec`, `ManuallyDrop`, unions over a type -parameter, and a raw pointer to a value JS copies taken through a reference. +hand. What is left: `ManuallyDrop`, unions over a type parameter, a raw pointer to a +value JS copies taken through a reference, and pointers into an array of values JS keeps +as objects. ## Plan @@ -25,8 +26,20 @@ none of its own `unsafe`. - An `unsafe` operation outside the modeled set is refused with its span, as today. - Undefined behavior in Rust need not be reproduced, but a translated program never silently differs from native where Rust's behavior is defined. -- Element pointers, `{ array, index }`, with `add`, `offset`, `offset_from`, and - `slice::from_raw_parts` over them: smallvec, arrayvec, and moq-net's `frame.rs`. +- Element pointers into arrays of values JS copies are `rt.AtIndex`, `{ array, index }`, + which `add`, `offset`, `offset_from`, `ptr::copy`, and `slice::from_raw_parts` act on. + Decided with the maintainer on 2026-11-01: where one body shows a pointer crossing what + JS keeps apart (a value in no array, a copied shared subslice), translation refuses it + with a span; a pointer from elsewhere, such as a field, fails there loudly at run time. + A pointer to an element JS keeps as an object, such as a tuple or the crate's struct, is + that object, which knows no array it is in, so arithmetic on one stays refused: + hashbrown's buckets and kio's `log.rs` reach it. smallvec's pointer arithmetic + translates, but it spills to memory from `alloc::alloc`, which this Plan refuses, so it + translates whole only once the maintainer decides how a typed allocation + (`Layout::array::`, then a cast to `*mut T`) is modeled or that smallvec is mapped. +- Decided with the maintainer on 2026-11-02: a raw slice pointer is `rt.RawSlice`, its + first element's pointer and its length, read through only where Rust reads, and an empty + slice from a pointer into no array keeps that pointer through every part of it. - `ManuallyDrop` as `T` with its drop skipped, as `MaybeUninit` is: crossbeam-epoch's `Local` reaches it, though crossbeam's tagged pointers stay refused. - A union over a type parameter, whose layout has no fixed offsets until an instance @@ -34,6 +47,10 @@ none of its own `unsafe`. - `&raw const *r` where `r` is a `&T` to a value JS copies or a library object, such as foldhash's seed and arrayvec's `as_ptr`: the reference is the value, not where it is kept, so it needs the place the reference came from. +- A `Vec`'s spare capacity written through `as_mut_ptr` and committed by `set_len`: + `set_len` is refused, and a write past the length fails loudly at run time, since the + JS array would grow by it. The spare slots need to live outside the array's length + until `set_len` takes them. - `UnsafeCell::raw_get`, `from_mut`, and `replace`, refused with their span: none is in moq's census at the pin. - A slice's pointer cast to an array's of its length, `*(s as *const [u8] as *const diff --git a/ts/README.md b/ts/README.md index 8e3047b1d..994be55cc 100644 --- a/ts/README.md +++ b/ts/README.md @@ -107,7 +107,7 @@ crate translation reaches is refused, and so are two versions of one crate it re | tuple, `[T; N]`, `&[T]`, `Box<[T]>`, `Vec` | an array; for bytes, `Uint8Array` for `[u8; N]`, `rt.ByteSlice` for `&[u8]`, `&[u8; N]`, and `Box<[u8]>`, and `rt.ByteVec` for `Vec` | | `Cow<[T]>` | `rt.Cow`: the `Vec` it owns, or an `rt.Borrowed` holding the slice it borrows; `rt.ByteCow` for bytes, an `rt.ByteVec` or an `rt.ByteSlice` | | `&T`, `&mut T`, `Box`, `Reverse`, `NonZero` | the value; a `&mut` to a value JS copies, such as a number, an `Option`, a `String`, or a reference, or to an `Arc` or a `Waker`, is an `rt.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 `rt.ByteSlice` of a `[u8; N]`'s bytes, which compares by the buffer and offset they are at, or an `rt.Place` to a value JS copies and to a library type such as a `Vec` or an `Arc`; `rt.NULL` for null and `rt.DANGLING` for dangling. A `*const ()` is an identity, such as a waker's data, and `rt.DANGLING_UNIT` when dangling; one to a place of `()` or from a reference to one is refused. A write of a whole object copies into it one object deep, as an assignment through a `&mut` does, 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 `rt.ByteSlice` of a `[u8; N]`'s bytes, which compares by the buffer and offset they are at, or an `rt.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 `rt.AtIndex` of its element, which `add`, `sub`, `offset`, their wrapping forms, `offset_from`, `ptr::copy`, and `slice::from_raw_parts` move, measure, copy between, and view; one to a value in no array is element 0 of an array of one, as in Rust; JS keeps that array, and a shared subslice, which JS copies, apart from what holds it in Rust, so a pointer, slice, or copy the body shows reaching past either, a raw or mutable slice of a byte in no array, and `offset_from` between pointers the body shows JS keeps in different arrays are refused, and through a pointer from elsewhere, such as a field, any of these, or a read or write past the array JS keeps it in, fails at run time; a pointer into an array of values JS keeps as objects is refused; `rt.NULL` for null and `rt.DANGLING` for dangling. A `*const ()` is an identity, such as a waker's data, and `rt.DANGLING_UNIT` when dangling; one to a place of `()` or from a reference to one is refused. A write of a whole object copies into it one object deep, as an assignment through a `&mut` does, 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 | | `MaybeUninit` | `T`: `undefined` until written, or an object whose parts kept as objects are there, holding `undefined` | | `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 | | an iterator | `rt.Iter`, whose adaptors are runtime functions: `rt.iterMap(rt.iterSlice(v), f)` | @@ -844,7 +844,7 @@ they need. ## Not yet -Byte reinterpretation, pointer arithmetic and addresses, raw allocation, and unions whose +Byte reinterpretation, pointer addresses, pointer arithmetic on a value JS keeps as an object (other than wrapping arithmetic on a null or dangling pointer), raw allocation, and unions whose fields overlap in different types ([unsafe](/quest/m3/ts-unsafe.md)), and trait objects other than `&dyn Debug`, `&dyn Display`, `dyn Future`, `dyn Any`, `dyn Fn*`, and the translated crates' traits' behind `&`, `&mut`, `Box`, or `Arc` ([trait objects](/quest/m3/ts-dyn.md)), fail translation. Until they translate, an item that needs one, such as a `dyn Error`, binds to @@ -913,7 +913,9 @@ built natively for wasm32 and run beside its translation on the same inputs by `NonZero`, and raw pointers: a linked list of boxed nodes as `hashlink` keeps one, through a union and a `MaybeUninit` entry, pointers to locals, fields, and whole structs, those `UnsafeCell::get` gives, as crossbeam-epoch's own `OnceLock` keeps its collector, and - those a generic type holds to its parameter, at each form a pointer takes, and a + those a generic type holds to its parameter, at each form a pointer takes, pointers + into a `Vec`, an array, a boxed slice, and a `MaybeUninit` array, as `smallvec` and + moq-net's frame segments move and copy through them, and a generic type's arrays, slices, `Vec`s, and `Cow`s of its parameter at bytes, at `u16`, and at values that drop. Its differential test runs through `caller.ts`, a diff --git a/ts/fixtures/crates/dep/src/lib.rs b/ts/fixtures/crates/dep/src/lib.rs index 2b5a0be32..d2caf2c0c 100644 --- a/ts/fixtures/crates/dep/src/lib.rs +++ b/ts/fixtures/crates/dep/src/lib.rs @@ -126,7 +126,7 @@ fn read(bytes: &[u8]) -> u8 { pub fn last(bytes: &[u8]) -> u8 { assert!(!bytes.is_empty(), "no last byte"); // SAFETY: it holds a byte. - unsafe { *bytes.as_ptr().add(bytes.len() - 1) } + unsafe { *bytes.as_ptr().byte_add(bytes.len() - 1) } } /// What [`tally`] counts with, through trait objects of either crate's types. diff --git a/ts/fixtures/crates/refused.txt b/ts/fixtures/crates/refused.txt index bfc265279..844f8718b 100644 --- a/ts/fixtures/crates/refused.txt +++ b/ts/fixtures/crates/refused.txt @@ -1,2 +1 @@ -unsupported call to `core::slice::::as_ptr` at fixtures/crates/dep/src/lib.rs:129:18 -unsupported call to `std::ptr::const_ptr::::add` at fixtures/crates/dep/src/lib.rs:129:27 +unsupported call to `std::ptr::const_ptr::::byte_add` at fixtures/crates/dep/src/lib.rs:129:27 diff --git a/ts/fixtures/lang/caller.ts b/ts/fixtures/lang/caller.ts index b4656a033..b6e847b50 100644 --- a/ts/fixtures/lang/caller.ts +++ b/ts/fixtures/lang/caller.ts @@ -3,6 +3,7 @@ // which nothing translated calls. `differential.ts` runs it beside native Rust's // `harness::entry`, from the output directory. +import { mulAddsWithin } from "./fma.ts"; import * as Fault from "./fmt.Fault.ts"; import { cases as named, run as harness, op as pick } from "./harness.ts"; import * as rt from "./rt.ts"; @@ -231,7 +232,7 @@ const mulAdds = (max: number, tiny: number, eps: number, op: number[], bytes: (x const floatsMulAdds = mulAdds(Number.MAX_VALUE, 5e-324, 2 ** -52, op("floats::mul_adds"), double); const singlesMulAdds = mulAdds(3.4028234663852886e38, 2 ** -149, 2 ** -23, op("singles::mul_adds"), single); -const [floatsAt, singlesAt] = [pick("floats::floats"), pick("singles::singles")]; +const [floatsAt, singlesAt, floatsMulAddsAt] = [pick("floats::floats"), pick("singles::singles"), pick("floats::mul_adds")]; /** How many ULPs from native Rust's each float of `floats`' and `singles`' kind 7 may be, which Rust leaves to libm. */ const ULPS = 2n; @@ -240,6 +241,7 @@ export function within(input: Uint8Array, native: Uint8Array, translated: Uint8A // A first byte of 0xff prints a `Fault`, and picks no case. if (input[0] === 0xff) return false; const at = ((input[0] << 8) | input[1]) % count; + if (at === floatsMulAddsAt) return mulAddsWithin(native, translated); const width = at === floatsAt ? 8 : at === singlesAt ? 4 : 0; if (width === 0 || input[2] % 10 !== 7 || native.length !== translated.length) return false; const [a, b] = [native, translated].map((v) => new DataView(v.buffer, v.byteOffset, v.length)); diff --git a/ts/fixtures/lang/expected/core/bool.ts b/ts/fixtures/lang/expected/core/bool.ts index 91fbaa4c3..500833f8d 100644 --- a/ts/fixtures/lang/expected/core/bool.ts +++ b/ts/fixtures/lang/expected/core/bool.ts @@ -98,6 +98,13 @@ export function thenSome$1jqdi5p(self: boolean, t: rt.Duration): rt.Duration | u return t; } +export function thenSome$03chm1q(self: boolean, t: [rt.u8, rt.u8]): [rt.u8, rt.u8] | undefined { + if (!self) { + return undefined; + } + return t; +} + export function thenSome$0b23a33(self: boolean, t: rt.u16): rt.u16 | undefined { if (!self) { return undefined; diff --git a/ts/fixtures/lang/expected/core/option.ts b/ts/fixtures/lang/expected/core/option.ts index 253343235..cc5889673 100644 --- a/ts/fixtures/lang/expected/core/option.ts +++ b/ts/fixtures/lang/expected/core/option.ts @@ -650,6 +650,13 @@ export function map$12l9avg(self: rt.u8 | undefined, f: (a0: rt.u8) => Ticked.Ti return f(self); } +export function map$1fmzefe(self: rt.u8 | undefined, f: (a0: rt.u8) => [rt.u8, rt.u8]): [rt.u8, rt.u8] | undefined { + if (self === undefined) { + return undefined; + } + return f(self); +} + export function map$1qp1ym6(self: rt.u8 | undefined, f: (a0: rt.u8) => rt.u32): rt.u32 | undefined { if (self === undefined) { return undefined; @@ -805,13 +812,6 @@ export function map$1pb5zw8(self: rt.u8 | undefined, f: (a0: rt.u8) => [rt.u16, return f(self); } -export function map$1fmzefe(self: rt.u8 | undefined, f: (a0: rt.u8) => [rt.u8, rt.u8]): [rt.u8, rt.u8] | undefined { - if (self === undefined) { - return undefined; - } - return f(self); -} - export function map$1c7zybz(self: rt.u8 | undefined, _: (a0: rt.u8) => rt.u32): rt.u32 | undefined { if (self === undefined) { return undefined; @@ -1092,6 +1092,13 @@ export function mapOr$1w5qtj7(self: undefined | rt.None, default_: rt.u8, f: (a0 return f(self); } +export function mapOr$0grxkdu(self: rt.u32[] | undefined, default_: rt.u32, f: (a0: rt.u32[]) => rt.u32): rt.u32 { + if (self === undefined) { + return default_; + } + return f(self); +} + export function mapOr$1giiemz(self: rt.Place | undefined, default_: rt.u32, f: (a0: rt.Place) => rt.u32): rt.u32 { if (self === undefined) { return default_; diff --git a/ts/fixtures/lang/expected/elements.Inline.ts b/ts/fixtures/lang/expected/elements.Inline.ts new file mode 100644 index 000000000..b5c7daf82 --- /dev/null +++ b/ts/fixtures/lang/expected/elements.Inline.ts @@ -0,0 +1,108 @@ +// Generated by rust2-ts. Do not edit. + +import * as rt from "./rt.ts"; +import { copy$Option_tuple2_u8_u8, copy$mem_MaybeUninit_array_Option_tuple2_u8_u8 } from "./lib.ts"; + +/** + * Up to four values kept inline in a `MaybeUninit` array, as `smallvec` keeps them, + * inserted and removed by moving the rest through pointers, and dropped in place. + */ +export class Inline { + len: rt.usize; + items: rt.ArrayOf; + + constructor(len: rt.usize, items: rt.ArrayOf) { + this.len = len; + this.items = items; + } +} + +export function new$Option_tuple2_u8_u8(): Inline<[rt.u8, rt.u8] | undefined> { + return new Inline(0, copy$mem_MaybeUninit_array_Option_tuple2_u8_u8((new Array(4) as unknown) as Array<[rt.u8, rt.u8] | undefined>)); +} + +export function new$sync_Arc_u8(): Inline> { + return new Inline(0, (new Array(4) as unknown) as rt.Arc[]); +} + +export function ptr$Option_tuple2_u8_u8(self: Inline<[rt.u8, rt.u8] | undefined>): rt.Place<[rt.u8, rt.u8] | undefined> { + return rt.sliceAsPtr(self.items as Array<[rt.u8, rt.u8] | undefined>); +} + +export function ptr$sync_Arc_u8(self: Inline>): rt.Place> { + return rt.sliceAsPtr(self.items as rt.Arc[]); +} + +export function asSlice$Option_tuple2_u8_u8(self: Inline<[rt.u8, rt.u8] | undefined>): Array<[rt.u8, rt.u8] | undefined> { + return rt.ptrSlice(rt.sliceAsPtr(self.items as Array<[rt.u8, rt.u8] | undefined>), self.len); +} + +export function asSlice$sync_Arc_u8(self: Inline>): rt.Arc[] { + return rt.ptrSlice(rt.sliceAsPtr(self.items as rt.Arc[]), self.len); +} + +export function insert$Option_tuple2_u8_u8(self: Inline<[rt.u8, rt.u8] | undefined>, at: rt.usize, value: [rt.u8, rt.u8] | undefined): void { + if (self.len < 4) { + if (at <= self.len) { + const len: rt.usize = self.len; + const p: rt.Place<[rt.u8, rt.u8] | undefined> = rt.ptrAdd(ptr$Option_tuple2_u8_u8(self), at); + rt.ptrCopy(p, rt.ptrAdd(p, 1), rt.usizeSub(len, at), copy$Option_tuple2_u8_u8); + p.v = copy$Option_tuple2_u8_u8(value); + self.len = rt.usizeAdd(self.len, 1); + return; + } + } + rt.panic("assertion failed: self.len < 4 && at <= self.len"); +} + +export function insert$sync_Arc_u8(self: Inline>, at: rt.usize, value: rt.Arc): void { + if (self.len < 4) { + if (at <= self.len) { + const len: rt.usize = self.len; + const p: rt.Place> = rt.ptrAdd(ptr$sync_Arc_u8(self), at); + rt.ptrCopy(p, rt.ptrAdd(p, 1), rt.usizeSub(len, at)); + p.v = value; + self.len = rt.usizeAdd(self.len, 1); + return; + } + } + rt.panic("assertion failed: self.len < 4 && at <= self.len"); +} + +export function remove$Option_tuple2_u8_u8(self: Inline<[rt.u8, rt.u8] | undefined>, at: rt.usize): [rt.u8, rt.u8] | undefined { + if (!(at < self.len)) { + rt.panic("assertion failed: at < self.len"); + } else { + self.len = rt.usizeSub(self.len, 1); + const len: rt.usize = self.len; + const p: rt.Place<[rt.u8, rt.u8] | undefined> = rt.ptrAdd(ptr$Option_tuple2_u8_u8(self), at); + const value: [rt.u8, rt.u8] | undefined = copy$Option_tuple2_u8_u8(p.v); + rt.ptrCopy(rt.ptrAdd(p, 1), p, rt.usizeSub(len, at), copy$Option_tuple2_u8_u8); + return value; + } +} + +export function remove$sync_Arc_u8(self: Inline>, at: rt.usize): rt.Arc { + if (!(at < self.len)) { + rt.panic("assertion failed: at < self.len"); + } else { + self.len = rt.usizeSub(self.len, 1); + const len: rt.usize = self.len; + const p: rt.Place> = rt.ptrAdd(ptr$sync_Arc_u8(self), at); + const value: rt.Arc = p.v; + rt.ptrCopy(rt.ptrAdd(p, 1), p, rt.usizeSub(len, at)); + return value; + } +} + +export function drop$ops_Drop_Option_tuple2_u8_u8(self: Inline<[rt.u8, rt.u8] | undefined>): void { + const len: rt.usize = self.len; + new rt.RawSlice(ptr$Option_tuple2_u8_u8(self), len); +} + +export function drop$ops_Drop_sync_Arc_u8(self: Inline>): void { + const len: rt.usize = self.len; + rt.dropAll(new rt.RawSlice(ptr$sync_Arc_u8(self), len).deref()); +} + +//# sourceMappingURL=elements.Inline.ts.map diff --git a/ts/fixtures/lang/expected/elements.Storage.ts b/ts/fixtures/lang/expected/elements.Storage.ts new file mode 100644 index 000000000..6b9471060 --- /dev/null +++ b/ts/fixtures/lang/expected/elements.Storage.ts @@ -0,0 +1,43 @@ +// Generated by rust2-ts. Do not edit. + +import * as rt from "./rt.ts"; + +export class Storage { + data: rt.Place; + capacity: rt.usize; + + constructor(data: rt.Place, capacity: rt.usize) { + this.data = data; + this.capacity = capacity; + } + + [Symbol.dispose](): void { + drop$ops_Drop(this); + } +} + +function new_(capacity: rt.usize): Storage { + const boxed: rt.ByteSlice = rt.byteVecSlice(rt.byteVecOf(0, capacity)); + const capacity2: rt.usize = boxed.length; + return new Storage(rt.rawByteSliceOf(boxed).ptr, capacity2); +} + +export function write(self: Storage, offset: rt.usize, src: rt.ByteSlice): void { + if (!(rt.usizeAdd(offset, src.length) <= self.capacity)) { + rt.panic("assertion failed: offset + src.len() <= self.capacity"); + } else { + rt.ptrCopy(rt.byteSliceAsPtr(src), rt.ptrAdd(self.data, offset), src.length); + } +} + +export function read(self: Storage, len: rt.usize): rt.ByteSlice { + return rt.ptrByteSlice(self.data, len, true); +} + +export function drop$ops_Drop(self: Storage): void { + new rt.RawSlice(self.data, self.capacity).derefBytes(false); +} + +export { new_ as new }; + +//# sourceMappingURL=elements.Storage.ts.map diff --git a/ts/fixtures/lang/expected/elements.ts b/ts/fixtures/lang/expected/elements.ts new file mode 100644 index 000000000..179126a4b --- /dev/null +++ b/ts/fixtures/lang/expected/elements.ts @@ -0,0 +1,508 @@ +// Generated by rust2-ts. Do not edit. + +import * as rt from "./rt.ts"; +import { copy$Option_tuple2_u8_u8, copy$array_Option_tuple2_u8_u8, copy$mem_MaybeUninit_Result_tuple2_u32_u32_u8, drop$elements_Inline_Option_tuple2_u8_u8, drop$elements_Inline_sync_Arc_u8 } from "./lib.ts"; +import { thenSome$03chm1q } from "./core/bool.ts"; +import { unwrapOr$0r2bmpr as option_unwrapOr$0r2bmpr, map$1fmzefe as option_map$1fmzefe, mapOr$0grxkdu as option_mapOr$0grxkdu, mapOr$1nlkf9b as option_mapOr$1nlkf9b } from "./core/option.ts"; +import { offsetFrom } from "./elements/mine/ptr.ts"; +import { fromRawPartsMut } from "./elements/mine/raw.ts"; +import * as Inline from "./elements.Inline.ts"; +import * as Storage from "./elements.Storage.ts"; + +/** + * A `Vec`'s elements moved within it as `insert` and `remove` move them, and copied to + * another, read back through slices made of the pointers. + */ +export function vecs(data: rt.ByteSlice, out: rt.ByteVec): void { + const v: rt.u32[] = rt.iterToArray(rt.iterMap(rt.iterSlice(rt.byteSliceView(data)), (_2: rt.u8): rt.u32 => { + return rt.u32Mul(_2 as rt.u32, 3); + }), undefined, 4, true); + rt.iterExtend(v, rt.iterSlice([7, 8]), undefined, 4, true); + const len: rt.usize = v.length; + const at: rt.usize = rt.usizeRem(option_unwrapOr$0r2bmpr(rt.byteSliceFirst(data), 0) as rt.usize, rt.usizeSub(len, 1)); + const p: rt.Place = rt.sliceAsPtr(v); + rt.ptrCopy(rt.ptrAdd(p, at), rt.ptrAdd(p, rt.usizeAdd(at, 1)), rt.usizeSub(rt.usizeSub(len, at), 1)); + rt.ptrAdd(p, at).v = 1000; + emit(rt.ptrSlice(p, len), out); + rt.ptrCopy(rt.ptrAdd(p, rt.usizeAdd(at, 1)), rt.ptrAdd(p, at), rt.usizeSub(rt.usizeSub(len, at), 1)); + rt.ptrAdd(rt.ptrAdd(p, rt.usizeSub(len, 1)), -0).v = 2000; + emit(rt.ptrSlice(p, len), out); + const end: rt.Place = rt.ptrAdd(p, len); + const last: rt.Place = rt.ptrAdd(end, -1); + const _54: rt.Place = rt.ptrWrappingAdd(last, -1, 4, 4); + _54.v = rt.u32Add(_54.v, 1); + const _56: rt.Place = rt.ptrWrappingAdd(rt.ptrWrappingAdd(p, 1, 4, 4), -1, 4, 4); + _56.v = rt.u32Add(_56.v, 5); + rt.byteVecPush(out, rt.ptrOffsetFrom(end, p) & 0xff); + rt.byteVecPush(out, rt.ptrOffsetFrom(p, last) & 0xff); + rt.byteVecPush(out, rt.ptrOffsetFrom(last, last) & 0xff); + const part: rt.u32[] = rt.ptrSlice(rt.ptrAdd(p, at), rt.usizeSub(len, at)); + rt.idx(part, 0); + part[0] = rt.u32Add(part[0], 3); + part.reverse(); + const w: rt.u32[] = rt.vecFromElem(0, len, undefined); + rt.ptrCopy(p, rt.sliceAsPtr(w), len); + rt.ptrCopy(p, rt.ptrAdd(rt.sliceAsPtr(w), at), 1); + rt.ptrCopy(p, rt.ptrAdd(rt.sliceAsPtr(w), rt.usizeSub(len, 1)), 1); + emit(w, out); + emit(v, out); + rt.byteVecPush(out, rt.ptrAdd(rt.sliceAsPtr(v), rt.usizeDiv(len, 2)).v & 0xff); + rt.byteVecPush(out, rt.ptrSlice(rt.DANGLING, 0).length & 0xff); +} + +/** An array's elements, from a pointer to one, to the array, and to its first. */ +export function arrays(data: rt.ByteSlice, out: rt.ByteVec): void { + for (const n of rt.byteSliceView(data)) { + const a: rt.u16[] = [n as rt.u16, 1, 2, 3, 4]; + const i: rt.usize = rt.usizeRem(n as rt.usize, 4); + if (!(i < 5)) { + rt.panic("index out of bounds: the len is " + 5 + " but the index is " + i); + } + const at: rt.Place = rt.sliceAsPtr(a, i); + at.v = 99; + const whole: rt.Place = rt.sliceAsPtr(a); + const _18: rt.Place = rt.ptrAdd(whole, rt.usizeAdd(i, 1)); + _18.v = rt.u16Add(_18.v, 10); + const _22: rt.u16 = n as rt.u16; + const _23: rt.Place = rt.ptrAdd(whole, 4); + _23.v = rt.u16Add(_23.v, _22); + const first: rt.Place = rt.sliceAsPtr(a); + rt.byteVecPush(out, rt.ptrOffsetFrom(at, whole) & 0xff); + rt.byteVecPush(out, rt.ptrOffsetFrom(first, at) & 0xff); + const p: rt.Place = first; + const _37: rt.Place = rt.ptrAdd(p, 2); + _37.v = rt.u16Add(_37.v, 1); + const _40: rt.Place = rt.ptrAdd(rt.ptrAdd(p, 3), -3); + _40.v = rt.u16Add(_40.v, 2); + rt.byteVecPush(out, rt.ptrOffsetFrom(rt.ptrAdd(p, 4), p) & 0xff); + const tail: rt.RawSlice = new rt.RawSlice(rt.ptrAdd(p, i), rt.usizeSub(5, i)); + rt.byteVecPush(out, tail.deref().length & 0xff); + tail.ptr.v = 7; + const unit: rt.Place = rt.DANGLING_UNIT; + n as rt.usize; + rt.byteVecPush(out, unit === unit ? 1 : 0); + rt.iterExtendBytes(out, rt.iterFlatMap(rt.iterSlice(a), (x: rt.u16): Uint8Array => { + return rt.u16ToBeBytes(x).reverse(); + }, (value: Uint8Array) => rt.iterSlice(value), false, 2), undefined, true); + } +} + +/** + * A boxed slice of bytes kept behind a pointer to its first, as moq-net's frame + * segment keeps its storage: written by copies at offsets, read by slices of it, and + * freed as the slice it was. + */ +export function segments(data: rt.ByteSlice, out: rt.ByteVec): void { + const segment: Storage.Storage = Storage.new(rt.usizeAdd(data.length, 4)); + Storage.write(segment, 0, data); + Storage.write(segment, data.length, rt.byteSliceFrom(Uint8Array.of(1, 2, 3, 4))); + rt.byteVecExtendFromSlice(out, Storage.read(segment, rt.usizeAdd(data.length, 4))); + const p: rt.Place = segment.data; + rt.ptrCopy(p, rt.ptrAdd(p, 2), data.length); + rt.byteVecExtendFromSlice(out, Storage.read(segment, rt.usizeAdd(data.length, 4))); + rt.ptrCopy(rt.ptrAdd(p, 3), rt.ptrAdd(p, 1), data.length); + const _36: rt.Place = rt.ptrAdd(p, rt.usizeAdd(data.length, 3)); + _36.v = _36.v ^ 255; + rt.byteVecExtendFromSlice(out, Storage.read(segment, rt.usizeAdd(data.length, 4))); + const v: rt.ByteVec = rt.byteSliceToByteVec(data); + rt.byteVecPush(v, 9); + const a: Uint8Array = rt.bytes(4); + const p2: rt.Place = rt.byteVecAsPtr(v); + const _52: rt.Place = rt.ptrAdd(p2, rt.usizeSub(v.length, 1)); + _52.v = rt.u8Add(_52.v, 1); + rt.ptrCopy(p2, rt.byteSliceAsPtr(rt.byteSliceFrom(a)), rt.min(v.length, 4)); + const byte: rt.Place = rt.ptrAdd(rt.byteSliceAsPtr(rt.byteSliceFrom(a)), 1); + rt.ptrAdd(byte, 2).v = 85; + rt.byteVecExtendFromSlice(out, rt.ptrByteSlice(rt.ptrAdd(byte, -1), 4, true)); + const one: rt.Place = rt.sliceAsPtr(a, 2); + one.v = one.v ^ 15; + rt.byteVecPush(out, one.v); + rt.iterExtendBytes(out, rt.iterSlice(rt.byteVecView(v)), undefined, true); + segment[Symbol.dispose](); +} + +/** + * Shared values and `Option`s of pairs moved through an inline array, counting the + * shared values' owners, and a pair changed in place after its copy moved. + */ +export function inline(data: rt.ByteSlice, out: rt.ByteVec): void { + let item!: rt.Arc; + const shared: rt.Arc = new rt.Arc(0); + const held: Inline.Inline> = Inline.new$sync_Arc_u8(); + const pairs: Inline.Inline<[rt.u8, rt.u8] | undefined> = Inline.new$Option_tuple2_u8_u8(); + const iter: rt.Iter<[rt.usize, rt.u8]> = rt.iterEnumerate(rt.iterSlice(rt.byteSliceView(data))); + while (true) { + const _10: [rt.usize, rt.u8] | rt.Done = iter.next(); + if (_10 === rt.DONE) { + break; + } + const i: rt.usize = _10[0]; + const n: rt.u8 = _10[1]; + if (held.len === 4) { + const gone: rt.Arc = Inline.remove$sync_Arc_u8(held, rt.usizeRem(n as rt.usize, 4)); + rt.byteVecPush(out, gone.value); + Inline.remove$Option_tuple2_u8_u8(pairs, rt.usizeRem(n as rt.usize, 4)); + gone[Symbol.dispose](); + } + const at: rt.usize = rt.usizeRem(n as rt.usize, rt.usizeAdd(held.len, 1)); + if (rt.u8Rem(n, 3) === 0) { + item = rt.arcClone(shared); + } else { + item = new rt.Arc(n); + } + Inline.insert$sync_Arc_u8(held, at, item); + const _46: [rt.u8, rt.u8] | undefined = thenSome$03chm1q(rt.u8Rem(n, 2) === 0, [n, i & 0xff] as [rt.u8, rt.u8]); + Inline.insert$Option_tuple2_u8_u8(pairs, at, _46); + const _52: [rt.u8, rt.u8] | undefined | rt.None = rt.sliceFirst(Inline.asSlice$Option_tuple2_u8_u8(pairs), rt.None); + if (_52 !== rt.None) { + if (_52 !== undefined) { + rt.byteVecPush(out, _52[0]); + } + } + const pointer = Inline.ptr$Option_tuple2_u8_u8(pairs); + const _60: rt.Place<[rt.u8, rt.u8] | undefined> | undefined = pointer === rt.NULL ? undefined : pointer; + if (_60 !== undefined) { + if (_60.v !== undefined) { + _60.v[1] = rt.u8WrappingAdd(_60.v[1], 1); + } + } + rt.byteVecPush(out, shared.strong & 0xff); + rt.iterExtendBytes(out, rt.iterMap(rt.iterSlice(Inline.asSlice$sync_Arc_u8(held)), (x: rt.Arc): rt.u8 => { + return x.value; + }), undefined, true); + rt.iterExtendBytes(out, rt.iterFlatMap(rt.iterSlice(Inline.asSlice$Option_tuple2_u8_u8(pairs)), (p: [rt.u8, rt.u8] | undefined): Uint8Array => { + return option_mapOr$1nlkf9b(p, Uint8Array.of(0, 0), (_2: [rt.u8, rt.u8]): Uint8Array => { + return Uint8Array.of(_2[0], _2[1]); + }); + }, (value: Uint8Array) => rt.iterSlice(value), false, 2), undefined, true); + } + drop$elements_Inline_sync_Arc_u8(held); + rt.byteVecPush(out, shared.strong & 0xff); + const src: Array<[rt.u8, rt.u8] | undefined> = copy$array_Option_tuple2_u8_u8([[1, 2] as [rt.u8, rt.u8], undefined, option_map$1fmzefe(rt.byteSliceFirst(data), (_22: rt.u8): [rt.u8, rt.u8] => { + return [_22, 4] as [rt.u8, rt.u8]; + })]).map(copy$Option_tuple2_u8_u8); + const dst: Array<[rt.u8, rt.u8] | undefined> = rt.vecFromElem(undefined as [rt.u8, rt.u8] | undefined, 3, copy$Option_tuple2_u8_u8); + rt.ptrCopy(rt.sliceAsPtr(src), rt.sliceAsPtr(dst), 3, copy$Option_tuple2_u8_u8); + const at2 = rt.idx(dst, 0); + if (dst[at2] !== undefined) { + dst[at2][0] = 9; + } + const at3 = rt.idx(src, 2); + if (src[at3] !== undefined) { + src[at3][1] = 8; + } + rt.iterExtendBytes(out, rt.iterFlatMap(rt.iterChain(rt.iterSlice(src), rt.iterSlice(dst)), (p2: [rt.u8, rt.u8] | undefined): Uint8Array => { + return option_mapOr$1nlkf9b(p2, Uint8Array.of(0, 0), (_25: [rt.u8, rt.u8]): Uint8Array => { + return Uint8Array.of(_25[0], _25[1]); + }); + }, (value: Uint8Array) => rt.iterSlice(value), false, 2), undefined, true); + drop$elements_Inline_Option_tuple2_u8_u8(pairs); + shared[Symbol.dispose](); +} + +/** + * Pointers wrapped around wasm32's 32-bit addresses and back, to a value in no array, as + * the array of one Rust treats it as, and into a mutable part of an array, measured from + * the array, the pointer an empty slice keeps, within a shared part, which JS copies, + * a `&mut` into that part, memory nothing wrote copied, null and dangling pointers moved by + * address, kept by empty raw slices, and cast from pointers to arrays, and the crate's + * functions named as std's. + */ +export function edges(data: rt.ByteSlice, out: rt.ByteVec): void { + let _200!: rt.Result<[rt.u32, rt.u32], rt.u8>; + let _206!: rt.u8; + for (const n of rt.byteSliceView(data)) { + const bytes: Uint8Array = Uint8Array.of(n, 1, 2, 3); + const p: rt.Place = rt.byteSliceAsPtr(rt.byteSliceFrom(bytes)); + const q: rt.Place = rt.sliceAsPtr([n as rt.u16, 7, 8]); + rt.byteVecPush(out, rt.ptrWrappingAdd(rt.ptrWrappingAdd(p, 4294967295, 1, 1), 1, 1, 1).v); + rt.byteVecPush(out, rt.ptrWrappingAdd(rt.ptrWrappingAdd(p, 2147483648, 1, 1), 2147483648, 1, 1).v); + rt.byteVecPush(out, rt.ptrWrappingAdd(rt.ptrWrappingAdd(p, -4294967295, 1, 1), -1, 1, 1).v); + rt.iterExtendBytes(out, rt.iterSlice(rt.u16ToBeBytes(rt.ptrWrappingAdd(rt.ptrWrappingAdd(q, 2147483648, 2, 2), 1, 2, 2).v).reverse()), undefined, true); + rt.iterExtendBytes(out, rt.iterSlice(rt.u16ToBeBytes(rt.ptrWrappingAdd(rt.ptrWrappingAdd(rt.ptrWrappingAdd(q, 3221225472, 2, 2), 1 << 30, 2, 2), 2, 2, 2).v).reverse()), undefined, true); + let lone: rt.u32 = rt.u32Add(n as rt.u32, 42); + const at: rt.Place = rt.place(() => lone, (value) => lone = value); + const _63: rt.u32[] = rt.ptrSlice(at, 1); + rt.idx(_63, 0); + rt.byteVecPush(out, _63[0] & 0xff); + const _68: rt.u32[] = rt.ptrSlice(at, 1); + rt.idx(_68, 0); + _68[0] = rt.u32Add(_68[0], 1); + rt.byteVecPush(out, rt.ptrAdd(rt.ptrAdd(at, 1), -1).v & 0xff); + rt.byteVecPush(out, rt.ptrOffsetFrom(rt.ptrAdd(at, 1), at) & 0xff); + rt.byteVecPush(out, rt.ptrOffsetFrom(rt.sliceAsPtr(rt.ptrSlice(at, 0)), at) & 0xff); + rt.byteVecPush(out, lone & 0xff); + let byte: rt.u8 = n ^ 90; + const _97: rt.ByteSlice = rt.ptrByteSlice(rt.place(() => byte, (value2) => byte = value2), 1, true); + rt.idx(_97, 0); + rt.byteVecPush(out, _97.data[_97.start]); + const a: rt.u32[] = [n as rt.u32, 1, 2, 3]; + const base: rt.Place = rt.sliceAsPtr(a); + const part: rt.u32[] = rt.sliceRangeMut(a, 1, 3); + rt.idx(part, 1); + const at2: rt.Place = rt.sliceAsPtr(part, 1); + rt.byteVecPush(out, rt.ptrOffsetFrom(at2, base) & 0xff); + at2.v = rt.u32Add(at2.v, 5); + const got: rt.Place = rt.optionUnwrap(rt.sliceGetMut(part, 0)); + rt.byteVecPush(out, rt.ptrOffsetFrom(got, base) & 0xff); + rt.byteVecPush(out, rt.ptrAdd(got, -1).v & 0xff); + rt.byteVecPush(out, rt.ptrOffsetFrom(rt.sliceAsPtr(rt.ptrSlice(rt.ptrAdd(at2, 1), 0)), base) & 0xff); + rt.byteVecPush(out, rt.ptrOffsetFrom(rt.byteSliceAsPtr(rt.ptrByteSlice(rt.ptrAdd(rt.byteSliceAsPtr(rt.byteSliceFrom(bytes)), 2), 0, true)), p) & 0xff); + rt.iterExtendBytes(out, rt.iterMap(rt.iterSlice(a), (_2: rt.u32): rt.u8 => { + return _2 & 0xff; + }), undefined, true); + const lanes: rt.u32[] = [n as rt.u32, 4, 5, 6]; + const first: rt.Place = rt.sliceAsPtr(rt.sliceRange(lanes, 1, 3)); + rt.byteVecPush(out, rt.ptrAdd(first, 1).v & 0xff); + rt.byteVecPush(out, rt.ptrOffsetFrom(rt.ptrAdd(first, 2), first) & 0xff); + const _170: rt.u32[] = rt.ptrSlice(rt.ptrAdd(first, 1), 1); + rt.idx(_170, 0); + rt.byteVecPush(out, _170[0] & 0xff); + const _177: [rt.u32, rt.u8] = [n as rt.u32, n]; + let x: rt.u32 = _177[0]; + let y: rt.u8 = n; + rt.byteVecPush(out, offsetFrom(rt.place(() => x, (value3) => x = value3), rt.place(() => x, (value4) => x = value4)) & 0xff); + rt.byteVecPush(out, fromRawPartsMut(rt.place(() => y, (value5) => y = value5), 2)); + const item = (undefined as unknown) as rt.Result<[rt.u32, rt.u32], rt.u8>; + const item2 = (undefined as unknown) as rt.Result<[rt.u32, rt.u32], rt.u8>; + const dst: rt.Result<[rt.u32, rt.u32], rt.u8>[] = Array.from({ length: 2 }, (): rt.Result<[rt.u32, rt.u32], rt.u8> => { + return copy$mem_MaybeUninit_Result_tuple2_u32_u32_u8(item2); + }); + rt.ptrCopy(rt.sliceAsPtr(Array.from({ length: 2 }, (): rt.Result<[rt.u32, rt.u32], rt.u8> => { + return copy$mem_MaybeUninit_Result_tuple2_u32_u32_u8(item); + })), rt.sliceAsPtr(dst), 2, copy$mem_MaybeUninit_Result_tuple2_u32_u32_u8); + if (!((n & 1) === 0)) { + _200 = rt.Err(n); + } else { + _200 = rt.Ok([x, 1] as [rt.u32, rt.u32]); + } + const uninit = rt.place(() => dst[1], (value6) => dst[1] = value6); + uninit.v = _200; + uninit; + const _207: rt.Result<[rt.u32, rt.u32], rt.u8> = dst[1]; + if (_207.ok) { + _206 = rt.u32Add(_207.value[0], _207.value[1]) & 0xff; + } else { + _206 = _207.error; + } + rt.byteVecPush(out, _206); + const null_: rt.Place = rt.NULL; + rt.byteVecPush(out, rt.ptrWrappingAdd(rt.ptrWrappingAdd(null_, 1, 4, 4), -1, 4, 4) === rt.NULL ? 1 : 0); + rt.byteVecPush(out, rt.ptrWrappingAdd(null_, n as rt.usize, 4, 4) === rt.NULL ? 1 : 0); + rt.byteVecPush(out, rt.ptrWrappingAdd(null_, 1 << 30, 4, 4) === rt.NULL ? 1 : 0); + const _237: [rt.Place, rt.Place] = [rt.ptrWrappingAdd(null_, rt.usizeAdd(n as rt.usize, 1), 4, 4), rt.ptrWrappingAdd(null_, rt.usizeAdd(n as rt.usize, 1), 4, 4)]; + rt.byteVecPush(out, rt.ptrOffsetFrom(_237[0], _237[1]) & 0xff); + const dangling: rt.Place = rt.DANGLING; + rt.byteVecPush(out, rt.ptrWrappingAdd(dangling, -1, 2, 2) === rt.NULL ? 1 : 0); + rt.byteVecPush(out, rt.ptrWrappingAdd(dangling, 4294967295, 2, 2) === rt.NULL ? 1 : 0); + rt.byteVecPush(out, rt.ptrWrappingAdd(rt.ptrWrappingAdd(dangling, -((n as rt.usize)), 2, 2), n as rt.usize, 2, 2) === rt.NULL ? 1 : 0); + rt.byteVecPush(out, rt.ptrWrappingAdd(rt.DANGLING, rt.isizeSub(rt.u8Rem(n, 3) as rt.isize, 1), 8, 8) === rt.NULL ? 1 : 0); + rt.byteVecPush(out, new rt.RawSlice(rt.NULL, 0).ptr === rt.NULL ? 1 : 0); + rt.byteVecPush(out, new rt.RawSlice(rt.NULL, 0).ptr === rt.NULL ? 1 : 0); + rt.byteVecPush(out, rt.ptrWrappingAdd(new rt.RawSlice(rt.DANGLING, 0).ptr, -1, 2, 2) === rt.NULL ? 1 : 0); + rt.byteVecPush(out, new rt.RawSlice(rt.NULL, 0).ptr === rt.NULL ? 1 : 0); + rt.byteVecPush(out, new rt.RawSlice(rt.NULL, 0).ptr === rt.NULL ? 1 : 0); + const none2: rt.ByteSlice = rt.ptrByteSlice(rt.DANGLING, 0, true); + rt.byteVecPush(out, rt.ptrWrappingAdd(rt.byteSliceAsPtr(none2), -1, 1, 1) === rt.NULL ? 1 : 0); + rt.byteVecPush(out, rt.ptrWrappingAdd(rt.byteSliceAsPtr(rt.byteSliceRange(none2, 0, 0)), -1, 1, 1) === rt.NULL ? 1 : 0); + const wide2: rt.u16[] = rt.ptrSlice(rt.DANGLING, 0); + rt.byteVecPush(out, rt.ptrWrappingAdd(rt.sliceAsPtr(rt.sliceRange(wide2, 0, 0)), -1, 2, 2) === rt.NULL ? 1 : 0); + rt.byteVecPush(out, rt.ptrWrappingAdd(rt.sliceAsPtr(rt.optionUnwrap(rt.sliceGetRange(wide2, 0, 0))), -1, 2, 2) === rt.NULL ? 1 : 0); + rt.byteVecPush(out, new rt.RawSlice(rt.sliceAsPtr(rt.NULL), 3).ptr === rt.NULL ? 1 : 0); + const row: rt.u32[] = [n as rt.u32, 9, 10]; + const unsizedLanes: rt.RawSlice = new rt.RawSlice(rt.sliceAsPtr(row), 3); + rt.byteVecPush(out, unsizedLanes.ptr === rt.NULL ? 1 : 0); + rt.byteVecPush(out, rt.ptrAdd(unsizedLanes.ptr, 2).v & 0xff); + rt.byteVecPush(out, rt.ptrWrappingAdd(new rt.RawSlice(rt.ptrWrappingAdd(rt.NULL, rt.usizeAdd(rt.u8Rem(n, 3) as rt.usize, 1), 4, 4), 0).ptr, -rt.usizeAdd(rt.u8Rem(n, 3) as rt.usize, 1), 4, 4) === rt.NULL ? 1 : 0); + rt.byteVecPush(out, rt.sliceAsPtr(rt.NULL) === rt.NULL ? 1 : 0); + rt.byteVecPush(out, rt.ptrWrappingAdd(rt.byteSliceAsPtr(rt.NULL), n as rt.usize, 1, 1) === rt.NULL ? 1 : 0); + rt.byteVecPush(out, rt.ptrWrappingAdd(rt.sliceAsPtr(rt.DANGLING), -1, 2, 2) === rt.NULL ? 1 : 0); + } +} + +/** + * Raw pointers to slices, each where it starts and how long it is: read and written + * through, whole and in part, of bytes too, made longer than what is there and never + * read past it, `NonNull`'s, a boxed slice's, and null's. + */ +export function rawSlices(data: rt.ByteSlice, out: rt.ByteVec): void { + const n: rt.u8 = option_unwrapOr$0r2bmpr(rt.byteSliceFirst(data), 0); + const v: rt.u32[] = rt.iterToArray(rt.iterChain(rt.iterMap(rt.iterSlice(rt.byteSliceView(data)), (_2: rt.u8): rt.u32 => { + return _2 as rt.u32; + }), rt.iterSlice([5, 6, 7])), undefined, 4, true); + const len: rt.usize = v.length; + const p: rt.RawSlice = new rt.RawSlice(rt.sliceAsPtr(v), len); + rt.idx(p, 0); + const _19: rt.u32 = rt.u32Add(p.deref()[0], 1); + p.deref()[0] = _19; + const _20: rt.u32 = n as rt.u32; + const _21: rt.usize = rt.usizeSub(len, 1); + rt.idx(p, _21); + p.deref()[_21] = _20; + rt.byteVecPush(out, p.deref().length & 0xff); + rt.byteVecPush(out, p.length & 0xff); + rt.idx(p, 1); + rt.byteVecPush(out, p.deref()[1] & 0xff); + const tail: rt.u32[] = rt.sliceRangeMut(p.deref(), 1); + rt.idx(tail, 0); + tail[0] = 9; + rt.byteVecPush(out, rt.iterSum(rt.iterSlice(p.deref()), rt.u32Add, 0) & 0xff); + emit(v, out); + const bytes: rt.ByteVec = rt.byteSliceToByteVec(data); + rt.iterExtendBytes(bytes, rt.iterSlice(Uint8Array.of(3, 4)), undefined, true); + const b: rt.RawSlice = new rt.RawSlice(rt.byteVecAsPtr(bytes), bytes.length); + rt.idx(b, 0); + const slice2 = b.derefBytes(false); + const slice3 = b.derefBytes(false); + slice3.data[slice3.start] = slice2.data[slice2.start] ^ 255; + const _67: rt.usize = rt.usizeSub(b.derefBytes(false).length, 1); + rt.idx(b, _67); + const slice4 = b.derefBytes(false); + rt.byteVecPush(out, slice4.data[slice4.start + _67]); + rt.byteVecPush(out, rt.byteSliceRange(b.derefBytes(false), 1).length & 0xff); + rt.iterExtendBytes(out, rt.iterSlice(rt.byteVecView(bytes)), undefined, true); + const short: rt.u32[] = [n as rt.u32, 1]; + const long: rt.RawSlice = new rt.RawSlice(rt.sliceAsPtr(short), 10); + rt.byteVecPush(out, long.length & 0xff); + rt.byteVecPush(out, rt.ptrAdd(long.ptr, 1).v & 0xff); + const pointer = rt.sliceAsPtr(v); + const nn: rt.RawSlice = new rt.RawSlice(rt.optionUnwrap(pointer === rt.NULL ? undefined : pointer), 2); + rt.byteVecPush(out, nn.length & 0xff); + const _103: rt.RawSlice = nn; + rt.idx(_103, 1); + _103.deref()[1] = 11; + const _110: rt.u32[] = nn.deref(); + rt.idx(_110, 1); + rt.byteVecPush(out, _110[1] & 0xff); + const pointer2 = nn; + rt.byteVecPush(out, option_mapOr$0grxkdu(pointer2.ptr === rt.NULL ? undefined : pointer2.deref(), 0, (s: rt.u32[]): rt.u32 => { + rt.idx(s, 0); + return s[0]; + }) & 0xff); + const _121: rt.u32[] = nn.deref(); + rt.idx(_121, 0); + _121[0] = 12; + const pointer3 = rt.byteVecAsPtr(bytes); + const two: rt.RawSlice = new rt.RawSlice(rt.optionUnwrap(pointer3 === rt.NULL ? undefined : pointer3), 2); + const _132: rt.ByteSlice = two.derefBytes(false); + rt.idx(_132, 1); + _132.data[_132.start + 1] = _132.data[_132.start + 1] ^ 15; + const _140: rt.ByteSlice = two.derefBytes(true); + rt.idx(_140, 1); + rt.byteVecPush(out, _140.data[_140.start + 1]); + const null_: rt.RawSlice = new rt.RawSlice(rt.NULL, 0); + const pointer4 = null_; + rt.byteVecPush(out, ((pointer4.ptr === rt.NULL ? undefined : pointer4)) === undefined ? 1 : 0); + const pointer5 = null_; + rt.byteVecPush(out, ((pointer5.ptr === rt.NULL ? undefined : pointer5.deref())) === undefined ? 1 : 0); + const raw: rt.RawSlice = rt.rawSliceOf([n as rt.u32, 2, 3]); + rt.idx(raw, 2); + raw.deref()[2] = 8; + emit(raw.deref(), out); + const whole2: rt.RawSlice = new rt.RawSlice(rt.sliceAsPtr(short), 2); + const ofRef: rt.RawSlice = rt.rawSliceOf(rt.sliceRange(v, 1, 3)); + rt.byteVecPush(out, rt.usizeAdd(whole2.length, ofRef.length) & 0xff); + rt.idx(ofRef, 1); + rt.byteVecPush(out, ofRef.deref()[1] & 0xff); + emit(v, out); + rt.iterExtendBytes(out, rt.iterSlice(rt.byteVecView(bytes)), undefined, true); +} + +/** + * Empty slices made from pointers into no array, a dangling one and one off null, each + * part of which keeps the pointer as Rust's does: after moving back to address 0 it is null. + */ +export function emptyParts(data: rt.ByteSlice, out: rt.ByteVec): void { + const _130: (a0: rt.Place) => rt.u8 = (p2: rt.Place): rt.u8 => { + return rt.ptrWrappingAdd(p2, -1, 1, 1) === rt.NULL ? 1 : 0; + }; + const k: rt.usize = rt.usizeAdd(rt.u8Rem(option_unwrapOr$0r2bmpr(rt.byteSliceFirst(data), 0), 4) as rt.usize, 1); + const wide: rt.Place = rt.DANGLING; + const off: rt.Place = rt.ptrWrappingAdd(rt.NULL, k, 2, 2); + const iter: rt.Iter<[rt.u16[], rt.usize]> = rt.iterSlice([[rt.ptrSlice(wide, 0), 1] as [rt.u16[], rt.usize], [rt.ptrSlice(off, 0), k] as [rt.u16[], rt.usize]]); + while (true) { + const _23: [rt.u16[], rt.usize] | rt.Done = iter.next(); + if (_23 === rt.DONE) { + break; + } + const back: rt.usize = _23[1]; + const null_: (a0: rt.Place) => rt.u8 = (p3: rt.Place): rt.u8 => { + return rt.ptrWrappingAdd(p3, -back, 2, 2) === rt.NULL ? 1 : 0; + }; + const _32: [rt.u16[], rt.u16[]] = rt.sliceSplitAt(_23[0], 0); + const part = rt.sliceAsPtr(_32[0]); + const _35: rt.u8 = null_(part); + const part2 = rt.sliceAsPtr(_32[1]); + rt.iterExtendBytes(out, rt.iterSlice(Uint8Array.of(_35, null_(part2))), undefined, true); + const _45: [rt.u16[], rt.u16[]] = rt.optionUnwrap(rt.sliceSplitAtChecked(_23[0], 0)); + const part3 = rt.sliceAsPtr(_45[0]); + const _49: rt.u8 = null_(part3); + const part4 = rt.sliceAsPtr(_45[1]); + rt.iterExtendBytes(out, rt.iterSlice(Uint8Array.of(_49, null_(part4))), undefined, true); + const part5 = rt.sliceAsPtr(rt.partsRemainder(rt.sliceChunksExact(_23[0], 2))); + rt.byteVecPush(out, null_(part5)); + const part6 = rt.sliceAsPtr(rt.partsRemainder(rt.sliceRchunksExact(_23[0], 3))); + rt.byteVecPush(out, null_(part6)); + rt.iterExtendBytes(out, rt.iterMap(rt.sliceSplit(_23[0], (_2: rt.u16): boolean => { + return _2 === 0; + }), (part7: rt.u16[]): rt.u8 => { + return null_(rt.sliceAsPtr(part7)); + }), "next"); + rt.iterExtendBytes(out, rt.iterMap(rt.sliceRsplit(_23[0], (_22: rt.u16): boolean => { + return _22 === 0; + }), (part9: rt.u16[]): rt.u8 => { + return null_(rt.sliceAsPtr(part9)); + }), "next"); + rt.iterExtendBytes(out, rt.iterMap(rt.sliceSplitn(_23[0], 2, (_24: rt.u16): boolean => { + return _24 === 0; + }), (part11: rt.u16[]): rt.u8 => { + return null_(rt.sliceAsPtr(part11)); + }), "next"); + const part13 = rt.sliceAsPtr(rt.optionUnwrap(rt.sliceGetRange(_23[0], 0, 0))); + rt.byteVecPush(out, null_(part13)); + const part14 = rt.sliceAsPtr(rt.optionUnwrap(rt.sliceLastChunk(_23[0], 0))); + rt.byteVecPush(out, null_(part14)); + const part15 = rt.sliceAsPtr(rt.optionUnwrap(rt.sliceGetRange(_23[0], 0))); + rt.byteVecPush(out, null_(part15)); + const part16 = rt.sliceAsPtr(rt.sliceRange(_23[0], 0, 0)); + rt.byteVecPush(out, null_(part16)); + rt.byteVecPush(out, rt.sliceSplitFirst(_23[0]) === undefined ? 1 : 0); + rt.byteVecPush(out, rt.iterCount(rt.sliceChunks(_23[0], 2)) & 0xff); + } + const byte: rt.Place = rt.DANGLING; + const s: rt.ByteSlice = rt.ptrByteSlice(byte, 0, false); + const part17 = rt.byteSliceAsPtr(s); + rt.byteVecPush(out, _130(part17)); + const _139: [rt.ByteSlice, rt.ByteSlice] = rt.byteSliceSplitAt(s, 0); + const part18 = rt.byteSliceAsPtr(_139[0]); + const _142: rt.u8 = _130(part18); + const part19 = rt.byteSliceAsPtr(_139[1]); + rt.iterExtendBytes(out, rt.iterSlice(Uint8Array.of(_142, _130(part19))), undefined, true); + const part20 = rt.byteSliceAsPtr(rt.byteSliceRange(s, 0)); + rt.byteVecPush(out, _130(part20)); + const part21 = rt.byteSliceAsPtr(rt.optionUnwrap(rt.iterToOption(rt.byteSliceSplit(s, (_25: rt.u8): boolean => { + return _25 === 0; + }).next()))); + rt.byteVecPush(out, _130(part21)); + const raw: rt.RawSlice = new rt.RawSlice(off, 0); + rt.byteVecPush(out, raw.length & 0xff); + rt.byteVecPush(out, rt.ptrWrappingAdd(raw.ptr, -k, 2, 2) === rt.NULL ? 1 : 0); + const part22 = rt.ptrWrappingAdd(new rt.RawSlice(byte, 0).ptr, 0, 1, 1); + rt.byteVecPush(out, _130(part22)); + let lone: rt.u8 = data.length & 0xff; + const p: rt.Place = rt.place(() => lone, (value) => lone = value); + rt.byteVecPush(out, rt.ptrOffsetFrom(rt.byteSliceAsPtr(rt.ptrByteSlice(p, 0, true)), p) & 0xff); + rt.byteVecPush(out, rt.ptrOffsetFrom(rt.byteSliceAsPtr(rt.ptrByteSlice(rt.ptrAdd(p, 1), 0, true)), p) & 0xff); + let held: rt.u8 = lone; + const q: rt.Place = rt.place(() => held, (value2) => held = value2); + const r: rt.ByteSlice = rt.ptrByteSlice(q, 0, false); + rt.byteVecPush(out, rt.u8Add(r.length & 0xff, rt.ptrOffsetFrom(rt.byteSliceAsPtr(r), q) & 0xff)); +} + +export function emit(values: rt.u32[], out: rt.ByteVec): void { + rt.iterExtendBytes(out, rt.iterFlatMap(rt.iterSlice(values), (x: rt.u32): Uint8Array => { + return rt.u32ToBeBytes(x).reverse(); + }, (value: Uint8Array) => rt.iterSlice(value), false, 4), undefined, true); +} + +//# sourceMappingURL=elements.ts.map diff --git a/ts/fixtures/lang/expected/elements/mine/ptr.ts b/ts/fixtures/lang/expected/elements/mine/ptr.ts new file mode 100644 index 000000000..63614033e --- /dev/null +++ b/ts/fixtures/lang/expected/elements/mine/ptr.ts @@ -0,0 +1,9 @@ +// Generated by rust2-ts. Do not edit. + +import * as rt from "../../rt.ts"; + +export function offsetFrom(a: rt.Place, b: rt.Place): rt.isize { + return rt.isizeAdd(a.v | 0, b.v | 0); +} + +//# sourceMappingURL=ptr.ts.map diff --git a/ts/fixtures/lang/expected/elements/mine/raw.ts b/ts/fixtures/lang/expected/elements/mine/raw.ts new file mode 100644 index 000000000..5534e81f9 --- /dev/null +++ b/ts/fixtures/lang/expected/elements/mine/raw.ts @@ -0,0 +1,9 @@ +// Generated by rust2-ts. Do not edit. + +import * as rt from "../../rt.ts"; + +export function fromRawPartsMut(p: rt.Place, n: rt.usize): rt.u8 { + return rt.u8WrappingAdd(p.v, n & 0xff); +} + +//# sourceMappingURL=raw.ts.map diff --git a/ts/fixtures/lang/expected/floats.ts b/ts/fixtures/lang/expected/floats.ts index 3417c6ae3..e1411a2db 100644 --- a/ts/fixtures/lang/expected/floats.ts +++ b/ts/fixtures/lang/expected/floats.ts @@ -270,11 +270,12 @@ export function floats(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void { * `mul_add`, rounded once, of three operands, or of 32 sets made from a seed: any bits, * a product's error alone, half the product's last place, where rounding the product * first would tie, a product of half `c`'s last place, subnormal sums, products past `f64::MAX` that the sum brings back, and - * each side of where the runtime's fast path ends. + * each side of where the runtime's fast path ends. Each result follows its operands, which + * `fma.ts` rounds exactly where native's differs. */ export function mulAdds(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void { - let _53!: [rt.f64, rt.f64, rt.f64]; - let _103!: rt.f64; + let _52!: [rt.f64, rt.f64, rt.f64]; + let _102!: rt.f64; let ea4!: rt.i64; b0: { if (!(rt.u8Rem(kind, 2) === 0)) { @@ -294,60 +295,71 @@ export function mulAdds(kind: rt.u8, data: rt.ByteSlice, out: rt.ByteVec): void const a2: rt.f64 = near(step(rt.place(() => state, (value2) => state = value2)), ea); const b2: rt.f64 = near(step(rt.place(() => state, (value3) => state = value3)), rt.i64Sub(e, ea)); const c2: rt.u64 = step(rt.place(() => state, (value4) => state = value4)); - const _54: rt.u64 = rt.u64Rem(r, 6); - if (_54 === 0) { - _53 = [rt.f64FromBits(r), rt.f64FromBits(step(rt.place(() => state, (value5) => state = value5))), rt.f64FromBits(c2)] as [rt.f64, rt.f64, rt.f64]; - } else if (_54 === 1) { - _53 = [a2, b2, -(a2 * b2)] as [rt.f64, rt.f64, rt.f64]; - } else if (_54 === 2) { + const _53: rt.u64 = rt.u64Rem(r, 6); + if (_53 === 0) { + _52 = [rt.f64FromBits(r), rt.f64FromBits(step(rt.place(() => state, (value5) => state = value5))), rt.f64FromBits(c2)] as [rt.f64, rt.f64, rt.f64]; + } else if (_53 === 1) { + _52 = [a2, b2, -(a2 * b2)] as [rt.f64, rt.f64, rt.f64]; + } else if (_53 === 2) { if (!(rt.u64Shr(r, 63) === 0)) { const c4: rt.f64 = near(c2, e); - _53 = [a2, (rt.f64NextUp(Math.abs(c4)) - Math.abs(c4)) * 0.5 / a2, c4] as [rt.f64, rt.f64, rt.f64]; + _52 = [a2, (rt.f64NextUp(Math.abs(c4)) - Math.abs(c4)) * 0.5 / a2, c4] as [rt.f64, rt.f64, rt.f64]; } else { const p: rt.f64 = Math.abs(a2 * b2); - const _71: rt.usize = rt.i64Low(pick(24, 5)); - if (!(_71 < 5)) { - rt.panic("index out of bounds: the len is " + 5 + " but the index is " + _71); + const _70: rt.usize = rt.i64Low(pick(24, 5)); + if (!(_70 < 5)) { + rt.panic("index out of bounds: the len is " + 5 + " but the index is " + _70); } - const half: rt.f64 = [0.5, -0.5, 0.25, -0.75, 1.5][_71]; - _53 = [a2, b2, (rt.f64NextUp(p) - p) * half] as [rt.f64, rt.f64, rt.f64]; + const half: rt.f64 = [0.5, -0.5, 0.25, -0.75, 1.5][_70]; + _52 = [a2, b2, (rt.f64NextUp(p) - p) * half] as [rt.f64, rt.f64, rt.f64]; } - } else if (_54 === 3) { + } else if (_53 === 3) { const ea2: rt.i64 = rt.i64Sub(pick(24, 1000), 500); const e2: rt.i64 = rt.i64Sub(-1000, pick(40, 100)); - const _98: [rt.f64, rt.f64] = [near(r, ea2), near(c2, rt.i64Sub(e2, ea2))]; - const a4: rt.f64 = _98[0]; - const b4: rt.f64 = _98[1]; + const _97: [rt.f64, rt.f64] = [near(r, ea2), near(c2, rt.i64Sub(e2, ea2))]; + const a4: rt.f64 = _97[0]; + const b4: rt.f64 = _97[1]; if (!(rt.u64Shr(r, 63) === 0)) { - _103 = near(rt.u64Shr(c2, 1), rt.i64Sub(rt.i64Add(e2, pick(48, 60)), 30)); + _102 = near(rt.u64Shr(c2, 1), rt.i64Sub(rt.i64Add(e2, pick(48, 60)), 30)); } else { - _103 = -(a4 * b4); + _102 = -(a4 * b4); } - _53 = [a4, b4, _103] as [rt.f64, rt.f64, rt.f64]; - } else if (_54 === 4) { + _52 = [a4, b4, _102] as [rt.f64, rt.f64, rt.f64]; + } else if (_53 === 4) { const ea3: rt.i64 = rt.i64Add(990, pick(24, 60)); const a5: rt.f64 = near(r, ea3); const b5: rt.f64 = near(c2, rt.i64Sub(rt.i64Add(rt.i64Sub(1023, ea3), pick(40, 4)), 1)); - _53 = [a5, b5, rt.f64Copysign(near(rt.u64Shr(c2, 1), rt.i64Add(1020, pick(48, 4))), -(a5 * b5))] as [rt.f64, rt.f64, rt.f64]; + _52 = [a5, b5, rt.f64Copysign(near(rt.u64Shr(c2, 1), rt.i64Add(1020, pick(48, 4))), -(a5 * b5))] as [rt.f64, rt.f64, rt.f64]; } else { if (!(rt.u64Shr(r, 63) === 0)) { ea4 = rt.i64Sub(pick(24, 1000), 500); } else { ea4 = rt.i64Add(990, pick(24, 10)); } - const _162: [rt.f64, rt.f64] = [near(r, ea4), near(c2, rt.i64Sub(rt.i64Sub(rt.i64Add(-968, pick(40, 8)), 4), ea4))]; - const a6: rt.f64 = _162[0]; - const b6: rt.f64 = _162[1]; + const _161: [rt.f64, rt.f64] = [near(r, ea4), near(c2, rt.i64Sub(rt.i64Sub(rt.i64Add(-968, pick(40, 8)), 4), ea4))]; + const a6: rt.f64 = _161[0]; + const b6: rt.f64 = _161[1]; const p2: rt.f64 = Math.abs(a6 * b6); - _53 = [a6, b6, (rt.f64NextUp(p2) - p2) * 0.5] as [rt.f64, rt.f64, rt.f64]; + _52 = [a6, b6, (rt.f64NextUp(p2) - p2) * 0.5] as [rt.f64, rt.f64, rt.f64]; } - putF64(out, rt.f64MulAdd(_53[0], _53[1], _53[2])); + putMulAdd(out, _52[0], _52[1], _52[2]); } } const _9: [rt.f64, rt.ByteSlice] = take(data); const a: rt.f64 = _9[0]; const _12: [rt.f64, rt.ByteSlice] = take(_9[1]); - putF64(out, rt.f64MulAdd(a, _12[0], take(_12[1])[0])); + putMulAdd(out, a, _12[0], take(_12[1])[0]); + } +} + +export function putMulAdd(out: rt.ByteVec, a: rt.f64, b: rt.f64, c: rt.f64): void { + const iter: rt.Iter = rt.iterSlice([a, b, c, rt.f64MulAdd(a, b, c)]); + while (true) { + const _9: rt.f64 | rt.Done = iter.next(); + if (_9 === rt.DONE) { + break; + } + putF64(out, _9); } } diff --git a/ts/fixtures/lang/expected/harness.ts b/ts/fixtures/lang/expected/harness.ts index 40f9dc8d7..7fd1ea045 100644 --- a/ts/fixtures/lang/expected/harness.ts +++ b/ts/fixtures/lang/expected/harness.ts @@ -24,6 +24,7 @@ import { defaults as defaults_defaults } from "./defaults.ts"; import { durations } from "./durations.ts"; import { dyns, shared as dyns_shared, anys, fns } from "./dyns.ts"; import { edits as edits_edits, bytes as edits_bytes } from "./edits.ts"; +import { vecs as elements_vecs, arrays as elements_arrays, segments as elements_segments, inline, edges as elements_edges, rawSlices, emptyParts } from "./elements.ts"; import { eqs } from "./errors.ts"; import { fields as fields_fields } from "./fields.ts"; import { floats, mulAdds as floats_mulAdds, formats as floats_formats, parses as floats_parses } from "./floats.ts"; @@ -45,7 +46,7 @@ import { u8s as methods_u8s, u16s as methods_u16s, u32s as methods_u32s, u64s as import { nested as nested_nested } from "./nested.ts"; import { objects as objects_objects } from "./objects.ts"; import { orders, bytes as orders_bytes, tuples as orders_tuples, held as orders_held, btrees as orders_btrees } from "./orders.ts"; -import { lists as pointers_lists, shared as pointers_shared, places as pointers_places, unchecked, nested as pointers_nested, copies as pointers_copies, sentinels, cells as pointers_cells, rebound as pointers_rebound, lapsed, writers, made as pointers_made, segments, uninitRuns, iteratedRuns, besideWritten, waiters, taken as pointers_taken, bytes as pointers_bytes, loaded, reattached, partWritten, aimed } from "./pointers.ts"; +import { lists as pointers_lists, shared as pointers_shared, places as pointers_places, unchecked, nested as pointers_nested, copies as pointers_copies, sentinels, cells as pointers_cells, rebound as pointers_rebound, lapsed, writers, made as pointers_made, segments as pointers_segments, uninitRuns, iteratedRuns, besideWritten, waiters, taken as pointers_taken, bytes as pointers_bytes, loaded, reattached, partWritten, aimed } from "./pointers.ts"; import { refs, toRefs, adapted as refs_adapted, compared } from "./refs.ts"; import { results as results_results } from "./results.ts"; import { returns } from "./returns.ts"; @@ -226,7 +227,7 @@ export function cases(): [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["cows::slices", cows_slices as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["cows::bytes", cows_bytes as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["wraps::u8s", wraps_u8s as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["wraps::u16s", wraps_u16s as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["wraps::u32s", wraps_u32s as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["wraps::u64s", wraps_u64s as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["wraps::u128s", wraps_u128s as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["wraps::usizes", wraps_usizes as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["wraps::i8s", wraps_i8s as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["wraps::i16s", wraps_i16s as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["wraps::i32s", wraps_i32s as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["wraps::i64s", wraps_i64s as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["wraps::i128s", wraps_i128s as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["wraps::isizes", wraps_isizes as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["fmt::enums", (_2174: rt.u8, data74: rt.ByteSlice, out75: rt.ByteVec): undefined => { enums(data74, out75); }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["pointers::segments", (_2175: rt.u8, data75: rt.ByteSlice, out76: rt.ByteVec): undefined => { - segments(data75, out76); + pointers_segments(data75, out76); }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["pointers::uninit_runs", (_2176: rt.u8, data76: rt.ByteSlice, out77: rt.ByteVec): undefined => { uninitRuns(data76, out77); }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["pointers::beside_written", (_2177: rt.u8, data77: rt.ByteSlice, out78: rt.ByteVec): undefined => { @@ -281,12 +282,26 @@ export function cases(): [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) downgraded(data97, out98); }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["borrows::leaks", (_2198: rt.u8, data98: rt.ByteSlice, out99: rt.ByteVec): undefined => { leaks(data98, out99); - }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["hashes::own", (_2199: rt.u8, data99: rt.ByteSlice, out100: rt.ByteVec): undefined => { - hashes_own(data99, out100); + }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["elements::vecs", (_2199: rt.u8, data99: rt.ByteSlice, out100: rt.ByteVec): undefined => { + elements_vecs(data99, out100); + }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["elements::arrays", (_2200: rt.u8, data100: rt.ByteSlice, out101: rt.ByteVec): undefined => { + elements_arrays(data100, out101); + }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["elements::segments", (_2201: rt.u8, data101: rt.ByteSlice, out102: rt.ByteVec): undefined => { + elements_segments(data101, out102); + }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["elements::inline", (_2202: rt.u8, data102: rt.ByteSlice, out103: rt.ByteVec): undefined => { + inline(data102, out103); + }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["elements::edges", (_2203: rt.u8, data103: rt.ByteSlice, out104: rt.ByteVec): undefined => { + elements_edges(data103, out104); + }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["elements::raw_slices", (_2204: rt.u8, data104: rt.ByteSlice, out105: rt.ByteVec): undefined => { + rawSlices(data104, out105); + }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["elements::empty_parts", (_2205: rt.u8, data105: rt.ByteSlice, out106: rt.ByteVec): undefined => { + emptyParts(data105, out106); + }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["hashes::own", (_2206: rt.u8, data106: rt.ByteSlice, out107: rt.ByteVec): undefined => { + hashes_own(data106, out107); }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["refs::compared", (a0159: rt.u8, a1159: rt.ByteSlice, a2159: rt.ByteVec): undefined => { compared(a0159, a1159.data, a1159.start, a1159.length, a2159); - }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["fmt::objects", (_2200: rt.u8, data100: rt.ByteSlice, out101: rt.ByteVec): undefined => { - fmt_objects(data100, out101); + }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["fmt::objects", (_2207: rt.u8, data107: rt.ByteSlice, out108: rt.ByteVec): undefined => { + fmt_objects(data107, out108); }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["sorts::sorts", (a0160: rt.u8, a1160: rt.ByteSlice, a2160: rt.ByteVec): undefined => { sorts(a0160, a1160.data, a1160.start, a1160.length, a2160); }] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["sorts::disorders", disorders as (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined] as [string, (a0: rt.u8, a1: rt.ByteSlice, a2: rt.ByteVec) => undefined], ["sorts::btrees", (a0162: rt.u8, a1162: rt.ByteSlice, a2162: rt.ByteVec): undefined => { diff --git a/ts/fixtures/lang/expected/lib.ts b/ts/fixtures/lang/expected/lib.ts index 27db459a9..f7c6197e7 100644 --- a/ts/fixtures/lang/expected/lib.ts +++ b/ts/fixtures/lang/expected/lib.ts @@ -16,6 +16,7 @@ import type * as counts_Noisy from "./counts.Noisy.ts"; import type * as RcReleasing from "./counts.RcReleasing.ts"; import type * as RcShared from "./counts.RcShared.ts"; import type * as durations_Window from "./durations.Window.ts"; +import * as Inline from "./elements.Inline.ts"; import type * as Couple from "./fields.Couple.ts"; import type * as Either from "./generics.Either.ts"; import * as Fixed from "./generics.Fixed.ts"; @@ -217,6 +218,10 @@ export function copy$array_Option_Option_array5_u8(v: Array): Array<[rt.u8, rt.u8] | undefined> { + return v.map(copy$Option_tuple2_u8_u8); +} + export function copy$array_Option_u8(v: Array): Array { return v.slice(); } @@ -411,6 +416,27 @@ export function copy$locals_Twin(v: locals_Twin.Twin): locals_Twin.Twin { return { _0: v._0, _1: v._1 }; } +export function copy$mem_MaybeUninit_Option_tuple2_u8_u8(v: [rt.u8, rt.u8] | undefined): [rt.u8, rt.u8] | undefined { + if (v === undefined) { + return v; + } + return v === undefined ? v : copy$tuple2_u8_u8(v); +} + +export function copy$mem_MaybeUninit_Result_tuple2_u32_u32_u8(v: rt.Result<[rt.u32, rt.u32], rt.u8>): rt.Result<[rt.u32, rt.u32], rt.u8> { + if (v === undefined) { + return v; + } + return v.ok ? rt.Ok(copy$tuple2_u32_u32(v.value)) : rt.Err(v.error); +} + +export function copy$mem_MaybeUninit_array_Option_tuple2_u8_u8(v: Array<[rt.u8, rt.u8] | undefined>): Array<[rt.u8, rt.u8] | undefined> { + if (v === undefined) { + return v; + } + return v.map(copy$mem_MaybeUninit_Option_tuple2_u8_u8); +} + export function copy$mem_MaybeUninit_array_array0_u8(v: Uint8Array[]): Uint8Array[] { if (v === undefined) { return v; @@ -1163,6 +1189,14 @@ export function drop$cell_RefCell_Vec_sync_Arc_counts_ArcReleasing(value: rt.Ref rt.dropAll(value.value); } +export function drop$elements_Inline_Option_tuple2_u8_u8(_1: Inline.Inline<[rt.u8, rt.u8] | undefined>): void { + Inline.drop$ops_Drop_Option_tuple2_u8_u8(_1); +} + +export function drop$elements_Inline_sync_Arc_u8(_1: Inline.Inline>): void { + Inline.drop$ops_Drop_sync_Arc_u8(_1); +} + export function drop$generics_Run_generics_Ticked(_1: generics_Run.Run): void { if (_1.tag === "Listed") { rt.dropAll(_1._0 as Ticked.Ticked[]); diff --git a/ts/fixtures/lang/expected/tuples.ts b/ts/fixtures/lang/expected/tuples.ts index 9e9287985..e67121585 100644 --- a/ts/fixtures/lang/expected/tuples.ts +++ b/ts/fixtures/lang/expected/tuples.ts @@ -2,7 +2,7 @@ import * as rt from "./rt.ts"; import { copy$tuple2_u8_u8, copy$tuples_Spot, drop$Result_tuple2_tuples_Logged_u8_u8, drop$tuple2_Vec_tuples_Logged_tuples_Tally, drop$tuple2_tuples_Logged_u8, drop$tuple2_u8_tuples_Logged } from "./lib.ts"; -import { unwrapOr$0r2bmpr as option_unwrapOr$0r2bmpr, map$1pb5zw8 as option_map$1pb5zw8, map$1fmzefe as option_map$1fmzefe } from "./core/option.ts"; +import { unwrapOr$0r2bmpr as option_unwrapOr$0r2bmpr, map$1fmzefe as option_map$1fmzefe, map$1pb5zw8 as option_map$1pb5zw8 } from "./core/option.ts"; import { set$0xuu1fw as local_set$0xuu1fw } from "./std/thread/local.ts"; import * as Last from "./tuples.Last.ts"; import * as tuples_Logged from "./tuples.Logged.ts"; diff --git a/ts/fixtures/lang/fma.ts b/ts/fixtures/lang/fma.ts new file mode 100644 index 000000000..ab510650e --- /dev/null +++ b/ts/fixtures/lang/fma.ts @@ -0,0 +1,72 @@ +// The one difference from native Rust that the lang fixture accepts in `floats::mul_adds`: +// wasm32's `fma`, compiler-builtins' generic libm, rounds some near-ties a last place off +// (rust-lang/compiler-builtins#1353, fixed by #1358). `/quest/m3/ts-fma-allowance.md` +// removes this once the toolchain ships the fix. + +/** A finite double as `m * 2^e`, exactly. */ +function parts(x: number): { m: bigint; e: number } { + const view = new DataView(new ArrayBuffer(8)); + view.setFloat64(0, x); + const bits = view.getBigUint64(0); + const biased = Number((bits >> 52n) & 0x7ffn); + const frac = bits & ((1n << 52n) - 1n); + const m = biased === 0 ? frac : frac | (1n << 52n); + return { m: bits >> 63n === 1n ? -m : m, e: Math.max(biased, 1) - 1075 }; +} + +/** `a * b + c` rounded once to nearest, ties to even, from exact integers; `undefined` if an operand is not finite. */ +export function exactMulAdd(a: number, b: number, c: number): number | undefined { + if (![a, b, c].every(Number.isFinite)) return undefined; + const [pa, pb, pc] = [parts(a), parts(b), parts(c)]; + const [mp, ep] = [pa.m * pb.m, pa.e + pb.e]; + const e = Math.min(ep, pc.e); + const sum = (mp << BigInt(ep - e)) + (pc.m << BigInt(pc.e - e)); + // An exact zero is -0 only if the product and `c` both are, so of zeros JS's sum is exact. + if (sum === 0n) return a === 0 || b === 0 ? a * b + c : 0; + const sign = sum < 0n ? -1 : 1; + const mag = sum < 0n ? -sum : sum; + // The last place of the result: 53 bits below its leading one, or a subnormal's. + const lead = mag.toString(2).length - 1 + e; + const q = Math.max(lead - 52, -1074); + let kept = mag; + if (q > e) { + const shift = BigInt(q - e); + kept = mag >> shift; + const rest = mag - (kept << shift); + const half = 1n << (shift - 1n); + if (rest > half || (rest === half && (kept & 1n) === 1n)) kept += 1n; + } else { + kept = mag << BigInt(e - q); + } + // `kept` is at most 2^53 and `2^q` a double, so this product rounds only past the range, to infinity. + return sign * Number(kept) * 2 ** q; +} + +/** A finite double's bits as a line through zero, so the distance between two is how many doubles apart they are. */ +function line(x: number): bigint { + const view = new DataView(new ArrayBuffer(8)); + view.setFloat64(0, x); + const bits = view.getBigInt64(0); + return bits < 0n ? -(bits & ((1n << 63n) - 1n)) : bits; +} + +/** + * Whether `mul_adds`' outputs, each result after its operands as four big-endian doubles, + * differ only where the translation's result is the exactly rounded one and native's is + * a last place from it. + */ +export function mulAddsWithin(native: Uint8Array, translated: Uint8Array): boolean { + if (native.length !== translated.length || native.length % 32 !== 0) return false; + const [n, t] = [native, translated].map((v) => new DataView(v.buffer, v.byteOffset, v.length)); + for (let i = 0; i < native.length; i += 32) { + const differs = (from: number, to: number) => native.subarray(from, to).some((x, j) => x !== translated[from + j]); + if (differs(i, i + 24)) return false; + if (!differs(i + 24, i + 32)) continue; + const [a, b, c, want, got] = [t.getFloat64(i), t.getFloat64(i + 8), t.getFloat64(i + 16), n.getFloat64(i + 24), t.getFloat64(i + 24)]; + const exact = exactMulAdd(a, b, c); + if (exact === undefined || !Object.is(got, exact) || !Number.isFinite(want) || !Number.isFinite(got)) return false; + const d = line(want) - line(got); + if (d !== 1n && d !== -1n) return false; + } + return true; +} diff --git a/ts/fixtures/lang/src/elements.rs b/ts/fixtures/lang/src/elements.rs new file mode 100644 index 000000000..67e9d7f27 --- /dev/null +++ b/ts/fixtures/lang/src/elements.rs @@ -0,0 +1,536 @@ +//! Pointers into arrays: a `Vec`'s, a slice's, an array's, a boxed slice's, and a +//! `MaybeUninit` array's elements, moved by `add`, `sub`, `offset`, and their wrapping +//! forms, measured by `offset_from`, copied between by `ptr::copy` and +//! `copy_nonoverlapping`, overlapping either way, and made slices again, as `smallvec`'s +//! inline storage and moq-net's frame segments use them, and the edges: addresses that +//! wrap, a value in no array, and a slice of none. + +use std::mem::MaybeUninit; +use std::ptr::{self, NonNull}; +use std::slice; +use std::sync::Arc; + +/// A `Vec`'s elements moved within it as `insert` and `remove` move them, and copied to +/// another, read back through slices made of the pointers. +pub fn vecs(data: &[u8], out: &mut Vec) { + let mut v: Vec = data.iter().map(|&b| u32::from(b) * 3).collect(); + v.extend([7, 8]); + let len = v.len(); + let at = usize::from(data.first().copied().unwrap_or(0)) % (len - 1); + let p = v.as_mut_ptr(); + unsafe { + // The tail moved right over the last element, and the gap filled. + ptr::copy(p.add(at), p.add(at + 1), len - at - 1); + *p.add(at) = 1000; + emit(slice::from_raw_parts(p, len), out); + // And moved back left. + ptr::copy(p.add(at + 1), p.add(at), len - at - 1); + *p.add(len - 1).sub(0) = 2000; + emit(slice::from_raw_parts(p, len), out); + // Pointers moved forward and back, and measured. + let end = p.add(len); + let last = end.offset(-1); + *last.wrapping_sub(1) += 1; + *p.wrapping_add(1).wrapping_offset(-1) += 5; + out.push(end.offset_from(p) as u8); + out.push(p.offset_from(last) as u8); + out.push(last.offset_from(last) as u8); + // A part of it, written through a mutable slice of the pointers. + let part = slice::from_raw_parts_mut(p.add(at), len - at); + part[0] += 3; + part.reverse(); + let mut w = vec![0u32; len]; + ptr::copy_nonoverlapping(p, w.as_mut_ptr(), len); + w.as_mut_ptr().add(at).copy_from(p, 1); + p.copy_to_nonoverlapping(w.as_mut_ptr().add(len - 1), 1); + emit(&w, out); + } + emit(&v, out); + let shared: &[u32] = &v; + let q = shared.as_ptr(); + out.push(unsafe { *q.add(len / 2) } as u8); + // A slice of none from a dangling pointer, as an empty collection's is. + let none = unsafe { slice::from_raw_parts(NonNull::::dangling().as_ptr(), 0) }; + out.push(none.len() as u8); +} + +/// An array's elements, from a pointer to one, to the array, and to its first. +pub fn arrays(data: &[u8], out: &mut Vec) { + for &n in data { + let mut a = [u16::from(n), 1, 2, 3, 4]; + let i = usize::from(n) % 4; + // Each pointer is read and written through before the next borrow of `a`. + let at = &raw mut a[i]; + unsafe { *at = 99 }; + let whole = (&raw mut a).cast::(); + unsafe { + *whole.add(i + 1) += 10; + *whole.add(4) += u16::from(n); + } + let first = a.as_mut_ptr(); + unsafe { + out.push(at.offset_from(whole) as u8); + out.push(first.offset_from(at) as u8); + let p = NonNull::new_unchecked(first); + *p.add(2).as_ptr() += 1; + *p.add(3).sub(3).as_ptr() += 2; + out.push(p.add(4).offset_from(p) as u8); + let tail = NonNull::slice_from_raw_parts(p.add(i), 5 - i); + out.push(tail.as_ref().len() as u8); + tail.as_ptr().cast::().write(7); + } + // A pointer to `()` stays where it is. + let unit = NonNull::<()>::dangling(); + let moved = unsafe { unit.add(usize::from(n)) }; + out.push(u8::from(moved == unit)); + out.extend(a.iter().flat_map(|x| x.to_le_bytes())); + } +} + +/// A boxed slice of bytes kept behind a pointer to its first, as moq-net's frame +/// segment keeps its storage: written by copies at offsets, read by slices of it, and +/// freed as the slice it was. +pub fn segments(data: &[u8], out: &mut Vec) { + let segment = Storage::new(data.len() + 4); + unsafe { + segment.write(0, data); + segment.write(data.len(), &[1, 2, 3, 4]); + out.extend_from_slice(segment.read(data.len() + 4)); + // Overlapping moves of its bytes, either way. + let p = segment.data; + ptr::copy(p, p.add(2), data.len()); + out.extend_from_slice(segment.read(data.len() + 4)); + ptr::copy(p.add(3), p.add(1), data.len()); + *p.add(data.len() + 3) ^= 0xff; + out.extend_from_slice(segment.read(data.len() + 4)); + } + // A `Vec`'s bytes and an array's, through pointers to them. + let mut v = data.to_vec(); + v.push(9); + let mut a = [0u8; 4]; + unsafe { + let p = v.as_mut_ptr(); + *p.add(v.len() - 1) += 1; + ptr::copy_nonoverlapping(p, (&raw mut a).cast::(), v.len().min(4)); + let byte = (&raw mut a).cast::().add(1); + *byte.add(2) = 0x55; + out.extend_from_slice(slice::from_raw_parts(byte.sub(1), 4)); + let one = &raw mut a[2]; + *one ^= 0x0f; + out.push(*one); + } + out.extend(v); +} + +struct Storage { + data: *mut u8, + capacity: usize, +} + +impl Storage { + fn new(capacity: usize) -> Self { + let boxed: Box<[u8]> = vec![0u8; capacity].into_boxed_slice(); + let capacity = boxed.len(); + let data = Box::into_raw(boxed) as *mut u8; + Self { data, capacity } + } + + unsafe fn write(&self, offset: usize, src: &[u8]) { + assert!(offset + src.len() <= self.capacity); + unsafe { ptr::copy_nonoverlapping(src.as_ptr(), self.data.add(offset), src.len()) }; + } + + unsafe fn read(&self, len: usize) -> &[u8] { + unsafe { slice::from_raw_parts(self.data, len) } + } +} + +impl Drop for Storage { + fn drop(&mut self) { + unsafe { + let slice = ptr::slice_from_raw_parts_mut(self.data, self.capacity); + drop(Box::from_raw(slice)); + } + } +} + +/// Up to four values kept inline in a `MaybeUninit` array, as `smallvec` keeps them, +/// inserted and removed by moving the rest through pointers, and dropped in place. +struct Inline { + len: usize, + items: MaybeUninit<[T; 4]>, +} + +impl Inline { + fn new() -> Self { + Inline { + len: 0, + items: MaybeUninit::uninit(), + } + } + + fn ptr(&mut self) -> *mut T { + self.items.as_mut_ptr() as *mut T + } + + fn as_slice(&self) -> &[T] { + unsafe { slice::from_raw_parts(self.items.as_ptr() as *const T, self.len) } + } + + fn insert(&mut self, at: usize, value: T) { + assert!(self.len < 4 && at <= self.len); + let len = self.len; + unsafe { + let p = self.ptr().add(at); + ptr::copy(p, p.add(1), len - at); + ptr::write(p, value); + } + self.len += 1; + } + + fn remove(&mut self, at: usize) -> T { + assert!(at < self.len); + self.len -= 1; + let len = self.len; + unsafe { + let p = self.ptr().add(at); + let value = ptr::read(p); + ptr::copy(p.add(1), p, len - at); + value + } + } +} + +impl Drop for Inline { + fn drop(&mut self) { + let len = self.len; + unsafe { ptr::drop_in_place(ptr::slice_from_raw_parts_mut(self.ptr(), len)) }; + } +} + +/// Shared values and `Option`s of pairs moved through an inline array, counting the +/// shared values' owners, and a pair changed in place after its copy moved. +pub fn inline(data: &[u8], out: &mut Vec) { + let shared = Arc::new(0u8); + let mut held: Inline> = Inline::new(); + let mut pairs = Inline::new(); + for (i, &n) in data.iter().enumerate() { + if held.len == 4 { + let gone = held.remove(usize::from(n) % 4); + out.push(*gone); + pairs.remove(usize::from(n) % 4); + } + let at = usize::from(n) % (held.len + 1); + let item = match n % 3 { + 0 => shared.clone(), + _ => Arc::new(n), + }; + held.insert(at, item); + pairs.insert(at, (n % 2 == 0).then_some((n, i as u8))); + if let Some(Some(pair)) = pairs.as_slice().first() { + out.push(pair.0); + } + if let Some(Some(pair)) = unsafe { pairs.ptr().as_mut() } { + pair.1 = pair.1.wrapping_add(1); + } + out.push(Arc::strong_count(&shared) as u8); + out.extend(held.as_slice().iter().map(|x| **x)); + out.extend(pairs.as_slice().iter().flat_map(|p| p.map_or([0, 0], |(a, b)| [a, b]))); + } + drop(held); + out.push(Arc::strong_count(&shared) as u8); + // Copies that change apart. + let mut src = vec![Some((1u8, 2u8)), None, data.first().map(|&n| (n, 4))]; + let mut dst = vec![None; 3]; + unsafe { ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), 3) }; + if let Some(pair) = &mut dst[0] { + pair.0 = 9; + } + if let Some(pair) = &mut src[2] { + pair.1 = 8; + } + out.extend(src.iter().chain(&dst).flat_map(|p| p.map_or([0, 0], |(a, b)| [a, b]))); +} + +/// Pointers wrapped around wasm32's 32-bit addresses and back, to a value in no array, as +/// the array of one Rust treats it as, and into a mutable part of an array, measured from +/// the array, the pointer an empty slice keeps, within a shared part, which JS copies, +/// a `&mut` into that part, memory nothing wrote copied, null and dangling pointers moved by +/// address, kept by empty raw slices, and cast from pointers to arrays, and the crate's +/// functions named as std's. +pub fn edges(data: &[u8], out: &mut Vec) { + for &n in data { + let bytes = [n, 1, 2, 3]; + let p = bytes.as_ptr(); + let wide = [u16::from(n), 7, 8]; + let q = wide.as_ptr(); + unsafe { + out.push(*p.wrapping_add(usize::MAX).wrapping_add(1)); + out.push(*p.wrapping_add(1 << 31).wrapping_add(1 << 31)); + out.push(*p.wrapping_sub(usize::MAX).wrapping_offset(-1)); + out.extend(q.wrapping_add(1 << 31).wrapping_add(1).read().to_le_bytes()); + out.extend( + q.wrapping_add(3 << 30) + .wrapping_add(1 << 30) + .wrapping_add(2) + .read() + .to_le_bytes(), + ); + } + let mut lone = u32::from(n) + 42; + let at = &raw mut lone; + unsafe { + out.push(slice::from_raw_parts(at, 1)[0] as u8); + slice::from_raw_parts_mut(at, 1)[0] += 1; + out.push(at.add(1).sub(1).read() as u8); + out.push(at.add(1).offset_from(at) as u8); + out.push(slice::from_raw_parts(at, 0).as_ptr().offset_from(at) as u8); + } + out.push(lone as u8); + let byte = n ^ 0x5a; + out.push(unsafe { slice::from_raw_parts(&raw const byte, 1)[0] }); + let mut a = [u32::from(n), 1, 2, 3]; + let base = a.as_mut_ptr(); + let part = &mut a[1..3]; + let at = &raw mut part[1]; + unsafe { + out.push(at.offset_from(base) as u8); + *at += 5; + // A `&mut` into the part, measured from the array and moved out of the part. + let got: *mut u32 = part.get_mut(0).unwrap(); + out.push(got.offset_from(base) as u8); + out.push(*got.sub(1) as u8); + let empty = slice::from_raw_parts(at.add(1), 0); + out.push(empty.as_ptr().offset_from(base) as u8); + let none = slice::from_raw_parts(bytes.as_ptr().add(2), 0); + out.push(none.as_ptr().offset_from(p) as u8); + } + out.extend(a.iter().map(|&x| x as u8)); + // Within a shared subslice, a copy, and to its end. + let lanes = [u32::from(n), 4, 5, 6]; + let shared = &lanes[1..3]; + let first = shared.as_ptr(); + unsafe { + out.push(*first.add(1) as u8); + out.push(first.add(2).offset_from(first) as u8); + out.push(slice::from_raw_parts(first.add(1), 1)[0] as u8); + } + // The crate's own functions named as std's, which are only theirs. + let (x, mut y) = (u32::from(n), n); + out.push(mine::ptr::offset_from(&raw const x, &raw const x) as u8); + out.push(mine::raw::from_raw_parts_mut(&raw mut y, 2)); + // Memory nothing wrote, copied before it is written. + let src = [const { MaybeUninit::>::uninit() }; 2]; + let mut dst = [const { MaybeUninit::>::uninit() }; 2]; + unsafe { + ptr::copy_nonoverlapping(src.as_ptr(), dst.as_mut_ptr(), 2); + dst[1].write(if n & 1 == 0 { Ok((x, 1)) } else { Err(n) }); + out.push(match dst[1].assume_init() { + Ok((a, b)) => (a + b) as u8, + Err(e) => e, + }); + } + // A null pointer moved and back, and around wasm32's addresses to null. + let null = ptr::null::(); + out.push(null.wrapping_add(1).wrapping_sub(1).is_null() as u8); + out.push(null.wrapping_add(usize::from(n)).is_null() as u8); + out.push(null.wrapping_add(1 << 30).is_null() as u8); + // Two pointers at the same address off null, which are none apart. + let (p, q) = ( + null.wrapping_add(usize::from(n) + 1), + null.wrapping_add(usize::from(n) + 1), + ); + out.push(unsafe { p.offset_from(q) } as u8); + // A dangling pointer, at its alignment, moved to null and past it. + let dangling = NonNull::::dangling().as_ptr(); + out.push(dangling.wrapping_sub(1).is_null() as u8); + out.push(dangling.wrapping_add(usize::MAX).is_null() as u8); + out.push( + dangling + .wrapping_sub(usize::from(n)) + .wrapping_add(usize::from(n)) + .is_null() as u8, + ); + out.push( + NonNull::::dangling() + .as_ptr() + .wrapping_offset(isize::from(n % 3) - 1) + .is_null() as u8, + ); + // Empty raw slices from null and dangling pointers, which keep them. + out.push((ptr::slice_from_raw_parts(ptr::null::(), 0) as *const u32).is_null() as u8); + out.push((ptr::slice_from_raw_parts(ptr::null::(), 0) as *const u8).is_null() as u8); + let dangling = NonNull::::dangling().as_ptr(); + out.push( + (ptr::slice_from_raw_parts(dangling, 0) as *const u16) + .wrapping_sub(1) + .is_null() as u8, + ); + out.push(ptr::slice_from_raw_parts(ptr::null::(), 0).is_null() as u8); + out.push(ptr::slice_from_raw_parts_mut(ptr::null_mut::(), 0).is_null() as u8); + let none = unsafe { slice::from_raw_parts(NonNull::::dangling().as_ptr(), 0) }; + out.push(none.as_ptr().wrapping_sub(1).is_null() as u8); + out.push(none[0..0].as_ptr().wrapping_sub(1).is_null() as u8); + let wide = unsafe { slice::from_raw_parts(NonNull::::dangling().as_ptr(), 0) }; + out.push(wide[0..0].as_ptr().wrapping_sub(1).is_null() as u8); + out.push(wide.get(0..0).unwrap().as_ptr().wrapping_sub(1).is_null() as u8); + // A raw pointer to an array, as one to a slice of it. + let unsized_null: *const [u32] = ptr::null::<[u32; 3]>(); + out.push(unsized_null.is_null() as u8); + let row = [u32::from(n), 9, 10]; + let unsized_lanes: *const [u32] = &raw const row; + out.push(unsized_lanes.is_null() as u8); + out.push(unsafe { (unsized_lanes as *const u32).add(2).read() } as u8); + let off = ptr::null::().wrapping_add(usize::from(n % 3) + 1); + out.push( + (ptr::slice_from_raw_parts(off, 0) as *const u32) + .wrapping_sub(usize::from(n % 3) + 1) + .is_null() as u8, + ); + // Null and dangling pointers to arrays, cast to ones to their first elements. + out.push((ptr::null::<[u32; 2]>() as *const u32).is_null() as u8); + out.push( + (ptr::null_mut::<[u8; 4]>() as *mut u8) + .wrapping_add(usize::from(n)) + .is_null() as u8, + ); + out.push( + (NonNull::<[u16; 3]>::dangling().as_ptr() as *mut u16) + .wrapping_sub(1) + .is_null() as u8, + ); + } +} + +/// Raw pointers to slices, each where it starts and how long it is: read and written +/// through, whole and in part, of bytes too, made longer than what is there and never +/// read past it, `NonNull`'s, a boxed slice's, and null's. +pub fn raw_slices(data: &[u8], out: &mut Vec) { + let n = data.first().copied().unwrap_or(0); + let mut v: Vec = data.iter().map(|&b| u32::from(b)).chain([5, 6, 7]).collect(); + let len = v.len(); + let p = ptr::slice_from_raw_parts_mut(v.as_mut_ptr(), len); + unsafe { + (*p)[0] += 1; + (*p)[len - 1] = u32::from(n); + out.push((&*p).len() as u8); + out.push(p.len() as u8); + out.push((*p)[1] as u8); + let tail = &mut (&mut *p)[1..]; + tail[0] = 9; + out.push((&*p).iter().sum::() as u8); + } + emit(&v, out); + let mut bytes = data.to_vec(); + bytes.extend([3, 4]); + let b = ptr::slice_from_raw_parts_mut(bytes.as_mut_ptr(), bytes.len()); + unsafe { + (*b)[0] ^= 0xff; + out.push((*b)[(&*b).len() - 1]); + out.push((&*b)[1..].len() as u8); + } + out.extend(&bytes); + // Longer than its array, which nothing reads past. + let short = [u32::from(n), 1]; + let long = ptr::slice_from_raw_parts(short.as_ptr(), 10); + out.push(long.len() as u8); + out.push(unsafe { (long as *const u32).add(1).read() } as u8); + let nn = NonNull::slice_from_raw_parts(NonNull::new(v.as_mut_ptr()).unwrap(), 2); + out.push(nn.len() as u8); + unsafe { + (*nn.as_ptr())[1] = 11; + out.push(nn.as_ref()[1] as u8); + out.push(nn.as_ptr().as_mut().map_or(0, |s| s[0]) as u8); + let mut whole = nn; + whole.as_mut()[0] = 12; + let mut two = NonNull::slice_from_raw_parts(NonNull::new(bytes.as_mut_ptr()).unwrap(), 2); + two.as_mut()[1] ^= 0x0f; + out.push(two.as_ref()[1]); + } + let null = ptr::slice_from_raw_parts_mut(ptr::null_mut::(), 0); + out.push(NonNull::new(null).is_none() as u8); + out.push(unsafe { null.as_ref() }.is_none() as u8); + let boxed: Box<[u32]> = vec![u32::from(n), 2, 3].into_boxed_slice(); + let raw = Box::into_raw(boxed); + unsafe { (*raw)[2] = 8 }; + let back = unsafe { Box::from_raw(raw) }; + emit(&back, out); + let whole: *const [u32] = &raw const short; + let of_ref: *const [u32] = &v[1..3]; + out.push((whole.len() + of_ref.len()) as u8); + out.push(unsafe { (*of_ref)[1] } as u8); + emit(&v, out); + out.extend(&bytes); +} + +/// Empty slices made from pointers into no array, a dangling one and one off null, each +/// part of which keeps the pointer as Rust's does: after moving back to address 0 it is null. +pub fn empty_parts(data: &[u8], out: &mut Vec) { + let k = usize::from(data.first().copied().unwrap_or(0) % 4) + 1; + let wide = NonNull::::dangling().as_ptr(); + let off = ptr::null::().wrapping_add(k); + for (s, back) in [ + (unsafe { slice::from_raw_parts(wide, 0) }, 1), + (unsafe { slice::from_raw_parts(off, 0) }, k), + ] { + let null = |p: *const u16| p.wrapping_sub(back).is_null() as u8; + let (front, rest) = s.split_at(0); + out.extend([null(front.as_ptr()), null(rest.as_ptr())]); + let (front, rest) = s.split_at_checked(0).unwrap(); + out.extend([null(front.as_ptr()), null(rest.as_ptr())]); + out.push(null(s.chunks_exact(2).remainder().as_ptr())); + out.push(null(s.rchunks_exact(3).remainder().as_ptr())); + out.extend(s.split(|&x| x == 0).map(|part| null(part.as_ptr()))); + out.extend(s.rsplit(|&x| x == 0).map(|part| null(part.as_ptr()))); + out.extend(s.splitn(2, |&x| x == 0).map(|part| null(part.as_ptr()))); + out.push(null(s.first_chunk::<0>().unwrap().as_ptr())); + out.push(null(s.last_chunk::<0>().unwrap().as_ptr())); + out.push(null(s.get(..).unwrap().as_ptr())); + out.push(null(s[..0].as_ptr())); + out.push(s.split_first().is_none() as u8); + out.push(s.chunks(2).count() as u8); + } + let byte = NonNull::::dangling().as_ptr(); + let s = unsafe { slice::from_raw_parts_mut(byte, 0) }; + let null = |p: *const u8| p.wrapping_sub(1).is_null() as u8; + out.push(null(s.as_ptr())); + let (front, rest) = s.split_at_mut(0); + out.extend([null(front.as_ptr()), null(rest.as_ptr())]); + out.push(null(s[..].as_ptr())); + out.push(null(s.split(|&x| x == 0).next().unwrap().as_ptr())); + // A raw pointer to an empty slice, its length, and its start. + let raw = ptr::slice_from_raw_parts(off, 0); + out.push(raw.len() as u8); + out.push((raw as *const u16).wrapping_sub(k).is_null() as u8); + out.push(null((ptr::slice_from_raw_parts(byte, 0) as *const u8).wrapping_add(0))); + // None of a lone byte, at it and past it, shared and mutable. + let lone = data.len() as u8; + let p = &raw const lone; + unsafe { + out.push(slice::from_raw_parts(p, 0).as_ptr().offset_from(p) as u8); + out.push(slice::from_raw_parts(p.add(1), 0).as_ptr().offset_from(p) as u8); + } + let mut held = lone; + let q = &raw mut held; + let r = unsafe { slice::from_raw_parts_mut(q, 0) }; + out.push(r.len() as u8 + unsafe { r.as_ptr().offset_from(q) } as u8); +} + +/// Functions of the crate's named as std's pointer functions. +mod mine { + pub mod ptr { + pub fn offset_from(a: *const u32, b: *const u32) -> isize { + unsafe { *a as isize + *b as isize } + } + } + + pub mod raw { + pub fn from_raw_parts_mut(p: *mut u8, n: usize) -> u8 { + unsafe { *p }.wrapping_add(n as u8) + } + } +} + +fn emit(values: &[u32], out: &mut Vec) { + out.extend(values.iter().flat_map(|x| x.to_le_bytes())); +} diff --git a/ts/fixtures/lang/src/floats.rs b/ts/fixtures/lang/src/floats.rs index 666321cd6..0e2d2e056 100644 --- a/ts/fixtures/lang/src/floats.rs +++ b/ts/fixtures/lang/src/floats.rs @@ -314,13 +314,14 @@ pub fn floats(kind: u8, data: &[u8], out: &mut Vec) { /// `mul_add`, rounded once, of three operands, or of 32 sets made from a seed: any bits, /// a product's error alone, half the product's last place, where rounding the product /// first would tie, a product of half `c`'s last place, subnormal sums, products past `f64::MAX` that the sum brings back, and -/// each side of where the runtime's fast path ends. +/// each side of where the runtime's fast path ends. Each result follows its operands, which +/// `fma.ts` rounds exactly where native's differs. pub fn mul_adds(kind: u8, data: &[u8], out: &mut Vec) { if kind % 2 == 0 { let (a, rest) = take(data); let (b, rest) = take(rest); let (c, _) = take(rest); - put_f64(out, a.mul_add(b, c)); + put_mul_add(out, a, b, c); return; } let mut state = take_bits(data).0 | 1; @@ -378,7 +379,13 @@ pub fn mul_adds(kind: u8, data: &[u8], out: &mut Vec) { (a, b, (p.next_up() - p) * 0.5) } }; - put_f64(out, a.mul_add(b, c)); + put_mul_add(out, a, b, c); + } +} + +fn put_mul_add(out: &mut Vec, a: f64, b: f64, c: f64) { + for x in [a, b, c, a.mul_add(b, c)] { + put_f64(out, x); } } diff --git a/ts/fixtures/lang/src/harness.rs b/ts/fixtures/lang/src/harness.rs index a5814398b..2d78ca385 100644 --- a/ts/fixtures/lang/src/harness.rs +++ b/ts/fixtures/lang/src/harness.rs @@ -3,10 +3,10 @@ use crate::{ args, arrays, ascii, atomics, borrows, bounds, boxes, capacity, captures, cells, chars, chunks, clones, - collections, combinators, conversions, counts, cows, cycles, defaults, durations, dyns, edits, errors, fields, - floats, fmt, folds, generics, hashes, hints, holds, ints, iters, joins, keys, lengths, lists, locals, locks, - methods, nested, objects, orders, pointers, refs, results, returns, saturates, separated, shapes, singles, slices, - sorts, splits, statics, strs, tuples, utf8, wrappers, wraps, zeros, + collections, combinators, conversions, counts, cows, cycles, defaults, durations, dyns, edits, elements, errors, + fields, floats, fmt, folds, generics, hashes, hints, holds, ints, iters, joins, keys, lengths, lists, locals, + locks, methods, nested, objects, orders, pointers, refs, results, returns, saturates, separated, shapes, singles, + slices, sorts, splits, statics, strs, tuples, utf8, wrappers, wraps, zeros, }; /// The native side's entry: [`run`], except that a case starting with 0xff prints a @@ -288,6 +288,13 @@ fn cases() -> Vec<(&'static str, Case)> { ("capacity::maps", capacity::maps), ("locks::downgraded", |_, data, out| locks::downgraded(data, out)), ("borrows::leaks", |_, data, out| borrows::leaks(data, out)), + ("elements::vecs", |_, data, out| elements::vecs(data, out)), + ("elements::arrays", |_, data, out| elements::arrays(data, out)), + ("elements::segments", |_, data, out| elements::segments(data, out)), + ("elements::inline", |_, data, out| elements::inline(data, out)), + ("elements::edges", |_, data, out| elements::edges(data, out)), + ("elements::raw_slices", |_, data, out| elements::raw_slices(data, out)), + ("elements::empty_parts", |_, data, out| elements::empty_parts(data, out)), ("hashes::own", |_, data, out| hashes::own(data, out)), ("refs::compared", refs::compared), ("fmt::objects", |_, data, out| fmt::objects(data, out)), diff --git a/ts/fixtures/lang/src/lib.rs b/ts/fixtures/lang/src/lib.rs index dc583cb8f..3375f9f39 100644 --- a/ts/fixtures/lang/src/lib.rs +++ b/ts/fixtures/lang/src/lib.rs @@ -18,7 +18,8 @@ //! hashes of bytes, ranges of `Bound`s and of the crate's own `RangeBounds`, the equality //! of `Result`s and std's errors, inclusive ranges read through each adaptor, iterators' //! size hints, the capacity overflows collections reserving by them panic with, the -//! capacity of a `Vec`, `VecDeque`, and `BinaryHeap` as each grows and shrinks, tuples of +//! capacity of a `Vec`, `VecDeque`, and `BinaryHeap` as each grows and shrinks, pointers +//! into arrays moved, measured, and copied between, tuples of //! collections collected and extended by their items, zero-sized values, which MIR //! never assigns, with std's impls of them, functions generic over an array's length, and //! std's conversions of a reference: clones into it, searches, sizes, slices, cells, and @@ -62,6 +63,7 @@ mod defaults; mod durations; mod dyns; mod edits; +mod elements; mod errors; mod fields; mod floats; diff --git a/ts/fixtures/reject/census.md b/ts/fixtures/reject/census.md index 3ac7c9fd3..4ae4e6f58 100644 --- a/ts/fixtures/reject/census.md +++ b/ts/fixtures/reject/census.md @@ -1,16 +1,16 @@ # Census of `reject-fixture` -- Refusals: 229 -- Function instances: 289 +- Refusals: 249 +- Function instances: 296 - Generic functions no instance reached, lowered over their type parameters: 13 ## By construct | Count | Construct | First | | ---: | --- | --- | -| 49 | a raw pointer to `_` in `_`, which the crate may put another object in place of | fixtures/reject/src/lib.rs:586:10 | -| 25 | unsupported call to `_` | fixtures/reject/src/lib.rs:160:8 | -| 22 | a raw pointer to `_` in a variable assigned again while it may point there | fixtures/reject/src/lib.rs:594:10 | +| 50 | a raw pointer to `_` in `_`, which the crate may put another object in place of | fixtures/reject/src/lib.rs:586:10 | +| 23 | a raw pointer to `_` in a variable assigned again while it may point there | fixtures/reject/src/lib.rs:594:10 | +| 20 | unsupported call to `_` | fixtures/reject/src/lib.rs:160:8 | | 15 | a reference through which a raw pointer may be made into `_` in `_`, which the crate may put another object in place of | fixtures/reject/src/lib.rs:1315:33 | | 15 | a reference to `_`, through which a raw pointer may be made, in a variable assigned again while that pointer may point there | fixtures/reject/src/lib.rs:1633:18 | | 5 | `_` with a mutable reference to a value JS copies, which its mapping would copy | fixtures/reject/src/lib.rs:1213:22 | @@ -18,16 +18,22 @@ | 4 | a variant of `_`, which is not supported: either is the same JS string | fixtures/reject/src/lib.rs:309:2 | | 4 | an assignment through a reference to `_` | fixtures/reject/src/lib.rs:83:2 | | 3 | `_` of `_`, whose `clone_from` is its own, which is not supported yet | fixtures/reject/src/lib.rs:21:4 | +| 3 | a copy between pointers to `_`, which JS keeps as an object that knows no array it is in | fixtures/reject/src/lib.rs:1688:3 | | 3 | a copy of a closure that changes what it captured | fixtures/reject/src/lib.rs:224:14 | -| 3 | a slice or a cell of a mutable reference to `_` that names no place, which a whole write would replace only there | fixtures/reject/src/lib.rs:2142:2 | +| 3 | a slice or a cell of a mutable reference to `_` that names no place, which a whole write would replace only there | fixtures/reject/src/lib.rs:2244:2 | +| 3 | an offset of a pointer to `_`, which JS keeps as an object that knows no array it is in | fixtures/reject/src/lib.rs:1691:56 | | 3 | replacing the value behind a reference to `_` | fixtures/reject/src/lib.rs:91:2 | | 3 | unsupported type `_` | fixtures/reject/src/lib.rs:7:14 | | 2 | `_` is not supported: its `None` and `Some` would both be `rt.None` | fixtures/reject/src/lib.rs:16:15 | | 2 | `_` of `_`, whose `None` is `rt.None`, which is not supported yet | fixtures/reject/src/lib.rs:37:4 | | 2 | `_`, a trait object of another crate's trait, which is not supported yet | fixtures/reject/src/lib.rs:11:16 | | 2 | `_`, a trait object of the generic trait `_`, which is not supported yet | fixtures/reject/src/lib.rs:442:23 | +| 2 | `offset_from` between a pointer into a value in no array and one into a value in no array, which JS keeps in different arrays | fixtures/reject/src/lib.rs:2203:5 | +| 2 | `offset_from` between a pointer into an array of this body's own and one into an array of this body's own, which JS keeps in different arrays | fixtures/reject/src/lib.rs:2205:24 | | 2 | a cast from a pointer to `_` to one to `_`, which reads the bytes of one type as another | fixtures/reject/src/lib.rs:533:27 | | 2 | a coercion to `_`, which is not supported yet | fixtures/reject/src/lib.rs:433:40 | +| 2 | a pointer reaching past a shared slice JS copies, which JS keeps apart from what holds it in Rust | fixtures/reject/src/lib.rs:2228:18 | +| 2 | a raw or mutable slice of a byte in no array of bytes | fixtures/reject/src/lib.rs:2214:12 | | 2 | a raw pointer to a place of `()`, which has no address of its own | fixtures/reject/src/lib.rs:706:15 | | 2 | an iterator of the crate implementing `_`, which is not supported yet | fixtures/reject/src/lib.rs:145:1 | | 2 | comparing pointers to `_`, each a place JS makes of its own | fixtures/reject/src/lib.rs:574:2 | @@ -49,24 +55,33 @@ | 1 | `clone_from` of `_`, whose items' `clone_from` is their own, which is not supported yet | fixtures/reject/src/lib.rs:2096:4 | | 1 | `collect` of `_`, which sorts by `_`'s own `Ord`, of entries that drop, which is not supported yet | fixtures/reject/src/lib.rs:933:22 | | 1 | `fmt::Arguments` with a placeholder, or not made in place, written to `_`'s own `fmt::Write`, which is not supported yet: std splits what a placeholder writes into `write_str` calls the runtime does not make | fixtures/reject/src/lib.rs:55:10 | +| 1 | `offset_from` between a pointer into a shared slice JS copies and one into a value in no array, which JS keeps in different arrays | fixtures/reject/src/lib.rs:2208:21 | +| 1 | `offset_from` between a pointer into a shared slice JS copies and one into an array of this body's own, which JS keeps in different arrays | fixtures/reject/src/lib.rs:2207:20 | +| 1 | `offset_from` of pointers to `()`, which has no bytes | fixtures/reject/src/lib.rs:2150:13 | +| 1 | `offset_from` of pointers to `_`, which JS keeps as an object that knows no array it is in | fixtures/reject/src/lib.rs:2143:36 | | 1 | a `collect` in place of `_`, a zip std reads by index, holding values that drop, which is not supported yet | fixtures/reject/src/lib.rs:209:40 | | 1 | a `skip` of `_`, a zip std reads by index, of items that drop, which is not supported yet | fixtures/reject/src/lib.rs:202:8 | | 1 | a cast from `_` to `_` | fixtures/reject/src/lib.rs:68:2 | -| 1 | a cast from `_` to `_`, which reads the bytes of one type as another | fixtures/reject/src/lib.rs:1691:33 | -| 1 | a coercion of `_` to `_`, a pointer to a slice | fixtures/reject/src/lib.rs:1692:28 | | 1 | a coercion to `_` | fixtures/reject/src/lib.rs:276:2 | | 1 | a constant of type `_`, a pointer into memory translation can't name | fixtures/reject/src/lib.rs:756:3 | +| 1 | a copy reaching past a shared slice JS copies, which JS keeps apart from what holds it in Rust | fixtures/reject/src/lib.rs:2226:3 | | 1 | a dangling pointer to `_`, a value of no bytes made of more than structs and tuples | fixtures/reject/src/lib.rs:715:11 | | 1 | a future of `_`, which can be `rt.Pending`, as the future's own `Pending` is | fixtures/reject/src/lib.rs:77:34 | | 1 | a mapped guard of `_`, which JS can't write in place | fixtures/reject/src/lib.rs:2130:10 | | 1 | a mutable reference into a `Result`'s payload, passed on or kept | fixtures/reject/src/lib.rs:340:12 | | 1 | a mutable static, which any code may write | fixtures/reject/src/lib.rs:61:11 | +| 1 | a pointer into `_`, which JS keeps as an object that knows no array it is in | fixtures/reject/src/lib.rs:2137:12 | +| 1 | a pointer into an array of `_`, which JS keeps as an object that knows no array it is in | fixtures/reject/src/lib.rs:2142:30 | +| 1 | a pointer reaching past a value in no array, which JS keeps apart from what holds it in Rust | fixtures/reject/src/lib.rs:2230:27 | | 1 | a pointer's address, which JS has no counterpart of | fixtures/reject/src/lib.rs:538:17 | | 1 | a printable type must implement `Display` or `Debug` | fixtures/reject/src/lib.rs:64:1 | | 1 | a raw pointer to `_` behind a reference, which is the object and not where it is kept | fixtures/reject/src/lib.rs:555:10 | | 1 | a reference through which a raw pointer may be made into `_` in an element, a payload, or a union field, which is not supported yet | fixtures/reject/src/lib.rs:1342:11 | | 1 | a scalar constant of type `_` | fixtures/reject/src/lib.rs:8:2 | -| 1 | a slice of one byte's place, which a byte slice can view only in an array of bytes | fixtures/reject/src/lib.rs:2136:2 | +| 1 | a slice from a pointer to `_`, which JS keeps as an object that knows no array it is in | fixtures/reject/src/lib.rs:2141:11 | +| 1 | a slice of `()`, which has no bytes, from a pointer | fixtures/reject/src/lib.rs:2150:39 | +| 1 | a slice of one byte's place, which a byte slice can view only in an array of bytes | fixtures/reject/src/lib.rs:2238:2 | +| 1 | a slice reaching past a value in no array, which JS keeps apart from what holds it in Rust | fixtures/reject/src/lib.rs:2229:6 | | 1 | a trait object of `_`, which is not supported yet | fixtures/reject/src/lib.rs:459:26 | | 1 | an upcast from `_` to `_`, which is not supported yet | fixtures/reject/src/lib.rs:510:32 | | 1 | arithmetic on `_` | fixtures/reject/src/lib.rs:8:2 | @@ -89,22 +104,20 @@ The type or module each refused type or path belongs to. | ---: | --- | --- | | 39 | `std::cell::UnsafeCell` | fixtures/reject/src/lib.rs:665:15 | | 27 | `(u32, u32)` | fixtures/reject/src/lib.rs:586:10 | -| 6 | `[u8; 4]` | fixtures/reject/src/lib.rs:459:26 | +| 11 | `(u8, u8)` | fixtures/reject/src/lib.rs:1250:31 | +| 9 | `[u8; 4]` | fixtures/reject/src/lib.rs:459:26 | +| 7 | `u32` | fixtures/reject/src/lib.rs:533:27 | | 6 | `f16` | fixtures/reject/src/lib.rs:7:14 | | 6 | `std::borrow::Cow` | fixtures/reject/src/lib.rs:309:2 | -| 6 | `u32` | fixtures/reject/src/lib.rs:533:27 | -| 5 | `(u8, u8)` | fixtures/reject/src/lib.rs:1250:31 | | 5 | `Gap` | fixtures/reject/src/lib.rs:886:10 | | 5 | `Option` | fixtures/reject/src/lib.rs:16:15 | -| 5 | `u8` | fixtures/reject/src/lib.rs:253:6 | | 4 | `Iterator` | fixtures/reject/src/lib.rs:37:4 | | 4 | `std::pin::Pin` | fixtures/reject/src/lib.rs:91:2 | | 4 | `std::str` | fixtures/reject/src/lib.rs:417:4 | -| 3 | `*const [u8; 4]` | fixtures/reject/src/lib.rs:1691:33 | +| 4 | `u8` | fixtures/reject/src/lib.rs:253:6 | | 3 | `Mid` | fixtures/reject/src/lib.rs:1333:34 | | 3 | `Outer` | fixtures/reject/src/lib.rs:1333:34 | | 3 | `Reused` | fixtures/reject/src/lib.rs:21:4 | -| 3 | `std::ptr` | fixtures/reject/src/lib.rs:1688:3 | | 3 | `std::vec::Vec` | fixtures/reject/src/lib.rs:295:16 | | 3 | `{closure}` | fixtures/reject/src/lib.rs:304:4 | | 2 | `(u16, u16)` | fixtures/reject/src/lib.rs:1154:31 | @@ -116,6 +129,7 @@ The type or module each refused type or path belongs to. | 2 | `Inside` | fixtures/reject/src/lib.rs:1823:20 | | 2 | `Poll` | fixtures/reject/src/lib.rs:72:20 | | 2 | `Scaled` | fixtures/reject/src/lib.rs:442:23 | +| 2 | `[u32; 3]` | fixtures/reject/src/lib.rs:2168:10 | | 2 | `[u8; 2]` | fixtures/reject/src/lib.rs:1704:37 | | 2 | `[u8; 3]` | fixtures/reject/src/lib.rs:1704:15 | | 2 | `dyn Scaled` | fixtures/reject/src/lib.rs:442:23 | @@ -128,15 +142,14 @@ The type or module each refused type or path belongs to. | 2 | `std::mem::MaybeUninit` | fixtures/reject/src/lib.rs:900:19 | | 2 | `std::ops::Range` | fixtures/reject/src/lib.rs:160:8 | | 2 | `std::option::Option` | fixtures/reject/src/lib.rs:347:2 | -| 2 | `std::ptr::const_ptr` | fixtures/reject/src/lib.rs:1691:73 | | 2 | `std::std` | fixtures/reject/src/lib.rs:545:11 | | 2 | `std::sync::Arc` | fixtures/reject/src/lib.rs:276:2 | | 2 | `std::sync::MutexGuard` | fixtures/reject/src/lib.rs:83:2 | | 2 | `std::vec::IntoIter` | fixtures/reject/src/lib.rs:412:17 | -| 1 | `(Dropped, (u8, u8))` | fixtures/reject/src/lib.rs:2241:2 | -| 1 | `([Cloned; 2], (u8, u8))` | fixtures/reject/src/lib.rs:2225:2 | -| 1 | `([Greatest; 2], (u8, u8))` | fixtures/reject/src/lib.rs:2259:13 | -| 1 | `([Touched; 2], (u8, u8))` | fixtures/reject/src/lib.rs:2290:2 | +| 1 | `(Dropped, (u8, u8))` | fixtures/reject/src/lib.rs:2343:2 | +| 1 | `([Cloned; 2], (u8, u8))` | fixtures/reject/src/lib.rs:2327:2 | +| 1 | `([Greatest; 2], (u8, u8))` | fixtures/reject/src/lib.rs:2361:13 | +| 1 | `([Touched; 2], (u8, u8))` | fixtures/reject/src/lib.rs:2392:2 | | 1 | `(std::vec::Vec, Tens)` | fixtures/reject/src/lib.rs:1250:31 | | 1 | `(u16, ByKeyReplace)` | fixtures/reject/src/lib.rs:1599:55 | | 1 | `(u16, ByKeySet)` | fixtures/reject/src/lib.rs:1591:51 | @@ -144,8 +157,7 @@ The type or module each refused type or path belongs to. | 1 | `(u16, ByRefReplace)` | fixtures/reject/src/lib.rs:1575:32 | | 1 | `(u16, ByRefTake)` | fixtures/reject/src/lib.rs:1583:32 | | 1 | `(u16, InTuple)` | fixtures/reject/src/lib.rs:1324:33 | -| 1 | `*const [u8]` | fixtures/reject/src/lib.rs:1692:28 | -| 1 | `*const u8` | fixtures/reject/src/lib.rs:1691:33 | +| 1 | `*const [u8; 4]` | fixtures/reject/src/lib.rs:1694:5 | | 1 | `*mut [u8; 4]` | fixtures/reject/src/lib.rs:1690:41 | | 1 | `Aliased` | fixtures/reject/src/lib.rs:1098:29 | | 1 | `Assigned` | fixtures/reject/src/lib.rs:1990:20 | @@ -172,7 +184,7 @@ The type or module each refused type or path belongs to. | 1 | `BytesMut` | fixtures/reject/src/lib.rs:361:6 | | 1 | `Called` | fixtures/reject/src/lib.rs:1064:28 | | 1 | `Chosen` | fixtures/reject/src/lib.rs:1884:17 | -| 1 | `Clone` | fixtures/reject/src/lib.rs:2147:4 | +| 1 | `Clone` | fixtures/reject/src/lib.rs:2249:4 | | 1 | `Closed` | fixtures/reject/src/lib.rs:2056:15 | | 1 | `Compared` | fixtures/reject/src/lib.rs:1851:22 | | 1 | `Copied` | fixtures/reject/src/lib.rs:1154:31 | @@ -190,6 +202,7 @@ The type or module each refused type or path belongs to. | 1 | `InGeneric` | fixtures/reject/src/lib.rs:1300:9 | | 1 | `InNested` | fixtures/reject/src/lib.rs:1288:9 | | 1 | `Lane` | fixtures/reject/src/lib.rs:850:19 | +| 1 | `Lanes` | fixtures/reject/src/lib.rs:2170:10 | | 1 | `Loaded` | fixtures/reject/src/lib.rs:1120:29 | | 1 | `Looped` | fixtures/reject/src/lib.rs:1088:26 | | 1 | `Mapped` | fixtures/reject/src/lib.rs:1165:39 | @@ -222,31 +235,32 @@ The type or module each refused type or path belongs to. | 1 | `[InArray; 2]` | fixtures/reject/src/lib.rs:1315:33 | | 1 | `[InWritten; 1]` | fixtures/reject/src/lib.rs:1376:35 | | 1 | `[std::cell::UnsafeCell; 2]` | fixtures/reject/src/lib.rs:2069:20 | +| 1 | `[u32; 2]` | fixtures/reject/src/lib.rs:2155:13 | | 1 | `dyn Keyed` | fixtures/reject/src/lib.rs:510:32 | | 1 | `poll` | fixtures/reject/src/lib.rs:480:2 | | 1 | `rt.Poll` | fixtures/reject/src/lib.rs:480:2 | | 1 | `std::any::Any` | fixtures/reject/src/lib.rs:516:4 | | 1 | `std::cell::Cell` | fixtures/reject/src/lib.rs:933:22 | -| 1 | `std::cell::LazyCell` | fixtures/reject/src/lib.rs:2184:3 | +| 1 | `std::cell::LazyCell` | fixtures/reject/src/lib.rs:2286:3 | | 1 | `std::collections::BTreeMap` | fixtures/reject/src/lib.rs:933:22 | | 1 | `std::collections::HashMap` | fixtures/reject/src/lib.rs:253:6 | | 1 | `std::default::Default` | fixtures/reject/src/lib.rs:2097:2 | | 1 | `std::iter::Map` | fixtures/reject/src/lib.rs:209:40 | | 1 | `std::iter::Zip` | fixtures/reject/src/lib.rs:202:8 | | 1 | `std::iter` | fixtures/reject/src/lib.rs:1213:22 | -| 1 | `std::mem` | fixtures/reject/src/lib.rs:2152:2 | +| 1 | `std::mem` | fixtures/reject/src/lib.rs:2254:2 | | 1 | `std::net::AddrParseError` | fixtures/reject/src/lib.rs:416:32 | | 1 | `std::net::Ipv4Addr` | fixtures/reject/src/lib.rs:416:32 | | 1 | `std::num` | fixtures/reject/src/lib.rs:1255:4 | | 1 | `std::ops::RangeFrom` | fixtures/reject/src/lib.rs:962:32 | | 1 | `std::ops::RangeTo` | fixtures/reject/src/lib.rs:962:11 | -| 1 | `std::ptr::mut_ptr` | fixtures/reject/src/lib.rs:1691:56 | +| 1 | `std::ptr` | fixtures/reject/src/lib.rs:1690:14 | | 1 | `std::slice::IterMut` | fixtures/reject/src/lib.rs:744:15 | | 1 | `std::std::Layout` | fixtures/reject/src/lib.rs:543:15 | -| 1 | `std::sync::LazyLock` | fixtures/reject/src/lib.rs:2189:2 | +| 1 | `std::sync::LazyLock` | fixtures/reject/src/lib.rs:2291:2 | | 1 | `std::sync::Weak` | fixtures/reject/src/lib.rs:433:40 | | 1 | `std::task::Poll` | fixtures/reject/src/lib.rs:77:34 | -| 1 | `std::thread::LocalKey` | fixtures/reject/src/lib.rs:2195:2 | +| 1 | `std::thread::LocalKey` | fixtures/reject/src/lib.rs:2297:2 | | 1 | `str` | fixtures/reject/src/lib.rs:304:4 | | 1 | `u64` | fixtures/reject/src/lib.rs:68:2 | | 1 | `value` | fixtures/reject/src/lib.rs:31:2 | @@ -264,6 +278,7 @@ The type or module each refused type or path belongs to. | 2 | `_` of `_`, whose `None` is `rt.None`, which is not supported yet | `Iterator::scan`, `Option` | fixtures/reject/src/lib.rs:37:4 | | 2 | `_`, a trait object of another crate's trait, which is not supported yet | `dyn std::iter::Iterator` | fixtures/reject/src/lib.rs:11:16 | | 2 | `_`, a trait object of the generic trait `_`, which is not supported yet | `dyn Scaled`, `Scaled` | fixtures/reject/src/lib.rs:442:23 | +| 2 | a copy between pointers to `_`, which JS keeps as an object that knows no array it is in | `[u8; 4]` | fixtures/reject/src/lib.rs:1688:3 | | 2 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `std::mem::MaybeUninit`, `Gap` | fixtures/reject/src/lib.rs:900:19 | | 2 | a raw pointer to `_` in a variable assigned again while it may point there | `[u8; 4]` | fixtures/reject/src/lib.rs:1711:26 | | 2 | a raw pointer to `_` in a variable assigned again while it may point there | `std::cell::UnsafeCell` | fixtures/reject/src/lib.rs:674:15 | @@ -272,6 +287,7 @@ The type or module each refused type or path belongs to. | 2 | a reference to `_`, through which a raw pointer may be made, in a variable assigned again while that pointer may point there | `Inside` | fixtures/reject/src/lib.rs:1823:20 | | 2 | a reference to `_`, through which a raw pointer may be made, in a variable assigned again while that pointer may point there | `[u8; 4]` | fixtures/reject/src/lib.rs:1711:26 | | 2 | an assignment through a reference to `_` | `std::pin::Pin` | fixtures/reject/src/lib.rs:107:2 | +| 2 | an offset of a pointer to `_`, which JS keeps as an object that knows no array it is in | `[u8; 4]` | fixtures/reject/src/lib.rs:1691:56 | | 2 | comparing pointers to `_`, each a place JS makes of its own | `u32` | fixtures/reject/src/lib.rs:574:2 | | 2 | replacing the value behind a reference to `_` | `std::pin::Pin` | fixtures/reject/src/lib.rs:91:2 | | 2 | unsupported call to `_` | `::sum` | fixtures/reject/src/lib.rs:412:17 | @@ -285,7 +301,7 @@ The type or module each refused type or path belongs to. | 1 | `_` is not supported: its field `_` is `_`, typed `_`, whose `Pending` would be `rt.OuterPending` as `_` | `Waiting`, `poll`, `Poll`, `rt.Poll`, `Poll` | fixtures/reject/src/lib.rs:480:2 | | 1 | `_` of `_` by `_`, which is not supported yet | `HashMap::contains_key`, `std::collections::HashMap`, `u8` | fixtures/reject/src/lib.rs:253:6 | | 1 | `_` of `_`, which is not supported yet | `std::any::Any::type_id`, `dyn std::any::Any` | fixtures/reject/src/lib.rs:516:4 | -| 1 | `_` of `_`, whose `clone_from` is its own, which is not supported yet | `Clone::clone_from`, `std::vec::Vec` | fixtures/reject/src/lib.rs:2147:4 | +| 1 | `_` of `_`, whose `clone_from` is its own, which is not supported yet | `Clone::clone_from`, `std::vec::Vec` | fixtures/reject/src/lib.rs:2249:4 | | 1 | `_` of `_`, whose `clone_from` is its own, which is not supported yet | `slice::clone_from_slice`, `Reused` | fixtures/reject/src/lib.rs:271:4 | | 1 | `_` of `_`, whose `clone_from` is its own, which is not supported yet | `slice::fill`, `Reused` | fixtures/reject/src/lib.rs:21:4 | | 1 | `_` with a mutable reference to a value JS copies, which its mapping would copy | `std::collections::LinkedList::retain` | fixtures/reject/src/lib.rs:2095:4 | @@ -297,20 +313,22 @@ The type or module each refused type or path belongs to. | 1 | `clone_from` of `_`, whose items' `clone_from` is their own, which is not supported yet | `std::collections::LinkedList` | fixtures/reject/src/lib.rs:2096:4 | | 1 | `collect` of `_`, which sorts by `_`'s own `Ord`, of entries that drop, which is not supported yet | `std::collections::BTreeMap`, `std::cell::Cell` | fixtures/reject/src/lib.rs:933:22 | | 1 | `fmt::Arguments` with a placeholder, or not made in place, written to `_`'s own `fmt::Write`, which is not supported yet: std splits what a placeholder writes into `write_str` calls the runtime does not make | `Sink` | fixtures/reject/src/lib.rs:55:10 | +| 1 | `offset_from` of pointers to `_`, which JS keeps as an object that knows no array it is in | `(u8, u8)` | fixtures/reject/src/lib.rs:2143:36 | | 1 | a `collect` in place of `_`, a zip std reads by index, holding values that drop, which is not supported yet | `std::iter::Map` | fixtures/reject/src/lib.rs:209:40 | | 1 | a `skip` of `_`, a zip std reads by index, of items that drop, which is not supported yet | `std::iter::Zip` | fixtures/reject/src/lib.rs:202:8 | | 1 | a cast from `_` to `_` | `u64`, `f16` | fixtures/reject/src/lib.rs:68:2 | -| 1 | a cast from `_` to `_`, which reads the bytes of one type as another | `*const [u8; 4]`, `*const u8` | fixtures/reject/src/lib.rs:1691:33 | -| 1 | a cast from a pointer to `_` to one to `_`, which reads the bytes of one type as another | `[u8; 4]`, `u8` | fixtures/reject/src/lib.rs:1691:17 | +| 1 | a cast from a pointer to `_` to one to `_`, which reads the bytes of one type as another | `u32`, `[u32; 2]` | fixtures/reject/src/lib.rs:2155:13 | | 1 | a cast from a pointer to `_` to one to `_`, which reads the bytes of one type as another | `u32`, `u8` | fixtures/reject/src/lib.rs:533:27 | -| 1 | a coercion of `_` to `_`, a pointer to a slice | `*const [u8; 4]`, `*const [u8]` | fixtures/reject/src/lib.rs:1692:28 | | 1 | a coercion to `_` | `std::sync::Arc` | fixtures/reject/src/lib.rs:276:2 | | 1 | a coercion to `_`, which is not supported yet | `&Tail` | fixtures/reject/src/lib.rs:469:30 | | 1 | a coercion to `_`, which is not supported yet | `std::sync::Weak` | fixtures/reject/src/lib.rs:433:40 | | 1 | a constant of type `_`, a pointer into memory translation can't name | `&u32` | fixtures/reject/src/lib.rs:756:3 | +| 1 | a copy between pointers to `_`, which JS keeps as an object that knows no array it is in | `(u8, u8)` | fixtures/reject/src/lib.rs:2140:3 | | 1 | a dangling pointer to `_`, a value of no bytes made of more than structs and tuples | `Only` | fixtures/reject/src/lib.rs:715:11 | | 1 | a future of `_`, which can be `rt.Pending`, as the future's own `Pending` is | `std::task::Poll` | fixtures/reject/src/lib.rs:77:34 | | 1 | a mapped guard of `_`, which JS can't write in place | `{closure}` | fixtures/reject/src/lib.rs:2130:10 | +| 1 | a pointer into `_`, which JS keeps as an object that knows no array it is in | `(u8, u8)` | fixtures/reject/src/lib.rs:2137:12 | +| 1 | a pointer into an array of `_`, which JS keeps as an object that knows no array it is in | `(u8, u8)` | fixtures/reject/src/lib.rs:2142:30 | | 1 | a raw pointer to `_` behind a reference, which is the object and not where it is kept | `std::vec::Vec` | fixtures/reject/src/lib.rs:555:10 | | 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `(u16, u16)`, `Copied` | fixtures/reject/src/lib.rs:1154:31 | | 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `(u16, u16)`, `Pushed` | fixtures/reject/src/lib.rs:1226:22 | @@ -320,15 +338,16 @@ The type or module each refused type or path belongs to. | 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `(u32, u32)`, `Track` | fixtures/reject/src/lib.rs:830:19 | | 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `(u32, u32)`, `Window` | fixtures/reject/src/lib.rs:647:2 | | 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `(u32, u32)`, `std::cell::UnsafeCell` | fixtures/reject/src/lib.rs:665:15 | -| 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `(u8, u8)`, `(Dropped, (u8, u8))` | fixtures/reject/src/lib.rs:2241:2 | -| 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `(u8, u8)`, `([Cloned; 2], (u8, u8))` | fixtures/reject/src/lib.rs:2225:2 | -| 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `(u8, u8)`, `([Greatest; 2], (u8, u8))` | fixtures/reject/src/lib.rs:2259:13 | -| 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `(u8, u8)`, `([Touched; 2], (u8, u8))` | fixtures/reject/src/lib.rs:2290:2 | +| 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `(u8, u8)`, `(Dropped, (u8, u8))` | fixtures/reject/src/lib.rs:2343:2 | +| 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `(u8, u8)`, `([Cloned; 2], (u8, u8))` | fixtures/reject/src/lib.rs:2327:2 | +| 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `(u8, u8)`, `([Greatest; 2], (u8, u8))` | fixtures/reject/src/lib.rs:2361:13 | +| 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `(u8, u8)`, `([Touched; 2], (u8, u8))` | fixtures/reject/src/lib.rs:2392:2 | | 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `ByKeyReplace`, `(u16, ByKeyReplace)` | fixtures/reject/src/lib.rs:1599:55 | | 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `ByKeySet`, `(u16, ByKeySet)` | fixtures/reject/src/lib.rs:1591:51 | | 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `ByKeyTake`, `(u16, ByKeyTake)` | fixtures/reject/src/lib.rs:1606:52 | | 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `ByRefReplace`, `(u16, ByRefReplace)` | fixtures/reject/src/lib.rs:1575:32 | | 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `ByRefTake`, `(u16, ByRefTake)` | fixtures/reject/src/lib.rs:1583:32 | +| 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `[u32; 3]`, `Lanes` | fixtures/reject/src/lib.rs:2170:10 | | 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `std::cell::UnsafeCell`, `Aliased` | fixtures/reject/src/lib.rs:1098:29 | | 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `std::cell::UnsafeCell`, `ByBox` | fixtures/reject/src/lib.rs:1482:35 | | 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `std::cell::UnsafeCell`, `ByClosure` | fixtures/reject/src/lib.rs:1497:35 | @@ -357,6 +376,7 @@ The type or module each refused type or path belongs to. | 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `std::cell::UnsafeCell`, `Repeated` | fixtures/reject/src/lib.rs:1199:18 | | 1 | a raw pointer to `_` in `_`, which the crate may put another object in place of | `std::cell::UnsafeCell`, `Rewritten` | fixtures/reject/src/lib.rs:1108:47 | | 1 | a raw pointer to `_` in a variable assigned again while it may point there | `Target` | fixtures/reject/src/lib.rs:1647:10 | +| 1 | a raw pointer to `_` in a variable assigned again while it may point there | `[u32; 3]` | fixtures/reject/src/lib.rs:2168:10 | | 1 | a raw pointer to `_` in an element, a payload, or a union field, which is not supported yet | `(u32, u32)` | fixtures/reject/src/lib.rs:657:10 | | 1 | a reference through which a raw pointer may be made into `_` in `_`, which the crate may put another object in place of | `InArray`, `[InArray; 2]` | fixtures/reject/src/lib.rs:1315:33 | | 1 | a reference through which a raw pointer may be made into `_` in `_`, which the crate may put another object in place of | `InTuple`, `(u16, InTuple)` | fixtures/reject/src/lib.rs:1324:33 | @@ -383,14 +403,16 @@ The type or module each refused type or path belongs to. | 1 | a reference to `_`, through which a raw pointer may be made, in a variable assigned again while that pointer may point there | `std::cell::UnsafeCell` | fixtures/reject/src/lib.rs:1633:18 | | 1 | a reference to `_`, through which a raw pointer may be made, in a variable assigned again while that pointer may point there | `std::option::Option` | fixtures/reject/src/lib.rs:2086:13 | | 1 | a scalar constant of type `_` | `f16` | fixtures/reject/src/lib.rs:8:2 | -| 1 | a slice or a cell of a mutable reference to `_` that names no place, which a whole write would replace only there | `Reused` | fixtures/reject/src/lib.rs:2142:2 | -| 1 | a slice or a cell of a mutable reference to `_` that names no place, which a whole write would replace only there | `std::cell::LazyCell` | fixtures/reject/src/lib.rs:2184:3 | -| 1 | a slice or a cell of a mutable reference to `_` that names no place, which a whole write would replace only there | `std::thread::LocalKey` | fixtures/reject/src/lib.rs:2195:2 | +| 1 | a slice from a pointer to `_`, which JS keeps as an object that knows no array it is in | `(u8, u8)` | fixtures/reject/src/lib.rs:2141:11 | +| 1 | a slice or a cell of a mutable reference to `_` that names no place, which a whole write would replace only there | `Reused` | fixtures/reject/src/lib.rs:2244:2 | +| 1 | a slice or a cell of a mutable reference to `_` that names no place, which a whole write would replace only there | `std::cell::LazyCell` | fixtures/reject/src/lib.rs:2286:3 | +| 1 | a slice or a cell of a mutable reference to `_` that names no place, which a whole write would replace only there | `std::thread::LocalKey` | fixtures/reject/src/lib.rs:2297:2 | | 1 | a trait object of `_`, which is not supported yet | `[u8; 4]` | fixtures/reject/src/lib.rs:459:26 | -| 1 | an assignment through a reference to `_` | `std::sync::LazyLock` | fixtures/reject/src/lib.rs:2189:2 | +| 1 | an assignment through a reference to `_` | `std::sync::LazyLock` | fixtures/reject/src/lib.rs:2291:2 | | 1 | an assignment through a reference to `_` | `std::sync::MutexGuard` | fixtures/reject/src/lib.rs:83:2 | | 1 | an iterator of the crate implementing `_`, which is not supported yet | `DoubleEndedIterator` | fixtures/reject/src/lib.rs:145:1 | | 1 | an iterator of the crate implementing `_`, which is not supported yet | `FusedIterator` | fixtures/reject/src/lib.rs:193:1 | +| 1 | an offset of a pointer to `_`, which JS keeps as an object that knows no array it is in | `(u8, u8)` | fixtures/reject/src/lib.rs:2139:13 | | 1 | an upcast from `_` to `_`, which is not supported yet | `dyn Keyed`, `dyn std::any::Any` | fixtures/reject/src/lib.rs:510:32 | | 1 | arithmetic on `_` | `f16` | fixtures/reject/src/lib.rs:8:2 | | 1 | comparing `_` | `*mut [u8; 4]` | fixtures/reject/src/lib.rs:1690:41 | @@ -415,17 +437,12 @@ The type or module each refused type or path belongs to. | 1 | unsupported call to `_` | `::hash` | fixtures/reject/src/lib.rs:286:10 | | 1 | unsupported call to `_` | `std::any::TypeId::of` | fixtures/reject/src/lib.rs:516:17 | | 1 | unsupported call to `_` | `std::borrow::Cow::is_borrowed` | fixtures/reject/src/lib.rs:321:2 | -| 1 | unsupported call to `_` | `std::mem::size_of_val` | fixtures/reject/src/lib.rs:2152:2 | +| 1 | unsupported call to `_` | `std::mem::size_of_val` | fixtures/reject/src/lib.rs:2254:2 | | 1 | unsupported call to `_` | `std::num::is_ascii_octdigit` | fixtures/reject/src/lib.rs:1255:4 | | 1 | unsupported call to `_` | `std::ops::Range::contains` | fixtures/reject/src/lib.rs:160:8 | | 1 | unsupported call to `_` | `std::ops::Range::is_empty` | fixtures/reject/src/lib.rs:967:9 | | 1 | unsupported call to `_` | `std::ops::RangeTo::contains` | fixtures/reject/src/lib.rs:962:11 | | 1 | unsupported call to `_` | `std::ptr::addr_eq` | fixtures/reject/src/lib.rs:1690:14 | -| 1 | unsupported call to `_` | `std::ptr::const_ptr::len` | fixtures/reject/src/lib.rs:1695:58 | -| 1 | unsupported call to `_` | `std::ptr::const_ptr::offset` | fixtures/reject/src/lib.rs:1691:73 | -| 1 | unsupported call to `_` | `std::ptr::copy_nonoverlapping` | fixtures/reject/src/lib.rs:1689:3 | -| 1 | unsupported call to `_` | `std::ptr::copy` | fixtures/reject/src/lib.rs:1688:3 | -| 1 | unsupported call to `_` | `std::ptr::mut_ptr::add` | fixtures/reject/src/lib.rs:1691:56 | | 1 | unsupported call to `_` | `std::std::Layout::new` | fixtures/reject/src/lib.rs:543:15 | | 1 | unsupported call to `_` | `std::std::alloc` | fixtures/reject/src/lib.rs:545:11 | | 1 | unsupported call to `_` | `std::std::dealloc` | fixtures/reject/src/lib.rs:548:3 | @@ -436,7 +453,7 @@ The type or module each refused type or path belongs to. ## Spans -
a raw pointer to `_` in `_`, which the crate may put another object in place of (49) +
a raw pointer to `_` in `_`, which the crate may put another object in place of (50) - fixtures/reject/src/lib.rs:586:10: a raw pointer to `(u32, u32)` in `Span2`, which the crate may put another object in place of - fixtures/reject/src/lib.rs:647:2: a raw pointer to `(u32, u32)` in `Window`, which the crate may put another object in place of @@ -483,44 +500,15 @@ The type or module each refused type or path belongs to. - fixtures/reject/src/lib.rs:1591:51: a raw pointer to `ByKeySet` in `(u16, ByKeySet)`, which the crate may put another object in place of - fixtures/reject/src/lib.rs:1599:55: a raw pointer to `ByKeyReplace` in `(u16, ByKeyReplace)`, which the crate may put another object in place of - fixtures/reject/src/lib.rs:1606:52: a raw pointer to `ByKeyTake` in `(u16, ByKeyTake)`, which the crate may put another object in place of -- fixtures/reject/src/lib.rs:2225:2: a raw pointer to `(u8, u8)` in `([Cloned; 2], (u8, u8))`, which the crate may put another object in place of -- fixtures/reject/src/lib.rs:2241:2: a raw pointer to `(u8, u8)` in `(Dropped, (u8, u8))`, which the crate may put another object in place of -- fixtures/reject/src/lib.rs:2259:13: a raw pointer to `(u8, u8)` in `([Greatest; 2], (u8, u8))`, which the crate may put another object in place of -- fixtures/reject/src/lib.rs:2290:2: a raw pointer to `(u8, u8)` in `([Touched; 2], (u8, u8))`, which the crate may put another object in place of +- fixtures/reject/src/lib.rs:2170:10: a raw pointer to `[u32; 3]` in `Lanes`, which the crate may put another object in place of +- fixtures/reject/src/lib.rs:2327:2: a raw pointer to `(u8, u8)` in `([Cloned; 2], (u8, u8))`, which the crate may put another object in place of +- fixtures/reject/src/lib.rs:2343:2: a raw pointer to `(u8, u8)` in `(Dropped, (u8, u8))`, which the crate may put another object in place of +- fixtures/reject/src/lib.rs:2361:13: a raw pointer to `(u8, u8)` in `([Greatest; 2], (u8, u8))`, which the crate may put another object in place of +- fixtures/reject/src/lib.rs:2392:2: a raw pointer to `(u8, u8)` in `([Touched; 2], (u8, u8))`, which the crate may put another object in place of
-
unsupported call to `_` (25) - -- fixtures/reject/src/lib.rs:160:8: unsupported call to `std::ops::Range::::contains::` -- fixtures/reject/src/lib.rs:286:10: unsupported call to `::hash::` -- fixtures/reject/src/lib.rs:321:2: unsupported call to `std::borrow::Cow::<'_, [u8]>::is_borrowed` -- fixtures/reject/src/lib.rs:347:2: unsupported call to ` as std::cmp::PartialEq>::eq` -- fixtures/reject/src/lib.rs:412:17: unsupported call to ` as std::iter::Iterator>::sum::` -- fixtures/reject/src/lib.rs:412:38: unsupported call to ` as std::iter::Iterator>::sum::` -- fixtures/reject/src/lib.rs:417:4: unsupported call to `core::str::::parse::` -- fixtures/reject/src/lib.rs:516:2: unsupported call to `::eq` -- fixtures/reject/src/lib.rs:516:17: unsupported call to `std::any::TypeId::of::` -- fixtures/reject/src/lib.rs:543:15: unsupported call to `std::alloc::Layout::new::` -- fixtures/reject/src/lib.rs:545:11: unsupported call to `std::alloc::alloc` -- fixtures/reject/src/lib.rs:548:3: unsupported call to `std::alloc::dealloc` -- fixtures/reject/src/lib.rs:744:15: unsupported call to ` as std::iter::Iterator>::is_sorted` -- fixtures/reject/src/lib.rs:962:11: unsupported call to `std::ops::RangeTo::::contains::` -- fixtures/reject/src/lib.rs:962:32: unsupported call to ` as std::ops::RangeBounds>::contains::` -- fixtures/reject/src/lib.rs:967:9: unsupported call to `std::ops::Range::<&mut u8>::is_empty` -- fixtures/reject/src/lib.rs:1255:4: unsupported call to `core::num::::is_ascii_octdigit` -- fixtures/reject/src/lib.rs:1614:13: unsupported call to `core::str::::as_bytes_mut` -- fixtures/reject/src/lib.rs:1688:3: unsupported call to `std::ptr::copy::<[u8; 4]>` -- fixtures/reject/src/lib.rs:1689:3: unsupported call to `std::ptr::copy_nonoverlapping::<[u8; 4]>` -- fixtures/reject/src/lib.rs:1690:14: unsupported call to `std::ptr::addr_eq::<[u8; 4], [u8; 4]>` -- fixtures/reject/src/lib.rs:1691:56: unsupported call to `std::ptr::mut_ptr::::add` -- fixtures/reject/src/lib.rs:1691:73: unsupported call to `std::ptr::const_ptr::::offset` -- fixtures/reject/src/lib.rs:1695:58: unsupported call to `std::ptr::const_ptr::::len` -- fixtures/reject/src/lib.rs:2152:2: unsupported call to `std::mem::size_of_val::` - -
- -
a raw pointer to `_` in a variable assigned again while it may point there (22) +
a raw pointer to `_` in a variable assigned again while it may point there (23) - fixtures/reject/src/lib.rs:594:10: a raw pointer to `(u32, u32)` in a variable assigned again while it may point there - fixtures/reject/src/lib.rs:602:10: a raw pointer to `(u32, u32)` in a variable assigned again while it may point there @@ -541,9 +529,35 @@ The type or module each refused type or path belongs to. - fixtures/reject/src/lib.rs:1675:10: a raw pointer to `(u32, u32)` in a variable assigned again while it may point there - fixtures/reject/src/lib.rs:1711:26: a raw pointer to `[u8; 4]` in a variable assigned again while it may point there - fixtures/reject/src/lib.rs:1712:10: a raw pointer to `[u8; 4]` in a variable assigned again while it may point there -- fixtures/reject/src/lib.rs:2158:10: a raw pointer to `(u32, u32)` in a variable assigned again while it may point there -- fixtures/reject/src/lib.rs:2166:10: a raw pointer to `(u32, u32)` in a variable assigned again while it may point there -- fixtures/reject/src/lib.rs:2174:10: a raw pointer to `(u32, u32)` in a variable assigned again while it may point there +- fixtures/reject/src/lib.rs:2168:10: a raw pointer to `[u32; 3]` in a variable assigned again while it may point there +- fixtures/reject/src/lib.rs:2260:10: a raw pointer to `(u32, u32)` in a variable assigned again while it may point there +- fixtures/reject/src/lib.rs:2268:10: a raw pointer to `(u32, u32)` in a variable assigned again while it may point there +- fixtures/reject/src/lib.rs:2276:10: a raw pointer to `(u32, u32)` in a variable assigned again while it may point there + +
+ +
unsupported call to `_` (20) + +- fixtures/reject/src/lib.rs:160:8: unsupported call to `std::ops::Range::::contains::` +- fixtures/reject/src/lib.rs:286:10: unsupported call to `::hash::` +- fixtures/reject/src/lib.rs:321:2: unsupported call to `std::borrow::Cow::<'_, [u8]>::is_borrowed` +- fixtures/reject/src/lib.rs:347:2: unsupported call to ` as std::cmp::PartialEq>::eq` +- fixtures/reject/src/lib.rs:412:17: unsupported call to ` as std::iter::Iterator>::sum::` +- fixtures/reject/src/lib.rs:412:38: unsupported call to ` as std::iter::Iterator>::sum::` +- fixtures/reject/src/lib.rs:417:4: unsupported call to `core::str::::parse::` +- fixtures/reject/src/lib.rs:516:2: unsupported call to `::eq` +- fixtures/reject/src/lib.rs:516:17: unsupported call to `std::any::TypeId::of::` +- fixtures/reject/src/lib.rs:543:15: unsupported call to `std::alloc::Layout::new::` +- fixtures/reject/src/lib.rs:545:11: unsupported call to `std::alloc::alloc` +- fixtures/reject/src/lib.rs:548:3: unsupported call to `std::alloc::dealloc` +- fixtures/reject/src/lib.rs:744:15: unsupported call to ` as std::iter::Iterator>::is_sorted` +- fixtures/reject/src/lib.rs:962:11: unsupported call to `std::ops::RangeTo::::contains::` +- fixtures/reject/src/lib.rs:962:32: unsupported call to ` as std::ops::RangeBounds>::contains::` +- fixtures/reject/src/lib.rs:967:9: unsupported call to `std::ops::Range::<&mut u8>::is_empty` +- fixtures/reject/src/lib.rs:1255:4: unsupported call to `core::num::::is_ascii_octdigit` +- fixtures/reject/src/lib.rs:1614:13: unsupported call to `core::str::::as_bytes_mut` +- fixtures/reject/src/lib.rs:1690:14: unsupported call to `std::ptr::addr_eq::<[u8; 4], [u8; 4]>` +- fixtures/reject/src/lib.rs:2254:2: unsupported call to `std::mem::size_of_val::`
@@ -621,7 +635,7 @@ The type or module each refused type or path belongs to. - fixtures/reject/src/lib.rs:83:2: an assignment through a reference to `std::sync::MutexGuard<'_, u8>` - fixtures/reject/src/lib.rs:107:2: an assignment through a reference to `std::pin::Pin>>` - fixtures/reject/src/lib.rs:214:2: an assignment through a reference to `std::pin::Pin<&mut Noisy>` -- fixtures/reject/src/lib.rs:2189:2: an assignment through a reference to `std::sync::LazyLock` +- fixtures/reject/src/lib.rs:2291:2: an assignment through a reference to `std::sync::LazyLock`
@@ -629,7 +643,15 @@ The type or module each refused type or path belongs to. - fixtures/reject/src/lib.rs:21:4: `slice::fill` of `Reused`, whose `clone_from` is its own, which is not supported yet - fixtures/reject/src/lib.rs:271:4: `slice::clone_from_slice` of `Reused`, whose `clone_from` is its own, which is not supported yet -- fixtures/reject/src/lib.rs:2147:4: `Clone::clone_from` of `std::vec::Vec`, whose `clone_from` is its own, which is not supported yet +- fixtures/reject/src/lib.rs:2249:4: `Clone::clone_from` of `std::vec::Vec`, whose `clone_from` is its own, which is not supported yet + +
+ +
a copy between pointers to `_`, which JS keeps as an object that knows no array it is in (3) + +- fixtures/reject/src/lib.rs:1688:3: a copy between pointers to `[u8; 4]`, which JS keeps as an object that knows no array it is in +- fixtures/reject/src/lib.rs:1689:3: a copy between pointers to `[u8; 4]`, which JS keeps as an object that knows no array it is in +- fixtures/reject/src/lib.rs:2140:3: a copy between pointers to `(u8, u8)`, which JS keeps as an object that knows no array it is in
@@ -643,9 +665,17 @@ The type or module each refused type or path belongs to.
a slice or a cell of a mutable reference to `_` that names no place, which a whole write would replace only there (3) -- fixtures/reject/src/lib.rs:2142:2: a slice or a cell of a mutable reference to `Reused` that names no place, which a whole write would replace only there -- fixtures/reject/src/lib.rs:2184:3: a slice or a cell of a mutable reference to `std::cell::LazyCell` that names no place, which a whole write would replace only there -- fixtures/reject/src/lib.rs:2195:2: a slice or a cell of a mutable reference to `std::thread::LocalKey` that names no place, which a whole write would replace only there +- fixtures/reject/src/lib.rs:2244:2: a slice or a cell of a mutable reference to `Reused` that names no place, which a whole write would replace only there +- fixtures/reject/src/lib.rs:2286:3: a slice or a cell of a mutable reference to `std::cell::LazyCell` that names no place, which a whole write would replace only there +- fixtures/reject/src/lib.rs:2297:2: a slice or a cell of a mutable reference to `std::thread::LocalKey` that names no place, which a whole write would replace only there + +
+ +
an offset of a pointer to `_`, which JS keeps as an object that knows no array it is in (3) + +- fixtures/reject/src/lib.rs:1691:56: an offset of a pointer to `[u8; 4]`, which JS keeps as an object that knows no array it is in +- fixtures/reject/src/lib.rs:1691:73: an offset of a pointer to `[u8; 4]`, which JS keeps as an object that knows no array it is in +- fixtures/reject/src/lib.rs:2139:13: an offset of a pointer to `(u8, u8)`, which JS keeps as an object that knows no array it is in
@@ -693,10 +723,24 @@ The type or module each refused type or path belongs to. +
`offset_from` between a pointer into a value in no array and one into a value in no array, which JS keeps in different arrays (2) + +- fixtures/reject/src/lib.rs:2203:5: `offset_from` between a pointer into a value in no array and one into a value in no array, which JS keeps in different arrays +- fixtures/reject/src/lib.rs:2204:26: `offset_from` between a pointer into a value in no array and one into a value in no array, which JS keeps in different arrays + +
+ +
`offset_from` between a pointer into an array of this body's own and one into an array of this body's own, which JS keeps in different arrays (2) + +- fixtures/reject/src/lib.rs:2205:24: `offset_from` between a pointer into an array of this body's own and one into an array of this body's own, which JS keeps in different arrays +- fixtures/reject/src/lib.rs:2206:23: `offset_from` between a pointer into an array of this body's own and one into an array of this body's own, which JS keeps in different arrays + +
+
a cast from a pointer to `_` to one to `_`, which reads the bytes of one type as another (2) - fixtures/reject/src/lib.rs:533:27: a cast from a pointer to `u32` to one to `u8`, which reads the bytes of one type as another -- fixtures/reject/src/lib.rs:1691:17: a cast from a pointer to `[u8; 4]` to one to `u8`, which reads the bytes of one type as another +- fixtures/reject/src/lib.rs:2155:13: a cast from a pointer to `u32` to one to `[u32; 2]`, which reads the bytes of one type as another
@@ -707,6 +751,20 @@ The type or module each refused type or path belongs to. +
a pointer reaching past a shared slice JS copies, which JS keeps apart from what holds it in Rust (2) + +- fixtures/reject/src/lib.rs:2228:18: a pointer reaching past a shared slice JS copies, which JS keeps apart from what holds it in Rust +- fixtures/reject/src/lib.rs:2231:21: a pointer reaching past a shared slice JS copies, which JS keeps apart from what holds it in Rust + +
+ +
a raw or mutable slice of a byte in no array of bytes (2) + +- fixtures/reject/src/lib.rs:2214:12: a raw or mutable slice of a byte in no array of bytes +- fixtures/reject/src/lib.rs:2215:19: a raw or mutable slice of a byte in no array of bytes + +
+
a raw pointer to a place of `()`, which has no address of its own (2) - fixtures/reject/src/lib.rs:706:15: a raw pointer to a place of `()`, which has no address of its own @@ -839,33 +897,45 @@ The type or module each refused type or path belongs to.
-
a `collect` in place of `_`, a zip std reads by index, holding values that drop, which is not supported yet (1) +
`offset_from` between a pointer into a shared slice JS copies and one into a value in no array, which JS keeps in different arrays (1) -- fixtures/reject/src/lib.rs:209:40: a `collect` in place of `std::iter::Map, {closure@fixtures/reject/src/lib.rs:208:32: 208:40}>, std::ops::Range>, {closure@fixtures/reject/src/lib.rs:209:24: 209:32}>`, a zip std reads by index, holding values that drop, which is not supported yet +- fixtures/reject/src/lib.rs:2208:21: `offset_from` between a pointer into a shared slice JS copies and one into a value in no array, which JS keeps in different arrays
-
a `skip` of `_`, a zip std reads by index, of items that drop, which is not supported yet (1) +
`offset_from` between a pointer into a shared slice JS copies and one into an array of this body's own, which JS keeps in different arrays (1) -- fixtures/reject/src/lib.rs:202:8: a `skip` of `std::iter::Zip, {closure@fixtures/reject/src/lib.rs:201:27: 201:31}>, std::iter::Map, {closure@fixtures/reject/src/lib.rs:201:59: 201:63}>>`, a zip std reads by index, of items that drop, which is not supported yet +- fixtures/reject/src/lib.rs:2207:20: `offset_from` between a pointer into a shared slice JS copies and one into an array of this body's own, which JS keeps in different arrays
-
a cast from `_` to `_` (1) +
`offset_from` of pointers to `()`, which has no bytes (1) -- fixtures/reject/src/lib.rs:68:2: a cast from `u64` to `f16` +- fixtures/reject/src/lib.rs:2150:13: `offset_from` of pointers to `()`, which has no bytes
-
a cast from `_` to `_`, which reads the bytes of one type as another (1) +
`offset_from` of pointers to `_`, which JS keeps as an object that knows no array it is in (1) -- fixtures/reject/src/lib.rs:1691:33: a cast from `*const [u8; 4]` to `*const u8`, which reads the bytes of one type as another +- fixtures/reject/src/lib.rs:2143:36: `offset_from` of pointers to `(u8, u8)`, which JS keeps as an object that knows no array it is in
-
a coercion of `_` to `_`, a pointer to a slice (1) +
a `collect` in place of `_`, a zip std reads by index, holding values that drop, which is not supported yet (1) -- fixtures/reject/src/lib.rs:1692:28: a coercion of `*const [u8; 4]` to `*const [u8]`, a pointer to a slice +- fixtures/reject/src/lib.rs:209:40: a `collect` in place of `std::iter::Map, {closure@fixtures/reject/src/lib.rs:208:32: 208:40}>, std::ops::Range>, {closure@fixtures/reject/src/lib.rs:209:24: 209:32}>`, a zip std reads by index, holding values that drop, which is not supported yet + +
+ +
a `skip` of `_`, a zip std reads by index, of items that drop, which is not supported yet (1) + +- fixtures/reject/src/lib.rs:202:8: a `skip` of `std::iter::Zip, {closure@fixtures/reject/src/lib.rs:201:27: 201:31}>, std::iter::Map, {closure@fixtures/reject/src/lib.rs:201:59: 201:63}>>`, a zip std reads by index, of items that drop, which is not supported yet + +
+ +
a cast from `_` to `_` (1) + +- fixtures/reject/src/lib.rs:68:2: a cast from `u64` to `f16`
@@ -881,6 +951,12 @@ The type or module each refused type or path belongs to.
+
a copy reaching past a shared slice JS copies, which JS keeps apart from what holds it in Rust (1) + +- fixtures/reject/src/lib.rs:2226:3: a copy reaching past a shared slice JS copies, which JS keeps apart from what holds it in Rust + +
+
a dangling pointer to `_`, a value of no bytes made of more than structs and tuples (1) - fixtures/reject/src/lib.rs:715:11: a dangling pointer to `Only`, a value of no bytes made of more than structs and tuples @@ -911,6 +987,24 @@ The type or module each refused type or path belongs to.
+
a pointer into `_`, which JS keeps as an object that knows no array it is in (1) + +- fixtures/reject/src/lib.rs:2137:12: a pointer into `(u8, u8)`, which JS keeps as an object that knows no array it is in + +
+ +
a pointer into an array of `_`, which JS keeps as an object that knows no array it is in (1) + +- fixtures/reject/src/lib.rs:2142:30: a pointer into an array of `(u8, u8)`, which JS keeps as an object that knows no array it is in + +
+ +
a pointer reaching past a value in no array, which JS keeps apart from what holds it in Rust (1) + +- fixtures/reject/src/lib.rs:2230:27: a pointer reaching past a value in no array, which JS keeps apart from what holds it in Rust + +
+
a pointer's address, which JS has no counterpart of (1) - fixtures/reject/src/lib.rs:538:17: a pointer's address, which JS has no counterpart of @@ -941,9 +1035,27 @@ The type or module each refused type or path belongs to.
+
a slice from a pointer to `_`, which JS keeps as an object that knows no array it is in (1) + +- fixtures/reject/src/lib.rs:2141:11: a slice from a pointer to `(u8, u8)`, which JS keeps as an object that knows no array it is in + +
+ +
a slice of `()`, which has no bytes, from a pointer (1) + +- fixtures/reject/src/lib.rs:2150:39: a slice of `()`, which has no bytes, from a pointer + +
+
a slice of one byte's place, which a byte slice can view only in an array of bytes (1) -- fixtures/reject/src/lib.rs:2136:2: a slice of one byte's place, which a byte slice can view only in an array of bytes +- fixtures/reject/src/lib.rs:2238:2: a slice of one byte's place, which a byte slice can view only in an array of bytes + +
+ +
a slice reaching past a value in no array, which JS keeps apart from what holds it in Rust (1) + +- fixtures/reject/src/lib.rs:2229:6: a slice reaching past a value in no array, which JS keeps apart from what holds it in Rust
diff --git a/ts/fixtures/reject/expected.txt b/ts/fixtures/reject/expected.txt index 48ce74a1f..451b30565 100644 --- a/ts/fixtures/reject/expected.txt +++ b/ts/fixtures/reject/expected.txt @@ -175,17 +175,13 @@ a raw pointer to `ByKeyTake` in `(u16, ByKeyTake)`, which the crate may put anot unsupported call to `core::str::::as_bytes_mut` at fixtures/reject/src/lib.rs:1614:13 `core::str::::get_unchecked_mut` with a mutable reference to a value JS copies, which its mapping would copy at fixtures/reject/src/lib.rs:1615:13 `std::str::from_utf8_mut` with a mutable reference to a value JS copies, which its mapping would copy at fixtures/reject/src/lib.rs:1620:20 -unsupported call to `std::ptr::copy::<[u8; 4]>` at fixtures/reject/src/lib.rs:1688:3 -unsupported call to `std::ptr::copy_nonoverlapping::<[u8; 4]>` at fixtures/reject/src/lib.rs:1689:3 +a copy between pointers to `[u8; 4]`, which JS keeps as an object that knows no array it is in at fixtures/reject/src/lib.rs:1688:3 +a copy between pointers to `[u8; 4]`, which JS keeps as an object that knows no array it is in at fixtures/reject/src/lib.rs:1689:3 unsupported call to `std::ptr::addr_eq::<[u8; 4], [u8; 4]>` at fixtures/reject/src/lib.rs:1690:14 comparing `*mut [u8; 4]` at fixtures/reject/src/lib.rs:1690:41 -a cast from a pointer to `[u8; 4]` to one to `u8`, which reads the bytes of one type as another at fixtures/reject/src/lib.rs:1691:17 -a cast from `*const [u8; 4]` to `*const u8`, which reads the bytes of one type as another at fixtures/reject/src/lib.rs:1691:33 -unsupported call to `std::ptr::mut_ptr::::add` at fixtures/reject/src/lib.rs:1691:56 -unsupported call to `std::ptr::const_ptr::::offset` at fixtures/reject/src/lib.rs:1691:73 -a coercion of `*const [u8; 4]` to `*const [u8]`, a pointer to a slice at fixtures/reject/src/lib.rs:1692:28 +an offset of a pointer to `[u8; 4]`, which JS keeps as an object that knows no array it is in at fixtures/reject/src/lib.rs:1691:56 +an offset of a pointer to `[u8; 4]`, which JS keeps as an object that knows no array it is in at fixtures/reject/src/lib.rs:1691:73 hashing `*const [u8; 4]` by its address, which JS has no counterpart of at fixtures/reject/src/lib.rs:1694:5 -unsupported call to `std::ptr::const_ptr::::len` at fixtures/reject/src/lib.rs:1695:58 a raw pointer to `[u8; 3]` in an element, a payload, or a union field, which is not supported yet at fixtures/reject/src/lib.rs:1704:15 a raw pointer to `[u8; 3]` in an element, a payload, or a union field, which is not supported yet at fixtures/reject/src/lib.rs:1704:18 a raw pointer to `[u8; 2]` in an element, a payload, or a union field, which is not supported yet at fixtures/reject/src/lib.rs:1704:37 @@ -198,16 +194,40 @@ const generic types are not supported: `Block` at fixtures/reject/src/lib.rs:207 `clone_from` of `std::collections::LinkedList`, whose items' `clone_from` is their own, which is not supported yet at fixtures/reject/src/lib.rs:2096:4 `std::default::Default::default` with a mutable reference to a value JS copies, which its mapping would copy at fixtures/reject/src/lib.rs:2097:2 a mapped guard of `{closure@fixtures/reject/src/lib.rs:2129:38: 2129:50}`, which JS can't write in place at fixtures/reject/src/lib.rs:2130:10 -a slice of one byte's place, which a byte slice can view only in an array of bytes at fixtures/reject/src/lib.rs:2136:2 -a slice or a cell of a mutable reference to `Reused` that names no place, which a whole write would replace only there at fixtures/reject/src/lib.rs:2142:2 -`Clone::clone_from` of `std::vec::Vec`, whose `clone_from` is its own, which is not supported yet at fixtures/reject/src/lib.rs:2147:4 -unsupported call to `std::mem::size_of_val::` at fixtures/reject/src/lib.rs:2152:2 -a raw pointer to `(u32, u32)` in a variable assigned again while it may point there at fixtures/reject/src/lib.rs:2158:10 -a raw pointer to `(u32, u32)` in a variable assigned again while it may point there at fixtures/reject/src/lib.rs:2166:10 -a raw pointer to `(u32, u32)` in a variable assigned again while it may point there at fixtures/reject/src/lib.rs:2174:10 -a slice or a cell of a mutable reference to `std::cell::LazyCell` that names no place, which a whole write would replace only there at fixtures/reject/src/lib.rs:2184:3 -an assignment through a reference to `std::sync::LazyLock` at fixtures/reject/src/lib.rs:2189:2 -a slice or a cell of a mutable reference to `std::thread::LocalKey` that names no place, which a whole write would replace only there at fixtures/reject/src/lib.rs:2195:2 +a pointer into `(u8, u8)`, which JS keeps as an object that knows no array it is in at fixtures/reject/src/lib.rs:2137:12 +an offset of a pointer to `(u8, u8)`, which JS keeps as an object that knows no array it is in at fixtures/reject/src/lib.rs:2139:13 +a copy between pointers to `(u8, u8)`, which JS keeps as an object that knows no array it is in at fixtures/reject/src/lib.rs:2140:3 +a slice from a pointer to `(u8, u8)`, which JS keeps as an object that knows no array it is in at fixtures/reject/src/lib.rs:2141:11 +a pointer into an array of `(u8, u8)`, which JS keeps as an object that knows no array it is in at fixtures/reject/src/lib.rs:2142:30 +`offset_from` of pointers to `(u8, u8)`, which JS keeps as an object that knows no array it is in at fixtures/reject/src/lib.rs:2143:36 +`offset_from` of pointers to `()`, which has no bytes at fixtures/reject/src/lib.rs:2150:13 +a slice of `()`, which has no bytes, from a pointer at fixtures/reject/src/lib.rs:2150:39 +a cast from a pointer to `u32` to one to `[u32; 2]`, which reads the bytes of one type as another at fixtures/reject/src/lib.rs:2155:13 +a raw pointer to `[u32; 3]` in a variable assigned again while it may point there at fixtures/reject/src/lib.rs:2168:10 +a raw pointer to `[u32; 3]` in `Lanes`, which the crate may put another object in place of at fixtures/reject/src/lib.rs:2170:10 +`offset_from` between a pointer into a value in no array and one into a value in no array, which JS keeps in different arrays at fixtures/reject/src/lib.rs:2203:5 +`offset_from` between a pointer into a value in no array and one into a value in no array, which JS keeps in different arrays at fixtures/reject/src/lib.rs:2204:26 +`offset_from` between a pointer into an array of this body's own and one into an array of this body's own, which JS keeps in different arrays at fixtures/reject/src/lib.rs:2205:24 +`offset_from` between a pointer into an array of this body's own and one into an array of this body's own, which JS keeps in different arrays at fixtures/reject/src/lib.rs:2206:23 +`offset_from` between a pointer into a shared slice JS copies and one into an array of this body's own, which JS keeps in different arrays at fixtures/reject/src/lib.rs:2207:20 +`offset_from` between a pointer into a shared slice JS copies and one into a value in no array, which JS keeps in different arrays at fixtures/reject/src/lib.rs:2208:21 +a raw or mutable slice of a byte in no array of bytes at fixtures/reject/src/lib.rs:2214:12 +a raw or mutable slice of a byte in no array of bytes at fixtures/reject/src/lib.rs:2215:19 +a copy reaching past a shared slice JS copies, which JS keeps apart from what holds it in Rust at fixtures/reject/src/lib.rs:2226:3 +a pointer reaching past a shared slice JS copies, which JS keeps apart from what holds it in Rust at fixtures/reject/src/lib.rs:2228:18 +a slice reaching past a value in no array, which JS keeps apart from what holds it in Rust at fixtures/reject/src/lib.rs:2229:6 +a pointer reaching past a value in no array, which JS keeps apart from what holds it in Rust at fixtures/reject/src/lib.rs:2230:27 +a pointer reaching past a shared slice JS copies, which JS keeps apart from what holds it in Rust at fixtures/reject/src/lib.rs:2231:21 +a slice of one byte's place, which a byte slice can view only in an array of bytes at fixtures/reject/src/lib.rs:2238:2 +a slice or a cell of a mutable reference to `Reused` that names no place, which a whole write would replace only there at fixtures/reject/src/lib.rs:2244:2 +`Clone::clone_from` of `std::vec::Vec`, whose `clone_from` is its own, which is not supported yet at fixtures/reject/src/lib.rs:2249:4 +unsupported call to `std::mem::size_of_val::` at fixtures/reject/src/lib.rs:2254:2 +a raw pointer to `(u32, u32)` in a variable assigned again while it may point there at fixtures/reject/src/lib.rs:2260:10 +a raw pointer to `(u32, u32)` in a variable assigned again while it may point there at fixtures/reject/src/lib.rs:2268:10 +a raw pointer to `(u32, u32)` in a variable assigned again while it may point there at fixtures/reject/src/lib.rs:2276:10 +a slice or a cell of a mutable reference to `std::cell::LazyCell` that names no place, which a whole write would replace only there at fixtures/reject/src/lib.rs:2286:3 +an assignment through a reference to `std::sync::LazyLock` at fixtures/reject/src/lib.rs:2291:2 +a slice or a cell of a mutable reference to `std::thread::LocalKey` that names no place, which a whole write would replace only there at fixtures/reject/src/lib.rs:2297:2 overriding `Iterator::fold`, which std's adaptors call in place of `next` or beside it at fixtures/reject/src/lib.rs:125:2 an iterator of the crate implementing `DoubleEndedIterator`, which is not supported yet at fixtures/reject/src/lib.rs:145:1 overriding `Iterator::size_hint`, which std's adaptors call in place of `next` or beside it at fixtures/reject/src/lib.rs:173:2 @@ -223,7 +243,7 @@ a raw pointer to `std::cell::UnsafeCell` in `InField`, which the crate may a raw pointer to `std::cell::UnsafeCell` in `InGeneric`, which the crate may put another object in place of at fixtures/reject/src/lib.rs:1300:9 a raw pointer to `std::cell::UnsafeCell` in `InWritten`, which the crate may put another object in place of at fixtures/reject/src/lib.rs:1304:9 a reference to `std::option::Option>`, through which a raw pointer may be made, in a variable assigned again while that pointer may point there at fixtures/reject/src/lib.rs:2086:13 -a raw pointer to `(u8, u8)` in `([Cloned; 2], (u8, u8))`, which the crate may put another object in place of at fixtures/reject/src/lib.rs:2203:23 -a raw pointer to `(u8, u8)` in `(Dropped, (u8, u8))`, which the crate may put another object in place of at fixtures/reject/src/lib.rs:2203:23 -a raw pointer to `(u8, u8)` in `([Greatest; 2], (u8, u8))`, which the crate may put another object in place of at fixtures/reject/src/lib.rs:2259:13 -a raw pointer to `(u8, u8)` in `([Touched; 2], (u8, u8))`, which the crate may put another object in place of at fixtures/reject/src/lib.rs:2203:23 +a raw pointer to `(u8, u8)` in `([Cloned; 2], (u8, u8))`, which the crate may put another object in place of at fixtures/reject/src/lib.rs:2305:23 +a raw pointer to `(u8, u8)` in `(Dropped, (u8, u8))`, which the crate may put another object in place of at fixtures/reject/src/lib.rs:2305:23 +a raw pointer to `(u8, u8)` in `([Greatest; 2], (u8, u8))`, which the crate may put another object in place of at fixtures/reject/src/lib.rs:2361:13 +a raw pointer to `(u8, u8)` in `([Touched; 2], (u8, u8))`, which the crate may put another object in place of at fixtures/reject/src/lib.rs:2305:23 diff --git a/ts/fixtures/reject/src/lib.rs b/ts/fixtures/reject/src/lib.rs index bbd87e9cf..81f118fc2 100644 --- a/ts/fixtures/reject/src/lib.rs +++ b/ts/fixtures/reject/src/lib.rs @@ -1678,8 +1678,8 @@ pub fn swapped_in_loop(n: u32) -> u32 { } /// Pointer operations on arrays of bytes that read the address or other bytes than the -/// array's: comparing addresses only, copying, offsetting, casting to another pointee, -/// hashing, and ordering. +/// array's: comparing addresses only, copying, offsetting, hashing, and ordering, beside +/// pointers to their first bytes. pub fn byte_pointer_ops(mut a: [u8; 4], b: [u8; 4]) -> u64 { use std::hash::{Hash, Hasher}; let p: *mut [u8; 4] = &mut a; @@ -2131,6 +2131,108 @@ pub fn mapped_closure(add: u8) -> u8 { f(1) } +/// Pointers into arrays whose elements JS keeps as objects: a pointer to one is the +/// object, which knows no array it is in. +pub fn object_elements(v: &mut Vec<(u8, u8)>, a: [(u8, u8); 2]) -> u8 { + let p = v.as_mut_ptr(); + unsafe { + let q = p.add(1); + std::ptr::copy(p, q, 1); + let s = std::slice::from_raw_parts(p, 2); + let first = (&raw const a).cast::<(u8, u8)>(); + (*q).0 + s[1].1 + (*first).0 + q.offset_from(p) as u8 + } +} + +/// Pointers to values of no bytes, which std's `offset_from` panics on, and a slice of +/// them from one, which has no array to be a view of. +pub fn unit_elements(p: *const ()) -> usize { + unsafe { p.offset_from(p) as usize + std::slice::from_raw_parts(p, 3).len() } +} + +/// A pointer to an element cast to one to an array of elements from it. +pub fn element_to_array(v: &[u32]) -> u32 { + unsafe { (*(v.as_ptr() as *const [u32; 2]))[1] } +} + +/// A struct holding an array. +pub struct Lanes { + /// The array a pointer points into. + pub lanes: [u32; 3], +} + +/// Pointers to elements of arrays that a later assignment gives another JS array, which a +/// pointer into the first would not see: a variable's, and a struct's field. +pub fn elements_replaced(n: u32, l: &mut Lanes) -> u32 { + let mut a = [n; 3]; + let p = &raw mut a[1]; + a = [0; 3]; + let q = &raw mut l.lanes[2]; + l.lanes = [1; 3]; + unsafe { *p + *q + a[0] } +} + +#[repr(C)] +pub struct Halves { + pub low: u32, + pub high: u32, +} + +/// Two arrays in one allocation, which JS keeps apart. +#[repr(C)] +pub struct Rows { + /// The first. + pub top: [u32; 2], + /// The second. + pub bottom: [u32; 2], +} + +/// `offset_from` of pointers the body shows JS keeps in different arrays: two made apart +/// of one variable, two of a struct's fields, into two of its array fields, into two rows +/// of a nested array, one into a shared subslice, a copy, against one into what it was +/// taken of, and one into a shared slice of a byte, also a copy, against the byte's. +pub fn pointers_apart(n: u32, h: Halves, r: Rows, byte: u8) -> isize { + let a = &raw const n; + let b = &raw const n; + let grid = [[n; 2]; 2]; + let lanes = [n; 4]; + let part = &lanes[1..3]; + let at = &raw const byte; + unsafe { + let bytes = std::slice::from_raw_parts(at, 1); + a.offset_from(b) + + (&raw const h.high).offset_from(&raw const h.low) + + r.bottom.as_ptr().offset_from(r.top.as_ptr()) + + grid[1].as_ptr().offset_from(grid[0].as_ptr()) + + part.as_ptr().offset_from(lanes.as_ptr()) + + bytes.as_ptr().offset_from(at) + } +} + +/// A raw and a mutable slice of a byte in no array, which no `Uint8Array` can share. +pub fn lone_byte_slices(mut b: u8) -> u8 { + let raw = std::ptr::slice_from_raw_parts_mut(&raw mut b, 1); + let s = unsafe { std::slice::from_raw_parts_mut(&raw mut b, 1) }; + s[0] = 1; + unsafe { (*raw)[0] } +} + +/// Pointers, slices, and copies the body shows reach out of an array of one or a copied +/// subslice, where Rust's reach the rest of what holds them. +pub fn reached_out(h: Halves, lanes: [u32; 4]) -> u32 { + let part = &lanes[1..3]; + let mut to = [0u32; 3]; + unsafe { + std::ptr::copy_nonoverlapping(part.as_ptr(), to.as_mut_ptr(), 3); + let tail = lanes.get_unchecked(2..); + *part.as_ptr().sub(1) + + std::slice::from_raw_parts(&raw const h.low, 2)[1] + + *(&raw const h.high).sub(1) + + *tail.as_ptr().offset(-2) + + to[0] + } +} + /// A slice of one `u8`'s place, which no `rt.ByteSlice` views. pub fn byte_slice_of_place(b: &mut u8) { std::slice::from_mut(b)[0] = 1; diff --git a/ts/fixtures/reject/ts.toml b/ts/fixtures/reject/ts.toml index 02e3e4438..415f4ec3f 100644 --- a/ts/fixtures/reject/ts.toml +++ b/ts/fixtures/reject/ts.toml @@ -164,6 +164,13 @@ roots = [ "block_cloned", "lists", "mapped_closure", + "object_elements", + "unit_elements", + "element_to_array", + "elements_replaced", + "pointers_apart", + "lone_byte_slices", + "reached_out", "byte_slice_of_place", "slice_of_object", "vec_clone_from", diff --git a/ts/runtime/rt.ts b/ts/runtime/rt.ts index 6a597149d..fe9ad5f75 100644 --- a/ts/runtime/rt.ts +++ b/ts/runtime/rt.ts @@ -157,6 +157,261 @@ export type VecOf = T[] | ByteVec; /** A generic type's `Cow<[T]>`: the `Cow`, or the `ByteCow` of a `Cow<[u8]>`. */ export type CowOf = Cow | ByteCow; +/** + * The pointer to element `index` of `s`, a slice, a `[T; N]`, or a `Vec`'s storage. A null + * or dangling pointer to an array is one to its first element, at the same address. + */ +export function sliceAsPtr(s: { [i: number]: T }, index = 0): Place { + if (s === NULL) return NULL; + if (s === DANGLING) return DANGLING; + const view = (s as { [VIEW]?: View })[VIEW]; + const [array, at] = view ? [view.s, view.start + index] : [s, index]; + // Null's is `null`, which `??` would pass over. + const from = EMPTY.get(array) as Place | undefined; + return from !== undefined ? from : new AtIndex(array, at); +} + +/** The pointer to byte `index` of `s`, or `s` if it is null or dangling, as `sliceAsPtr`'s. */ +export function byteSliceAsPtr(s: ByteSlice, index = 0): Place { + if (s === NULL) return NULL; + if (s === DANGLING) return DANGLING; + const from = EMPTY.get(s.data) as Place | undefined; + if (from !== undefined) return from; + return new AtIndex(s.data, s.start + index); +} + +/** The pointer to the first byte of `v`'s storage. */ +export function byteVecAsPtr(v: ByteVec): Place { + return new AtIndex(v.data, 0); +} + +/** A pointer wrapping arithmetic moved off null or a dangling one: an address no memory is at. */ +class Address implements Place { + readonly addr: number; + + constructor(addr: number) { + this.addr = addr; + } + + get v(): never { + return nowhere(); + } + + set v(_: never) { + nowhere(); + } +} + +function nowhere(): never { + throw new Error("rust2-ts: a read or write through a pointer to no memory"); +} + +/** The array of one element that a pointer to a value in no array points into, by its place. */ +const LONE = /* @__PURE__ */ new WeakMap, unknown[]>(); + +/** The traps of an array of one element, the value at `p`. */ +class Lone implements ProxyHandler { + readonly p: Place; + + constructor(p: Place) { + this.p = p; + } + + get(target: T[], key: string | symbol): unknown { + return key === "0" ? this.p.v : Reflect.get(target, key); + } + + set(target: T[], key: string | symbol, value: T): boolean { + if (key !== "0") return Reflect.set(target, key, value); + this.p.v = value; + return true; + } + + has(target: T[], key: string | symbol): boolean { + return key === "0" || Reflect.has(target, key); + } + + getOwnPropertyDescriptor(target: T[], key: string | symbol): PropertyDescriptor | undefined { + if (key !== "0") return Reflect.getOwnPropertyDescriptor(target, key); + return { value: this.p.v, writable: true, enumerable: true, configurable: true }; + } +} + +/** + * Where `p` points, as an element of an array: its own, or for a pointer to a value in no + * array, element 0 of an array of one that reads and writes it, as Rust treats one. Null + * and dangling pointers point into an array of none, so any access through them fails. + */ +function elemOf(p: Place): AtIndex { + if (p instanceof AtIndex) { + // A pointer into a mutable part of an array points into the array. + const view = (p.array as { [VIEW]?: View })[VIEW]; + return view ? new AtIndex(view.s, view.start + p.index) : p; + } + if (p === NULL || p === DANGLING) return new AtIndex([] as T[], 0); + let lone = LONE.get(p) as T[] | undefined; + if (!lone) { + lone = new Proxy(new Array(1), new Lone(p)); + LONE.set(p, lone); + } + return new AtIndex(lone, 0); +} + +/** + * The index `n` elements of `size` bytes past `index`, where wasm32's 32-bit addresses + * wrap: two indices whose addresses differ by a multiple of 2^32 are the same. + */ +function wrapIndex(index: number, n: number, size: number): number { + // The addresses of indices a period apart are the same. + const low = size & -size; + const period = low ? 2 ** 32 / low : 2 ** 32; + const at = (index + n) % period; + return at >= period / 2 ? at - period : at < -period / 2 ? at + period : at; +} + +/** `p.add(n)` and `p.offset(n)`: `n` elements past where `p` points. */ +export function ptrAdd(p: Place, n: number): Place { + if (n === 0) return p; + const e = elemOf(p); + return new AtIndex(e.array, e.index + n); +} + +/** + * `p.wrapping_add(n)` and `wrapping_offset`, `n` an offset of `size`-byte elements read + * as wasm32's `isize`, wrapping as a 32-bit address does. Null is at address 0, and a + * dangling pointer at `align`, which the arithmetic moves by address. + */ +export function ptrWrappingAdd(p: Place, n: number, size: number, align: number): Place { + if (n === 0) return p; + if (p === NULL || p === DANGLING || p instanceof Address) { + const base = p === NULL ? 0 : p === DANGLING ? align : (p as Address).addr; + const addr = Number((BigInt(base) + BigInt(n | 0) * BigInt(size)) & 0xffffffffn); + return addr === 0 ? NULL : addr === align ? DANGLING : (new Address(addr) as Place); + } + const e = elemOf(p); + return new AtIndex(e.array, wrapIndex(e.index, n | 0, size)); +} + +/** `p.offset_from(origin)`: how many elements `p` is past `origin`, in the same array. */ +export function ptrOffsetFrom(p: Place, origin: Place): number { + // Equal addresses are none apart, in no allocation as in one. + if (p === origin || (p instanceof Address && origin instanceof Address && p.addr === origin.addr)) return 0; + const [a, b] = [elemOf(p), elemOf(origin)]; + if (a.array !== b.array) throw new Error("rust2-ts: `offset_from` of pointers JS keeps in different arrays"); + return a.index - b.index; +} + +/** + * `ptr::copy` and `copy_nonoverlapping` of `n` elements from `src` to `dst`, as if through + * a buffer between, each made a value of its own by `copy` where the crate writes into its parts. + */ +export function ptrCopy(src: Place, dst: Place, n: number, copy?: (value: T) => T): void { + if (n === 0) return; + const [from, to] = [elemOf(src), elemOf(dst)]; + from.reach(n); + to.reach(n); + if (ArrayBuffer.isView(from.array) && ArrayBuffer.isView(to.array)) { + const bytes = from.array as unknown as Uint8Array; + (to.array as unknown as Uint8Array).set(bytes.subarray(from.index, from.index + n), to.index); + return; + } + const values = Array.from({ length: n }, (_, i) => from.array[from.index + i]); + // Memory nothing wrote yet is copied as it is, which no value's copy can read. + for (let i = 0; i < n; i++) to.array[to.index + i] = copy && values[i] !== undefined ? copy(values[i]) : values[i]; +} + +/** + * The pointer each empty slice made from a pointer into no array was made from, by the + * storage every part of it shares, which `sliceAsPtr` and `byteSliceAsPtr` give back. + */ +const EMPTY = /* @__PURE__ */ new WeakMap>(); + +/** `slice::from_raw_parts` of `len` elements from `p`. */ +export function ptrSlice(p: Place, len: number): T[] { + // None from a pointer into no array, such as null's or a dangling one's, keeps it. + if (len === 0 && !(p instanceof AtIndex)) { + const none = Object.freeze([]) as unknown as T[]; + EMPTY.set(none, p); + return none; + } + const at = elemOf(p); + at.reach(len); + const array = at.array as T[]; + return at.index === 0 && len === array.length ? array : sliceView(array, at.index, at.index + len); +} + +/** + * A raw pointer to a slice, `*const [T]`, `*mut [T]`, or `NonNull<[T]>`: where its first + * element is and how many it spans, which nothing checks until a read through it, as in Rust. + */ +export class RawSlice { + readonly ptr: Place; + readonly length: number; + /** What the first read through it made, which later reads share while it spans `length`. */ + #made: T[] | ByteSlice | undefined; + /** Where `#made` starts in the array beneath it, if anywhere. */ + #at: AtIndex | undefined; + /** Whether `#made` is the array itself, which spans more once it grows. */ + #whole = false; + + constructor(ptr: Place, length: number) { + this.ptr = ptr; + this.length = length; + } + + /** The slice it points at, read through it, which must be there. */ + deref(): T[] { + const made = this.#made as T[] | undefined; + if (made !== undefined) { + // Fails as a new read would once the array has shrunk past it. + this.#at?.reach(this.length); + if (!this.#whole || made.length === this.length) return made; + } + const slice = ptrSlice(this.ptr, this.length); + this.#at = this.ptr instanceof AtIndex ? elemOf(this.ptr) : undefined; + this.#whole = slice === this.#at?.array; + return (this.#made = slice); + } + + /** The bytes it points at, read through it, `shared` as `ptrByteSlice` takes it. */ + derefBytes(this: RawSlice, shared: boolean): ByteSlice { + if (this.#made !== undefined) return this.#made as ByteSlice; + const made = ptrByteSlice(this.ptr, this.length, shared); + // A copy of a byte in no array would miss a later write to the byte. + if (this.length === 0 || (this.ptr instanceof AtIndex && this.ptr.array instanceof Uint8Array)) this.#made = made; + return made; + } +} + +/** The raw pointer to `s`, a slice, a `[T; N]`, or a `Vec`'s storage. */ +export function rawSliceOf(s: { readonly length: number; [i: number]: T }): RawSlice { + return new RawSlice(sliceAsPtr(s), s.length); +} + +/** The raw pointer to the bytes of `s`. */ +export function rawByteSliceOf(s: ByteSlice): RawSlice { + return new RawSlice(byteSliceAsPtr(s), s.length); +} + +/** + * `slice::from_raw_parts` of `len` bytes from `p`. Only a `shared` reference to bytes in + * no array of bytes may be a copy of them, since nothing writes them while it lives. + */ +export function ptrByteSlice(p: Place, len: number, shared: boolean): ByteSlice { + // None from a pointer into no array of bytes, such as a lone byte's, keeps it. + if (len === 0 && !(p instanceof AtIndex && p.array instanceof Uint8Array)) { + const none = new Uint8Array(0); + EMPTY.set(none, p); + return new ByteSlice(none, 0, 0); + } + const at = elemOf(p); + at.reach(len); + if (at.array instanceof Uint8Array) return new ByteSlice(at.array, at.index, len); + // A byte in no array, which no `Uint8Array` can share. + if (!shared) throw new Error("rust2-ts: a raw or mutable slice of a byte in no array of bytes"); + return new ByteSlice(Uint8Array.from({ length: len }, (_, i) => at.array[at.index + i]), 0, len); +} + /** * `ptr::write` of `value` through a pointer to an object: `value` copied into it, one * object deep, so every pointer to it sees the write. @@ -313,7 +568,15 @@ export function rangeCheck(length: number, start: number, end: number): void { /** `&s[start..end]`, with `end` defaulting to the length. */ export function sliceRange(s: T[], start: number, end: number = s.length): T[] { rangeCheck(s.length, start, end); - return s.slice(start, end); + return slicePart(s, start, end); +} + +/** + * Elements `start` to `end` of `s`, a copy as a shared part is, or of an empty slice the + * slice itself, which keeps the pointer it was made from as Rust's part does. + */ +export function slicePart(s: T[], start: number, end: number = s.length): T[] { + return s.length === 0 ? s : s.slice(start, end); } /** @@ -402,7 +665,7 @@ export function sliceRangeMut(s: T[], start: number, end: number = s.length): /** `get` of `start..end`: the part, or `undefined` unless it is within the slice. */ export function sliceGetRange(s: T[], start: number, end: number = s.length): T[] | undefined { - return start <= end && end <= s.length ? s.slice(start, end) : undefined; + return start <= end && end <= s.length ? slicePart(s, start, end) : undefined; } /** `get_mut` of `start..end`: the runtime's view of it, or `undefined` as `sliceGetRange`. */ @@ -412,7 +675,7 @@ export function sliceGetRangeMut(s: T[], start: number, end: number = s.lengt /** `last_chunk`: the last `n` elements, or `undefined` if fewer. */ export function sliceLastChunk(s: T[], n: number): T[] | undefined { - return n > s.length ? undefined : s.slice(s.length - n); + return n > s.length ? undefined : slicePart(s, s.length - n); } /** `last_chunk_mut`: a view of the last `n` elements, or `undefined` if fewer. */ @@ -438,12 +701,12 @@ export function sliceSplitFirst(s: T[]): [T, T[]] | undefined { /** `split_at`, panicking past the length. */ export function sliceSplitAt(s: T[], n: number): [T[], T[]] { if (n > s.length) panic("mid > len"); - return [s.slice(0, n), s.slice(n)]; + return [slicePart(s, 0, n), slicePart(s, n)]; } /** `split_at_checked`: the halves, or `undefined` past the length. */ export function sliceSplitAtChecked(s: T[], n: number): [T[], T[]] | undefined { - return n > s.length ? undefined : [s.slice(0, n), s.slice(n)]; + return n > s.length ? undefined : [slicePart(s, 0, n), slicePart(s, n)]; } /** `split_at_mut`: views of either half, panicking past the length. */ diff --git a/ts/runtime/rt/collections.ts b/ts/runtime/rt/collections.ts index 1f8bc8d47..b47c42ef7 100644 --- a/ts/runtime/rt/collections.ts +++ b/ts/runtime/rt/collections.ts @@ -362,22 +362,37 @@ function itself(value: T): T { return value; } -/** The place of index `i` of `a`. */ +/** The place of element `index` of `array`, which a raw pointer into it is too. */ export class AtIndex implements Place { - private readonly a: { [i: number]: T }; - private readonly i: number; + readonly array: { [i: number]: T }; + readonly index: number; - constructor(a: { [i: number]: T }, i: number) { - this.a = a; - this.i = i; + constructor(array: { [i: number]: T }, index: number) { + this.array = array; + this.index = index; } get v(): T { - return this.a[this.i]; + this.reach(1); + return this.array[this.index]; } set v(value: T) { - this.a[this.i] = value; + this.reach(1); + this.array[this.index] = value; + } + + /** + * Fails where `n` elements from here run past either end of the array, which JS + * doesn't keep Rust's memory around: a JS array grows by a write past its end, where a + * `Vec`'s length stays until `set_len`, and a shared subslice is a copy, which a + * pointer moved past it would read nothing of. + */ + reach(n: number): void { + const length = (this.array as ArrayLike).length; + if (this.index < 0 || this.index + n > length) { + throw new Error("rust2-ts: a read or write through a pointer past the array JS keeps it in"); + } } } diff --git a/ts/runtime/rt/iter.ts b/ts/runtime/rt/iter.ts index 4cd135baa..0f1f20cba 100644 --- a/ts/runtime/rt/iter.ts +++ b/ts/runtime/rt/iter.ts @@ -22,7 +22,7 @@ // `owned` functions follow it, dispatching on the kind, so a bundle that never drops an // item carries none of it. -import { ByteSlice, ByteVec, byteVecExtendFromSlice, byteVecPush, byteVecReserve, byteVecWithCapacity, type Capped, capOf, capReserve, capSet, Err, type None, Ok, type Place, panic, place, type RangeBound, type Result, sliceView, vecCapacity, vecExtendFromSlice, vecMax, vecWithCapacity } from "../rt.ts"; +import { ByteSlice, ByteVec, byteVecExtendFromSlice, byteVecPush, byteVecReserve, byteVecWithCapacity, type Capped, capOf, capReserve, capSet, Err, type None, Ok, type Place, panic, place, type RangeBound, type Result, slicePart, sliceView, vecCapacity, vecExtendFromSlice, vecMax, vecWithCapacity } from "../rt.ts"; import { AtIndex, BTreeIter, HashIter, type Ordering } from "./collections.ts"; import { type Hasher, hash8 } from "./hash.ts"; import { charStep, intToBig, type Limbs, lt as wideLt, type Step } from "./int.ts"; @@ -471,7 +471,7 @@ function chunks(s: S, length: number, size: number, exact: boolean, rev: bool } function sliceCopy(s: T[], start: number, end: number): T[] { - return s.slice(start, end); + return slicePart(s, start, end); } function byteSlicePart(s: ByteSlice, start: number, end: number): ByteSlice { diff --git a/ts/src/census.rs b/ts/src/census.rs index adcec11af..3064d2631 100644 --- a/ts/src/census.rs +++ b/ts/src/census.rs @@ -188,6 +188,7 @@ const VERBATIM: &[&str] = &[ "let ... else", "match", "next", + "offset_from", "ref mut", "rt.None", "rt.OuterPending", diff --git a/ts/src/lower/copy.rs b/ts/src/lower/copy.rs index 82d0f206a..e12b8aa8a 100644 --- a/ts/src/lower/copy.rs +++ b/ts/src/lower/copy.rs @@ -128,12 +128,17 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { /// copies its bytes: the JS objects it holds by value copied where the crate's code /// writes into them in place. pub fn snapshot(&mut self, value: Expr, ty: Ty<'tcx>, span: Span) -> Expr { - match self.changes(ty).then(|| self.shim(ty, span)).flatten() { + match self.snapshot_fn(ty, span) { Some(copy) => copy.call(vec![value]), None => value, } } + /// The function [`Self::snapshot`] calls on a `ty`, if it copies one. + pub fn snapshot_fn(&mut self, ty: Ty<'tcx>, span: Span) -> Option { + self.changes(ty).then(|| self.shim(ty, span)).flatten() + } + /// `value`, copied if a `ty` is. pub fn copied(&mut self, value: Expr, ty: Ty<'tcx>, span: Span) -> Expr { match self.copy_fn(ty, span) { diff --git a/ts/src/lower/mir/call.rs b/ts/src/lower/mir/call.rs index 2a8009e3d..a64e6168d 100644 --- a/ts/src/lower/mir/call.rs +++ b/ts/src/lower/mir/call.rs @@ -102,6 +102,9 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { self.call_def(def_id, gargs.skip_binder(), args, result, span, &mut pre), true, ), + ty::FnDef(def_id, gargs) if let Some(why) = self.stray(def_id, gargs.skip_binder(), args) => { + (self.err(span, why), true) + } ty::FnDef(def_id, gargs) => match self.intercept(def_id, gargs.skip_binder(), args, dest, span, &mut pre) { Some(Some(done)) => done, // Done: the destination is set. diff --git a/ts/src/lower/mir/mod.rs b/ts/src/lower/mir/mod.rs index d4190a8d2..14da47c00 100644 --- a/ts/src/lower/mir/mod.rs +++ b/ts/src/lower/mir/mod.rs @@ -18,6 +18,7 @@ pub mod glue; pub(super) mod holders; mod inline; mod memory; +mod origin; mod place; mod rvalue; mod scalar; diff --git a/ts/src/lower/mir/origin.rs b/ts/src/lower/mir/origin.rs new file mode 100644 index 000000000..0896b6618 --- /dev/null +++ b/ts/src/lower/mir/origin.rs @@ -0,0 +1,356 @@ +//! Where a raw pointer a body makes points, as far as the body shows. JS keeps a value in +//! no array, and a shared subslice of values it copies, apart from what holds them in +//! Rust, so a pointer leaving one, or measured against another, fails at run time; where +//! the body proves that it does, translation refuses it with a span instead. A pointer the +//! body takes from elsewhere, such as a field or a parameter, is left to the runtime. + +use rustc_middle::mir::{self, BasicBlock, Local, Location, Operand, ProjectionElem, Rvalue, TerminatorKind}; +use rustc_middle::ty::{self, GenericArgsRef}; +use rustc_span::Spanned; + +use super::Fx; +use crate::lower::library::{self, Std}; +use crate::lower::num::Int; +use crate::lower::pointer::Pointee; + +/// The array JS keeps what a pointer points at in. +#[derive(Clone, Copy, PartialEq, Eq)] +enum Array<'tcx> { + /// An array of one, around the place a `&raw` here took, which each `&raw` makes anew. + Lone(Location), + /// A shared slice JS copies, of values it copies or of a byte in no array, made by the + /// call ending this block, with its length if the body fixes it. + Copy(BasicBlock, Option), + /// The array of a variable of this body's own, or of a field of one, at this place. + Owned(mir::Place<'tcx>), +} + +impl<'tcx> Array<'tcx> { + /// How many elements the array holds, if the body shows it. + fn len(self) -> Option { + match self { + Array::Lone(_) => Some(1), + Array::Copy(_, len) => len, + Array::Owned(_) => None, + } + } + + /// What the array is, as a refusal names it. + fn what(self) -> &'static str { + match self { + Array::Lone(_) => "a value in no array", + Array::Copy(..) => "a shared slice JS copies", + Array::Owned(_) => "an array of this body's own", + } + } +} + +/// Where a pointer points: into which array, at which index if the body fixes it. +#[derive(Clone, Copy)] +struct Origin<'tcx> { + array: Array<'tcx>, + index: Option, +} + +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, a + /// raw or mutable slice of a byte in no array of bytes, or a pointer, slice, or copy + /// reaching out of an array of one or a copied subslice. + pub(super) fn stray( + &self, + def_id: rustc_hir::def_id::DefId, + gargs: GenericArgsRef<'tcx>, + args: &[Spanned>], + ) -> Option { + if !library::is_std(self.tcx, def_id) { + return None; + } + let (owner, name) = library::key(self.tcx, def_id)?; + let arg = |i: usize| args.get(i).map(|a| &a.node); + let pointee = gargs.types().next()?; + if self.b.t.zero_sized(pointee) || self.b.t.pointee(pointee) != Pointee::Place { + return None; + } + 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() + ) + }) + }; + match (owner.as_str(), name.as_str()) { + ("ptr" | "NonNull", "offset_from" | "offset_from_unsigned") => { + let (a, b) = (self.origin(arg(0)?)?, self.origin(arg(1)?)?); + self.apart(a.array, b.array).then(|| { + format!( + "`offset_from` between a pointer into {} and one into {}, which JS keeps in different arrays", + a.array.what(), + b.array.what() + ) + }) + } + ("ptr" | "NonNull", "add" | "sub" | "offset") => { + let o = self.moved(self.origin(arg(0)?)?, name.as_str(), arg(1)?); + out(o, None, "a pointer") + } + ("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)?)?; + let byte = matches!(pointee.kind(), ty::Uint(ty::UintTy::U8)); + let none = self.const_int(arg(1)?) == Some(0); + if byte && name != "from_raw_parts" && !none && matches!(o.array, Array::Lone(_)) { + return Some("a raw or mutable slice of a byte in no array of bytes".into()); + } + // A raw pointer to a slice reads nothing until a read through it. + if owner != "raw" { + return None; + } + out(o, Some(self.const_int(arg(1)?)?), "a slice") + } + ( + "ptr", + "copy" + | "copy_nonoverlapping" + | "copy_to" + | "copy_to_nonoverlapping" + | "copy_from" + | "copy_from_nonoverlapping", + ) + | ("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")) + } + _ => None, + } + } + + /// 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. + fn apart(&self, a: Array<'tcx>, b: Array<'tcx>) -> bool { + let (Array::Owned(a), Array::Owned(b)) = (a, b) else { + return a != b; + }; + let index = |elem: ProjectionElem>| match elem { + ProjectionElem::ConstantIndex { + offset, + from_end: false, + .. + } => Some(i128::from(offset)), + ProjectionElem::Index(local) => match self.def_rvalue(self.traced(&Operand::Copy(local.into()))?)? { + Rvalue::Use(op, _) => self.const_int(op), + _ => None, + }, + _ => None, + }; + a.local != b.local + || a.projection + .iter() + .zip(b.projection) + .find(|(x, y)| x != y) + .is_some_and(|pair| match pair { + (ProjectionElem::Field(x, _), ProjectionElem::Field(y, _)) => x != y, + (x, y) => index(x).zip(index(y)).is_some_and(|(x, y)| x != y), + }) + } + + /// Where the pointer `op` points, if the body shows it. + fn origin(&self, op: &Operand<'tcx>) -> Option> { + let local = self.traced(op)?; + if let Some((bb, i)) = self.def_at[local] { + let rvalue = self.def_rvalue(local)?; + return match rvalue { + Rvalue::Use(op, _) + | Rvalue::Cast( + mir::CastKind::PtrToPtr + | mir::CastKind::PointerCoercion(ty::adjustment::PointerCoercion::MutToConstPointer, _), + op, + _, + ) if self.same_pointee(op, local) => self.origin(op), + 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), + }), + } + } + _ => None, + }; + } + let (def_id, _, args) = self.def_call(local)?; + let (owner, name) = library::key(self.tcx, def_id)?; + match (owner.as_str(), name.as_str()) { + ("ptr" | "NonNull", "add" | "sub" | "offset") => { + Some(self.moved(self.origin(&args[0].node)?, name.as_str(), &args[1].node)) + } + ("NonNull", "as_ptr" | "new_unchecked") => self.origin(&args[0].node), + ("slice" | "Vec", "as_ptr" | "as_mut_ptr") => self.start(&args[0].node), + _ => None, + } + } + + /// `o` moved by the offset `op` as `add`, `sub`, or `offset` 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, + _ => n, + }); + Origin { + index: o.index.zip(n).map(|(i, n)| i + n), + ..o + } + } + + /// Where the slice or `Vec`, or reference to one, `op` starts. + fn start(&self, op: &Operand<'tcx>) -> Option> { + let local = self.traced(op)?; + if self.def_at[local].is_some() { + return match self.def_rvalue(local)? { + Rvalue::Use(op, _) | Rvalue::Cast(mir::CastKind::PointerCoercion(..), op, _) => self.start(op), + Rvalue::Ref(_, _, place) | Rvalue::CopyForDeref(place) => match place.projection.as_slice() { + [ProjectionElem::Deref] => self.start(&Operand::Copy(mir::Place::from(place.local))), + projection + if !projection.contains(&ProjectionElem::Deref) + && match place.ty(&self.mir.local_decls, self.tcx).ty.kind() { + ty::Array(..) => true, + ty::Adt(adt, _) => Std::of(self.tcx, adt.did()) == Some(Std::Vec), + _ => false, + } => + { + Some(Origin { + array: Array::Owned(*place), + index: Some(0), + }) + } + _ => None, + }, + _ => None, + }; + } + let (def_id, gargs, args) = self.def_call(local)?; + let (owner, name) = library::key(self.tcx, def_id)?; + let mut types = gargs.types(); + let container = types.next()?.peel_refs(); + let byte = |ty: ty::Ty<'tcx>| matches!(ty.kind(), ty::Uint(ty::UintTy::U8)); + let copy = |len| { + Some(Origin { + array: Array::Copy(self.def_block(local)?, len), + index: Some(0), + }) + }; + // `get_unchecked` and `from_raw_parts` are generic over the element, not the slice. + let elem = match (owner.as_str(), name.as_str(), container.kind()) { + ("raw", "from_raw_parts" | "from_raw_parts_mut", _) => { + let o = self.origin(&args[0].node)?; + // A shared slice of a byte in no array is a copy of it, as no `Uint8Array` can + // share it, unless empty, which keeps the pointer. + let len = self.const_int(&args[1].node); + return match (byte(container), o.array) { + (true, Array::Lone(_)) if len != Some(0) => copy(len), + _ => Some(o), + }; + } + ("slice", "get_unchecked", _) => container, + ("Index", "index", ty::Slice(e) | ty::Array(e, _)) => *e, + ("Index", "index", ty::Adt(adt, a)) if Std::of(self.tcx, adt.did()) == Some(Std::Vec) => a.type_at(0), + _ => return None, + }; + let range = types.next()?; + // A shared range of bytes is a view of them. + if byte(elem) || Int::of(range).is_some() { + return None; + } + copy(self.range_len(&args[1].node, container)) + } + + /// How many elements the range `op` takes of `container`, if the body fixes it. + fn range_len(&self, op: &Operand<'tcx>, container: ty::Ty<'tcx>) -> Option { + let local = self.traced(op)?; + let Rvalue::Aggregate(kind, ops) = self.def_rvalue(local)? else { + return None; + }; + let mir::AggregateKind::Adt(did, ..) = **kind else { + return None; + }; + let all = match container.kind() { + ty::Array(_, n) => n.try_to_target_usize(self.tcx).map(i128::from), + _ => None, + }; + let ops: Vec<_> = ops.iter().map(|op| self.const_int(op)).collect(); + match (self.tcx.item_name(did).as_str(), ops.as_slice()) { + ("Range", [Some(start), Some(end)]) => Some(end - start), + ("RangeTo", [Some(end)]) => Some(*end), + ("RangeFrom", [Some(start)]) => Some(all? - start), + ("RangeFull", []) => all, + _ => None, + } + } + + /// The local `op` reads whole, if the body assigns it once. + fn traced(&self, op: &Operand<'tcx>) -> Option { + let local = op.place()?.as_local()?; + (self.defs[local] == 1 && !self.is_arg(local)).then_some(local) + } + + /// Whether `op` is a pointer to what `local` points at. + fn same_pointee(&self, op: &Operand<'tcx>, local: Local) -> bool { + let pointee = |ty: ty::Ty<'tcx>| ty.builtin_deref(true); + let from = op.ty(&self.mir.local_decls, self.tcx); + pointee(from).is_some() && pointee(from) == pointee(self.mir.local_decls[local].ty) + } + + /// The function, its generic arguments, and its arguments, of the call of std's that + /// assigns `local`. + fn def_call( + &self, + local: Local, + ) -> Option<( + rustc_hir::def_id::DefId, + GenericArgsRef<'tcx>, + &[Spanned>], + )> { + let bb = self.def_block(local)?; + let TerminatorKind::Call { func, args, .. } = &self.mir.basic_blocks[bb].terminator().kind else { + return None; + }; + let ty::FnDef(def_id, gargs) = *func.ty(&self.mir.local_decls, self.tcx).kind() else { + return None; + }; + library::is_std(self.tcx, def_id).then_some((def_id, gargs.skip_binder(), args)) + } + + /// The block whose call assigns `local`. + fn def_block(&self, local: Local) -> Option { + self.mir + .basic_blocks + .iter_enumerated() + .find_map(|(bb, data)| match &data.terminator().kind { + TerminatorKind::Call { destination, .. } if destination.as_local() == Some(local) => Some(bb), + _ => None, + }) + } +} diff --git a/ts/src/lower/mir/place.rs b/ts/src/lower/mir/place.rs index 4debb5135..1f0a6fe95 100644 --- a/ts/src/lower/mir/place.rs +++ b/ts/src/lower/mir/place.rs @@ -204,9 +204,21 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { let glue = matches!(self.env, Env::Shim) && place.local.as_u32() == 1 && pty.ty == decls[place.local].ty; // A box's pointer is the box, which is its contents. - if self.b.t.place_pointee(ty).is_some() && !glue && !self.box_pointer(place.local) { + let boxed = self.box_pointer(place.local); + if self.b.t.place_pointee(ty).is_some() && !glue && !boxed { e = e.member("v"); } + // A raw pointer to a slice is where it starts, read through as the slice. + if let ty::RawPtr(pointee, _) = ty.kind() + && !glue + && !boxed + && self.b.t.pointee(*pointee) == crate::lower::pointer::Pointee::Slice + { + e = match crate::lower::pointer::slice_of_bytes(*pointee) { + true => e.member("derefBytes").call(vec![Expr::lit("false")]), + false => e.member("deref").call(Vec::new()), + }; + } } ProjectionElem::Field(field, _) => { e = self.field(e, ty, pty.variant_index, field, span, out); diff --git a/ts/src/lower/mir/rvalue.rs b/ts/src/lower/mir/rvalue.rs index d0a8571d9..e30e73933 100644 --- a/ts/src/lower/mir/rvalue.rs +++ b/ts/src/lower/mir/rvalue.rs @@ -77,6 +77,28 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { 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 crate::lower::pointer::slice_of_bytes(pointee) { + true => Expr::rt("rawByteSliceOf", vec![value]), + false => Expr::rt("rawSliceOf", vec![value]), + } + } + }, // A slice of all of an array's bytes, as a borrow of it is. Pointee::Bytes => { let value = self.place_expr(*place, span, out); @@ -114,6 +136,29 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { "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::rt("sliceAsPtr", 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) @@ -213,6 +258,28 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { } /// The integer an operand has, if it is a constant one, a named `const` included. + /// `p`, a raw pointer to an array, `from`, as one to a slice of all of it: its first + /// element's pointer and the array's length. + fn raw_unsize(&mut self, p: Expr, from: Ty<'tcx>, span: Span) -> Expr { + let Some(array) = from.builtin_deref(true) else { + return self.err(span, format!("a coercion of `{from}` to a pointer to a slice")); + }; + let ty::Array(elem, len) = *array.kind() else { + return self.err(span, format!("a coercion of `{from}` to a pointer to a slice")); + }; + let Some(len) = len.try_to_target_usize(self.tcx) else { + return self.err( + span, + format!("a coercion of `{from}`, of no fixed length, to a pointer to a slice"), + ); + }; + let first = crate::lower::pointer::cast(self.b, p, array, elem, span); + Expr::New( + Box::new(Expr::Rt("RawSlice".into())), + vec![first, Expr::lit(len.to_string())], + ) + } + pub(super) fn const_int(&self, op: &Operand<'tcx>) -> Option { let Operand::Constant(c) = op else { return None; @@ -309,29 +376,33 @@ impl<'m, 'b, 'a, 'tcx> Fx<'m, 'b, 'a, 'tcx> { let value = self.operand(op, span, out); match crate::lower::bytes_ref(from) { true => value, - // A raw pointer to a slice is an `rt.Place` of one, which one to an array is not. - false if from.is_raw_ptr() => { - self.err(span, format!("a coercion of `{from}` to `{to}`, a pointer to a slice")) - } + false if from.is_raw_ptr() => self.raw_unsize(value, from, span), false => Expr::rt("byteSliceFrom", vec![value]), } } _ => self.err(span, format!("a coercion to `{to}`")), } } + PointerCoercion::Unsize + if from.is_raw_ptr() && to.builtin_deref(true).is_some_and(|t| t.is_slice()) => + { + let value = self.operand(op, span, out); + self.raw_unsize(value, from, span) + } PointerCoercion::Unsize | PointerCoercion::ReifyFnPointer(_) | PointerCoercion::ClosureFnPointer(_) | PointerCoercion::MutToConstPointer => self.operand(op, span, out), _ => self.err(span, format!("unsupported pointer coercion {coercion:?}")), }, - // The same pointer, as one to the same pointee: `*mut T` as `*const T`, or a - // `MaybeUninit` as the `T` it holds. + // The same pointer, as one to the same pointee, `*mut T` as `*const T` or a + // `MaybeUninit` as the `T` it holds, or one to the first element of an array. CastKind::PtrToPtr if let (Some(a), Some(b)) = (from.builtin_deref(true), to.builtin_deref(true)) - && self.b.t.uninit_inner(a) == self.b.t.uninit_inner(b) => + && from.is_raw_ptr() => { - self.operand(op, span, out) + let p = self.operand(op, span, out); + crate::lower::pointer::cast(self.b, p, a, b, span) } CastKind::PtrToPtr => self.err( span, diff --git a/ts/src/lower/mod.rs b/ts/src/lower/mod.rs index 62adb5c1f..76a37e88e 100644 --- a/ts/src/lower/mod.rs +++ b/ts/src/lower/mod.rs @@ -1306,7 +1306,7 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { // A place, unless a pointer to what it holds is that object. Some(library::Std::MaybeUninit) => match self.pointee(args.type_at(0)) { pointer::Pointee::Object | pointer::Pointee::Bytes => self.object_kind(args.type_at(0)), - pointer::Pointee::Place => None, + pointer::Pointee::Place | pointer::Pointee::Slice => None, }, Some(_) => None, None if adt.is_struct() || adt.is_union() => Some(ObjectKind::Class), diff --git a/ts/src/lower/pointer.rs b/ts/src/lower/pointer.rs index 1a3c695d3..b0949a3b5 100644 --- a/ts/src/lower/pointer.rs +++ b/ts/src/lower/pointer.rs @@ -7,6 +7,10 @@ //! value JS copies is, which a `Vec` or an `Arc` also is here: its JS object is what it //! owns, not where Rust keeps it. Null is `rt.NULL`, and a dangling pointer `rt.DANGLING`. //! +//! An `rt.Place` into an array, slice, or `Vec` is the `rt.AtIndex` of its element, which +//! 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 pointer to `()`, as std erases one, is an identity that compares as itself: null, //! `rt.DANGLING_UNIT`, or a waker's data. One to a place of `()` or from a reference to //! one is refused, as an `rt.Place` of it would be an identity of its own each borrow. @@ -43,6 +47,9 @@ pub enum Pointee { /// An `rt.ByteSlice` of the `[u8; N]` it points at, as a reference to one is, which /// compares by where its bytes are. Bytes, + /// An `rt.RawSlice` of a `[T]`: the pointer to its first element and its length, which + /// a read through it makes the slice of. + Slice, } impl<'a, 'tcx> Translator<'a, 'tcx> { @@ -56,6 +63,9 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { if super::byte_array(ty) { return Pointee::Bytes; } + if ty.is_slice() { + return Pointee::Slice; + } let object = match (self.object_kind(ty), ty.kind()) { (Some(ObjectKind::Array | ObjectKind::Bytes), ty::Array(..) | ty::Tuple(_)) => true, // An async body passes its context as a pointer to it, and a waker's vtable is @@ -98,6 +108,9 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { Pointee::Place => Some(format!( "comparing pointers to `{ty}`, each a place JS makes of its own" )), + Pointee::Slice => Some(format!( + "comparing pointers to `{ty}`, whose element pointers are places JS makes of their own" + )), } } @@ -136,6 +149,10 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { Pointee::Place => Type::Rt("Place", vec![self.ty(pointee, span)]), Pointee::Object => self.ty(pointee, span), Pointee::Bytes => Type::Rt("ByteSlice", Vec::new()), + Pointee::Slice => match pointee.kind() { + ty::Slice(elem) => Type::Rt("RawSlice", vec![self.ty(*elem, span)]), + _ => unreachable!("a slice's pointee"), + }, } } @@ -189,7 +206,7 @@ impl<'a, 'tcx> Translator<'a, 'tcx> { match (elem.kind(), self.pointee(*elem)) { (ty::Uint(ty::UintTy::U8), _) => Expr::New(Box::new(Expr::ident("Uint8Array")), vec![len]), (_, Pointee::Place) => Expr::New(Box::new(Expr::ident("Array")), vec![len]), - (_, Pointee::Object | Pointee::Bytes) => { + (_, Pointee::Object | Pointee::Bytes | Pointee::Slice) => { let elem_ty = self.ty(*elem, span); let each = Expr::Arrow( Vec::new(), @@ -429,6 +446,16 @@ pub fn call<'tcx>( let bytes = |b: &Body<'_, '_, 'tcx>, ty: Ty<'tcx>| b.t.pointee(ty) == Pointee::Bytes; let to_pointer = |e: Expr| Expr::rt("byteSliceFrom", vec![e]); let to_reference = |e: Expr| Expr::rt("byteSliceView", vec![e]); + // A raw pointer to a slice, from a reference to one, and the slice a read through it + // makes, shared if nothing writes through it. + let raw_of = |ty: Ty<'tcx>, s: Expr| match slice_of_bytes(ty) { + true => Expr::rt("rawByteSliceOf", vec![s]), + false => Expr::rt("rawSliceOf", vec![s]), + }; + let deref = |ty: Ty<'tcx>, p: Expr, shared: bool| match slice_of_bytes(ty) { + true => p.member("derefBytes").call(vec![Expr::lit(shared.to_string())]), + false => p.member("deref").call(Vec::new()), + }; let mut args = args.into_iter(); let mut arg = || args.next().expect("an argument"); let e = match (owner, name) { @@ -467,24 +494,24 @@ pub fn call<'tcx>( } } ("NonNull", "dangling") | ("ptr", "dangling" | "dangling_mut") => Expr::Rt("DANGLING".into()), - // The same pointer, as one to what a `MaybeUninit` holds or the other way. ("ptr" | "NonNull", "cast") => { - let to = targ(gargs, 1); - match b.t.uninit_inner(pointee()) == b.t.uninit_inner(to) { - true => arg(), - false => b.err( - span, - format!( - "a cast from a pointer to `{}` to one to `{to}`, which reads the bytes of one type as another", - pointee() - ), - ), - } + let p = arg(); + cast(b, p, pointee(), targ(gargs, 1), span) } ("ptr" | "NonNull", "addr" | "expose_provenance" | "with_addr" | "map_addr" | "without_provenance") => { b.err(span, "a pointer's address, which JS has no counterpart of") } + // A raw pointer to a slice is null where its first element's is. + ("ptr", "is_null") if kind(b, pointee()) == Some(Pointee::Slice) => { + arg().member("ptr").binary("===", Expr::Rt("NULL".into())) + } + // Only a pointer to a slice has these, generic over its element. + ("ptr" | "NonNull", "len") => arg().member("length"), + ("ptr" | "NonNull", "is_empty") => arg().member("length").binary("===", Expr::lit("0")), ("ptr", "is_null") => arg().binary("===", Expr::Rt("NULL".into())), + ("NonNull", "as_ref" | "as_mut") if kind(b, pointee()) == Some(Pointee::Slice) => { + deref(pointee(), arg(), name == "as_ref") + } ("NonNull", "as_mut") if bytes(b, pointee()) => to_reference(arg()), // The same pointer, as another type of the same pointee. ("ptr", "cast_const" | "cast_mut") @@ -505,10 +532,12 @@ pub fn call<'tcx>( } // A reference to a `MaybeUninit` of bytes is the `Uint8Array`. (Some(Pointee::Bytes), _) if !super::bytes_ref(from) => to_pointer(arg()), + (Some(Pointee::Slice), _) => raw_of(ty, arg()), _ => arg(), } } // As `NonNull::from` makes one, of a `[u8; N]` given as the view of its bytes. + ("ptr", "from_ref" | "from_mut") if kind(b, pointee()) == Some(Pointee::Slice) => raw_of(pointee(), arg()), ("ptr", "from_ref") => match kind(b, pointee()) { Some(Pointee::Place) => Expr::Object(vec![(crate::ts::Key::Name("v".into()), arg())]), Some(Pointee::Bytes) => to_pointer(arg()), @@ -516,12 +545,24 @@ pub fn call<'tcx>( }, ("NonNull", "new") => { let p = b.spill(arg(), "pointer", out); + let at = match kind(b, pointee()) { + Some(Pointee::Slice) => p.clone().member("ptr"), + _ => p.clone(), + }; Expr::Cond( - Box::new(p.clone().binary("===", Expr::Rt("NULL".into()))), + Box::new(at.binary("===", Expr::Rt("NULL".into()))), Box::new(Expr::undefined()), Box::new(p), ) } + ("ptr", "as_ref" | "as_mut") if kind(b, pointee()) == Some(Pointee::Slice) => { + let p = b.spill(arg(), "pointer", out); + Expr::Cond( + Box::new(p.clone().member("ptr").binary("===", Expr::Rt("NULL".into()))), + Box::new(Expr::undefined()), + Box::new(deref(pointee(), p, name == "as_ref")), + ) + } // `Option<&T>`: a shared reference to a value JS copies is the value. ("ptr", "as_ref" | "as_mut") => { let p = b.spill(arg(), "pointer", out); @@ -544,11 +585,13 @@ pub fn call<'tcx>( }, // A pointer to what a box held, which no box holds any longer. ("Box", "into_raw" | "leak") => match kind(b, pointee()) { + Some(Pointee::Slice) if name == "into_raw" => raw_of(pointee(), arg()), Some(Pointee::Place) => Expr::Object(vec![(crate::ts::Key::Name("v".into()), arg())]), Some(Pointee::Bytes) if name == "into_raw" => to_pointer(arg()), _ => arg(), }, ("Box", "from_raw") => match kind(b, pointee()) { + Some(Pointee::Slice) => deref(pointee(), arg(), false), Some(Pointee::Place) => arg().member("v"), Some(Pointee::Bytes) => to_reference(arg()), _ => arg(), @@ -590,6 +633,7 @@ pub fn call<'tcx>( let p = arg(); let value = match kind(b, pointee()) { Some(Pointee::Place) => p.member("v"), + Some(Pointee::Slice) => deref(pointee(), p, false), _ => p, }; b.t.drop_value(value, pointee(), span).unwrap_or_else(Expr::undefined) @@ -640,12 +684,156 @@ pub fn call<'tcx>( ("UnsafeCell", "get") if bytes(b, pointee()) => to_pointer(arg().member("v")), ("UnsafeCell", "get_mut") if b.t.needs_place(pointee()) => arg(), ("UnsafeCell", "get" | "get_mut" | "into_inner") => arg().member("v"), + // Pointers into an array: `rt.AtIndex`es, which a `Vec`'s and a slice's give. + ("slice" | "Vec", "as_ptr" | "as_mut_ptr") => { + let elem = targ(gargs, 0); + let s = arg(); + if let Some(why) = unmodeled(b, elem, "a pointer into") { + return Ok(Some(b.err(span, why))); + } + let byte = matches!(elem.kind(), ty::Uint(ty::UintTy::U8)); + match (owner, byte) { + ("slice", true) => Expr::rt("byteSliceAsPtr", vec![s]), + ("Vec", true) => Expr::rt("byteVecAsPtr", vec![s]), + _ => Expr::rt("sliceAsPtr", vec![s]), + } + } + ("ptr" | "NonNull", "add" | "sub" | "offset" | "wrapping_add" | "wrapping_sub" | "wrapping_offset") => { + let (p, n) = (arg(), arg()); + if b.t.zero_sized(pointee()) { + // Every pointer to a value of no bytes is where it was. + out.push(StmtKind::Expr(n).at(None)); + p + } else if let Some(why) = unmodeled(b, pointee(), "an offset of a pointer to") { + b.err(span, why) + } else { + let n = match name { + "sub" | "wrapping_sub" => Expr::unary("-", n), + _ => n, + }; + match name.starts_with("wrapping") { + true => match super::iter::layout(b, pointee(), span) { + Ok((size, align)) => Expr::rt( + "ptrWrappingAdd", + vec![p, n, Expr::lit(size.to_string()), Expr::lit(align.to_string())], + ), + Err(e) => e, + }, + false => Expr::rt("ptrAdd", vec![p, n]), + } + } + } + ("ptr" | "NonNull", "offset_from" | "offset_from_unsigned") => { + let (p, origin) = (arg(), arg()); + match unmodeled(b, pointee(), "`offset_from` of pointers to") { + // std asserts that the pointee has bytes. + _ if b.t.zero_sized(pointee()) => b.err( + span, + format!("`offset_from` of pointers to `{}`, which has no bytes", pointee()), + ), + Some(why) => b.err(span, why), + None => Expr::rt("ptrOffsetFrom", vec![p, origin]), + } + } + ( + "ptr", + "copy" + | "copy_nonoverlapping" + | "copy_to" + | "copy_to_nonoverlapping" + | "copy_from" + | "copy_from_nonoverlapping", + ) + | ("NonNull", "copy_to" | "copy_to_nonoverlapping" | "copy_from" | "copy_from_nonoverlapping") => { + let (x, y, n) = (arg(), arg(), arg()); + let (src, dst) = match name { + "copy_from" | "copy_from_nonoverlapping" => (y, x), + _ => (x, y), + }; + if b.t.zero_sized(pointee()) { + out.push(StmtKind::Expr(n).at(None)); + Expr::undefined() + } else if let Some(why) = unmodeled(b, pointee(), "a copy between pointers to") { + b.err(span, why) + } else { + let mut args = vec![src, dst, n]; + args.extend(b.t.snapshot_fn(pointee(), span)); + Expr::rt("ptrCopy", args) + } + } + ("raw", "from_raw_parts" | "from_raw_parts_mut") + | ("ptr", "slice_from_raw_parts" | "slice_from_raw_parts_mut") + | ("NonNull", "slice_from_raw_parts") => { + let (p, len) = (arg(), arg()); + match unmodeled(b, pointee(), "a slice from a pointer to") { + _ if b.t.zero_sized(pointee()) => { + return Ok(Some(b.err( + span, + format!("a slice of `{}`, which has no bytes, from a pointer", pointee()), + ))); + } + Some(why) => return Ok(Some(b.err(span, why))), + // A raw pointer to a slice is where it starts and how long it is, which + // nothing checks until a read through it. + None if owner != "raw" => Expr::New(Box::new(Expr::Rt("RawSlice".into())), vec![p, len]), + None if matches!(pointee().kind(), ty::Uint(ty::UintTy::U8)) => { + let shared = name == "from_raw_parts"; + Expr::rt("ptrByteSlice", vec![p, len, Expr::lit(shared.to_string())]) + } + None => Expr::rt("ptrSlice", vec![p, len]), + } + } ("mem", "forget") => Expr::undefined(), _ => return Err(args.collect()), }; Ok(Some(e)) } +/// Why pointers to `ty` can't point into an array, if they can't: an element JS keeps as +/// an object or as bytes is what a pointer to it is, which knows no array it is in. Only an +/// instance decides what a parameter is. +fn unmodeled<'tcx>(b: &Body<'_, '_, 'tcx>, ty: Ty<'tcx>, what: &str) -> Option { + if b.t.polymorphic() && ty.has_param() { + return None; + } + match b.t.pointee(ty) { + Pointee::Place => None, + Pointee::Object | Pointee::Bytes | Pointee::Slice => Some(format!( + "{what} `{ty}`, which JS keeps as an object that knows no array it is in" + )), + } +} + +/// The pointer `p` to `from`, as one to `to`: the same pointer, as one to what a +/// `MaybeUninit` holds or the other way, or the pointer to the first element of the array +/// or slice it points at. +pub fn cast<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, from: Ty<'tcx>, to: Ty<'tcx>, span: Span) -> Expr { + let (inner, into) = (b.t.uninit_inner(from), b.t.uninit_inner(to)); + if inner == into { + return p; + } + let elem = match inner.kind() { + ty::Array(elem, _) | ty::Slice(elem) if b.t.uninit_inner(*elem) == into => Some(*elem), + _ => None, + }; + let Some(elem) = elem else { + return b.err( + span, + format!("a cast from a pointer to `{from}` to one to `{to}`, which reads the bytes of one type as another"), + ); + }; + if let Some(why) = unmodeled(b, elem, "a pointer into an array of") { + return b.err(span, why); + } + // A pointer to a slice holds the pointer to its first element, and one to an array is + // the array, or the `rt.ByteSlice` of its bytes. + match b.t.pointee(inner) { + Pointee::Slice => p.member("ptr"), + Pointee::Bytes => Expr::rt("byteSliceAsPtr", vec![p]), + _ => Expr::rt("sliceAsPtr", vec![p]), + } +} + /// What a `PartialEq` or `Clone` call's `Self` points at, if it is a raw pointer or a /// `NonNull`. fn pointer_self<'tcx>(b: &Body<'_, '_, 'tcx>, call: &Call<'tcx>) -> Option> { @@ -667,13 +855,13 @@ fn read<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, ty: Ty<'tcx>, span: Span) -> Pointee::Place => p.member("v"), Pointee::Object => p, Pointee::Bytes => return Expr::rt("byteSliceToArray", vec![p]), + Pointee::Slice => return b.err(span, format!("a read of `{ty}`, which has no size")), }; b.t.snapshot(value, ty, span) } /// The write of `value`, a `ty`, where `p` points. fn write<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, value: Expr, ty: Ty<'tcx>, span: Span) -> Expr { - let _ = span; if b.t.zero_sized(ty) { return Expr::undefined(); } @@ -681,9 +869,15 @@ fn write<'tcx>(b: &mut Body<'_, '_, 'tcx>, p: Expr, value: Expr, ty: Ty<'tcx>, s Pointee::Place => p.member("v").assign(value), Pointee::Object => Expr::rt("ptrWrite", vec![p, value]), Pointee::Bytes => Expr::rt("byteSliceSet", vec![p, value]), + Pointee::Slice => b.err(span, format!("a write of `{ty}`, which has no size")), } } +/// Whether `ty` is `[u8]`, whose slices are `rt.ByteSlice`s. +pub fn slice_of_bytes(ty: Ty<'_>) -> bool { + matches!(ty.kind(), ty::Slice(elem) if matches!(elem.kind(), ty::Uint(ty::UintTy::U8))) +} + /// Whether pointers `x` and `y` to `ty` point at the same place: the same object or /// bytes, or null both. pub fn compare<'tcx>(b: &mut Body<'_, '_, 'tcx>, x: Expr, y: Expr, ty: Ty<'tcx>, span: Span) -> Expr { diff --git a/ts/tests/fixtures.rs b/ts/tests/fixtures.rs index b3ed2a2eb..6bae71b5f 100644 --- a/ts/tests/fixtures.rs +++ b/ts/tests/fixtures.rs @@ -373,9 +373,10 @@ fn lang_out() -> &'static Path { } fn lang_differential(out: &Path) { - // Hand-written callers, type-checked against the output they call: one runs it, and - // one mixes integer types, which `tsc` must refuse. - for file in ["caller.ts", "brands.ts"] { + // Hand-written callers, type-checked against the output they call: one runs it, with + // the one difference from native it accepts, and one mixes integer types, which `tsc` + // must refuse. + for file in ["caller.ts", "fma.ts", "brands.ts"] { std::fs::copy(manifest_dir().join("fixtures/lang").join(file), out.join(file)).unwrap(); } typecheck(out); diff --git a/ts/tests/fma.ts b/ts/tests/fma.ts new file mode 100644 index 000000000..ff819b563 --- /dev/null +++ b/ts/tests/fma.ts @@ -0,0 +1,88 @@ +// The lang fixture's `mul_add` allowance: its exact rounding agrees with the runtime's, +// and it accepts native a last place off only where the translation is exact. +// +// usage: node fma.ts + +import { strict as assert } from "node:assert"; + +import { exactMulAdd, mulAddsWithin } from "../fixtures/lang/fma.ts"; +import * as rt from "../runtime/rt.ts"; + +function double(bits: bigint): number { + const view = new DataView(new ArrayBuffer(8)); + view.setBigUint64(0, bits); + return view.getFloat64(0); +} + +function bits(x: number): bigint { + const view = new DataView(new ArrayBuffer(8)); + view.setFloat64(0, x); + return view.getBigUint64(0); +} + +/** `mul_adds`' output for one set: its operands, then `result`. */ +function set(a: number, b: number, c: number, result: number): Uint8Array { + const out = new Uint8Array(32); + const view = new DataView(out.buffer); + [a, b, c, result].forEach((x, i) => view.setFloat64(i * 8, x)); + return out; +} + +/** The double `n` places above `x`, below if negative. */ +const step = (x: number, n: bigint) => double(bits(x) + n); + +// Exact rounding at the edges: a tie broken by the product's error alone, an overflow the +// sum brings back, subnormal results, and signed zeros. +const eps = 2 ** -52; +assert.equal(exactMulAdd(1 + eps, 1 + eps, -1), 2 * eps + eps * eps); +assert.equal(exactMulAdd(1 + 2 ** -27, 1 - 2 ** -27, -1), -(2 ** -54)); +assert.equal(exactMulAdd(eps / 2 + (eps * eps) / 2, 1 - eps / 2, 1), 1 + eps); +assert.equal(exactMulAdd(Number.MAX_VALUE, 2, -Number.MAX_VALUE), Number.MAX_VALUE); +assert.equal(exactMulAdd(Number.MAX_VALUE, 1 + eps, 0), Infinity); +assert.equal(exactMulAdd(5e-324, 0.5, 5e-324), 1e-323); +assert.equal(exactMulAdd(5e-324, 1.5, 0), 1e-323); +assert.ok(Object.is(exactMulAdd(-0, 1, -0), -0)); +assert.ok(Object.is(exactMulAdd(0, -1, 0), 0)); +assert.ok(Object.is(exactMulAdd(1, -1, 1), 0)); +assert.equal(exactMulAdd(Infinity, 1, 1), undefined); + +// It agrees with the runtime's `f64MulAdd` over operands of any bits, and near-cancelling ones. +let state = 0x9e3779b97f4a7c15n; +function next(): bigint { + state ^= (state << 13n) & 0xffffffffffffffffn; + state ^= state >> 7n; + state ^= (state << 17n) & 0xffffffffffffffffn; + return state; +} +for (let i = 0; i < 20000; i++) { + const [a, b] = [double(next()), double(next())]; + const c = i % 2 === 0 ? double(next()) : -(a * b); + const exact = exactMulAdd(a, b, c); + if (exact === undefined) continue; + assert.ok(Object.is(exact, rt.f64MulAdd(a, b, c)), `${a}, ${b}, ${c}`); +} + +// A product of half `c`'s last place that only its error moves off the tie, which wasm32's +// `fma` rounds to `c` (rust-lang/compiler-builtins#1353). +const [a, b, c] = [double(0xbe8c27a8edc66e22n), double(0xbd522f6292409f88n), double(0x3f41e8176d8609aan)]; +const exact = exactMulAdd(a, b, c) as number; +assert.equal(bits(exact), 0x3f41e8176d8609abn); +const right = set(a, b, c, exact); +assert.ok(mulAddsWithin(set(a, b, c, c), right), "native's own result"); +assert.ok(mulAddsWithin(set(a, b, c, step(exact, 1n)), right), "native a place above"); +assert.ok(mulAddsWithin(set(a, b, c, step(exact, -1n)), right), "native a place below"); +assert.ok(mulAddsWithin(right, right)); +// Native two places off. +assert.ok(!mulAddsWithin(set(a, b, c, step(exact, 2n)), right)); +// The translation is the side a place off. +assert.ok(!mulAddsWithin(right, set(a, b, c, step(exact, 1n)))); +// Both off, a place apart. +assert.ok(!mulAddsWithin(set(a, b, c, step(exact, 1n)), set(a, b, c, step(exact, 2n)))); +// Operands that differ. +assert.ok(!mulAddsWithin(set(a, b, c, step(exact, 1n)), set(a, b, step(c, 1n), exact))); +// A set past the first, and outputs of different lengths. +assert.ok(!mulAddsWithin(new Uint8Array([...right, ...set(a, b, c, step(exact, 2n))]), new Uint8Array([...right, ...right]))); +assert.ok(!mulAddsWithin(set(a, b, c, step(exact, 1n)), new Uint8Array([...right, ...right]))); +// Native infinite beside the largest double. +const max = Number.MAX_VALUE; +assert.ok(!mulAddsWithin(set(max, 1, 0, Infinity), set(max, 1, 0, max))); diff --git a/ts/tests/pointers.ts b/ts/tests/pointers.ts new file mode 100644 index 000000000..6295114e7 --- /dev/null +++ b/ts/tests/pointers.ts @@ -0,0 +1,79 @@ +// A pointer the translation can't follow, such as one a struct's field holds, fails with +// a rust2-ts error where JS keeps apart what Rust's reaches, which native Rust can't show: +// there each is defined, as `repr(C)` fields and a shared subslice's parent are one +// allocation, and a `Vec`'s capacity is past its length. +// +// usage: node pointers.ts + +import { strict as assert } from "node:assert"; + +import * as rt from "../runtime/rt.ts"; + +const unsure = (f: () => void) => assert.throws(f, (e: Error) => !(e instanceof rt.Panic) && e.message.startsWith("rust2-ts: ")); + +// Two `&raw const x` of one variable, each its own `rt.Place`. +let x = 5; +const a = rt.place(() => x, (v) => (x = v)); +const b = rt.place(() => x, (v) => (x = v)); +unsure(() => rt.ptrOffsetFrom(a, b)); +assert.equal(rt.ptrOffsetFrom(rt.ptrAdd(a, 1), a), 1); + +// A `repr(C)` struct's fields, each a value in no array: reached from the other, and as a slice of both. +const pair = { low: 1, high: 2 }; +const low = rt.place(() => pair.low, (v) => (pair.low = v)); +const high = rt.place(() => pair.high, (v) => (pair.high = v)); +unsure(() => rt.ptrOffsetFrom(high, low)); +unsure(() => rt.ptrAdd(high, -1).v); +unsure(() => rt.ptrSlice(low, 2)); +unsure(() => rt.ptrCopy(low, high, 2)); + +// A shared subslice, which is a copy: before it, past it, and against its parent. +const lanes = [1, 2, 3, 4]; +const part = rt.sliceRange(lanes, 1, 3); +unsure(() => rt.ptrAdd(rt.sliceAsPtr(part), -1).v); +unsure(() => rt.ptrAdd(rt.sliceAsPtr(part), 2).v); +unsure(() => rt.ptrOffsetFrom(rt.sliceAsPtr(part), rt.sliceAsPtr(lanes))); +unsure(() => rt.ptrSlice(rt.sliceAsPtr(part), 3)); +unsure(() => rt.ptrCopy(rt.sliceAsPtr(part), rt.sliceAsPtr(lanes), 3)); +assert.equal(rt.ptrAdd(rt.sliceAsPtr(part), 1).v, 3); + +// A byte in no array, as a raw or mutable slice, as a shared one past it, and the +// pointer of a shared one, a copy, against the byte's. +let byte = 3; +const lone = rt.place(() => byte, (v) => (byte = v)); +unsure(() => rt.ptrByteSlice(lone, 1, false)); +unsure(() => rt.ptrByteSlice(lone, 2, true)); +const shared = rt.ptrByteSlice(lone, 1, true); +assert.deepEqual(shared.data, Uint8Array.of(3)); +unsure(() => rt.ptrOffsetFrom(rt.byteSliceAsPtr(shared), lone)); + +// A write into a `Vec`'s capacity past its length, which a JS array would grow by. +const vec: number[] = [7]; +unsure(() => { + rt.ptrAdd(rt.sliceAsPtr(vec), 1).v = 8; +}); +assert.equal(vec.length, 1); + +// A dangling pointer read through, written through, or copied from, which Rust leaves +// undefined, rather than reading `undefined`. +const dangling: rt.Place = rt.DANGLING; +unsure(() => rt.ptrAdd(dangling, 1).v); +unsure(() => { + rt.ptrAdd(dangling, 1).v = 1; +}); +unsure(() => rt.ptrCopy(dangling, rt.sliceAsPtr(vec), 1)); +unsure(() => rt.ptrSlice(dangling, 1)); +assert.equal(vec[0], 7); + +// A raw slice pointer read through once its `Vec` has shrunk past it, which Rust keeps +// the old values for, and one past the array, which Rust allows until a read. +const rows = [1, 2, 3, 4]; +const all = new rt.RawSlice(rt.sliceAsPtr(rows), 4); +const tail = new rt.RawSlice(rt.sliceAsPtr(rows, 1), 3); +assert.equal(all.deref()[3] + tail.deref()[2], 8); +rows.length = 3; +unsure(() => all.deref()); +unsure(() => tail.deref()); +const long = new rt.RawSlice(rt.sliceAsPtr(rows), 10); +assert.equal(long.length, 10); +unsure(() => long.deref()); diff --git a/ts/tests/runtime.rs b/ts/tests/runtime.rs index 07e642470..93f357965 100644 --- a/ts/tests/runtime.rs +++ b/ts/tests/runtime.rs @@ -25,6 +25,18 @@ fn nan_payloads() { assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr)); } +/// The lang fixture's `mul_add` allowance rounds exactly and accepts native a last place +/// off only where the translation is exact, which `fma.ts` checks. +#[test] +fn fma_allowance() { + let script = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests").join("fma.ts"); + let output = Command::new("node") + .arg(script) + .output() + .expect("running node, which the dev shell provides"); + assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr)); +} + /// A recursive `OnceLock` init, or `LazyLock` or `LazyCell` force, panics with std's /// message, which `once.ts` checks. #[test] @@ -294,3 +306,17 @@ fn unicode() { path.display() ); } + +/// A pointer the translation can't follow fails where JS keeps apart what Rust's reaches, +/// which `pointers.ts` checks. +#[test] +fn pointers_apart() { + let script = PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("tests") + .join("pointers.ts"); + let output = Command::new("node") + .arg(script) + .output() + .expect("running node, which the dev shell provides"); + assert!(output.status.success(), "{}", String::from_utf8_lossy(&output.stderr)); +}